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