• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.content.cts;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotNull;
24 import static org.junit.Assert.assertNull;
25 import static org.junit.Assert.assertTrue;
26 import static org.junit.Assume.assumeTrue;
27 
28 import android.Manifest;
29 import android.app.Activity;
30 import android.content.ClipData;
31 import android.content.ClipData.Item;
32 import android.content.ClipDescription;
33 import android.content.ClipboardManager;
34 import android.content.ClipboardManager.OnPrimaryClipChangedListener;
35 import android.content.ContentResolver;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.pm.PackageManager;
39 import android.net.Uri;
40 
41 import androidx.test.InstrumentationRegistry;
42 import androidx.test.runner.AndroidJUnit4;
43 import androidx.test.uiautomator.By;
44 import androidx.test.uiautomator.UiDevice;
45 import androidx.test.uiautomator.Until;
46 
47 import com.android.compatibility.common.util.SystemUtil;
48 
49 import org.junit.After;
50 import org.junit.Before;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 
54 import java.util.concurrent.CountDownLatch;
55 import java.util.concurrent.TimeUnit;
56 
57 @RunWith(AndroidJUnit4.class)
58 //@AppModeFull // TODO(Instant) Should clip board data be visible?
59 public class ClipboardManagerTest {
60     private final Context mContext = InstrumentationRegistry.getTargetContext();
61     private ClipboardManager mClipboardManager;
62     private UiDevice mUiDevice;
63 
64     @Before
setUp()65     public void setUp() throws Exception {
66         assumeTrue("Skipping Test: Wear-Os does not support ClipboardService", hasAutoFillFeature());
67         mClipboardManager = mContext.getSystemService(ClipboardManager.class);
68         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
69         mUiDevice.wakeUp();
70 
71         // Clear any dialogs and launch an activity as focus is needed to access clipboard.
72         mUiDevice.pressHome();
73         mUiDevice.pressBack();
74         launchActivity(MockActivity.class);
75     }
76 
77     @After
cleanUp()78     public void cleanUp() {
79         if (mClipboardManager != null) {
80             mClipboardManager.clearPrimaryClip();
81         }
82         InstrumentationRegistry.getInstrumentation().getUiAutomation()
83                 .dropShellPermissionIdentity();
84     }
85 
86     @Test
testSetGetText()87     public void testSetGetText() {
88         ClipboardManager clipboardManager = mClipboardManager;
89         clipboardManager.setText("Test Text 1");
90         assertEquals("Test Text 1", clipboardManager.getText());
91 
92         clipboardManager.setText("Test Text 2");
93         assertEquals("Test Text 2", clipboardManager.getText());
94     }
95 
96     @Test
testHasPrimaryClip()97     public void testHasPrimaryClip() {
98         ClipboardManager clipboardManager = mClipboardManager;
99         if (clipboardManager.hasPrimaryClip()) {
100             assertNotNull(clipboardManager.getPrimaryClip());
101             assertNotNull(clipboardManager.getPrimaryClipDescription());
102         } else {
103             assertNull(clipboardManager.getPrimaryClip());
104             assertNull(clipboardManager.getPrimaryClipDescription());
105         }
106 
107         clipboardManager.setPrimaryClip(ClipData.newPlainText("Label", "Text"));
108         assertTrue(clipboardManager.hasPrimaryClip());
109     }
110 
111     @Test
testSetPrimaryClip_plainText()112     public void testSetPrimaryClip_plainText() {
113         ClipData textData = ClipData.newPlainText("TextLabel", "Text");
114         assertSetPrimaryClip(textData, "TextLabel",
115                 new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN},
116                 new ExpectedClipItem("Text", null, null));
117     }
118 
119     @Test
testSetPrimaryClip_intent()120     public void testSetPrimaryClip_intent() {
121         Intent intent = new Intent(mContext, ClipboardManagerTest.class);
122         ClipData intentData = ClipData.newIntent("IntentLabel", intent);
123         assertSetPrimaryClip(intentData, "IntentLabel",
124                 new String[] {ClipDescription.MIMETYPE_TEXT_INTENT},
125                 new ExpectedClipItem(null, intent, null));
126     }
127 
128     @Test
testSetPrimaryClip_rawUri()129     public void testSetPrimaryClip_rawUri() {
130         Uri uri = Uri.parse("http://www.google.com");
131         ClipData uriData = ClipData.newRawUri("UriLabel", uri);
132         assertSetPrimaryClip(uriData, "UriLabel",
133                 new String[] {ClipDescription.MIMETYPE_TEXT_URILIST},
134                 new ExpectedClipItem(null, null, uri));
135     }
136 
137     @Test
testSetPrimaryClip_contentUri()138     public void testSetPrimaryClip_contentUri() {
139         Uri contentUri = Uri.parse("content://cts/test/for/clipboardmanager");
140         ClipData contentUriData = ClipData.newUri(mContext.getContentResolver(),
141                 "ContentUriLabel", contentUri);
142         assertSetPrimaryClip(contentUriData, "ContentUriLabel",
143                 new String[] {ClipDescription.MIMETYPE_TEXT_URILIST},
144                 new ExpectedClipItem(null, null, contentUri));
145     }
146 
147     @Test
testSetPrimaryClip_complexItem()148     public void testSetPrimaryClip_complexItem() {
149         Intent intent = new Intent(mContext, ClipboardManagerTest.class);
150         Uri uri = Uri.parse("http://www.google.com");
151         ClipData multiData = new ClipData(new ClipDescription("ComplexItemLabel",
152                 new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN,
153                         ClipDescription.MIMETYPE_TEXT_INTENT,
154                         ClipDescription.MIMETYPE_TEXT_URILIST}),
155                 new Item("Text", intent, uri));
156         assertSetPrimaryClip(multiData, "ComplexItemLabel",
157                 new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN,
158                         ClipDescription.MIMETYPE_TEXT_INTENT,
159                         ClipDescription.MIMETYPE_TEXT_URILIST},
160                 new ExpectedClipItem("Text", intent, uri));
161     }
162 
163     @Test
testSetPrimaryClip_multipleItems()164     public void testSetPrimaryClip_multipleItems() {
165         Intent intent = new Intent(mContext, ClipboardManagerTest.class);
166         Uri uri = Uri.parse("http://www.google.com");
167         ClipData textData = ClipData.newPlainText("TextLabel", "Text");
168         textData.addItem(new Item("More Text"));
169         textData.addItem(new Item(intent));
170         textData.addItem(new Item(uri));
171         assertSetPrimaryClip(textData, "TextLabel",
172                 new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN},
173                 new ExpectedClipItem("Text", null, null),
174                 new ExpectedClipItem("More Text", null, null),
175                 new ExpectedClipItem(null, intent, null),
176                 new ExpectedClipItem(null, null, uri));
177     }
178 
179     @Test
testSetPrimaryClip_multipleMimeTypes()180     public void testSetPrimaryClip_multipleMimeTypes() {
181         ContentResolver contentResolver = mContext.getContentResolver();
182 
183         Intent intent = new Intent(mContext, ClipboardManagerTest.class);
184         Uri uri = Uri.parse("http://www.google.com");
185         Uri contentUri1 = Uri.parse("content://ctstest/testtable1");
186         Uri contentUri2 = Uri.parse("content://ctstest/testtable2");
187         Uri contentUri3 = Uri.parse("content://ctstest/testtable1/0");
188         Uri contentUri4 = Uri.parse("content://ctstest/testtable1/1");
189         Uri contentUri5 = Uri.parse("content://ctstest/testtable2/0");
190         Uri contentUri6 = Uri.parse("content://ctstest/testtable2/1");
191         Uri contentUri7 = Uri.parse("content://ctstest/testtable2/2");
192         Uri contentUri8 = Uri.parse("content://ctstest/testtable2/3");
193 
194         ClipData clipData = ClipData.newPlainText("TextLabel", "Text");
195         clipData.addItem(contentResolver, new Item("More Text"));
196         clipData.addItem(contentResolver, new Item(intent));
197         clipData.addItem(contentResolver, new Item(uri));
198         clipData.addItem(contentResolver, new Item(contentUri1));
199         clipData.addItem(contentResolver, new Item(contentUri2));
200         clipData.addItem(contentResolver, new Item(contentUri3));
201         clipData.addItem(contentResolver, new Item(contentUri4));
202         clipData.addItem(contentResolver, new Item(contentUri5));
203         clipData.addItem(contentResolver, new Item(contentUri6));
204         clipData.addItem(contentResolver, new Item(contentUri7));
205         clipData.addItem(contentResolver, new Item(contentUri8));
206 
207         assertClipData(clipData, "TextLabel",
208                 new String[] {
209                         ClipDescription.MIMETYPE_TEXT_PLAIN,
210                         ClipDescription.MIMETYPE_TEXT_INTENT,
211                         ClipDescription.MIMETYPE_TEXT_URILIST,
212                         "vnd.android.cursor.dir/com.android.content.testtable1",
213                         "vnd.android.cursor.dir/com.android.content.testtable2",
214                         "vnd.android.cursor.item/com.android.content.testtable1",
215                         "vnd.android.cursor.item/com.android.content.testtable2",
216                         "image/jpeg",
217                         "audio/mpeg",
218                         "video/mpeg"
219                 },
220                 new ExpectedClipItem("Text", null, null),
221                 new ExpectedClipItem("More Text", null, null),
222                 new ExpectedClipItem(null, intent, null),
223                 new ExpectedClipItem(null, null, uri),
224                 new ExpectedClipItem(null, null, contentUri1),
225                 new ExpectedClipItem(null, null, contentUri2),
226                 new ExpectedClipItem(null, null, contentUri3),
227                 new ExpectedClipItem(null, null, contentUri4),
228                 new ExpectedClipItem(null, null, contentUri5),
229                 new ExpectedClipItem(null, null, contentUri6),
230                 new ExpectedClipItem(null, null, contentUri7),
231                 new ExpectedClipItem(null, null, contentUri8));
232     }
233 
234     @Test
testPrimaryClipChangedListener()235     public void testPrimaryClipChangedListener() throws Exception {
236         final CountDownLatch latch = new CountDownLatch(1);
237         mClipboardManager.addPrimaryClipChangedListener(new OnPrimaryClipChangedListener() {
238             @Override
239             public void onPrimaryClipChanged() {
240                 latch.countDown();
241             }
242         });
243 
244         final ClipData clipData = ClipData.newPlainText("TextLabel", "Text");
245         mClipboardManager.setPrimaryClip(clipData);
246 
247         latch.await(5, TimeUnit.SECONDS);
248     }
249 
250     @Test
testClearPrimaryClip()251     public void testClearPrimaryClip() {
252         final ClipData clipData = ClipData.newPlainText("TextLabel", "Text");
253         mClipboardManager.setPrimaryClip(clipData);
254         assertTrue(mClipboardManager.hasPrimaryClip());
255         assertTrue(mClipboardManager.hasText());
256         assertNotNull(mClipboardManager.getPrimaryClip());
257         assertNotNull(mClipboardManager.getPrimaryClipDescription());
258 
259         mClipboardManager.clearPrimaryClip();
260         assertFalse(mClipboardManager.hasPrimaryClip());
261         assertFalse(mClipboardManager.hasText());
262         assertNull(mClipboardManager.getPrimaryClip());
263         assertNull(mClipboardManager.getPrimaryClipDescription());
264     }
265 
266     @Test
testPrimaryClipNotAvailableWithoutFocus()267     public void testPrimaryClipNotAvailableWithoutFocus() throws Exception {
268         ClipData textData = ClipData.newPlainText("TextLabel", "Text1");
269         assertSetPrimaryClip(textData, "TextLabel",
270                 new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN},
271                 new ExpectedClipItem("Text1", null, null));
272 
273         // Press the home button to unfocus the app.
274         mUiDevice.pressHome();
275         mUiDevice.wait(Until.gone(By.pkg(MockActivity.class.getPackageName())), 5000);
276 
277         // We should see an empty clipboard now.
278         assertFalse(mClipboardManager.hasPrimaryClip());
279         assertFalse(mClipboardManager.hasText());
280         assertNull(mClipboardManager.getPrimaryClip());
281         assertNull(mClipboardManager.getPrimaryClipDescription());
282 
283         // We should be able to set the clipboard but not see the contents.
284         mClipboardManager.setPrimaryClip(ClipData.newPlainText("TextLabel", "Text2"));
285         assertFalse(mClipboardManager.hasPrimaryClip());
286         assertFalse(mClipboardManager.hasText());
287         assertNull(mClipboardManager.getPrimaryClip());
288         assertNull(mClipboardManager.getPrimaryClipDescription());
289 
290         // Launch an activity to get back in focus.
291         launchActivity(MockActivity.class);
292 
293         // Verify clipboard access is restored.
294         assertNotNull(mClipboardManager.getPrimaryClip());
295         assertNotNull(mClipboardManager.getPrimaryClipDescription());
296 
297         // Verify we were unable to change the clipboard while out of focus.
298         assertClipData(mClipboardManager.getPrimaryClip(),
299                 "TextLabel",
300                 new String[] {ClipDescription.MIMETYPE_TEXT_PLAIN},
301                 new ExpectedClipItem("Text2", null, null));
302     }
303 
304     @Test
testReadInBackgroundRequiresPermission()305     public void testReadInBackgroundRequiresPermission() throws Exception {
306         ClipData clip = ClipData.newPlainText("TextLabel", "Text1");
307         mClipboardManager.setPrimaryClip(clip);
308 
309         // Press the home button to unfocus the app.
310         mUiDevice.pressHome();
311         mUiDevice.wait(Until.gone(By.pkg(MockActivity.class.getPackageName())), 5000);
312 
313         // Without the READ_CLIPBOARD_IN_BACKGROUND permission, we should see an empty clipboard.
314         assertThat(mClipboardManager.hasPrimaryClip()).isFalse();
315         assertThat(mClipboardManager.hasText()).isFalse();
316         assertThat(mClipboardManager.getPrimaryClip()).isNull();
317         assertThat(mClipboardManager.getPrimaryClipDescription()).isNull();
318 
319         // Having the READ_CLIPBOARD_IN_BACKGROUND permission should allow us to read the clipboard
320         // even when we are not in the foreground. We use the shell identity to simulate holding
321         // this permission; in practice, only privileged system apps can hold this permission (e.g.
322         // an app that has the SYSTEM_TEXT_INTELLIGENCE role).
323         ClipData actual = SystemUtil.callWithShellPermissionIdentity(
324                 () -> mClipboardManager.getPrimaryClip(),
325                 android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND);
326         assertThat(actual).isNotNull();
327         assertThat(actual.getItemAt(0).getText()).isEqualTo("Text1");
328     }
329 
330     @Test
testClipSourceRecordedWhenClipSet()331     public void testClipSourceRecordedWhenClipSet() {
332         ClipData clipData = ClipData.newPlainText("TextLabel", "Text1");
333         mClipboardManager.setPrimaryClip(clipData);
334 
335         InstrumentationRegistry.getInstrumentation().getUiAutomation()
336                 .adoptShellPermissionIdentity(Manifest.permission.SET_CLIP_SOURCE);
337         assertThat(
338                 mClipboardManager.getPrimaryClipSource()).isEqualTo("android.content.cts");
339     }
340 
341     @Test
testSetPrimaryClipAsPackage()342     public void testSetPrimaryClipAsPackage() {
343         InstrumentationRegistry.getInstrumentation().getUiAutomation()
344                 .adoptShellPermissionIdentity(Manifest.permission.SET_CLIP_SOURCE);
345 
346         ClipData clipData = ClipData.newPlainText("TextLabel", "Text1");
347         mClipboardManager.setPrimaryClipAsPackage(clipData, "test.package");
348 
349         assertThat(
350                 mClipboardManager.getPrimaryClipSource()).isEqualTo("test.package");
351     }
352 
launchActivity(Class<? extends Activity> clazz)353     private void launchActivity(Class<? extends Activity> clazz) {
354         Intent intent = new Intent(Intent.ACTION_MAIN);
355         intent.setClassName(mContext.getPackageName(), clazz.getName());
356         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
357         mContext.startActivity(intent);
358         mUiDevice.wait(Until.hasObject(By.pkg(clazz.getPackageName())), 15000);
359     }
360 
361     private class ExpectedClipItem {
362         CharSequence mText;
363         Intent mIntent;
364         Uri mUri;
365 
ExpectedClipItem(CharSequence text, Intent intent, Uri uri)366         ExpectedClipItem(CharSequence text, Intent intent, Uri uri) {
367             mText = text;
368             mIntent = intent;
369             mUri = uri;
370         }
371     }
372 
assertSetPrimaryClip(ClipData clipData, String expectedLabel, String[] expectedMimeTypes, ExpectedClipItem... expectedClipItems)373     private void assertSetPrimaryClip(ClipData clipData,
374             String expectedLabel,
375             String[] expectedMimeTypes,
376             ExpectedClipItem... expectedClipItems) {
377         ClipboardManager clipboardManager = mClipboardManager;
378 
379         clipboardManager.setPrimaryClip(clipData);
380         assertTrue(clipboardManager.hasPrimaryClip());
381 
382         if (expectedClipItems != null
383                 && expectedClipItems.length > 0
384                 && expectedClipItems[0].mText != null) {
385             assertTrue(clipboardManager.hasText());
386         } else {
387             assertFalse(clipboardManager.hasText());
388         }
389 
390         assertNotNull(clipboardManager.getPrimaryClip());
391         assertNotNull(clipboardManager.getPrimaryClipDescription());
392 
393         assertClipData(clipboardManager.getPrimaryClip(),
394                 expectedLabel, expectedMimeTypes, expectedClipItems);
395 
396         assertClipDescription(clipboardManager.getPrimaryClipDescription(),
397                 expectedLabel, expectedMimeTypes);
398     }
399 
assertClipData(ClipData actualData, String expectedLabel, String[] expectedMimeTypes, ExpectedClipItem... expectedClipItems)400     private static void assertClipData(ClipData actualData, String expectedLabel,
401             String[] expectedMimeTypes, ExpectedClipItem... expectedClipItems) {
402         if (expectedClipItems != null) {
403             assertEquals(expectedClipItems.length, actualData.getItemCount());
404             for (int i = 0; i < expectedClipItems.length; i++) {
405                 assertClipItem(expectedClipItems[i], actualData.getItemAt(i));
406             }
407         } else {
408             throw new IllegalArgumentException("Should have at least one expectedClipItem...");
409         }
410 
411         assertClipDescription(actualData.getDescription(), expectedLabel, expectedMimeTypes);
412     }
413 
assertClipDescription(ClipDescription description, String expectedLabel, String... mimeTypes)414     private static void assertClipDescription(ClipDescription description, String expectedLabel,
415             String... mimeTypes) {
416         assertEquals(expectedLabel, description.getLabel());
417         assertEquals(mimeTypes.length, description.getMimeTypeCount());
418         int mimeTypeCount = description.getMimeTypeCount();
419         for (int i = 0; i < mimeTypeCount; i++) {
420             assertEquals(mimeTypes[i], description.getMimeType(i));
421         }
422     }
423 
assertClipItem(ExpectedClipItem expectedItem, Item item)424     private static void assertClipItem(ExpectedClipItem expectedItem, Item item) {
425         assertEquals(expectedItem.mText, item.getText());
426         if (expectedItem.mIntent != null) {
427             assertNotNull(item.getIntent());
428         } else {
429             assertNull(item.getIntent());
430         }
431         if (expectedItem.mUri != null) {
432             assertEquals(expectedItem.mUri.toString(), item.getUri().toString());
433         } else {
434             assertNull(item.getUri());
435         }
436     }
437 
hasAutoFillFeature()438     private boolean hasAutoFillFeature() {
439         return mContext.getPackageManager().hasSystemFeature(
440                 PackageManager.FEATURE_AUTOFILL);
441     }
442 }
443