Merge "Test NdkMediaDataSource"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index ff340b4..7ed77ac 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -3581,6 +3581,51 @@
<string name="managed_user_check_managed_user_test">Check affiliated profile owner</string>
<string name="managed_user_incorrect_managed_user">Missing or incorrect affiliated profile owner: CTSVerifier is not affilaited PO!</string>
+ <string name="device_owner_disallow_user_switch">Disallow user switch</string>
+ <string name="device_owner_disallow_user_switch_info">
+ Please press the Set restriction button to set the user restriction.
+ Then press Go to open the Settings, and manually find and open user settings section.
+ Confirm that:\n
+ \n
+ - Trying to switch to guest users triggers a support message.\n
+ - Trying to switch to secondary users triggers a support message.\n
+ \n
+ In additional, if quick settings is available, confirm that user switcher is hidden or
+ disabled.
+ Use the Back button to return to this page.
+ </string>
+
+ <string name="device_owner_user_switcher_message">User switcher message</string>
+ <string name="device_owner_user_switcher_message_info">
+ 1. Please press the \'With user switcher message\' button to set the user switcher message.
+ You will then be automatically switched to a secondary user. If a user switcher dialog shows
+ up, it should read \'Start user session\'. Wait until you are automatically switched back to
+ primary, if a user switcher dialog shows up, it should read \'End user session\'.
+
+ \n
+ 2. Please press the \'Without user switcher message\' button to clear the user switcher
+ message. You will then be automatically switched to a secondary user. If a user switcher
+ dialog shows up, it should read \'Switching to managed user\'. Wait until you are
+ automatically switched back to primary, if a user switcher dialog shows up, it should read
+ \'Switching to (name of primary user)\'.
+ </string>
+ <string name="device_owner_with_user_switcher_message">With user switcher message</string>
+ <string name="device_owner_without_user_switcher_message">Without user switcher message</string>
+
+ <string name="device_owner_enable_logout">Logout</string>
+ <string name="device_owner_enable_logout_info">
+ Please press the Go button to enable logout. You will then be switched to a newly created
+ user.
+ Look for a way to logout the current user without unlocking the lock screen. The control is
+ usually named \'End session\'.\n
+ The location may vary depending on manufacturer, typical locations are:\n
+ - In power button menu by long pressing power button.\n
+ - On the lock screen.\n
+ \n
+ When successfully logout and switched back to primary user, confirm that the logout control
+ is not available in primary user.
+ </string>
+
<!-- Strings for JobScheduler Tests -->
<string name="js_test_description">This test is mostly automated, but requires some user interaction. You can pass this test once the list items below are checked.</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 c62f111..7384f5b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -111,6 +111,10 @@
public static final String COMMAND_SET_DEFAULT_IME = "set-default-ime";
public static final String COMMAND_CLEAR_DEFAULT_IME = "clear-default-ime";
public static final String COMMAND_CREATE_MANAGED_USER = "create-managed-user";
+ public static final String COMMAND_WITH_USER_SWITCHER_MESSAGE = "with-user-switcher-message";
+ public static final String COMMAND_WITHOUT_USER_SWITCHER_MESSAGE =
+ "without-user-switcher-message";
+ public static final String COMMAND_ENABLE_LOGOUT = "enable-logout";
public static final String EXTRA_USER_RESTRICTION =
"com.android.cts.verifier.managedprovisioning.extra.USER_RESTRICTION";
@@ -486,6 +490,22 @@
Collections.singleton(DeviceAdminTestReceiver.AFFILIATION_ID));
mDpm.startUserInBackground(mAdmin, userHandle);
} break;
+ case COMMAND_WITH_USER_SWITCHER_MESSAGE: {
+ createAndSwitchUserWithMessage("Start user session", "End user session");
+ } break;
+ case COMMAND_WITHOUT_USER_SWITCHER_MESSAGE: {
+ createAndSwitchUserWithMessage(null, null);
+ } break;
+ case COMMAND_ENABLE_LOGOUT: {
+ if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+ return;
+ }
+ mDpm.addUserRestriction(mAdmin, UserManager.DISALLOW_USER_SWITCH);
+ mDpm.setLogoutEnabled(mAdmin, true);
+ UserHandle userHandle = mDpm.createAndManageUser(mAdmin, "managed user", mAdmin,
+ null, SKIP_SETUP_WIZARD | MAKE_USER_EPHEMERAL);
+ mDpm.switchUser(mAdmin, userHandle);
+ } break;
}
} catch (Exception e) {
Log.e(TAG, "Failed to execute command: " + intent, e);
@@ -533,6 +553,7 @@
mDpm.clearUserRestriction(mAdmin, UserManager.DISALLOW_CONFIG_BLUETOOTH);
mDpm.clearUserRestriction(mAdmin, UserManager.DISALLOW_CONFIG_VPN);
mDpm.clearUserRestriction(mAdmin, UserManager.DISALLOW_DATA_ROAMING);
+ mDpm.clearUserRestriction(mAdmin, UserManager.DISALLOW_USER_SWITCH);
mDpm.setDeviceOwnerLockScreenInfo(mAdmin, null);
mDpm.setKeyguardDisabled(mAdmin, false);
@@ -554,6 +575,10 @@
mDpm.uninstallCaCert(mAdmin, TEST_CA.getBytes());
mDpm.setMaximumFailedPasswordsForWipe(mAdmin, 0);
mDpm.setSecureSetting(mAdmin, Settings.Secure.DEFAULT_INPUT_METHOD, null);
+ mDpm.setAffiliationIds(mAdmin, Collections.emptySet());
+ mDpm.setStartUserSessionMessage(mAdmin, null);
+ mDpm.setEndUserSessionMessage(mAdmin, null);
+ mDpm.setLogoutEnabled(mAdmin, false);
uninstallHelperPackage();
removeManagedProfile();
@@ -611,4 +636,22 @@
private boolean isSystemInputMethodInfo(InputMethodInfo inputMethodInfo) {
return inputMethodInfo.getServiceInfo().applicationInfo.isSystemApp();
}
+
+ private void createAndSwitchUserWithMessage(String startUserSessionMessage,
+ String endUserSessionMessage) {
+ if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+ return;
+ }
+ mDpm.setStartUserSessionMessage(mAdmin, startUserSessionMessage);
+ mDpm.setEndUserSessionMessage(mAdmin, endUserSessionMessage);
+ mDpm.setAffiliationIds(mAdmin,
+ Collections.singleton(DeviceAdminTestReceiver.AFFILIATION_ID));
+
+ PersistableBundle extras = new PersistableBundle();
+ extras.putBoolean(DeviceAdminTestReceiver.EXTRA_LOGOUT_ON_START, true);
+ UserHandle userHandle = mDpm.createAndManageUser(mAdmin, "managed user", mAdmin,
+ extras,
+ SKIP_SETUP_WIZARD | MAKE_USER_EPHEMERAL);
+ mDpm.switchUser(mAdmin, userHandle);
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
index 974b063..e9f62a1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -55,6 +55,8 @@
DEVICE_OWNER_PKG, ADMIN_RECEIVER_TEST_CLASS);
public static final String EXTRA_MANAGED_USER_TEST =
"com.android.cts.verifier.managedprovisioning.extra.MANAGED_USER_TEST";
+ public static final String EXTRA_LOGOUT_ON_START =
+ "com.android.cts.verifier.managedprovisioning.extra.LOGOUT_ON_START";
public static final String AFFILIATION_ID = "affiliationId";
public static ComponentName getReceiverComponentName() {
@@ -120,6 +122,12 @@
Log.e(TAG, "Error when calling primary user", re);
}
});
+ } else if (intent.getBooleanExtra(EXTRA_LOGOUT_ON_START, false)) {
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ ComponentName admin = getReceiverComponentName();
+ dpm.setAffiliationIds(admin,
+ Collections.singleton(DeviceAdminTestReceiver.AFFILIATION_ID));
+ dpm.logoutUser(admin);
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index 6c87b84..887d795 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -70,6 +70,9 @@
private static final String POLICY_TRANSPARENCY_TEST_ID = "POLICY_TRANSPARENCY";
private static final String ENTERPRISE_PRIVACY_TEST_ID = "ENTERPRISE_PRIVACY";
private static final String NETWORK_LOGGING_UI_TEST_ID = "NETWORK_LOGGING_UI";
+ private static final String DISALLOW_USER_SWITCH_TEST_ID = "DISALLOW_USER_SWITCH";
+ private static final String USER_SWITCHER_MESSAGE_TEST_ID = "USER_SWITCHER_MESSAGE";
+ private static final String ENABLE_LOGOUT_TEST_ID = "ENABLE_LOGOUT";
private static final String COMP_TEST_ID = "COMP_UI";
private static final String MANAGED_USER_TEST_ID = "MANAGED_USER_UI";
private static final String REMOVE_DEVICE_OWNER_TEST_ID = "REMOVE_DEVICE_OWNER";
@@ -333,9 +336,9 @@
compIntent));
}
- // Managed user
if (packageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS)
&& UserManager.supportsMultipleUsers()) {
+ // Managed user
adapter.add(createInteractiveTestItem(this, MANAGED_USER_TEST_ID,
R.string.managed_user_test,
R.string.managed_user_positive_tests_instructions,
@@ -343,6 +346,40 @@
new ButtonInfo(
R.string.device_owner_settings_go,
createCreateManagedUserIntent())}));
+
+ // DISALLOW_USER_SWITCH
+ adapter.add(createInteractiveTestItem(this, DISALLOW_USER_SWITCH_TEST_ID,
+ R.string.device_owner_disallow_user_switch,
+ R.string.device_owner_disallow_user_switch_info,
+ new ButtonInfo[]{
+ new ButtonInfo(
+ R.string.device_owner_user_restriction_set,
+ CommandReceiverActivity.createSetUserRestrictionIntent(
+ UserManager.DISALLOW_USER_SWITCH, true)),
+ new ButtonInfo(
+ R.string.device_owner_settings_go,
+ new Intent(Settings.ACTION_SETTINGS))}));
+
+ // User switcher message
+ adapter.add(createInteractiveTestItem(this, USER_SWITCHER_MESSAGE_TEST_ID,
+ R.string.device_owner_user_switcher_message,
+ R.string.device_owner_user_switcher_message_info,
+ new ButtonInfo[]{
+ new ButtonInfo(
+ R.string.device_owner_with_user_switcher_message,
+ createWithUserSwitcherMessageIntent()),
+ new ButtonInfo(
+ R.string.device_owner_without_user_switcher_message,
+ createWithoutUserSwitcherMessageIntent())}));
+
+ // Enable logout
+ adapter.add(createInteractiveTestItem(this, ENABLE_LOGOUT_TEST_ID,
+ R.string.device_owner_enable_logout,
+ R.string.device_owner_enable_logout_info,
+ new ButtonInfo[]{
+ new ButtonInfo(
+ R.string.device_owner_settings_go,
+ createEnableLogoutIntent())}));
}
// Network logging UI
@@ -409,6 +446,24 @@
CommandReceiverActivity.COMMAND_CREATE_MANAGED_USER);
}
+ private Intent createWithUserSwitcherMessageIntent() {
+ return new Intent(this, CommandReceiverActivity.class)
+ .putExtra(CommandReceiverActivity.EXTRA_COMMAND,
+ CommandReceiverActivity.COMMAND_WITH_USER_SWITCHER_MESSAGE);
+ }
+
+ private Intent createWithoutUserSwitcherMessageIntent() {
+ return new Intent(this, CommandReceiverActivity.class)
+ .putExtra(CommandReceiverActivity.EXTRA_COMMAND,
+ CommandReceiverActivity.COMMAND_WITHOUT_USER_SWITCHER_MESSAGE);
+ }
+
+ private Intent createEnableLogoutIntent() {
+ return new Intent(this, CommandReceiverActivity.class)
+ .putExtra(CommandReceiverActivity.EXTRA_COMMAND,
+ CommandReceiverActivity.COMMAND_ENABLE_LOGOUT);
+ }
+
private boolean isStatusBarEnabled() {
// Watches don't support the status bar so this is an ok proxy, but this is not the most
// general test for that. TODO: add a test API to do a real check for status bar support.
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
index 6ce71df..08c3665 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
@@ -24,6 +24,7 @@
import com.android.tradefed.config.Option;
import com.android.tradefed.config.Option.Importance;
import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.suite.BaseTestSuite;
import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
@@ -32,6 +33,7 @@
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.LinkedHashMap;
+import java.util.Set;
/**
* A Test for running Compatibility Test Suite with new suite system.
@@ -107,4 +109,23 @@
public final void resetRetryId() {
mRetrySessionId = null;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public LinkedHashMap<String, IConfiguration> loadingStrategy(
+ Set<IAbi> abis, File testsDir, String suitePrefix, String suiteTag) {
+ LinkedHashMap<String, IConfiguration> loadedConfigs = new LinkedHashMap<>();
+ // Load the configs that are part of the tests dir
+ loadedConfigs.putAll(
+ getModuleLoader().loadConfigsFromDirectory(testsDir, abis, suitePrefix, suiteTag));
+ // Add an extra check in CTS since we never expect the config folder to be empty.
+ if (loadedConfigs.size() == 0) {
+ throw new IllegalArgumentException(
+ String.format("No config files found in %s or in resources.",
+ testsDir.getAbsolutePath()));
+ }
+ return loadedConfigs;
+ }
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
index 28c44fc..b797b32 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
@@ -16,6 +16,8 @@
package android.appsecurity.cts;
+import android.platform.test.annotations.Presubmit;
+
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.DeviceNotAvailableException;
@@ -271,6 +273,7 @@
"testRequestNonExistentPermission");
}
+ @Presubmit
public void testRequestPermissionFromTwoGroups23() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), false, false));
runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
index b4ed09f..9e408b3 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
@@ -450,7 +450,7 @@
Manifest.permission.WRITE_CALENDAR
};
- // Request the permission and do nothing
+ // Request the permission and allow it
BasePermissionActivity.Result result = requestPermissions(permissions,
REQUEST_CODE_PERMISSIONS, BasePermissionActivity.class, () -> {
try {
@@ -463,9 +463,24 @@
}
});
- // Expect the permission is not granted
+ // Expect the permission are reported as granted
assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
permissions, new boolean[] {true, true});
+
+ // The permissions are granted
+ assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
+ .checkSelfPermission(Manifest.permission.WRITE_CONTACTS));
+ assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
+ .checkSelfPermission(Manifest.permission.WRITE_CALENDAR));
+
+ // In API < N_MR1 all permissions of a group are granted. I.e. the grant was "expanded"
+ assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
+ .checkSelfPermission(Manifest.permission.READ_CALENDAR));
+
+ // Even the contacts group was expanded, the read-calendar permission is not in the
+ // manifest, hence not granted.
+ assertEquals(PackageManager.PERMISSION_DENIED, getInstrumentation().getContext()
+ .checkSelfPermission(Manifest.permission.READ_CONTACTS));
}
@Test
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java
index 40dfbb3..9b1ae92 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java
@@ -26,19 +26,46 @@
*/
public class ClearApplicationDataTest extends BaseDeviceAdminTest {
private static final String TEST_PKG = "com.android.cts.intent.receiver";
+ private static final String DEVICE_ADMIN_PKG = "com.android.cts.deviceandprofileowner";
private static final Semaphore mSemaphore = new Semaphore(0);
private static final long CLEAR_APPLICATION_DATA_TIMEOUT_S = 10;
- public void testClearApplicationData() throws Exception {
- mDevicePolicyManager.clearApplicationUserData(ADMIN_RECEIVER_COMPONENT, TEST_PKG,
- AsyncTask.THREAD_POOL_EXECUTOR,
+ public void testClearApplicationData_testPkg() throws Exception {
+ clearApplicationDataTest(TEST_PKG, /* shouldSucceed */ true);
+ }
+
+ public void testClearApplicationData_deviceProvisioning() throws Exception {
+ String deviceProvisioningPackageName = getDeviceProvisioningPackageName();
+ if (deviceProvisioningPackageName == null) {
+ return;
+ }
+ clearApplicationDataTest(deviceProvisioningPackageName, /* shouldSucceed */ false);
+ }
+
+ public void testClearApplicationData_activeAdmin() throws Exception {
+ clearApplicationDataTest(DEVICE_ADMIN_PKG, /* shouldSucceed */ false);
+ }
+
+ private void clearApplicationDataTest(String packageName, boolean shouldSucceed)
+ throws Exception {
+ mDevicePolicyManager.clearApplicationUserData(ADMIN_RECEIVER_COMPONENT,
+ packageName, AsyncTask.THREAD_POOL_EXECUTOR,
(String pkg, boolean succeeded) -> {
- assertEquals(TEST_PKG, pkg);
- assertTrue(succeeded);
+ assertEquals(packageName, pkg);
+ assertEquals(shouldSucceed, succeeded);
mSemaphore.release();
});
-
assertTrue("Clearing application data took too long",
mSemaphore.tryAcquire(CLEAR_APPLICATION_DATA_TIMEOUT_S, TimeUnit.SECONDS));
}
+
+ private String getDeviceProvisioningPackageName() {
+ final int provisioning_app_id = mContext.getResources().getIdentifier(
+ "config_deviceProvisioningPackage", "string", "android");
+ if (provisioning_app_id > 0) {
+ return mContext.getResources().getString(provisioning_app_id);
+ } else {
+ return null;
+ }
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 42dce85..8609684 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -856,18 +856,36 @@
Collections.singletonMap(ARG_ALLOW_FAILURE, Boolean.toString(allowFailures)));
}
- public void testClearApplicationData() throws Exception {
+ public void testClearApplicationData_testPkg() throws Exception {
if (!mHasFeature) {
return;
}
installAppAsUser(INTENT_RECEIVER_APK, mUserId);
runDeviceTestsAsUser(INTENT_RECEIVER_PKG, INTENT_RECEIVER_PKG + ".ClearApplicationDataTest",
"testWriteToSharedPreference", mUserId);
- executeDeviceTestMethod(".ClearApplicationDataTest", "testClearApplicationData");
+ executeDeviceTestMethod(".ClearApplicationDataTest", "testClearApplicationData_testPkg");
runDeviceTestsAsUser(INTENT_RECEIVER_PKG, INTENT_RECEIVER_PKG + ".ClearApplicationDataTest",
"testSharedPreferenceCleared", mUserId);
}
+ public void testClearApplicationData_deviceProvisioning() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ // Clearing data of device configuration app should fail
+ executeDeviceTestMethod(".ClearApplicationDataTest",
+ "testClearApplicationData_deviceProvisioning");
+ }
+
+ public void testClearApplicationData_activeAdmin() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ // Clearing data of active admin should fail
+ executeDeviceTestMethod(".ClearApplicationDataTest",
+ "testClearApplicationData_activeAdmin");
+ }
+
public void testPrintingPolicy() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
index b1f16e5..5245612 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
@@ -58,14 +58,13 @@
public void testCreateIme1() throws Throwable {
final TestHelper helper = new TestHelper(getClass(), DeviceTestConstants.TEST_CREATE_IME1);
+ final long startActivityTime = SystemClock.uptimeMillis();
+ helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
+
pollingCheck(() -> helper.queryAllEvents()
.collect(startingFrom(helper.isStartOfTest()))
.anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_CREATE))),
TIMEOUT, "CtsInputMethod1.onCreate is called");
-
- final long startActivityTime = SystemClock.uptimeMillis();
- helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
-
pollingCheck(() -> helper.queryAllEvents()
.filter(isNewerThan(startActivityTime))
.anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT))),
@@ -78,14 +77,13 @@
final TestHelper helper = new TestHelper(
getClass(), DeviceTestConstants.TEST_SWITCH_IME1_TO_IME2);
+ final long startActivityTime = SystemClock.uptimeMillis();
+ helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
+
pollingCheck(() -> helper.queryAllEvents()
.collect(startingFrom(helper.isStartOfTest()))
.anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_CREATE))),
TIMEOUT, "CtsInputMethod1.onCreate is called");
-
- final long startActivityTime = SystemClock.uptimeMillis();
- helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
-
pollingCheck(() -> helper.queryAllEvents()
.filter(isNewerThan(startActivityTime))
.anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT))),
diff --git a/hostsidetests/security/AndroidTest.xml b/hostsidetests/security/AndroidTest.xml
index 1bba585..ef9581b 100644
--- a/hostsidetests/security/AndroidTest.xml
+++ b/hostsidetests/security/AndroidTest.xml
@@ -86,7 +86,6 @@
<!-- Bulletin 2017-11 -->
<!-- Please add tests solely from this bulletin below to avoid merge conflict -->
-
<option name="append-bitness" value="true" />
</target_preparer>
diff --git a/hostsidetests/security/src/android/security/cts/Poc16_10.java b/hostsidetests/security/src/android/security/cts/Poc16_10.java
index 5b54eea..0c9a5b3 100644
--- a/hostsidetests/security/src/android/security/cts/Poc16_10.java
+++ b/hostsidetests/security/src/android/security/cts/Poc16_10.java
@@ -16,21 +16,8 @@
package android.security.cts;
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.DeviceTestCase;
-
import android.platform.test.annotations.SecurityTest;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Scanner;
-
@SecurityTest
public class Poc16_10 extends SecurityTestCase {
diff --git a/hostsidetests/security/src/android/security/cts/Poc16_12.java b/hostsidetests/security/src/android/security/cts/Poc16_12.java
index b102d4e..8ae30d6 100644
--- a/hostsidetests/security/src/android/security/cts/Poc16_12.java
+++ b/hostsidetests/security/src/android/security/cts/Poc16_12.java
@@ -16,21 +16,8 @@
package android.security.cts;
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.DeviceTestCase;
-
import android.platform.test.annotations.SecurityTest;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Scanner;
-
@SecurityTest
public class Poc16_12 extends SecurityTestCase {
diff --git a/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml b/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml
index db7c886..03e3979 100644
--- a/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml
+++ b/hostsidetests/statsd/apps/statsdapp/AndroidManifest.xml
@@ -29,7 +29,6 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.DUMP" /> <!-- must be granted via pm grant -->
-
<application android:label="@string/app_name">
<uses-library android:name="android.test.runner" />
<uses-library android:name="org.apache.http.legacy" android:required="false" />
@@ -72,6 +71,12 @@
<service android:name=".StatsdJobService"
android:permission="android.permission.BIND_JOB_SERVICE" />
+ <receiver android:name=".AlertTests$BroadcastSubscriberReceiver1"
+ android:exported="false" >
+ </receiver>
+ <receiver android:name=".AlertTests$BroadcastSubscriberReceiver2"
+ android:exported="false" >
+ </receiver>
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AlertTests.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AlertTests.java
new file mode 100644
index 0000000..c303ad8
--- /dev/null
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AlertTests.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.cts.device.statsd;
+
+import android.app.PendingIntent;
+import android.app.StatsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.StatsDimensionsValue;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+import android.util.StatsLog;
+import com.android.internal.os.StatsdConfigProto.Alert;
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.BroadcastSubscriberDetails;
+import com.android.internal.os.StatsdConfigProto.CountMetric;
+import com.android.internal.os.StatsdConfigProto.FieldMatcher;
+import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
+import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.internal.os.StatsdConfigProto.Subscription;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
+import com.android.os.AtomsProto.AppHook;
+import com.android.os.AtomsProto.Atom;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AlertTests {
+ public static final String TAG = "statsd.AlertTests";
+
+ private Context mContext;
+ private StatsManager mStatsManager;
+
+ private PendingIntent mPendingIntent1;
+ private PendingIntent mPendingIntent2;
+ private static CountDownLatch sLatch1; // static since used by receiver
+ private static CountDownLatch sLatch2; // static since used by receiver
+ private static final int UID = android.os.Process.myUid(); // static since used by receiver
+
+ public static final int SLEEP_TIME_AFTER_SET_SUBSCRIBER_MS = 2_000;
+
+ public static final int CONFIG_ID = 12;
+ public static final int ATOM_MATCHER_ID = 1;
+ public static final int METRIC_ID = 1;
+ public static final int ALERT_ID = 1;
+ public static final int SUBSCRIPTION_ID_1 = 1;
+ public static final int SUBSCRIPTION_ID_2 = 2;
+ public static final int SUBSCRIBER_ID_1 = 1;
+ public static final int SUBSCRIBER_ID_2 = 2;
+
+ private static final StatsdConfig CONFIG = StatsdConfig.newBuilder().setId(CONFIG_ID)
+ .addAtomMatcher(AtomMatcher.newBuilder()
+ .setId(ATOM_MATCHER_ID)
+ .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+ .setAtomId(Atom.APP_HOOK_FIELD_NUMBER)
+ // Event only when the uid is this app's uid.
+ .addFieldValueMatcher(FieldValueMatcher.newBuilder()
+ .setField(AppHook.UID_FIELD_NUMBER)
+ .setEqInt(UID)
+ )
+ )
+ )
+ .addCountMetric(CountMetric.newBuilder()
+ .setId(METRIC_ID)
+ .setWhat(ATOM_MATCHER_ID)
+ .setBucket(TimeUnit.CTS)
+ // Slice by uid (since that's the typical case)
+ .setDimensionsInWhat(FieldMatcher.newBuilder()
+ .setField(Atom.APP_HOOK_FIELD_NUMBER)
+ .addChild(FieldMatcher.newBuilder().setField(AppHook.UID_FIELD_NUMBER))
+ )
+ )
+ .addAlert(Alert.newBuilder()
+ .setId(ALERT_ID)
+ .setMetricId(METRIC_ID)
+ .setNumBuckets(4)
+ .setRefractoryPeriodSecs(0)
+ .setTriggerIfSumGt(0) // even a single event triggers it
+ )
+ .addSubscription(Subscription.newBuilder()
+ .setId(SUBSCRIPTION_ID_1)
+ .setRuleType(Subscription.RuleType.ALERT)
+ .setRuleId(ALERT_ID)
+ .setBroadcastSubscriberDetails(BroadcastSubscriberDetails.newBuilder()
+ .setSubscriberId(SUBSCRIBER_ID_1))
+ )
+ .addSubscription(Subscription.newBuilder()
+ .setId(SUBSCRIPTION_ID_2)
+ .setRuleType(Subscription.RuleType.ALERT)
+ .setRuleId(ALERT_ID)
+ .setBroadcastSubscriberDetails(BroadcastSubscriberDetails.newBuilder()
+ .setSubscriberId(SUBSCRIBER_ID_2))
+ )
+ .build();
+
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mStatsManager = (StatsManager) mContext.getSystemService("stats");
+ mPendingIntent1 = PendingIntent.getBroadcast(mContext, 1,
+ new Intent(mContext, BroadcastSubscriberReceiver1.class), 0);
+ mPendingIntent2 = PendingIntent.getBroadcast(mContext, 2,
+ new Intent(mContext, BroadcastSubscriberReceiver2.class), 0);
+ }
+
+ /** Tests that setBroadcastSubscriber() works as intended. */
+ @Test
+ public void testBroadcastSubscriber() {
+ Log.d(TAG, "Running testBroadcastSubscriber under UID=" + UID);
+ try {
+ logPendingIntent(mPendingIntent1);
+ logPendingIntent(mPendingIntent2);
+ sLatch1 = new CountDownLatch(1);
+ sLatch2 = new CountDownLatch(1);
+ mStatsManager.removeConfiguration(CONFIG_ID);
+ mStatsManager.addConfiguration(CONFIG_ID, CONFIG.toByteArray(), "", "");
+
+ mStatsManager.setBroadcastSubscriber(CONFIG_ID, SUBSCRIBER_ID_1, mPendingIntent1);
+ mStatsManager.setBroadcastSubscriber(CONFIG_ID, SUBSCRIBER_ID_2, mPendingIntent2);
+ AtomTests.sleep(SLEEP_TIME_AFTER_SET_SUBSCRIBER_MS);
+ StatsLog.write(StatsLog.APP_HOOK, UID, /* label */ 1, StatsLog.APP_HOOK__STATE__START);
+
+ Assert.assertTrue(waitForLatch(sLatch1, 10_000));
+ Assert.assertTrue(waitForLatch(sLatch2, 10_000));
+ } finally {
+ mStatsManager.removeConfiguration(CONFIG_ID);
+ }
+ }
+
+ /** Tests that setBroadcastSubscriber(,, null) works as intended. */
+ @Test
+ public void testUnsetBroadcastSubscriber() {
+ Log.d(TAG, "Running testUnsetBroadcastSubscriber under UID=" + UID);
+ try {
+ logPendingIntent(mPendingIntent1);
+ logPendingIntent(mPendingIntent2);
+ sLatch1 = new CountDownLatch(2);
+ sLatch2 = new CountDownLatch(2);
+ mStatsManager.removeConfiguration(CONFIG_ID);
+ mStatsManager.addConfiguration(CONFIG_ID, CONFIG.toByteArray(), "", "");
+
+ mStatsManager.setBroadcastSubscriber(CONFIG_ID, SUBSCRIBER_ID_1, mPendingIntent1);
+ mStatsManager.setBroadcastSubscriber(CONFIG_ID, SUBSCRIBER_ID_2, mPendingIntent2);
+ AtomTests.sleep(SLEEP_TIME_AFTER_SET_SUBSCRIBER_MS);
+ StatsLog.write(StatsLog.APP_HOOK, UID, /* label */ 1, StatsLog.APP_HOOK__STATE__START);
+ AtomTests.sleep(SLEEP_TIME_AFTER_SET_SUBSCRIBER_MS);
+ // During sleep, both latches count down by 1, as tested in testBroadcastSubscriber.
+
+ // Now remove subscriber2 and make sure only subscriber1 receives broadcast.
+ mStatsManager.setBroadcastSubscriber(CONFIG_ID, SUBSCRIBER_ID_2, null);
+ AtomTests.sleep(SLEEP_TIME_AFTER_SET_SUBSCRIBER_MS);
+ StatsLog.write(StatsLog.APP_HOOK, UID, /* label */ 1, StatsLog.APP_HOOK__STATE__START);
+ Assert.assertTrue(waitForLatch(sLatch1, 10_000));
+ Log.d(TAG, "About to wait for a latch, expecting it to not finish");
+ Assert.assertFalse(waitForLatch(sLatch2, 2_000)); // should fail
+
+ // Now add subscriber2 back again and make sure it receives this time.
+ mStatsManager.setBroadcastSubscriber(CONFIG_ID, SUBSCRIBER_ID_2, mPendingIntent2);
+ AtomTests.sleep(SLEEP_TIME_AFTER_SET_SUBSCRIBER_MS);
+ StatsLog.write(StatsLog.APP_HOOK, UID, /* label */ 1, StatsLog.APP_HOOK__STATE__START);
+ Assert.assertTrue(waitForLatch(sLatch2, 10_000));
+ } finally {
+ mStatsManager.removeConfiguration(CONFIG_ID);
+ }
+ }
+
+ /** Prints some information about the PendingIntent to logcat. */
+ private static void logPendingIntent(PendingIntent pendingIntent) {
+ Log.d(TAG, String.format("Created PendingIntent with " +
+ "CreatorPackage=%s, CreatorUid=%d, toString=%s",
+ pendingIntent.getCreatorPackage(),
+ pendingIntent.getCreatorUid(),
+ pendingIntent.toString()));
+ }
+
+ /** Checks that the intent (presumably received from a broadcast) is as expected. */
+ private static void checkIntent(Intent intent, long subscriptionId) {
+ Log.d(TAG, "Received " + (intent != null ? intent.toString() : "null intent")
+ + "\nwith extras " + (intent != null && intent.getExtras() != null ?
+ intent.getExtras().toString() : "(null intent)"));
+
+ Assert.assertNotNull(intent);
+
+ long intentConfigUid = intent.getLongExtra(StatsManager.EXTRA_STATS_CONFIG_UID, -1);
+ long intentConfigKey = intent.getLongExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, -1);
+ long intentSubscrId = intent.getLongExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, -1);
+ long intentRuleId = intent.getLongExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, -1);
+ StatsDimensionsValue intentDimsValue =
+ intent.getParcelableExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE);
+ String intentDimsValueStr =
+ intentDimsValue != null ? intentDimsValue.toString() : "null";
+
+ Log.d(TAG, "Relevant extras are"
+ + " {ConfigUid=" + intentConfigUid
+ + ", ConfigKey=" + intentConfigKey
+ + ", SubscriptionId=" + intentSubscrId
+ + ", RuleId=" + intentRuleId
+ + ", DimensionsValue=" + intentDimsValueStr
+ + "}");
+
+ Assert.assertEquals(UID, intentConfigUid);
+ Assert.assertEquals(CONFIG_ID, intentConfigKey);
+ Assert.assertEquals(subscriptionId, intentSubscrId);
+ Assert.assertEquals(METRIC_ID, intentRuleId);
+
+ String expectedDimValue = String.format("%d:{%d:%d|}",
+ Atom.APP_HOOK_FIELD_NUMBER, AppHook.UID_FIELD_NUMBER, UID);
+ Assert.assertEquals(expectedDimValue, intentDimsValueStr);
+
+ Assert.assertEquals(Atom.APP_HOOK_FIELD_NUMBER, intentDimsValue.getField());
+ List<StatsDimensionsValue> intentTuple = intentDimsValue.getTupleValueList();
+ Assert.assertEquals(1, intentTuple.size());
+ StatsDimensionsValue intentTupleValue = intentTuple.get(0);
+ Assert.assertEquals(AppHook.UID_FIELD_NUMBER, intentTupleValue.getField());
+ Assert.assertTrue(intentTupleValue.isValueType(StatsDimensionsValue.INT_VALUE_TYPE));
+ Assert.assertEquals(UID, intentTupleValue.getIntValue());
+ }
+
+ public final static class BroadcastSubscriberReceiver1 extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context mContext, Intent intent) {
+ Log.d(TAG, "Broadcast received by BroadcastSubscriberReceiver1");
+ checkIntent(intent, SUBSCRIPTION_ID_1);
+ Assert.assertNotNull(sLatch1);
+ sLatch1.countDown();
+ }
+ }
+ public final static class BroadcastSubscriberReceiver2 extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context mContext, Intent intent) {
+ Log.d(TAG, "Broadcast received by BroadcastSubscriberReceiver2");
+ checkIntent(intent, SUBSCRIPTION_ID_2);
+ Assert.assertNotNull(sLatch2);
+ sLatch2.countDown();
+ }
+ }
+
+ /** Waits for up to maxWaitTimeMs for the latch to count down, and returns whether it did so. */
+ private static boolean waitForLatch(CountDownLatch latch, int maxWaitTimeMs) {
+ try {
+ boolean didFinish
+ = latch.await(maxWaitTimeMs, java.util.concurrent.TimeUnit.MILLISECONDS);
+ if (didFinish) {
+ Log.v(TAG, "Latch finished");
+ return true;
+ } else {
+ Log.w(TAG, "Latch did not finish in specified time.");
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted exception while awaiting latch to finish", e);
+ }
+ return false;
+ }
+}
+
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
index 3cc2816..791fc86 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
@@ -61,11 +61,12 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-public class AtomTests{
+public class AtomTests {
private static final String TAG = AtomTests.class.getSimpleName();
@Test
public void testAudioState() {
+ // TODO: This should surely be getTargetContext(), here and everywhere, but test first.
Context context = InstrumentationRegistry.getContext();
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.good);
mediaPlayer.start();
@@ -365,6 +366,7 @@
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "Received broadcast.");
onReceiveLatch.countDown();
}
};
diff --git a/hostsidetests/statsd/src/android/cts/statsd/alert/BroadcastSubscriberTests.java b/hostsidetests/statsd/src/android/cts/statsd/alert/BroadcastSubscriberTests.java
new file mode 100644
index 0000000..f0e14fc
--- /dev/null
+++ b/hostsidetests/statsd/src/android/cts/statsd/alert/BroadcastSubscriberTests.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.cts.statsd.alert;
+
+import android.cts.statsd.atom.DeviceAtomTestCase;
+import com.android.tradefed.log.LogUtil.CLog;
+
+/**
+ * Statsd broadcast subscriber tests. Some may be done device-side.
+ */
+public class BroadcastSubscriberTests extends DeviceAtomTestCase {
+
+ private static final String TAG = "Statsd.BroadcastSubscriberTests";
+
+ // These tests currently require selinux policy to be disabled, so keep false!
+ // TODO: Fix this.
+ private static final boolean TESTS_ENABLED = false;
+
+ public static final int SUBSCRIBER_TESTS_CONFIG_ID = 12;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (!TESTS_ENABLED) {
+ CLog.w(TAG, TAG + " tests are disabled by a flag. Change flag to true to run.");
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ getDevice().executeShellCommand(
+ String.join(" ", REMOVE_CONFIG_CMD, String.valueOf(getUid()),
+ String.valueOf(SUBSCRIBER_TESTS_CONFIG_ID)));
+ super.tearDown();
+ }
+
+ /** Tests that anomaly detection generic subscribers work. */
+ public void testBroadcastSubscriber() throws Exception {
+ runAlertTest("testBroadcastSubscriber");
+ }
+
+ /** Tests that unsetting generic subscribers work. */
+ public void testUnsetBroadcastSubscriber() throws Exception {
+ runAlertTest("testUnsetBroadcastSubscriber");
+ }
+
+ public void runAlertTest(String methodName) throws Exception {
+ if (!TESTS_ENABLED) return;
+ CLog.d("\nPerforming device-side test of " + methodName + " for uid " + getUid());
+ runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AlertTests", methodName);
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
index 8561614..06fd98e 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
@@ -54,9 +54,9 @@
*/
public class AtomTestCase extends BaseTestCase {
- private static final String UPDATE_CONFIG_CMD = "cmd stats config update";
- private static final String DUMP_REPORT_CMD = "cmd stats dump-report";
- private static final String REMOVE_CONFIG_CMD = "cmd stats config remove";
+ protected static final String UPDATE_CONFIG_CMD = "cmd stats config update";
+ protected static final String DUMP_REPORT_CMD = "cmd stats dump-report";
+ protected static final String REMOVE_CONFIG_CMD = "cmd stats config remove";
protected static final String CONFIG_UID = "1000";
/** ID of the config, which evaluates to -1572883457. */
protected static final long CONFIG_ID = "cts_config".hashCode();
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
index ddb59d4..00995ae 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
@@ -36,6 +36,7 @@
import com.android.os.AtomsProto.ScreenStateChanged;
import com.android.os.AtomsProto.SubsystemSleepState;
import com.android.os.StatsLog.EventMetricData;
+import com.android.tradefed.log.LogUtil.CLog;
import java.util.Arrays;
import java.util.HashSet;
@@ -53,6 +54,17 @@
// For tests that require incidentd. Keep as true until TESTS_ENABLED is permanently enabled.
private static final boolean INCIDENTD_TESTS_ENABLED = true;
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (!TESTS_ENABLED) {
+ CLog.w(TAG, TAG + " tests are disabled by a flag. Change flag to true to run.");
+ }
+ if (!INCIDENTD_TESTS_ENABLED) {
+ CLog.w(TAG, TAG + " anomaly tests are disabled by a flag. Change flag to true to run");
+ }
+ }
+
public void testScreenStateChangedAtom() throws Exception {
if (!TESTS_ENABLED) {return;}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
index 8743277..1a7f42e0 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
@@ -19,6 +19,7 @@
import com.android.os.AtomsProto.Atom;
import com.android.os.StatsLog.EventMetricData;
+import com.android.tradefed.log.LogUtil.CLog;
import java.util.Arrays;
import java.util.HashSet;
@@ -104,6 +105,14 @@
private static final int PROC_STATE_ATOM_TAG = Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER;
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (!TESTS_ENABLED) {
+ CLog.w(TAG, TAG + " tests are disabled by a flag. Change flag to true to run.");
+ }
+ }
+
public void testForegroundService() throws Exception {
if (!TESTS_ENABLED) return;
Set<Integer> onStates = new HashSet<>(Arrays.asList(
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 36e34f8..a8a32ce 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -33,6 +33,7 @@
import com.android.os.AtomsProto.WifiLockStateChanged;
import com.android.os.AtomsProto.WifiScanStateChanged;
import com.android.os.StatsLog.EventMetricData;
+import com.android.tradefed.log.LogUtil.CLog;
import java.util.Arrays;
import java.util.HashSet;
@@ -57,6 +58,13 @@
private static final String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
private static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (!TESTS_ENABLED) {
+ CLog.w(TAG, TAG + " tests are disabled by a flag. Change flag to true to run.");
+ }
+ }
public void testBleScan() throws Exception {
if (!TESTS_ENABLED) return;
if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
diff --git a/tests/AlarmManager/app/AndroidManifest.xml b/tests/AlarmManager/app/AndroidManifest.xml
index ceb8fb9..f5b04b6 100644
--- a/tests/AlarmManager/app/AndroidManifest.xml
+++ b/tests/AlarmManager/app/AndroidManifest.xml
@@ -18,7 +18,7 @@
package="android.alarmmanager.alarmtestapp.cts">
<application>
- <activity android:name=".TestAlarmActivity"
+ <receiver android:name=".TestAlarmScheduler"
android:exported="true" />
<receiver android:name=".TestAlarmReceiver" />
</application>
diff --git a/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmActivity.java b/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmScheduler.java
similarity index 75%
rename from tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmActivity.java
rename to tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmScheduler.java
index 8f666dd8..35763be 100644
--- a/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmActivity.java
+++ b/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmScheduler.java
@@ -16,39 +16,38 @@
package android.alarmmanager.alarmtestapp.cts;
-import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.Intent;
-import android.os.Bundle;
import android.util.Log;
/**
- * This Activity is to be used as part of
- * {@link android.alarmmanager.cts.BackgroundRestrictedAlarmsTest}
+ * This receiver is to be used to schedule alarms as part of tests in
+ * {@link android.alarmmanager.cts}
*/
-public class TestAlarmActivity extends Activity {
- private static final String TAG = TestAlarmActivity.class.getSimpleName();
+public class TestAlarmScheduler extends BroadcastReceiver {
+ private static final String TAG = TestAlarmScheduler.class.getSimpleName();
private static final String PACKAGE_NAME = "android.alarmmanager.alarmtestapp.cts";
public static final String ACTION_SET_ALARM = PACKAGE_NAME + ".action.SET_ALARM";
public static final String EXTRA_TRIGGER_TIME = PACKAGE_NAME + ".extra.TRIGGER_TIME";
public static final String EXTRA_REPEAT_INTERVAL = PACKAGE_NAME + ".extra.REPEAT_INTERVAL";
public static final String EXTRA_TYPE = PACKAGE_NAME + ".extra.TYPE";
+ public static final String EXTRA_ALLOW_WHILE_IDLE = PACKAGE_NAME + ".extra.ALLOW_WHILE_IDLE";
public static final String ACTION_SET_ALARM_CLOCK = PACKAGE_NAME + ".action.SET_ALARM_CLOCK";
public static final String EXTRA_ALARM_CLOCK_INFO = PACKAGE_NAME + ".extra.ALARM_CLOCK_INFO";
public static final String ACTION_CANCEL_ALL_ALARMS = PACKAGE_NAME + ".action.CANCEL_ALARMS";
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final AlarmManager am = getSystemService(AlarmManager.class);
- final Intent intent = getIntent();
- final Intent receiverIntent = new Intent(this, TestAlarmReceiver.class);
+ public void onReceive(Context context, Intent intent) {
+ final AlarmManager am = context.getSystemService(AlarmManager.class);
+ final Intent receiverIntent = new Intent(context, TestAlarmReceiver.class);
receiverIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
final PendingIntent alarmClockSender =
- PendingIntent.getBroadcast(this, 0, receiverIntent, 0);
- final PendingIntent alarmSender = PendingIntent.getBroadcast(this, 1, receiverIntent, 0);
+ PendingIntent.getBroadcast(context, 0, receiverIntent, 0);
+ final PendingIntent alarmSender = PendingIntent.getBroadcast(context, 1, receiverIntent, 0);
switch (intent.getAction()) {
case ACTION_SET_ALARM_CLOCK:
if (!intent.hasExtra(EXTRA_ALARM_CLOCK_INFO)) {
@@ -68,10 +67,14 @@
final int type = intent.getIntExtra(EXTRA_TYPE, 0);
final long triggerTime = intent.getLongExtra(EXTRA_TRIGGER_TIME, 0);
final long interval = intent.getLongExtra(EXTRA_REPEAT_INTERVAL, 0);
+ final boolean allowWhileIdle = intent.getBooleanExtra(EXTRA_ALLOW_WHILE_IDLE,
+ false);
Log.d(TAG, "Setting alarm: type=" + type + ", triggerTime=" + triggerTime
- + ", interval=" + interval);
+ + ", interval=" + interval + ", allowWhileIdle=" + allowWhileIdle);
if (interval > 0) {
am.setRepeating(type, triggerTime, interval, alarmSender);
+ } else if (allowWhileIdle) {
+ am.setExactAndAllowWhileIdle(type, triggerTime, alarmSender);
} else {
am.setExact(type, triggerTime, alarmSender);
}
@@ -85,6 +88,5 @@
Log.e(TAG, "Unspecified action " + intent.getAction());
break;
}
- finish();
}
}
\ No newline at end of file
diff --git a/tests/AlarmManager/src/android/alarmmanager/cts/BackgroundRestrictedAlarmsTest.java b/tests/AlarmManager/src/android/alarmmanager/cts/BackgroundRestrictedAlarmsTest.java
index 2b37bfd..cf83c8f 100644
--- a/tests/AlarmManager/src/android/alarmmanager/cts/BackgroundRestrictedAlarmsTest.java
+++ b/tests/AlarmManager/src/android/alarmmanager/cts/BackgroundRestrictedAlarmsTest.java
@@ -19,7 +19,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import android.alarmmanager.alarmtestapp.cts.TestAlarmActivity;
+import android.alarmmanager.alarmtestapp.cts.TestAlarmScheduler;
import android.alarmmanager.alarmtestapp.cts.TestAlarmReceiver;
import android.app.AlarmManager;
import android.content.BroadcastReceiver;
@@ -41,23 +41,27 @@
import java.io.IOException;
+/**
+ * Tests that apps put in forced app standby by the user do not get to run alarms while in the
+ * background
+ */
@LargeTest
@RunWith(AndroidJUnit4.class)
public class BackgroundRestrictedAlarmsTest {
private static final String TAG = BackgroundRestrictedAlarmsTest.class.getSimpleName();
private static final String TEST_APP_PACKAGE = "android.alarmmanager.alarmtestapp.cts";
- private static final String TEST_APP_ACTIVITY = TEST_APP_PACKAGE + ".TestAlarmActivity";
+ private static final String TEST_APP_RECEIVER = TEST_APP_PACKAGE + ".TestAlarmScheduler";
private static final String APP_OP_RUN_ANY_IN_BACKGROUND = "RUN_ANY_IN_BACKGROUND";
private static final String APP_OP_MODE_ALLOWED = "allow";
private static final String APP_OP_MODE_IGNORED = "ignore";
private static final long DEFAULT_WAIT = 1_000;
- private static final long POLL_INTERVAL = 1_000;
+ private static final long POLL_INTERVAL = 200;
private static final long MIN_REPEATING_INTERVAL = 10_000;
private Object mLock = new Object();
private Context mContext;
- private ComponentName mAlarmActivity;
+ private ComponentName mAlarmScheduler;
private UiDevice mUiDevice;
private int mAlarmCount;
@@ -76,7 +80,7 @@
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- mAlarmActivity = new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY);
+ mAlarmScheduler = new ComponentName(TEST_APP_PACKAGE, TEST_APP_RECEIVER);
mAlarmCount = 0;
updateAlarmManagerConstants();
setAppOpsMode(APP_OP_MODE_IGNORED);
@@ -86,23 +90,21 @@
}
private void scheduleAlarm(int type, long triggerMillis, long interval) {
- final Intent setAlarmIntent = new Intent(TestAlarmActivity.ACTION_SET_ALARM);
- setAlarmIntent.setComponent(mAlarmActivity);
- setAlarmIntent.putExtra(TestAlarmActivity.EXTRA_TYPE, type);
- setAlarmIntent.putExtra(TestAlarmActivity.EXTRA_TRIGGER_TIME, triggerMillis);
- setAlarmIntent.putExtra(TestAlarmActivity.EXTRA_REPEAT_INTERVAL, interval);
- setAlarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(setAlarmIntent);
+ final Intent setAlarmIntent = new Intent(TestAlarmScheduler.ACTION_SET_ALARM);
+ setAlarmIntent.setComponent(mAlarmScheduler);
+ setAlarmIntent.putExtra(TestAlarmScheduler.EXTRA_TYPE, type);
+ setAlarmIntent.putExtra(TestAlarmScheduler.EXTRA_TRIGGER_TIME, triggerMillis);
+ setAlarmIntent.putExtra(TestAlarmScheduler.EXTRA_REPEAT_INTERVAL, interval);
+ mContext.sendBroadcast(setAlarmIntent);
}
private void scheduleAlarmClock(long triggerRTC) {
AlarmManager.AlarmClockInfo alarmInfo = new AlarmManager.AlarmClockInfo(triggerRTC, null);
- final Intent setAlarmClockIntent = new Intent(TestAlarmActivity.ACTION_SET_ALARM_CLOCK);
- setAlarmClockIntent.setComponent(mAlarmActivity);
- setAlarmClockIntent.putExtra(TestAlarmActivity.EXTRA_ALARM_CLOCK_INFO, alarmInfo);
- setAlarmClockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(setAlarmClockIntent);
+ final Intent setAlarmClockIntent = new Intent(TestAlarmScheduler.ACTION_SET_ALARM_CLOCK);
+ setAlarmClockIntent.setComponent(mAlarmScheduler);
+ setAlarmClockIntent.putExtra(TestAlarmScheduler.EXTRA_ALARM_CLOCK_INFO, alarmInfo);
+ mContext.sendBroadcast(setAlarmClockIntent);
}
private static int getMinExpectedExpirations(long now, long start, long interval) {
@@ -118,7 +120,6 @@
final long triggerElapsed = SystemClock.elapsedRealtime() + interval;
scheduleAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed, interval);
Thread.sleep(DEFAULT_WAIT);
- makeTestPackageIdle();
Thread.sleep(2 * interval);
assertFalse("Alarm got triggered even under restrictions", waitForAlarms(1, DEFAULT_WAIT));
Thread.sleep(interval);
@@ -147,10 +148,9 @@
deleteAlarmManagerConstants();
setAppOpsMode(APP_OP_MODE_ALLOWED);
// Cancel any leftover alarms
- final Intent cancelAlarmsIntent = new Intent(TestAlarmActivity.ACTION_CANCEL_ALL_ALARMS);
- cancelAlarmsIntent.setComponent(mAlarmActivity);
- cancelAlarmsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(cancelAlarmsIntent);
+ final Intent cancelAlarmsIntent = new Intent(TestAlarmScheduler.ACTION_CANCEL_ALL_ALARMS);
+ cancelAlarmsIntent.setComponent(mAlarmScheduler);
+ mContext.sendBroadcast(cancelAlarmsIntent);
mContext.unregisterReceiver(mAlarmStateReceiver);
// Broadcast unregister may race with the next register in setUp
Thread.sleep(DEFAULT_WAIT);
@@ -176,12 +176,6 @@
mUiDevice.executeShellCommand(commandBuilder.toString());
}
- private void makeTestPackageIdle() throws IOException {
- StringBuilder commandBuilder = new StringBuilder("am make-uid-idle --user current ")
- .append(TEST_APP_PACKAGE);
- mUiDevice.executeShellCommand(commandBuilder.toString());
- }
-
private boolean waitForAlarms(int expectedAlarms, long timeout) throws InterruptedException {
final long deadLine = SystemClock.uptimeMillis() + timeout;
int alarmCount;
diff --git a/tests/AlarmManager/src/android/alarmmanager/cts/BatterySaverTests.java b/tests/AlarmManager/src/android/alarmmanager/cts/BatterySaverTests.java
new file mode 100644
index 0000000..12e80a2
--- /dev/null
+++ b/tests/AlarmManager/src/android/alarmmanager/cts/BatterySaverTests.java
@@ -0,0 +1,185 @@
+/*
+ * 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.alarmmanager.cts;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.alarmmanager.alarmtestapp.cts.TestAlarmScheduler;
+import android.alarmmanager.alarmtestapp.cts.TestAlarmReceiver;
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+/**
+ * Tests that battery saver imposes the appropriate restrictions on alarms
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BatterySaverTests {
+ private static final String TAG = BatterySaverTests.class.getSimpleName();
+ private static final String TEST_APP_PACKAGE = "android.alarmmanager.alarmtestapp.cts";
+ private static final String TEST_APP_RECEIVER = TEST_APP_PACKAGE + ".TestAlarmScheduler";
+
+ private static final long DEFAULT_WAIT = 1_000;
+ private static final long POLL_INTERVAL = 200;
+
+ // Tweaked alarm manager constants to facilitate testing
+ private static final long MIN_REPEATING_INTERVAL = 5_000;
+ private static final long ALLOW_WHILE_IDLE_SHORT_TIME = 10_000;
+ private static final long MIN_FUTURITY = 2_000;
+
+ private Context mContext;
+ private ComponentName mAlarmScheduler;
+ private UiDevice mUiDevice;
+ private volatile int mAlarmCount;
+
+ private final BroadcastReceiver mAlarmStateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mAlarmCount = intent.getIntExtra(TestAlarmReceiver.EXTRA_ALARM_COUNT, 1);
+ Log.d(TAG, "No. of expirations: " + mAlarmCount
+ + " elapsed: " + SystemClock.elapsedRealtime());
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ mAlarmScheduler = new ComponentName(TEST_APP_PACKAGE, TEST_APP_RECEIVER);
+ mAlarmCount = 0;
+ updateAlarmManagerConstants();
+ setBatteryCharging(false);
+ setBatterySaverMode(true);
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(TestAlarmReceiver.ACTION_REPORT_ALARM_EXPIRED);
+ mContext.registerReceiver(mAlarmStateReceiver, intentFilter);
+ }
+
+ private void scheduleAlarm(int type, boolean whileIdle, long triggerMillis, long interval) {
+ final Intent setAlarmIntent = new Intent(TestAlarmScheduler.ACTION_SET_ALARM);
+ setAlarmIntent.setComponent(mAlarmScheduler);
+ setAlarmIntent.putExtra(TestAlarmScheduler.EXTRA_TYPE, type);
+ setAlarmIntent.putExtra(TestAlarmScheduler.EXTRA_TRIGGER_TIME, triggerMillis);
+ setAlarmIntent.putExtra(TestAlarmScheduler.EXTRA_REPEAT_INTERVAL, interval);
+ setAlarmIntent.putExtra(TestAlarmScheduler.EXTRA_ALLOW_WHILE_IDLE, whileIdle);
+ mContext.sendBroadcast(setAlarmIntent);
+ }
+
+ @Test
+ public void testAllowWhileIdleNotBlocked() throws Exception {
+ final long triggerElapsed1 = SystemClock.elapsedRealtime() + MIN_FUTURITY;
+ scheduleAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, true, triggerElapsed1, 0);
+ Thread.sleep(MIN_FUTURITY);
+ assertTrue("Allow-while-idle alarm blocked in battery saver",
+ waitForAlarms(1));
+ final long triggerElapsed2 = triggerElapsed1 + 2_000;
+ scheduleAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, true, triggerElapsed2, 0);
+ Thread.sleep(2_000);
+ assertFalse("Follow up allow-while-idle alarm went off before short time",
+ waitForAlarms(1));
+ final long expectedTriggerElapsed = triggerElapsed1 + ALLOW_WHILE_IDLE_SHORT_TIME;
+ Thread.sleep(expectedTriggerElapsed - SystemClock.elapsedRealtime());
+ assertTrue("Follow-up allow-while-idle alarm did not go off even after short time",
+ waitForAlarms(1));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ setBatterySaverMode(false);
+ setBatteryCharging(true);
+ deleteAlarmManagerConstants();
+ final Intent cancelAlarmsIntent = new Intent(TestAlarmScheduler.ACTION_CANCEL_ALL_ALARMS);
+ cancelAlarmsIntent.setComponent(mAlarmScheduler);
+ mContext.sendBroadcast(cancelAlarmsIntent);
+ mContext.unregisterReceiver(mAlarmStateReceiver);
+ // Broadcast unregister may race with the next register in setUp
+ Thread.sleep(1000);
+ }
+
+ private void updateAlarmManagerConstants() throws IOException {
+ final String cmd = "settings put global alarm_manager_constants "
+ + "min_interval=" + MIN_REPEATING_INTERVAL + ","
+ + "min_futurity=" + MIN_FUTURITY + ","
+ + "allow_while_idle_short_time=" + ALLOW_WHILE_IDLE_SHORT_TIME;
+ executeAndLog(cmd);
+ }
+
+ private void deleteAlarmManagerConstants() throws IOException {
+ executeAndLog("settings delete global alarm_manager_constants");
+ }
+
+ private void setBatteryCharging(final boolean charging) throws Exception {
+ final BatteryManager bm = mContext.getSystemService(BatteryManager.class);
+ final String cmd = "dumpsys battery " + (charging ? "reset" : "unplug");
+ executeAndLog(cmd);
+ if (!charging) {
+ assertTrue("Battery could not be unplugged", waitUntil(() -> !bm.isCharging(), 5_000));
+ }
+ }
+
+ private void setBatterySaverMode(final boolean enabled) throws Exception {
+ final PowerManager pm = mContext.getSystemService(PowerManager.class);
+ final String cmd = "settings put global low_power " + (enabled ? "1" : "0");
+ executeAndLog(cmd);
+ assertTrue("Battery saver state could not be changed to " + enabled,
+ waitUntil(() -> (enabled == pm.isPowerSaveMode()), 5_000));
+ }
+
+ private void executeAndLog(String cmd) throws IOException {
+ final String output = mUiDevice.executeShellCommand(cmd);
+ Log.d(TAG, "command: [" + cmd + "], output: [" + output.trim() + "]");
+ }
+
+ private boolean waitForAlarms(final int minExpirations) throws InterruptedException {
+ final boolean success = waitUntil(() -> (mAlarmCount >= minExpirations), DEFAULT_WAIT);
+ mAlarmCount = 0;
+ return success;
+ }
+
+ private boolean waitUntil(Condition condition, long timeout) throws InterruptedException {
+ final long deadLine = SystemClock.uptimeMillis() + timeout;
+ while (!condition.isMet() && SystemClock.uptimeMillis() < deadLine) {
+ Thread.sleep(POLL_INTERVAL);
+ }
+ return condition.isMet();
+ }
+
+ @FunctionalInterface
+ interface Condition {
+ boolean isMet();
+ }
+}
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index eb3578b..e9902f8 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 = 34;
+ private static final int NUM_MARSHALLED_PROPERTIES = 35;
/**
* The number of properties that are purposely not marshalled
@@ -205,6 +205,7 @@
info.setBoundsInScreen(new Rect(2,2,2,2));
info.setClassName("foo.bar.baz.Class");
info.setContentDescription("content description");
+ info.setTooltipText("tooltip");
info.setPackageName("foo.bar.baz");
info.setText("text");
info.setHintText("hint");
@@ -255,6 +256,8 @@
receivedInfo.getClassName());
assertEquals("contentDescription has incorrect value", expectedInfo.getContentDescription(),
receivedInfo.getContentDescription());
+ assertEquals("tooltip text has incorrect value", expectedInfo.getTooltipText(),
+ receivedInfo.getTooltipText());
assertEquals("packageName has incorrect value", expectedInfo.getPackageName(),
receivedInfo.getPackageName());
assertEquals("text has incorrect value", expectedInfo.getText(), receivedInfo.getText());
@@ -324,6 +327,7 @@
assertTrue("boundsInScreen not properly recycled", bounds.isEmpty());
assertNull("className not properly recycled", info.getClassName());
assertNull("contentDescription not properly recycled", info.getContentDescription());
+ assertNull("tooltiptext not properly recycled", info.getTooltipText());
assertNull("packageName not properly recycled", info.getPackageName());
assertNull("text not properly recycled", info.getText());
assertNull("Hint text not properly recycled", info.getHintText());
diff --git a/tests/accessibilityservice/res/layout/end_to_end_test.xml b/tests/accessibilityservice/res/layout/end_to_end_test.xml
index cba3a15..fdc3998 100644
--- a/tests/accessibilityservice/res/layout/end_to_end_test.xml
+++ b/tests/accessibilityservice/res/layout/end_to_end_test.xml
@@ -39,6 +39,7 @@
<Button android:id="@+id/button"
android:text="@string/button_title"
android:accessibilityPaneTitle="@string/paneTitle"
+ android:tooltipText="@string/button_tooltip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:bufferType="normal">
diff --git a/tests/accessibilityservice/res/values/strings.xml b/tests/accessibilityservice/res/values/strings.xml
index 0e8d78a..454102a 100644
--- a/tests/accessibilityservice/res/values/strings.xml
+++ b/tests/accessibilityservice/res/values/strings.xml
@@ -34,6 +34,8 @@
<!-- String title of the button -->
<string name="button_title">Click me</string>
+ <string name="button_tooltip">Never press this button</string>
+
<!-- String value of the first list item -->
<string name="first_list_item">First list item</string>
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index 7052fc3..8202399 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -18,6 +18,12 @@
import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventType;
import static android.accessibilityservice.cts.utils.RunOnMainUtils.getOnMain;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_HIDE_TOOLTIP;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_TOOLTIP;
+
+import static org.hamcrest.Matchers.in;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertThat;
import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
import android.app.Activity;
@@ -41,6 +47,7 @@
import android.test.suitebuilder.annotation.MediumTest;
import android.text.TextUtils;
import android.util.Log;
+import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -51,6 +58,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* This class performs end-to-end testing of the accessibility feature by
@@ -581,6 +589,42 @@
editTextNode.isHeading());
}
+ @MediumTest
+ public void testTooltipTextReportedToAccessibility() {
+ final Instrumentation instrumentation = getInstrumentation();
+ final UiAutomation uiAutomation = instrumentation.getUiAutomation();
+ final AccessibilityNodeInfo buttonNode = uiAutomation.getRootInActiveWindow()
+ .findAccessibilityNodeInfosByViewId(
+ "android.accessibilityservice.cts:id/button")
+ .get(0);
+ assertEquals("Tooltip text not reported to accessibility",
+ instrumentation.getContext().getString(R.string.button_tooltip),
+ buttonNode.getTooltipText());
+ }
+
+ @MediumTest
+ public void testTooltipTextActionsReportedToAccessibility() throws Exception {
+ final Instrumentation instrumentation = getInstrumentation();
+ final UiAutomation uiAutomation = instrumentation.getUiAutomation();
+ final AccessibilityNodeInfo buttonNode = uiAutomation.getRootInActiveWindow()
+ .findAccessibilityNodeInfosByViewId(
+ "android.accessibilityservice.cts:id/button")
+ .get(0);
+ assertFalse(hasTooltip(R.id.button));
+ assertThat(ACTION_SHOW_TOOLTIP, in(buttonNode.getActionList()));
+ assertThat(ACTION_HIDE_TOOLTIP, not(in(buttonNode.getActionList())));
+ uiAutomation.executeAndWaitForEvent(() -> buttonNode.performAction(
+ ACTION_SHOW_TOOLTIP.getId()),
+ filterForEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED),
+ TIMEOUT_ASYNC_PROCESSING);
+
+ // The button should now be showing the tooltip, so it should have the option to hide it.
+ buttonNode.refresh();
+ assertThat(ACTION_HIDE_TOOLTIP, in(buttonNode.getActionList()));
+ assertThat(ACTION_SHOW_TOOLTIP, not(in(buttonNode.getActionList())));
+ assertTrue(hasTooltip(R.id.button));
+ }
+
private static void assertPackageName(AccessibilityNodeInfo node, String packageName) {
if (node == null) {
return;
@@ -690,4 +734,15 @@
}
return true;
}
+
+ private boolean hasTooltip(int id) {
+ return getOnMain(getInstrumentation(), () -> {
+ final View viewWithTooltip = getActivity().findViewById(id);
+ if (viewWithTooltip == null) {
+ return false;
+ }
+ final View tooltipView = viewWithTooltip.getTooltipView();
+ return (tooltipView != null) && (tooltipView.getParent() != null);
+ });
+ }
}
diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 3b3c4d5..9751d05 100644
--- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -29,10 +29,17 @@
import android.os.Process;
import android.os.UserManager;
import android.provider.Settings;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;
+import java.io.ByteArrayInputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -940,4 +947,44 @@
assertProfileOwnerMessage(e.getMessage());
}
}
+
+ public void testGenerateKeyPair_failIfNotProfileOwner() {
+ if (!mDeviceAdmin) {
+ Log.w(TAG, "Skipping testGenerateKeyPair_failIfNotProfileOwner");
+ return;
+ }
+ try {
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+ "gen-should-fail",
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .setKeySize(2048)
+ .setDigests(KeyProperties.DIGEST_SHA256)
+ .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+ .build();
+
+ mDevicePolicyManager.generateKeyPair(mComponent, "RSA", spec, 0);
+ fail("did not throw expected SecurityException");
+ } catch (SecurityException e) {
+ assertProfileOwnerMessage(e.getMessage());
+ }
+ }
+
+ public void testSetKeyPairCertificate_failIfNotProfileOwner() throws CertificateException {
+ if (!mDeviceAdmin) {
+ Log.w(TAG, "Skipping testSetKeyPairCertificate_failIfNotProfileOwner");
+ return;
+ }
+ try {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ Certificate cert = cf.generateCertificate(
+ new ByteArrayInputStream(TEST_CA_STRING1.getBytes()));
+ List<Certificate> certs = new ArrayList();
+ certs.add(cert);
+ mDevicePolicyManager.setKeyPairCertificate(mComponent, "set-should-fail", certs, true);
+ fail("did not throw expected SecurityException");
+ } catch (SecurityException e) {
+ assertProfileOwnerMessage(e.getMessage());
+ }
+ }
}
diff --git a/tests/app/src/android/app/backup/cts/BackupManagerTest.java b/tests/app/src/android/app/backup/cts/BackupManagerTest.java
deleted file mode 100644
index 510e8d1..0000000
--- a/tests/app/src/android/app/backup/cts/BackupManagerTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2011 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.backup.cts;
-
-import android.app.backup.BackupManager;
-import android.app.backup.RestoreObserver;
-import android.test.AndroidTestCase;
-
-public class BackupManagerTest extends AndroidTestCase {
-
- public void testBackupManager() throws Exception {
- // Check that these don't crash as if they were called in an app...
- BackupManager backupManager = new BackupManager(mContext);
- backupManager.dataChanged();
- BackupManager.dataChanged("android.app.stubs");
-
- // Backup isn't expected to work in this test but check for obvious bugs...
- int result = backupManager.requestRestore(new RestoreObserver() {});
- assertTrue(result != 0);
- }
-}
diff --git a/tests/app/src/android/app/cts/SystemFeaturesTest.java b/tests/app/src/android/app/cts/SystemFeaturesTest.java
index 53b1bf2..fb31878 100644
--- a/tests/app/src/android/app/cts/SystemFeaturesTest.java
+++ b/tests/app/src/android/app/cts/SystemFeaturesTest.java
@@ -125,6 +125,7 @@
assertNotAvailable(PackageManager.FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR);
assertNotAvailable(PackageManager.FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING);
assertNotAvailable(PackageManager.FEATURE_CAMERA_CAPABILITY_RAW);
+ assertNotAvailable(PackageManager.FEATURE_CAMERA_AR);
assertFalse("Devices supporting external cameras must have a representative camera " +
"connected for testing",
@@ -142,6 +143,7 @@
boolean fullCamera = false;
boolean manualSensor = false;
boolean manualPostProcessing = false;
+ boolean motionTracking = false;
boolean raw = false;
CameraCharacteristics[] cameraChars = new CameraCharacteristics[cameraIds.length];
for (String cameraId : cameraIds) {
@@ -163,6 +165,9 @@
case CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW:
raw = true;
break;
+ case CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING:
+ motionTracking = true;
+ break;
default:
// Capabilities don't have a matching system feature
break;
@@ -174,6 +179,7 @@
assertFeature(manualPostProcessing,
PackageManager.FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING);
assertFeature(raw, PackageManager.FEATURE_CAMERA_CAPABILITY_RAW);
+ assertFeature(motionTracking, PackageManager.FEATURE_CAMERA_AR);
}
private void checkFrontCamera() {
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
index b77dae0..deb9fca 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -22,6 +22,7 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraMetadata;
@@ -37,10 +38,13 @@
import android.hardware.camera2.params.RggbChannelVector;
import android.hardware.camera2.params.TonemapCurve;
import android.media.Image;
+import android.os.Parcel;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Range;
import android.util.Rational;
import android.util.Size;
+import android.view.Surface;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -129,6 +133,84 @@
}
/**
+ * Test CaptureRequest settings parcelling.
+ */
+ public void testSettingsBinderParcel() throws Exception {
+ SurfaceTexture outputTexture = new SurfaceTexture(/* random texture ID */ 5);
+ Surface surface = new Surface(outputTexture);
+
+ for (int i = 0; i < mCameraIds.length; i++) {
+ try {
+ openDevice(mCameraIds[i]);
+ CaptureRequest.Builder requestBuilder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ requestBuilder.addTarget(surface);
+
+ // Check regular/default case
+ CaptureRequest captureRequestOriginal = requestBuilder.build();
+ Parcel p;
+ p = Parcel.obtain();
+ captureRequestOriginal.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ CaptureRequest captureRequestParcelled = CaptureRequest.CREATOR.createFromParcel(p);
+ assertEquals("Parcelled camera settings should match",
+ captureRequestParcelled.get(CaptureRequest.CONTROL_CAPTURE_INTENT),
+ new Integer(CameraMetadata.CONTROL_CAPTURE_INTENT_PREVIEW));
+ p.recycle();
+
+ // Check capture request with additional physical camera settings
+ String physicalId = new String(Integer.toString(i + 1));
+ ArraySet<String> physicalIds = new ArraySet<String> ();
+ physicalIds.add(physicalId);
+
+ requestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW,
+ physicalIds);
+ requestBuilder.addTarget(surface);
+ captureRequestOriginal = requestBuilder.build();
+ captureRequestOriginal.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ captureRequestParcelled = CaptureRequest.CREATOR.createFromParcel(p);
+ assertEquals("Parcelled camera settings should match",
+ captureRequestParcelled.get(CaptureRequest.CONTROL_CAPTURE_INTENT),
+ new Integer(CameraMetadata.CONTROL_CAPTURE_INTENT_PREVIEW));
+ p.recycle();
+
+ // Check various invalid cases
+ p.writeInt(-1);
+ p.setDataPosition(0);
+ try {
+ captureRequestParcelled = CaptureRequest.CREATOR.createFromParcel(p);
+ fail("should get RuntimeException due to invalid number of settings");
+ } catch (RuntimeException e) {
+ // Expected
+ }
+ p.recycle();
+
+ p.writeInt(0);
+ p.setDataPosition(0);
+ try {
+ captureRequestParcelled = CaptureRequest.CREATOR.createFromParcel(p);
+ fail("should get RuntimeException due to invalid number of settings");
+ } catch (RuntimeException e) {
+ // Expected
+ }
+ p.recycle();
+
+ p.writeInt(1);
+ p.setDataPosition(0);
+ try {
+ captureRequestParcelled = CaptureRequest.CREATOR.createFromParcel(p);
+ fail("should get RuntimeException due to absent settings");
+ } catch (RuntimeException e) {
+ // Expected
+ }
+ } finally {
+ closeDevice();
+ }
+ }
+ }
+
+ /**
* Test black level lock when exposure value change.
* <p>
* When {@link CaptureRequest#BLACK_LEVEL_LOCK} is true in a request, the
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 41366fe..aa0c064 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -908,7 +908,6 @@
verifyLensCalibration(poseRotation, poseTranslation, poseReference,
cameraIntrinsics, radialDistortion, precorrectionArray);
-
} else {
boolean hasFields =
hasDepth16 && (poseTranslation != null) &&
@@ -1463,6 +1462,63 @@
counter++;
}
}
+
+ /**
+ * Check Logical camera capability
+ */
+ public void testLogicalCameraCharacteristics() throws Exception {
+ int counter = 0;
+ List<String> cameraIdList = Arrays.asList(mIds);
+
+ for (CameraCharacteristics c : mCharacteristics) {
+ int[] capabilities = CameraTestUtils.getValueNotNull(
+ c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ boolean supportLogicalCamera = arrayContains(capabilities,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA);
+ if (supportLogicalCamera) {
+ List<String> physicalCameraIds = c.getPhysicalCameraIds();
+ assertNotNull("android.logicalCam.physicalCameraIds shouldn't be null",
+ physicalCameraIds);
+ assertTrue("Logical camera must contain at least 2 physical camera ids",
+ physicalCameraIds.size() >= 2);
+
+ mCollector.expectKeyValueInRange(c,
+ CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE,
+ CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE,
+ CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED);
+
+ for (String physicalCameraId : physicalCameraIds) {
+ assertNotNull("Physical camera id shouldn't be null", physicalCameraId);
+ assertTrue(
+ String.format("Physical camera id %s shouldn't be the same as logical"
+ + " camera id %s", physicalCameraId, mIds[counter]),
+ physicalCameraId != mIds[counter]);
+ assertTrue(
+ String.format("Physical camera id %s should be in available camera ids",
+ physicalCameraId),
+ cameraIdList.contains(physicalCameraId));
+
+ //validation for depth static metadata of physical cameras
+ CameraCharacteristics pc =
+ mCameraManager.getCameraCharacteristics(physicalCameraId);
+
+ float[] poseRotation = pc.get(CameraCharacteristics.LENS_POSE_ROTATION);
+ float[] poseTranslation = pc.get(CameraCharacteristics.LENS_POSE_TRANSLATION);
+ Integer poseReference = pc.get(CameraCharacteristics.LENS_POSE_REFERENCE);
+ float[] cameraIntrinsics = pc.get(
+ CameraCharacteristics.LENS_INTRINSIC_CALIBRATION);
+ float[] radialDistortion = pc.get(CameraCharacteristics.LENS_RADIAL_DISTORTION);
+ Rect precorrectionArray = pc.get(
+ CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
+
+ verifyLensCalibration(poseRotation, poseTranslation, poseReference,
+ cameraIntrinsics, radialDistortion, precorrectionArray);
+ }
+ }
+ counter++;
+ }
+ }
+
/**
* Create an invalid size that's close to one of the good sizes in the list, but not one of them
*/
diff --git a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
new file mode 100644
index 0000000..b7bd5aa
--- /dev/null
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -0,0 +1,499 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.cts;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.media.CamcorderProfile;
+import android.media.ImageReader;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Range;
+import android.util.Size;
+import android.view.Display;
+import android.view.Surface;
+import android.view.WindowManager;
+
+
+import com.android.compatibility.common.util.Stat;
+import com.android.ex.camera2.blocking.BlockingSessionCallback;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import static org.mockito.Mockito.*;
+
+/**
+ * Tests exercising logical camera setup, configuration, and usage.
+ */
+public final class LogicalCameraDeviceTest extends Camera2SurfaceViewTestCase {
+ private static final String TAG = "LogicalCameraTest";
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+ private static final int CONFIGURE_TIMEOUT = 5000; //ms
+
+ private static final double NS_PER_MS = 1000000.0;
+ private static final int MAX_IMAGE_COUNT = 3;
+ private static final int NUM_FRAMES_CHECKED = 30;
+
+ private static final double FRAME_DURATION_THRESHOLD = 0.1;
+
+ /**
+ * Test that passing in invalid physical camera ids in OutputConfiguragtion behaves as expected
+ * for logical multi-camera and non-logical multi-camera.
+ */
+ public void testInvalidPhysicalCameraIdInOutputConfiguration() throws Exception {
+ for (String id : mCameraIds) {
+ try {
+ Log.i(TAG, "Testing Camera " + id);
+ openDevice(id);
+
+ Size yuvSize = mOrderedPreviewSizes.get(0);
+ // Create a YUV image reader.
+ ImageReader imageReader = ImageReader.newInstance(yuvSize.getWidth(),
+ yuvSize.getHeight(), ImageFormat.YUV_420_888, /*maxImages*/1);
+
+ CameraCaptureSession.StateCallback sessionListener =
+ mock(CameraCaptureSession.StateCallback.class);
+ List<OutputConfiguration> outputs = new ArrayList<>();
+ OutputConfiguration outputConfig = new OutputConfiguration(
+ imageReader.getSurface());
+ outputConfig.setPhysicalCameraId(id);
+
+ // Regardless of logical camera or non-logical camera, create a session of an
+ // output configuration with invalid physical camera id, verify that the
+ // createCaptureSession fails.
+ outputs.add(outputConfig);
+ CameraCaptureSession session =
+ CameraTestUtils.configureCameraSessionWithConfig(mCamera, outputs,
+ sessionListener, mHandler);
+ verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()).
+ onConfigureFailed(any(CameraCaptureSession.class));
+ verify(sessionListener, never()).onConfigured(any(CameraCaptureSession.class));
+ verify(sessionListener, never()).onReady(any(CameraCaptureSession.class));
+ verify(sessionListener, never()).onActive(any(CameraCaptureSession.class));
+ verify(sessionListener, never()).onClosed(any(CameraCaptureSession.class));
+ } finally {
+ closeDevice();
+ }
+ }
+ }
+
+ /**
+ * Test for making sure that streaming from physical streams work as expected, and
+ * FPS isn't slowed down.
+ */
+ public void testBasicPhysicalStreaming() throws Exception {
+
+ for (String id : mCameraIds) {
+ try {
+ Log.i(TAG, "Testing Camera " + id);
+ openDevice(id);
+
+ if (!mStaticInfo.isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
+ continue;
+ }
+
+ if (!mStaticInfo.isLogicalMultiCamera()) {
+ Log.i(TAG, "Camera " + id + " is not a logical multi-camera, skipping");
+ continue;
+ }
+
+ assertTrue("Logical multi-camera must be LIMITED or higher",
+ mStaticInfo.isHardwareLevelAtLeastLimited());
+
+ // Figure out yuv size to use.
+ Size yuvSize = findMaxPhysicalYuvSize(id);
+ if (yuvSize == null) {
+ Log.i(TAG, "Camera " + id + ": No matching physical YUV streams, skipping");
+ continue;
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, "Camera " + id + ": Testing YUV size of " + yuvSize.getWidth() +
+ " x " + yuvSize.getHeight());
+ }
+ List<String> physicalCameraIds =
+ mStaticInfo.getCharacteristics().getPhysicalCameraIds();
+ assertTrue("Logical camera must contain at least 2 physical camera ids",
+ physicalCameraIds.size() >= 2);
+
+ List<String> noPhysicalIds = new ArrayList<>();
+ double avgLogicalDurationsMs = measureYuvFrameDuration(id, noPhysicalIds, yuvSize);
+
+ List<String> onePhysicalIds = new ArrayList<>();
+ onePhysicalIds.add(physicalCameraIds.get(0));
+ double avg1PhysicalDurationsMs = measureYuvFrameDuration(id,
+ onePhysicalIds, yuvSize);
+ mCollector.expectLessOrEqual("The average frame duration increase of a physical "
+ + "stream is larger than threshold: "
+ + String.format("increase = %.2f, threshold = %.2f",
+ (avg1PhysicalDurationsMs - avgLogicalDurationsMs)/avgLogicalDurationsMs,
+ FRAME_DURATION_THRESHOLD),
+ avgLogicalDurationsMs*(1+FRAME_DURATION_THRESHOLD),
+ avg1PhysicalDurationsMs);
+
+ double avgAllPhysicalDurationsMs = measureYuvFrameDuration(
+ id, physicalCameraIds, yuvSize);
+
+ mCollector.expectLessOrEqual("The average frame duration increase of all physical "
+ + "streams is larger than threshold: "
+ + String.format("increase = %.2f, threshold = %.2f",
+ (avgAllPhysicalDurationsMs - avgLogicalDurationsMs)/avgLogicalDurationsMs,
+ FRAME_DURATION_THRESHOLD),
+ avgLogicalDurationsMs*(1+FRAME_DURATION_THRESHOLD),
+ avgAllPhysicalDurationsMs);
+
+ } finally {
+ closeDevice();
+ }
+ }
+ }
+
+ /**
+ * Test for making sure that multiple requests for physical cameras work as expected.
+ */
+ public void testBasicPhysicalRequests() throws Exception {
+
+ for (String id : mCameraIds) {
+ try {
+ Log.i(TAG, "Testing Camera " + id);
+ openDevice(id);
+
+ if (!mStaticInfo.isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
+ continue;
+ }
+
+ if (!mStaticInfo.isLogicalMultiCamera()) {
+ Log.i(TAG, "Camera " + id + " is not a logical multi-camera, skipping");
+ continue;
+ }
+
+ assertTrue("Logical multi-camera must be LIMITED or higher",
+ mStaticInfo.isHardwareLevelAtLeastLimited());
+
+ // Figure out yuv size to use.
+ Size yuvSize= findMaxPhysicalYuvSize(id);
+ if (yuvSize == null) {
+ Log.i(TAG, "Camera " + id + ": No matching physical YUV streams, skipping");
+ continue;
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, "Camera " + id + ": Testing YUV size of " + yuvSize.getWidth() +
+ " x " + yuvSize.getHeight());
+ }
+ List<CaptureRequest.Key<?>> physicalRequestKeys =
+ mStaticInfo.getCharacteristics().getAvailablePhysicalCameraRequestKeys();
+ if (physicalRequestKeys == null) {
+ Log.i(TAG, "Camera " + id + ": no available physical request keys, skipping");
+ continue;
+ }
+
+ List<String> physicalCameraIds =
+ mStaticInfo.getCharacteristics().getPhysicalCameraIds();
+ assertTrue("Logical camera must contain at least 2 physical camera ids",
+ physicalCameraIds.size() >= 2);
+ ArraySet<String> physicalIdSet = new ArraySet<String>(physicalCameraIds.size());
+ physicalIdSet.addAll(physicalCameraIds);
+
+ List<OutputConfiguration> outputConfigs = new ArrayList<>();
+ List<ImageReader> imageReaders = new ArrayList<>();
+ SimpleImageReaderListener readerListener = new SimpleImageReaderListener();
+ ImageReader yuvTarget = CameraTestUtils.makeImageReader(yuvSize,
+ ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
+ readerListener, mHandler);
+ imageReaders.add(yuvTarget);
+ OutputConfiguration config = new OutputConfiguration(yuvTarget.getSurface());
+ outputConfigs.add(new OutputConfiguration(yuvTarget.getSurface()));
+
+ CaptureRequest.Builder requestBuilder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW, physicalIdSet);
+ requestBuilder.addTarget(config.getSurface());
+
+ mSessionListener = new BlockingSessionCallback();
+ mSession = configureCameraSessionWithConfig(mCamera, outputConfigs,
+ mSessionListener, mHandler);
+
+ for (int i = 0; i < MAX_IMAGE_COUNT; i++) {
+ mSession.capture(requestBuilder.build(), new SimpleCaptureCallback(), mHandler);
+ readerListener.getImage(WAIT_FOR_RESULT_TIMEOUT_MS);
+ }
+
+ if (mSession != null) {
+ mSession.close();
+ }
+
+ } finally {
+ closeDevice();
+ }
+ }
+ }
+
+ /**
+ * Tests invalid/incorrect multiple physical capture request cases.
+ */
+ public void testInvalidPhysicalCameraRequests() throws Exception {
+
+ for (String id : mCameraIds) {
+ try {
+ Log.i(TAG, "Testing Camera " + id);
+ openDevice(id);
+
+ if (!mStaticInfo.isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
+ continue;
+ }
+
+ assertTrue("Logical multi-camera must be LIMITED or higher",
+ mStaticInfo.isHardwareLevelAtLeastLimited());
+
+ // Figure out yuv size to use.
+ Size yuvSize= findMaxPhysicalYuvSize(id);
+ if (yuvSize == null) {
+ Log.i(TAG, "Camera " + id + ": No matching physical YUV streams, skipping");
+ continue;
+ }
+
+ List<OutputConfiguration> outputConfigs = new ArrayList<>();
+ List<ImageReader> imageReaders = new ArrayList<>();
+ SimpleImageReaderListener readerListener = new SimpleImageReaderListener();
+ ImageReader yuvTarget = CameraTestUtils.makeImageReader(yuvSize,
+ ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
+ readerListener, mHandler);
+ imageReaders.add(yuvTarget);
+ OutputConfiguration config = new OutputConfiguration(yuvTarget.getSurface());
+ outputConfigs.add(new OutputConfiguration(yuvTarget.getSurface()));
+
+ ArraySet<String> physicalIdSet = new ArraySet<String>();
+ // Invalid physical id
+ physicalIdSet.add("-2");
+
+ CaptureRequest.Builder requestBuilder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW, physicalIdSet);
+ requestBuilder.addTarget(config.getSurface());
+
+ // Check for invalid setting get/set
+ try {
+ requestBuilder.getPhysicalCameraKey(CaptureRequest.CONTROL_CAPTURE_INTENT, "-1");
+ fail("No exception for invalid physical camera id");
+ } catch (IllegalArgumentException e) {
+ //expected
+ }
+
+ try {
+ requestBuilder.setPhysicalCameraKey(CaptureRequest.CONTROL_CAPTURE_INTENT,
+ new Integer(0), "-1");
+ fail("No exception for invalid physical camera id");
+ } catch (IllegalArgumentException e) {
+ //expected
+ }
+
+ mSessionListener = new BlockingSessionCallback();
+ mSession = configureCameraSessionWithConfig(mCamera, outputConfigs,
+ mSessionListener, mHandler);
+
+ try {
+ mSession.capture(requestBuilder.build(), new SimpleCaptureCallback(),
+ mHandler);
+ fail("No exception for invalid physical camera id");
+ } catch (IllegalArgumentException e) {
+ //expected
+ }
+
+ if (mStaticInfo.isLogicalMultiCamera()) {
+ List<String> physicalCameraIds =
+ mStaticInfo.getCharacteristics().getPhysicalCameraIds();
+ assertTrue("Logical camera must contain at least 2 physical camera ids",
+ physicalCameraIds.size() >= 2);
+
+ physicalIdSet.clear();
+ physicalIdSet.addAll(physicalCameraIds);
+ requestBuilder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW, physicalIdSet);
+ requestBuilder.addTarget(config.getSurface());
+ CaptureRequest request = requestBuilder.build();
+
+ // Streaming requests with individual physical camera settings are not
+ // supported.
+ try {
+ mSession.setRepeatingRequest(request, new SimpleCaptureCallback(),
+ mHandler);
+ fail("Streaming requests that include physical camera settings are " +
+ "supported");
+ } catch (IllegalArgumentException e) {
+ //expected
+ }
+
+ try {
+ ArrayList<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
+ requestList.add(request);
+ mSession.setRepeatingBurst(requestList, new SimpleCaptureCallback(),
+ mHandler);
+ fail("Streaming requests that include physical camera settings are " +
+ "supported");
+ } catch (IllegalArgumentException e) {
+ //expected
+ }
+ }
+
+ if (mSession != null) {
+ mSession.close();
+ }
+ } finally {
+ closeDevice();
+ }
+ }
+ }
+
+ /**
+ * Find the maximum YUV stream size that's supported by physical cameras.
+ */
+ private Size findMaxPhysicalYuvSize(String cameraId) throws Exception {
+ List<String> physicalCameras =
+ mStaticInfo.getCharacteristics().getPhysicalCameraIds();
+ List<Size> yuvSizes = CameraTestUtils.getSortedSizesForFormat(
+ cameraId, mCameraManager, ImageFormat.YUV_420_888, /*bound*/null);
+ Size bestYuvSize = null;
+ for (Size yuvSize : yuvSizes) {
+ boolean physicalSizeSupported = true;
+ for (String physicalCameraId : physicalCameras) {
+ List<Size> yuvSizesForPhysicalCamera =
+ CameraTestUtils.getSortedSizesForFormat(physicalCameraId,
+ mCameraManager, ImageFormat.YUV_420_888, /*bound*/null);
+ if (!yuvSizesForPhysicalCamera.contains(yuvSize)) {
+ physicalSizeSupported = false;
+ break;
+ }
+ }
+ if (physicalSizeSupported) {
+ bestYuvSize = yuvSize;
+ break;
+ }
+ }
+ return bestYuvSize;
+ }
+
+ /**
+ * Measure the average frame duration of logical YUV stream.
+ */
+ private double measureYuvFrameDuration(String logicalCameraId,
+ List<String> physicalCameraIds, Size yuvSize) throws Exception {
+ List<OutputConfiguration> outputConfigs = new ArrayList<>();
+ List<ImageReader> imageReaders = new ArrayList<>();
+ if (physicalCameraIds.size() == 0) {
+ ImageReader yuvTarget = CameraTestUtils.makeImageReader(yuvSize,
+ ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
+ new ImageDropperListener(), mHandler);
+ imageReaders.add(yuvTarget);
+ OutputConfiguration config = new OutputConfiguration(yuvTarget.getSurface());
+ outputConfigs.add(new OutputConfiguration(yuvTarget.getSurface()));
+ } else {
+ for (String physicalCameraId : physicalCameraIds) {
+ ImageReader yuvTarget = CameraTestUtils.makeImageReader(yuvSize,
+ ImageFormat.YUV_420_888, MAX_IMAGE_COUNT,
+ new ImageDropperListener(), mHandler);
+ OutputConfiguration config = new OutputConfiguration(yuvTarget.getSurface());
+ config.setPhysicalCameraId(physicalCameraId);
+ outputConfigs.add(config);
+ imageReaders.add(yuvTarget);
+ }
+ }
+
+ // Stream YUV size and note down the FPS
+ CaptureRequest.Builder requestBuilder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ for (OutputConfiguration c : outputConfigs) {
+ requestBuilder.addTarget(c.getSurface());
+ }
+
+ mSessionListener = new BlockingSessionCallback();
+ mSession = configureCameraSessionWithConfig(mCamera, outputConfigs,
+ mSessionListener, mHandler);
+
+ SimpleCaptureCallback simpleResultListener =
+ new SimpleCaptureCallback();
+ StreamConfigurationMap config = mStaticInfo.getCharacteristics().get(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ final long minFrameDuration = config.getOutputMinFrameDuration(
+ ImageFormat.YUV_420_888, yuvSize);
+ if (minFrameDuration > 0) {
+ Range<Integer> targetRange = getSuitableFpsRangeForDuration(logicalCameraId,
+ minFrameDuration);
+ requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, targetRange);
+ }
+ mSession.setRepeatingRequest(requestBuilder.build(),
+ simpleResultListener, mHandler);
+
+ // Converge AE
+ waitForAeStable(simpleResultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+ if (mStaticInfo.isAeLockSupported()) {
+ // Lock AE if supported.
+ requestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
+ mSession.setRepeatingRequest(requestBuilder.build(), simpleResultListener,
+ mHandler);
+ waitForResultValue(simpleResultListener, CaptureResult.CONTROL_AE_STATE,
+ CaptureResult.CONTROL_AE_STATE_LOCKED, NUM_RESULTS_WAIT_TIMEOUT);
+ }
+
+ long prevTimestamp = -1;
+ double[] frameDurationMs = new double[NUM_FRAMES_CHECKED-1];
+ for (int i = 0; i < NUM_FRAMES_CHECKED; i++) {
+ CaptureResult captureResult =
+ simpleResultListener.getCaptureResult(
+ CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
+ long timestamp = captureResult.get(
+ CaptureResult.SENSOR_TIMESTAMP);
+ if (prevTimestamp != -1) {
+ frameDurationMs[i-1] = (double)(timestamp - prevTimestamp)/NS_PER_MS;
+ }
+ prevTimestamp = timestamp;
+ }
+ double avgDurationMs = Stat.getAverage(frameDurationMs);
+ if (VERBOSE) {
+ Log.v(TAG, "average Duration is " + avgDurationMs + " ms");
+ }
+
+ // Stop preview
+ if (mSession != null) {
+ mSession.close();
+ }
+
+ return avgDurationMs;
+ }
+}
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index b449118..3045da0 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -30,10 +30,11 @@
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.CaptureFailure;
-import android.hardware.camera2.params.InputConfiguration;
-import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+import android.hardware.camera2.params.InputConfiguration;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.CamcorderProfile;
import android.media.Image;
import android.media.ImageReader;
@@ -1680,36 +1681,69 @@
private void testOutputCombination(String cameraId, int[] config, MaxStreamSizes maxSizes)
throws Exception {
- Log.i(TAG, String.format("Testing Camera %s, config %s",
+ //TODO: Remove below test workaround once HAL is fixed.
+ if (mStaticInfo.isLogicalMultiCamera()) {
+ int yuvStreams = 0;
+ int rawStreams = 0;
+ for (int i = 0; i < config.length; i+= 2) {
+ int format = config[i];
+ if (format == JPEG) {
+ return;
+ } else if (format == YUV || format == PRIV) {
+ yuvStreams++;
+ } else if (format == RAW) {
+ rawStreams++;
+ }
+ }
+ if ((yuvStreams > 2) || (yuvStreams == 2 && rawStreams == 1)) {
+ return;
+ }
+ }
+
+ Log.i(TAG, String.format("Testing single Camera %s, config %s",
cameraId, MaxStreamSizes.configToString(config)));
+ testSingleCameraOutputCombination(cameraId, config, maxSizes);
+
+ if (mStaticInfo.isLogicalMultiCamera()) {
+ Log.i(TAG, String.format("Testing logical Camera %s, config %s",
+ cameraId, MaxStreamSizes.configToString(config)));
+
+ testMultiCameraOutputCombination(cameraId, config, maxSizes);
+ }
+ }
+
+ private void testSingleCameraOutputCombination(String cameraId, int[] config,
+ MaxStreamSizes maxSizes) throws Exception {
+
// Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
final int MIN_RESULT_COUNT = 3;
// Set up outputs
- List<Surface> outputSurfaces = new ArrayList<Surface>();
+ List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
List<SurfaceTexture> privTargets = new ArrayList<SurfaceTexture>();
List<ImageReader> jpegTargets = new ArrayList<ImageReader>();
List<ImageReader> yuvTargets = new ArrayList<ImageReader>();
List<ImageReader> rawTargets = new ArrayList<ImageReader>();
setupConfigurationTargets(config, maxSizes, privTargets, jpegTargets, yuvTargets,
- rawTargets, outputSurfaces, MIN_RESULT_COUNT);
+ rawTargets, outputConfigs, MIN_RESULT_COUNT, -1 /*overrideStreamIndex*/,
+ null /*overridePhysicalCameraIds*/);
boolean haveSession = false;
try {
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
- for (Surface s : outputSurfaces) {
- requestBuilder.addTarget(s);
+ for (OutputConfiguration c : outputConfigs) {
+ requestBuilder.addTarget(c.getSurface());
}
CameraCaptureSession.CaptureCallback mockCaptureCallback =
mock(CameraCaptureSession.CaptureCallback.class);
- createSession(outputSurfaces);
+ createSessionByConfigs(outputConfigs);
haveSession = true;
CaptureRequest request = requestBuilder.build();
mCameraSession.setRepeatingRequest(request, mockCaptureCallback, mHandler);
@@ -1756,55 +1790,209 @@
}
}
- private void setupConfigurationTargets(int[] outputConfigs, MaxStreamSizes maxSizes,
+ private void testMultiCameraOutputCombination(String cameraId, int[] config,
+ MaxStreamSizes maxSizes) throws Exception {
+
+ // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
+ final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
+ final int MIN_RESULT_COUNT = 3;
+ List<String> physicalCameraIds = mStaticInfo.getCharacteristics().getPhysicalCameraIds();
+
+ for (int i = 0; i < config.length; i += 2) {
+ int format = config[i];
+ if (format != YUV && format != RAW) {
+ continue;
+ }
+
+ // Set up outputs
+ List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
+ List<SurfaceTexture> privTargets = new ArrayList<SurfaceTexture>();
+ List<ImageReader> jpegTargets = new ArrayList<ImageReader>();
+ List<ImageReader> yuvTargets = new ArrayList<ImageReader>();
+ List<ImageReader> rawTargets = new ArrayList<ImageReader>();
+
+ setupConfigurationTargets(config, maxSizes, privTargets, jpegTargets, yuvTargets,
+ rawTargets, outputConfigs, MIN_RESULT_COUNT, i, physicalCameraIds);
+
+ boolean haveSession = false;
+ try {
+ CaptureRequest.Builder requestBuilder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+ for (OutputConfiguration c : outputConfigs) {
+ requestBuilder.addTarget(c.getSurface());
+ }
+
+ CameraCaptureSession.CaptureCallback mockCaptureCallback =
+ mock(CameraCaptureSession.CaptureCallback.class);
+
+ createSessionByConfigs(outputConfigs);
+ haveSession = true;
+ CaptureRequest request = requestBuilder.build();
+ mCameraSession.setRepeatingRequest(request, mockCaptureCallback, mHandler);
+
+ verify(mockCaptureCallback,
+ timeout(TIMEOUT_FOR_RESULT_MS * MIN_RESULT_COUNT).atLeast(MIN_RESULT_COUNT))
+ .onCaptureCompleted(
+ eq(mCameraSession),
+ eq(request),
+ isA(TotalCaptureResult.class));
+ verify(mockCaptureCallback, never()).
+ onCaptureFailed(
+ eq(mCameraSession),
+ eq(request),
+ isA(CaptureFailure.class));
+
+ } catch (Throwable e) {
+ mCollector.addMessage(String.format("Output combination %s failed due to: %s",
+ MaxStreamSizes.configToString(config), e.getMessage()));
+ }
+ if (haveSession) {
+ try {
+ Log.i(TAG, String.format("Done with camera %s, config %s, closing session",
+ cameraId, MaxStreamSizes.configToString(config)));
+ stopCapture(/*fast*/false);
+ } catch (Throwable e) {
+ mCollector.addMessage(
+ String.format("Closing down for output combination %s failed due to: %s",
+ MaxStreamSizes.configToString(config), e.getMessage()));
+ }
+ }
+
+ for (SurfaceTexture target : privTargets) {
+ target.release();
+ }
+ for (ImageReader target : jpegTargets) {
+ target.close();
+ }
+ for (ImageReader target : yuvTargets) {
+ target.close();
+ }
+ for (ImageReader target : rawTargets) {
+ target.close();
+ }
+ }
+ }
+
+ private void setupConfigurationTargets(int[] configs, MaxStreamSizes maxSizes,
List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets,
List<ImageReader> yuvTargets, List<ImageReader> rawTargets,
List<Surface> outputSurfaces, int numBuffers) {
+ List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration> ();
+
+ setupConfigurationTargets(configs, maxSizes, privTargets, jpegTargets, yuvTargets,
+ rawTargets, outputConfigs, numBuffers, -1 /*overrideStreamIndex*/,
+ null /*overridePhysicalCameraIds*/);
+
+ for (OutputConfiguration outputConfig : outputConfigs) {
+ outputSurfaces.add(outputConfig.getSurface());
+ }
+ }
+
+ private void setupConfigurationTargets(int[] configs, MaxStreamSizes maxSizes,
+ List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets,
+ List<ImageReader> yuvTargets, List<ImageReader> rawTargets,
+ List<OutputConfiguration> outputConfigs, int numBuffers,
+ int overrideStreamIndex, List<String> overridePhysicalCameraIds) {
ImageDropperListener imageDropperListener = new ImageDropperListener();
- for (int i = 0; i < outputConfigs.length; i += 2) {
- int format = outputConfigs[i];
- int sizeLimit = outputConfigs[i + 1];
+ for (int i = 0; i < configs.length; i += 2) {
+ int format = configs[i];
+ int sizeLimit = configs[i + 1];
+ Surface newSurface;
- switch (format) {
- case PRIV: {
- Size targetSize = maxSizes.maxPrivSizes[sizeLimit];
- SurfaceTexture target = new SurfaceTexture(/*random int*/1);
- target.setDefaultBufferSize(targetSize.getWidth(), targetSize.getHeight());
- outputSurfaces.add(new Surface(target));
- privTargets.add(target);
- break;
+ int numConfigs = 1;
+ StaticMetadata physicalStaticMetadata;
+
+ if (overridePhysicalCameraIds != null &&
+ overridePhysicalCameraIds.size() > 1) {
+ numConfigs = overridePhysicalCameraIds.size();
+ }
+ for (int j = 0; j < numConfigs; j++) {
+ switch (format) {
+ case PRIV: {
+ Size targetSize = maxSizes.maxPrivSizes[sizeLimit];
+ SurfaceTexture target = new SurfaceTexture(/*random int*/1);
+ target.setDefaultBufferSize(targetSize.getWidth(), targetSize.getHeight());
+ OutputConfiguration config = new OutputConfiguration(new Surface(target));
+ if (numConfigs > 1) {
+ physicalStaticMetadata =
+ mAllStaticInfo.get(overridePhysicalCameraIds.get(j));
+ Size[] privSizes = physicalStaticMetadata.getAvailableSizesForFormatChecked(PRIV,
+ StaticMetadata.StreamDirection.Output);
+ if (!Arrays.asList(privSizes).contains(targetSize)) {
+ continue;
+ }
+ config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+ }
+ outputConfigs.add(config);
+ privTargets.add(target);
+ break;
+ }
+ case JPEG: {
+ Size targetSize = maxSizes.maxJpegSizes[sizeLimit];
+ ImageReader target = ImageReader.newInstance(
+ targetSize.getWidth(), targetSize.getHeight(), JPEG, numBuffers);
+ target.setOnImageAvailableListener(imageDropperListener, mHandler);
+ OutputConfiguration config = new OutputConfiguration(target.getSurface());
+ if (numConfigs > 1) {
+ physicalStaticMetadata =
+ mAllStaticInfo.get(overridePhysicalCameraIds.get(j));
+ Size[] jpegSizes = physicalStaticMetadata.getAvailableSizesForFormatChecked(JPEG,
+ StaticMetadata.StreamDirection.Output);
+ if (!Arrays.asList(jpegSizes).contains(targetSize)) {
+ continue;
+ }
+ config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+ }
+ outputConfigs.add(config);
+ jpegTargets.add(target);
+ break;
+ }
+ case YUV: {
+ Size targetSize = maxSizes.maxYuvSizes[sizeLimit];
+ ImageReader target = ImageReader.newInstance(
+ targetSize.getWidth(), targetSize.getHeight(), YUV, numBuffers);
+ target.setOnImageAvailableListener(imageDropperListener, mHandler);
+ OutputConfiguration config = new OutputConfiguration(target.getSurface());
+ if (numConfigs > 1) {
+ physicalStaticMetadata =
+ mAllStaticInfo.get(overridePhysicalCameraIds.get(j));
+ Size[] yuvSizes = physicalStaticMetadata.getAvailableSizesForFormatChecked(YUV,
+ StaticMetadata.StreamDirection.Output);
+ if (!Arrays.asList(yuvSizes).contains(targetSize)) {
+ continue;
+ }
+ config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+ }
+ outputConfigs.add(config);
+ yuvTargets.add(target);
+ break;
+ }
+ case RAW: {
+ Size targetSize = maxSizes.maxRawSize;
+ ImageReader target = ImageReader.newInstance(
+ targetSize.getWidth(), targetSize.getHeight(), RAW, numBuffers);
+ target.setOnImageAvailableListener(imageDropperListener, mHandler);
+ OutputConfiguration config = new OutputConfiguration(target.getSurface());
+ if (numConfigs > 1) {
+ physicalStaticMetadata =
+ mAllStaticInfo.get(overridePhysicalCameraIds.get(j));
+ Size[] rawSizes = physicalStaticMetadata.getAvailableSizesForFormatChecked(RAW,
+ StaticMetadata.StreamDirection.Output);
+ if (!Arrays.asList(rawSizes).contains(targetSize)) {
+ continue;
+ }
+ config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+ }
+ outputConfigs.add(config);
+ rawTargets.add(target);
+ break;
+ }
+ default:
+ fail("Unknown output format " + format);
}
- case JPEG: {
- Size targetSize = maxSizes.maxJpegSizes[sizeLimit];
- ImageReader target = ImageReader.newInstance(
- targetSize.getWidth(), targetSize.getHeight(), JPEG, numBuffers);
- target.setOnImageAvailableListener(imageDropperListener, mHandler);
- outputSurfaces.add(target.getSurface());
- jpegTargets.add(target);
- break;
- }
- case YUV: {
- Size targetSize = maxSizes.maxYuvSizes[sizeLimit];
- ImageReader target = ImageReader.newInstance(
- targetSize.getWidth(), targetSize.getHeight(), YUV, numBuffers);
- target.setOnImageAvailableListener(imageDropperListener, mHandler);
- outputSurfaces.add(target.getSurface());
- yuvTargets.add(target);
- break;
- }
- case RAW: {
- Size targetSize = maxSizes.maxRawSize;
- ImageReader target = ImageReader.newInstance(
- targetSize.getWidth(), targetSize.getHeight(), RAW, numBuffers);
- target.setOnImageAvailableListener(imageDropperListener, mHandler);
- outputSurfaces.add(target.getSurface());
- rawTargets.add(target);
- break;
- }
- default:
- fail("Unknown output format " + format);
}
}
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
index 0108ee6..777e0cd 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
@@ -27,6 +27,7 @@
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.OutputConfiguration;
import android.util.Size;
import android.hardware.camera2.cts.CameraTestUtils;
import android.hardware.camera2.cts.helpers.CameraErrorCollector;
@@ -48,6 +49,7 @@
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
public class Camera2AndroidTestCase extends AndroidTestCase {
@@ -66,6 +68,7 @@
protected BlockingSessionCallback mCameraSessionListener;
protected BlockingStateCallback mCameraListener;
protected String[] mCameraIds;
+ protected HashMap<String, StaticMetadata> mAllStaticInfo;
protected ImageReader mReader;
protected Surface mReaderSurface;
protected Handler mHandler;
@@ -109,6 +112,14 @@
mHandler = new Handler(mHandlerThread.getLooper());
mCameraListener = new BlockingStateCallback();
mCollector = new CameraErrorCollector();
+
+ mAllStaticInfo = new HashMap<String, StaticMetadata>();
+ for (String cameraId : mCameraIds) {
+ StaticMetadata staticMetadata = new StaticMetadata(
+ mCameraManager.getCameraCharacteristics(cameraId),
+ CheckLevel.ASSERT, /*collector*/null);
+ mAllStaticInfo.put(cameraId, staticMetadata);
+ }
}
@Override
@@ -217,6 +228,18 @@
}
/**
+ * Create a {@link #CameraCaptureSession} using the currently open camera with
+ * OutputConfigurations.
+ *
+ * @param outputSurfaces The set of output surfaces to configure for this session
+ */
+ protected void createSessionByConfigs(List<OutputConfiguration> outputConfigs) throws Exception {
+ mCameraSessionListener = new BlockingSessionCallback();
+ mCameraSession = CameraTestUtils.configureCameraSessionWithConfig(mCamera, outputConfigs,
+ mCameraSessionListener, mHandler);
+ }
+
+ /**
* Close a {@link #CameraDevice camera device} and clear the associated StaticInfo field for a
* given camera id. The default mCameraListener is used to wait for states.
* <p>
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index 30f56eb..24b948b 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -1890,7 +1890,7 @@
checkArrayValuesInRange(key, availableCaps,
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING);
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA);
capList = Arrays.asList(CameraTestUtils.toObject(availableCaps));
return capList;
}
@@ -2212,6 +2212,18 @@
}
/**
+ * Check if this camera device is a logical multi-camera backed by multiple
+ * physical cameras.
+ *
+ * @return true if this is a logical multi-camera.
+ */
+ public boolean isLogicalMultiCamera() {
+ List<Integer> availableCapabilities = getAvailableCapabilitiesChecked();
+ return (availableCapabilities.contains(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA));
+ }
+
+ /**
* Check if high speed video is supported (HIGH_SPEED_VIDEO scene mode is
* supported, supported high speed fps ranges and sizes are valid).
*
diff --git a/tests/framework/base/activitymanager/AndroidTest.xml b/tests/framework/base/activitymanager/AndroidTest.xml
index 7f487ef..213cabb 100644
--- a/tests/framework/base/activitymanager/AndroidTest.xml
+++ b/tests/framework/base/activitymanager/AndroidTest.xml
@@ -25,6 +25,7 @@
<option name="test-file-name" value="CtsDeviceServicesTestSecondApp.apk" />
<option name="test-file-name" value="CtsDeviceServicesTestThirdApp.apk" />
<option name="test-file-name" value="CtsDeviceDebuggableApp.apk" />
+ <option name="test-file-name" value="CtsDeviceDeprecatedSdkApp.apk" />
<option name="test-file-name" value="CtsDeviceDisplaySizeApp.apk" />
<option name="test-file-name" value="CtsDevicePrereleaseSdkApp.apk" />
<option name="test-file-name" value="CtsDisplayServiceApp.apk" />
diff --git a/tests/framework/base/activitymanager/appDeprecatedSdk/Android.mk b/tests/framework/base/activitymanager/appDeprecatedSdk/Android.mk
new file mode 100644
index 0000000..d28d30b
--- /dev/null
+++ b/tests/framework/base/activitymanager/appDeprecatedSdk/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := 16
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsDeviceDeprecatedSdkApp
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/framework/base/activitymanager/appDeprecatedSdk/AndroidManifest.xml b/tests/framework/base/activitymanager/appDeprecatedSdk/AndroidManifest.xml
new file mode 100644
index 0000000..19847b3
--- /dev/null
+++ b/tests/framework/base/activitymanager/appDeprecatedSdk/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Target sdk version set < 17. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.server.am.deprecatedsdk">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+ <application>
+ <activity android:name=".MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/tests/framework/base/activitymanager/appDeprecatedSdk/src/android/server/am/deprecatedsdk/MainActivity.java b/tests/framework/base/activitymanager/appDeprecatedSdk/src/android/server/am/deprecatedsdk/MainActivity.java
new file mode 100644
index 0000000..57b4e44
--- /dev/null
+++ b/tests/framework/base/activitymanager/appDeprecatedSdk/src/android/server/am/deprecatedsdk/MainActivity.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am.deprecatedsdk;
+
+import android.app.Activity;
+
+public class MainActivity extends Activity {
+
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
index 18bf397..56aedd0 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
@@ -72,7 +72,6 @@
}
@Presubmit
- @FlakyTest(bugId = 71877849)
@Test
public void testChangeFontScaleRelaunch() throws Exception {
// Should relaunch and receive no onConfigurationChanged()
@@ -80,7 +79,6 @@
}
@Presubmit
- @FlakyTest(bugId = 71877849)
@Test
public void testChangeFontScaleNoRelaunch() throws Exception {
// Should receive onConfigurationChanged() and no relaunch
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
index 36e6f04..c5f13f1 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
@@ -83,7 +83,6 @@
@Test
@Presubmit
- @FlakyTest(bugId = 71918731)
public void testStackList() throws Exception {
launchActivity(TEST_ACTIVITY_NAME);
mAmWmState.computeState(new String[] {TEST_ACTIVITY_NAME});
@@ -235,7 +234,6 @@
@Test
@Presubmit
- @FlakyTest(bugId = 71918731)
public void testLaunchToSideSingleInstance() throws Exception {
launchTargetToSide(SINGLE_INSTANCE_ACTIVITY_NAME, false);
}
@@ -245,7 +243,6 @@
launchTargetToSide(SINGLE_TASK_ACTIVITY_NAME, false);
}
- @FlakyTest
@Presubmit
@Test
public void testLaunchToSideMultipleWithDifferentIntent() throws Exception {
diff --git a/tests/framework/base/activitymanager/src/android/server/am/DeprecatedTargetSdkTest.java b/tests/framework/base/activitymanager/src/android/server/am/DeprecatedTargetSdkTest.java
new file mode 100644
index 0000000..42edc5f
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/DeprecatedTargetSdkTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Ensure that compatibility dialog is shown when launching an application
+ * targeting a deprecated version of SDK.
+ * <p>Build/Install/Run:
+ * atest CtsActivityManagerDeviceTestCases:DeprecatedTargetSdkTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class DeprecatedTargetSdkTest extends ActivityManagerTestBase {
+ private static final String AM_START_COMMAND = "am start -n %s/%s.%s";
+ private static final String AM_FORCE_STOP = "am force-stop %s";
+
+ private static final int ACTIVITY_TIMEOUT_MILLIS = 1000;
+ private static final int WINDOW_TIMEOUT_MILLIS = 1000;
+
+ @After
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ // Ensure app process is stopped.
+ forceStopPackage("android.server.am.deprecatedsdk");
+ forceStopPackage("android.server.am");
+ }
+
+ @Test
+ public void testCompatibilityDialog() throws Exception {
+ // Launch target app.
+ startActivity("android.server.am.deprecatedsdk", "MainActivity");
+ verifyWindowDisplayed("MainActivity", ACTIVITY_TIMEOUT_MILLIS);
+ verifyWindowDisplayed("DeprecatedTargetSdkVersionDialog", WINDOW_TIMEOUT_MILLIS);
+
+ // Go back to dismiss the warning dialog.
+ executeShellCommand("input keyevent 4");
+
+ // Go back again to formally stop the app. If we just kill the process, it'll attempt to
+ // resume rather than starting from scratch (as far as ActivityStack is concerned) and it
+ // won't invoke the warning dialog.
+ executeShellCommand("input keyevent 4");
+ }
+
+ private void forceStopPackage(String packageName) {
+ final String forceStopCmd = String.format(AM_FORCE_STOP, packageName);
+ executeShellCommand(forceStopCmd);
+ }
+
+ private void startActivity(String packageName, String activityName){
+ executeShellCommand(getStartCommand(packageName, activityName));
+ }
+
+ private String getStartCommand(String packageName, String activityName) {
+ return String.format(AM_START_COMMAND, packageName, packageName, activityName);
+ }
+
+ private void verifyWindowDisplayed(String windowName, long timeoutMillis) {
+ boolean success = false;
+
+ // Verify that compatibility dialog is shown within 1000ms.
+ final long timeoutTimeMillis = System.currentTimeMillis() + timeoutMillis;
+ while (!success && System.currentTimeMillis() < timeoutTimeMillis) {
+ final String output = executeShellCommand("dumpsys window");
+ success = output.contains(windowName);
+ }
+
+ assertTrue(windowName + " was not displayed", success);
+ }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
index 89ff760..14c0179 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
@@ -53,7 +53,6 @@
* Run: cts/tests/framework/base/activitymanager/util/run-test CtsWindowManagerDeviceTestCases android.server.wm.CrossAppDragAndDropTests
*/
@Presubmit
-@FlakyTest(bugId = 65739235)
public class CrossAppDragAndDropTests {
private static final String TAG = "CrossAppDragAndDrop";
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
index 79c35f1..b4d1c27 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -31,6 +31,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.LocaleList;
import android.os.Looper;
import android.os.Parcel;
import android.os.Process;
@@ -132,6 +133,12 @@
getCurrentInputConnection().commitText(text, newCursorPosition);
break;
}
+ case "reportLanguageHint": {
+ final LocaleList languageHint =
+ command.getExtras().getParcelable("languageHint");
+ getCurrentInputConnection().reportLanguageHint(languageHint);
+ break;
+ }
}
}
});
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
index 4fe9f5a..ed48d05 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
@@ -27,6 +27,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.LocaleList;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
@@ -297,4 +298,30 @@
mContext.sendBroadcast(intent);
return command;
}
+
+ /**
+ * Lets {@link MockIme} to call
+ * {@link android.view.inputmethod.InputConnection#reportLanguageHint(LocaleList)} with the
+ * given parameters.
+ *
+ * <p>This triggers {@code getCurrentInputConnection().reportLanguageHint(languageHint)}.</p>
+ *
+ * @param languageHint to be passed as the {@code languageHint} parameter
+ * @return {@link ImeCommand} object that can be passed to
+ * {@link ImeEventStreamTestUtils#expectCommand(ImeEventStream, ImeCommand, long)} to
+ * wait until this event is handled by {@link MockIme}
+ */
+ @NonNull
+ public ImeCommand callReportLnaguageHint(@NonNull LocaleList languageHint) {
+ final Bundle params = new Bundle();
+ params.putParcelable("languageHint", languageHint);
+ final ImeCommand command = new ImeCommand(
+ "reportLanguageHint", SystemClock.elapsedRealtimeNanos(), true, params);
+ final Intent intent = new Intent();
+ intent.setPackage(mContext.getPackageName());
+ intent.setAction(MockIme.getCommandActionName(mImeEventActionName));
+ intent.putExtras(command.toBundle());
+ mContext.sendBroadcast(intent);
+ return command;
+ }
}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputConnectionTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputConnectionTest.java
new file mode 100644
index 0000000..ce05e01
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputConnectionTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod.cts;
+
+import static android.view.inputmethod.cts.util.TestUtils.getOnMainSync;
+import static android.view.inputmethod.cts.util.TestUtils.waitOnMainUntil;
+
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.LocaleList;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.cts.util.EndToEndImeTestBase;
+import android.view.inputmethod.cts.util.TestActivity;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+import com.android.cts.mockime.ImeCommand;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class InputConnectionTest extends EndToEndImeTestBase {
+ private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+
+ @Test
+ public void testReportLanguageHint() throws Exception {
+ final String testMarker = "testReportLanguageHint-" + SystemClock.elapsedRealtimeNanos();
+
+ try(MockImeSession imeSession = MockImeSession.create(
+ InstrumentationRegistry.getContext(),
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ new ImeSettings.Builder())) {
+ final ImeEventStream stream = imeSession.openEventStream();
+
+ final AtomicReference<ArrayList<LocaleList>> languageHintHistoryRef =
+ new AtomicReference<>();
+ TestActivity.startSync(activity -> {
+ final LinearLayout layout = new LinearLayout(activity);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ final EditText editText = new EditText(activity) {
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
+ final InputConnection original = super.onCreateInputConnection(editorInfo);
+ final ArrayList<LocaleList> languageHintHistory = new ArrayList<>();
+ final InputConnectionWrapper wrapper =
+ new InputConnectionWrapper(original, false) {
+ @Override
+ public void reportLanguageHint(LocaleList languageHint) {
+ languageHintHistory.add(languageHint);
+ }
+ };
+ // In case onCreateInputConnection() gets called twice, make sure that only
+ // the first call is used in later tests.
+ if (languageHintHistoryRef.compareAndSet(null, languageHintHistory)) {
+ editorInfo.privateImeOptions = testMarker;
+ }
+ return wrapper;
+ }
+ };
+ editText.requestFocus();
+ layout.addView(editText);
+ return layout;
+ });
+
+ // Wait until "onStartInput" gets called for the EditText.
+ expectEvent(stream, event -> {
+ if (!TextUtils.equals("onStartInput", event.getEventName())) {
+ return false;
+ }
+ final EditorInfo editorInfo = event.getArguments().getParcelable("editorInfo");
+ return TextUtils.equals(testMarker, editorInfo.privateImeOptions);
+ }, TIMEOUT);
+
+ final List<LocaleList> languageHintHistory = languageHintHistoryRef.get();
+ assertNotNull(languageHintHistory);
+ assertTrue(getOnMainSync(() -> languageHintHistoryRef.get().isEmpty()));
+
+ final LocaleList localeList1 = LocaleList.forLanguageTags("sr-Cyrl-RS");
+ final ImeCommand reportLanguageHint1 = imeSession.callReportLnaguageHint(localeList1);
+ expectCommand(stream, reportLanguageHint1, TIMEOUT);
+ waitOnMainUntil(() -> languageHintHistoryRef.get().size() == 1
+ && localeList1.equals(languageHintHistoryRef.get().get(0)), TIMEOUT);
+
+ final LocaleList localeList2 = LocaleList.forLanguageTags("sr-Latn-RS-x-android,en-US");
+ final ImeCommand reportLanguageHint2 = imeSession.callReportLnaguageHint(localeList2);
+ expectCommand(stream, reportLanguageHint2, TIMEOUT);
+ waitOnMainUntil(() -> languageHintHistoryRef.get().size() == 2
+ && localeList2.equals(languageHintHistoryRef.get().get(1)), TIMEOUT);
+ }
+ }
+}
diff --git a/tests/tests/car/src/android/car/cts/CarAudioManagerTest.java b/tests/tests/car/src/android/car/cts/CarAudioManagerTest.java
deleted file mode 100644
index 0f9c8f1..0000000
--- a/tests/tests/car/src/android/car/cts/CarAudioManagerTest.java
+++ /dev/null
@@ -1,55 +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.car.cts;
-
-import android.car.Car;
-import android.car.media.CarAudioManager;
-import android.media.AudioAttributes;
-import android.platform.test.annotations.RequiresDevice;
-import android.test.suitebuilder.annotation.SmallTest;
-
-
-@SmallTest
-@RequiresDevice
-/** Unit tests for {@link CarAudioManager}. */
-public class CarAudioManagerTest extends CarApiTestBase {
- private static final String TAG = CarAudioManagerTest.class.getSimpleName();
- private CarAudioManager mManager;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mManager = (CarAudioManager) getCar().getCarManager(Car.AUDIO_SERVICE);
- assertNotNull(mManager);
- }
-
- public void testGetAudioAttributesForCarUsageForMusic() throws Exception {
- AudioAttributes.Builder musicBuilder = new AudioAttributes.Builder();
- musicBuilder.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
- .setUsage(AudioAttributes.USAGE_MEDIA);
-
- assertEquals(musicBuilder.build(), mManager.getAudioAttributesForCarUsage(
- CarAudioManager.CAR_AUDIO_USAGE_MUSIC));
- }
-
- public void testGetAudioAttributesForCarUsageForUnknown() throws Exception {
- AudioAttributes.Builder unknownBuilder = new AudioAttributes.Builder();
- unknownBuilder.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
- .setUsage(AudioAttributes.USAGE_UNKNOWN);
-
- assertEquals(unknownBuilder.build(), mManager.getAudioAttributesForCarUsage(10007));
- }
-}
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index 2695fac..1ba9753 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -181,6 +181,17 @@
}
/**
+ * Test ACTION_SHOW_ASSISTED_DIALING_SETTINGS, it will display the assisted dialing preferences.
+ */
+ public void testShowAssistedDialingSettings() {
+ PackageManager packageManager = mContext.getPackageManager();
+ if (packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ Intent intent = new Intent(TelecomManager.ACTION_SHOW_ASSISTED_DIALING_SETTINGS);
+ assertCanBeHandled(intent);
+ }
+ }
+
+ /**
* Test ACTION_SHOW_CALL_SETTINGS, it will display the call preferences.
*/
public void testShowCallSettings() {
diff --git a/tests/tests/content/src/android/content/res/cts/AssetManager_AssetInputStreamTest.java b/tests/tests/content/src/android/content/res/cts/AssetManager_AssetInputStreamTest.java
index 32c1a3a..32db789 100644
--- a/tests/tests/content/src/android/content/res/cts/AssetManager_AssetInputStreamTest.java
+++ b/tests/tests/content/src/android/content/res/cts/AssetManager_AssetInputStreamTest.java
@@ -15,114 +15,145 @@
*/
package android.content.res.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import android.content.res.AssetManager;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
-public class AssetManager_AssetInputStreamTest extends AndroidTestCase {
- private AssetManager.AssetInputStream mAssetInputStream;
- private final String CONTENT_STRING = "OneTwoThreeFourFiveSixSevenEightNineTen";
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mAssetInputStream = (AssetManager.AssetInputStream)mContext.getAssets().open("text.txt");
+@SmallTest
+public class AssetManager_AssetInputStreamTest {
+ private static final byte[] EXPECTED_BYTES = "OneTwoThreeFourFiveSixSevenEightNineTen".getBytes(
+ StandardCharsets.UTF_8);
+ private InputStream mAssetInputStream;
+
+ @Before
+ public void setUp() throws Exception {
+ mAssetInputStream = InstrumentationRegistry.getContext().getAssets().open("text.txt");
}
- public void testClose() throws IOException {
+ @After
+ public void tearDown() throws Exception {
mAssetInputStream.close();
+ }
+ @Test
+ public void testClose() throws Exception {
+ mAssetInputStream.close();
try {
mAssetInputStream.read();
- fail("should throw exception");
- } catch (NullPointerException e) {
+ fail("read after close should throw an exception");
+ } catch (IllegalStateException e) {
// expected
}
}
+ @Test
public void testGetAssetInt() {
+ AssetManager.AssetInputStream assetInputStream =
+ (AssetManager.AssetInputStream) mAssetInputStream;
try {
// getAssetInt is no longer supported.
- mAssetInputStream.getAssetInt();
- fail();
+ assetInputStream.getAssetInt();
+ fail("getAssetInt should throw an exception");
} catch (UnsupportedOperationException expected) {
}
}
+ @Test
public void testMarkReset() throws IOException {
assertTrue(mAssetInputStream.markSupported());
- final int readlimit = 10;
- final byte[] bytes = CONTENT_STRING.getBytes();
- for (int i = 0; i < readlimit; i++) {
- assertEquals(bytes[i], mAssetInputStream.read());
+ for (int i = 0; i < 10; i++) {
+ assertEquals(EXPECTED_BYTES[i], mAssetInputStream.read());
}
- mAssetInputStream.mark(readlimit);
+ mAssetInputStream.mark(10);
mAssetInputStream.reset();
- for (int i = 0; i < readlimit; i++) {
- assertEquals(bytes[i + readlimit], mAssetInputStream.read());
+ for (int i = 0; i < 10; i++) {
+ assertEquals(EXPECTED_BYTES[10 + i], mAssetInputStream.read());
}
}
- public void testReadMethods() throws IOException {
- // test available()
- final byte[] bytes = CONTENT_STRING.getBytes();
- int len = mAssetInputStream.available();
- int end = -1;
- assertEquals(CONTENT_STRING.length(), len);
- for (int i = 0; i < len; i++) {
- assertEquals(bytes[i], mAssetInputStream.read());
+ @Test
+ public void testSingleByteRead() throws Exception {
+ assertEquals(EXPECTED_BYTES.length, mAssetInputStream.available());
+ for (int i = 0; i < EXPECTED_BYTES.length; i++) {
+ assertEquals(EXPECTED_BYTES[i], mAssetInputStream.read());
+ assertEquals(EXPECTED_BYTES.length - i - 1, mAssetInputStream.available());
}
- assertEquals(end, mAssetInputStream.read());
- // test read(byte[])
- mAssetInputStream.reset();
- int dataLength = 10;
- byte[] data = new byte[dataLength];
- int ret = mAssetInputStream.read(data);
- assertEquals(dataLength, ret);
- for (int i = 0; i < dataLength; i++) {
- assertEquals(bytes[i], data[i]);
- }
- data = new byte[len - dataLength];
- assertEquals(len - dataLength, mAssetInputStream.read(data));
- for (int i = 0; i < len - dataLength; i++) {
- assertEquals(bytes[i + dataLength], data[i]);
- }
- assertEquals(end, mAssetInputStream.read(data));
+ // Check end-of-file condition.
+ assertEquals(-1, mAssetInputStream.read());
+ assertEquals(0, mAssetInputStream.available());
+ }
- // test read(bytep[], int, int)
- mAssetInputStream.reset();
- int offset = 0;
- ret = mAssetInputStream.read(data, offset, dataLength);
- assertEquals(dataLength, ret);
- for (int i = offset; i < ret; i++) {
- assertEquals(bytes[i], data[offset + i]);
+ @Test
+ public void testByteArrayRead() throws Exception {
+ byte[] buffer = new byte[10];
+ assertEquals(10, mAssetInputStream.read(buffer));
+ for (int i = 0; i < 10; i++) {
+ assertEquals(EXPECTED_BYTES[i], buffer[i]);
}
- mAssetInputStream.reset();
- offset = 2;
- ret = mAssetInputStream.read(data, offset, dataLength);
- assertEquals(dataLength, ret);
- for (int i = offset; i < ret; i++) {
- assertEquals(bytes[i], data[offset + i]);
- }
- data = new byte[len + offset];
- ret = mAssetInputStream.read(data, offset, len);
- assertEquals(len - dataLength, ret);
- for (int i = offset; i < ret; i++) {
- assertEquals(bytes[i + dataLength], data[offset + i]);
- }
- assertEquals(end, mAssetInputStream.read(data, offset, len));
- // test len is zero,
- mAssetInputStream.reset();
- assertEquals(0, mAssetInputStream.read(data, 0, 0));
- // test skip(int)
- int skipLenth = 8;
- mAssetInputStream.reset();
- mAssetInputStream.skip(skipLenth);
- assertEquals(CONTENT_STRING.charAt(skipLenth), mAssetInputStream.read());
- // test read(byte[] b), b is null
+ buffer = new byte[5];
+ assertEquals(5, mAssetInputStream.read(buffer));
+ for (int i = 0; i < 5; i++) {
+ assertEquals(EXPECTED_BYTES[10 + i], buffer[i]);
+ }
+
+ // Check end-of-file condition.
+ buffer = new byte[EXPECTED_BYTES.length - 15];
+ assertEquals(buffer.length, mAssetInputStream.read(buffer));
+ assertEquals(-1, mAssetInputStream.read(buffer));
+ assertEquals(0, mAssetInputStream.available());
+ }
+
+ @Test
+ public void testByteArrayReadOffset() throws Exception {
+ byte[] buffer = new byte[15];
+ assertEquals(10, mAssetInputStream.read(buffer, 0, 10));
+ assertEquals(EXPECTED_BYTES.length - 10, mAssetInputStream.available());
+ for (int i = 0; i < 10; i++) {
+ assertEquals(EXPECTED_BYTES[i], buffer[i]);
+ }
+
+ assertEquals(5, mAssetInputStream.read(buffer, 10, 5));
+ assertEquals(EXPECTED_BYTES.length - 15, mAssetInputStream.available());
+ for (int i = 0; i < 15; i++) {
+ assertEquals(EXPECTED_BYTES[i], buffer[i]);
+ }
+
+ // Check end-of-file condition.
+ buffer = new byte[EXPECTED_BYTES.length];
+ assertEquals(EXPECTED_BYTES.length - 15,
+ mAssetInputStream.read(buffer, 15, EXPECTED_BYTES.length - 15));
+ assertEquals(-1, mAssetInputStream.read(buffer, 0, 1));
+ assertEquals(0, mAssetInputStream.available());
+ }
+
+ @Test
+ public void testSkip() throws Exception {
+ assertEquals(8, mAssetInputStream.skip(8));
+ assertEquals(EXPECTED_BYTES.length - 8, mAssetInputStream.available());
+ assertEquals(EXPECTED_BYTES[8], mAssetInputStream.read());
+
+ // Check that skip respects the available space.
+ assertEquals(EXPECTED_BYTES.length - 8 - 1, mAssetInputStream.skip(1000));
+ assertEquals(0, mAssetInputStream.available());
+ }
+
+ @Test
+ public void testArgumentEdgeCases() throws Exception {
+ // test read(byte[]): byte[] is null
try {
mAssetInputStream.read(null);
fail("should throw NullPointerException ");
@@ -130,34 +161,39 @@
// expected
}
+ // test read(byte[], int, int): byte[] is null
try {
mAssetInputStream.read(null, 0, mAssetInputStream.available());
fail("should throw NullPointerException ");
} catch (NullPointerException e) {
// expected
}
- // test read(bytep[], int, int): offset is negative,
+
+ // test read(byte[]): byte[] is len 0
+ final int previousAvailable = mAssetInputStream.available();
+ assertEquals(0, mAssetInputStream.read(new byte[0]));
+ assertEquals(previousAvailable, mAssetInputStream.available());
+
+ // test read(byte[]): byte[] is len 0
+ assertEquals(0, mAssetInputStream.read(new byte[0], 0, 0));
+ assertEquals(previousAvailable, mAssetInputStream.available());
+
+ // test read(byte[], int, int): offset is negative
try {
- data = new byte[10];
+ byte[] data = new byte[10];
mAssetInputStream.read(data, -1, mAssetInputStream.available());
fail("should throw IndexOutOfBoundsException ");
} catch (IndexOutOfBoundsException e) {
// expected
}
- // test read(bytep[], int, int): len+offset greater than data length
+ // test read(byte[], int, int): len + offset greater than data length
try {
- data = new byte[10];
+ byte[] data = new byte[10];
assertEquals(0, mAssetInputStream.read(data, 0, data.length + 2));
fail("should throw IndexOutOfBoundsException ");
} catch (IndexOutOfBoundsException e) {
+ // expected
}
}
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- mAssetInputStream.close();
- }
-
}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index 0ea3ae0..16c6417 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -466,6 +466,89 @@
}
}
+ private MediaFormat createVideoFormatForBitrateMode(String mime, int width, int height,
+ int bitrateMode, CodecCapabilities caps) {
+ MediaCodecInfo.EncoderCapabilities encoderCaps = caps.getEncoderCapabilities();
+ if (!encoderCaps.isBitrateModeSupported(bitrateMode)) {
+ return null;
+ }
+
+ VideoCapabilities vidCaps = caps.getVideoCapabilities();
+ MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
+
+ // bitrate
+ int maxWidth = vidCaps.getSupportedWidths().getUpper();
+ int maxHeight = vidCaps.getSupportedHeightsFor(width).getUpper();
+ int maxRate = vidCaps.getSupportedFrameRatesFor(width, height).getUpper().intValue();
+ format.setInteger(MediaFormat.KEY_BITRATE_MODE, bitrateMode);
+ if (bitrateMode == MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ) {
+ int quality = encoderCaps.getQualityRange().getLower();
+ Log.i(TAG, "reasonable quality for " + width + "x" + height + "@" + maxRate
+ + " " + mime + " = " + quality);
+ format.setInteger(MediaFormat.KEY_QUALITY, quality);
+ } else {
+ int bitrate = vidCaps.getBitrateRange().clamp(
+ (int)(vidCaps.getBitrateRange().getUpper()
+ / Math.sqrt((double)maxWidth * maxHeight / width / height)));
+ Log.i(TAG, "reasonable bitrate for " + width + "x" + height + "@" + maxRate
+ + " " + mime + " = " + bitrate + " mode " + bitrateMode);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
+ }
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, maxRate);
+ format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+ CodecCapabilities.COLOR_FormatYUV420Flexible);
+
+ return format;
+ }
+
+ public void testAllAdvertisedVideoEncoderBitrateModes() throws IOException {
+ boolean skipped = true;
+ final int[] modes = {
+ MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ,
+ MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR,
+ MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR
+ };
+ for (MediaCodecInfo info : mAllInfos) {
+ if (!info.isEncoder()) {
+ continue;
+ }
+
+ for (String mime: info.getSupportedTypes()) {
+ boolean isVideo = isVideoMime(mime);
+ if (!isVideo) {
+ continue;
+ }
+ skipped = false;
+
+ int numSupportedModes = 0;
+ for (int mode : modes) {
+ MediaFormat format = createVideoFormatForBitrateMode(
+ mime, 176, 144, mode, info.getCapabilitiesForType(mime));
+ if (format == null) {
+ continue;
+ }
+ MediaCodec codec = null;
+ try {
+ codec = MediaCodec.createByCodecName(info.getName());
+ codec.configure(format, null /* surface */, null /* crypto */,
+ MediaCodec.CONFIGURE_FLAG_ENCODE);
+ } finally {
+ if (codec != null) {
+ codec.release();
+ }
+ }
+ numSupportedModes++;
+ }
+ assertTrue(info.getName() + " has no supported bitrate mode",
+ numSupportedModes > 0);
+ }
+ }
+ if (skipped) {
+ MediaUtils.skipTest("no video encoders found");
+ }
+ }
+
public void testAllNonTunneledVideoCodecsSupportFlexibleYUV() throws IOException {
boolean skipped = true;
for (MediaCodecInfo info : mAllInfos) {
diff --git a/tests/tests/media/src/android/media/cts/MediaDrmMetricsTest.java b/tests/tests/media/src/android/media/cts/MediaDrmMetricsTest.java
new file mode 100644
index 0000000..88e10e4
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaDrmMetricsTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.media.MediaDrm;
+import android.media.MediaDrm.MediaDrmStateException;
+import android.os.PersistableBundle;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import java.util.UUID;
+
+
+/**
+ * MediaDrm tests covering {@link MediaDrm#getMetrics} and related
+ * functionality.
+ */
+public class MediaDrmMetricsTest extends AndroidTestCase {
+ private static final String TAG = MediaDrmMetricsTest.class.getSimpleName();
+
+ private static final UUID CLEARKEY_SCHEME_UUID =
+ new UUID(0xe2719d58a985b3c9L, 0x781ab030af78d30eL);
+
+ public void testGetMetricsEmpty() throws Exception {
+ MediaDrm drm = new MediaDrm(CLEARKEY_SCHEME_UUID);
+ assertNotNull(drm);
+
+ PersistableBundle metrics = drm.getMetrics();
+ assertNotNull(metrics);
+ assertTrue(metrics.isEmpty());
+ }
+
+ public void testGetMetricsSession() throws Exception {
+ MediaDrm drm = new MediaDrm(CLEARKEY_SCHEME_UUID);
+ assertNotNull(drm);
+ byte[] sid = drm.openSession();
+ assertNotNull(sid);
+ byte[] sid2 = drm.openSession();
+ assertNotNull(sid2);
+
+ PersistableBundle metrics = drm.getMetrics();
+ assertNotNull(metrics);
+ assertEquals(1, metrics.keySet().size());
+
+ assertEquals(2, metrics.getLong(
+ MediaDrm.MetricsConstants.OPEN_SESSION_OK_COUNT, -1));
+ assertEquals(0, metrics.getLong(
+ MediaDrm.MetricsConstants.OPEN_SESSION_ERROR_COUNT));
+ }
+
+ public void testGetMetricsGetKeyRequest() throws Exception {
+ MediaDrm drm = new MediaDrm(CLEARKEY_SCHEME_UUID);
+ assertNotNull(drm);
+ byte[] sid = drm.openSession();
+ assertNotNull(sid);
+
+ try {
+ drm.getKeyRequest(sid, null, "", 2, null);
+ } catch (MediaDrmStateException e) {
+ // Exception expected.
+ }
+
+ PersistableBundle metrics = drm.getMetrics();
+ assertNotNull(metrics);
+ Log.v(TAG, metrics.toString());
+ Log.v(TAG, metrics.keySet().toString());
+ for(String key : metrics.keySet()) {
+ Log.v(TAG, "key, value: " + key + "," + metrics.get(key));
+ }
+
+ assertEquals(2, metrics.keySet().size());
+ assertEquals(-1, metrics.getLong(
+ MediaDrm.MetricsConstants.GET_KEY_REQUEST_OK_COUNT, -1));
+ assertEquals(1, metrics.getLong(
+ MediaDrm.MetricsConstants.GET_KEY_REQUEST_ERROR_COUNT));
+ }
+}
diff --git a/tests/tests/security/res/raw/cve_2017_0763.mp4 b/tests/tests/security/res/raw/cve_2017_0763.mp4
new file mode 100644
index 0000000..94027bf
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_0763.mp4
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index df37d77..3e75ea6 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -255,6 +255,11 @@
doStagefrightTest(R.raw.cve_2017_13229);
}
+ @SecurityTest
+ public void testStagefright_cve_2017_0763() throws Exception {
+ doStagefrightTest(R.raw.cve_2017_0763);
+ }
+
/***********************************************************
to prevent merge conflicts, add M tests below this comment,
before any existing test methods
diff --git a/tests/tests/telecom/AndroidManifest.xml b/tests/tests/telecom/AndroidManifest.xml
index f0cad67..76e7743 100644
--- a/tests/tests/telecom/AndroidManifest.xml
+++ b/tests/tests/telecom/AndroidManifest.xml
@@ -24,6 +24,7 @@
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.REGISTER_CALL_PROVIDER" />
+ <uses-permission android:name="android.permission.ACCEPT_HANDOVER" />
<uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index 321d0b5..7610176 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -81,6 +81,8 @@
TestUtils.InvokeCounter mOnRttStatusChangedCounter;
TestUtils.InvokeCounter mOnRttInitiationFailedCounter;
TestUtils.InvokeCounter mOnRttRequestCounter;
+ TestUtils.InvokeCounter mOnHandoverCompleteCounter;
+ TestUtils.InvokeCounter mOnHandoverFailedCounter;
Bundle mPreviousExtras;
int mPreviousProperties = -1;
@@ -316,6 +318,15 @@
mOnRttInitiationFailedCounter.invoke(call, reason);
}
+ @Override
+ public void onHandoverComplete(Call call) {
+ mOnHandoverCompleteCounter.invoke(call);
+ }
+
+ @Override
+ public void onHandoverFailed(Call call, int reason) {
+ mOnHandoverFailedCounter.invoke(call, reason);
+ }
};
MockInCallService.setCallbacks(mInCallCallbacks);
@@ -335,6 +346,8 @@
mOnRttInitiationFailedCounter =
new TestUtils.InvokeCounter("mOnRttInitiationFailedCounter");
mOnRttRequestCounter = new TestUtils.InvokeCounter("mOnRttRequestCounter");
+ mOnHandoverCompleteCounter = new TestUtils.InvokeCounter("mOnHandoverCompleteCounter");
+ mOnHandoverFailedCounter = new TestUtils.InvokeCounter("mOnHandoverFailedCounter");
}
/**
@@ -600,7 +613,10 @@
if (extras == null) {
extras = new Bundle();
}
- extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TestUtils.TEST_PHONE_ACCOUNT_HANDLE);
+ if (!extras.containsKey(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE)) {
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+ TestUtils.TEST_PHONE_ACCOUNT_HANDLE);
+ }
if (!VideoProfile.isAudioOnly(videoState)) {
extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
diff --git a/tests/tests/telecom/src/android/telecom/cts/CtsSelfManagedConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/CtsSelfManagedConnectionService.java
index 16e7b27..6610705 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CtsSelfManagedConnectionService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CtsSelfManagedConnectionService.java
@@ -24,6 +24,7 @@
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
@@ -41,7 +42,8 @@
public static int CONNECTION_CREATED_LOCK = 0;
public static int CREATE_INCOMING_CONNECTION_FAILED_LOCK = 1;
public static int CREATE_OUTGOING_CONNECTION_FAILED_LOCK = 2;
- private static int NUM_LOCKS = CREATE_OUTGOING_CONNECTION_FAILED_LOCK + 1;
+ public static int HANDOVER_FAILED_LOCK = 3;
+ private static int NUM_LOCKS = HANDOVER_FAILED_LOCK + 1;
private static CtsSelfManagedConnectionService sConnectionService;
@@ -60,6 +62,10 @@
private Object mLock = new Object();
private List<SelfManagedConnection> mConnections = new ArrayList<>();
+ private TestUtils.InvokeCounter mOnCreateIncomingHandoverConnectionCounter =
+ new TestUtils.InvokeCounter("incomingHandoverConnection");
+ private TestUtils.InvokeCounter mOnCreateOutgoingHandoverConnectionCounter =
+ new TestUtils.InvokeCounter("outgoingHandoverConnection");
public static CtsSelfManagedConnectionService getConnectionService() {
return sConnectionService;
@@ -106,6 +112,26 @@
mLocks[CREATE_OUTGOING_CONNECTION_FAILED_LOCK].countDown();
}
+ @Override
+ public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
+ ConnectionRequest request) {
+ mOnCreateIncomingHandoverConnectionCounter.invoke(fromPhoneAccountHandle, request);
+ return createSelfManagedConnection(request, true /* incoming */);
+ }
+
+ @Override
+ public Connection onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
+ ConnectionRequest request) {
+ mOnCreateOutgoingHandoverConnectionCounter.invoke(fromPhoneAccountHandle, request);
+ return createSelfManagedConnection(request, false /* incoming */);
+ }
+
+ @Override
+ public void onHandoverFailed(ConnectionRequest request, int error) {
+ mLocks[HANDOVER_FAILED_LOCK].countDown();
+ }
+
+
public void tearDown() {
synchronized(mLock) {
if (mConnections != null && mConnections.size() > 0) {
@@ -192,4 +218,12 @@
return null;
}
}
+
+ public TestUtils.InvokeCounter getOnCreateIncomingHandoverConnectionCounter() {
+ return mOnCreateIncomingHandoverConnectionCounter;
+ }
+
+ public TestUtils.InvokeCounter getOnCreateOutgoingHandoverConnectionCounter() {
+ return mOnCreateOutgoingHandoverConnectionCounter;
+ }
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/HandoverTest.java b/tests/tests/telecom/src/android/telecom/cts/HandoverTest.java
new file mode 100644
index 0000000..597694b
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/HandoverTest.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telecom.cts;
+
+import static android.telecom.cts.TestUtils.TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE;
+import static android.telecom.cts.TestUtils.TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE;
+import static android.telecom.cts.TestUtils.TEST_PHONE_ACCOUNT_HANDOVER_SRC;
+import static android.telecom.cts.TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS;
+import static android.telecom.cts.TestUtils.waitOnLocalMainLooper;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.telecom.Call;
+import android.telecom.Connection;
+import android.telecom.ConnectionRequest;
+import android.telecom.ConnectionService;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
+
+/**
+ * Tests the Telecom handover APIs.
+ */
+public class HandoverTest extends BaseTelecomTestWithMockServices {
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mContext = getInstrumentation().getContext();
+ if (mShouldTestTelecom) {
+ setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE);
+
+ // Test handover source is a managed ConnectionService
+ mTelecomManager.registerPhoneAccount(TestUtils.TEST_PHONE_ACCOUNT_HANDOVER_SRC);
+ TestUtils.enablePhoneAccount(getInstrumentation(),
+ TestUtils.TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE);
+ assertPhoneAccountEnabled(TestUtils.TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE);
+
+ // Test handover destination is a self-managed ConnectionService.
+ mTelecomManager.registerPhoneAccount(TestUtils.TEST_PHONE_ACCOUNT_HANDOVER_DEST);
+ }
+
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ CtsSelfManagedConnectionService connectionService =
+ CtsSelfManagedConnectionService.getConnectionService();
+ if (connectionService != null) {
+ connectionService.tearDown();
+ mTelecomManager.unregisterPhoneAccount(
+ TestUtils.TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE);
+ mTelecomManager.unregisterPhoneAccount(
+ TestUtils.TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE);
+ }
+
+ super.tearDown();
+ }
+
+ /**
+ * Ensures a call handover cannot be initiated for a {@link android.telecom.PhoneAccount} which
+ * does not declare {@link android.telecom.PhoneAccount#EXTRA_SUPPORTS_HANDOVER_FROM}.
+ */
+ public void testHandoverSourceFailed() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ placeAndVerifyCall();
+ Call call = mInCallCallbacks.getService().getLastCall();
+
+ call.handoverTo(TestUtils.TEST_SELF_MANAGED_HANDLE_1, VideoProfile.STATE_BIDIRECTIONAL,
+ null);
+
+ // Expect the handover failed callback to be called.
+ mOnHandoverFailedCounter.waitForCount(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ Call callbackCall = (Call) mOnHandoverFailedCounter.getArgs(0)[0];
+ int failureReason = (int) mOnHandoverFailedCounter.getArgs(0)[1];
+ assertEquals(call, callbackCall);
+ assertEquals(Call.Callback.HANDOVER_FAILURE_DEST_NOT_SUPPORTED, failureReason);
+
+ call.disconnect();
+ }
+
+ /**
+ * Ensures a call handover cannot be initiated to a {@link android.telecom.PhoneAccount} which
+ * does not declare {@link android.telecom.PhoneAccount#EXTRA_SUPPORTS_HANDOVER_TO}.
+ */
+ public void testHandoverDestinationFailed() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+ startSourceCall();
+ Call call = mInCallCallbacks.getService().getLastCall();
+
+ // Now try to handover to an account which does not support handover.
+ call.handoverTo(TestUtils.TEST_SELF_MANAGED_HANDLE_1, VideoProfile.STATE_BIDIRECTIONAL,
+ null);
+
+ // Expect the handover failed callback to be called.
+ mOnHandoverFailedCounter.waitForCount(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ Call callbackCall = (Call) mOnHandoverFailedCounter.getArgs(0)[0];
+ int failureReason = (int) mOnHandoverFailedCounter.getArgs(0)[1];
+ assertEquals(call, callbackCall);
+ assertEquals(Call.Callback.HANDOVER_FAILURE_DEST_NOT_SUPPORTED, failureReason);
+
+ call.disconnect();
+ }
+
+ /**
+ * Ensures that when the source and destination both support handover that an outgoing handover
+ * request will be successfully relayed.
+ */
+ public void testOutgoingHandoverRequestValid() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ // Begin our source call on the CS which supports handover from it.
+ startSourceCall();
+ final Call call = mInCallCallbacks.getService().getLastCall();
+
+ // Now try to handover to an account which does support handover to it.
+ call.handoverTo(TestUtils.TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE,
+ VideoProfile.STATE_BIDIRECTIONAL, null);
+
+ // Ensure Telecom bound to the self managed CS
+ if (!CtsSelfManagedConnectionService.waitForBinding()) {
+ fail("Could not bind to Self-Managed ConnectionService");
+ }
+
+ // Wait for binding to self managed CS and invocation of outgoing handover method.
+ TestUtils.InvokeCounter counter =
+ CtsSelfManagedConnectionService.getConnectionService()
+ .getOnCreateOutgoingHandoverConnectionCounter();
+ counter.waitForCount(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+
+ SelfManagedConnection connection = TestUtils.waitForAndGetConnection(
+ call.getDetails().getHandle());
+
+ // Verify the from handle is as expected.
+ PhoneAccountHandle fromHandle = (PhoneAccountHandle) counter.getArgs(0)[0];
+ assertEquals(TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE, fromHandle);
+ // Verify the to handle is as expected.
+ ConnectionRequest request = (ConnectionRequest) counter.getArgs(0)[1];
+ assertEquals(TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE, request.getAccountHandle());
+
+ completeHandoverAndVerify(call, connection);
+ }
+
+ /**
+ * Tests use of the
+ * {@link android.telecom.TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)} API on
+ * the receiving side of the handover.
+ */
+ public void testIncomingHandoverRequestValid() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ // Begin our source call on the CS which supports handover from it.
+ startSourceCall();
+ final Call call = mInCallCallbacks.getService().getLastCall();
+
+ // Request to accept handover of that call to another app.
+ mTelecomManager.acceptHandover(call.getDetails().getHandle(),
+ VideoProfile.STATE_BIDIRECTIONAL, TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE);
+
+ // Ensure Telecom bound to the self managed CS
+ if (!CtsSelfManagedConnectionService.waitForBinding()) {
+ fail("Could not bind to Self-Managed ConnectionService");
+ }
+
+ // Wait for binding to self managed CS and invocation of incoming handover method.
+ TestUtils.InvokeCounter counter =
+ CtsSelfManagedConnectionService.getConnectionService()
+ .getOnCreateIncomingHandoverConnectionCounter();
+ counter.waitForCount(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+
+ SelfManagedConnection connection = TestUtils.waitForAndGetConnection(
+ call.getDetails().getHandle());
+
+ // Verify the from handle is as expected.
+ PhoneAccountHandle fromHandle = (PhoneAccountHandle) counter.getArgs(0)[0];
+ assertEquals(TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE, fromHandle);
+ // Verify the to handle is as expected.
+ ConnectionRequest request = (ConnectionRequest) counter.getArgs(0)[1];
+ assertEquals(TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE, request.getAccountHandle());
+ // The original call's address should match the address of the handover request.
+ assertEquals(call.getDetails().getHandle(), request.getAddress());
+
+ completeHandoverAndVerify(call, connection);
+ }
+
+ /**
+ * Begins a call which will be the source of a handover.
+ */
+ private void startSourceCall() {
+ // Ensure the ongoing account is on a PhoneAccount which supports handover from.
+ Bundle extras = new Bundle();
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+ TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE);
+ placeAndVerifyCall(extras);
+ }
+
+ /**
+ * Complete the a call handover and verify that it was successfully reported.
+ * @param call
+ * @param connection
+ */
+ private void completeHandoverAndVerify(final Call call, SelfManagedConnection connection) {
+ // Make the connection active, indicating that the user has accepted the handover.
+ connection.setActive();
+
+ // Expect the original call to have been informed of handover completion.
+ mOnHandoverCompleteCounter.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ assertEquals(1, mOnHandoverCompleteCounter.getInvokeCount());
+
+ // Also expect the connection to be informed of handover completion.
+ connection.getHandoverCompleteCounter().waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ assertEquals(1, connection.getHandoverCompleteCounter().getInvokeCount());
+
+ // Now, we expect that the original connection will get disconnected.
+ waitUntilConditionIsTrueOrTimeout(new Condition() {
+ @Override
+ public Object expected() {
+ return Call.STATE_DISCONNECTED;
+ }
+ @Override
+ public Object actual() {
+ return call.getState();
+ }
+ },
+ WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Expected original call to be disconnected."
+ );
+ }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
index e13335e..338a225 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
@@ -65,6 +65,8 @@
public void onRttStatusChanged(Call call, boolean enabled, Call.RttCall rttCall) {}
public void onRttRequest(Call call, int id) {}
public void onRttInitiationFailure(Call call, int reason) {}
+ public void onHandoverComplete(Call call) {}
+ public void onHandoverFailed(Call call, int failureReason) {}
final public MockInCallService getService() {
return mService;
@@ -189,6 +191,22 @@
getCallbacks().onRttInitiationFailure(call, reason);
}
}
+
+ @Override
+ public void onHandoverComplete(Call call) {
+ super.onHandoverComplete(call);
+ if (getCallbacks() != null) {
+ getCallbacks().onHandoverComplete(call);
+ }
+ }
+
+ @Override
+ public void onHandoverFailed(Call call, int failureReason) {
+ super.onHandoverFailed(call, failureReason);
+ if (getCallbacks() != null) {
+ getCallbacks().onHandoverFailed(call, failureReason);
+ }
+ }
};
private void saveVideoCall(Call call, VideoCall videoCall) {
diff --git a/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnection.java b/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnection.java
index f9a3d91..e53d789 100644
--- a/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnection.java
+++ b/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnection.java
@@ -16,6 +16,7 @@
package android.telecom.cts;
+import android.os.Bundle;
import android.telecom.CallAudioState;
import android.telecom.Connection;
import android.telecom.DisconnectCause;
@@ -31,6 +32,8 @@
InvokeCounter mCallAudioRouteInvokeCounter = new InvokeCounter("onCallAudioStateChanged");
InvokeCounter mOnShowIncomingUiInvokeCounter = new InvokeCounter(
"onShowIncomingUiInvokeCounter");
+ InvokeCounter mCallEventCounter = new InvokeCounter("onCallEvent");
+ InvokeCounter mHandoverCompleteCounter = new InvokeCounter("handoverCompleteCounter");
CountDownLatch mOnHoldLatch = new CountDownLatch(1);
public static abstract class Listener {
@@ -70,6 +73,16 @@
mOnHoldLatch.countDown();
}
+ @Override
+ public void onCallEvent(String event, Bundle extras) {
+ mCallEventCounter.invoke(event, extras);
+ }
+
+ @Override
+ public void onHandoverComplete() {
+ mHandoverCompleteCounter.invoke();
+ }
+
public InvokeCounter getCallAudioStateChangedInvokeCounter() {
return mCallAudioRouteInvokeCounter;
}
@@ -78,6 +91,14 @@
return mOnShowIncomingUiInvokeCounter;
}
+ public InvokeCounter getCallEventCounter() {
+ return mCallEventCounter;
+ }
+
+ public InvokeCounter getHandoverCompleteCounter() {
+ return mHandoverCompleteCounter;
+ }
+
public boolean waitOnHold() {
mOnHoldLatch = TestUtils.waitForLock(mOnHoldLatch);
return mOnHoldLatch != null;
diff --git a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
index ac717d7..71ce645 100644
--- a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
+++ b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
@@ -63,6 +63,11 @@
public static final String ACCOUNT_ID = "xtstest_CALL_PROVIDER_ID";
public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE =
new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID);
+ public static final PhoneAccountHandle TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE =
+ new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), "handoverFrom");
+ public static final PhoneAccountHandle TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE =
+ new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT),
+ "handoverTo");
public static final String REMOTE_ACCOUNT_ID = "xtstest_REMOTE_CALL_PROVIDER_ID";
public static final String SELF_MANAGED_ACCOUNT_ID_1 = "ctstest_SELF_MANAGED_ID_1";
public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_1 =
@@ -87,6 +92,34 @@
.addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
.addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
.build();
+ private static final Bundle SUPPORTS_HANDOVER_FROM_EXTRAS = new Bundle();
+ private static final Bundle SUPPORTS_HANDOVER_TO_EXTRAS = new Bundle();
+ static {
+ SUPPORTS_HANDOVER_FROM_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_FROM, true);
+ SUPPORTS_HANDOVER_TO_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_TO, true);
+ }
+ public static final PhoneAccount TEST_PHONE_ACCOUNT_HANDOVER_SRC = PhoneAccount.builder(
+ TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL)
+ .setAddress(Uri.parse("tel:555-TEST"))
+ .setExtras(SUPPORTS_HANDOVER_FROM_EXTRAS)
+ .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
+ .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+ .setHighlightColor(Color.BLUE)
+ .setShortDescription(ACCOUNT_LABEL)
+ .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
+ .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
+ .build();
+ public static final PhoneAccount TEST_PHONE_ACCOUNT_HANDOVER_DEST = PhoneAccount.builder(
+ TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL)
+ .setAddress(Uri.parse("tel:555-TEST"))
+ .setExtras(SUPPORTS_HANDOVER_TO_EXTRAS)
+ .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
+ .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
+ .setHighlightColor(Color.MAGENTA)
+ .setShortDescription(ACCOUNT_LABEL)
+ .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
+ .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
+ .build();
public static final String REMOTE_ACCOUNT_LABEL = "CTSRemoteConnectionService";
public static final String SELF_MANAGED_ACCOUNT_LABEL = "android.telecom.cts";
public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_2 = PhoneAccount.builder(
diff --git a/tests/tests/telephony/src/android/telephony/cts/CellInfoTest.java b/tests/tests/telephony/src/android/telephony/cts/CellInfoTest.java
index a074783..6b68a4a 100644
--- a/tests/tests/telephony/src/android/telephony/cts/CellInfoTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/CellInfoTest.java
@@ -110,6 +110,8 @@
}
private void verifyCdmaInfo(CellInfoCdma cdma) {
+ verifyCellConnectionStatus(cdma.getCellConnectionStatus());
+
String alphaLong = (String) cdma.getCellIdentity().getOperatorAlphaLong();
assertNotNull("getOperatorAlphaLong() returns NULL!", alphaLong);
String alphaShort = (String) cdma.getCellIdentity().getOperatorAlphaShort();
@@ -128,11 +130,17 @@
// Physical cell id should be within [0, 503].
assertTrue("getPci() out of range [0, 503], pci=" + pci, pci >= 0 && pci <= 503);
+ verifyCellConnectionStatus(lte.getCellConnectionStatus());
+
String alphaLong = (String) lte.getCellIdentity().getOperatorAlphaLong();
assertNotNull("getOperatorAlphaLong() returns NULL!", alphaLong);
String alphaShort = (String) lte.getCellIdentity().getOperatorAlphaShort();
assertNotNull("getOperatorAlphaShort() returns NULL!", alphaShort);
+ int bw = lte.getCellIdentity().getBandwidth();
+ assertTrue("getBandwidth out of range [1400, 20000] | Integer.Max_Value, bw=",
+ bw == Integer.MAX_VALUE || bw >= 1400 && bw <= 20000);
+
String mccStr = lte.getCellIdentity().getMccStr();
// mccStr is set as NULL if empty, unknown or invalid.
assertTrue("getMccStr() out of range [0, 999], mcc=" + mccStr,
@@ -185,6 +193,8 @@
int psc = wcdma.getCellIdentity().getPsc();
assertTrue("getPsc() out of range [0, 511], psc=" + psc, psc >= 0 && psc <= 511);
+ verifyCellConnectionStatus(wcdma.getCellConnectionStatus());
+
int uarfcn = wcdma.getCellIdentity().getUarfcn();
// Reference 3GPP 25.101 Table 5.2
assertTrue("getUarfcn() out of range [400,11000], uarfcn=" + uarfcn,
@@ -229,6 +239,8 @@
assertTrue("getCid() out range [0, 65535], cid=" + cid, !gsm.isRegistered() ||
cid >= 0 && cid <= 65535);
+ verifyCellConnectionStatus(gsm.getCellConnectionStatus());
+
int arfcn = gsm.getCellIdentity().getArfcn();
// Reference 3GPP 45.005 Table 2-2
assertTrue("getArfcn() out of range [0,1024], arfcn=" + arfcn,
@@ -274,4 +286,12 @@
assertTrue("getCellSignalStrength().getDbm() out of range, dbm=" + dbm,
dbm >= MIN_RSSI && dbm <= MAX_RSSI);
}
+
+ private void verifyCellConnectionStatus(int status) {
+ assertTrue("getCellConnectionStatus() invalid [0,2] | Integer.MAX_VALUE, status=",
+ status == CellInfo.CONNECTION_NONE
+ || status == CellInfo.CONNECTION_PRIMARY_SERVING
+ || status == CellInfo.CONNECTION_SECONDARY_SERVING
+ || status == CellInfo.CONNECTION_UNKNOWN);
+ }
}
diff --git a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
index 51e6102..7bbc08a 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
@@ -41,6 +41,7 @@
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -60,11 +61,13 @@
import android.util.Xml;
import android.view.ActionMode;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.KeyEvent;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
@@ -413,6 +416,53 @@
}
@Test
+ public void testSelectorOnScreen() throws Throwable {
+ // leave touch-mode
+ mInstrumentation.setInTouchMode(false);
+ setAdapter();
+ mInstrumentation.waitForIdleSync();
+ // Entering touch mode hides selector
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)) {
+ // make sure we've left touchmode (including message sending. instrumentation just sets
+ // a variable without any broadcast).
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
+ mActivityRule.runOnUiThread(() -> {
+ mListView.requestFocus();
+ mListView.setSelectionFromTop(1, 0);
+ });
+ mInstrumentation.waitForIdleSync();
+ assertEquals(1, mListView.getSelectedItemPosition());
+ final int[] pt = new int[2];
+ mListView.getLocationOnScreen(pt);
+ CtsTouchUtils.emulateDragGesture(mInstrumentation, pt[0] + 2, pt[1] + 2, 0, 10);
+ mInstrumentation.waitForIdleSync();
+ assertEquals(AdapterView.INVALID_POSITION, mListView.getSelectedItemPosition());
+ // leave touch-mode
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
+ }
+
+ // Scroll off-screen hides selector, but shows up again when on-screen
+ mActivityRule.runOnUiThread(() -> {
+ mListView.requestFocus();
+ mListView.setSelectionFromTop(1, 0);
+ });
+ mInstrumentation.waitForIdleSync();
+ assertEquals(1, mListView.getSelectedItemPosition());
+ int selViewHeight = mListView.getSelectedView().getHeight();
+ final int[] pt = new int[2];
+ mListView.getLocationOnScreen(pt);
+ pt[0] += mListView.getWidth() / 2;
+ pt[1] += selViewHeight / 2;
+ mActivityRule.runOnUiThread(() -> mListView.scrollListBy(selViewHeight * 2));
+ mInstrumentation.waitForIdleSync();
+ assertEquals(1, mListView.getSelectedItemPosition());
+ assertFalse(mListView.shouldDrawSelector());
+ mActivityRule.runOnUiThread(() -> mListView.scrollListBy(-(selViewHeight * 4) / 3));
+ assertEquals(1, mListView.getSelectedItemPosition());
+ assertTrue(mListView.shouldDrawSelector());
+ }
+
+ @Test
public void testSetScrollIndicators() throws Throwable {
final Activity activity = mActivityRule.getActivity();
TextView tv1 = (TextView) activity.findViewById(R.id.headerview1);
diff --git a/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java b/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
index 01917d3..cfa369a 100644
--- a/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
+++ b/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
@@ -524,6 +524,7 @@
charsKeyNames.add(CameraCharacteristics.SYNC_MAX_LATENCY.getName());
charsKeyNames.add(CameraCharacteristics.REPROCESS_MAX_CAPTURE_STALL.getName());
charsKeyNames.add(CameraCharacteristics.DEPTH_DEPTH_IS_EXCLUSIVE.getName());
+ charsKeyNames.add(CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE.getName());
return charsKeyNames;
}
diff --git a/tools/cts-tradefed/res/config/cts-suite.xml b/tools/cts-tradefed/res/config/cts-suite.xml
index f2672ec..9f50292 100644
--- a/tools/cts-tradefed/res/config/cts-suite.xml
+++ b/tools/cts-tradefed/res/config/cts-suite.xml
@@ -16,7 +16,9 @@
<configuration description="Runs CTS as a suite">
<!-- running config -->
<build_provider class="com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider" />
- <test class="com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite" />
+ <test class="com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite">
+ <option name="run-suite-tag" value="cts" />
+ </test>
<option name="compatibility:test-arg" value="com.android.tradefed.testtype.AndroidJUnitTest:rerun-from-file:true" />
<option name="compatibility:test-arg" value="com.android.tradefed.testtype.AndroidJUnitTest:fallback-to-serial-rerun:false" />