blob: 10df30455183ab5eac1f4066834780aa6cef1699 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.cts;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import android.Manifest;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipData.Item;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.ClipboardManager.OnPrimaryClipChangedListener;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.Until;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.SystemUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
//@AppModeFull // TODO(Instant) Should clip board data be visible?
public class ClipboardManagerTest {
private final Context mContext = InstrumentationRegistry.getTargetContext();
private ClipboardManager mClipboardManager;
private UiDevice mUiDevice;
@Before
public void setUp() throws Exception {
assumeTrue("Skipping Test: Wear-Os does not support ClipboardService", hasAutoFillFeature());
mClipboardManager = mContext.getSystemService(ClipboardManager.class);
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mUiDevice.wakeUp();
launchActivity(MockActivity.class);
}
@After
public void cleanUp() {
mClipboardManager.clearPrimaryClip();
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.dropShellPermissionIdentity();
}
@Test
public void testSetGetText() {
ClipboardManager clipboardManager = mClipboardManager;
clipboardManager.setText("Test Text 1");
assertEquals("Test Text 1", clipboardManager.getText());
clipboardManager.setText("Test Text 2");
assertEquals("Test Text 2", clipboardManager.getText());
}
@Test
public void testHasPrimaryClip() {
ClipboardManager clipboardManager = mClipboardManager;
if (clipboardManager.hasPrimaryClip()) {
assertNotNull(clipboardManager.getPrimaryClip());
assertNotNull(clipboardManager.getPrimaryClipDescription());
} else {
assertNull(clipboardManager.getPrimaryClip());
assertNull(clipboardManager.getPrimaryClipDescription());
}
clipboardManager.setPrimaryClip(ClipData.newPlainText("Label", "Text"));
assertTrue(clipboardManager.hasPrimaryClip());
}
@Test
public void testSetPrimaryClip_plainText() {
ClipData textData = ClipData.newPlainText("TextLabel", "Text");
assertSetPrimaryClip(textData, "TextLabel",
new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN},
new ExpectedClipItem("Text", null, null));
}
@Test
public void testSetPrimaryClip_intent() {
Intent intent = new Intent(mContext, ClipboardManagerTest.class);
ClipData intentData = ClipData.newIntent("IntentLabel", intent);
assertSetPrimaryClip(intentData, "IntentLabel",
new String[] {ClipDescription.MIMETYPE_TEXT_INTENT},
new ExpectedClipItem(null, intent, null));
}
@Test
public void testSetPrimaryClip_rawUri() {
Uri uri = Uri.parse("http://www.google.com");
ClipData uriData = ClipData.newRawUri("UriLabel", uri);
assertSetPrimaryClip(uriData, "UriLabel",
new String[] {ClipDescription.MIMETYPE_TEXT_URILIST},
new ExpectedClipItem(null, null, uri));
}
@Test
public void testSetPrimaryClip_contentUri() {
Uri contentUri = Uri.parse("content://cts/test/for/clipboardmanager");
ClipData contentUriData = ClipData.newUri(mContext.getContentResolver(),
"ContentUriLabel", contentUri);
assertSetPrimaryClip(contentUriData, "ContentUriLabel",
new String[] {ClipDescription.MIMETYPE_TEXT_URILIST},
new ExpectedClipItem(null, null, contentUri));
}
@Test
public void testSetPrimaryClip_complexItem() {
Intent intent = new Intent(mContext, ClipboardManagerTest.class);
Uri uri = Uri.parse("http://www.google.com");
ClipData multiData = new ClipData(new ClipDescription("ComplexItemLabel",
new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN,
ClipDescription.MIMETYPE_TEXT_INTENT,
ClipDescription.MIMETYPE_TEXT_URILIST}),
new Item("Text", intent, uri));
assertSetPrimaryClip(multiData, "ComplexItemLabel",
new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN,
ClipDescription.MIMETYPE_TEXT_INTENT,
ClipDescription.MIMETYPE_TEXT_URILIST},
new ExpectedClipItem("Text", intent, uri));
}
@Test
public void testSetPrimaryClip_multipleItems() {
Intent intent = new Intent(mContext, ClipboardManagerTest.class);
Uri uri = Uri.parse("http://www.google.com");
ClipData textData = ClipData.newPlainText("TextLabel", "Text");
textData.addItem(new Item("More Text"));
textData.addItem(new Item(intent));
textData.addItem(new Item(uri));
assertSetPrimaryClip(textData, "TextLabel",
new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN},
new ExpectedClipItem("Text", null, null),
new ExpectedClipItem("More Text", null, null),
new ExpectedClipItem(null, intent, null),
new ExpectedClipItem(null, null, uri));
}
@Test
public void testSetPrimaryClip_multipleMimeTypes() {
ContentResolver contentResolver = mContext.getContentResolver();
Intent intent = new Intent(mContext, ClipboardManagerTest.class);
Uri uri = Uri.parse("http://www.google.com");
Uri contentUri1 = Uri.parse("content://ctstest/testtable1");
Uri contentUri2 = Uri.parse("content://ctstest/testtable2");
Uri contentUri3 = Uri.parse("content://ctstest/testtable1/0");
Uri contentUri4 = Uri.parse("content://ctstest/testtable1/1");
Uri contentUri5 = Uri.parse("content://ctstest/testtable2/0");
Uri contentUri6 = Uri.parse("content://ctstest/testtable2/1");
Uri contentUri7 = Uri.parse("content://ctstest/testtable2/2");
Uri contentUri8 = Uri.parse("content://ctstest/testtable2/3");
ClipData clipData = ClipData.newPlainText("TextLabel", "Text");
clipData.addItem(contentResolver, new Item("More Text"));
clipData.addItem(contentResolver, new Item(intent));
clipData.addItem(contentResolver, new Item(uri));
clipData.addItem(contentResolver, new Item(contentUri1));
clipData.addItem(contentResolver, new Item(contentUri2));
clipData.addItem(contentResolver, new Item(contentUri3));
clipData.addItem(contentResolver, new Item(contentUri4));
clipData.addItem(contentResolver, new Item(contentUri5));
clipData.addItem(contentResolver, new Item(contentUri6));
clipData.addItem(contentResolver, new Item(contentUri7));
clipData.addItem(contentResolver, new Item(contentUri8));
assertClipData(clipData, "TextLabel",
new String[] {
ClipDescription.MIMETYPE_TEXT_PLAIN,
ClipDescription.MIMETYPE_TEXT_INTENT,
ClipDescription.MIMETYPE_TEXT_URILIST,
"vnd.android.cursor.dir/com.android.content.testtable1",
"vnd.android.cursor.dir/com.android.content.testtable2",
"vnd.android.cursor.item/com.android.content.testtable1",
"vnd.android.cursor.item/com.android.content.testtable2",
"image/jpeg",
"audio/mpeg",
"video/mpeg"
},
new ExpectedClipItem("Text", null, null),
new ExpectedClipItem("More Text", null, null),
new ExpectedClipItem(null, intent, null),
new ExpectedClipItem(null, null, uri),
new ExpectedClipItem(null, null, contentUri1),
new ExpectedClipItem(null, null, contentUri2),
new ExpectedClipItem(null, null, contentUri3),
new ExpectedClipItem(null, null, contentUri4),
new ExpectedClipItem(null, null, contentUri5),
new ExpectedClipItem(null, null, contentUri6),
new ExpectedClipItem(null, null, contentUri7),
new ExpectedClipItem(null, null, contentUri8));
}
@Test
public void testPrimaryClipChangedListener() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
mClipboardManager.addPrimaryClipChangedListener(new OnPrimaryClipChangedListener() {
@Override
public void onPrimaryClipChanged() {
latch.countDown();
}
});
final ClipData clipData = ClipData.newPlainText("TextLabel", "Text");
mClipboardManager.setPrimaryClip(clipData);
latch.await(5, TimeUnit.SECONDS);
}
@Test
public void testClearPrimaryClip() {
final ClipData clipData = ClipData.newPlainText("TextLabel", "Text");
mClipboardManager.setPrimaryClip(clipData);
assertTrue(mClipboardManager.hasPrimaryClip());
assertTrue(mClipboardManager.hasText());
assertNotNull(mClipboardManager.getPrimaryClip());
assertNotNull(mClipboardManager.getPrimaryClipDescription());
mClipboardManager.clearPrimaryClip();
assertFalse(mClipboardManager.hasPrimaryClip());
assertFalse(mClipboardManager.hasText());
assertNull(mClipboardManager.getPrimaryClip());
assertNull(mClipboardManager.getPrimaryClipDescription());
}
@Test
public void testPrimaryClipNotAvailableWithoutFocus() throws Exception {
ClipData textData = ClipData.newPlainText("TextLabel", "Text1");
assertSetPrimaryClip(textData, "TextLabel",
new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN},
new ExpectedClipItem("Text1", null, null));
// Press the home button to unfocus the app.
mUiDevice.pressHome();
mUiDevice.wait(Until.gone(By.pkg(MockActivity.class.getPackageName())), 5000);
// We should see an empty clipboard now.
assertFalse(mClipboardManager.hasPrimaryClip());
assertFalse(mClipboardManager.hasText());
assertNull(mClipboardManager.getPrimaryClip());
assertNull(mClipboardManager.getPrimaryClipDescription());
// We should be able to set the clipboard but not see the contents.
mClipboardManager.setPrimaryClip(ClipData.newPlainText("TextLabel", "Text2"));
assertFalse(mClipboardManager.hasPrimaryClip());
assertFalse(mClipboardManager.hasText());
assertNull(mClipboardManager.getPrimaryClip());
assertNull(mClipboardManager.getPrimaryClipDescription());
// Launch an activity to get back in focus.
launchActivity(MockActivity.class);
// Verify clipboard access is restored.
assertNotNull(mClipboardManager.getPrimaryClip());
assertNotNull(mClipboardManager.getPrimaryClipDescription());
// Verify we were unable to change the clipboard while out of focus.
assertClipData(mClipboardManager.getPrimaryClip(),
"TextLabel",
new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN},
new ExpectedClipItem("Text2", null, null));
}
@Test
public void testReadInBackgroundRequiresPermission() throws Exception {
ClipData clip = ClipData.newPlainText("TextLabel", "Text1");
mClipboardManager.setPrimaryClip(clip);
// Press the home button to unfocus the app.
mUiDevice.pressHome();
mUiDevice.wait(Until.gone(By.pkg(MockActivity.class.getPackageName())), 5000);
// Without the READ_CLIPBOARD_IN_BACKGROUND permission, we should see an empty clipboard.
assertThat(mClipboardManager.hasPrimaryClip()).isFalse();
assertThat(mClipboardManager.hasText()).isFalse();
assertThat(mClipboardManager.getPrimaryClip()).isNull();
assertThat(mClipboardManager.getPrimaryClipDescription()).isNull();
// Having the READ_CLIPBOARD_IN_BACKGROUND permission should allow us to read the clipboard
// even when we are not in the foreground. We use the shell identity to simulate holding
// this permission; in practice, only privileged system apps can hold this permission (e.g.
// an app that has the SYSTEM_TEXT_INTELLIGENCE role).
ClipData actual = SystemUtil.callWithShellPermissionIdentity(
() -> mClipboardManager.getPrimaryClip(),
android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND);
assertThat(actual).isNotNull();
assertThat(actual.getItemAt(0).getText()).isEqualTo("Text1");
}
@Test
public void testClipSourceRecordedWhenClipSet() {
ClipData clipData = ClipData.newPlainText("TextLabel", "Text1");
mClipboardManager.setPrimaryClip(clipData);
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity(Manifest.permission.SET_CLIP_SOURCE);
assertThat(
mClipboardManager.getPrimaryClipSource()).isEqualTo("android.content.cts");
}
@Test
public void testSetPrimaryClipAsPackage() {
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity(Manifest.permission.SET_CLIP_SOURCE);
ClipData clipData = ClipData.newPlainText("TextLabel", "Text1");
mClipboardManager.setPrimaryClipAsPackage(clipData, "test.package");
assertThat(
mClipboardManager.getPrimaryClipSource()).isEqualTo("test.package");
}
private void launchActivity(Class<? extends Activity> clazz) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClassName(mContext.getPackageName(), clazz.getName());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
mUiDevice.wait(Until.hasObject(By.pkg(clazz.getPackageName())), 15000);
}
private class ExpectedClipItem {
CharSequence mText;
Intent mIntent;
Uri mUri;
ExpectedClipItem(CharSequence text, Intent intent, Uri uri) {
mText = text;
mIntent = intent;
mUri = uri;
}
}
private void assertSetPrimaryClip(ClipData clipData,
String expectedLabel,
String[] expectedMimeTypes,
ExpectedClipItem... expectedClipItems) {
ClipboardManager clipboardManager = mClipboardManager;
clipboardManager.setPrimaryClip(clipData);
assertTrue(clipboardManager.hasPrimaryClip());
if (expectedClipItems != null
&& expectedClipItems.length > 0
&& expectedClipItems[0].mText != null) {
assertTrue(clipboardManager.hasText());
} else {
assertFalse(clipboardManager.hasText());
}
assertNotNull(clipboardManager.getPrimaryClip());
assertNotNull(clipboardManager.getPrimaryClipDescription());
assertClipData(clipboardManager.getPrimaryClip(),
expectedLabel, expectedMimeTypes, expectedClipItems);
assertClipDescription(clipboardManager.getPrimaryClipDescription(),
expectedLabel, expectedMimeTypes);
}
private static void assertClipData(ClipData actualData, String expectedLabel,
String[] expectedMimeTypes, ExpectedClipItem... expectedClipItems) {
if (expectedClipItems != null) {
assertEquals(expectedClipItems.length, actualData.getItemCount());
for (int i = 0; i < expectedClipItems.length; i++) {
assertClipItem(expectedClipItems[i], actualData.getItemAt(i));
}
} else {
throw new IllegalArgumentException("Should have at least one expectedClipItem...");
}
assertClipDescription(actualData.getDescription(), expectedLabel, expectedMimeTypes);
}
private static void assertClipDescription(ClipDescription description, String expectedLabel,
String... mimeTypes) {
assertEquals(expectedLabel, description.getLabel());
assertEquals(mimeTypes.length, description.getMimeTypeCount());
int mimeTypeCount = description.getMimeTypeCount();
for (int i = 0; i < mimeTypeCount; i++) {
assertEquals(mimeTypes[i], description.getMimeType(i));
}
}
private static void assertClipItem(ExpectedClipItem expectedItem, Item item) {
assertEquals(expectedItem.mText, item.getText());
if (expectedItem.mIntent != null) {
assertNotNull(item.getIntent());
} else {
assertNull(item.getIntent());
}
if (expectedItem.mUri != null) {
assertEquals(expectedItem.mUri.toString(), item.getUri().toString());
} else {
assertNull(item.getUri());
}
}
private boolean hasAutoFillFeature() {
return mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_AUTOFILL);
}
}