Merge "Add empty DynamicConfig.xml at suite-level to CTS"
diff --git a/common/device-side/device-info/Android.mk b/common/device-side/device-info/Android.mk
index c04b51d..927101b 100644
--- a/common/device-side/device-info/Android.mk
+++ b/common/device-side/device-info/Android.mk
@@ -22,7 +22,8 @@
LOCAL_MODULE := compatibility-device-info
-LOCAL_SDK_VERSION := current
+# uncomment when b/13282254 is fixed
+#LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
index 969e85d..467269e 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
@@ -17,6 +17,10 @@
import android.os.Build;
import android.os.Bundle;
+import android.os.Environment;
+import android.os.SystemProperties;
+import android.os.UserManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import java.lang.Integer;
@@ -51,7 +55,7 @@
public static final String BUILD_VERSION_SDK = "build_version_sdk";
public static final String BUILD_VERSION_SDK_INT = "build_version_sdk_int";
public static final String BUILD_VERSION_BASE_OS = "build_version_base_os";
- public static final String BUILD_VERSION_SECURITY_PATH = "build_version_security_patch";
+ public static final String BUILD_VERSION_SECURITY_PATCH = "build_version_security_patch";
private final Map<String, String> mDeviceInfo = new HashMap<>();
@@ -73,12 +77,23 @@
addDeviceInfo(BUILD_VERSION_SDK, Build.VERSION.SDK);
addDeviceInfo(BUILD_VERSION_SDK_INT, Integer.toString(Build.VERSION.SDK_INT));
+ // Collect build fields available in API level 21
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
addDeviceInfo(BUILD_ABIS, TextUtils.join(",", Build.SUPPORTED_ABIS));
addDeviceInfo(BUILD_ABIS_32, TextUtils.join(",", Build.SUPPORTED_32_BIT_ABIS));
addDeviceInfo(BUILD_ABIS_64, TextUtils.join(",", Build.SUPPORTED_64_BIT_ABIS));
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
addDeviceInfo(BUILD_VERSION_BASE_OS, Build.VERSION.BASE_OS);
- addDeviceInfo(BUILD_VERSION_SECURITY_PATH, Build.VERSION.SECURITY_PATCH);
+ addDeviceInfo(BUILD_VERSION_SECURITY_PATCH, Build.VERSION.SECURITY_PATCH);
+ } else {
+ // Access system properties directly because Build.Version.BASE_OS and
+ // Build.Version.SECURITY_PATCH are not defined pre-M.
+ addDeviceInfo(BUILD_VERSION_BASE_OS,
+ SystemProperties.get("ro.build.version.base_os", ""));
+ addDeviceInfo(BUILD_VERSION_SECURITY_PATCH,
+ SystemProperties.get("ro.build.version.security_patch", ""));
}
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index 32a1789..c49340e 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -164,15 +164,20 @@
getDevice().uninstallPackage(NONE_PKG);
getDevice().uninstallPackage(READ_PKG);
getDevice().uninstallPackage(WRITE_PKG);
- String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+ final String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+
+ // We purposefully delay the installation of the reading apps to
+ // verify that the daemon correctly invalidates any caches.
+ assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
+ for (int user : users) {
+ runDeviceTests(WRITE_PKG, ".WriteGiftTest", user);
+ }
+
assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
assertNull(getDevice().installPackage(getTestAppFile(READ_APK), false, options));
- assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
-
for (int user : users) {
- runDeviceTests(WRITE_PKG, "WriteGiftTest", user);
- runDeviceTests(READ_PKG, "ReadGiftTest", user);
- runDeviceTests(NONE_PKG, "GiftTest", user);
+ runDeviceTests(READ_PKG, ".ReadGiftTest", user);
+ runDeviceTests(NONE_PKG, ".GiftTest", user);
}
} finally {
getDevice().uninstallPackage(NONE_PKG);
diff --git a/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java b/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
index 40d3cff..ba27114 100644
--- a/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
+++ b/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
@@ -18,17 +18,20 @@
import java.io.BufferedReader;
import java.io.DataInputStream;
+import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.test.AndroidTestCase;
/**
* Test that another app's private data cannot be accessed, while its public data can.
*
- * Assumes that {@link APP_WITH_DATA_PKG} has already created the private and public data.
+ * Assumes that {@link #APP_WITH_DATA_PKG} has already created the private and public data.
*/
public class AccessPrivateDataTest extends AndroidTestCase {
@@ -39,12 +42,12 @@
/**
* Name of private file to access. This must match the name of the file created by
- * {@link APP_WITH_DATA_PKG}.
+ * {@link #APP_WITH_DATA_PKG}.
*/
private static final String PRIVATE_FILE_NAME = "private_file.txt";
/**
* Name of public file to access. This must match the name of the file created by
- * {@link APP_WITH_DATA_PKG}.
+ * {@link #APP_WITH_DATA_PKG}.
*/
private static final String PUBLIC_FILE_NAME = "public_file.txt";
@@ -56,20 +59,26 @@
public void testAccessPrivateData() throws IOException {
try {
// construct the absolute file path to the app's private file
- String privateFilePath = String.format("/data/data/%s/%s", APP_WITH_DATA_PKG,
- PRIVATE_FILE_NAME);
- FileInputStream inputStream = new FileInputStream(privateFilePath);
+ ApplicationInfo applicationInfo = getApplicationInfo(APP_WITH_DATA_PKG);
+ File privateFile = new File(applicationInfo.dataDir, "files/" + PRIVATE_FILE_NAME);
+ FileInputStream inputStream = new FileInputStream(privateFile);
inputStream.read();
inputStream.close();
fail("Was able to access another app's private data");
- } catch (FileNotFoundException e) {
+ } catch (FileNotFoundException | SecurityException e) {
// expected
- } catch (SecurityException e) {
- // also valid
}
accessPrivateTrafficStats();
}
+ private ApplicationInfo getApplicationInfo(String packageName) {
+ try {
+ return mContext.getPackageManager().getApplicationInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalStateException("Expected package not found: " + e);
+ }
+ }
+
/**
* Tests that another app's public file can be accessed
* @throws IOException
@@ -86,10 +95,10 @@
private int getOtherAppUid() throws IOException, FileNotFoundException, SecurityException {
// construct the absolute file path to the other app's public file
- String publicFilePath = String.format("/data/data/%s/files/%s", APP_WITH_DATA_PKG,
- PUBLIC_FILE_NAME);
- DataInputStream inputStream = new DataInputStream(new FileInputStream(publicFilePath));
- int otherAppUid = (int)inputStream.readInt();
+ ApplicationInfo applicationInfo = getApplicationInfo(APP_WITH_DATA_PKG);
+ File publicFile = new File(applicationInfo.dataDir, "files/" + PUBLIC_FILE_NAME);
+ DataInputStream inputStream = new DataInputStream(new FileInputStream(publicFile));
+ int otherAppUid = inputStream.readInt();
inputStream.close();
return otherAppUid;
}
@@ -98,9 +107,7 @@
int otherAppUid = -1;
try {
otherAppUid = getOtherAppUid();
- } catch (FileNotFoundException e) {
- fail("Was not able to access another app's public file: " + e);
- } catch (SecurityException e) {
+ } catch (FileNotFoundException | SecurityException e) {
fail("Was not able to access another app's public file: " + e);
}
diff --git a/hostsidetests/appsecurity/test-apps/AppWithData/src/com/android/cts/appwithdata/CreatePrivateDataTest.java b/hostsidetests/appsecurity/test-apps/AppWithData/src/com/android/cts/appwithdata/CreatePrivateDataTest.java
index e11681a..378ef42 100644
--- a/hostsidetests/appsecurity/test-apps/AppWithData/src/com/android/cts/appwithdata/CreatePrivateDataTest.java
+++ b/hostsidetests/appsecurity/test-apps/AppWithData/src/com/android/cts/appwithdata/CreatePrivateDataTest.java
@@ -24,8 +24,8 @@
import android.database.sqlite.SQLiteOpenHelper;
import android.net.TrafficStats;
import android.test.AndroidTestCase;
-import android.util.Log;
+import java.io.File;
import java.net.ServerSocket;
import java.net.Socket;
@@ -97,14 +97,11 @@
try {
// construct the absolute file path to the app's public's file the same
// way as the appaccessdata package will.
- String publicFilePath = String.format("/data/data/%s/files/%s", APP_WITH_DATA_PKG,
- PUBLIC_FILE_NAME);
- DataInputStream inputStream = new DataInputStream(new FileInputStream(publicFilePath));
- int otherAppUid = (int)inputStream.readInt();
+ File publicFile = new File(mContext.getFilesDir(), PUBLIC_FILE_NAME);
+ DataInputStream inputStream = new DataInputStream(new FileInputStream(publicFile));
+ inputStream.readInt();
inputStream.close();
- } catch (FileNotFoundException e) {
- fail("Was not able to access own public file: " + e);
- } catch (SecurityException e) {
+ } catch (FileNotFoundException | SecurityException e) {
fail("Was not able to access own public file: " + e);
}
}
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
index 7fe0b80..4bd494d 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
@@ -149,6 +149,22 @@
return paths;
}
+ /**
+ * Return a set of several package-specific external storage paths pointing
+ * at "gift" files designed to be exchanged with the target package.
+ */
+ public static List<File> getAllPackageSpecificGiftPaths(Context context,
+ String targetPackageName) {
+ final List<File> files = getAllPackageSpecificPaths(context);
+ final List<File> targetFiles = new ArrayList<>();
+ for (File file : files) {
+ final File targetFile = new File(
+ file.getAbsolutePath().replace(context.getPackageName(), targetPackageName));
+ targetFiles.add(new File(targetFile, targetPackageName + ".gift"));
+ }
+ return targetFiles;
+ }
+
public static List<File> getPrimaryPackageSpecificPaths(Context context) {
final List<File> paths = new ArrayList<File>();
Collections.addAll(paths, context.getExternalCacheDir());
@@ -189,12 +205,6 @@
return after;
}
- public static File buildGiftForPackage(Context context, String packageName) {
- final File myCache = context.getExternalCacheDir();
- return new File(myCache.getAbsolutePath().replace(context.getPackageName(), packageName),
- packageName + ".gift");
- }
-
public static File buildProbeFile(File dir) {
return new File(dir, ".probe_" + System.nanoTime());
}
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/GiftTest.java b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/GiftTest.java
index e482b2f..14a0180 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/GiftTest.java
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/GiftTest.java
@@ -21,26 +21,33 @@
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_WRITE;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertFileNoAccess;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertFileReadWriteAccess;
-import static com.android.cts.externalstorageapp.CommonExternalStorageTest.buildGiftForPackage;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getAllPackageSpecificGiftPaths;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.readInt;
import android.test.AndroidTestCase;
import java.io.File;
+import java.util.List;
public class GiftTest extends AndroidTestCase {
/**
* Verify we can read only our gifts.
*/
public void testGifts() throws Exception {
- final File none = buildGiftForPackage(getContext(), PACKAGE_NONE);
- assertFileReadWriteAccess(none);
- assertEquals(100, readInt(none));
+ final List<File> noneList = getAllPackageSpecificGiftPaths(getContext(), PACKAGE_NONE);
+ for (File none : noneList) {
+ assertFileReadWriteAccess(none);
+ assertEquals(100, readInt(none));
+ }
- final File read = buildGiftForPackage(getContext(), PACKAGE_READ);
- assertFileNoAccess(read);
+ final List<File> readList = getAllPackageSpecificGiftPaths(getContext(), PACKAGE_READ);
+ for (File read : readList) {
+ assertFileNoAccess(read);
+ }
- final File write = buildGiftForPackage(getContext(), PACKAGE_WRITE);
- assertFileNoAccess(write);
+ final List<File> writeList = getAllPackageSpecificGiftPaths(getContext(), PACKAGE_WRITE);
+ for (File write : writeList) {
+ assertFileNoAccess(write);
+ }
}
}
diff --git a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/src/com/android/cts/readexternalstorageapp/ReadGiftTest.java b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/src/com/android/cts/readexternalstorageapp/ReadGiftTest.java
index e72be77..78bb738 100644
--- a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/src/com/android/cts/readexternalstorageapp/ReadGiftTest.java
+++ b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/src/com/android/cts/readexternalstorageapp/ReadGiftTest.java
@@ -21,28 +21,35 @@
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_WRITE;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertFileReadOnlyAccess;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertFileReadWriteAccess;
-import static com.android.cts.externalstorageapp.CommonExternalStorageTest.buildGiftForPackage;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getAllPackageSpecificGiftPaths;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.readInt;
import android.test.AndroidTestCase;
import java.io.File;
+import java.util.List;
public class ReadGiftTest extends AndroidTestCase {
/**
* Verify we can read all gifts.
*/
public void testGifts() throws Exception {
- final File none = buildGiftForPackage(getContext(), PACKAGE_NONE);
- assertFileReadOnlyAccess(none);
- assertEquals(100, readInt(none));
+ final List<File> noneList = getAllPackageSpecificGiftPaths(getContext(), PACKAGE_NONE);
+ for (File none : noneList) {
+ assertFileReadOnlyAccess(none);
+ assertEquals(100, readInt(none));
+ }
- final File read = buildGiftForPackage(getContext(), PACKAGE_READ);
- assertFileReadWriteAccess(read);
- assertEquals(101, readInt(read));
+ final List<File> readList = getAllPackageSpecificGiftPaths(getContext(), PACKAGE_READ);
+ for (File read : readList) {
+ assertFileReadWriteAccess(read);
+ assertEquals(101, readInt(read));
+ }
- final File write = buildGiftForPackage(getContext(), PACKAGE_WRITE);
- assertFileReadOnlyAccess(write);
- assertEquals(102, readInt(write));
+ final List<File> writeList = getAllPackageSpecificGiftPaths(getContext(), PACKAGE_WRITE);
+ for (File write : writeList) {
+ assertFileReadOnlyAccess(write);
+ assertEquals(102, readInt(write));
+ }
}
}
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
index 3861ddf..ea6c0ea 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
@@ -16,6 +16,7 @@
package com.android.cts.writeexternalstorageapp;
+import static android.test.MoreAsserts.assertNotEqual;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_NONE;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.TAG;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirNoWriteAccess;
@@ -31,7 +32,10 @@
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.writeInt;
import android.os.Environment;
+import android.os.SystemClock;
+import android.system.Os;
import android.test.AndroidTestCase;
+import android.text.format.DateUtils;
import android.util.Log;
import com.android.cts.externalstorageapp.CommonExternalStorageTest;
@@ -297,4 +301,29 @@
}
}
}
+
+ /**
+ * Verify that moving around package-specific directories causes permissions
+ * to be updated.
+ */
+ public void testMovePackageSpecificPaths() throws Exception {
+ final File before = getContext().getExternalCacheDir();
+ final File beforeFile = new File(before, "test.probe");
+ assertTrue(beforeFile.createNewFile());
+ assertEquals(Os.getuid(), Os.stat(before.getAbsolutePath()).st_uid);
+ assertEquals(Os.getuid(), Os.stat(beforeFile.getAbsolutePath()).st_uid);
+
+ final File after = new File(before.getAbsolutePath()
+ .replace(getContext().getPackageName(), "com.example.does.not.exist"));
+ after.getParentFile().mkdirs();
+
+ Os.rename(before.getAbsolutePath(), after.getAbsolutePath());
+
+ // Sit around long enough for VFS cache to expire
+ SystemClock.sleep(15 * DateUtils.SECOND_IN_MILLIS);
+
+ final File afterFile = new File(after, "test.probe");
+ assertNotEqual(Os.getuid(), Os.stat(after.getAbsolutePath()).st_uid);
+ assertNotEqual(Os.getuid(), Os.stat(afterFile.getAbsolutePath()).st_uid);
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteGiftTest.java b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteGiftTest.java
index 5da42da..db3813f 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteGiftTest.java
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteGiftTest.java
@@ -20,41 +20,48 @@
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_READ;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_WRITE;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertFileReadWriteAccess;
-import static com.android.cts.externalstorageapp.CommonExternalStorageTest.buildGiftForPackage;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getAllPackageSpecificGiftPaths;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.readInt;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.writeInt;
import android.test.AndroidTestCase;
import java.io.File;
+import java.util.List;
public class WriteGiftTest extends AndroidTestCase {
/**
* Leave gifts for other packages in their primary external cache dirs.
*/
public void testGifts() throws Exception {
- final File none = buildGiftForPackage(getContext(), PACKAGE_NONE);
- none.getParentFile().mkdirs();
- none.createNewFile();
- assertFileReadWriteAccess(none);
+ final List<File> noneList = getAllPackageSpecificGiftPaths(getContext(), PACKAGE_NONE);
+ for (File none : noneList) {
+ none.getParentFile().mkdirs();
+ none.createNewFile();
+ assertFileReadWriteAccess(none);
- writeInt(none, 100);
- assertEquals(100, readInt(none));
+ writeInt(none, 100);
+ assertEquals(100, readInt(none));
+ }
- final File read = buildGiftForPackage(getContext(), PACKAGE_READ);
- read.getParentFile().mkdirs();
- read.createNewFile();
- assertFileReadWriteAccess(read);
+ final List<File> readList = getAllPackageSpecificGiftPaths(getContext(), PACKAGE_READ);
+ for (File read : readList) {
+ read.getParentFile().mkdirs();
+ read.createNewFile();
+ assertFileReadWriteAccess(read);
- writeInt(read, 101);
- assertEquals(101, readInt(read));
+ writeInt(read, 101);
+ assertEquals(101, readInt(read));
+ }
- final File write = buildGiftForPackage(getContext(), PACKAGE_WRITE);
- write.getParentFile().mkdirs();
- write.createNewFile();
- assertFileReadWriteAccess(write);
+ final List<File> writeList = getAllPackageSpecificGiftPaths(getContext(), PACKAGE_WRITE);
+ for (File write : writeList) {
+ write.getParentFile().mkdirs();
+ write.createNewFile();
+ assertFileReadWriteAccess(write);
- writeInt(write, 102);
- assertEquals(102, readInt(write));
+ writeInt(write, 102);
+ assertEquals(102, readInt(write));
+ }
}
}
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index 7bf46a3..022e2f4 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -353,9 +353,9 @@
*/
public void testValidServiceContexts() throws Exception {
- /* run checkfc -p on service_contexts */
+ /* run checkfc -s on service_contexts */
ProcessBuilder pb = new ProcessBuilder(checkFc.getAbsolutePath(),
- "-p", devicePolicyFile.getAbsolutePath(),
+ "-s", devicePolicyFile.getAbsolutePath(),
deviceSvcFile.getAbsolutePath());
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
diff --git a/hostsidetests/services/activitymanager/OldAndroidTest.xml b/hostsidetests/services/activitymanager/OldAndroidTest.xml
index 669fe02..ba9d998 100644
--- a/hostsidetests/services/activitymanager/OldAndroidTest.xml
+++ b/hostsidetests/services/activitymanager/OldAndroidTest.xml
@@ -15,5 +15,6 @@
-->
<configuration description="CTS package preparer for install/uninstall of the apk used as a test operation target">
<include name="common-config" />
- <option name="cts-apk-installer:test-file-name" value="CtsDeviceServicesTestApp.apk" />
+ <!-- This will tell tradefed to install the test apk. -->
+ <option name="cts-apk-installer:test-file-name" value="CtsServicesTestApp.apk" />
</configuration>
diff --git a/hostsidetests/services/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
index 027a73d..3cd5219 100755
--- a/hostsidetests/services/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
@@ -21,8 +21,14 @@
<application>
<activity android:name=".TestActivity"
android:resizeable="true"
+ />
+ <activity android:name=".LaunchToSideActivity"
+ android:resizeable="true"
+ />
+ <activity android:name=".PipActivity"
+ android:resizeable="true"
android:supportsPictureInPicture="true"
- />
+ />
</application>
</manifest>
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchToSideActivity.java b/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchToSideActivity.java
new file mode 100644
index 0000000..31c169e
--- /dev/null
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchToSideActivity.java
@@ -0,0 +1,24 @@
+package android.server.app;
+
+import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_TO_SIDE;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+public class LaunchToSideActivity extends Activity {
+ private static final String TAG = "LaunchToSide";
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ final Bundle extras = intent.getExtras();
+ if (extras != null && extras.getBoolean("launch_to_the_side")) {
+ Intent newIntent = new Intent("android.settings.SETTINGS");
+ newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_LAUNCH_TO_SIDE);
+ startActivity(newIntent);
+ }
+ }
+}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/PipActivity.java b/hostsidetests/services/activitymanager/app/src/android/server/app/PipActivity.java
new file mode 100644
index 0000000..8a89deb
--- /dev/null
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/PipActivity.java
@@ -0,0 +1,27 @@
+/*
+ * 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.app;
+
+import android.app.Activity;
+
+public class PipActivity extends Activity {
+ @Override
+ protected void onResume() {
+ super.onResume();
+ enterPictureInPictureMode();
+ }
+}
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTests.java
index b31a945..aed23d8 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTests.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTests.java
@@ -20,6 +20,7 @@
import com.android.tradefed.device.CollectingOutputReceiver;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceTestCase;
@@ -44,21 +45,32 @@
/** ID of stack that occupies a dedicated region of the screen. */
public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
+ /** ID of stack that always on top (always visible) when it exist. */
+ public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
+
private static final String STACK_ID_PREFIX = "Stack id=";
private static final String TASK_ID_PREFIX = "taskId";
private static final String TEST_ACTIVITY_NAME = "TestActivity";
+ private static final String LAUNCH_TO_SIDE_ACTIVITY_NAME = "LaunchToSideActivity";
+ private static final String PIP_ACTIVITY_NAME = "PipActivity";
private static final String AM_STACK_LIST = "am stack list";
- private static final String AM_START_ACTIVITY = "am start -n android.server.app/.TestActivity";
- private static final String AM_FORCE_STOP = "am force-stop android.server.app";
+ private static final String AM_START_TEST_ACTIVITY =
+ "am start -n android.server.app/." + TEST_ACTIVITY_NAME;
+ private static final String AM_START_LAUNCH_TO_SIDE_ACTIVITY =
+ "am start -n android.server.app/." + LAUNCH_TO_SIDE_ACTIVITY_NAME;
+ private static final String AM_START_PIP_ACTIVITY =
+ "am start -n android.server.app/." + PIP_ACTIVITY_NAME;
+ private static final String AM_FORCE_STOP_TEST = "am force-stop android.server.app";
+ private static final String AM_FORCE_STOP_SETTINGS = "com.android.settings";
private static final String AM_MOVE_TASK = "am stack movetask ";
- /**
- * A reference to the device under test.
- */
+ /** A reference to the device under test. */
private ITestDevice mDevice;
+ private HashSet<String> mAvailableFeatures;
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -70,27 +82,15 @@
@Override
protected void tearDown() {
try {
- mDevice.executeShellCommand(AM_FORCE_STOP);
+ mDevice.executeShellCommand(AM_FORCE_STOP_TEST);
+ mDevice.executeShellCommand(AM_FORCE_STOP_SETTINGS);
} catch (DeviceNotAvailableException e) {
}
}
public void testStackList() throws Exception {
- mDevice.executeShellCommand(AM_START_ACTIVITY);
- CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
- mDevice.executeShellCommand(AM_STACK_LIST, outputReceiver);
- String output = outputReceiver.getOutput();
- HashSet<Integer> stacks = new HashSet<>();
- for (String line : output.split("\\n")) {
- CLog.logAndDisplay(LogLevel.INFO, line);
- if (line.startsWith(STACK_ID_PREFIX)) {
- final String sub = line.substring(STACK_ID_PREFIX.length());
- final int index = sub.indexOf(" ");
- final int currentStack = Integer.parseInt(sub.substring(0, index));
- stacks.add(currentStack);
- } else if (line.startsWith(TASK_ID_PREFIX)) {
- }
- }
+ mDevice.executeShellCommand(AM_START_TEST_ACTIVITY);
+ HashSet<Integer> stacks = collectStacks();
assertTrue("At least two stacks expected, home and fullscreen.", stacks.size() >= 2);
assertTrue("Stacks must contain home stack.", stacks.contains(HOME_STACK_ID));
assertTrue("Stacks must contain fullscreen stack.", stacks.contains(
@@ -107,12 +107,12 @@
}
}
- private int getTestActivityTaskId() throws DeviceNotAvailableException {
+ private int getActivityTaskId(String name) throws DeviceNotAvailableException {
CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
mDevice.executeShellCommand(AM_STACK_LIST, outputReceiver);
final String output = outputReceiver.getOutput();
for (String line : output.split("\\n")) {
- if (line.contains(TEST_ACTIVITY_NAME)) {
+ if (line.contains(name)) {
for (String word : line.split("\\s+")) {
if (word.startsWith(TASK_ID_PREFIX)) {
final String withColon = word.split("=")[1];
@@ -125,11 +125,45 @@
}
public void testDockActivity() throws Exception {
- mDevice.executeShellCommand(AM_START_ACTIVITY);
- final int taskId = getTestActivityTaskId();
+ mDevice.executeShellCommand(AM_START_TEST_ACTIVITY);
+ final int taskId = getActivityTaskId(TEST_ACTIVITY_NAME);
final String cmd = AM_MOVE_TASK + taskId + " " + DOCKED_STACK_ID + " true";
mDevice.executeShellCommand(cmd);
- CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
+ HashSet<Integer> stacks = collectStacks();
+ assertTrue("At least two stacks expected, home and docked.", stacks.size() >= 2);
+ assertTrue("Stacks must contain home stack.", stacks.contains(HOME_STACK_ID));
+ assertTrue("Stacks must contain docked stack.", stacks.contains(DOCKED_STACK_ID));
+ }
+
+ public void testLaunchToSide() throws Exception {
+ mDevice.executeShellCommand(AM_START_LAUNCH_TO_SIDE_ACTIVITY);
+ final int taskId = getActivityTaskId(LAUNCH_TO_SIDE_ACTIVITY_NAME);
+ final String cmd = AM_MOVE_TASK + taskId + " " + DOCKED_STACK_ID + " true";
+ mDevice.executeShellCommand(cmd);
+ printStacksAndTasks();
+ mDevice.executeShellCommand(AM_START_LAUNCH_TO_SIDE_ACTIVITY
+ + " -f 0x20000000 --ez launch_to_the_side true");
+ HashSet<Integer> stacks = collectStacks();
+ assertTrue("At least two stacks expected, docked and fullscreen.", stacks.size() >= 2);
+ assertTrue("Stacks must contain fullscreen stack.", stacks.contains(
+ FULLSCREEN_WORKSPACE_STACK_ID));
+ assertTrue("Stacks must contain docked stack.", stacks.contains(DOCKED_STACK_ID));
+ }
+
+ public void testEnterPictureInPictureMode() throws Exception {
+ final boolean supportsPip = hasDeviceFeature("android.software.picture_in_picture");
+ mDevice.executeShellCommand(AM_START_PIP_ACTIVITY);
+ final HashSet<Integer> stacks = collectStacks();
+ final boolean containsPinnedStack = stacks.contains(PINNED_STACK_ID);
+ if (supportsPip) {
+ assertTrue("Stacks must contain pinned stack.", containsPinnedStack);
+ } else {
+ assertFalse("Stacks must not contain pinned stack.", containsPinnedStack);
+ }
+ }
+
+ private HashSet<Integer> collectStacks() throws DeviceNotAvailableException {
+ final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
mDevice.executeShellCommand(AM_STACK_LIST, outputReceiver);
final String output = outputReceiver.getOutput();
HashSet<Integer> stacks = new HashSet<>();
@@ -142,8 +176,32 @@
stacks.add(currentStack);
}
}
- assertTrue("At least two stacks expected, home and docked.", stacks.size() >= 2);
- assertTrue("Stacks must contain home stack.", stacks.contains(HOME_STACK_ID));
- assertTrue("Stacks must contain docked stack.", stacks.contains(DOCKED_STACK_ID));
+ return stacks;
+ }
+
+ private boolean hasDeviceFeature(String requiredFeature) throws DeviceNotAvailableException {
+ if (mAvailableFeatures == null) {
+ // TODO: Move this logic to ITestDevice.
+ String command = "pm list features";
+ String commandOutput = mDevice.executeShellCommand(command);
+ CLog.i("Output for command " + command + ": " + commandOutput);
+
+ // Extract the id of the new user.
+ mAvailableFeatures = new HashSet<>();
+ for (String feature: commandOutput.split("\\s+")) {
+ // Each line in the output of the command has the format "feature:{FEATURE_VALUE}".
+ String[] tokens = feature.split(":");
+ assertTrue("\"" + feature + "\" expected to have format feature:{FEATURE_VALUE}",
+ tokens.length > 1);
+ assertEquals(feature, "feature", tokens[0]);
+ mAvailableFeatures.add(tokens[1]);
+ }
+ }
+ boolean result = mAvailableFeatures.contains(requiredFeature);
+ if (!result) {
+ CLog.logAndDisplay(LogLevel.INFO, "Device doesn't have required feature "
+ + requiredFeature + ". Test won't run.");
+ }
+ return result;
}
}
diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 86c708a..2e79557 100644
--- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -37,6 +37,7 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
/**
+ * TODO: Make sure DO APIs are not called by PO.
* Test that exercises {@link DevicePolicyManager}. The test requires that the
* CtsDeviceAdminReceiver be installed via the CtsDeviceAdmin.apk and be
* activated via "Settings > Location & security > Select device administrators".
@@ -1142,4 +1143,18 @@
return false;
}
}
+
+ public void testReboot_failIfNotDeviceOwner() {
+ if (!mDeviceAdmin) {
+ Log.w(TAG, "Skipping testReboot_failIfNotDeviceOwner");
+ return;
+ }
+ try {
+ mDevicePolicyManager.reboot(mComponent);
+ fail("did not throw expected SecurityException");
+ } catch (SecurityException e) {
+ assertDeviceOwnerMessage(e.getMessage());
+ }
+ }
+
}
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index 21cf064..c45bb2f 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -287,6 +287,39 @@
<activity android:name="android.app.stubs.ActivityManagerMemoryClassTestActivity"
android:process=":memoryclass" />
+ <activity android:name="android.app.stubs.PipActivity"
+ android:label="PipActivity"
+ android:resizeable="true"
+ android:supportsPictureInPicture="true"
+ android:configChanges="smallestScreenSize|orientation|screenSize">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.app.stubs.PipNotResizeableActivity"
+ android:label="PipNotResizeableActivity"
+ android:resizeable="false"
+ android:supportsPictureInPicture="true"
+ android:configChanges="smallestScreenSize|orientation|screenSize">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.app.stubs.PipNotSupportedActivity"
+ android:label="PipNotSupportedActivity"
+ android:resizeable="true"
+ android:supportsPictureInPicture="false"
+ android:configChanges="smallestScreenSize|orientation|screenSize">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
</application>
</manifest>
diff --git a/tests/app/app/src/android/app/stubs/PipActivity.java b/tests/app/app/src/android/app/stubs/PipActivity.java
new file mode 100644
index 0000000..533f054
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/PipActivity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.app.stubs;
+
+import android.app.Activity;
+
+public class PipActivity extends Activity {
+
+}
diff --git a/tests/app/app/src/android/app/stubs/PipNotResizeableActivity.java b/tests/app/app/src/android/app/stubs/PipNotResizeableActivity.java
new file mode 100644
index 0000000..7ed1acc
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/PipNotResizeableActivity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.app.stubs;
+
+import android.app.Activity;
+
+public class PipNotResizeableActivity extends Activity {
+
+}
diff --git a/tests/app/app/src/android/app/stubs/PipNotSupportedActivity.java b/tests/app/app/src/android/app/stubs/PipNotSupportedActivity.java
new file mode 100644
index 0000000..e6656a0
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/PipNotSupportedActivity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.app.stubs;
+
+import android.app.Activity;
+
+public class PipNotSupportedActivity extends Activity {
+
+}
diff --git a/tests/app/src/android/app/cts/PipActivityTest.java b/tests/app/src/android/app/cts/PipActivityTest.java
new file mode 100644
index 0000000..a553169
--- /dev/null
+++ b/tests/app/src/android/app/cts/PipActivityTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.app.cts;
+
+import android.app.Instrumentation;
+import android.app.stubs.PipActivity;
+import android.test.ActivityInstrumentationTestCase2;
+
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+
+public class PipActivityTest extends ActivityInstrumentationTestCase2<PipActivity> {
+
+ private Instrumentation mInstrumentation;
+ private PipActivity mActivity;
+
+ public PipActivityTest() {
+ super("android.app.stubs", PipActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
+ }
+
+ public void testLaunchPipActivity() throws Throwable {
+ runTestOnUiThread(new Runnable() {
+ public void run() {
+ final boolean supportsPip =
+ mActivity.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
+ if (supportsPip) {
+ mActivity.enterPictureInPictureMode();
+ assertTrue(mActivity.inMultiWindowMode());
+ assertTrue(mActivity.inPictureInPictureMode());
+ } else {
+ boolean pipSupportDisabled = false;
+ try {
+ mActivity.enterPictureInPictureMode();
+ } catch (IllegalStateException e) {
+ // Pip not supported
+ pipSupportDisabled = true;
+ }
+ assertTrue(pipSupportDisabled);
+ assertFalse(mActivity.inMultiWindowMode());
+ assertFalse(mActivity.inPictureInPictureMode());
+ }
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ }
+}
diff --git a/tests/app/src/android/app/cts/PipNotResizeableActivityTest.java b/tests/app/src/android/app/cts/PipNotResizeableActivityTest.java
new file mode 100644
index 0000000..4f7f63e
--- /dev/null
+++ b/tests/app/src/android/app/cts/PipNotResizeableActivityTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.app.cts;
+
+import android.app.Instrumentation;
+import android.app.stubs.PipNotResizeableActivity;
+import android.test.ActivityInstrumentationTestCase2;
+
+public class PipNotResizeableActivityTest
+ extends ActivityInstrumentationTestCase2<PipNotResizeableActivity> {
+
+ private Instrumentation mInstrumentation;
+ private PipNotResizeableActivity mActivity;
+
+ public PipNotResizeableActivityTest() {
+ super("android.app.stubs", PipNotResizeableActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
+ }
+
+ public void testLaunchPipNotResizeableActivity() throws Throwable {
+ runTestOnUiThread(new Runnable() {
+ public void run() {
+ boolean pipSupportDisabled = false;
+ try {
+ mActivity.enterPictureInPictureMode();
+ } catch (IllegalStateException e) {
+ // Pip not supported
+ pipSupportDisabled = true;
+ } catch (IllegalArgumentException e) {
+ // Pip not supported
+ pipSupportDisabled = true;
+ }
+ assertTrue(pipSupportDisabled);
+ assertFalse(mActivity.inMultiWindowMode());
+ assertFalse(mActivity.inPictureInPictureMode());
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ }
+ }
diff --git a/tests/app/src/android/app/cts/PipNotSupportedActivityTest.java b/tests/app/src/android/app/cts/PipNotSupportedActivityTest.java
new file mode 100644
index 0000000..245421a
--- /dev/null
+++ b/tests/app/src/android/app/cts/PipNotSupportedActivityTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.app.cts;
+
+import android.app.Instrumentation;
+import android.app.stubs.PipNotSupportedActivity;
+import android.test.ActivityInstrumentationTestCase2;
+
+public class PipNotSupportedActivityTest
+ extends ActivityInstrumentationTestCase2<PipNotSupportedActivity> {
+
+ private Instrumentation mInstrumentation;
+ private PipNotSupportedActivity mActivity;
+
+ public PipNotSupportedActivityTest() {
+ super("android.app.stubs", PipNotSupportedActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
+ }
+
+ public void testLaunchPipNotSupportedActivity() throws Throwable {
+ runTestOnUiThread(new Runnable() {
+ public void run() {
+ boolean pipSupportDisabled = false;
+ try {
+ mActivity.enterPictureInPictureMode();
+ } catch (IllegalStateException e) {
+ // Pip not supported
+ pipSupportDisabled = true;
+ } catch (IllegalArgumentException e) {
+ // Pip not supported
+ pipSupportDisabled = true;
+ }
+ assertTrue(pipSupportDisabled);
+ assertFalse(mActivity.inMultiWindowMode());
+ assertFalse(mActivity.inPictureInPictureMode());
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ }
+}
diff --git a/tests/tests/content/Android.mk b/tests/tests/content/Android.mk
index 924c639..1c8fbfc 100644
--- a/tests/tests/content/Android.mk
+++ b/tests/tests/content/Android.mk
@@ -25,10 +25,13 @@
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 ctsdeviceutil ctstestrunner
-# Resource unit tests use a private locale and some densities
-LOCAL_AAPT_FLAGS = -c xx_YY -c cs -c small -c normal -c large -c xlarge \
+# Resource unit tests use various locales (including a private locale) and some densities
+LOCAL_AAPT_FLAGS = -c small -c normal -c large -c xlarge \
-c 320dpi -c 240dpi -c 160dpi -c 32dpi \
- -c kok,kok_IN,kok_419,kok_419_VARIANT,kok_Knda_419,kok_Knda_419_VARIANT,kok_VARIANT,kok_Knda,tgl,tgl_PH
+ -c cs,fa_IR \
+ -c kok,kok_IN,kok_419,kok_419_VARIANT \
+ -c kok_Knda_419,kok_Knda_419_VARIANT,kok_VARIANT,kok_Knda \
+ -c tgl,tgl_PH,xx_YY
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/content/res/values-fa-rIR/strings.xml b/tests/tests/content/res/values-fa-rIR/strings.xml
new file mode 100644
index 0000000..25fd20c
--- /dev/null
+++ b/tests/tests/content/res/values-fa-rIR/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="am">صبح</string>
+</resources>
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 edf1d4b..a3c1b49 100644
--- a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
@@ -272,6 +272,67 @@
assertEquals(LocaleList.getDefault(), res.getConfiguration().getLocales());
}
+ public void testUpdateConfiguration_ResolvedLocaleIsRecalculated() {
+ final DisplayMetrics dm = new DisplayMetrics();
+ dm.setToDefaults();
+ final Configuration cfg = new Configuration();
+ cfg.setToDefaults();
+
+ // Avestan has no assets, but Czech does
+ cfg.setLocales(LocaleList.forLanguageTags("ae"));
+ Resources res = new Resources(mResources.getAssets(), dm, cfg);
+ cfg.setLocales(LocaleList.forLanguageTags("ae,cs"));
+ res.updateConfiguration(cfg, null);
+ assertEquals("cs", res.getResolvedLocale().toLanguageTag());
+ }
+
+ public void testGetResolvedLocale_unsupportedLocale() {
+ final DisplayMetrics dm = new DisplayMetrics();
+ dm.setToDefaults();
+ final Configuration cfg = new Configuration();
+ cfg.setToDefaults();
+ cfg.setLocales(LocaleList.forLanguageTags("ae")); // Avestan has no assets
+
+ Resources res = new Resources(mResources.getAssets(), dm, cfg);
+ assertEquals("ae", res.getResolvedLocale().toLanguageTag());
+ }
+
+ public void testGetResolvedLocale_secondaryLocaleIsSupported() {
+ final DisplayMetrics dm = new DisplayMetrics();
+ dm.setToDefaults();
+ final Configuration cfg = new Configuration();
+ cfg.setToDefaults();
+ // Avestan has no assets, but Czech does
+ cfg.setLocales(LocaleList.forLanguageTags("ae,cs"));
+
+ Resources res = new Resources(mResources.getAssets(), dm, cfg);
+ assertEquals("cs", res.getResolvedLocale().toLanguageTag());
+ }
+
+ public void testGetResolvedLocale_secondaryLocaleIsPartiallySupported() {
+ final DisplayMetrics dm = new DisplayMetrics();
+ dm.setToDefaults();
+ final Configuration cfg = new Configuration();
+ cfg.setToDefaults();
+ // Avestan has no assets;
+ // Persian has assets for Iran, but not Afghanistan (partial match is accepted);
+ // Czech has assets (but we don't get to it)
+ cfg.setLocales(LocaleList.forLanguageTags("ae,fa-AF,cs"));
+
+ Resources res = new Resources(mResources.getAssets(), dm, cfg);
+ assertEquals("fa-AF", res.getResolvedLocale().toLanguageTag());
+ }
+
+ public void testGetResolvedLocale_SystemResourcesLocaleNonNull() {
+ Resources res = Resources.getSystem();
+ assertNotNull(res.getResolvedLocale());
+ }
+
+ public void testGetResolvedLocale_NonNull() {
+ Resources res = createNewResources();
+ assertNotNull(res.getResolvedLocale());
+ }
+
public void testGetDimensionPixelSize() {
try {
mResources.getDimensionPixelSize(-1);
diff --git a/tests/tests/graphics/res/drawable/grayscale_jpg.jpg b/tests/tests/graphics/res/drawable/grayscale_jpg.jpg
new file mode 100644
index 0000000..6c6ae32
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/grayscale_jpg.jpg
Binary files differ
diff --git a/tests/tests/graphics/res/drawable/grayscale_png.png b/tests/tests/graphics/res/drawable/grayscale_png.png
new file mode 100644
index 0000000..9f1beac
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/grayscale_png.png
Binary files differ
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
index 2b05f99..a93fd27 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
@@ -571,6 +571,116 @@
verifyScaled(BitmapFactory.decodeStream(obtainInputStream(), null, scaledOpt));
}
+ public void testConfigs() {
+ // The output Config of a BitmapFactory decode depends on the request from the
+ // client and the properties of the image to be decoded.
+ //
+ // Options.inPreferredConfig = Config.ARGB_8888
+ // This is the default value of inPreferredConfig. In this case, the image
+ // will always be decoded to Config.ARGB_8888.
+ // Options.inPreferredConfig = Config.RGB_565
+ // If the encoded image is opaque, we will decode to Config.RGB_565,
+ // otherwise we will decode to whichever color type is the most natural match
+ // for the encoded data.
+ // Options.inPreferredConfig = Config.ARGB_4444
+ // This is deprecated and will always decode to Config.ARGB_8888.
+ // Options.inPreferredConfig = Config.ALPHA_8
+ // If the encoded image is gray, we will decode to 8-bit grayscale values
+ // and indicate that the output bitmap is Config.ALPHA_8. This is somewhat
+ // misleading because the image is really opaque and grayscale, but we are
+ // labeling each pixel as if it is a translucency (alpha) value. If the
+ // encoded image is not gray, we will decode to whichever color type is the
+ // most natural match for the encoded data.
+ // Options.inPreferredConfig = null
+ // We will decode to whichever Config is the most natural match with the
+ // encoded data. This could be 8-bit indices into a color table (call this
+ // INDEX_8), ALPHA_8 (gray), or ARGB_8888.
+ //
+ // This test ensures that images are decoded to the intended Config and that the
+ // decodes match regardless of the Config.
+ decodeConfigs(R.drawable.alpha, 31, 31, true, false, false);
+ decodeConfigs(R.drawable.baseline_jpeg, 1280, 960, false, false, false);
+ decodeConfigs(R.drawable.bmp_test, 320, 240, false, false, false);
+ decodeConfigs(R.drawable.scaled2, 6, 8, false, false, true);
+ decodeConfigs(R.drawable.grayscale_jpg, 128, 128, false, true, false);
+ decodeConfigs(R.drawable.grayscale_png, 128, 128, false, true, false);
+ }
+
+ private void decodeConfigs(int id, int width, int height, boolean hasAlpha, boolean isGray,
+ boolean hasColorTable) {
+ Options opts = new BitmapFactory.Options();
+ opts.inScaled = false;
+ assertEquals(Config.ARGB_8888, opts.inPreferredConfig);
+ Bitmap reference = BitmapFactory.decodeResource(mRes, id, opts);
+ assertNotNull(reference);
+ assertEquals(width, reference.getWidth());
+ assertEquals(height, reference.getHeight());
+ assertEquals(Config.ARGB_8888, reference.getConfig());
+
+ opts.inPreferredConfig = Config.ARGB_4444;
+ Bitmap argb4444 = BitmapFactory.decodeResource(mRes, id, opts);
+ assertNotNull(argb4444);
+ assertEquals(width, argb4444.getWidth());
+ assertEquals(height, argb4444.getHeight());
+ // ARGB_4444 is deprecated and we should decode to ARGB_8888.
+ assertEquals(Config.ARGB_8888, argb4444.getConfig());
+ compareBitmaps(reference, argb4444, 0, true, true);
+
+ opts.inPreferredConfig = Config.RGB_565;
+ Bitmap rgb565 = BitmapFactory.decodeResource(mRes, id, opts);
+ assertNotNull(rgb565);
+ assertEquals(width, rgb565.getWidth());
+ assertEquals(height, rgb565.getHeight());
+ if (!hasAlpha) {
+ assertEquals(Config.RGB_565, rgb565.getConfig());
+ // Convert the RGB_565 bitmap to ARGB_8888 and test that it is similar to
+ // the reference. We lose information when decoding to 565, so there must
+ // be some tolerance. The tolerance is intentionally loose to allow us some
+ // flexibility regarding if we dither and how we color convert.
+ compareBitmaps(reference, rgb565.copy(Config.ARGB_8888, false), 30, true, true);
+ }
+
+ opts.inPreferredConfig = Config.ALPHA_8;
+ Bitmap alpha8 = BitmapFactory.decodeResource(mRes, id, opts);
+ assertNotNull(alpha8);
+ assertEquals(width, reference.getWidth());
+ assertEquals(height, reference.getHeight());
+ if (isGray) {
+ assertEquals(Config.ALPHA_8, alpha8.getConfig());
+ // Convert the ALPHA_8 bitmap to ARGB_8888 and test that it is identical to
+ // the reference. We must do this manually because we are abusing ALPHA_8
+ // in order to represent grayscale.
+ compareBitmaps(reference, grayToARGB(alpha8), 0, true, true);
+ }
+
+ // Setting inPreferredConfig to null selects the most natural color type for
+ // the encoded data. If the image has a color table, this should be INDEX_8.
+ // If we decode to INDEX_8, the output bitmap will report that the Config is
+ // null.
+ opts.inPreferredConfig = null;
+ Bitmap index8 = BitmapFactory.decodeResource(mRes, id, opts);
+ assertNotNull(index8);
+ assertEquals(width, index8.getWidth());
+ assertEquals(height, index8.getHeight());
+ if (hasColorTable) {
+ assertEquals(null, index8.getConfig());
+ // Convert the INDEX_8 bitmap to ARGB_8888 and test that it is identical to
+ // the reference.
+ compareBitmaps(reference, index8.copy(Config.ARGB_8888, false), 0, true, true);
+ }
+ }
+
+ private Bitmap grayToARGB(Bitmap gray) {
+ Bitmap argb = Bitmap.createBitmap(gray.getWidth(), gray.getHeight(), Config.ARGB_8888);
+ for (int y = 0; y < argb.getHeight(); y++) {
+ for (int x = 0; x < argb.getWidth(); x++) {
+ int grayByte = Color.alpha(gray.getPixel(x, y));
+ argb.setPixel(x, y, Color.rgb(grayByte, grayByte, grayByte));
+ }
+ }
+ return argb;
+ }
+
private byte[] obtainArray() {
ByteArrayOutputStream stm = new ByteArrayOutputStream();
Options opt = new BitmapFactory.Options();