Merge "Added new tests on LoginActivityTest:" into oc-dev
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
index 0355cb4..23744c7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
@@ -32,7 +32,6 @@
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.util.Log;
import android.view.View;
@@ -101,7 +100,7 @@
tests.add(new SetModeAllTest());
tests.add(new AllInterceptsNothingTest());
tests.add(new DefaultOrderTest());
- tests.add(new PrioritytOrderTest());
+ tests.add(new PriorityOrderTest());
tests.add(new InterruptionOrderTest());
tests.add(new AmbientBitsTest());
tests.add(new LookupUriOrderTest());
@@ -113,13 +112,11 @@
private void createChannels() {
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
- NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+ NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_MIN);
mNm.createNotificationChannel(channel);
NotificationChannel noisyChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID_NOISY,
NOTIFICATION_CHANNEL_ID_NOISY, NotificationManager.IMPORTANCE_HIGH);
noisyChannel.enableVibration(true);
- noisyChannel.setSound(
- Settings.System.DEFAULT_RINGTONE_URI, Notification.AUDIO_ATTRIBUTES_DEFAULT);
mNm.createNotificationChannel(noisyChannel);
}
@@ -528,7 +525,7 @@
}
// ordered by priority: B, C, A
- protected class PrioritytOrderTest extends InteractiveTestCase {
+ protected class PriorityOrderTest extends InteractiveTestCase {
@Override
View inflate(ViewGroup parent) {
return createAutoItem(parent, R.string.attention_priority_order);
@@ -585,8 +582,8 @@
@Override
void setUp() {
createChannels();
- // send B & C noisy
- sendNotifications(SEND_B | SEND_C, MODE_NONE, false, true);
+ // send B & C noisy with contact affinity
+ sendNotifications(SEND_B | SEND_C, MODE_URI, false, true);
status = READY;
// wait for then to not be recently noisy any more
delay(15000);
@@ -595,7 +592,7 @@
@Override
void test() {
if (status == READY) {
- // send A noisy
+ // send A noisy but no contact affinity
sendNotifications(SEND_A, MODE_NONE, false, true);
status = RETEST;
delay();
@@ -650,7 +647,8 @@
@Override
void setUp() {
createChannels();
- sendNotifications(MODE_NONE, true, false);
+ sendNotifications(SEND_B | SEND_C, MODE_NONE, true, true);
+ sendNotifications(SEND_A, MODE_NONE, true, false);
status = READY;
// wait for notifications to move through the system
delay();
diff --git a/build/device_info_package.mk b/build/device_info_package.mk
index b91c264..e584bc4 100644
--- a/build/device_info_package.mk
+++ b/build/device_info_package.mk
@@ -26,7 +26,6 @@
$(DEVICE_INFO_PACKAGE).GenericDeviceInfo \
$(DEVICE_INFO_PACKAGE).GlesStubActivity \
$(DEVICE_INFO_PACKAGE).GraphicsDeviceInfo \
- $(DEVICE_INFO_PACKAGE).LibraryDeviceInfo \
$(DEVICE_INFO_PACKAGE).LocaleDeviceInfo \
$(DEVICE_INFO_PACKAGE).MediaDeviceInfo \
$(DEVICE_INFO_PACKAGE).MemoryDeviceInfo \
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/LibraryDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/LibraryDeviceInfo.java
deleted file mode 100644
index 792c88a..0000000
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/LibraryDeviceInfo.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.deviceinfo;
-
-import android.util.Log;
-
-import com.android.compatibility.common.util.DeviceInfoStore;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Formatter;
-
-/**
- * Library device info collector.
- */
-public final class LibraryDeviceInfo extends DeviceInfo {
-
- private static final String TAG = "LibraryDeviceInfo";
- private static final int BUFFER_SIZE_BYTES = 4096;
-
- @Override
- protected void collectDeviceInfo(DeviceInfoStore store) throws Exception {
- collectSystemLibs(store);
- collectVendorLibs(store);
- collectFrameworkJars(store);
- }
-
- private void collectSystemLibs(DeviceInfoStore store) throws Exception {
- store.startArray("lib");
- collectFileDetails(store, "/system/lib", ".so");
- store.endArray();
- }
-
- private void collectVendorLibs(DeviceInfoStore store) throws Exception {
- store.startArray("vendor_lib");
- collectFileDetails(store, "/system/vendor/lib", ".so");
- store.endArray();
- }
-
- private void collectFrameworkJars(DeviceInfoStore store) throws Exception {
- store.startArray("framework_jar");
- collectFileDetails(store, "/system/framework", ".jar");
- store.endArray();
- }
-
- private void collectFileDetails(DeviceInfoStore store, String path, String suffix)
- throws Exception {
- File dir = new File(path);
- File[] files = dir.listFiles();
- if (files == null) {
- return;
- }
- for (File file : files) {
- String name = file.getName();
- if (file.isFile() && name.endsWith(suffix)) {
- String sha1 = "unknown";
- try {
- sha1 = getSha1sum(file);
- } catch (IOException e) {
- Log.e(TAG, "Failed to hash " + file + ": ", e);
- }
- store.startGroup();
- store.addResult("name", name);
- store.addResult("sha1", sha1);
- store.endGroup();
- }
- }
- }
-
- private static String getSha1sum(File file) throws IOException {
- InputStream in = null;
- try {
- in = new FileInputStream(file);
- return sha1(in);
- } finally {
- close(in);
- }
- }
-
- private static void close(Closeable s) throws IOException {
- if (s == null) {
- return;
- }
- s.close();
- }
-
- /**
- * @return the SHA-1 digest of input as a hex string
- */
- public static String sha1(InputStream input) throws IOException {
- try {
- return toHexString(digest(input, "sha1"));
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static byte[] digest(InputStream in, String algorithm)
- throws NoSuchAlgorithmException, IOException {
- MessageDigest digest = MessageDigest.getInstance(algorithm);
- byte[] buffer = new byte[BUFFER_SIZE_BYTES];
- while (true) {
- int read = in.read(buffer);
- if (read < 0) {
- break;
- }
- digest.update(buffer, 0, read);
- }
- return digest.digest();
- }
-
- private static String toHexString(byte[] buffer) {
- Formatter formatter = new Formatter();
- try {
- for (byte b : buffer) {
- formatter.format("%02X", b);
- }
- return formatter.toString();
- } finally {
- formatter.close();
- }
- }
-}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
index 3a282bf..6a6c27d 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
@@ -47,8 +47,13 @@
private static final String MODE_EMULATED = "emulated";
private static final String MODE_NONE = "none";
+ private static final String FEATURE_DEVICE_ADMIN = "feature:android.software.device_admin\n";
+ private static final String FEATURE_AUTOMOTIVE = "feature:android.hardware.type.automotive\n";
+
private static final long SHUTDOWN_TIME_MS = 30 * 1000;
+ private String mFeatureList = null;
+
private IAbi mAbi;
private IBuildInfo mCtsBuild;
@@ -82,6 +87,22 @@
}
/**
+ * Automotive devices MUST support native FBE.
+ */
+ public void testAutomotiveNativeFbe() throws Exception {
+ if (!isSupportedDevice()) {
+ Log.v(TAG, "Device not supported; skipping test");
+ return;
+ } else if (!isAutomotiveDevice()) {
+ Log.v(TAG, "Device not automotive; skipping test");
+ return;
+ }
+
+ assertTrue("Automotive devices must support native FBE",
+ MODE_NATIVE.equals(getFbeMode()));
+ }
+
+ /**
* If device has native FBE, verify lifecycle.
*/
public void testDirectBootNative() throws Exception {
@@ -227,9 +248,20 @@
return "1".equals(output);
}
+ private boolean hasSystemFeature(final String feature) throws Exception {
+ if (mFeatureList == null) {
+ mFeatureList = getDevice().executeShellCommand("pm list features");
+ }
+
+ return mFeatureList.contains(feature);
+ }
+
private boolean isSupportedDevice() throws Exception {
- final String featureList = getDevice().executeShellCommand("pm list features");
- return featureList.contains("feature:android.software.device_admin\n");
+ return hasSystemFeature(FEATURE_DEVICE_ADMIN);
+ }
+
+ private boolean isAutomotiveDevice() throws Exception {
+ return hasSystemFeature(FEATURE_AUTOMOTIVE);
}
private void waitForBootCompleted() throws Exception {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
index 77513c5..7de83d6 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
@@ -110,12 +110,9 @@
runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartEphemeral");
}
- /*
- * Disabled pending drops of updated prebuilts
public void testExposedSystemActivities() throws Exception {
runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testExposedSystemActivities");
}
- */
public void testBuildSerialUnknown() throws Exception {
runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testBuildSerialUnknown");
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
index 21afece..fa8730d 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
@@ -97,9 +97,6 @@
ContactsContract.CommonDataKinds.Email.CONTENT_TYPE, null),
makeIntent(Intent.ACTION_PICK, null,
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_TYPE, null),
- makeIntent(Intent.ACTION_INSERT, null, ContactsContract.Contacts.CONTENT_TYPE, null),
- // Email
- makeIntent(Intent.ACTION_SEND, null, "text/plain", Uri.parse("mailto:")),
// File Storage
makeIntent(Intent.ACTION_OPEN_DOCUMENT, Intent.CATEGORY_OPENABLE, "*/*", null),
makeIntent(Intent.ACTION_OPEN_DOCUMENT, null, "*/*", null),
@@ -108,13 +105,8 @@
makeIntent(Intent.ACTION_OPEN_DOCUMENT_TREE, null, null, null),
makeIntent(Intent.ACTION_CREATE_DOCUMENT, Intent.CATEGORY_OPENABLE, "text/plain", null),
makeIntent(Intent.ACTION_CREATE_DOCUMENT, null, "text/plain", null),
- // Phone call
- makeIntent(Intent.ACTION_DIAL, null, null, Uri.parse("tel:")),
- // SMS
- makeIntent(Intent.ACTION_SEND, null, "text/plain", Uri.parse("sms:")),
- makeIntent(Intent.ACTION_SEND, null, "text/plain", Uri.parse("smsto:")),
- // Web
- makeIntent(Intent.ACTION_VIEW, null, "text/html", Uri.parse("https://example.com")),
+ // Framework
+ makeIntent(Intent.ACTION_CHOOSER, null, null, null),
};
private BroadcastReceiver mReceiver;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
index b8cc99d..f0b8279 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
@@ -105,68 +105,91 @@
}
// Install
+ CLog.i("Installing apk1...");
installAppAsUser(OWNER_APK_1, getUserId());
+
+ CLog.i("Making it a device/profile owner...");
setAsOwnerOrFail(OWNER_COMPONENT);
withRetry(() -> assertServiceBound(OWNER_SERVICE));
// Remove admin.
+ CLog.i("Removing admin...");
removeAdmin(OWNER_COMPONENT, getUserId());
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
// Overwrite -> update.
+ CLog.i("Re-installing apk1...");
installAppAsUser(OWNER_APK_1, getUserId());
+
+ CLog.i("Making it a device/profile owner...");
setAsOwnerOrFail(OWNER_COMPONENT);
withRetry(() -> assertServiceBound(OWNER_SERVICE));
+ CLog.i("Installing apk2...");
installAppAsUser(OWNER_APK_2, getUserId());
withRetry(() -> assertServiceBound(OWNER_SERVICE)); // Should still be bound.
// Service exported -> not bound.
+ CLog.i("Installing apk3...");
installAppAsUser(OWNER_APK_3, getUserId());
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
// Recover.
+ CLog.i("Installing apk2 again...");
installAppAsUser(OWNER_APK_2, getUserId());
withRetry(() -> assertServiceBound(OWNER_SERVICE));
// Multiple service found -> not bound.
+ CLog.i("Installing apk4...");
installAppAsUser(OWNER_APK_4, getUserId());
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
// Disable service1 -> now there's only one service, so should be bound.
+ CLog.i("Running testDisableService1...");
executeDeviceTestMethod(".ComponentController", "testDisableService1");
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
withRetry(() -> assertServiceBound(OWNER_SERVICE2));
+ CLog.i("Running testDisableService2...");
executeDeviceTestMethod(".ComponentController", "testDisableService2");
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
+ CLog.i("Running testEnableService1...");
executeDeviceTestMethod(".ComponentController", "testEnableService1");
withRetry(() -> assertServiceBound(OWNER_SERVICE));
withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
+ CLog.i("Running testEnableService2...");
executeDeviceTestMethod(".ComponentController", "testEnableService2");
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
// Remove admin.
+ CLog.i("Removing admin again...");
removeAdmin(OWNER_COMPONENT, getUserId());
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
// Retry with package 1 and remove admin.
+ CLog.i("Installing apk1 again...");
installAppAsUser(OWNER_APK_1, getUserId());
+
+ CLog.i("Making it a device/profile owner again...");
setAsOwnerOrFail(OWNER_COMPONENT);
withRetry(() -> assertServiceBound(OWNER_SERVICE));
+ CLog.i("Removing admin again...");
removeAdmin(OWNER_COMPONENT, getUserId());
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
// Now install package B and make it the owner. OWNER_APK_1 still exists, but it shouldn't
// interfere.
+ CLog.i("Installing apk B...");
installAppAsUser(OWNER_APK_B, getUserId());
+
+ CLog.i("Making it a device/profile owner...");
setAsOwnerOrFail(OWNER_COMPONENT_B);
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
withRetry(() -> assertServiceBound(OWNER_SERVICE_B));
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
index fd3e85d..8ba3064 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
@@ -387,7 +387,7 @@
}
private void checkBattery(String[] parts) {
- assertEquals(13, parts.length);
+ assertEquals(15, parts.length);
if (!parts[4].equals("N/A")) {
assertInteger(parts[4]); // startCount
}
@@ -399,6 +399,9 @@
long bOffReal = assertInteger(parts[10]); // batteryScreenOffRealtime
long bOffUp = assertInteger(parts[11]); // batteryScreenOffUptime
long bEstCap = assertInteger(parts[12]); // batteryEstimatedCapacity
+ assertInteger(parts[13]); // minLearnedBatteryCapacity
+ assertInteger(parts[14]); // maxLearnedBatteryCapacity
+
// The device cannot be up more than there are real-world seconds.
assertTrue("batteryRealtime must be >= batteryUptime", bReal >= bUp);
assertTrue("totalRealtime must be >= totalUptime", tReal >= tUp);
diff --git a/hostsidetests/edi/Android.mk b/hostsidetests/edi/Android.mk
new file mode 100644
index 0000000..efa093f
--- /dev/null
+++ b/hostsidetests/edi/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE_TAGS := tests
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_MODULE := CtsEdiHostTestCases
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/hostsidetests/edi/AndroidTest.xml b/hostsidetests/edi/AndroidTest.xml
new file mode 100644
index 0000000..94be6b6
--- /dev/null
+++ b/hostsidetests/edi/AndroidTest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS EDI host test cases">
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsEdiHostTestCases.jar" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/hostsidetests/edi/src/android/edi/cts/LibraryDeviceInfo.java b/hostsidetests/edi/src/android/edi/cts/LibraryDeviceInfo.java
new file mode 100644
index 0000000..b9f83f4
--- /dev/null
+++ b/hostsidetests/edi/src/android/edi/cts/LibraryDeviceInfo.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.edi.cts;
+
+import com.android.compatibility.common.util.DeviceInfo;
+import com.android.compatibility.common.util.HostInfoStore;
+import com.android.ddmlib.FileListingService.FileEntry;
+import com.android.tradefed.device.IFileEntry;
+import com.android.tradefed.device.ITestDevice;
+
+import java.util.Arrays;
+
+public class LibraryDeviceInfo extends DeviceInfo {
+
+ private ITestDevice mDevice;
+
+ @Override
+ protected void collectDeviceInfo(HostInfoStore store) throws Exception {
+
+ mDevice = getDevice();
+
+ collectSystemLibs(store);
+ collectVendorLibs(store);
+ collectFrameworkJars(store);
+ }
+
+ private void collectSystemLibs(HostInfoStore store) throws Exception {
+ store.startArray("lib");
+ collectFileDetails(store, "/system/lib", ".so");
+ store.endArray();
+ }
+
+ private void collectVendorLibs(HostInfoStore store) throws Exception {
+ store.startArray("vendor_lib");
+ collectFileDetails(store, "/system/vendor/lib", ".so");
+ store.endArray();
+ }
+
+ private void collectFrameworkJars(HostInfoStore store) throws Exception {
+ store.startArray("framework_jar");
+ collectFileDetails(store, "/system/framework", ".jar");
+ store.endArray();
+ }
+
+ private void collectFileDetails(HostInfoStore store, String path, String suffix)
+ throws Exception {
+ IFileEntry dir = mDevice.getFileEntry(path);
+
+ if(dir == null || !dir.isDirectory()) {
+ return;
+ }
+
+ for (IFileEntry file : dir.getChildren(false)) {
+ String name = file.getName();
+ if (!file.isDirectory() && name.endsWith(suffix)) {
+ String sha1 = getSha1(file.getFullPath());
+ store.startGroup();
+ store.addResult("name", name);
+ store.addResult("sha1", sha1);
+ store.endGroup();
+ }
+ }
+ }
+
+ private String getSha1(String filePath) {
+ String sha1 = "unknown";
+ try {
+ String out = mDevice.executeShellCommand("sha1sum " + filePath);
+ sha1 = out.split(" ", 2)[0].toUpperCase();
+ } catch (Exception e) {
+ // Do nothing.
+ }
+ return sha1;
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java
index 496e61a..bc28c07 100644
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java
@@ -17,6 +17,7 @@
package com.android.server.cts.device.batterystats;
import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_ACTION;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_REQUEST_CODE;
import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.doAction;
import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.isAppInBackground;
@@ -45,7 +46,9 @@
Log.w(TAG, "Couldn't determine if app is in background. Proceeding with test anyway.");
}
- Log.i(TAG, "Starting action from background service");
- doAction(this, intent.getStringExtra(KEY_ACTION));
+ String action = intent.getStringExtra(KEY_ACTION);
+ String requestCode = intent.getStringExtra(KEY_REQUEST_CODE);
+ Log.i(TAG, "Starting " + action + " from background service as request " + requestCode);
+ doAction(this, action, requestCode);
}
}
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java
index 480ca2f..3c9abda 100644
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java
@@ -55,9 +55,10 @@
public static final String ACTION_WIFI_DOWNLOAD = "action.wifi_download";
public static final String ACTION_WIFI_UPLOAD = "action.wifi_upload";
+ public static final String KEY_REQUEST_CODE = "request_code";
/** Perform the action specified by the given action code (see constants above). */
- public static void doAction(Context ctx, String actionCode) {
+ public static void doAction(Context ctx, String actionCode, String requestCode) {
if (actionCode == null) {
Log.e(TAG, "Intent was missing action.");
return;
@@ -65,22 +66,22 @@
sleep(100);
switch(actionCode) {
case ACTION_BLE_SCAN:
- doBleScan(ctx);
+ doBleScan(ctx, requestCode);
break;
case ACTION_JOB_SCHEDULE:
- doScheduleJob(ctx);
+ doScheduleJob(ctx, requestCode);
break;
case ACTION_SYNC:
- doSync(ctx);
+ doSync(ctx, requestCode);
break;
case ACTION_WIFI_SCAN:
- doWifiScan(ctx);
+ doWifiScan(ctx, requestCode);
break;
case ACTION_WIFI_DOWNLOAD:
- doWifiDownload(ctx);
+ doWifiDownload(ctx, requestCode);
break;
case ACTION_WIFI_UPLOAD:
- doWifiUpload(ctx);
+ doWifiUpload(ctx, requestCode);
break;
default:
Log.e(TAG, "Intent had invalid action");
@@ -88,7 +89,7 @@
sleep(100);
}
- private static void doBleScan(Context ctx) {
+ private static void doBleScan(Context ctx, String requestCode) {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
Log.e(TAG, "Device does not support Bluetooth");
@@ -128,9 +129,10 @@
bleScanner.startScan(null, scanSettings, scanCallback);
sleep(2_000);
bleScanner.stopScan(scanCallback);
+ tellHostActionFinished(ACTION_BLE_SCAN, requestCode);
}
- private static void doScheduleJob(Context ctx) {
+ private static void doScheduleJob(Context ctx, String requestCode) {
final ComponentName JOB_COMPONENT_NAME =
new ComponentName("com.android.server.cts.device.batterystats",
SimpleJobService.class.getName());
@@ -148,13 +150,14 @@
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
- waitForReceiver(null, 3_000, latch, null);
+ waitForReceiver(null, 60_000, latch, null);
+ tellHostActionFinished(ACTION_JOB_SCHEDULE, requestCode);
return null;
}
}.execute();
}
- private static void doSync(Context ctx) {
+ private static void doSync(Context ctx, String requestCode) {
BatteryStatsAuthenticator.removeAllAccounts(ctx);
final Account account = BatteryStatsAuthenticator.getTestAccount();
// Create the test account.
@@ -185,36 +188,50 @@
if(ctx instanceof Activity){
((Activity) ctx).finish();
}
+ tellHostActionFinished(ACTION_SYNC, requestCode);
}
}.execute();
}
- private static void doWifiScan(Context ctx) {
+ private static void doWifiScan(Context ctx, String requestCode) {
IntentFilter intentFilter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
CountDownLatch onReceiveLatch = new CountDownLatch(1);
BroadcastReceiver receiver = registerReceiver(ctx, onReceiveLatch, intentFilter);
ctx.getSystemService(WifiManager.class).startScan();
- waitForReceiver(ctx, 3_000, onReceiveLatch, receiver);
+ waitForReceiver(ctx, 60_000, onReceiveLatch, receiver);
+ tellHostActionFinished(ACTION_WIFI_SCAN, requestCode);
}
- private static void doWifiDownload(Context ctx) {
+ private static void doWifiDownload(Context ctx, String requestCode) {
+ CountDownLatch latch = new CountDownLatch(1);
+
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
- BatteryStatsWifiTransferTests.download();
+ BatteryStatsWifiTransferTests.download(requestCode);
+ latch.countDown();
return null;
}
}.execute();
+
+ waitForReceiver(null, 60_000, latch, null);
+ tellHostActionFinished(ACTION_WIFI_DOWNLOAD, requestCode);
}
- private static void doWifiUpload(Context ctx) {
+ private static void doWifiUpload(Context ctx, String requestCode) {
+ CountDownLatch latch = new CountDownLatch(1);
+
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
BatteryStatsWifiTransferTests.upload();
+ latch.countDown();
return null;
}
}.execute();
+
+ waitForReceiver(null, 60_000, latch, null);
+ tellHostActionFinished(ACTION_WIFI_UPLOAD, requestCode);
}
/** Register receiver to determine when given action is complete. */
@@ -258,6 +275,12 @@
}
}
+ /** Communicates to hostside (via logcat) that action has completed (regardless of success). */
+ private static void tellHostActionFinished(String actionCode, String requestCode) {
+ String s = String.format("Completed performing %s for request %s", actionCode, requestCode);
+ Log.i(TAG, s);
+ }
+
/** Determines whether the package is running as a background process. */
public static boolean isAppInBackground(Context context) throws ReflectiveOperationException {
String pkgName = context.getPackageName();
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java
index c41b244..fab306d 100644
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java
@@ -16,10 +16,9 @@
package com.android.server.cts.device.batterystats;
-import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions
- .ACTION_JOB_SCHEDULE;
import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.ACTION_SYNC;
import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_ACTION;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_REQUEST_CODE;
import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.doAction;
import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.isAppInBackground;
@@ -53,7 +52,9 @@
}
String action = intent.getStringExtra(KEY_ACTION);
- doAction(this, action);
+ String requestCode = intent.getStringExtra(KEY_REQUEST_CODE);
+ Log.i(TAG, "Starting " + action + " from foreground activity as request " + requestCode);
+ doAction(this, action, requestCode);
// ACTION_SYNC will finish itself. Others get finished here.
if (!ACTION_SYNC.equals(action)) {
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiTransferTests.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiTransferTests.java
index 1633301..aa5c018 100644
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiTransferTests.java
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiTransferTests.java
@@ -15,35 +15,14 @@
*/
package com.android.server.cts.device.batterystats;
-import static org.junit.Assert.assertTrue;
-
-import android.app.IntentService;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
-import java.util.Random;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
public class BatteryStatsWifiTransferTests {
private static final String TAG = "BatteryStatsWifiTransferTests";
@@ -53,7 +32,7 @@
/** Server to send requests to. */
private static final String SERVER_URL = "https://developer.android.com/index.html";
- public static String download() {
+ public static String download(String requestCode) {
HttpURLConnection conn = null;
try {
URL url = new URL(SERVER_URL);
@@ -69,7 +48,7 @@
while ((count = in.read(data)) != -1) {
total += count;
}
- Log.i(TAG, Integer.toString(total));
+ Log.i(TAG, String.format("request %s d=%d", requestCode, total));
} catch (IOException e) {
Log.i(TAG, e.toString());
return "Caught exception";
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
index 758c443..ebe1996 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
@@ -15,8 +15,15 @@
*/
package com.android.server.cts;
+import com.android.ddmlib.IShellOutputReceiver;
import com.android.tradefed.log.LogUtil;
+import com.google.common.base.Charsets;
+
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
/**
* Test for "dumpsys batterystats -c
*
@@ -52,6 +59,9 @@
public static final String ACTION_WIFI_DOWNLOAD = "action.wifi_download";
public static final String ACTION_WIFI_UPLOAD = "action.wifi_upload";
+ public static final String KEY_REQUEST_CODE = "request_code";
+ public static final String BG_VS_FG_TAG = "BatteryStatsBgVsFgActions";
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -130,14 +140,12 @@
installPackage(DEVICE_SIDE_TEST_APK, true);
// Foreground test.
- executeForeground(ACTION_BLE_SCAN);
- Thread.sleep(2_500);
+ executeForeground(ACTION_BLE_SCAN, 30_000);
assertValueRange("blem", "", 5, 1, 1); // ble_scan_count
assertValueRange("blem", "", 6, 0, 0); // ble_scan_count_bg
// Background test.
- executeBackground(ACTION_BLE_SCAN);
- Thread.sleep(2_500);
+ executeBackground(ACTION_BLE_SCAN, 30_000);
assertValueRange("blem", "", 5, 2, 2); // ble_scan_count
assertValueRange("blem", "", 6, 1, 1); // ble_scan_count_bg
@@ -149,14 +157,12 @@
installPackage(DEVICE_SIDE_TEST_APK, true);
// Foreground test.
- executeForeground(ACTION_JOB_SCHEDULE);
- Thread.sleep(4_000);
+ executeForeground(ACTION_JOB_SCHEDULE, 60_000);
assertValueRange("jb", "", 6, 1, 1); // count
assertValueRange("jb", "", 8, 0, 0); // background_count
// Background test.
- executeBackground(ACTION_JOB_SCHEDULE);
- Thread.sleep(4_000);
+ executeBackground(ACTION_JOB_SCHEDULE, 60_000);
assertValueRange("jb", "", 6, 2, 2); // count
assertValueRange("jb", "", 8, 1, 1); // background_count
@@ -168,15 +174,13 @@
installPackage(DEVICE_SIDE_TEST_APK, true);
// Foreground test.
- executeForeground(ACTION_SYNC);
- Thread.sleep(3_000);
+ executeForeground(ACTION_SYNC, 60_000);
// Allow one or two syncs in this time frame (not just one) due to unpredictable syncs.
assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 6, 1, 2); // count
assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 8, 0, 0); // background_count
// Background test.
- executeBackground(ACTION_SYNC);
- Thread.sleep(3_000);
+ executeBackground(ACTION_SYNC, 60_000);
assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 6, 2, 4); // count
assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 8, 1, 2); // background_count
@@ -188,14 +192,12 @@
installPackage(DEVICE_SIDE_TEST_APK, true);
// Foreground count test.
- executeForeground(ACTION_WIFI_SCAN);
- Thread.sleep(4_000);
+ executeForeground(ACTION_WIFI_SCAN, 60_000);
assertValueRange("wfl", "", 7, 1, 1); // scan_count
assertValueRange("wfl", "", 11, 0, 0); // scan_count_bg
// Background count test.
- executeBackground(ACTION_WIFI_SCAN);
- Thread.sleep(6_000);
+ executeBackground(ACTION_WIFI_SCAN, 60_000);
assertValueRange("wfl", "", 7, 2, 2); // scan_count
assertValueRange("wfl", "", 11, 1, 1); // scan_count_bg
@@ -275,8 +277,8 @@
long prevBytes = getLongValue(getUid(), "nt", "", 6);
- executeForeground(ACTION_WIFI_DOWNLOAD);
- long downloadedBytes = getDownloadedBytes();
+ String requestCode = executeForeground(ACTION_WIFI_DOWNLOAD, 60_000);
+ long downloadedBytes = getDownloadedBytes(requestCode);
assertTrue(downloadedBytes > 0);
long min = prevBytes + downloadedBytes + MIN_HTTP_HEADER_BYTES;
long max = prevBytes + downloadedBytes + FUZZ; // Add some fuzzing.
@@ -285,9 +287,8 @@
// Do the background download
long prevBgBytes = getLongValue(getUid(), "nt", "", 20);
- executeBackground(ACTION_WIFI_DOWNLOAD);
- Thread.sleep(4000);
- downloadedBytes = getDownloadedBytes();
+ requestCode = executeBackground(ACTION_WIFI_DOWNLOAD, 60_000);
+ downloadedBytes = getDownloadedBytes(requestCode);
long minBg = prevBgBytes + downloadedBytes + MIN_HTTP_HEADER_BYTES;
long maxBg = prevBgBytes + downloadedBytes + FUZZ;
@@ -310,14 +311,12 @@
batteryOnScreenOff();
installPackage(DEVICE_SIDE_TEST_APK, true);
- executeForeground(ACTION_WIFI_UPLOAD);
- Thread.sleep(2000);
+ executeForeground(ACTION_WIFI_UPLOAD, 60_000);
int min = MIN_HTTP_HEADER_BYTES + (2 * 1024);
int max = min + (6 * 1024); // Add some fuzzing.
assertValueRange("nt", "", 7, min, max); // wifi_bytes_tx
- executeBackground(ACTION_WIFI_UPLOAD);
- Thread.sleep(4000);
+ executeBackground(ACTION_WIFI_UPLOAD, 60_000);
assertValueRange("nt", "", 21, min * 2, max * 2); // wifi_bytes_bg_tx
batteryOffScreenOn();
@@ -371,15 +370,39 @@
}
/**
+ * Runs a (background) service to perform the given action, and waits for
+ * the device to report that the action has finished (via a logcat message) before returning.
+ * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+ * action to perform.
+ * @param maxTimeMs max time to wait (in ms) for action to report that it has completed.
+ * @return A string, representing a random integer, assigned to this particular request for the
+ * device to perform the given action. This value can be used to receive
+ * communications via logcat from the device about this action.
+ */
+ private String executeBackground(String actionValue, int maxTimeMs) throws Exception {
+ String requestCode = executeBackground(actionValue);
+ String searchString = getCompletedActionString(actionValue, requestCode);
+ checkLogcatForText(BG_VS_FG_TAG, searchString, maxTimeMs);
+ return requestCode;
+ }
+
+ /**
* Runs a (background) service to perform the given action.
* @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
* action to perform.
+ * @return A string, representing a random integer, assigned to this particular request for the
+ * device to perform the given action. This value can be used to receive
+ * communications via logcat from the device about this action.
*/
- private void executeBackground(String actionValue) throws Exception {
+ private String executeBackground(String actionValue) throws Exception {
allowBackgroundServices();
+ String requestCode = Integer.toString(new Random().nextInt());
getDevice().executeShellCommand(String.format(
- "am startservice -n '%s' -e %s %s",
- DEVICE_SIDE_BG_SERVICE_COMPONENT, KEY_ACTION, actionValue));
+ "am startservice -n '%s' -e %s %s -e %s %s",
+ DEVICE_SIDE_BG_SERVICE_COMPONENT,
+ KEY_ACTION, actionValue,
+ KEY_REQUEST_CODE, requestCode));
+ return requestCode;
}
/** Required to successfully start a background service from adb in O. */
@@ -389,26 +412,119 @@
}
/**
+ * Runs an activity (in the foreground) to perform the given action, and waits
+ * for the device to report that the action has finished (via a logcat message) before returning.
+ * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+ * action to perform.
+ * @param maxTimeMs max time to wait (in ms) for action to report that it has completed.
+ * @return A string, representing a random integer, assigned to this particular request for the
+ * device to perform the given action. This value can be used to receive
+ * communications via logcat from the device about this action.
+ */
+ private String executeForeground(String actionValue, int maxTimeMs) throws Exception {
+ String requestCode = executeForeground(actionValue);
+ String searchString = getCompletedActionString(actionValue, requestCode);
+ checkLogcatForText(BG_VS_FG_TAG, searchString, maxTimeMs);
+ return requestCode;
+ }
+
+ /**
* Runs an activity (in the foreground) to perform the given action.
* @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
* action to perform.
+ * @return A string, representing a random integer, assigned to this particular request for the
+ * device to perform the given action. This value can be used to receive
+ * communications via logcat from the device about this action.
*/
- private void executeForeground(String actionValue) throws Exception {
+ private String executeForeground(String actionValue) throws Exception {
+ String requestCode = Integer.toString(new Random().nextInt());
getDevice().executeShellCommand(String.format(
- "am start -n '%s' -e %s %s",
- DEVICE_SIDE_FG_ACTIVITY_COMPONENT, KEY_ACTION, actionValue));
+ "am start -n '%s' -e %s %s -e %s %s",
+ DEVICE_SIDE_FG_ACTIVITY_COMPONENT,
+ KEY_ACTION, actionValue,
+ KEY_REQUEST_CODE, requestCode));
+ return requestCode;
+ }
+
+ /**
+ * The string that will be printed in the logcat when the action completes. This needs to be
+ * identical to {@link com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions#tellHostActionFinished}.
+ */
+ private String getCompletedActionString(String actionValue, String requestCode) {
+ return String.format("Completed performing %s for request %s", actionValue, requestCode);
+ }
+
+ /**
+ * Runs logcat and waits (for a maximumum of maxTimeMs) until the desired text is displayed with
+ * the given tag.
+ * Logcat is not cleared, so make sure that text is unique (won't get false hits from old data).
+ * Note that, in practice, the actual max wait time seems to be about 10s longer than maxTimeMs.
+ */
+ private void checkLogcatForText(String logcatTag, String text, int maxTimeMs) {
+ IShellOutputReceiver receiver = new IShellOutputReceiver() {
+ private final StringBuilder mOutputBuffer = new StringBuilder();
+ private final AtomicBoolean mIsCanceled = new AtomicBoolean(false);
+
+ @Override
+ public void addOutput(byte[] data, int offset, int length) {
+ if (!isCancelled()) {
+ synchronized (mOutputBuffer) {
+ String s = new String(data, offset, length, Charsets.UTF_8);
+ mOutputBuffer.append(s);
+ if (checkBufferForText()) {
+ mIsCanceled.set(true);
+ }
+ }
+ }
+ }
+
+ private boolean checkBufferForText() {
+ if (mOutputBuffer.indexOf(text) > -1) {
+ return true;
+ } else {
+ // delete all old data (except the last few chars) since they don't contain text
+ // (presumably large chunks of data will be added at a time, so this is
+ // sufficiently efficient.)
+ int newStart = mOutputBuffer.length() - text.length();
+ if (newStart > 0) {
+ mOutputBuffer.delete(0, newStart);
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return mIsCanceled.get();
+ }
+
+ @Override
+ public void flush() {
+ }
+ };
+
+ try {
+ // Wait for at most maxTimeMs for logcat to display the desired text.
+ getDevice().executeShellCommand(String.format("logcat -s %s -e '%s'", logcatTag, text),
+ receiver, maxTimeMs, TimeUnit.MILLISECONDS, 0);
+ } catch (com.android.tradefed.device.DeviceNotAvailableException e) {
+ System.err.println(e);
+ }
}
/**
* Returns the bytes downloaded for the wifi transfer download tests.
+ * @param requestCode the output of executeForeground() or executeBackground() to identify in
+ * the logcat the line associated with the desired download information
*/
- private long getDownloadedBytes() throws Exception {
+ private long getDownloadedBytes(String requestCode) throws Exception {
String log = getDevice().executeShellCommand(
- "logcat -d -s BatteryStatsWifiTransferTests -e '\\d+'");
+ String.format("logcat -d -s BatteryStatsWifiTransferTests -e 'request %s d=\\d+'",
+ requestCode));
String[] lines = log.split("\n");
long size = 0;
for (int i = lines.length - 1; i >= 0; i--) {
- String[] parts = lines[i].split(":");
+ String[] parts = lines[i].split("d=");
String num = parts[parts.length - 1].trim();
if (num.matches("\\d+")) {
size = Integer.parseInt(num);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
index f406735..3f87165 100755
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
@@ -30,6 +30,12 @@
android:supportsPictureInPicture="true"
android:exported="true"
/>
+ <activity android:name=".TestActivityWithSameAffinity"
+ android:resizeableActivity="true"
+ android:supportsPictureInPicture="true"
+ android:exported="true"
+ android:taskAffinity="nobody.but.PipActivitySameAffinity"
+ />
<activity android:name=".TranslucentTestActivity"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
@@ -103,7 +109,20 @@
android:exported="true"
android:taskAffinity="nobody.but.PipActivity2"
/>
-
+ <activity android:name=".PipOnStopActivity"
+ android:resizeableActivity="false"
+ android:supportsPictureInPicture="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="true"
+ android:taskAffinity="nobody.but.PipOnStopActivity"
+ />
+ <activity android:name=".PipActivityWithSameAffinity"
+ android:resizeableActivity="false"
+ android:supportsPictureInPicture="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="true"
+ android:taskAffinity="nobody.but.PipActivitySameAffinity"
+ />
<activity android:name=".AlwaysFocusablePipActivity"
android:theme="@style/Theme.Transparent"
android:resizeableActivity="false"
@@ -134,19 +153,12 @@
android:exported="true"
/>
<activity android:name=".LaunchImeWithPipActivity"
- android:resizeableActivity="false"
- android:supportsPictureInPicture="true"
- androidprv:alwaysFocusable="true"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
- android:exported="true"
- android:windowSoftInputMode="stateAlwaysVisible"
- />
- <activity android:name=".PipOnStopActivity"
- android:resizeableActivity="false"
- android:supportsPictureInPicture="true"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
- android:exported="true"
- android:taskAffinity="nobody.but.PipOnStopActivity"
+ android:resizeableActivity="false"
+ android:supportsPictureInPicture="true"
+ androidprv:alwaysFocusable="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="true"
+ android:windowSoftInputMode="stateAlwaysVisible"
/>
<activity android:name=".FreeformActivity"
android:resizeableActivity="true"
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.java
new file mode 100644
index 0000000..e97dc0e
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.cts;
+
+import android.app.Activity;
+import android.app.PictureInPictureParams;
+
+/**
+ * An activity that can enter PiP with a fixed affinity to match
+ * {@link TestActivityWithSameAffinity}.
+ */
+public class PipActivityWithSameAffinity extends Activity {
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java
new file mode 100644
index 0000000..d835da7
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 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;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestActivityWithSameAffinity extends TestActivity {
+
+ private static final String TAG = TestActivityWithSameAffinity.class.getSimpleName();
+
+ // Starts the activity (component name) provided by the value at the end of onCreate
+ private static final String EXTRA_START_ACTIVITY = "start_activity";
+ // Finishes the activity at the end of onResume (after EXTRA_START_ACTIVITY is handled)
+ private static final String EXTRA_FINISH_SELF_ON_RESUME = "finish_self_on_resume";
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Launch a new activity if requested
+ String launchActivityComponent = getIntent().getStringExtra(EXTRA_START_ACTIVITY);
+ if (launchActivityComponent != null) {
+ Intent launchIntent = new Intent();
+ launchIntent.setComponent(ComponentName.unflattenFromString(launchActivityComponent));
+ startActivity(launchIntent);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // Finish self if requested
+ if (getIntent().hasExtra(EXTRA_FINISH_SELF_ON_RESUME)) {
+ finish();
+ }
+ }
+
+ @Override
+ protected String getTag() {
+ return TAG;
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
index 8374f7c..c04ef90 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
@@ -102,6 +102,9 @@
case "destroy_display":
destroyVirtualDisplays();
break;
+ case "resize_display":
+ resizeDisplay();
+ break;
}
}
@@ -195,4 +198,12 @@
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
+
+ /** Resize virtual display to half of the surface frame size. */
+ private void resizeDisplay() {
+ final VirtualDisplayEntry vd = (VirtualDisplayEntry) mVirtualDisplays.values().toArray()[0];
+ final SurfaceHolder surfaceHolder = vd.surfaceView.getHolder();
+ vd.display.resize(surfaceHolder.getSurfaceFrame().width() / 2,
+ surfaceHolder.getSurfaceFrame().height() / 2, vd.density);
+ }
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
index 9ad9d29..dc37d9b 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
@@ -410,7 +410,7 @@
* on the primary display. It should land on the primary display and dismiss docked stack.
*/
public void testLaunchNonResizeableActivityWithSplitScreen() throws Exception {
- if (!supportsMultiDisplay()) { return; }
+ if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
// Start launching activity.
launchActivityInDockStack(LAUNCHING_ACTIVITY);
@@ -540,10 +540,9 @@
if (!supportsMultiDisplay()) { return; }
// Start launching activity.
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
+ launchActivity(LAUNCHING_ACTIVITY);
// Create new virtual display.
- final DisplayState newDisplay =
- new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
+ final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
// Launch activity on secondary display from the app on primary display.
getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME)
@@ -569,28 +568,21 @@
if (!supportsMultiDisplay()) { return; }
// Start launching activity.
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
+ launchActivity(LAUNCHING_ACTIVITY);
// Create new virtual display.
- final DisplayState newDisplay =
- new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
+ final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
// Launch activity on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
// Launch activity on primary display and check if it doesn't affect activity on secondary
// display.
getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
- mAmWmState.waitForValidState(mDevice, RESIZEABLE_ACTIVITY_NAME,
- FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.waitForValidState(mDevice, RESIZEABLE_ACTIVITY_NAME);
mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
}
@@ -606,46 +598,54 @@
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
VIRTUAL_DISPLAY_ACTIVITY);
- mAmWmState.assertFocusedStack("Focus must remain on primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(defaultDisplayStackId);
+ assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
// Launch activity on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
mAmWmState.assertFocusedActivity("Focus must be on secondary display", TEST_ACTIVITY_NAME);
- mAmWmState.assertNotFocusedStack("Focused stack must be on secondary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
+ assertEquals("Focused stack must be on secondary display",
+ newDisplay.mDisplayId, focusedStack.mDisplayId);
// Move activity from secondary display to primary.
- moveActivityToStack(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.waitForFocusedStack(mDevice, FULLSCREEN_WORKSPACE_STACK_ID);
+ moveActivityToStack(TEST_ACTIVITY_NAME, defaultDisplayStackId);
+ mAmWmState.waitForFocusedStack(mDevice, defaultDisplayStackId);
mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY_NAME);
- mAmWmState.assertFocusedStack("Focus must return to primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
+ assertEquals("Focus must return to primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
}
/**
* Tests launching activities on secondary display and then removing it to see if stack focus
* is moved correctly.
- * This version launches virtual display creator to fullscreen stack.
+ * This version launches virtual display creator to fullscreen stack in split-screen.
*/
@Presubmit
public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
- if (!supportsMultiDisplay()) { return; }
+ if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
// Start launching activity into docked stack.
launchActivityInDockStack(LAUNCHING_ACTIVITY);
mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
- tryCreatingAndRemovingDisplayWithActivity();
+ tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
+ FULLSCREEN_WORKSPACE_STACK_ID);
}
/**
* Tests launching activities on secondary display and then removing it to see if stack focus
* is moved correctly.
- * This version launches virtual display creator to docked stack.
+ * This version launches virtual display creator to docked stack in split-screen.
*/
public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
- if (!supportsMultiDisplay()) { return; }
+ if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
// Setup split-screen.
launchActivityInDockStack(RESIZEABLE_ACTIVITY_NAME);
@@ -654,21 +654,45 @@
launchActivityInStack(LAUNCHING_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
- tryCreatingAndRemovingDisplayWithActivity();
+ tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
+ FULLSCREEN_WORKSPACE_STACK_ID);
}
/**
- * Create a virtual display to side from LaunchingActivity, launch a test activity there,
- * destroy the display and check if test activity is moved to fullscreen stack.
+ * Tests launching activities on secondary display and then removing it to see if stack focus
+ * is moved correctly.
+ * This version works without split-screen.
*/
- private void tryCreatingAndRemovingDisplayWithActivity() throws Exception {
+ public void testStackFocusSwitchOnDisplayRemoved3() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
+ // Start an activity on default display to determine default stack.
+ launchActivity(BROADCAST_RECEIVER_ACTIVITY);
+ final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+ // Finish probing activity.
+ executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+
+
+ tryCreatingAndRemovingDisplayWithActivity(false /* splitScreen */, focusedStackId);
+ }
+
+ /**
+ * Create a virtual display, launch a test activity there, destroy the display and check if test
+ * activity is moved to a stack on the default display.
+ */
+ private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int defaultStackId)
+ throws Exception {
// Create new virtual display.
- final DisplayState newDisplay = new VirtualDisplayBuilder(this)
- .setLaunchInSplitScreen(true)
- .setPublicDisplay(true)
- .build();
+ final VirtualDisplayBuilder builder = new VirtualDisplayBuilder(this)
+ .setPublicDisplay(true);
+ if (splitScreen) {
+ builder.setLaunchInSplitScreen(true);
+ }
+ final DisplayState newDisplay = builder.build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+ if (splitScreen) {
+ mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+ }
// Launch activity on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
@@ -679,15 +703,17 @@
// Destroy virtual display.
destroyVirtualDisplays();
- mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY_NAME, defaultStackId);
mAmWmState.assertSanity();
mAmWmState.assertValidBounds(true /* compareTaskAndStackBounds */);
// Check if the focus is switched back to primary display.
mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
- mAmWmState.assertFocusedStack("Fullscreen stack must be focused after display removed",
- FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
+ mAmWmState.assertFocusedStack(
+ "Default stack on primary display must be focused after display removed",
+ defaultStackId);
+ mAmWmState.assertFocusedActivity(
+ "Focus must be switched back to activity on primary display",
TEST_ACTIVITY_NAME);
}
@@ -698,15 +724,10 @@
public void testStackFocusSwitchOnStackEmptied() throws Exception {
if (!supportsMultiDisplay()) { return; }
- // Start launching activity.
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
// Create new virtual display.
- final DisplayState newDisplay =
- new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
+ final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+ final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
// Launch activity on new secondary display.
launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mDisplayId);
@@ -721,9 +742,8 @@
// Unlock and check if the focus is switched back to primary display.
wakeUpAndUnlockDevice();
- mAmWmState.waitForFocusedStack(mDevice, FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.waitForValidState(mDevice, LAUNCHING_ACTIVITY);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+ mAmWmState.waitForFocusedStack(mDevice, focusedStackId);
+ mAmWmState.waitForValidState(mDevice, VIRTUAL_DISPLAY_ACTIVITY);
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
VIRTUAL_DISPLAY_ACTIVITY);
@@ -767,16 +787,20 @@
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
VIRTUAL_DISPLAY_ACTIVITY);
- mAmWmState.assertFocusedStack("Focus must remain on primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+ assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
// Launch activity on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
mAmWmState.assertFocusedActivity("Focus must be on secondary display",
TEST_ACTIVITY_NAME);
final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- assertTrue("Focused stack must be on secondary display",
- FULLSCREEN_WORKSPACE_STACK_ID != externalFocusedStackId);
+ focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+ assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
+ focusedStack.mDisplayId);
// Launch other activity with different uid and check it is launched on dynamic stack on
// secondary display.
@@ -801,8 +825,11 @@
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
VIRTUAL_DISPLAY_ACTIVITY);
- mAmWmState.assertFocusedStack("Focus must remain on primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+ assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
// Launch activity with different uid on secondary display.
final String startCmd = "am start -n " + SECOND_PACKAGE_NAME + "/." + SECOND_ACTIVITY_NAME;
@@ -814,8 +841,9 @@
mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME);
final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- assertTrue("Focused stack must be on secondary display",
- FULLSCREEN_WORKSPACE_STACK_ID != externalFocusedStackId);
+ focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+ assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
+ focusedStack.mDisplayId);
// Launch another activity with third different uid from app on secondary display and check
// it is launched on secondary display.
@@ -877,8 +905,11 @@
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
VIRTUAL_DISPLAY_ACTIVITY);
- mAmWmState.assertFocusedStack("Focus must remain on primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+ assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
// Launch other activity with different uid on secondary display.
final String startCmd = "am start -n " + SECOND_PACKAGE_NAME + "/." + SECOND_ACTIVITY_NAME;
@@ -890,8 +921,9 @@
mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME);
final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- assertTrue("Focused stack must be on secondary display",
- FULLSCREEN_WORKSPACE_STACK_ID != externalFocusedStackId);
+ focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+ assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
+ focusedStack.mDisplayId);
// Check that owner uid can launch its own activity on secondary display.
final String broadcastAction = componentName + ".LAUNCH_BROADCAST_ACTION";
@@ -917,16 +949,20 @@
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
VIRTUAL_DISPLAY_ACTIVITY);
- mAmWmState.assertFocusedStack("Focus must remain on primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+ assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
// Launch activity on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
mAmWmState.assertFocusedActivity("Focus must be on secondary display",
TEST_ACTIVITY_NAME);
final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- assertTrue("Focused stack must be on secondary display",
- FULLSCREEN_WORKSPACE_STACK_ID != externalFocusedStackId);
+ focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+ assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
+ focusedStack.mDisplayId);
final String logSeparator = clearLogcat();
@@ -1052,15 +1088,9 @@
public void testDisplayResize() throws Exception {
if (!supportsMultiDisplay()) { return; }
- // Start launching activity.
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
-
- mAmWmState.waitForValidState(mDevice, LAUNCHING_ACTIVITY, DOCKED_STACK_ID);
// Create new virtual display.
- final DisplayState newDisplay =
- new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
+ final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
// Launch a resizeable activity on new secondary display.
final String initialLogSeparator = clearLogcat();
@@ -1072,14 +1102,10 @@
// Grab reported sizes and compute new with slight size change.
final ReportedSizes initialSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
initialLogSeparator);
- final Rectangle initialBounds
- = mAmWmState.getAmState().getStackById(DOCKED_STACK_ID).getBounds();
- final Rectangle newBounds = new Rectangle(initialBounds.x, initialBounds.y,
- initialBounds.width + SIZE_VALUE_SHIFT, initialBounds.height + SIZE_VALUE_SHIFT);
// Resize the docked stack, so that activity with virtual display will also be resized.
final String logSeparator = clearLogcat();
- resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
+ executeShellCommand(getResizeVirtualDisplayCommand());
mAmWmState.waitForWithAmState(mDevice, amState -> {
try {
@@ -1091,16 +1117,8 @@
}
}, "Wait for the configuration change to happen and for activity to be resumed.");
- mAmWmState.computeState(mDevice, new String[] {RESIZEABLE_ACTIVITY_NAME, LAUNCHING_ACTIVITY,
+ mAmWmState.computeState(mDevice, new String[] {RESIZEABLE_ACTIVITY_NAME,
VIRTUAL_DISPLAY_ACTIVITY}, false /* compareTaskAndStackBounds */);
- mAmWmState.assertDockedTaskBounds(newBounds.width, newBounds.height,
- LAUNCHING_ACTIVITY);
- mAmWmState.assertContainsStack("Must contain docked stack", DOCKED_STACK_ID);
- mAmWmState.assertContainsStack("Must contain fullscreen stack",
- FULLSCREEN_WORKSPACE_STACK_ID);
- assertEquals(new Rectangle(0, 0, newBounds.width, newBounds.height),
- mAmWmState.getAmState().getStackById(DOCKED_STACK_ID).getBounds());
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true);
mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true);
@@ -1112,12 +1130,8 @@
logSeparator);
assertTrue(updatedSize.widthDp <= initialSize.widthDp);
assertTrue(updatedSize.heightDp <= initialSize.heightDp);
- assertTrue(updatedSize.displayWidth <= initialSize.displayWidth);
- assertTrue(updatedSize.displayHeight <= initialSize.displayHeight);
- final boolean widthUpdated = updatedSize.metricsWidth < initialSize.metricsWidth;
- final boolean heightUpdated = updatedSize.metricsHeight < initialSize.metricsHeight;
- assertTrue("Either width or height must be updated after split-screen resize",
- widthUpdated ^ heightUpdated);
+ assertTrue(updatedSize.displayWidth == initialSize.displayWidth / 2);
+ assertTrue(updatedSize.displayHeight == initialSize.displayHeight / 2);
}
/** Read the number of configuration changes sent to activity from logs. */
@@ -1174,7 +1188,7 @@
// Check that task has moved from primary display to secondary.
final int taskNumFinal = mAmWmState.getAmState().getStackById(defaultDisplayStackId)
.getTasks().size();
- mAmWmState.assertEquals("Task number in fullscreen stack must be decremented.", taskNum - 1,
+ mAmWmState.assertEquals("Task number in default stack must be decremented.", taskNum - 1,
taskNumFinal);
final int taskNumFinalOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
.getTasks().size();
@@ -1246,12 +1260,15 @@
null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
// Check that second activity gets launched on the default display
- final ActivityManagerState.ActivityStack fullscreenStack =
- mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID);
+ final int defaultDisplayFrontStackId = mAmWmState.getAmState().getFrontStackId(
+ DEFAULT_DISPLAY_ID);
+ final ActivityManagerState.ActivityStack defaultDisplayFrontStack =
+ mAmWmState.getAmState().getStackById(defaultDisplayFrontStackId);
assertEquals("Activity launched on default display must be resumed",
- getActivityComponentName(ALT_LAUNCHING_ACTIVITY), fullscreenStack.mResumedActivity);
+ getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
+ defaultDisplayFrontStack.mResumedActivity);
mAmWmState.assertFocusedStack("Focus must be on primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ defaultDisplayFrontStackId);
executeShellCommand("am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY));
mAmWmState.waitForFocusedStack(mDevice, frontStackId);
@@ -1293,13 +1310,14 @@
mAmWmState.waitForValidState(mDevice, new String[] {TEST_ACTIVITY_NAME},
null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
- // Check that the second activity is launched onto the fullscreen stack
- final ActivityManagerState.ActivityStack fullscreenStack =
- mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID);
+ // Check that the second activity is launched on the default display
+ final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ final ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(focusedStackId);
assertEquals("Activity launched on default display must be resumed",
- getActivityComponentName(TEST_ACTIVITY_NAME), fullscreenStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on primary display",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ getActivityComponentName(TEST_ACTIVITY_NAME), focusedStack.mResumedActivity);
+ assertEquals("Focus must be on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
+ "--ez new_task true --es target_activity " + LAUNCHING_ACTIVITY);
@@ -1793,6 +1811,11 @@
" --es command destroy_display";
}
+ private static String getResizeVirtualDisplayCommand() {
+ return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
+ " --es command resize_display";
+ }
+
/** Checks if the device supports multi-display. */
private boolean supportsMultiDisplay() throws Exception {
return hasDeviceFeature("android.software.activities_on_secondary_displays");
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
index 65f507e..37c9d93 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
@@ -35,11 +35,13 @@
*/
public class ActivityManagerPinnedStackTests extends ActivityManagerTestBase {
private static final String TEST_ACTIVITY = "TestActivity";
+ private static final String TEST_ACTIVITY_WITH_SAME_AFFINITY = "TestActivityWithSameAffinity";
private static final String TRANSLUCENT_TEST_ACTIVITY = "TranslucentTestActivity";
private static final String NON_RESIZEABLE_ACTIVITY = "NonResizeableActivity";
private static final String RESUME_WHILE_PAUSING_ACTIVITY = "ResumeWhilePausingActivity";
private static final String PIP_ACTIVITY = "PipActivity";
private static final String PIP_ACTIVITY2 = "PipActivity2";
+ private static final String PIP_ACTIVITY_WITH_SAME_AFFINITY = "PipActivityWithSameAffinity";
private static final String ALWAYS_FOCUSABLE_PIP_ACTIVITY = "AlwaysFocusablePipActivity";
private static final String LAUNCH_INTO_PINNED_STACK_PIP_ACTIVITY =
"LaunchIntoPinnedStackPipActivity";
@@ -914,6 +916,81 @@
assertTrue(lifecycleCounts.mPauseCount == 0);
}
+ public void testLaunchTaskByComponentMatchMultipleTasks() throws Exception {
+ if (!supportsPip()) return;
+
+ // Launch a fullscreen activity which will launch a PiP activity in a new task with the same
+ // affinity
+ launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+ launchActivityInStack(PIP_ACTIVITY_WITH_SAME_AFFINITY, PINNED_STACK_ID);
+ assertPinnedStackExists();
+
+ // Launch the root activity again...
+ int rootActivityTaskId = mAmWmState.getAmState().getTaskByActivityName(
+ TEST_ACTIVITY_WITH_SAME_AFFINITY).mTaskId;
+ launchHomeActivity();
+ launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+
+ // ...and ensure that the root activity task is found and reused, and that the pinned stack
+ // is unaffected
+ assertPinnedStackExists();
+ mAmWmState.assertFocusedActivity("Expected root activity focused",
+ TEST_ACTIVITY_WITH_SAME_AFFINITY);
+ assertTrue(rootActivityTaskId == mAmWmState.getAmState().getTaskByActivityName(
+ TEST_ACTIVITY_WITH_SAME_AFFINITY).mTaskId);
+ }
+
+ public void testLaunchTaskByAffinityMatchMultipleTasks() throws Exception {
+ if (!supportsPip()) return;
+
+ // Launch a fullscreen activity which will launch a PiP activity in a new task with the same
+ // affinity, and also launch another activity in the same task, while finishing itself. As
+ // a result, the task will not have a component matching the same activity as what it was
+ // started with
+ launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY,
+ EXTRA_START_ACTIVITY, getActivityComponentName(TEST_ACTIVITY),
+ EXTRA_FINISH_SELF_ON_RESUME, "true");
+ mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+ launchActivityInStack(PIP_ACTIVITY_WITH_SAME_AFFINITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY_WITH_SAME_AFFINITY, PINNED_STACK_ID);
+ assertPinnedStackExists();
+
+ // Launch the root activity again...
+ int rootActivityTaskId = mAmWmState.getAmState().getTaskByActivityName(
+ TEST_ACTIVITY).mTaskId;
+ launchHomeActivity();
+ launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+
+ // ...and ensure that even while matching purely by task affinity, the root activity task is
+ // found and reused, and that the pinned stack is unaffected
+ assertPinnedStackExists();
+ mAmWmState.assertFocusedActivity("Expected root activity focused", TEST_ACTIVITY);
+ assertTrue(rootActivityTaskId == mAmWmState.getAmState().getTaskByActivityName(
+ TEST_ACTIVITY).mTaskId);
+ }
+
+ public void testLaunchTaskByAffinityMatchSingleTask() throws Exception {
+ if (!supportsPip()) return;
+
+ // Launch an activity into the pinned stack with a fixed affinity
+ launchActivityInStack(TEST_ACTIVITY_WITH_SAME_AFFINITY, PINNED_STACK_ID,
+ EXTRA_START_ACTIVITY, getActivityComponentName(PIP_ACTIVITY),
+ EXTRA_FINISH_SELF_ON_RESUME, "true");
+ mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ assertPinnedStackExists();
+
+ // Launch the root activity again, of the matching task and ensure that we expand to
+ // fullscreen
+ int activityTaskId = mAmWmState.getAmState().getTaskByActivityName(
+ PIP_ACTIVITY).mTaskId;
+ launchHomeActivity();
+ launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+ mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+ assertPinnedStackDoesNotExist();
+ assertTrue(activityTaskId == mAmWmState.getAmState().getTaskByActivityName(
+ PIP_ACTIVITY).mTaskId);
+ }
+
/**
* Called after the given {@param activityName} has been moved to the fullscreen stack. Ensures
* that the {@param focusedStackId} is focused, and checks the top and/or bottom tasks in the
diff --git a/tests/fragment/src/android/fragment/cts/TransitionFragment.java b/tests/fragment/src/android/fragment/cts/TransitionFragment.java
index fbe2250..17363ac 100644
--- a/tests/fragment/src/android/fragment/cts/TransitionFragment.java
+++ b/tests/fragment/src/android/fragment/cts/TransitionFragment.java
@@ -71,7 +71,7 @@
}
void waitForTransition() throws InterruptedException {
- verify(mListener, within(300)).onTransitionEnd(any());
+ verify(mListener, within(500)).onTransitionEnd(any());
reset(mListener);
}
diff --git a/tests/sensor/jni/SensorTestCases.cpp b/tests/sensor/jni/SensorTestCases.cpp
index f86f262..43e07a4 100644
--- a/tests/sensor/jni/SensorTestCases.cpp
+++ b/tests/sensor/jni/SensorTestCases.cpp
@@ -135,7 +135,7 @@
// Test sensor direct report functionality
void SensorTest::testDirectReport(JNIEnv* env, int32_t sensorType, int32_t channelType, int32_t rateLevel) {
constexpr size_t kEventSize = sizeof(ASensorEvent);
- constexpr size_t kNEvent = 500;
+ constexpr size_t kNEvent = 4096; // enough to contain 1.5 * 800 * 2.2 events
constexpr size_t kMemSize = kEventSize * kNEvent;
// value check criterion
diff --git a/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java b/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
index 6c2462f..78ac59d 100644
--- a/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
@@ -65,7 +65,8 @@
private static final int TEST_RUN_TIME_PERIOD_MILLISEC = 5000;
private static final int ALLOWED_SENSOR_INIT_TIME_MILLISEC = 500;
private static final int SENSORS_EVENT_SIZE = 104;
- private static final int SHARED_MEMORY_SIZE = 2000 * SENSORS_EVENT_SIZE;
+ private static final int SENSORS_EVENT_COUNT = 10240; // 800Hz * 2.2 * 5 sec + extra
+ private static final int SHARED_MEMORY_SIZE = SENSORS_EVENT_COUNT * SENSORS_EVENT_SIZE;
private static final float MERCY_FACTOR = 0.1f;
private static native boolean nativeReadHardwareBuffer(HardwareBuffer hardwareBuffer,
diff --git a/tests/tests/content/res/font/broken_xmlfont.xml b/tests/tests/content/res/font/broken_xmlfont.xml
new file mode 100644
index 0000000..be30f2f
--- /dev/null
+++ b/tests/tests/content/res/font/broken_xmlfont.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+ <font android:fontStyle="normal" android:fontWeight="400" android:font="@font/brokenfont" />
+</font-family>
diff --git a/tests/tests/content/res/font/brokenfont.ttf b/tests/tests/content/res/font/brokenfont.ttf
new file mode 100644
index 0000000..12b2713
--- /dev/null
+++ b/tests/tests/content/res/font/brokenfont.ttf
Binary files differ
diff --git a/tests/tests/content/res/font/brokenfont_source.ttx b/tests/tests/content/res/font/brokenfont_source.ttx
new file mode 100644
index 0000000..34217b4
--- /dev/null
+++ b/tests/tests/content/res/font/brokenfont_source.ttx
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="a"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Mon May 1 00:00:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="a" width="500" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="a" />
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="a" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Broken Font
+ </namerecord>
+ <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Broken Font
+ </namerecord>
+ <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ BrokenFont-Regular
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Broken Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Broken Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ BrokenFont-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/tests/content/res/font/invalid_xmlfont_nosource.xml b/tests/tests/content/res/font/invalid_xmlfont_nosource.xml
new file mode 100644
index 0000000..6b70222
--- /dev/null
+++ b/tests/tests/content/res/font/invalid_xmlfont_nosource.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- missing the android:font entry -->
+ <font android:fontStyle="normal" android:fontWeight="400" />
+</font-family>
diff --git a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
index 89ea29b..be15e57 100644
--- a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
@@ -814,6 +814,30 @@
} catch (RuntimeException e) {
// pass
}
+
+ try {
+ mResources.getFont(R.font.invalid_xmlfont_nosource);
+ fail();
+ } catch (RuntimeException e) {
+ // pass
+ }
+
+ }
+
+ public void testGetFont_brokenFontFiles() {
+ try {
+ mResources.getFont(R.font.brokenfont);
+ fail();
+ } catch (RuntimeException e) {
+ // pass
+ }
+
+ try {
+ mResources.getFont(R.font.broken_xmlfont);
+ fail();
+ } catch (RuntimeException e) {
+ // pass
+ }
}
public void testGetFont_fontFileIsCached() {
diff --git a/tests/tests/graphics/Android.mk b/tests/tests/graphics/Android.mk
index 3059d71..af59cbf 100644
--- a/tests/tests/graphics/Android.mk
+++ b/tests/tests/graphics/Android.mk
@@ -40,6 +40,9 @@
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts
+# Enforce public / test api only
+LOCAL_SDK_VERSION := test_current
+
include $(BUILD_CTS_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
index 52db21f..b36c23a 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
@@ -1153,6 +1153,13 @@
}
@Test
+ public void testClipOutPath() {
+ final Path p = new Path();
+ p.addRect(new RectF(5, 5, 10, 10), Direction.CW);
+ assertTrue(mCanvas.clipOutPath(p));
+ }
+
+ @Test
public void testClipInversePath() {
final Path p = new Path();
p.addRoundRect(new RectF(0, 0, 10, 10), 0.5f, 0.5f, Direction.CW);
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp b/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp
index 37b434e..c57682f 100644
--- a/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp
+++ b/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp
@@ -57,6 +57,58 @@
}
+
+// Test creating a default stream with specific devices
+void runtest_aaudio_devices(int32_t deviceId, bool expectFail) {
+ AAudioStreamBuilder *aaudioBuilder = nullptr;
+ AAudioStream *aaudioStream = nullptr;
+
+ // Use an AAudioStreamBuilder to define the stream.
+ aaudio_result_t result = AAudio_createStreamBuilder(&aaudioBuilder);
+ ASSERT_EQ(AAUDIO_OK, result);
+ ASSERT_NE(nullptr, aaudioBuilder);
+
+ AAudioStreamBuilder_setDeviceId(aaudioBuilder,deviceId);
+
+ // Create an AAudioStream using the Builder.
+ result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
+ if (expectFail) {
+ ASSERT_NE(AAUDIO_OK, result);
+ ASSERT_EQ(nullptr, aaudioStream);
+ } else {
+ // Pass or fail is OK. Just don't crash.
+ ASSERT_TRUE(((result < 0) && (aaudioStream == nullptr))
+ || ((result == AAUDIO_OK) && (aaudioStream != nullptr)));
+ }
+
+ // Cleanup
+ EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_delete(aaudioBuilder));
+ if (aaudioStream != nullptr) {
+ AAudioStream_close(aaudioStream);
+ }
+}
+
+TEST(test_aaudio, aaudio_stream_device_unspecified) {
+ runtest_aaudio_devices(AAUDIO_DEVICE_UNSPECIFIED, false);
+}
+
+/* FIXME - why can we open this device? What is an illegal deviceId?
+TEST(test_aaudio, aaudio_stream_device_absurd) {
+ runtest_aaudio_devices(19736459, true);
+}
+*/
+/* FIXME review
+TEST(test_aaudio, aaudio_stream_device_reasonable) {
+ runtest_aaudio_devices(1, false);
+}
+*/
+
+/* FIXME - why can we open this device? What is an illegal deviceId?
+TEST(test_aaudio, aaudio_stream_device_negative) {
+ runtest_aaudio_devices(-765, true);
+}
+*/
+
// Test creating a default stream with everything unspecified.
TEST(test_aaudio, aaudio_stream_unspecified) {
AAudioStreamBuilder *aaudioBuilder = nullptr;
@@ -120,7 +172,12 @@
AAudioStreamBuilder_setBufferCapacityInFrames(aaudioBuilder, 2000);
// Create an AAudioStream using the Builder.
- ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
+ result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
+ if (requestedSharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE
+ && result != AAUDIO_OK) {
+ return; // EXCLUSIVE just may not be available. Should not crash.
+ }
+ ASSERT_EQ(AAUDIO_OK, result);
EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_delete(aaudioBuilder));
EXPECT_EQ(AAUDIO_STREAM_STATE_OPEN, AAudioStream_getState(aaudioStream));
@@ -184,15 +241,14 @@
// Write some data while we are running. Read counter should be advancing.
writeLoops = 1 * actualSampleRate / framesPerBurst; // 1 second
ASSERT_LT(2, writeLoops); // detect absurdly high framesPerBurst
- timeoutNanos = 10 * NANOS_PER_SECOND * framesPerBurst / actualSampleRate; // bursts
+ timeoutNanos = 100 * (NANOS_PER_SECOND * framesPerBurst / actualSampleRate); // N bursts
framesWritten = 1;
aaudioFramesRead = AAudioStream_getFramesRead(aaudioStream);
aaudioFramesRead1 = aaudioFramesRead;
int64_t beginTime = getNanoseconds(CLOCK_MONOTONIC);
do {
framesWritten = AAudioStream_write(aaudioStream, data, framesPerBurst, timeoutNanos);
- ASSERT_GE(framesWritten, 0);
- ASSERT_LE(framesWritten, framesPerBurst);
+ ASSERT_EQ(framesWritten, framesPerBurst);
framesTotal += framesWritten;
aaudioFramesWritten = AAudioStream_getFramesWritten(aaudioStream);
@@ -211,8 +267,8 @@
aaudioFramesRead2 = AAudioStream_getFramesRead(aaudioStream);
int64_t endTime = getNanoseconds(CLOCK_MONOTONIC);
ASSERT_GT(aaudioFramesRead2, 0);
- ASSERT_GT(aaudioFramesRead2, aaudioFramesRead1);
- ASSERT_LE(aaudioFramesRead2, aaudioFramesWritten);
+ EXPECT_GT(aaudioFramesRead2, aaudioFramesRead1);
+
// TODO why is AudioTrack path so inaccurate?
const double rateTolerance = 200.0; // arbitrary tolerance for sample rate
@@ -236,15 +292,16 @@
aaudioFramesRead = AAudioStream_getFramesRead(aaudioStream);
ASSERT_GE(aaudioFramesRead, aaudioFramesRead2); // monotonic increase
- // Use this to sleep by waiting for something that won't happen.
- AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_PAUSED, &state, timeoutNanos);
+ // Use this to sleep by waiting for a state that won't happen.
+ timeoutNanos = 100 * NANOS_PER_MILLISECOND;
+ AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_OPEN, &state, timeoutNanos);
aaudioFramesRead2 = AAudioStream_getFramesRead(aaudioStream);
EXPECT_EQ(aaudioFramesRead, aaudioFramesRead2);
// ------------------- TEST FLUSH -----------------
// Prime the buffer.
timeoutNanos = 0;
- writeLoops = 100;
+ writeLoops = 1000;
do {
framesWritten = AAudioStream_write(aaudioStream, data, framesPerBurst, timeoutNanos);
framesTotal += framesWritten;
@@ -282,12 +339,10 @@
runtest_aaudio_stream(AAUDIO_SHARING_MODE_SHARED);
}
-/* TODO Enable exclusive mode test.
-// Test Writing to an AAudioStream using EXCLUSIVE sharing mode.
+// Test Writing to an AAudioStream using EXCLUSIVE sharing mode. It may fail gracefully.
TEST(test_aaudio, aaudio_stream_exclusive) {
runtest_aaudio_stream(AAUDIO_SHARING_MODE_EXCLUSIVE);
}
-*/
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp b/tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp
index 2ec1e1c..3786967 100644
--- a/tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp
+++ b/tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp
@@ -18,17 +18,55 @@
#define LOG_TAG "AAudioTest"
#include <gtest/gtest.h>
+#include <atomic>
#include <utils/Log.h>
#include <aaudio/AAudio.h>
#include "test_aaudio.h"
typedef struct AAudioCallbackTestData {
- int32_t callbackCount;
int32_t expectedFramesPerCallback;
int32_t actualFramesPerCallback;
+ int32_t minLatency;
+ int32_t maxLatency;
+ std::atomic<aaudio_result_t> callbackError;
+ std::atomic<int32_t> callbackCount;
} AAudioCallbackTestData;
+static int32_t measureLatency(AAudioStream *stream) {
+ int64_t presentationTime = 0;
+ int64_t presentationPosition = 0;
+ int64_t now = getNanoseconds();
+ int32_t sampleRate = AAudioStream_getSampleRate(stream);
+ int64_t framesWritten = AAudioStream_getFramesWritten(stream);
+ aaudio_result_t result = AAudioStream_getTimestamp(stream,
+ CLOCK_MONOTONIC,
+ &presentationPosition,
+ &presentationTime);
+ if (result < 0) {
+ return result;
+ }
+ // Calculate when the last frame written would be played.
+ int64_t deltaFrames = framesWritten - presentationPosition;
+ EXPECT_GE(framesWritten, presentationPosition);
+ int64_t calculatedDeltaNanos = deltaFrames * NANOS_PER_SECOND / sampleRate;
+ int64_t calculatedTimeNanos = presentationTime + calculatedDeltaNanos;
+ int64_t latencyNanos = calculatedTimeNanos - now;
+ int32_t latencyMillis = (int32_t) ((latencyNanos + NANOS_PER_MILLISECOND - 1)
+ / NANOS_PER_MILLISECOND);
+ return latencyMillis;
+}
+
+static void MyErrorCallbackProc(
+ AAudioStream *stream,
+ void *userData,
+ aaudio_result_t error) {
+ (void) stream;
+ AAudioCallbackTestData *myData = (AAudioCallbackTestData *) userData;
+ myData->callbackError = error;
+
+}
+
// Callback function that fills the audio output buffer.
static aaudio_data_callback_result_t MyDataCallbackProc(
AAudioStream *stream,
@@ -54,6 +92,17 @@
float *floatData = (float *) audioData;
for (int i = 0; i < numSamples; i++) *floatData++ = 0.0f;
}
+
+ int32_t latency = measureLatency(stream);
+ if (latency > 0) {
+ if (latency < myData->minLatency) {
+ myData->minLatency = latency;
+ }
+ if (latency > myData->maxLatency) {
+ myData->maxLatency = latency;
+ }
+ }
+
myData->callbackCount++;
return AAUDIO_CALLBACK_RESULT_CONTINUE;
}
@@ -61,7 +110,7 @@
// Test Writing to an AAudioStream using a Callback
void runtest_aaudio_callback(aaudio_sharing_mode_t requestedSharingMode,
int32_t framesPerDataCallback) {
- AAudioCallbackTestData myTestData = { 0, 0, 0 };
+ AAudioCallbackTestData myTestData;
const int32_t requestedSampleRate = 48000;
const int32_t requestedSamplesPerFrame = 2;
const aaudio_audio_format_t requestedDataFormat = AAUDIO_FORMAT_PCM_I16;
@@ -79,6 +128,10 @@
AAudioStream *stream = nullptr;
aaudio_result_t result = AAUDIO_OK;
+ myTestData.callbackCount.store(0);
+ myTestData.callbackError = AAUDIO_OK;
+ myTestData.actualFramesPerCallback = 0;
+ myTestData.expectedFramesPerCallback = 0;
// Use an AAudioStreamBuilder to define the stream.
result = AAudio_createStreamBuilder(&builder);
@@ -93,6 +146,7 @@
AAudioStreamBuilder_setSharingMode(builder, requestedSharingMode);
AAudioStreamBuilder_setBufferCapacityInFrames(builder, 2000);
+ AAudioStreamBuilder_setErrorCallback(builder, MyErrorCallbackProc, &myTestData);
AAudioStreamBuilder_setDataCallback(builder, MyDataCallbackProc, &myTestData);
if (framesPerDataCallback != AAUDIO_UNSPECIFIED) {
AAudioStreamBuilder_setFramesPerDataCallback(builder, framesPerDataCallback);
@@ -118,8 +172,8 @@
actualDataFormat = AAudioStream_getFormat(stream);
- // TODO test this on full build
- // ASSERT_NE(AAUDIO_DEVICE_UNSPECIFIED, AAudioStream_getDeviceId(stream));
+ // TODO Why does getDeviceId() always return 0?
+ // EXPECT_NE(AAUDIO_DEVICE_UNSPECIFIED, AAudioStream_getDeviceId(stream));
framesPerBurst = AAudioStream_getFramesPerBurst(stream);
ASSERT_TRUE(framesPerBurst >= 16 && framesPerBurst <= 1024); // TODO what is min/max?
@@ -136,18 +190,27 @@
// Start/stop more than once to see if it fails after the first time.
// Write some data and measure the rate to see if the timing is OK.
for (int loopIndex = 0; loopIndex < 2; loopIndex++) {
+
myTestData.callbackCount = 0;
+ myTestData.minLatency = INT32_MAX;
+ myTestData.maxLatency = 0;
+ myTestData.callbackCount.store(0);
+
myTestData.expectedFramesPerCallback = actualFramesPerDataCallback;
// Start and wait for server to respond.
ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStart(stream));
+
ASSERT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(stream,
AAUDIO_STREAM_STATE_STARTING,
&state,
DEFAULT_STATE_TIMEOUT));
EXPECT_EQ(AAUDIO_STREAM_STATE_STARTED, state);
- sleep(2);
+ sleep(2); // let the stream run
+
+ ASSERT_EQ(myTestData.callbackError.load(), AAUDIO_OK);
+ ASSERT_GT(myTestData.callbackCount.load(), 10);
// For more coverage, alternate pausing and stopping.
if ((loopIndex & 1) == 0) {
@@ -168,29 +231,34 @@
EXPECT_EQ(AAUDIO_STREAM_STATE_STOPPED, state);
}
- int32_t oldCallbackCount = myTestData.callbackCount;
+ int32_t oldCallbackCount = myTestData.callbackCount.load();
EXPECT_GT(oldCallbackCount, 10);
sleep(1);
- EXPECT_EQ(oldCallbackCount, myTestData.callbackCount); // expect not advancing
+ EXPECT_EQ(oldCallbackCount, myTestData.callbackCount.load()); // expect not advancing
if (framesPerDataCallback != AAUDIO_UNSPECIFIED) {
ASSERT_EQ(framesPerDataCallback, myTestData.actualFramesPerCallback);
}
+
+ EXPECT_GE(myTestData.minLatency, 1); // Absurdly low
+ EXPECT_LE(myTestData.maxLatency, 200); // Absurdly high, should be < 30
+ //printf("latency: %d, %d\n", myTestData.minLatency, myTestData.maxLatency);
}
+ ASSERT_EQ(myTestData.callbackError.load(), AAUDIO_OK);
+
EXPECT_EQ(AAUDIO_OK, AAudioStream_close(stream));
}
// Test Using an AAudioStream callback in SHARED mode.
-
TEST(test_aaudio, aaudio_callback_shared_unspecified) {
-runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, AAUDIO_UNSPECIFIED);
+ runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, AAUDIO_UNSPECIFIED);
}
TEST(test_aaudio, aaudio_callback_shared_109) {
-runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, 109); // arbitrary prime number < 192
+ runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, 109); // arbitrary prime number < 192
}
TEST(test_aaudio, aaudio_callback_shared_223) {
-runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, 223); // arbitrary prime number > 192
+ runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, 223); // arbitrary prime number > 192
}
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp b/tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp
index 8599d48..6662b72 100644
--- a/tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp
+++ b/tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp
@@ -1,7 +1,7 @@
/*
* Copyright 2017 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Licensed under the Apache License, Version 2.0 (the "License", ENUM_CANNOT_CHANGE);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
@@ -25,53 +25,55 @@
// Make sure enums do not change value.
TEST(test_aaudio_misc, aaudio_freeze_enums) {
- ASSERT_EQ(0, AAUDIO_DIRECTION_OUTPUT);
- ASSERT_EQ(1, AAUDIO_DIRECTION_INPUT);
+#define ENUM_CANNOT_CHANGE "enum in API cannot change"
- ASSERT_EQ(-1, AAUDIO_FORMAT_INVALID);
- ASSERT_EQ(0, AAUDIO_FORMAT_UNSPECIFIED);
- ASSERT_EQ(1, AAUDIO_FORMAT_PCM_I16);
- ASSERT_EQ(2, AAUDIO_FORMAT_PCM_FLOAT);
+ static_assert(0 == AAUDIO_DIRECTION_OUTPUT, ENUM_CANNOT_CHANGE);
+ static_assert(1 == AAUDIO_DIRECTION_INPUT, ENUM_CANNOT_CHANGE);
- ASSERT_EQ(0, AAUDIO_OK);
- ASSERT_EQ(-900, AAUDIO_ERROR_BASE);
- ASSERT_EQ(-899, AAUDIO_ERROR_DISCONNECTED);
- ASSERT_EQ(-898, AAUDIO_ERROR_ILLEGAL_ARGUMENT);
- ASSERT_EQ(-897, AAUDIO_ERROR_INCOMPATIBLE);
- ASSERT_EQ(-896, AAUDIO_ERROR_INTERNAL);
- ASSERT_EQ(-895, AAUDIO_ERROR_INVALID_STATE);
- ASSERT_EQ(-894, AAUDIO_ERROR_UNEXPECTED_STATE);
- ASSERT_EQ(-893, AAUDIO_ERROR_UNEXPECTED_VALUE);
- ASSERT_EQ(-892, AAUDIO_ERROR_INVALID_HANDLE);
- ASSERT_EQ(-891, AAUDIO_ERROR_INVALID_QUERY);
- ASSERT_EQ(-890, AAUDIO_ERROR_UNIMPLEMENTED);
- ASSERT_EQ(-889, AAUDIO_ERROR_UNAVAILABLE);
- ASSERT_EQ(-888, AAUDIO_ERROR_NO_FREE_HANDLES);
- ASSERT_EQ(-887, AAUDIO_ERROR_NO_MEMORY);
- ASSERT_EQ(-886, AAUDIO_ERROR_NULL);
- ASSERT_EQ(-885, AAUDIO_ERROR_TIMEOUT);
- ASSERT_EQ(-884, AAUDIO_ERROR_WOULD_BLOCK);
- ASSERT_EQ(-883, AAUDIO_ERROR_INVALID_FORMAT);
- ASSERT_EQ(-882, AAUDIO_ERROR_OUT_OF_RANGE);
- ASSERT_EQ(-881, AAUDIO_ERROR_NO_SERVICE);
+ static_assert(-1 == AAUDIO_FORMAT_INVALID, ENUM_CANNOT_CHANGE);
+ static_assert(0 == AAUDIO_FORMAT_UNSPECIFIED, ENUM_CANNOT_CHANGE);
+ static_assert(1 == AAUDIO_FORMAT_PCM_I16, ENUM_CANNOT_CHANGE);
+ static_assert(2 == AAUDIO_FORMAT_PCM_FLOAT, ENUM_CANNOT_CHANGE);
- ASSERT_EQ(0, AAUDIO_STREAM_STATE_UNINITIALIZED);
- ASSERT_EQ(1, AAUDIO_STREAM_STATE_UNKNOWN);
- ASSERT_EQ(2, AAUDIO_STREAM_STATE_OPEN);
- ASSERT_EQ(3, AAUDIO_STREAM_STATE_STARTING);
- ASSERT_EQ(4, AAUDIO_STREAM_STATE_STARTED);
- ASSERT_EQ(5, AAUDIO_STREAM_STATE_PAUSING);
- ASSERT_EQ(6, AAUDIO_STREAM_STATE_PAUSED);
- ASSERT_EQ(7, AAUDIO_STREAM_STATE_FLUSHING);
- ASSERT_EQ(8, AAUDIO_STREAM_STATE_FLUSHED);
- ASSERT_EQ(9, AAUDIO_STREAM_STATE_STOPPING);
- ASSERT_EQ(10, AAUDIO_STREAM_STATE_STOPPED);
- ASSERT_EQ(11, AAUDIO_STREAM_STATE_CLOSING);
- ASSERT_EQ(12, AAUDIO_STREAM_STATE_CLOSED);
+ static_assert(0 == AAUDIO_OK, ENUM_CANNOT_CHANGE);
+ static_assert(-900 == AAUDIO_ERROR_BASE, ENUM_CANNOT_CHANGE);
+ static_assert(-899 == AAUDIO_ERROR_DISCONNECTED, ENUM_CANNOT_CHANGE);
+ static_assert(-898 == AAUDIO_ERROR_ILLEGAL_ARGUMENT, ENUM_CANNOT_CHANGE);
+ static_assert(-897 == AAUDIO_ERROR_INCOMPATIBLE, ENUM_CANNOT_CHANGE);
+ static_assert(-896 == AAUDIO_ERROR_INTERNAL, ENUM_CANNOT_CHANGE);
+ static_assert(-895 == AAUDIO_ERROR_INVALID_STATE, ENUM_CANNOT_CHANGE);
+ static_assert(-894 == AAUDIO_ERROR_UNEXPECTED_STATE, ENUM_CANNOT_CHANGE);
+ static_assert(-893 == AAUDIO_ERROR_UNEXPECTED_VALUE, ENUM_CANNOT_CHANGE);
+ static_assert(-892 == AAUDIO_ERROR_INVALID_HANDLE, ENUM_CANNOT_CHANGE);
+ static_assert(-891 == AAUDIO_ERROR_INVALID_QUERY, ENUM_CANNOT_CHANGE);
+ static_assert(-890 == AAUDIO_ERROR_UNIMPLEMENTED, ENUM_CANNOT_CHANGE);
+ static_assert(-889 == AAUDIO_ERROR_UNAVAILABLE, ENUM_CANNOT_CHANGE);
+ static_assert(-888 == AAUDIO_ERROR_NO_FREE_HANDLES, ENUM_CANNOT_CHANGE);
+ static_assert(-887 == AAUDIO_ERROR_NO_MEMORY, ENUM_CANNOT_CHANGE);
+ static_assert(-886 == AAUDIO_ERROR_NULL, ENUM_CANNOT_CHANGE);
+ static_assert(-885 == AAUDIO_ERROR_TIMEOUT, ENUM_CANNOT_CHANGE);
+ static_assert(-884 == AAUDIO_ERROR_WOULD_BLOCK, ENUM_CANNOT_CHANGE);
+ static_assert(-883 == AAUDIO_ERROR_INVALID_FORMAT, ENUM_CANNOT_CHANGE);
+ static_assert(-882 == AAUDIO_ERROR_OUT_OF_RANGE, ENUM_CANNOT_CHANGE);
+ static_assert(-881 == AAUDIO_ERROR_NO_SERVICE, ENUM_CANNOT_CHANGE);
- ASSERT_EQ(0, AAUDIO_SHARING_MODE_EXCLUSIVE);
- ASSERT_EQ(1, AAUDIO_SHARING_MODE_SHARED);
+ static_assert(0 == AAUDIO_STREAM_STATE_UNINITIALIZED, ENUM_CANNOT_CHANGE);
+ static_assert(1 == AAUDIO_STREAM_STATE_UNKNOWN, ENUM_CANNOT_CHANGE);
+ static_assert(2 == AAUDIO_STREAM_STATE_OPEN, ENUM_CANNOT_CHANGE);
+ static_assert(3 == AAUDIO_STREAM_STATE_STARTING, ENUM_CANNOT_CHANGE);
+ static_assert(4 == AAUDIO_STREAM_STATE_STARTED, ENUM_CANNOT_CHANGE);
+ static_assert(5 == AAUDIO_STREAM_STATE_PAUSING, ENUM_CANNOT_CHANGE);
+ static_assert(6 == AAUDIO_STREAM_STATE_PAUSED, ENUM_CANNOT_CHANGE);
+ static_assert(7 == AAUDIO_STREAM_STATE_FLUSHING, ENUM_CANNOT_CHANGE);
+ static_assert(8 == AAUDIO_STREAM_STATE_FLUSHED, ENUM_CANNOT_CHANGE);
+ static_assert(9 == AAUDIO_STREAM_STATE_STOPPING, ENUM_CANNOT_CHANGE);
+ static_assert(10 == AAUDIO_STREAM_STATE_STOPPED, ENUM_CANNOT_CHANGE);
+ static_assert(11 == AAUDIO_STREAM_STATE_CLOSING, ENUM_CANNOT_CHANGE);
+ static_assert(12 == AAUDIO_STREAM_STATE_CLOSED, ENUM_CANNOT_CHANGE);
- ASSERT_EQ(0, AAUDIO_CALLBACK_RESULT_CONTINUE);
- ASSERT_EQ(1, AAUDIO_CALLBACK_RESULT_STOP);
+ static_assert(0 == AAUDIO_SHARING_MODE_EXCLUSIVE, ENUM_CANNOT_CHANGE);
+ static_assert(1 == AAUDIO_SHARING_MODE_SHARED, ENUM_CANNOT_CHANGE);
+
+ static_assert(0 == AAUDIO_CALLBACK_RESULT_CONTINUE, ENUM_CANNOT_CHANGE);
+ static_assert(1 == AAUDIO_CALLBACK_RESULT_STOP, ENUM_CANNOT_CHANGE);
}
diff --git a/tests/tests/text/src/android/text/format/cts/FormatterTest.java b/tests/tests/text/src/android/text/format/cts/FormatterTest.java
index fa11023..8c7fdc9 100644
--- a/tests/tests/text/src/android/text/format/cts/FormatterTest.java
+++ b/tests/tests/text/src/android/text/format/cts/FormatterTest.java
@@ -19,6 +19,8 @@
import static org.junit.Assert.assertEquals;
import android.content.Context;
+import android.content.res.Configuration;
+import android.os.LocaleList;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -29,6 +31,7 @@
import java.math.BigDecimal;
import java.math.MathContext;
+import java.util.Locale;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -38,9 +41,12 @@
// test null Context
assertEquals("", Formatter.formatFileSize(null, 0));
- MathContext mc = MathContext.DECIMAL64;
- BigDecimal bd = new BigDecimal((long) 1000, mc);
- Context context = InstrumentationRegistry.getTargetContext();
+ final MathContext mc = MathContext.DECIMAL64;
+ final BigDecimal bd = new BigDecimal((long) 1000, mc);
+ final Configuration config = new Configuration();
+ config.setLocales(new LocaleList(Locale.US));
+ final Context context =
+ InstrumentationRegistry.getTargetContext().createConfigurationContext(config);
// test different long values with various length
assertEquals("0 B", Formatter.formatFileSize(context, 0));
@@ -53,6 +59,8 @@
assertEquals("0.90 kB", Formatter.formatFileSize(context, 901));
assertEquals("1.00 kB", Formatter.formatFileSize(context, bd.pow(1).longValue()));
+ assertEquals("1.50 kB", Formatter.formatFileSize(context, bd.pow(1).longValue() * 3 / 2));
+ assertEquals("12.50 kB", Formatter.formatFileSize(context, bd.pow(1).longValue() * 25 / 2));
assertEquals("1.00 MB", Formatter.formatFileSize(context, bd.pow(2).longValue()));
@@ -69,6 +77,48 @@
}
@Test
+ public void testFormatShortFileSize() {
+ // test null Context
+ assertEquals("", Formatter.formatFileSize(null, 0));
+
+ final MathContext mc = MathContext.DECIMAL64;
+ final BigDecimal bd = new BigDecimal((long) 1000, mc);
+ final Configuration config = new Configuration();
+ config.setLocales(new LocaleList(Locale.US));
+ final Context context =
+ InstrumentationRegistry.getTargetContext().createConfigurationContext(config);
+
+ // test different long values with various length
+ assertEquals("0 B", Formatter.formatShortFileSize(context, 0));
+ assertEquals("1 B", Formatter.formatShortFileSize(context, 1));
+ assertEquals("9 B", Formatter.formatShortFileSize(context, 9));
+ assertEquals("10 B", Formatter.formatShortFileSize(context, 10));
+ assertEquals("99 B", Formatter.formatShortFileSize(context, 99));
+ assertEquals("100 B", Formatter.formatShortFileSize(context, 100));
+ assertEquals("900 B", Formatter.formatShortFileSize(context, 900));
+ assertEquals("0.90 kB", Formatter.formatShortFileSize(context, 901));
+
+ assertEquals("1.0 kB", Formatter.formatShortFileSize(context, bd.pow(1).longValue()));
+ assertEquals("1.5 kB", Formatter.formatShortFileSize(context,
+ bd.pow(1).longValue() * 3 / 2));
+ assertEquals("13 kB", Formatter.formatShortFileSize(context,
+ bd.pow(1).longValue() * 25 / 2));
+
+ assertEquals("1.0 MB", Formatter.formatShortFileSize(context, bd.pow(2).longValue()));
+
+ assertEquals("1.0 GB", Formatter.formatShortFileSize(context, bd.pow(3).longValue()));
+
+ assertEquals("1.0 TB", Formatter.formatShortFileSize(context, bd.pow(4).longValue()));
+
+ assertEquals("1.0 PB", Formatter.formatShortFileSize(context, bd.pow(5).longValue()));
+
+ assertEquals("1000 PB", Formatter.formatShortFileSize(context, bd.pow(6).longValue()));
+
+ // test Negative value
+ assertEquals("-1 B", Formatter.formatShortFileSize(context, -1));
+ }
+
+ @Test
public void testFormatIpAddress() {
assertEquals("1.0.168.192", Formatter.formatIpAddress(0xC0A80001));
assertEquals("1.0.0.127", Formatter.formatIpAddress(0x7F000001));
diff --git a/tests/tests/text/src/android/text/format/cts/TimeTest.java b/tests/tests/text/src/android/text/format/cts/TimeTest.java
index 7c44c77..e085a29 100644
--- a/tests/tests/text/src/android/text/format/cts/TimeTest.java
+++ b/tests/tests/text/src/android/text/format/cts/TimeTest.java
@@ -1995,6 +1995,54 @@
}
@Test
+ public void testGetJulianMondayFromWeeksSinceEpoch() {
+ final int mondayBeforeEpoch = Time.MONDAY_BEFORE_JULIAN_EPOCH;
+ assertEquals(mondayBeforeEpoch, Time.getJulianMondayFromWeeksSinceEpoch(0));
+ assertEquals(mondayBeforeEpoch + 7, Time.getJulianMondayFromWeeksSinceEpoch(1));
+ assertEquals(mondayBeforeEpoch + 14, Time.getJulianMondayFromWeeksSinceEpoch(2));
+ assertEquals(mondayBeforeEpoch - 7, Time.getJulianMondayFromWeeksSinceEpoch(-1));
+ }
+
+ @Test
+ public void testGetWeeksSinceEpochFromJulianDay() {
+ final int epoch = Time.EPOCH_JULIAN_DAY; // a Thursday
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.SUNDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.MONDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.TUESDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.WEDNESDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.THURSDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.FRIDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.SATURDAY));
+
+ final int epochFriday = epoch + 1;
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.SUNDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.MONDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.TUESDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.WEDNESDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.THURSDAY));
+ assertEquals(1, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.FRIDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.SATURDAY));
+
+ final int epochSaturday = epoch + 2;
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.SUNDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.MONDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.TUESDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.WEDNESDAY));
+ assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.THURSDAY));
+ assertEquals(1, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.FRIDAY));
+ assertEquals(1, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.SATURDAY));
+
+ final int tenWeeksLater = epochSaturday + 10 * 7;
+ assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.SUNDAY));
+ assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.MONDAY));
+ assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.TUESDAY));
+ assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.WEDNESDAY));
+ assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.THURSDAY));
+ assertEquals(11, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.FRIDAY));
+ assertEquals(11, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.SATURDAY));
+ }
+
+ @Test
public void testNormalize_utc() {
Time t = new Time(Time.TIMEZONE_UTC);
Time expected = new Time(Time.TIMEZONE_UTC);
diff --git a/tests/tests/uirendering/res/drawable-nodpi/pathclippingtest_torus.png b/tests/tests/uirendering/res/drawable-nodpi/pathclippingtest_torus.png
new file mode 100644
index 0000000..76dcbc5
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/pathclippingtest_torus.png
Binary files differ
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
index ae4fee1..d78972f 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
@@ -29,6 +29,7 @@
import android.support.test.runner.AndroidJUnit4;
import android.uirendering.cts.R;
import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
+import android.uirendering.cts.bitmapverifiers.GoldenImageVerifier;
import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
import android.uirendering.cts.testinfrastructure.CanvasClient;
@@ -71,18 +72,38 @@
canvas.restore();
};
+ // draw circle with hole in it, by path operations + path clipping
+ static final CanvasClient sTorusClipOutCanvasClient = (canvas, width, height) -> {
+ canvas.save();
+
+ Path path1 = new Path();
+ path1.addCircle(30, 30, 50, Path.Direction.CW);
+
+ Path path2 = new Path();
+ path2.addCircle(30, 30, 30, Path.Direction.CW);
+
+ canvas.clipPath(path1);
+ canvas.clipOutPath(path2);
+ canvas.drawColor(Color.BLUE);
+
+ canvas.restore();
+ };
+
@Test
public void testCircleWithCircle() {
createTest()
.addCanvasClient("TorusDraw", sTorusDrawCanvasClient, false)
.addCanvasClient("TorusClip", sTorusClipCanvasClient)
- .runWithComparer(new MSSIMComparer(0.90));
+ .addCanvasClient("TorusClipOut", sTorusClipOutCanvasClient)
+ .runWithVerifier(new GoldenImageVerifier(getActivity(),
+ R.drawable.pathclippingtest_torus, new MSSIMComparer(0.95)));
}
@Test
public void testCircleWithPoints() {
createTest()
.addCanvasClient("TorusClip", sTorusClipCanvasClient)
+ .addCanvasClient("TorusClipOut", sTorusClipOutCanvasClient)
.runWithVerifier(new SamplePointVerifier(
new Point[] {
// inside of circle
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
index 94c981f..d6a196a 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -127,6 +127,7 @@
TEST_WIDTH, TEST_HEIGHT, Config.ARGB_8888);
Rect srcRect = new Rect(testOffset.x, testOffset.y,
testOffset.x + TEST_WIDTH, testOffset.y + TEST_HEIGHT);
+ Log.d("UiRendering", "capturing screenshot of " + srcRect.toShortString());
int copyResult = copy.request(getActivity().getWindow(), srcRect, dest);
Assert.assertEquals(PixelCopy.SUCCESS, copyResult);
return dest;
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
index 3b57af2..2b8f3b3 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -209,6 +209,7 @@
@Override
public void onDraw() {
mView.post(() -> {
+ Log.d("UiRendering", "notifying capture");
mView.getViewTreeObserver().removeOnDrawListener(this);
synchronized (mLock) {
mViewWrapper.getLocationOnScreen(mLocationOnScreen);
diff --git a/tests/tests/view/src/android/view/cts/FocusFinderTest.java b/tests/tests/view/src/android/view/cts/FocusFinderTest.java
index 8d56087..a8015ab 100644
--- a/tests/tests/view/src/android/view/cts/FocusFinderTest.java
+++ b/tests/tests/view/src/android/view/cts/FocusFinderTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import android.graphics.Rect;
@@ -244,6 +245,19 @@
assertTrue(nextFocus == mBottomRight || nextFocus == mBottomLeft);
}
+ @Test
+ public void testChainVisibility() {
+ mBottomRight.setNextFocusForwardId(mBottomLeft.getId());
+ mBottomLeft.setNextFocusForwardId(mTopRight.getId());
+ mBottomLeft.setVisibility(View.INVISIBLE);
+ View next = mFocusFinder.findNextFocus(mLayout, mBottomRight, View.FOCUS_FORWARD);
+ assertSame(mTopRight, next);
+
+ mBottomLeft.setNextFocusForwardId(View.NO_ID);
+ next = mFocusFinder.findNextFocus(mLayout, mBottomRight, View.FOCUS_FORWARD);
+ assertSame(mTopLeft, next);
+ }
+
private void verifyNextCluster(View currentCluster, int direction, View expectedNextCluster) {
View actualNextCluster = mFocusFinder.findNextKeyboardNavigationCluster(
mLayout, currentCluster, direction);
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index c75f2e4..b40b2f1 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -2808,12 +2808,18 @@
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.isFocused());
- // Arrows should not cause focus to leave the textfield
+ // Pure-keyboard arrows should not cause focus to leave the textfield
CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_UP);
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.isFocused());
- CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_DOWN);
+ // Non-pure-keyboard arrows, however, should.
+ int dpadRemote = InputDevice.SOURCE_DPAD | InputDevice.SOURCE_KEYBOARD;
+ sendSourceKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_UP, dpadRemote);
+ mInstrumentation.waitForIdleSync();
+ assertFalse(mTextView.isFocused());
+
+ sendSourceKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_DOWN, dpadRemote);
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.isFocused());
@@ -2823,6 +2829,16 @@
assertFalse(mTextView.isFocused());
}
+ private void sendSourceKeyDownUp(Instrumentation instrumentation, View targetView, int key,
+ int source) {
+ KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, key);
+ event.setSource(source);
+ CtsKeyEventUtil.sendKey(instrumentation, targetView, event);
+ event = new KeyEvent(KeyEvent.ACTION_UP, key);
+ event.setSource(source);
+ CtsKeyEventUtil.sendKey(instrumentation, targetView, event);
+ }
+
@Test
public void testSetIncludeFontPadding() throws Throwable {
mTextView = findTextView(R.id.textview_text);
@@ -3536,6 +3552,7 @@
public void testGetOffsetForPositionSingleLineLtr() throws Throwable {
// asserts getOffsetPosition returns correct values for a single line LTR text
final String text = "aaaaa";
+
mActivityRule.runOnUiThread(() -> {
mTextView = new TextView(mActivity);
mTextView.setText(text);
@@ -3559,17 +3576,21 @@
mActivityRule.runOnUiThread(() -> mActivity.setContentView(layout));
mInstrumentation.waitForIdleSync();
+ final float halfCharWidth = (float) Math.ceil(mTextView.getPaint().measureText("a") / 2f);
+ final int paddingTop = mTextView.getTotalPaddingTop();
+ final int paddingLeft = mTextView.getTotalPaddingLeft();
+
final int firstOffset = 0;
final int lastOffset = text.length() - 1;
final int midOffset = text.length() / 2;
// left edge of view
float x = 0f;
- float y = mTextView.getHeight() / 2f;
+ float y = mTextView.getHeight() / 2f + paddingTop;
assertEquals(firstOffset, mTextView.getOffsetForPosition(x, y));
// right edge of text
- x = mTextView.getLayout().getLineWidth(0) - 1f;
+ x = mTextView.getLayout().getLineWidth(0) + paddingLeft - halfCharWidth;
assertEquals(lastOffset, mTextView.getOffsetForPosition(x, y));
// right edge of view
@@ -3581,7 +3602,7 @@
assertEquals(firstOffset, mTextView.getOffsetForPosition(x, y));
// horizontal center of text
- x = (float) Math.floor(mTextView.getLayout().getLineWidth(0) / 2f + 0.5f);
+ x = mTextView.getLayout().getLineWidth(0) / 2f + paddingLeft - halfCharWidth;
assertEquals(midOffset, mTextView.getOffsetForPosition(x, y));
}
@@ -3615,9 +3636,13 @@
final Rect lineBounds = new Rect();
mTextView.getLayout().getLineBounds(0, lineBounds);
+ final float halfCharWidth = (float) Math.ceil(mTextView.getPaint().measureText("a") / 2f);
+ final int paddingTop = mTextView.getTotalPaddingTop();
+ final int paddingLeft = mTextView.getTotalPaddingLeft();
+
// left edge of view at first line
float x = 0f;
- float y = lineBounds.height() / 2f;
+ float y = lineBounds.height() / 2f + paddingTop;
assertEquals(0, mTextView.getOffsetForPosition(x, y));
// right edge of view at first line
@@ -3626,14 +3651,14 @@
// update lineBounds to be the second line
mTextView.getLayout().getLineBounds(1, lineBounds);
- y = lineBounds.top + lineBounds.height() / 2;
+ y = lineBounds.top + lineBounds.height() / 2f + paddingTop;
// left edge of view at second line
x = 0f;
assertEquals(line.length(), mTextView.getOffsetForPosition(x, y));
// right edge of text at second line
- x = mTextView.getLayout().getLineWidth(1) - 1f;
+ x = mTextView.getLayout().getLineWidth(1) + paddingLeft - halfCharWidth;
assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y));
// right edge of view at second line
@@ -3641,7 +3666,7 @@
assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y));
// horizontal center of text at second line
- x = (float) Math.floor(mTextView.getLayout().getLineWidth(1) / 2f + 0.5f);
+ x = mTextView.getLayout().getLineWidth(1) / 2f + paddingLeft - halfCharWidth;
// second line mid offset should not include next line, therefore subtract one
assertEquals(line.length() + (line.length() - 1) / 2, mTextView.getOffsetForPosition(x, y));
}
@@ -3676,9 +3701,14 @@
final Rect lineBounds = new Rect();
mTextView.getLayout().getLineBounds(0, lineBounds);
+ final float halfCharWidth = (float) Math.ceil(
+ mTextView.getPaint().measureText("\u0635") / 2f);
+ final int paddingTop = mTextView.getTotalPaddingTop();
+ final int paddingRight = mTextView.getTotalPaddingRight();
+
// right edge of view at first line
float x = mTextView.getWidth() - 1f;
- float y = lineBounds.height() / 2f;
+ float y = lineBounds.height() / 2f + paddingTop;
assertEquals(0, mTextView.getOffsetForPosition(x, y));
// left edge of view at first line
@@ -3687,7 +3717,7 @@
// update lineBounds to be the second line
mTextView.getLayout().getLineBounds(1, lineBounds);
- y = lineBounds.top + lineBounds.height() / 2f;
+ y = lineBounds.top + lineBounds.height() / 2f + paddingTop;
// right edge of view at second line
x = mTextView.getWidth() - 1f;
@@ -3697,13 +3727,14 @@
x = 0f;
assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y));
- // right edge of text at second line
- x = mTextView.getWidth() - mTextView.getLayout().getLineWidth(1) + 1f;
+ // left edge of text at second line
+ x = mTextView.getWidth() - (mTextView.getLayout().getLineWidth(1) + paddingRight
+ - halfCharWidth);
assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y));
// horizontal center of text at second line
- x = mTextView.getWidth() - (float) Math.floor(
- mTextView.getLayout().getLineWidth(1) / 2f + 0.5f);
+ x = mTextView.getWidth() - (mTextView.getLayout().getLineWidth(1) / 2f + paddingRight
+ - halfCharWidth);
// second line mid offset should not include next line, therefore subtract one
assertEquals(line.length() + (line.length() - 1) / 2, mTextView.getOffsetForPosition(x, y));
}
@@ -7309,12 +7340,17 @@
PollingCheck.waitFor(() -> mTextView.getSelectionStart() == SMARTSELECT_START
&& mTextView.getSelectionEnd() == SMARTSELECT_END);
- // Click to reset selection. Expect selection of original selection.
+ // Tap to reset selection. Expect tapped word to be selected.
+ startIndex = text.indexOf("Filip");
+ endIndex = startIndex + "Filip".length();
+ offset = getCenterPositionOfTextAt(mTextView, startIndex, endIndex);
emulateClickOnView(mTextView, offset.x, offset.y);
- PollingCheck.waitFor(() -> mTextView.getSelectionStart() == startIndex
- && mTextView.getSelectionEnd() == endIndex);
+ final int selStart = startIndex;
+ final int selEnd = endIndex;
+ PollingCheck.waitFor(() -> mTextView.getSelectionStart() == selStart
+ && mTextView.getSelectionEnd() == selEnd);
- // Click one more time to dismiss the selection.
+ // Tap one more time to dismiss the selection.
emulateClickOnView(mTextView, offset.x, offset.y);
assertFalse(mTextView.hasSelection());
}