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