CTS tests for cross-profile content uris.

Test that apps in other profiles receiving an intent can read/write uris passed in the ClipData.

BUG: 17746554

Change-Id: Ife0b9556d4b25fb99c93836309829d4c00412257
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 8f7b820..4a7725a 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -39,6 +39,7 @@
     CtsDeviceTaskswitchingAppB \
     CtsDeviceTaskswitchingControl \
     CtsDeviceUi \
+    CtsIntentReceiverApp \
     CtsManagedProfileApp \
     CtsMonkeyApp \
     CtsMonkeyApp2 \
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk b/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk
new file mode 100644
index 0000000..62c7f28
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsIntentReceiverApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml b/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
new file mode 100644
index 0000000..ab01ffb
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.intent.receiver">
+
+    <uses-sdk android:minSdkVersion="19"/>
+
+    <application>
+        <activity android:name="com.android.cts.intent.receiver.IntentReceiverActivity">
+            <intent-filter>
+                <action android:name="com.android.cts.action.READ_FROM_URI" />
+                <action android:name="com.android.cts.action.WRITE_TO_URI" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
new file mode 100644
index 0000000..2389402
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.intent.receiver;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+
+/**
+ * Class to receive intents sent across profile boundaries, and read/write to content uri specified
+ * in these intents to test cross-profile content uris.
+ */
+public class IntentReceiverActivity extends Activity {
+
+    private static final String TAG = "IntentReceiverActivity";
+
+    private static final String ACTION_READ_FROM_URI = "com.android.cts.action.READ_FROM_URI";
+
+    private static final String ACTION_WRITE_TO_URI = "com.android.cts.action.WRITE_TO_URI";
+
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Intent received = getIntent();
+        String action = received.getAction();
+
+        if (ACTION_READ_FROM_URI.equals(action)) {
+            Intent result = new Intent();
+            String message = getFirstLineFromUri(getIntent().getClipData().getItemAt(0).getUri());
+            Log.i(TAG, "message received in reading test: " + message);
+            result.putExtra("extra_response", message);
+            setResult(message != null ? Activity.RESULT_OK : Activity.RESULT_CANCELED, result);
+        } else if (ACTION_WRITE_TO_URI.equals(action)) {
+            Intent result = new Intent();
+            String message = received.getStringExtra("extra_message");
+            Log.i(TAG, "message received in writing test: " + message);
+            Uri uri = getIntent().getClipData().getItemAt(0).getUri();
+            boolean succeded = writeToUri(uri, message);
+            setResult(succeded ? Activity.RESULT_OK : Activity.RESULT_CANCELED);
+        }
+        finish();
+    }
+
+    /**
+     * Returns the first line of the file associated with uri.
+     */
+    private String getFirstLineFromUri(Uri uri) {
+        try {
+            InputStream is = getContentResolver().openInputStream(uri);
+            BufferedReader r = new BufferedReader(new InputStreamReader(is));
+            return r.readLine();
+        } catch (IOException e) {
+            Log.e(TAG, "could not read the uri " + uri, e);
+            return null;
+        }
+    }
+
+    private boolean writeToUri(Uri uri, String text) {
+        try {
+            OutputStreamWriter writer = new OutputStreamWriter(
+                    getContentResolver().openOutputStream(uri));
+            writer.write(text);
+            writer.flush();
+            writer.close();
+            return true;
+        } catch (IOException e) {
+            Log.e(TAG, "could not write to the uri " + uri, e);
+            return false;
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk b/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
index 00dace0..46e3cf7 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
@@ -26,6 +26,8 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
 
+LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4
+
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index 2c11b5c..4246b70 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -56,6 +56,20 @@
                 <action android:name="com.android.cts.managedprofile.ACTION_TEST_ALL_ACTIVITY" />
             </intent-filter>
         </activity>
+        <activity android:name=".crossprofilecontent.IntentSenderActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+            </intent-filter>
+        </activity>
+        <provider
+            android:name="android.support.v4.content.FileProvider"
+            android:authorities="com.android.cts.managedprofile.fileprovider"
+            android:grantUriPermissions="true"
+            android:exported="false">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/filepaths" />
+        </provider>
     </application>
 
     <instrumentation android:name="android.test.InstrumentationTestRunner"
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/filepaths.xml b/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/filepaths.xml
new file mode 100644
index 0000000..f7bf461
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/filepaths.xml
@@ -0,0 +1,17 @@
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+    <files-path path="texts/" name="texts" />
+</paths>
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BaseManagedProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BaseManagedProfileTest.java
index 8a2a6ec..2a54d97 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BaseManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BaseManagedProfileTest.java
@@ -32,7 +32,7 @@
     public static class BasicAdminReceiver extends DeviceAdminReceiver {
     }
 
-    static final ComponentName ADMIN_RECEIVER_COMPONENT = new ComponentName(
+    public static final ComponentName ADMIN_RECEIVER_COMPONENT = new ComponentName(
             BasicAdminReceiver.class.getPackage().getName(), BasicAdminReceiver.class.getName());
 
     protected DevicePolicyManager mDevicePolicyManager;
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/CrossProfileContentTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/CrossProfileContentTest.java
new file mode 100644
index 0000000..aa9506b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/CrossProfileContentTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.managedprofile.crossprofilecontent;
+
+import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.test.ActivityInstrumentationTestCase2;
+
+public class CrossProfileContentTest extends
+        ActivityInstrumentationTestCase2<IntentSenderActivity> {
+
+    private static final String MESSAGE = "Sample Message";
+
+    public CrossProfileContentTest() {
+        super(IntentSenderActivity.class);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        DevicePolicyManager dpm = (DevicePolicyManager)
+               getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
+        dpm.clearCrossProfileIntentFilters(ADMIN_RECEIVER_COMPONENT);
+        super.tearDown();
+    }
+
+    public void testReceiverCanRead() {
+        String response = getActivity().testReceiverCanRead(MESSAGE);
+        assertEquals(response, MESSAGE);
+    }
+
+    public void testReceiverCanWrite() {
+        String response = getActivity().testReceiverCanWrite(MESSAGE);
+        assertEquals(response, MESSAGE);
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/IntentSenderActivity.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/IntentSenderActivity.java
new file mode 100644
index 0000000..6c8020f
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/IntentSenderActivity.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.managedprofile.crossprofilecontent;
+
+import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.support.v4.content.FileProvider;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class IntentSenderActivity extends Activity {
+
+    private final static String TAG = "IntentSenderActivity";
+
+    private final CountDownLatch mLatch = new CountDownLatch(1);
+
+    private static final String ACTION_READ_FROM_URI = "com.android.cts.action.READ_FROM_URI";
+
+    private static final String ACTION_WRITE_TO_URI = "com.android.cts.action.WRITE_TO_URI";
+
+    private static final int TEST_RECEIVER_CAN_READ = 1;
+    private static final int TEST_RECEIVER_CAN_WRITE = 2;
+
+    private static final int WAIT_FOR_RESPONSE_TIMEOUT_SECONDS = 5;
+
+    private String mResponse;
+
+    private Uri mUriToWrite;
+
+    private DevicePolicyManager mDevicePolicyManager;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mDevicePolicyManager = (DevicePolicyManager)
+                getSystemService(Context.DEVICE_POLICY_SERVICE);
+    }
+
+    /**
+     * This method will send an intent to a receiver in another profile.
+     * This intent will have, in the ClipData, a uri whose associated file stores this message.
+     * The receiver will read the message from the uri, and put it inside the result intent.
+     * This method returns the response in the result intent, or null if no response was received.
+     */
+    String testReceiverCanRead(String message) {
+        IntentFilter testIntentFilter = new IntentFilter();
+        testIntentFilter.addAction(ACTION_READ_FROM_URI);
+        mDevicePolicyManager.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, testIntentFilter,
+                DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
+
+        Intent intent = new Intent(ACTION_READ_FROM_URI);
+        intent.setClipData(ClipData.newRawUri("", getUriWithTextInFile("reading_test", message)));
+        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        startActivityForResult(intent, TEST_RECEIVER_CAN_READ);
+        try {
+            mLatch.await(WAIT_FOR_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            return null;
+        }
+        return mResponse;
+    }
+
+    /**
+     * This method will send an intent to a receiver in another profile.
+     * This intent will have a message in an extra, and a uri specified by the ClipData.
+     * The receiver will read the message from the extra, and write it to the uri in
+     * the ClipData.
+     * This method returns what has been written in the uri.
+     */
+    String testReceiverCanWrite(String message) {
+        IntentFilter testIntentFilter = new IntentFilter();
+        testIntentFilter.addAction(ACTION_WRITE_TO_URI);
+        mDevicePolicyManager.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, testIntentFilter,
+                DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
+        // It's the receiver of the intent that should write to the uri, not us. So, for now, we
+        // write an empty string.
+        mUriToWrite = getUriWithTextInFile("writing_test", "");
+        Intent intent = new Intent(ACTION_WRITE_TO_URI);
+        intent.setClipData(ClipData.newRawUri("", mUriToWrite));
+        intent.putExtra("extra_message", message);
+        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        startActivityForResult(intent, TEST_RECEIVER_CAN_WRITE);
+        try {
+            mLatch.await(WAIT_FOR_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            return null;
+        }
+        return mResponse;
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == TEST_RECEIVER_CAN_READ) {
+            if (resultCode == Activity.RESULT_OK) {
+                mResponse = data.getStringExtra("extra_response");
+                Log.i(TAG, "response received in reading test: " + mResponse);
+            }
+        } else if (requestCode == TEST_RECEIVER_CAN_WRITE) {
+            if (resultCode == Activity.RESULT_OK) {
+                mResponse = getFirstLineFromUri(mUriToWrite);
+                Log.i(TAG, "response received in writing test: " + mResponse);
+            }
+        }
+        mLatch.countDown();
+        finish();
+    }
+
+    private Uri getUriWithTextInFile(String name, String text) {
+        String filename = getFilesDir() + File.separator + "texts" + File.separator + name + ".txt";
+        Log.i(TAG, "Creating file " + filename + " with text \"" + text + "\"");
+        final File file = new File(filename);
+        file.getParentFile().mkdirs(); // If the folder doesn't exists it is created
+        try {
+            FileWriter writer = new FileWriter(file);
+            writer.write(text);
+            writer.close();
+        } catch(IOException e) {
+            Log.e(TAG, "Could not create file " + filename + " with text " + text);
+            return null;
+        }
+        return FileProvider.getUriForFile(this,
+                "com.android.cts.managedprofile.fileprovider", file);
+    }
+
+    /**
+     * Returns the first line of the file associated with uri.
+     */
+    private String getFirstLineFromUri(Uri uri) {
+        try {
+            InputStream is = getContentResolver().openInputStream(uri);
+            BufferedReader r = new BufferedReader(new InputStreamReader(is));
+            return r.readLine();
+        } catch (IOException e) {
+            Log.e(TAG, "could not read the uri " + uri);
+            return null;
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 85ffb4c..b2c4ea5 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -28,6 +28,9 @@
     private static final String MANAGED_PROFILE_PKG = "com.android.cts.managedprofile";
     private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
 
+    private static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
+    private static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
+
     private static final String ADMIN_RECEIVER_TEST_CLASS =
             MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
 
@@ -105,6 +108,24 @@
 //        // TODO: Test with CtsVerifier for disambiguation cases
 //    }
 
+    public void testCrossProfileContent() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        try {
+            installApp(INTENT_RECEIVER_APK);
+
+            String command = "pm uninstall --user " + mUserId + " " + INTENT_RECEIVER_PKG;
+            CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": "
+                    + getDevice().executeShellCommand(command));
+
+            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+                    MANAGED_PROFILE_PKG + ".crossprofilecontent.CrossProfileContentTest", mUserId));
+        } finally {
+            getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
+        }
+    }
+
     private void disableActivityForUser(String activityName, int userId)
             throws DeviceNotAvailableException {
         String command = "pm disable --user " + userId + " " + MANAGED_PROFILE_PKG + "/."