Merge \"CTS: Replace deviceSerial with startTime as MetricsStore key.\" into nyc-dev
am: 6a2a6975fe
Change-Id: I93ce754df192136790ef098309dc9826890bbb46
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index 301ea73..52780eb 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -51,7 +51,8 @@
# Get all the scene0 and scene1 tests, which can be run using the same
# physical setup.
- scenes = ["scene0", "scene1", "scene2", "scene3", "scene4", "scene5"]
+ scenes = ["scene0", "scene1", "scene2", "scene3", "scene4", "scene5",
+ "sensor_fusion"]
scene_req = {
"scene0" : None,
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index a52ea7a..304c982 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -42,7 +42,9 @@
LOCAL_PACKAGE_NAME := CtsVerifier
-LOCAL_JNI_SHARED_LIBRARIES := libctsverifier_jni libaudioloopback_jni
+LOCAL_JNI_SHARED_LIBRARIES := libctsverifier_jni \
+ libaudioloopback_jni \
+ libnativehelper_compat_libc++
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
diff --git a/apps/CtsVerifier/jni/verifier/Android.mk b/apps/CtsVerifier/jni/verifier/Android.mk
index 4840e62..2350085 100644
--- a/apps/CtsVerifier/jni/verifier/Android.mk
+++ b/apps/CtsVerifier/jni/verifier/Android.mk
@@ -21,8 +21,6 @@
LOCAL_MODULE_TAGS := optional
-
-
LOCAL_SRC_FILES := \
CtsVerifierJniOnLoad.cpp \
com_android_cts_verifier_camera_StatsImage.cpp \
@@ -30,6 +28,10 @@
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_CXX_STL := libc++_static
+
+LOCAL_SHARED_LIBRARIES := liblog \
+ libnativehelper_compat_libc++
include $(BUILD_SHARED_LIBRARY)
+
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 52da8c4..ef77c84 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2217,8 +2217,7 @@
<string name="device_owner_disallow_config_wifi_info">
Please press the Set restriction button to set the user restriction.
Then press Go to open the WiFi page in Settings.
- Confirm that:\n
- \n
+ Confirm that:\n\n
- You cannot view WiFi networks in range.\n
- Trying to edit, add or remove any existing WiFi configs triggers a support message.\n
\n
@@ -2229,13 +2228,24 @@
Device should have a sim card to perform this test.
Please press the Set restriction button to set the user restriction.
Then press Go to open the Cellular network page in Settings.
- Confirm that:\n
- \n
+ Confirm that:\n\n
- Data roaming is disabled.\n
- - Enabling data roaming is not possible and triggers a support message.\n
- \n
+ - Enabling data roaming is not possible and triggers a support message.\n\n
Use the Back button to return to this page.
</string>
+ <string name="device_owner_disallow_factory_reset">Disallow factory reset</string>
+ <string name="device_owner_disallow_factory_reset_info">
+ Please press the Set button to set the user restriction.\n
+ 1. Go to the factory reset settings. It is often located in \"Backup & reset\" settings.\n
+ Confirm that:\n
+ - Factory data reset is disabled.\n
+ - Pressing factory data reset is not possible and triggers a support message.\n\n
+ 2. Go to OEM unlocking settings, if this device has this Settings option. It is often located under \"Developer options\".\n
+ Confirm that:\n
+ - Oem Unlocking is disabled.\n
+ - Enabling Oem unlocking is not possible and triggers a support message.\n\n
+ Return back to this page.
+ </string>
<string name="device_owner_user_restriction_set">Set restriction</string>
<string name="device_owner_settings_go">Go</string>
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 724f03d..024854c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -68,6 +68,7 @@
private static final String DISALLOW_USB_FILE_TRANSFER_ID = "DISALLOW_USB_FILE_TRANSFER";
private static final String SET_USER_ICON_TEST_ID = "SET_USER_ICON";
private static final String DISALLOW_DATA_ROAMING_ID = "DISALLOW_DATA_ROAMING";
+ private static final String DISALLOW_FACTORY_RESET_ID = "DISALLOW_FACTORY_RESET";
private static final String POLICY_TRANSPARENCY_TEST_ID = "POLICY_TRANSPARENCY";
private static final String REMOVE_DEVICE_OWNER_TEST_ID = "REMOVE_DEVICE_OWNER";
@@ -201,6 +202,16 @@
new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))}));
}
+ // DISALLOW_FACTORY_RESET
+ adapter.add(createInteractiveTestItem(this, DISALLOW_FACTORY_RESET_ID,
+ R.string.device_owner_disallow_factory_reset,
+ R.string.device_owner_disallow_factory_reset_info,
+ new ButtonInfo[] {
+ new ButtonInfo(
+ R.string.device_owner_user_restriction_set,
+ createSetUserRestrictionIntent(
+ UserManager.DISALLOW_FACTORY_RESET))}));
+
// DISALLOW_CONFIG_BLUETOOTH
if (packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
adapter.add(createInteractiveTestItem(this, DISALLOW_CONFIG_BT_ID,
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
index 554f53b..b467aed 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
@@ -16,6 +16,10 @@
package com.android.cts.devicepolicy;
+import com.android.tradefed.log.LogUtil.CLog;
+
+import java.lang.AssertionError;
+
/**
* Set of tests for managed profile owner use cases that also apply to device owners.
* Tests that should be run identically in both cases are added in DeviceAndProfileOwnerTest.
@@ -69,13 +73,29 @@
if (!mHasFeature) {
return;
}
- executeDeviceTestMethod(".ScreenCaptureDisabledTest", "testSetScreenCaptureDisabled_true");
- // start the ScreenCaptureDisabledActivity in the parent
- installAppAsUser(DEVICE_ADMIN_APK, mParentUserId);
- String command = "am start -W --user " + mParentUserId + " " + DEVICE_ADMIN_PKG + "/"
- + DEVICE_ADMIN_PKG + ".ScreenCaptureDisabledActivity";
- getDevice().executeShellCommand(command);
- executeDeviceTestMethod(".ScreenCaptureDisabledTest", "testScreenCapturePossible");
+ runDumpsysWindow();
+ try {
+ executeDeviceTestMethod(".ScreenCaptureDisabledTest",
+ "testSetScreenCaptureDisabled_true");
+ // start the ScreenCaptureDisabledActivity in the parent
+ installAppAsUser(DEVICE_ADMIN_APK, mParentUserId);
+ String command = "am start -W --user " + mParentUserId + " " + DEVICE_ADMIN_PKG + "/"
+ + DEVICE_ADMIN_PKG + ".ScreenCaptureDisabledActivity";
+ getDevice().executeShellCommand(command);
+ executeDeviceTestMethod(".ScreenCaptureDisabledTest", "testScreenCapturePossible");
+ } catch (AssertionError e) {
+ runDumpsysWindow();
+ CLog.e("testScreenCaptureDisabled_allowedPrimaryUser failed", e);
+ fail("testScreenCaptureDisabled_allowedPrimaryUser failed");
+ }
+ }
+
+ // TODO: Remove this after investigation in b/28995242 is done
+ private void runDumpsysWindow() throws Exception {
+ String command = "dumpsys window displays";
+ CLog.d("Output for command " + command + ": " + getDevice().executeShellCommand(command));
+ command = "dumpsys window policy";
+ CLog.d("Output for command " + command + ": " + getDevice().executeShellCommand(command));
}
@Override
diff --git a/hostsidetests/os/src/android/os/cts/OsHostTests.java b/hostsidetests/os/src/android/os/cts/OsHostTests.java
index c7b3cc7..200ef40 100644
--- a/hostsidetests/os/src/android/os/cts/OsHostTests.java
+++ b/hostsidetests/os/src/android/os/cts/OsHostTests.java
@@ -125,8 +125,7 @@
Arrays.asList(hostgroup.split(" ")));
assertEquals(2, allHosts.size());
assertTrue("AllHosts Contains: " + allHosts, allHosts.contains(HOST_EXPLICIT));
- // Disable wildcard test until next API bump
- // assertTrue("AllHosts Contains: " + allHosts, allHosts.contains(HOST_WILDCARD));
+ assertTrue("AllHosts Contains: " + allHosts, allHosts.contains(HOST_WILDCARD));
foundVerifierOutput = true;
break;
}
diff --git a/hostsidetests/services/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
index 5065ed6..3523478 100755
--- a/hostsidetests/services/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
@@ -46,7 +46,7 @@
/>
<activity android:name=".NoRelaunchActivity"
android:resizeableActivity="true"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|fontScale"
android:exported="true"
android:taskAffinity="nobody.but.NoRelaunchActivity"
/>
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
new file mode 100644
index 0000000..e5a121d
--- /dev/null
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
@@ -0,0 +1,93 @@
+/*
+ * 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.server.cts;
+
+public class ActivityManagerConfigChangeTests extends ActivityManagerTestBase {
+
+ private static final String TEST_ACTIVITY_NAME = "TestActivity";
+ private static final String NO_RELAUNCH_ACTIVITY_NAME = "NoRelaunchActivity";
+
+ public void testRotation90Relaunch() throws Exception{
+ // Should relaunch on every rotation and receive no onConfigurationChanged()
+ testRotation(TEST_ACTIVITY_NAME, 1, 1, 0);
+ }
+
+ public void testRotation90NoRelaunch() throws Exception {
+ // Should receive onConfigurationChanged() on every rotation and no relaunch
+ testRotation(NO_RELAUNCH_ACTIVITY_NAME, 1, 0, 1);
+ }
+
+ public void testRotation180Relaunch() throws Exception {
+ // Should receive nothing
+ testRotation(TEST_ACTIVITY_NAME, 2, 0, 0);
+ }
+
+ public void testRotation180NoRelaunch() throws Exception {
+ // Should receive nothing
+ testRotation(NO_RELAUNCH_ACTIVITY_NAME, 2, 0, 0);
+ }
+
+ public void testChangeFontScaleRelaunch() throws Exception {
+ // Should relaunch and receive no onConfigurationChanged()
+ testChangeFontScale(TEST_ACTIVITY_NAME, true);
+ }
+
+ public void testChangeFontScaleNoRelaunch() throws Exception {
+ // Should receive onConfigurationChanged() and no relaunch
+ testChangeFontScale(NO_RELAUNCH_ACTIVITY_NAME, false);
+ }
+
+ private void testRotation(
+ String activityName, int rotationStep, int numRelaunch, int numConfigChange)
+ throws Exception {
+ executeShellCommand(getAmStartCmd(activityName));
+
+ final String[] waitForActivitiesVisible = new String[] {activityName};
+ mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+ mAmWmState.assertContainsStack(
+ "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
+
+ setDeviceRotation(4 - rotationStep);
+ mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+
+ for (int rotation = 0; rotation < 4; rotation += rotationStep) {
+ clearLogcat();
+ setDeviceRotation(rotation);
+ mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+ assertRelaunchOrConfigChanged(activityName, numRelaunch, numConfigChange);
+ }
+ }
+
+ private void testChangeFontScale(
+ String activityName, boolean relaunch) throws Exception {
+ executeShellCommand(getAmStartCmd(activityName));
+ final String[] waitForActivitiesVisible = new String[] {activityName};
+ mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+ mAmWmState.assertContainsStack(
+ "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
+
+ setFontScale(1.0f);
+ mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+
+ for (float fontScale = 0.85f; fontScale <= 1.3f; fontScale += 0.15f) {
+ clearLogcat();
+ setFontScale(fontScale);
+ mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+ assertRelaunchOrConfigChanged(activityName, relaunch ? 1 : 0, relaunch ? 0 : 1);
+ }
+ }
+}
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
index 3a21fda..04b0653 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
@@ -99,6 +99,7 @@
private int mInitialAccelerometerRotation;
private int mUserRotation;
+ private float mFontScale;
@Override
protected void setUp() throws Exception {
@@ -114,6 +115,7 @@
// Store rotation settings.
mInitialAccelerometerRotation = getAccelerometerRotation();
mUserRotation = getUserRotation();
+ mFontScale = getFontScale();
}
@Override
@@ -125,6 +127,7 @@
// Restore rotation settings to the state they were before test.
setAccelerometerRotation(mInitialAccelerometerRotation);
setUserRotation(mUserRotation);
+ setFontScale(mFontScale);
// Remove special stacks.
executeShellCommand(AM_REMOVE_STACK + PINNED_STACK_ID);
executeShellCommand(AM_REMOVE_STACK + DOCKED_STACK_ID);
@@ -319,6 +322,22 @@
}
}
+ protected void setFontScale(float fontScale) throws DeviceNotAvailableException {
+ if (fontScale == 0.0f) {
+ runCommandAndPrintOutput(
+ "settings delete system font_scale");
+ } else {
+ runCommandAndPrintOutput(
+ "settings put system font_scale " + fontScale);
+ }
+ }
+
+ protected float getFontScale() throws DeviceNotAvailableException {
+ final String fontScale =
+ runCommandAndPrintOutput("settings get system font_scale").trim();
+ return Float.parseFloat(fontScale);
+ }
+
protected String runCommandAndPrintOutput(String command) throws DeviceNotAvailableException {
final String output = executeShellCommand(command);
log(output);
@@ -359,6 +378,23 @@
}
}
+ protected void assertRelaunchOrConfigChanged(
+ String activityName, int numRelaunch, int numConfigChange)
+ throws DeviceNotAvailableException {
+ final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName);
+
+ if (lifecycleCounts.mDestroyCount != numRelaunch) {
+ fail(activityName + " has been destroyed " + lifecycleCounts.mDestroyCount
+ + " time(s), expecting " + numRelaunch);
+ } else if (lifecycleCounts.mCreateCount != numRelaunch) {
+ fail(activityName + " has been (re)created " + lifecycleCounts.mCreateCount
+ + " time(s), expecting " + numRelaunch);
+ } else if (lifecycleCounts.mConfigurationChangedCount != numConfigChange) {
+ fail(activityName + " has received " + lifecycleCounts.mConfigurationChangedCount
+ + " onConfigurationChanged() calls, expecting " + numConfigChange);
+ }
+ }
+
private String[] getDeviceLogsForActivity(String activityName)
throws DeviceNotAvailableException {
return mDevice.executeAdbCommand("logcat", "-v", "brief", "-d", activityName + ":I", "*:S")
diff --git a/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java b/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
index cb331d1..2b84be6 100644
--- a/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
@@ -57,8 +57,11 @@
expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_MEDIUM, 32);
expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_TV, 32);
expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_HIGH, 36);
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_260, 36);
expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_280, 36);
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_300, 36);
expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_XHIGH, 48);
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_340, 48);
expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_360, 48);
expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_400, 56);
expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_420, 64);
@@ -72,8 +75,11 @@
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_MEDIUM, 32);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_TV, 48);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_HIGH, 48);
+ expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_260, 48);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_280, 48);
+ expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_300, 48);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_XHIGH, 80);
+ expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_340, 80);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_360, 80);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_400, 96);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_420, 112);
@@ -87,8 +93,11 @@
expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_MEDIUM, 64);
expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_TV, 80);
expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_HIGH, 80);
+ expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_260, 96);
expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_280, 96);
+ expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_300, 96);
expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_XHIGH, 128);
+ expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_340, 160);
expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_360, 160);
expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_400, 192);
expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_420, 228);
@@ -102,8 +111,11 @@
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_MEDIUM, 80);
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_TV, 96);
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_HIGH, 96);
+ expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_260, 144);
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_280, 144);
+ expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_300, 144);
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_XHIGH, 192);
+ expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_340, 192);
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_360, 240);
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_400, 288);
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_420, 336);
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index a2dddb5..03ab3cf 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -242,4 +242,8 @@
public void testViewDownloads() {
assertCanBeHandled(new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS));
}
+
+ public void testDeletionHelper() {
+ assertCanBeHandled(new Intent(Settings.ACTION_DELETION_HELPER_SETTINGS));
+ }
}
diff --git a/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java b/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
index fc38fdd..78ce89a 100644
--- a/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
+++ b/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
@@ -59,8 +59,11 @@
allowedDensities.add(DisplayMetrics.DENSITY_MEDIUM);
allowedDensities.add(DisplayMetrics.DENSITY_TV);
allowedDensities.add(DisplayMetrics.DENSITY_HIGH);
+ allowedDensities.add(DisplayMetrics.DENSITY_260);
allowedDensities.add(DisplayMetrics.DENSITY_280);
+ allowedDensities.add(DisplayMetrics.DENSITY_300);
allowedDensities.add(DisplayMetrics.DENSITY_XHIGH);
+ allowedDensities.add(DisplayMetrics.DENSITY_340);
allowedDensities.add(DisplayMetrics.DENSITY_360);
allowedDensities.add(DisplayMetrics.DENSITY_400);
allowedDensities.add(DisplayMetrics.DENSITY_420);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
index 487eb3b..d678e1e 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
@@ -147,9 +147,11 @@
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
+ mActivity.setContentView(mLayoutId);
+ ImageView imageView = (ImageView) mActivity.findViewById(mImageViewId);
+ imageView.setImageDrawable(d1);
d1.start();
d1.stop();
-
}
});
getInstrumentation().waitForIdleSync();
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
index 8c74158..1407cc9 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
@@ -126,7 +126,7 @@
*error_msg = "The library \"" + path + "\" should be accessible but isn't: " + dlerror();
return false;
}
- } else if (handle != nullptr) {
+ } else if (handle.get() != nullptr) {
*error_msg = "The library \"" + path + "\" should not be accessible";
return false;
} else { // (handle == nullptr && !shouldBeAccessible(path))
@@ -178,17 +178,43 @@
return true;
}
-static void jobject_array_to_set(JNIEnv* env,
+static bool jobject_array_to_set(JNIEnv* env,
jobjectArray java_libraries_array,
- std::unordered_set<std::string>* libraries) {
+ std::unordered_set<std::string>* libraries,
+ std::string* error_msg) {
size_t size = env->GetArrayLength(java_libraries_array);
for (size_t i = 0; i<size; ++i) {
ScopedLocalRef<jstring> java_soname(
env, (jstring) env->GetObjectArrayElement(java_libraries_array, i));
+ std::string soname(ScopedUtfChars(env, java_soname.get()).c_str());
- ScopedUtfChars soname(env, java_soname.get());
- libraries->insert(soname.c_str());
+ // Check to see if the string ends in " 32" or " 64" to indicate the
+ // library is only public for one bitness.
+ size_t space_pos = soname.rfind(' ');
+ if (space_pos != std::string::npos) {
+ std::string type = soname.substr(space_pos + 1);
+ if (type != "32" && type != "64") {
+ *error_msg = "public library line is malformed: " + soname;
+ return false;
+ }
+#if defined(__LP64__)
+ if (type == "32") {
+ // Skip this, it's a 32 bit only public library.
+ continue;
+ }
+#else
+ if (type == "64") {
+ // Skip this, it's a 64 bit only public library.
+ continue;
+ }
+#endif
+ soname.resize(space_pos);
+ }
+
+ libraries->insert(soname);
}
+
+ return true;
}
extern "C" JNIEXPORT jstring JNICALL
@@ -202,8 +228,12 @@
std::unordered_set<std::string> vendor_public_libraries;
std::unordered_set<std::string> system_public_libraries;
std::unordered_set<std::string> empty_set;
- jobject_array_to_set(env, java_vendor_public_libraries, &vendor_public_libraries);
- jobject_array_to_set(env, java_system_public_libraries, &system_public_libraries);
+ if (!jobject_array_to_set(env, java_vendor_public_libraries, &vendor_public_libraries, &error)) {
+ return env->NewStringUTF(("Vendor " + error).c_str());
+ }
+ if (!jobject_array_to_set(env, java_system_public_libraries, &system_public_libraries, &error)) {
+ return env->NewStringUTF(("System " + error).c_str());
+ }
if (!check_libs(kSystemLibraryPath, system_public_libraries, kSystemLibraries, &error) ||
!check_libs(kVendorLibraryPath, vendor_public_libraries, empty_set, &error)) {
diff --git a/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
index e596a91..ff84655 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
@@ -93,6 +93,7 @@
mConnection = (MockConnection) connection;
// Modify the connection object created with local values.
connection.setConnectionCapabilities(CONNECTION_CAPABILITIES);
+ connection.setConnectionProperties(CONNECTION_PROPERTIES);
connection.setCallerDisplayName(
CALLER_DISPLAY_NAME,
CALLER_DISPLAY_NAME_PRESENTATION);
@@ -273,8 +274,7 @@
assertThat(mCall.getDetails().getCallProperties(), is(Integer.class));
- // No public call properties at the moment, so ensure we have 0 as a return.
- assertEquals(0, mCall.getDetails().getCallProperties());
+ assertEquals(CALL_PROPERTIES, mCall.getDetails().getCallProperties());
}
/**
@@ -415,6 +415,214 @@
}
/**
+ * Tests that {@link Connection} extras changes made via {@link Connection#putExtras(Bundle)}
+ * are propagated to the {@link Call} via
+ * {@link android.telecom.Call.Callback#onDetailsChanged(Call, Call.Details)}.
+ */
+ public void testConnectionPutExtras() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ Bundle testBundle = new Bundle();
+ testBundle.putString(TEST_EXTRA_KEY, TEST_SUBJECT);
+ testBundle.putInt(TEST_EXTRA_KEY2, TEST_EXTRA_VALUE);
+ mConnection.putExtras(testBundle);
+ // Wait for the 2nd invocation; setExtras is called in the setup method.
+ mOnExtrasChangedCounter.waitForCount(2, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+
+ Bundle extras = mCall.getDetails().getExtras();
+ assertEquals(2, extras.size());
+ assertTrue(extras.containsKey(TEST_EXTRA_KEY));
+ assertEquals(TEST_SUBJECT, extras.getString(TEST_EXTRA_KEY));
+ assertTrue(extras.containsKey(TEST_EXTRA_KEY2));
+ assertEquals(TEST_EXTRA_VALUE, extras.getInt(TEST_EXTRA_KEY2));
+ }
+
+ /**
+ * Tests that {@link Connection} extras changes made via {@link Connection#removeExtras(List)}
+ * are propagated to the {@link Call} via
+ * {@link android.telecom.Call.Callback#onDetailsChanged(Call, Call.Details)}.
+ */
+ public void testConnectionRemoveExtras() {
+ testConnectionPutExtras();
+
+ mConnection.removeExtras(Arrays.asList(TEST_EXTRA_KEY));
+ verifyRemoveConnectionExtras();
+
+ }
+
+ /**
+ * Tests that {@link Connection} extras changes made via {@link Connection#removeExtras(List)}
+ * are propagated to the {@link Call} via
+ * {@link android.telecom.Call.Callback#onDetailsChanged(Call, Call.Details)}.
+ */
+ public void testConnectionRemoveExtras2() {
+ testConnectionPutExtras();
+
+ mConnection.removeExtras(TEST_EXTRA_KEY);
+ // testConnectionPutExtra will have waited for the 2nd invocation, so wait for the 3rd here.
+ verifyRemoveConnectionExtras();
+ }
+
+ private void verifyRemoveConnectionExtras() {
+ // testConnectionPutExtra will have waited for the 2nd invocation, so wait for the 3rd here.
+ mOnExtrasChangedCounter.waitForCount(3, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+
+ Bundle extras = mCall.getDetails().getExtras();
+ assertEquals(1, extras.size());
+ assertFalse(extras.containsKey(TEST_EXTRA_KEY));
+ assertTrue(extras.containsKey(TEST_EXTRA_KEY2));
+ assertEquals(TEST_EXTRA_VALUE, extras.getInt(TEST_EXTRA_KEY2));
+ }
+
+ /**
+ * Tests that {@link Call} extras changes made via {@link Call#putExtras(Bundle)} are propagated
+ * to {@link Connection#onExtrasChanged(Bundle)}.
+ */
+ public void testCallPutExtras() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ Bundle testBundle = new Bundle();
+ testBundle.putString(TEST_EXTRA_KEY, TEST_SUBJECT);
+ final InvokeCounter counter = mConnection.getInvokeCounter(
+ MockConnection.ON_EXTRAS_CHANGED);
+ mCall.putExtras(testBundle);
+ counter.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ Bundle extras = mConnection.getExtras();
+
+ assertNotNull(extras);
+ assertTrue(extras.containsKey(TEST_EXTRA_KEY));
+ assertEquals(TEST_SUBJECT, extras.getString(TEST_EXTRA_KEY));
+ }
+
+ /**
+ * Tests that {@link Call} extra operations using {@link Call#removeExtras(List)} are propagated
+ * to the {@link Connection} via {@link Connection#onExtrasChanged(Bundle)}.
+ *
+ * This test specifically tests addition and removal of extras values.
+ */
+ public void testCallRemoveExtras() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ final InvokeCounter counter = setupCallExtras();
+ Bundle extras;
+
+ mCall.removeExtras(Arrays.asList(TEST_EXTRA_KEY));
+ counter.waitForCount(2, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ extras = mConnection.getExtras();
+ assertNotNull(extras);
+ assertFalse(extras.containsKey(TEST_EXTRA_KEY));
+ assertTrue(extras.containsKey(TEST_EXTRA_KEY2));
+ assertEquals(TEST_EXTRA_VALUE, extras.getInt(TEST_EXTRA_KEY2));
+ assertTrue(extras.containsKey(TEST_EXTRA_KEY3));
+ assertEquals(TEST_SUBJECT, extras.getString(TEST_EXTRA_KEY3));
+
+ mCall.removeExtras(Arrays.asList(TEST_EXTRA_KEY2, TEST_EXTRA_KEY3));
+ counter.waitForCount(3, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ extras = mConnection.getExtras();
+ assertTrue(extras.isEmpty());
+ }
+
+ /**
+ * Tests that {@link Call} extra operations using {@link Call#removeExtras(String[])} are
+ * propagated to the {@link Connection} via {@link Connection#onExtrasChanged(Bundle)}.
+ *
+ * This test specifically tests addition and removal of extras values.
+ */
+ public void testCallRemoveExtras2() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ final InvokeCounter counter = setupCallExtras();
+ Bundle extras;
+
+ mCall.removeExtras(TEST_EXTRA_KEY);
+ counter.waitForCount(2, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ extras = mConnection.getExtras();
+ assertNotNull(extras);
+ assertFalse(extras.containsKey(TEST_EXTRA_KEY));
+ assertTrue(extras.containsKey(TEST_EXTRA_KEY2));
+ assertEquals(TEST_EXTRA_VALUE, extras.getInt(TEST_EXTRA_KEY2));
+ assertTrue(extras.containsKey(TEST_EXTRA_KEY3));
+ assertEquals(TEST_SUBJECT, extras.getString(TEST_EXTRA_KEY3));
+ }
+
+ private InvokeCounter setupCallExtras() {
+ Bundle testBundle = new Bundle();
+ testBundle.putString(TEST_EXTRA_KEY, TEST_SUBJECT);
+ testBundle.putInt(TEST_EXTRA_KEY2, TEST_EXTRA_VALUE);
+ testBundle.putString(TEST_EXTRA_KEY3, TEST_SUBJECT);
+ final InvokeCounter counter = mConnection.getInvokeCounter(
+ MockConnection.ON_EXTRAS_CHANGED);
+ mCall.putExtras(testBundle);
+ counter.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ Bundle extras = mConnection.getExtras();
+
+ assertNotNull(extras);
+ assertTrue(extras.containsKey(TEST_EXTRA_KEY));
+ assertEquals(TEST_SUBJECT, extras.getString(TEST_EXTRA_KEY));
+ assertTrue(extras.containsKey(TEST_EXTRA_KEY2));
+ assertEquals(TEST_EXTRA_VALUE, extras.getInt(TEST_EXTRA_KEY2));
+ assertTrue(extras.containsKey(TEST_EXTRA_KEY3));
+ assertEquals(TEST_SUBJECT, extras.getString(TEST_EXTRA_KEY3));
+ return counter;
+ }
+
+ /**
+ * Tests that {@link Connection} events are propagated from
+ * {@link Connection#sendConnectionEvent(String, Bundle)} to
+ * {@link android.telecom.Call.Callback#onConnectionEvent(Call, String, Bundle)}.
+ */
+ public void testConnectionEvent() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ Bundle testBundle = new Bundle();
+ testBundle.putString(TEST_EXTRA_KEY, TEST_SUBJECT);
+
+ mConnection.sendConnectionEvent(Connection.EVENT_CALL_PULL_FAILED, testBundle);
+ mOnConnectionEventCounter.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ String event = (String) (mOnConnectionEventCounter.getArgs(0)[1]);
+ Bundle extras = (Bundle) (mOnConnectionEventCounter.getArgs(0)[2]);
+
+ assertEquals(Connection.EVENT_CALL_PULL_FAILED, event);
+ assertNotNull(extras);
+ assertTrue(extras.containsKey(TEST_EXTRA_KEY));
+ assertEquals(TEST_SUBJECT, extras.getString(TEST_EXTRA_KEY));
+ }
+
+ /**
+ * Tests that {@link Call} events are propagated from {@link Call#sendCallEvent(String, Bundle)}
+ * to {@link Connection#onCallEvent(String, Bundle)}.
+ */
+ public void testCallEvent() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ Bundle testBundle = new Bundle();
+ testBundle.putString(TEST_EXTRA_KEY, TEST_SUBJECT);
+ final InvokeCounter counter = mConnection.getInvokeCounter(MockConnection.ON_CALL_EVENT);
+ mCall.sendCallEvent(TEST_EVENT, testBundle);
+ counter.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+
+ String event = (String) (counter.getArgs(0)[0]);
+ Bundle extras = (Bundle) (counter.getArgs(0)[1]);
+
+ assertEquals(TEST_EVENT, event);
+ assertNotNull(extras);
+ assertTrue(extras.containsKey(TEST_EXTRA_KEY));
+ assertEquals(TEST_SUBJECT, extras.getString(TEST_EXTRA_KEY));
+ }
+
+ /**
* Asserts that a call's extras contain a specified key.
*
* @param call The call.
diff --git a/tests/tests/telecom/src/android/telecom/cts/CallTest.java b/tests/tests/telecom/src/android/telecom/cts/CallTest.java
index d63a9e8..b892ede 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CallTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CallTest.java
@@ -32,6 +32,8 @@
| CAPABILITY_DISCONNECT_FROM_CONFERENCE | CAPABILITY_MUTE,
CAPABILITY_MUTE));
assertTrue(Call.Details.can(CAPABILITY_CAN_PAUSE_VIDEO, CAPABILITY_CAN_PAUSE_VIDEO));
+ assertTrue(Call.Details.can(CAPABILITY_CAN_PULL_CALL, CAPABILITY_CAN_PULL_CALL));
+
assertFalse(Call.Details.can(CAPABILITY_MUTE, CAPABILITY_HOLD));
assertFalse(Call.Details.can(CAPABILITY_MERGE_CONFERENCE
| CAPABILITY_DISCONNECT_FROM_CONFERENCE | CAPABILITY_MUTE,
@@ -78,6 +80,8 @@
assertTrue(Call.Details.hasProperty(PROPERTY_HIGH_DEF_AUDIO, PROPERTY_HIGH_DEF_AUDIO));
assertTrue(Call.Details.hasProperty(PROPERTY_HIGH_DEF_AUDIO | PROPERTY_CONFERENCE
| PROPERTY_WIFI, PROPERTY_CONFERENCE));
+ assertTrue(Call.Details.hasProperty(PROPERTY_IS_EXTERNAL_CALL, PROPERTY_IS_EXTERNAL_CALL));
+
assertFalse(Call.Details.hasProperty(PROPERTY_WIFI, PROPERTY_CONFERENCE));
assertFalse(Call.Details.hasProperty(PROPERTY_HIGH_DEF_AUDIO | PROPERTY_CONFERENCE
| PROPERTY_WIFI, PROPERTY_GENERIC_CONFERENCE));
diff --git a/tests/tests/telecom/src/android/telecom/cts/ConferenceTest.java b/tests/tests/telecom/src/android/telecom/cts/ConferenceTest.java
index 913ca82..c6c01f1 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ConferenceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ConferenceTest.java
@@ -222,6 +222,118 @@
assertCallState(conf, Call.STATE_DISCONNECTED);
}
+ /**
+ * Tests end to end propagation of the {@link Conference} properties to the associated
+ * {@link Call}.
+ */
+ public void testConferenceProperties() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+ final Call conf = mInCallService.getLastConferenceCall();
+ assertCallState(conf, Call.STATE_ACTIVE);
+
+ int properties = mConferenceObject.getConnectionProperties();
+ properties |= Connection.PROPERTY_IS_EXTERNAL_CALL;
+
+ mConferenceObject.setConnectionProperties(properties);
+
+ // Wait for 2nd properties change; the first will be when the conference is marked with
+ // Call.Details.PROPERTY_CONFERENCE.
+ assertCallProperties(conf, Call.Details.PROPERTY_IS_EXTERNAL_CALL);
+ assertTrue(conf.getDetails().hasProperty(Call.Details.PROPERTY_IS_EXTERNAL_CALL));
+ }
+
+ /**
+ * Verifies {@link Conference#putExtras(Bundle)} calls are propagated to
+ * {@link android.telecom.Call.Callback#onDetailsChanged(Call, Call.Details)}.
+ */
+ public void testConferencePutExtras() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+ final Call conf = mInCallService.getLastConferenceCall();
+ assertCallState(conf, Call.STATE_ACTIVE);
+
+ Bundle extras = new Bundle();
+ extras.putString(TEST_EXTRA_KEY_1, TEST_EXTRA_VALUE_1);
+ extras.putInt(TEST_EXTRA_KEY_2, TEST_EXTRA_VALUE_2);
+ mConferenceObject.putExtras(extras);
+
+ mOnExtrasChangedCounter.waitForCount(1);
+
+ assertTrue(areBundlesEqual(extras, conf.getDetails().getExtras()));
+ }
+
+ /**
+ * Verifies {@link Conference#removeExtras(List)} calls are propagated to
+ * {@link android.telecom.Call.Callback#onDetailsChanged(Call, Call.Details)}.
+ */
+ public void testConferenceRemoveExtras() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+ final Call conf = mInCallService.getLastConferenceCall();
+ assertCallState(conf, Call.STATE_ACTIVE);
+
+ setupExtras();
+
+ mConferenceObject.removeExtras(Arrays.asList(TEST_EXTRA_KEY_1));
+ mOnExtrasChangedCounter.waitForCount(2);
+ Bundle extras = mConferenceObject.getExtras();
+
+ assertFalse(extras.containsKey(TEST_EXTRA_KEY_1));
+ assertTrue(extras.containsKey(TEST_EXTRA_KEY_2));
+ }
+
+ /**
+ * Verifies {@link Conference#removeExtras(String[])} calls are propagated to
+ * {@link android.telecom.Call.Callback#onDetailsChanged(Call, Call.Details)}.
+ */
+ public void testConferenceRemoveExtras2() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+ final Call conf = mInCallService.getLastConferenceCall();
+ assertCallState(conf, Call.STATE_ACTIVE);
+
+ setupExtras();
+
+ mConferenceObject.removeExtras(TEST_EXTRA_KEY_1, TEST_EXTRA_KEY_2);
+ mOnExtrasChangedCounter.waitForCount(2);
+ Bundle extras = mConferenceObject.getExtras();
+
+ assertNull(extras);
+ }
+
+ private void setupExtras() {
+ Bundle extras = new Bundle();
+ extras.putString(TEST_EXTRA_KEY_1, TEST_EXTRA_VALUE_1);
+ extras.putInt(TEST_EXTRA_KEY_2, TEST_EXTRA_VALUE_2);
+ mConferenceObject.putExtras(extras);
+ mOnExtrasChangedCounter.waitForCount(1);
+ }
+
+ /**
+ * Verifies {@link android.telecom.Call#putExtras(Bundle)} changes are propagated to
+ * {@link Conference#onExtrasChanged(Bundle)}.
+ */
+ public void testConferenceOnExtraschanged() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+ final Call conf = mInCallService.getLastConferenceCall();
+ assertCallState(conf, Call.STATE_ACTIVE);
+
+ Bundle extras = new Bundle();
+ extras.putString(TEST_EXTRA_KEY_1, TEST_EXTRA_VALUE_1);
+ extras.putInt(TEST_EXTRA_KEY_2, TEST_EXTRA_VALUE_2);
+ conf.putExtras(extras);
+ mConferenceObject.mOnExtrasChanged.waitForCount(1);
+
+ assertTrue(areBundlesEqual(extras, mConferenceObject.getExtras()));
+ }
+
public void testConferenceAddAndRemoveConnection() {
if (!mShouldTestTelecom) {
return;
diff --git a/tests/tests/telecom/src/android/telecom/cts/ConnectionTest.java b/tests/tests/telecom/src/android/telecom/cts/ConnectionTest.java
index 58dfcdb..874f116 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ConnectionTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ConnectionTest.java
@@ -164,6 +164,22 @@
assertEquals(capabilities, connection.getConnectionCapabilities());
}
+ public void testSetAndGetConnectionProperties() {
+ if (!shouldTestTelecom(getContext())) {
+ return;
+ }
+
+ final Semaphore lock = new Semaphore(0);
+ Connection connection = createConnection(lock);
+ waitForStateChange(lock);
+
+ final int properties = Connection.PROPERTY_IS_EXTERNAL_CALL;
+
+ connection.setConnectionProperties(properties);
+
+ assertEquals(properties, connection.getConnectionProperties());
+ }
+
public void testSetAndGetDisconnectCause() {
if (!shouldTestTelecom(getContext())) {
return;
@@ -217,6 +233,79 @@
assertTrue(extras.getBoolean("test-extra-key"));
}
+ /**
+ * Basic local test of adding extra keys via {@link Connection#removeExtras(List)}.
+ *
+ * Extended end-to-end passing of extras is verified in
+ * {@link CallDetailsTest#testConnectionPutExtras()} and
+ * @link CallDetailsTest#testConnectionRemoveExtras()}.
+ */
+ public void testPutExtras() {
+ if (!shouldTestTelecom(getContext())) {
+ return;
+ }
+
+ final Semaphore lock = new Semaphore(0);
+ Connection connection = createConnection(lock);
+ waitForStateChange(lock);
+
+ assertEquals(null, connection.getExtras());
+
+ final Bundle extras = new Bundle();
+ extras.putBoolean("test-extra-key", true);
+ connection.putExtras(extras);
+
+ final Bundle retrieved = connection.getExtras();
+ assertNotNull(retrieved);
+ assertTrue(extras.getBoolean("test-extra-key"));
+ }
+
+ /**
+ * Basic local test of removing extra keys via {@link Connection#removeExtras(List)}.
+ *
+ * Extended end-to-end passing of extras is verified in
+ * {@link CallDetailsTest#testConnectionPutExtras()} and
+ * @link CallDetailsTest#testConnectionRemoveExtras()}.
+ */
+ public void testRemoveExtras() {
+ if (!shouldTestTelecom(getContext())) {
+ return;
+ }
+
+ final Semaphore lock = new Semaphore(0);
+ Connection connection = createConnection(lock);
+ waitForStateChange(lock);
+
+ assertEquals(null, connection.getExtras());
+
+ final Bundle extras = new Bundle();
+ extras.putBoolean("test-extra-key", true);
+ connection.putExtras(extras);
+ connection.removeExtras(Arrays.asList("test-extra-key"));
+
+ final Bundle retrieved = connection.getExtras();
+ assertNotNull(retrieved);
+ assertFalse(extras.containsKey("test-extra-key"));
+ }
+
+ /**
+ * Tests that the {@link Connection#sendConnectionEvent(String, Bundle)} method exists and can
+ * be called.
+ *
+ * Actual end-to-end tests can be found in {@link CallDetailsTest#testConnectionEvent()}.
+ */
+ public void testSendConnectionEvent() {
+ if (!shouldTestTelecom(getContext())) {
+ return;
+ }
+
+ final Semaphore lock = new Semaphore(0);
+ Connection connection = createConnection(lock);
+ waitForStateChange(lock);
+
+ connection.sendConnectionEvent("test", null);
+ }
+
public void testSetAndGetStatusHints() {
if (!shouldTestTelecom(getContext())) {
return;
@@ -295,6 +384,18 @@
| Connection.CAPABILITY_MANAGE_CONFERENCE));
}
+ /**
+ * Tests the {@link Connection#propertiesToString(int)} method.
+ */
+ public void testPropertiesToString() {
+ if (!shouldTestTelecom(getContext())) {
+ return;
+ }
+
+ assertEquals("[Properties: PROPERTY_IS_EXTERNAL_CALL]",
+ Connection.propertiesToString(Connection.PROPERTY_IS_EXTERNAL_CALL));
+ }
+
private static Connection createConnection(final Semaphore lock) {
BasicConnection connection = new BasicConnection();
connection.setLock(lock);
diff --git a/tests/tests/telecom/src/android/telecom/cts/ExternalCallTest.java b/tests/tests/telecom/src/android/telecom/cts/ExternalCallTest.java
new file mode 100644
index 0000000..b50e5cc
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/ExternalCallTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.telecom.cts;
+
+import android.telecom.Call;
+import android.telecom.Connection;
+import android.telecom.ConnectionRequest;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+
+import static android.telecom.cts.TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS;
+
+/**
+ * Tests which verify functionality related to {@link android.telecom.Connection}s and
+ * {@link android.telecom.Call}s with the
+ * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} and
+ * {@link android.telecom.Call.Details#PROPERTY_IS_EXTERNAL_CALL} properties, respectively, set.
+ */
+public class ExternalCallTest extends BaseTelecomTestWithMockServices {
+ public static final int CONNECTION_PROPERTIES = Connection.PROPERTY_IS_EXTERNAL_CALL;
+ public static final int CONNECTION_CAPABILITIES = Connection.CAPABILITY_CAN_PULL_CALL;
+
+ private Call mCall;
+ private MockConnection mConnection;
+ private MockInCallService mInCallService;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (mShouldTestTelecom) {
+ PhoneAccount account = setupConnectionService(
+ new MockConnectionService() {
+ @Override
+ public Connection onCreateOutgoingConnection(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ Connection connection = super.onCreateOutgoingConnection(
+ connectionManagerPhoneAccount,
+ request);
+ mConnection = (MockConnection) connection;
+ // Modify the connection object created with local values.
+ connection.setConnectionCapabilities(CONNECTION_CAPABILITIES);
+ connection.setConnectionProperties(CONNECTION_PROPERTIES);
+
+ lock.release();
+ return connection;
+ }
+ }, FLAG_REGISTER | FLAG_ENABLE);
+
+ placeAndVerifyCall();
+ verifyConnectionForOutgoingCall();
+
+ mInCallService = mInCallCallbacks.getService();
+ mCall = mInCallService.getLastCall();
+
+ assertCallState(mCall, Call.STATE_DIALING);
+ assertCallProperties(mCall, Call.Details.PROPERTY_IS_EXTERNAL_CALL);
+ assertCallCapabilities(mCall, Call.Details.CAPABILITY_CAN_PULL_CALL);
+ }
+ }
+
+ /**
+ * Tests that a request to pull an external call via {@link Call#pullExternalCall()} is
+ * communicated to the {@link Connection} via {@link Connection#onPullExternalCall()}.
+ */
+ public void testPullExternalCall() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ final InvokeCounter counter = mConnection.getInvokeCounter(
+ MockConnection.ON_PULL_EXTERNAL_CALL);
+ mCall.pullExternalCall();
+ counter.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ }
+
+ public void testNonPullableExternalCall() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ // Remove the pullable attribute of the connection.
+ mConnection.setConnectionCapabilities(0);
+ assertCallCapabilities(mCall, 0);
+
+ final InvokeCounter counter = mConnection.getInvokeCounter(
+ MockConnection.ON_PULL_EXTERNAL_CALL);
+ // Try to pull -- we expect Telecom to absorb the request since the call is not pullable.
+ mCall.pullExternalCall();
+ counter.waitForCount(0, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockConference.java b/tests/tests/telecom/src/android/telecom/cts/MockConference.java
index 89c3772..d84610d 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockConference.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockConference.java
@@ -152,4 +152,9 @@
public String getDtmfString() {
return mDtmfString;
}
+
+ @Override
+ public void onExtrasChanged(Bundle extras) {
+ mOnExtrasChanged.invoke(extras);
+ }
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockConnection.java b/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
index 708540a..4436219 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
@@ -163,6 +163,30 @@
}
}
+ @Override
+ public void onCallEvent(String event, Bundle extras) {
+ super.onCallEvent(event, extras);
+ if (mInvokeCounterMap.get(ON_CALL_EVENT) != null) {
+ mInvokeCounterMap.get(ON_CALL_EVENT).invoke(event, extras);
+ }
+ }
+
+ @Override
+ public void onPullExternalCall() {
+ super.onPullExternalCall();
+ if (mInvokeCounterMap.get(ON_PULL_EXTERNAL_CALL) != null) {
+ mInvokeCounterMap.get(ON_PULL_EXTERNAL_CALL).invoke();
+ }
+ }
+
+ @Override
+ public void onExtrasChanged(Bundle extras) {
+ super.onExtrasChanged(extras);
+ if (mInvokeCounterMap.get(ON_EXTRAS_CHANGED) != null) {
+ mInvokeCounterMap.get(ON_EXTRAS_CHANGED).invoke(extras);
+ }
+ }
+
public int getCurrentState() {
return mState;
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
index d59a801..4ff3cb6 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
@@ -145,6 +145,14 @@
getCallbacks().onCannedTextResponsesLoaded(call, cannedTextResponses);
}
}
+
+ @Override
+ public void onConnectionEvent(Call call, String event, Bundle extras) {
+ super.onConnectionEvent(call, event, extras);
+ if (getCallbacks() != null) {
+ getCallbacks().onConnectionEvent(call, event, extras);
+ }
+ }
};
private void saveVideoCall(Call call, VideoCall videoCall) {
diff --git a/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java b/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java
index f29e09d..3246b9c 100644
--- a/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java
@@ -343,6 +343,35 @@
mRemoteConferenceObject.unregisterCallback(callback);
}
+ public void testRemoteConferenceCallbacks_ConnectionProperties() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+ Handler handler = setupRemoteConferenceCallbacksTest();
+
+ final InvokeCounter callbackInvoker =
+ new InvokeCounter("testRemoteConferenceCallbacks_ConnectionProperties");
+ RemoteConference.Callback callback;
+
+ callback = new RemoteConference.Callback() {
+ @Override
+ public void onConnectionPropertiesChanged(
+ RemoteConference conference,
+ int connectionProperties) {
+ super.onConnectionPropertiesChanged(conference, connectionProperties);
+ callbackInvoker.invoke(conference, connectionProperties);
+ }
+ };
+ mRemoteConferenceObject.registerCallback(callback, handler);
+ int properties = mRemoteConference.getConnectionCapabilities()
+ | Connection.PROPERTY_IS_EXTERNAL_CALL;
+ mRemoteConference.setConnectionProperties(properties);
+ callbackInvoker.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ assertEquals(mRemoteConferenceObject, callbackInvoker.getArgs(0)[0]);
+ assertEquals(properties, callbackInvoker.getArgs(0)[1]);
+ mRemoteConferenceObject.unregisterCallback(callback);
+ }
+
public void testRemoteConferenceCallbacks_ConferenceableConnections() {
if (!mShouldTestTelecom) {
return;
diff --git a/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java b/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
index eb9e055..81080b0 100644
--- a/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
@@ -240,6 +240,36 @@
mRemoteConnectionObject.unregisterCallback(callback);
}
+ public void testRemoteConnectionCallbacks_ConnectionProperties() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ Handler handler = setupRemoteConnectionCallbacksTest();
+
+ final InvokeCounter callbackInvoker =
+ new InvokeCounter("testRemoteConnectionCallbacks_ConnectionCapabilities");
+ RemoteConnection.Callback callback;
+
+ callback = new RemoteConnection.Callback() {
+ @Override
+ public void onConnectionPropertiesChanged(
+ RemoteConnection connection,
+ int connectionProperties) {
+ super.onConnectionPropertiesChanged(connection, connectionProperties);
+ callbackInvoker.invoke(connection, connectionProperties);
+ }
+ };
+ mRemoteConnectionObject.registerCallback(callback, handler);
+ int properties = mRemoteConnection.getConnectionCapabilities()
+ | Connection.PROPERTY_IS_EXTERNAL_CALL;
+ mRemoteConnection.setConnectionProperties(properties);
+ callbackInvoker.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ assertEquals(mRemoteConnectionObject, callbackInvoker.getArgs(0)[0]);
+ assertEquals(properties, callbackInvoker.getArgs(0)[1]);
+ mRemoteConnectionObject.unregisterCallback(callback);
+ }
+
public void testRemoteConnectionCallbacks_PostDialWait() {
if (!mShouldTestTelecom) {
return;
@@ -528,6 +558,40 @@
mRemoteConnectionObject.unregisterCallback(callback);
}
+ /**
+ * Verifies that a {@link RemoteConnection} receives a
+ * {@link Connection#sendConnectionEvent(String, Bundle)} notification.
+ */
+ public void testRemoteConnectionCallbacks_ConnectionEvent() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ Handler handler = setupRemoteConnectionCallbacksTest();
+
+ final InvokeCounter callbackInvoker =
+ new InvokeCounter("testRemoteConnectionCallbacks_Extras");
+ RemoteConnection.Callback callback;
+
+ callback = new RemoteConnection.Callback() {
+ @Override
+ public void onConnectionEvent(RemoteConnection connection, String event,
+ Bundle extras) {
+ super.onConnectionEvent(connection, event, extras);
+ callbackInvoker.invoke(connection, event, extras);
+ }
+ };
+ mRemoteConnectionObject.registerCallback(callback, handler);
+ Bundle extras = new Bundle();
+ extras.putString(TelecomManager.EXTRA_CALL_DISCONNECT_MESSAGE, "Test");
+ mRemoteConnection.sendConnectionEvent(Connection.EVENT_CALL_PULL_FAILED, extras);
+ callbackInvoker.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
+ assertEquals(mRemoteConnectionObject, callbackInvoker.getArgs(0)[0]);
+ assertEquals(Connection.EVENT_CALL_PULL_FAILED, callbackInvoker.getArgs(0)[1]);
+ assertTrue(areBundlesEqual(extras, (Bundle) callbackInvoker.getArgs(0)[2]));
+ mRemoteConnectionObject.unregisterCallback(callback);
+ }
+
public void testRemoteConnectionCallbacks_Disconnect() {
if (!mShouldTestTelecom) {
return;
@@ -557,6 +621,23 @@
mRemoteConnectionObject.unregisterCallback(callback);
}
+ /**
+ * Verifies that a call to {@link RemoteConnection#pullExternalCall()} is proxied to
+ * {@link Connection#onPullExternalCall()}.
+ */
+ public void testRemoteConnectionCallbacks_PullExternalCall() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ Handler handler = setupRemoteConnectionCallbacksTest();
+
+ InvokeCounter counter =
+ mRemoteConnection.getInvokeCounter(MockConnection.ON_PULL_EXTERNAL_CALL);
+ mRemoteConnectionObject.pullExternalCall();
+ counter.waitForCount(1);
+ }
+
public void testRemoteConnectionCallbacks_Destroy() {
if (!mShouldTestTelecom) {
return;
@@ -1104,6 +1185,8 @@
remoteConnection.getCallerDisplayNamePresentation());
assertEquals(connection.getConnectionCapabilities(),
remoteConnection.getConnectionCapabilities());
+ assertEquals(connection.getConnectionProperties(),
+ remoteConnection.getConnectionProperties());
assertEquals(connection.getDisconnectCause(), remoteConnection.getDisconnectCause());
assertEquals(connection.getExtras(), remoteConnection.getExtras());
assertEquals(connection.getStatusHints(), remoteConnection.getStatusHints());
diff --git a/tests/tests/view/Android.mk b/tests/tests/view/Android.mk
index 6a8d49c..ba4be93 100644
--- a/tests/tests/view/Android.mk
+++ b/tests/tests/view/Android.mk
@@ -29,11 +29,15 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_STATIC_JAVA_LIBRARIES := \
- ctsdeviceutil ctstestrunner mockito-target
+ ctsdeviceutil \
+ ctstestrunner \
+ mockito-target \
+ ub-uiautomator \
+ android-support-test
LOCAL_JNI_SHARED_LIBRARIES := libctsview_jni libnativehelper_compat_libc++
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
LOCAL_PACKAGE_NAME := CtsViewTestCases
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index a98c447..ba1f3d2 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -239,6 +239,13 @@
</intent-filter>
</activity>
+ <activity android:name="android.view.cts.surfacevalidator.CapturedActivity"
+ android:theme="@style/WhiteBackgroundTheme">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/view/res/raw/colors_video.mp4 b/tests/tests/view/res/raw/colors_video.mp4
new file mode 100644
index 0000000..0bec670
--- /dev/null
+++ b/tests/tests/view/res/raw/colors_video.mp4
Binary files differ
diff --git a/tests/tests/view/res/values/styles.xml b/tests/tests/view/res/values/styles.xml
index 9de4abd..4979241 100644
--- a/tests/tests/view/res/values/styles.xml
+++ b/tests/tests/view/res/values/styles.xml
@@ -177,4 +177,13 @@
<item name="android:windowSwipeToDismiss">false</item>
</style>
+ <style name="WhiteBackgroundTheme" parent="@android:style/Theme.Holo.NoActionBar.Fullscreen">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowOverscan">true</item>
+ <item name="android:fadingEdge">none</item>
+ <item name="android:windowBackground">@android:color/white</item>
+ <item name="android:windowContentTransitions">false</item>
+ <item name="android:windowAnimationStyle">@null</item>
+ </style>
</resources>
diff --git a/tests/tests/view/src/android/view/cts/SurfaceViewSyncTests.java b/tests/tests/view/src/android/view/cts/SurfaceViewSyncTests.java
new file mode 100644
index 0000000..da453dd
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/SurfaceViewSyncTests.java
@@ -0,0 +1,300 @@
+/*
+ * 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.view.cts;
+
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.media.MediaPlayer;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiSelector;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.LinearInterpolator;
+import android.view.cts.surfacevalidator.AnimationFactory;
+import android.view.cts.surfacevalidator.AnimationTestCase;
+import android.view.cts.surfacevalidator.CapturedActivity;
+import android.view.cts.surfacevalidator.ViewFactory;
+import android.widget.FrameLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+@SuppressLint("RtlHardcoded")
+public class SurfaceViewSyncTests {
+ private static final String TAG = "SurfaceViewSyncTests";
+ private static final int PERMISSION_DIALOG_WAIT_MS = 500;
+
+ @Before
+ public void setUp() throws UiObjectNotFoundException {
+ // The permission dialog will be auto-opened by the activity - find it and accept
+ UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ UiSelector acceptButtonSelector = new UiSelector().resourceId("android:id/button1");
+ UiObject acceptButton = uiDevice.findObject(acceptButtonSelector);
+ if (acceptButton.waitForExists(PERMISSION_DIALOG_WAIT_MS)) {
+ assertTrue(acceptButton.click());
+ }
+ }
+
+ private CapturedActivity getActivity() {
+ return (CapturedActivity) mActivityRule.getActivity();
+ }
+
+ private MediaPlayer getMediaPlayer() {
+ return getActivity().getMediaPlayer();
+ }
+
+ @Rule
+ public ActivityTestRule mActivityRule = new ActivityTestRule<>(CapturedActivity.class);
+
+ static ValueAnimator makeInfinite(ValueAnimator a) {
+ a.setRepeatMode(ObjectAnimator.REVERSE);
+ a.setRepeatCount(ObjectAnimator.INFINITE);
+ a.setDuration(200);
+ a.setInterpolator(new LinearInterpolator());
+ return a;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // ViewFactories
+ ///////////////////////////////////////////////////////////////////////////
+
+ private ViewFactory sEmptySurfaceViewFactory = SurfaceView::new;
+
+ private ViewFactory sGreenSurfaceViewFactory = context -> {
+ SurfaceView surfaceView = new SurfaceView(context);
+ surfaceView.getHolder().setFixedSize(640, 480);
+ surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {}
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Canvas canvas = holder.lockCanvas();
+ canvas.drawColor(Color.GREEN);
+ holder.unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {}
+ });
+ return surfaceView;
+ };
+
+ private ViewFactory sVideoViewFactory = context -> {
+ SurfaceView surfaceView = new SurfaceView(context);
+ surfaceView.getHolder().setFixedSize(640, 480);
+ surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ getMediaPlayer().setSurface(holder.getSurface());
+ getMediaPlayer().start();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ getMediaPlayer().pause();
+ getMediaPlayer().setSurface(null);
+ }
+ });
+ return surfaceView;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+ // AnimationFactories
+ ///////////////////////////////////////////////////////////////////////////
+
+ private AnimationFactory sSmallScaleAnimationFactory = view -> {
+ view.setPivotX(0);
+ view.setPivotY(0);
+ PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat(View.SCALE_X, 0.01f, 1f);
+ PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 0.01f, 1f);
+ return makeInfinite(ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY));
+ };
+
+ private AnimationFactory sBigScaleAnimationFactory = view -> {
+ view.setTranslationX(10);
+ view.setTranslationY(10);
+ view.setPivotX(0);
+ view.setPivotY(0);
+ PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat(View.SCALE_X, 1f, 3f);
+ PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1f, 3f);
+ return makeInfinite(ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY));
+ };
+
+ private AnimationFactory sTranslateAnimationFactory = view -> {
+ PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 10f, 30f);
+ PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 10f, 30f);
+ return makeInfinite(ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY));
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Tests
+ ///////////////////////////////////////////////////////////////////////////
+
+ /** Draws a moving 10x10 black rectangle, validates 100 pixels of black are seen each frame */
+ @Test
+ public void testSmallRect() {
+ CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+ context -> new View(context) {
+ // draw a single pixel
+ final Paint sBlackPaint = new Paint();
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.drawRect(0, 0, 10, 10, sBlackPaint);
+ }
+
+ @SuppressWarnings("unused")
+ void setOffset(int offset) {
+ // Note: offset by integer values, to ensure no rounding
+ // is done in rendering layer, as that may be brittle
+ setTranslationX(offset);
+ setTranslationY(offset);
+ }
+ },
+ new FrameLayout.LayoutParams(100, 100, Gravity.LEFT | Gravity.TOP),
+ view -> makeInfinite(ObjectAnimator.ofInt(view, "offset", 10, 30)),
+ (blackishPixelCount, width, height) -> blackishPixelCount == 100));
+ assertTrue(result.passFrames > 100);
+ assertTrue(result.failFrames == 0);
+ }
+
+ /**
+ * Verifies that a SurfaceView without a surface is entirely black, with pixel count being
+ * approximate to avoid rounding brittleness.
+ */
+ @Test
+ public void testEmptySurfaceView() {
+ CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+ sEmptySurfaceViewFactory,
+ new FrameLayout.LayoutParams(100, 100, Gravity.LEFT | Gravity.TOP),
+ sTranslateAnimationFactory,
+ (blackishPixelCount, width, height) ->
+ blackishPixelCount > 9000 && blackishPixelCount < 11000));
+ assertTrue(result.passFrames > 100);
+ assertTrue(result.failFrames == 0);
+ }
+
+ @Test
+ public void testSurfaceViewSmallScale() {
+ CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+ sGreenSurfaceViewFactory,
+ new FrameLayout.LayoutParams(640, 480, Gravity.LEFT | Gravity.TOP),
+ sSmallScaleAnimationFactory,
+ (blackishPixelCount, width, height) -> blackishPixelCount == 0));
+ assertTrue(result.passFrames > 100);
+ assertTrue(result.failFrames == 0);
+ }
+
+ @Test
+ public void testSurfaceViewBigScale() {
+ CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+ sGreenSurfaceViewFactory,
+ new FrameLayout.LayoutParams(640, 480, Gravity.LEFT | Gravity.TOP),
+ sBigScaleAnimationFactory,
+ (blackishPixelCount, width, height) -> blackishPixelCount == 0));
+ assertTrue(result.passFrames > 100);
+ assertTrue(result.failFrames == 0);
+ }
+
+ @Test
+ public void testVideoSurfaceViewTranslate() {
+ CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+ sVideoViewFactory,
+ new FrameLayout.LayoutParams(640, 480, Gravity.LEFT | Gravity.TOP),
+ sTranslateAnimationFactory,
+ (blackishPixelCount, width, height) -> blackishPixelCount == 0));
+ assertTrue(result.passFrames > 100);
+ assertTrue(result.failFrames == 0);
+ }
+
+ @Test
+ public void testVideoSurfaceViewRotated() {
+ CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+ sVideoViewFactory,
+ new FrameLayout.LayoutParams(100, 100, Gravity.LEFT | Gravity.TOP),
+ view -> makeInfinite(ObjectAnimator.ofPropertyValuesHolder(view,
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 10f, 30f),
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 10f, 30f),
+ PropertyValuesHolder.ofFloat(View.ROTATION, 45f, 45f))),
+ (blackishPixelCount, width, height) -> blackishPixelCount == 0));
+ assertTrue(result.passFrames > 100);
+ assertTrue(result.failFrames == 0);
+ }
+
+ @Test
+ public void testVideoSurfaceViewEdgeCoverage() {
+ CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+ sVideoViewFactory,
+ new FrameLayout.LayoutParams(640, 480, Gravity.CENTER),
+ view -> {
+ ViewGroup parent = (ViewGroup) view.getParent();
+ final int x = parent.getWidth() / 2;
+ final int y = parent.getHeight() / 2;
+
+ // Animate from left, to top, to right, to bottom
+ return makeInfinite(ObjectAnimator.ofPropertyValuesHolder(view,
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_X, -x, 0, x, 0, -x),
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0, -y, 0, y, 0)));
+ },
+ (blackishPixelCount, width, height) -> blackishPixelCount == 0));
+ assertTrue(result.passFrames > 100);
+ assertTrue(result.failFrames == 0);
+ }
+
+ @Test
+ public void testVideoSurfaceViewCornerCoverage() {
+ CapturedActivity.TestResult result = getActivity().runTest(new AnimationTestCase(
+ sVideoViewFactory,
+ new FrameLayout.LayoutParams(640, 480, Gravity.CENTER),
+ view -> {
+ ViewGroup parent = (ViewGroup) view.getParent();
+ final int x = parent.getWidth() / 2;
+ final int y = parent.getHeight() / 2;
+
+ // Animate from top left, to top right, to bottom right, to bottom left
+ return makeInfinite(ObjectAnimator.ofPropertyValuesHolder(view,
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_X, -x, x, x, -x, -x),
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, -y, -y, y, y, -y)));
+ },
+ (blackishPixelCount, width, height) -> blackishPixelCount == 0));
+ assertTrue(result.passFrames > 100);
+ assertTrue(result.failFrames == 0);
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/surfacevalidator/AnimationFactory.java b/tests/tests/view/src/android/view/cts/surfacevalidator/AnimationFactory.java
new file mode 100644
index 0000000..c4c19cf
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/surfacevalidator/AnimationFactory.java
@@ -0,0 +1,23 @@
+/*
+ * 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.view.cts.surfacevalidator;
+
+import android.animation.ValueAnimator;
+import android.view.View;
+
+public interface AnimationFactory {
+ ValueAnimator createAnimator(View view);
+}
diff --git a/tests/tests/view/src/android/view/cts/surfacevalidator/AnimationTestCase.java b/tests/tests/view/src/android/view/cts/surfacevalidator/AnimationTestCase.java
new file mode 100644
index 0000000..6b455e2
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/surfacevalidator/AnimationTestCase.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.cts.surfacevalidator;
+
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.view.View;
+import android.widget.FrameLayout;
+
+public class AnimationTestCase {
+ private final ViewFactory mViewFactory;
+ private final FrameLayout.LayoutParams mLayoutParams;
+ private final AnimationFactory mAnimationFactory;
+ private final PixelChecker mPixelChecker;
+
+ private FrameLayout mParent;
+ private ValueAnimator mAnimator;
+
+ public AnimationTestCase(ViewFactory viewFactory,
+ FrameLayout.LayoutParams layoutParams,
+ AnimationFactory animationFactory,
+ PixelChecker pixelChecker) {
+ mViewFactory = viewFactory;
+ mLayoutParams = layoutParams;
+ mAnimationFactory = animationFactory;
+ mPixelChecker = pixelChecker;
+ }
+
+ PixelChecker getChecker() {
+ return mPixelChecker;
+ }
+
+ public void start(Context context, FrameLayout parent) {
+ mParent = parent;
+ mParent.removeAllViews();
+ View view = mViewFactory.createView(context);
+ mParent.addView(view, mLayoutParams);
+ mAnimator = mAnimationFactory.createAnimator(view);
+ mAnimator.start();
+ }
+
+ public void end() {
+ mAnimator.cancel();
+ mParent.removeAllViews();
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/surfacevalidator/CapturedActivity.java b/tests/tests/view/src/android/view/cts/surfacevalidator/CapturedActivity.java
new file mode 100644
index 0000000..a198136
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/surfacevalidator/CapturedActivity.java
@@ -0,0 +1,197 @@
+/*
+ * 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.view.cts.surfacevalidator;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.MediaPlayer;
+import android.media.projection.MediaProjection;
+import android.media.projection.MediaProjectionManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import android.view.cts.R;
+
+public class CapturedActivity extends Activity {
+ public static class TestResult {
+ public int passFrames;
+ public int failFrames;
+ }
+
+ private static final String TAG = "CapturedActivity";
+ private static final long TIME_OUT_MS = 10000;
+ private static final int PERMISSION_CODE = 1;
+ private MediaProjectionManager mProjectionManager;
+ private MediaProjection mMediaProjection;
+ private VirtualDisplay mVirtualDisplay;
+
+ private SurfacePixelValidator mSurfacePixelValidator;
+ private final Object mLock = new Object();
+
+ private static final long START_CAPTURE_DELAY_MS = 1000;
+ private static final long END_CAPTURE_DELAY_MS = START_CAPTURE_DELAY_MS + 4000;
+ private static final long END_DELAY_MS = END_CAPTURE_DELAY_MS + 500;
+
+ private MediaPlayer mMediaPlayer;
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private volatile boolean mOnWatch;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
+
+ mProjectionManager =
+ (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
+
+ startActivityForResult(mProjectionManager.createScreenCaptureIntent(), PERMISSION_CODE);
+
+ mMediaPlayer = MediaPlayer.create(this, R.raw.colors_video);
+ mMediaPlayer.setLooping(true);
+
+ int uiMode = getResources().getConfiguration().uiMode;
+ mOnWatch = (uiMode & Configuration.UI_MODE_TYPE_WATCH) == Configuration.UI_MODE_TYPE_WATCH;
+ }
+
+ /**
+ * MediaPlayer pre-loaded with a video with no black pixels. Be kind, rewind.
+ */
+ public MediaPlayer getMediaPlayer() {
+ return mMediaPlayer;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.d(TAG, "onDestroy");
+ if (mMediaProjection != null) {
+ mMediaProjection.stop();
+ mMediaProjection = null;
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode != PERMISSION_CODE) {
+ throw new IllegalStateException("Unknown request code: " + requestCode);
+ }
+ if (resultCode != RESULT_OK) {
+ throw new IllegalStateException("User denied screen sharing permission");
+ }
+ mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
+ mMediaProjection.registerCallback(new MediaProjectionCallback(), null);
+ }
+
+ public TestResult runTest(AnimationTestCase animationTestCase) {
+ TestResult testResult = new TestResult();
+ if (mOnWatch) {
+ /**
+ * Watch devices not supported, since they may not support:
+ * 1) displaying unmasked windows
+ * 2) RenderScript
+ * 3) Video playback
+ */
+ Log.d(TAG, "Skipping test on watch.");
+ testResult.passFrames = 1000;
+ testResult.failFrames = 0;
+ return testResult;
+ }
+
+ mHandler.post(() -> {
+ Log.d(TAG, "Setting up test case");
+
+ // shouldn't be necessary, since we've already done this in #create,
+ // but ensure status/nav are hidden for test
+ getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
+
+ animationTestCase.start(getApplicationContext(),
+ (FrameLayout) findViewById(android.R.id.content));
+ });
+
+ mHandler.postDelayed(() -> {
+ Log.d(TAG, "Starting capture");
+
+ Display display = getWindow().getDecorView().getDisplay();
+ Point size = new Point();
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealSize(size);
+ display.getMetrics(metrics);
+
+ mSurfacePixelValidator = new SurfacePixelValidator(CapturedActivity.this,
+ size, animationTestCase.getChecker());
+ Log.d("MediaProjection", "Size is " + size.toString());
+ mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenSharingDemo",
+ size.x, size.y,
+ metrics.densityDpi,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+ mSurfacePixelValidator.getSurface(),
+ null /*Callbacks*/,
+ null /*Handler*/);
+ }, START_CAPTURE_DELAY_MS);
+
+ mHandler.postDelayed(() -> {
+ Log.d(TAG, "Stopping capture");
+ mVirtualDisplay.release();
+ mVirtualDisplay = null;
+ }, END_CAPTURE_DELAY_MS);
+
+ mHandler.postDelayed(() -> {
+ Log.d(TAG, "Ending test case");
+ animationTestCase.end();
+ synchronized (mLock) {
+ mSurfacePixelValidator.finish(testResult);
+ mLock.notify();
+ }
+ mSurfacePixelValidator = null;
+ }, END_DELAY_MS);
+
+ synchronized (mLock) {
+ try {
+ mLock.wait(TIME_OUT_MS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ Log.d(TAG, "Test finished, passFrames " + testResult.passFrames
+ + ", failFrames " + testResult.failFrames);
+ return testResult;
+ }
+
+ private class MediaProjectionCallback extends MediaProjection.Callback {
+ @Override
+ public void onStop() {
+ Log.d(TAG, "MediaProjectionCallback#onStop");
+ if (mVirtualDisplay != null) {
+ mVirtualDisplay.release();
+ mVirtualDisplay = null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/cts/surfacevalidator/PixelChecker.java b/tests/tests/view/src/android/view/cts/surfacevalidator/PixelChecker.java
new file mode 100644
index 0000000..76f0adc
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/surfacevalidator/PixelChecker.java
@@ -0,0 +1,20 @@
+/*
+ * 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.view.cts.surfacevalidator;
+
+public interface PixelChecker {
+ boolean checkPixels(int blackishPixelCount, int width, int height);
+}
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/cts/surfacevalidator/PixelCounter.rs b/tests/tests/view/src/android/view/cts/surfacevalidator/PixelCounter.rs
new file mode 100644
index 0000000..55bc251
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/surfacevalidator/PixelCounter.rs
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+#pragma version(1)
+#pragma rs java_package_name(android.view.cts.surfacevalidator)
+
+int WIDTH;
+uchar THRESHOLD;
+
+rs_allocation image;
+
+void countBlackishPixels(const int32_t *v_in, int *v_out){
+ int y = v_in[0];
+ v_out[0] = 0;
+
+ for(int i = 0 ; i < WIDTH; i++){
+ uchar4 pixel = rsGetElementAt_uchar4(image, i, y);
+ if (pixel.r < THRESHOLD
+ && pixel.g < THRESHOLD
+ && pixel.b < THRESHOLD) {
+ v_out[0]++;
+ }
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/surfacevalidator/SurfacePixelValidator.java b/tests/tests/view/src/android/view/cts/surfacevalidator/SurfacePixelValidator.java
new file mode 100644
index 0000000..c9bff1d
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/surfacevalidator/SurfacePixelValidator.java
@@ -0,0 +1,183 @@
+/*
+ * 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.view.cts.surfacevalidator;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Trace;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Log;
+import android.view.Surface;
+import android.view.cts.surfacevalidator.PixelChecker;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class SurfacePixelValidator {
+ private static final String TAG = "SurfacePixelValidator";
+
+ /**
+ * Observed that first few frames have errors with SurfaceView placement, so we skip for now.
+ * b/29603849 tracking that issue.
+ */
+ private static final int NUM_FIRST_FRAMES_SKIPPED = 8;
+
+ // If no channel is greater than this value, pixel will be considered 'blackish'.
+ private static final short PIXEL_CHANNEL_THRESHOLD = 4;
+
+ private final int mWidth;
+ private final int mHeight;
+
+ private final HandlerThread mWorkerThread;
+ private final Handler mWorkerHandler;
+
+ private final PixelChecker mPixelChecker;
+
+ private final RenderScript mRS;
+
+ private final Allocation mInPixelsAllocation;
+ private final Allocation mInRowsAllocation;
+ private final Allocation mOutRowsAllocation;
+ private final ScriptC_PixelCounter mScript;
+
+
+ private final Object mResultLock = new Object();
+ private int mResultSuccessFrames;
+ private int mResultFailureFrames;
+
+ private Runnable mConsumeRunnable = new Runnable() {
+ int numSkipped = 0;
+ @Override
+ public void run() {
+ Trace.beginSection("consume buffer");
+ mInPixelsAllocation.ioReceive();
+ mScript.set_image(mInPixelsAllocation);
+ Trace.endSection();
+
+ Trace.beginSection("compare");
+ mScript.forEach_countBlackishPixels(mInRowsAllocation, mOutRowsAllocation);
+ Trace.endSection();
+
+ Trace.beginSection("sum");
+ int blackishPixelCount = sum1DIntAllocation(mOutRowsAllocation, mHeight);
+ Trace.endSection();
+
+ boolean success = mPixelChecker.checkPixels(blackishPixelCount, mWidth, mHeight);
+ synchronized (mResultLock) {
+ if (numSkipped < NUM_FIRST_FRAMES_SKIPPED) {
+ numSkipped++;
+ } else {
+
+ if (success) {
+ mResultSuccessFrames++;
+ } else {
+ mResultFailureFrames++;
+ int totalFramesSeen = mResultSuccessFrames + mResultFailureFrames;
+ Log.d(TAG, "Failure (pixel count = " + blackishPixelCount
+ + ") occurred on frame " + totalFramesSeen);
+ }
+ }
+ }
+ }
+ };
+
+ public SurfacePixelValidator(Context context, Point size, PixelChecker pixelChecker) {
+ mWidth = size.x;
+ mHeight = size.y;
+
+ mWorkerThread = new HandlerThread("SurfacePixelValidator");
+ mWorkerThread.start();
+ mWorkerHandler = new Handler(mWorkerThread.getLooper());
+
+ mPixelChecker = pixelChecker;
+
+ mRS = RenderScript.create(context);
+ mScript = new ScriptC_PixelCounter(mRS);
+
+ mInPixelsAllocation = createBufferQueueAllocation();
+ mInRowsAllocation = createInputRowIndexAllocation();
+ mOutRowsAllocation = createOutputRowAllocation();
+ mScript.set_WIDTH(mWidth);
+ mScript.set_THRESHOLD(PIXEL_CHANNEL_THRESHOLD);
+
+ mInPixelsAllocation.setOnBufferAvailableListener(
+ allocation -> mWorkerHandler.post(mConsumeRunnable));
+ }
+
+ public Surface getSurface() {
+ return mInPixelsAllocation.getSurface();
+ }
+
+ static private int sum1DIntAllocation(Allocation array, int length) {
+ //Get the values returned from the function
+ int[] returnValue = new int[length];
+ array.copyTo(returnValue);
+ int sum = 0;
+ //If any row had any different pixels, then it fails
+ for (int i = 0; i < length; i++) {
+ sum += returnValue[i];
+ }
+ return sum;
+ }
+
+ /**
+ * Creates an allocation where the values in it are the indices of each row
+ */
+ private Allocation createInputRowIndexAllocation() {
+ //Create an array with the index of each row
+ int[] inputIndices = new int[mHeight];
+ for (int i = 0; i < mHeight; i++) {
+ inputIndices[i] = i;
+ }
+ //Create the allocation from that given array
+ Allocation inputAllocation = Allocation.createSized(mRS, Element.I32(mRS),
+ inputIndices.length, Allocation.USAGE_SCRIPT);
+ inputAllocation.copyFrom(inputIndices);
+ return inputAllocation;
+ }
+
+ private Allocation createOutputRowAllocation() {
+ return Allocation.createSized(mRS, Element.I32(mRS), mHeight, Allocation.USAGE_SCRIPT);
+ }
+
+ private Allocation createBufferQueueAllocation() {
+ return Allocation.createAllocations(mRS, Type.createXY(mRS,
+ Element.U8_4(mRS), mWidth, mHeight),
+ Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_INPUT,
+ 1)[0];
+ }
+
+ /**
+ * Shuts down processing pipeline, and returns current pass/fail counts.
+ *
+ * Wait for pipeline to flush before calling this method. If not, frames that are still in
+ * flight may be lost.
+ */
+ public void finish(CapturedActivity.TestResult testResult) {
+ synchronized (mResultLock) {
+ // could in theory miss results still processing, but only if latency is extremely high.
+ // Caller should only call this
+ testResult.failFrames = mResultFailureFrames;
+ testResult.passFrames = mResultSuccessFrames;
+ }
+ mWorkerThread.quitSafely();
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/surfacevalidator/ViewFactory.java b/tests/tests/view/src/android/view/cts/surfacevalidator/ViewFactory.java
new file mode 100644
index 0000000..9ef2ef8
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/surfacevalidator/ViewFactory.java
@@ -0,0 +1,23 @@
+/*
+ * 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.view.cts.surfacevalidator;
+
+import android.content.Context;
+import android.view.View;
+
+public interface ViewFactory {
+ View createView(Context context);
+}
diff --git a/tests/tests/view/src/android/view/inputmethod/cts/EditorInfoTest.java b/tests/tests/view/src/android/view/inputmethod/cts/EditorInfoTest.java
index e9c3058..0780460 100644
--- a/tests/tests/view/src/android/view/inputmethod/cts/EditorInfoTest.java
+++ b/tests/tests/view/src/android/view/inputmethod/cts/EditorInfoTest.java
@@ -21,6 +21,7 @@
import android.os.LocaleList;
import android.os.Parcel;
import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
import android.text.TextUtils;
import android.util.Printer;
import android.view.inputmethod.EditorInfo;
@@ -50,6 +51,7 @@
b.putString(key, value);
info.extras = b;
info.hintLocales = LocaleList.forLanguageTags("en-PH,en-US");
+ info.contentMimeTypes = new String[]{"image/gif", "image/png"};
assertEquals(0, info.describeContents());
@@ -73,6 +75,7 @@
assertEquals(info.label.toString(), targetInfo.label.toString());
assertEquals(info.extras.getString(key), targetInfo.extras.getString(key));
assertEquals(info.hintLocales, targetInfo.hintLocales);
+ MoreAsserts.assertEquals(info.contentMimeTypes, targetInfo.contentMimeTypes);
TestPrinter printer = new TestPrinter();
String prefix = "TestEditorInfo";
diff --git a/tests/tests/view/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java b/tests/tests/view/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java
index 1ddfd2b..c9b3ad7 100644
--- a/tests/tests/view/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java
+++ b/tests/tests/view/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java
@@ -17,6 +17,8 @@
package android.view.inputmethod.cts;
+import android.content.ClipDescription;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.test.AndroidTestCase;
@@ -29,6 +31,7 @@
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.InputContentInfo;
public class InputConnectionWrapperTest extends AndroidTestCase {
@@ -94,6 +97,13 @@
assertFalse(inputConnection.isGetHandlerCalled);
assertNull(inputConnection.getHandler());
assertTrue(inputConnection.isGetHandlerCalled);
+ assertFalse(inputConnection.isCommitContentCalled);
+ final InputContentInfo inputContentInfo = new InputContentInfo(
+ Uri.parse("content://com.example/path"),
+ new ClipDescription("sample content", new String[]{"image/png"}),
+ Uri.parse("https://example.com"));
+ assertTrue(inputConnection.commitContent(inputContentInfo, null /* opt */));
+ assertTrue(inputConnection.isCommitContentCalled);
}
private class MockInputConnection implements InputConnection {
@@ -122,6 +132,7 @@
public boolean isRequestCursorUpdatesCalled;
public boolean isGetHandlerCalled;
public boolean isCloseConnectionCalled;
+ public boolean isCommitContentCalled;
public boolean beginBatchEdit() {
isBeginBatchEditCalled = true;
@@ -246,5 +257,10 @@
public void closeConnection() {
isCloseConnectionCalled = true;
}
+
+ public boolean commitContent(InputContentInfo inputContentInfo, Bundle opts) {
+ isCommitContentCalled = true;
+ return true;
+ }
}
}
diff --git a/tests/tests/view/src/android/view/inputmethod/cts/InputContentInfoTest.java b/tests/tests/view/src/android/view/inputmethod/cts/InputContentInfoTest.java
new file mode 100644
index 0000000..30c86bf
--- /dev/null
+++ b/tests/tests/view/src/android/view/inputmethod/cts/InputContentInfoTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.view.inputmethod.cts;
+
+
+import android.content.ClipDescription;
+import android.net.Uri;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+import android.view.inputmethod.InputContentInfo;
+
+import java.lang.NullPointerException;
+import java.security.InvalidParameterException;
+
+public class InputContentInfoTest extends AndroidTestCase {
+
+ public void testInputContentInfo() {
+ InputContentInfo info = new InputContentInfo(
+ Uri.parse("content://com.example/path"),
+ new ClipDescription("sample content", new String[]{"image/png"}),
+ Uri.parse("https://example.com"));
+
+ assertEquals(Uri.parse("content://com.example/path"), info.getContentUri());
+ assertEquals(1, info.getDescription().getMimeTypeCount());
+ assertEquals("image/png", info.getDescription().getMimeType(0));
+ assertEquals("sample content", info.getDescription().getLabel());
+ assertEquals(Uri.parse("https://example.com"), info.getLinkUri());
+
+ Parcel p = Parcel.obtain();
+ info.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ InputContentInfo targetInfo = InputContentInfo.CREATOR.createFromParcel(p);
+ p.recycle();
+
+ assertEquals(info.getContentUri(), targetInfo.getContentUri());
+ assertEquals(info.getDescription().getMimeTypeCount(),
+ targetInfo.getDescription().getMimeTypeCount());
+ assertEquals(info.getDescription().getMimeType(0),
+ targetInfo.getDescription().getMimeType(0));
+ assertEquals(info.getDescription().getLabel(), targetInfo.getDescription().getLabel());
+ assertEquals(info.getLinkUri(), targetInfo.getLinkUri());
+ }
+
+ public void testContentUri() {
+ try {
+ InputContentInfo info = new InputContentInfo(
+ null, new ClipDescription("sample content", new String[]{"image/png"}),
+ Uri.parse("https://example.com"));
+ fail("InputContentInfo must not accept a null content URI.");
+ } catch (NullPointerException e) {
+ // OK.
+ } catch (Exception e) {
+ fail("Unexpected exception=" + e);
+ }
+
+ try {
+ InputContentInfo info = new InputContentInfo(
+ Uri.parse("https://example.com"),
+ new ClipDescription("sample content", new String[]{"image/png"}),
+ Uri.parse("https://example.com"));
+ fail("InputContentInfo must accept content URI only.");
+ } catch (InvalidParameterException e) {
+ // OK.
+ } catch (Exception e) {
+ fail("Unexpected exception=" + e);
+ }
+ }
+
+ public void testMimeType() {
+ try {
+ InputContentInfo info = new InputContentInfo(
+ Uri.parse("content://com.example/path"), null,
+ Uri.parse("https://example.com"));
+ fail("InputContentInfo must not accept a null description.");
+ } catch (NullPointerException e) {
+ // OK.
+ } catch (Exception e) {
+ fail("Unexpected exception=" + e);
+ }
+ }
+
+ public void testLinkUri() {
+ try {
+ InputContentInfo info = new InputContentInfo(
+ Uri.parse("content://com.example/path"),
+ new ClipDescription("sample content", new String[]{"image/png"}),
+ null);
+ } catch (Exception e) {
+ fail("InputContentInfo must accept a null link Uri.");
+ }
+
+ try {
+ InputContentInfo info = new InputContentInfo(
+ Uri.parse("content://com.example/path"),
+ new ClipDescription("sample content", new String[]{"image/png"}),
+ Uri.parse("http://example.com/path"));
+ } catch (Exception e) {
+ fail("InputContentInfo must accept http link Uri.");
+ }
+
+ try {
+ InputContentInfo info = new InputContentInfo(
+ Uri.parse("content://com.example/path"),
+ new ClipDescription("sample content", new String[]{"image/png"}),
+ Uri.parse("https://example.com/path"));
+ } catch (Exception e) {
+ fail("InputContentInfo must accept https link Uri.");
+ }
+
+ try {
+ InputContentInfo info = new InputContentInfo(
+ Uri.parse("content://com.example/path"),
+ new ClipDescription("sample content", new String[]{"image/png"}),
+ Uri.parse("ftp://example.com/path"));
+ fail("InputContentInfo must accept http and https link Uri only.");
+ } catch (InvalidParameterException e) {
+ // OK.
+ } catch (Exception e) {
+ fail("Unexpected exception=" + e);
+ }
+
+ try {
+ InputContentInfo info = new InputContentInfo(
+ Uri.parse("content://com.example/path"),
+ new ClipDescription("sample content", new String[]{"image/png"}),
+ Uri.parse("content://com.example/path"));
+ fail("InputContentInfo must accept http and https link Uri only.");
+ } catch (InvalidParameterException e) {
+ // OK.
+ } catch (Exception e) {
+ fail("Unexpected exception=" + e);
+ }
+ }
+}