1 /*
2  * Copyright (C) 2014 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 package com.android.cts.intent.receiver;
17 
18 import android.app.Activity;
19 import android.content.ClipData;
20 import android.content.ClipboardManager;
21 import android.content.Intent;
22 import android.database.ContentObserver;
23 import android.net.Uri;
24 import android.os.Bundle;
25 import android.os.Handler;
26 import android.os.HandlerThread;
27 import android.util.Log;
28 
29 import java.io.BufferedReader;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.InputStreamReader;
33 import java.io.OutputStreamWriter;
34 import java.util.concurrent.Semaphore;
35 import java.util.concurrent.TimeUnit;
36 
37 /**
38  * Class to receive intents sent across profile boundaries, and read/write to content uri specified
39  * in these intents to test cross-profile content uris.
40  */
41 public class IntentReceiverActivity extends Activity {
42 
43     private static final String TAG = "IntentReceiverActivity";
44 
45     private static final String ACTION_COPY_TO_CLIPBOARD =
46             "com.android.cts.action.COPY_TO_CLIPBOARD";
47 
48     private static final String ACTION_READ_FROM_URI =
49             "com.android.cts.action.READ_FROM_URI";
50 
51     private static final String ACTION_TAKE_PERSISTABLE_URI_PERMISSION =
52             "com.android.cts.action.TAKE_PERSISTABLE_URI_PERMISSION";
53 
54     private static final String ACTION_WRITE_TO_URI =
55             "com.android.cts.action.WRITE_TO_URI";
56 
57     private static final String ACTION_JUST_CREATE =
58             "com.android.cts.action.JUST_CREATE";
59 
60     private static final String ACTION_CREATE_AND_WAIT =
61             "com.android.cts.action.CREATE_AND_WAIT";
62 
63     private static final String RECEIVER_ACTIVITY_CREATED_ACTION =
64             "com.android.cts.deviceowner.action.RECEIVER_ACTIVITY_CREATED";
65 
66     private static final String RECEIVER_ACTIVITY_DESTROYED_ACTION =
67             "com.android.cts.deviceowner.action.RECEIVER_ACTIVITY_DESTROYED";
68 
69     public static final String ACTION_NOTIFY_URI_CHANGE
70             = "com.android.cts.action.NOTIFY_URI_CHANGE";
71 
72     public static final String ACTION_OBSERVE_URI_CHANGE
73             = "com.android.cts.action.OBSERVE_URI_CHANGE";
74 
75     private static final String EXTRA_CAUGHT_SECURITY_EXCEPTION = "extra_caught_security_exception";
76 
onCreate(Bundle savedInstanceState)77     public void onCreate(Bundle savedInstanceState) {
78         super.onCreate(savedInstanceState);
79         final Intent received = getIntent();
80         final String action = received.getAction();
81         final ClipData clipData = getIntent().getClipData();
82         final Uri uri = clipData != null ? clipData.getItemAt(0).getUri() : null;
83         if (ACTION_COPY_TO_CLIPBOARD.equals(action)) {
84             String text = received.getStringExtra("extra_text");
85             Log.i(TAG, "Copying \"" + text + "\" to the clipboard");
86             ClipData clip = ClipData.newPlainText("", text);
87             ClipboardManager clipboard =
88                     (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
89             clipboard.setPrimaryClip(clip);
90             setResult(Activity.RESULT_OK);
91         } else if (ACTION_READ_FROM_URI.equals(action)) {
92             Intent result = new Intent();
93             String message = null;
94             try {
95                 message = getFirstLineFromUri(uri);
96             } catch (SecurityException e) {
97                 Log.i(TAG, "Caught a SecurityException while trying to read " + uri, e);
98                 result.putExtra(EXTRA_CAUGHT_SECURITY_EXCEPTION, true);
99             } catch (IOException e) {
100                 Log.i(TAG, "Caught a IOException while trying to read " + uri, e);
101             }
102             Log.i(TAG, "Message received in reading test: " + message);
103             result.putExtra("extra_response", message);
104             setResult(Activity.RESULT_OK, result);
105         } else if (ACTION_TAKE_PERSISTABLE_URI_PERMISSION.equals(action)) {
106             Log.i(TAG, "Taking persistable uri permission to " + uri);
107             getContentResolver().takePersistableUriPermission(uri,
108                     Intent.FLAG_GRANT_READ_URI_PERMISSION);
109             setResult(Activity.RESULT_OK);
110         } else if (ACTION_WRITE_TO_URI.equals(action)) {
111             Intent result = new Intent();
112             String message = received.getStringExtra("extra_message");
113             Log.i(TAG, "Message received in writing test: " + message);
114             try {
115                 writeToUri(uri, message);
116             } catch (SecurityException e) {
117                 Log.i(TAG, "Caught a SecurityException while trying to write to " + uri, e);
118                 result.putExtra(EXTRA_CAUGHT_SECURITY_EXCEPTION, true);
119             } catch (IOException e) {
120                 Log.i(TAG, "Caught a IOException while trying to write to " + uri, e);
121             }
122             setResult(Activity.RESULT_OK, result);
123         } else if (ACTION_NOTIFY_URI_CHANGE.equals(action)) {
124             Log.i(TAG, "Notifying a uri change to " + uri);
125             getContentResolver().notifyChange(uri, null);
126             setResult(Activity.RESULT_OK);
127         } else if (ACTION_OBSERVE_URI_CHANGE.equals(action)) {
128             Log.i(TAG, "Observing a uri change to " + uri);
129             HandlerThread handlerThread = new HandlerThread("observer");
130             handlerThread.start();
131             UriObserver uriObserver = new UriObserver(new Handler(handlerThread.getLooper()));
132             try {
133                 getContentResolver().registerContentObserver(uri, false, uriObserver);
134                 uriObserver.waitForNotify();
135                 setResult(Activity.RESULT_OK, new Intent());
136             } finally {
137                 getContentResolver().unregisterContentObserver(uriObserver);
138                 handlerThread.quit();
139             }
140         } else if (ACTION_JUST_CREATE.equals(action) || ACTION_CREATE_AND_WAIT.equals(action)) {
141             sendBroadcast(new Intent(RECEIVER_ACTIVITY_CREATED_ACTION));
142         }
143 
144         if (!ACTION_CREATE_AND_WAIT.equals(action)) {
145             finish();
146         }
147     }
148 
149     @Override
onDestroy()150     public void onDestroy() {
151         sendBroadcast(new Intent(RECEIVER_ACTIVITY_DESTROYED_ACTION));
152         super.onDestroy();
153     }
154 
155     private class UriObserver extends ContentObserver {
156         private final Semaphore mNotificationReceived = new Semaphore(0);
UriObserver(Handler handler)157         public UriObserver(Handler handler) {
158            super(handler);
159         }
160 
161         @Override
onChange(boolean selfChange, Uri uri)162         public void onChange(boolean selfChange, Uri uri) {
163             super.onChange(selfChange, uri);
164             // Here, we can't test that uri is the uri that was called with registerContentObserver
165             // because it doesn't have the userId in the userInfo part.
166             mNotificationReceived.release(1);
167         }
168 
waitForNotify()169         private boolean waitForNotify() {
170             // The uri notification may not come immediately.
171             try {
172                 return mNotificationReceived.tryAcquire(1, 30, TimeUnit.SECONDS);
173             } catch (InterruptedException e) {
174                 Log.e(TAG, "Interrupted while waiting for notification change", e);
175                 return false;
176             }
177         }
178     }
179 
180     /**
181      * Returns the first line of the file associated with uri.
182      */
getFirstLineFromUri(Uri uri)183     private String getFirstLineFromUri(Uri uri) throws IOException {
184         InputStream is = getContentResolver().openInputStream(uri);
185         BufferedReader r = new BufferedReader(new InputStreamReader(is));
186         return r.readLine();
187     }
188 
writeToUri(Uri uri, String text)189     private void writeToUri(Uri uri, String text) throws IOException {
190         OutputStreamWriter writer = new OutputStreamWriter(
191                 getContentResolver().openOutputStream(uri));
192         writer.write(text);
193         writer.flush();
194         writer.close();
195     }
196 }
197