1 /* 2 * Copyright (C) 2009 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.permissionpolicy.cts; 18 19 import android.app.Activity; 20 import android.app.PendingIntent; 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.pm.PackageManager; 26 import android.platform.test.annotations.AppModeFull; 27 import android.platform.test.annotations.SystemUserOnly; 28 import android.telephony.SmsManager; 29 import android.telephony.SubscriptionInfo; 30 import android.telephony.SubscriptionManager; 31 import android.telephony.TelephonyManager; 32 import android.test.AndroidTestCase; 33 import android.text.TextUtils; 34 import android.util.Log; 35 36 import java.util.Arrays; 37 import java.util.List; 38 import java.util.concurrent.Semaphore; 39 import java.util.concurrent.TimeUnit; 40 41 /** 42 * Verify Sms and Mms cannot be received without required permissions. 43 * Uses {@link android.telephony.SmsManager}. 44 */ 45 @AppModeFull(reason = "Instant apps cannot get the SEND_SMS permission") 46 @SystemUserOnly(reason = "Secondary users have the DISALLOW_SMS user restriction") 47 public class NoReceiveSmsPermissionTest extends AndroidTestCase { 48 49 private static final int SMS_DELIVERED_WAIT_TIME_MILLIS = 4000; 50 private static final String TELEPHONY_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED"; 51 private static final String MESSAGE_STATUS_RECEIVED_ACTION = 52 "com.android.cts.permission.sms.MESSAGE_STATUS_RECEIVED_ACTION"; 53 private static final String MESSAGE_SENT_ACTION = 54 "com.android.cts.permission.sms.MESSAGE_SENT"; 55 private static final String APP_SPECIFIC_SMS_RECEIVED_ACTION = 56 "com.android.cts.permission.sms.APP_SPECIFIC_SMS_RECEIVED"; 57 58 59 private static final String LOG_TAG = "NoReceiveSmsPermissionTest"; 60 61 // List of carrier-id that does not support loop back messages 62 // This is copied from 63 // cts/tests/tests/telephony/current/src/android/telephony/cts/CarrierCapability.java 64 public static final List<Integer> UNSUPPORT_LOOP_BACK_MESSAGES = 65 Arrays.asList( 66 1 // "T-Mobile - US" 67 ); 68 69 private Semaphore mSemaphore = new Semaphore(0); 70 71 /** 72 * Verify that SmsManager.sendTextMessage requires permissions. 73 * <p>Tests Permission: 74 * {@link android.Manifest.permission#SEND_SMS}. 75 * 76 * Note: this test requires that the device under test reports a valid phone number 77 */ testReceiveTextMessage()78 public void testReceiveTextMessage() { 79 PackageManager packageManager = mContext.getPackageManager(); 80 if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { 81 return; 82 } 83 84 // register our test receiver to receive SMSs. This won't throw a SecurityException, 85 // so test needs to wait to determine if it actual receives an SMS 86 // admittedly, this is a weak verification 87 // this test should be used in conjunction with a test that verifies an SMS can be 88 // received successfully using the same logic if all permissions are in place 89 IllegalSmsReceiver receiver = new IllegalSmsReceiver(); 90 IntentFilter filter = new IntentFilter(); 91 filter.addAction(TELEPHONY_SMS_RECEIVED); 92 filter.addAction(MESSAGE_SENT_ACTION); 93 filter.addAction(MESSAGE_STATUS_RECEIVED_ACTION); 94 95 getContext().registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED); 96 sendSMSToSelf("test"); 97 98 waitForForEvents(mSemaphore, 1); 99 assertTrue("[RERUN] Sms not sent successfully. Check signal.", 100 receiver.isMessageSent()); 101 assertFalse("Sms received without proper permissions", receiver.isSmsReceived()); 102 } 103 104 /** 105 * Verify that without {@link android.Manifest.permission#RECEIVE_SMS} that an SMS sent 106 * containing a nonce from {@link SmsManager#createAppSpecificSmsToken} is delivered 107 * to the app. 108 */ testAppSpecificSmsToken()109 public void testAppSpecificSmsToken() { 110 PackageManager packageManager = mContext.getPackageManager(); 111 if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { 112 return; 113 } 114 115 int carrierId = getContext().getSystemService(TelephonyManager.class).getSimCarrierId(); 116 assertFalse("[RERUN] Carrier [carrier-id: " + carrierId + "] does not support " 117 + "loop back messages. Use another carrier.", 118 UNSUPPORT_LOOP_BACK_MESSAGES.contains(carrierId)); 119 120 AppSpecificSmsReceiver receiver = new AppSpecificSmsReceiver(); 121 IntentFilter filter = new IntentFilter(); 122 filter.addAction(TELEPHONY_SMS_RECEIVED); 123 filter.addAction(MESSAGE_SENT_ACTION); 124 filter.addAction(MESSAGE_STATUS_RECEIVED_ACTION); 125 filter.addAction(APP_SPECIFIC_SMS_RECEIVED_ACTION); 126 getContext().registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED); 127 128 PendingIntent receivedIntent = PendingIntent.getBroadcast(getContext(), 0, 129 new Intent(APP_SPECIFIC_SMS_RECEIVED_ACTION) 130 .setPackage(getContext().getPackageName()), 131 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE); 132 133 String token = SmsManager.getDefault().createAppSpecificSmsToken(receivedIntent); 134 String message = "test message, token=" + token; 135 sendSMSToSelf(message); 136 137 waitForForEvents(mSemaphore, 1); 138 assertTrue("[RERUN] Sms not sent successfully. Check signal.", 139 receiver.isMessageSent()); 140 assertFalse("Sms received without proper permissions", receiver.isSmsReceived()); 141 waitForForEvents(mSemaphore, 1); 142 assertTrue("App specific SMS intent not triggered", receiver.isAppSpecificSmsReceived()); 143 } 144 waitForForEvents(Semaphore semaphore, int expectedNumberOfEvents)145 private boolean waitForForEvents(Semaphore semaphore, int expectedNumberOfEvents) { 146 for (int i = 0; i < expectedNumberOfEvents; i++) { 147 try { 148 if (!semaphore.tryAcquire(SMS_DELIVERED_WAIT_TIME_MILLIS, TimeUnit.MILLISECONDS)) { 149 return false; 150 } 151 } catch (Exception ex) { 152 return false; 153 } 154 } 155 return true; 156 } 157 sendSMSToSelf(String message)158 private void sendSMSToSelf(String message) { 159 PendingIntent sentIntent = PendingIntent.getBroadcast(getContext(), 0, 160 new Intent(MESSAGE_SENT_ACTION).setPackage(getContext().getPackageName()), 161 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE); 162 PendingIntent deliveryIntent = PendingIntent.getBroadcast(getContext(), 0, 163 new Intent(MESSAGE_STATUS_RECEIVED_ACTION) 164 .setPackage(getContext().getPackageName()), 165 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE); 166 167 SubscriptionManager subscription = (SubscriptionManager) 168 getContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); 169 int subscriptionId = subscription.getActiveDataSubscriptionId(); 170 171 assertFalse("[RERUN] No active telephony subscription. Check there is one enabled.", 172 subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID); 173 174 // get current phone number 175 String currentNumber = subscription.getPhoneNumber(subscriptionId); 176 177 // fallback to getActiveSubscriptionInfo if number is empty 178 if (TextUtils.isEmpty(currentNumber)) { 179 SubscriptionInfo subInfo = subscription.getActiveSubscriptionInfo(subscriptionId); 180 181 assertTrue("[RERUN] No info for the active telephony subscription.", 182 subInfo != null); 183 currentNumber = subInfo.getNumber(); 184 } 185 assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.", 186 TextUtils.isEmpty(currentNumber)); 187 188 Log.i(LOG_TAG, String.format("Sending SMS to self: %s", currentNumber)); 189 sendSms(currentNumber, message, sentIntent, deliveryIntent); 190 } 191 sendSms(String currentNumber, String text, PendingIntent sentIntent, PendingIntent deliveryIntent)192 protected void sendSms(String currentNumber, String text, PendingIntent sentIntent, 193 PendingIntent deliveryIntent) { 194 SmsManager.getDefault().sendTextMessage(currentNumber, null, text, sentIntent, 195 deliveryIntent); 196 } 197 198 /** 199 * A receiver that tracks if message was sent and received 200 */ 201 public class IllegalSmsReceiver extends BroadcastReceiver { 202 203 private boolean mIsSmsReceived = false; 204 private boolean mIsMessageSent = false; 205 onReceive(Context context, Intent intent)206 public void onReceive(Context context, Intent intent) { 207 if (TELEPHONY_SMS_RECEIVED.equals(intent.getAction())) { 208 // this is bad, received sms without having SMS permission 209 setSmsReceived(); 210 } else if (MESSAGE_STATUS_RECEIVED_ACTION.equals(intent.getAction())) { 211 handleResultCode(getResultCode(), "delivery"); 212 } else if (MESSAGE_SENT_ACTION.equals(intent.getAction())) { 213 handleResultCode(getResultCode(), "sent"); 214 } else { 215 Log.w(LOG_TAG, String.format("unknown intent received: %s", intent.getAction())); 216 } 217 218 } 219 isSmsReceived()220 public boolean isSmsReceived() { 221 return mIsSmsReceived; 222 } 223 setSmsReceived()224 private synchronized void setSmsReceived() { 225 mIsSmsReceived = true; 226 notify(); 227 } 228 isMessageSent()229 public boolean isMessageSent() { 230 return mIsMessageSent; 231 } 232 handleResultCode(int resultCode, String action)233 private void handleResultCode(int resultCode, String action) { 234 if (resultCode == Activity.RESULT_OK) { 235 Log.i(LOG_TAG, String.format("message %1$s successful", action)); 236 setMessageSentSuccess(); 237 } else { 238 setMessageSentFailure(); 239 String reason = getErrorReason(resultCode); 240 Log.e(LOG_TAG, String.format("message %1$s failed: %2$s", action, reason)); 241 } 242 } 243 setMessageSentSuccess()244 private synchronized void setMessageSentSuccess() { 245 mIsMessageSent = true; 246 // set this to true, but don't notify receiver since we don't know if message received 247 // yet 248 } 249 setMessageSentFailure()250 private synchronized void setMessageSentFailure() { 251 mIsMessageSent = false; 252 // test environment failure, notify observer so it can stop listening 253 // TODO: should test retry? 254 notify(); 255 } 256 getErrorReason(int resultCode)257 private String getErrorReason(int resultCode) { 258 switch (resultCode) { 259 case SmsManager.RESULT_ERROR_GENERIC_FAILURE: 260 return "generic failure"; 261 case SmsManager.RESULT_ERROR_NO_SERVICE: 262 return "no service"; 263 case SmsManager.RESULT_ERROR_NULL_PDU: 264 return "null pdu"; 265 case SmsManager.RESULT_ERROR_RADIO_OFF: 266 return "Radio off"; 267 } 268 return "unknown"; 269 } 270 } 271 272 public class AppSpecificSmsReceiver extends IllegalSmsReceiver { 273 private boolean mAppSpecificSmsReceived = false; 274 275 @Override onReceive(Context context, Intent intent)276 public void onReceive(Context context, Intent intent) { 277 if (APP_SPECIFIC_SMS_RECEIVED_ACTION.equals(intent.getAction())) { 278 mAppSpecificSmsReceived = true; 279 } else { 280 super.onReceive(context, intent); 281 } 282 try { 283 mSemaphore.release(); 284 } catch (Exception ex) { 285 Log.e(LOG_TAG, "mSemaphore: Got exception in releasing semaphore, ex=" + ex); 286 } 287 } 288 isAppSpecificSmsReceived()289 public boolean isAppSpecificSmsReceived() { 290 return mAppSpecificSmsReceived; 291 } 292 } 293 } 294