• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.shadows;
2 
3 import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
4 import static android.os.Build.VERSION_CODES.R;
5 import static android.os.Build.VERSION_CODES.S;
6 
7 import android.app.PendingIntent;
8 import android.content.Context;
9 import android.net.Uri;
10 import android.os.Bundle;
11 import android.telephony.SmsManager;
12 import android.text.TextUtils;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.Map;
16 import javax.annotation.Nullable;
17 import org.robolectric.RuntimeEnvironment;
18 import org.robolectric.annotation.Implementation;
19 import org.robolectric.annotation.Implements;
20 import org.robolectric.annotation.Resetter;
21 import org.robolectric.util.ReflectionHelpers;
22 
23 @Implements(value = SmsManager.class)
24 public class ShadowSmsManager {
25 
26   private String smscAddress;
27   private boolean hasSmscAddressPermission = true;
28   private static int defaultSmsSubscriptionId = -1;
29 
30   @Resetter
reset()31   public static void reset() {
32     if (RuntimeEnvironment.getApiLevel() >= LOLLIPOP_MR1) {
33       Map<String, Object> sSubInstances =
34           ReflectionHelpers.getStaticField(SmsManager.class, "sSubInstances");
35       sSubInstances.clear();
36       defaultSmsSubscriptionId = -1;
37     }
38   }
39 
40   // SMS functionality
41 
42   protected TextSmsParams lastTextSmsParams;
43   protected TextMultipartParams lastTextMultipartParams;
44   protected DataMessageParams lastDataParams;
45 
46   @Implementation
sendDataMessage( String destinationAddress, String scAddress, short destinationPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent)47   protected void sendDataMessage(
48       String destinationAddress,
49       String scAddress,
50       short destinationPort,
51       byte[] data,
52       PendingIntent sentIntent,
53       PendingIntent deliveryIntent) {
54     if (TextUtils.isEmpty(destinationAddress)) {
55       throw new IllegalArgumentException("Invalid destinationAddress");
56     }
57 
58     lastDataParams =
59         new DataMessageParams(
60             destinationAddress, scAddress, destinationPort, data, sentIntent, deliveryIntent);
61   }
62 
63   @Implementation
sendTextMessage( String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent)64   protected void sendTextMessage(
65       String destinationAddress,
66       String scAddress,
67       String text,
68       PendingIntent sentIntent,
69       PendingIntent deliveryIntent) {
70     if (TextUtils.isEmpty(destinationAddress)) {
71       throw new IllegalArgumentException("Invalid destinationAddress");
72     }
73 
74     if (TextUtils.isEmpty(text)) {
75       throw new IllegalArgumentException("Invalid message body");
76     }
77 
78     lastTextSmsParams =
79         new TextSmsParams(destinationAddress, scAddress, text, sentIntent, deliveryIntent);
80   }
81 
82   @Implementation(minSdk = R)
sendTextMessage( String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, long messageId)83   protected void sendTextMessage(
84       String destinationAddress,
85       String scAddress,
86       String text,
87       PendingIntent sentIntent,
88       PendingIntent deliveryIntent,
89       long messageId) {
90     if (TextUtils.isEmpty(destinationAddress)) {
91       throw new IllegalArgumentException("Invalid destinationAddress");
92     }
93 
94     if (TextUtils.isEmpty(text)) {
95       throw new IllegalArgumentException("Invalid message body");
96     }
97 
98     lastTextSmsParams =
99         new TextSmsParams(
100             destinationAddress, scAddress, text, sentIntent, deliveryIntent, messageId);
101   }
102 
103   @Implementation
sendMultipartTextMessage( String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents)104   protected void sendMultipartTextMessage(
105       String destinationAddress,
106       String scAddress,
107       ArrayList<String> parts,
108       ArrayList<PendingIntent> sentIntents,
109       ArrayList<PendingIntent> deliveryIntents) {
110     if (TextUtils.isEmpty(destinationAddress)) {
111       throw new IllegalArgumentException("Invalid destinationAddress");
112     }
113 
114     if (parts == null) {
115       throw new IllegalArgumentException("Invalid message parts");
116     }
117 
118     lastTextMultipartParams =
119         new TextMultipartParams(destinationAddress, scAddress, parts, sentIntents, deliveryIntents);
120   }
121 
122   /**
123    * @return Parameters for last call to {@link #sendDataMessage}.
124    */
getLastSentDataMessageParams()125   public DataMessageParams getLastSentDataMessageParams() {
126     return lastDataParams;
127   }
128 
129   /** Clear last recorded parameters for {@link #sendDataMessage}. */
clearLastSentDataMessageParams()130   public void clearLastSentDataMessageParams() {
131     lastDataParams = null;
132   }
133 
134   /**
135    * @return Parameters for last call to {@link #sendTextMessage}.
136    */
getLastSentTextMessageParams()137   public TextSmsParams getLastSentTextMessageParams() {
138     return lastTextSmsParams;
139   }
140 
141   /** Clear last recorded parameters for {@link #sendTextMessage}. */
clearLastSentTextMessageParams()142   public void clearLastSentTextMessageParams() {
143     lastTextSmsParams = null;
144   }
145 
146   /**
147    * @return Parameters for last call to {@link #sendMultipartTextMessage}.
148    */
getLastSentMultipartTextMessageParams()149   public TextMultipartParams getLastSentMultipartTextMessageParams() {
150     return lastTextMultipartParams;
151   }
152 
153   /** Clear last recorded parameters for {@link #sendMultipartTextMessage}. */
clearLastSentMultipartTextMessageParams()154   public void clearLastSentMultipartTextMessageParams() {
155     lastTextMultipartParams = null;
156   }
157 
158   public static class DataMessageParams {
159     private final String destinationAddress;
160     private final String scAddress;
161     private final short destinationPort;
162     private final byte[] data;
163     private final PendingIntent sentIntent;
164     private final PendingIntent deliveryIntent;
165 
DataMessageParams( String destinationAddress, String scAddress, short destinationPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent)166     public DataMessageParams(
167         String destinationAddress,
168         String scAddress,
169         short destinationPort,
170         byte[] data,
171         PendingIntent sentIntent,
172         PendingIntent deliveryIntent) {
173       this.destinationAddress = destinationAddress;
174       this.scAddress = scAddress;
175       this.destinationPort = destinationPort;
176       this.data = data;
177       this.sentIntent = sentIntent;
178       this.deliveryIntent = deliveryIntent;
179     }
180 
getDestinationAddress()181     public String getDestinationAddress() {
182       return destinationAddress;
183     }
184 
getScAddress()185     public String getScAddress() {
186       return scAddress;
187     }
188 
getDestinationPort()189     public short getDestinationPort() {
190       return destinationPort;
191     }
192 
getData()193     public byte[] getData() {
194       return data;
195     }
196 
getSentIntent()197     public PendingIntent getSentIntent() {
198       return sentIntent;
199     }
200 
getDeliveryIntent()201     public PendingIntent getDeliveryIntent() {
202       return deliveryIntent;
203     }
204   }
205 
206   public static class TextSmsParams {
207     private final String destinationAddress;
208     private final String scAddress;
209     private final String text;
210     private final PendingIntent sentIntent;
211     private final PendingIntent deliveryIntent;
212     private final long messageId;
213 
TextSmsParams( String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent)214     public TextSmsParams(
215         String destinationAddress,
216         String scAddress,
217         String text,
218         PendingIntent sentIntent,
219         PendingIntent deliveryIntent) {
220       this(destinationAddress, scAddress, text, sentIntent, deliveryIntent, 0L);
221     }
222 
TextSmsParams( String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, long messageId)223     public TextSmsParams(
224         String destinationAddress,
225         String scAddress,
226         String text,
227         PendingIntent sentIntent,
228         PendingIntent deliveryIntent,
229         long messageId) {
230       this.destinationAddress = destinationAddress;
231       this.scAddress = scAddress;
232       this.text = text;
233       this.sentIntent = sentIntent;
234       this.deliveryIntent = deliveryIntent;
235       this.messageId = messageId;
236     }
237 
getDestinationAddress()238     public String getDestinationAddress() {
239       return destinationAddress;
240     }
241 
getScAddress()242     public String getScAddress() {
243       return scAddress;
244     }
245 
getText()246     public String getText() {
247       return text;
248     }
249 
getSentIntent()250     public PendingIntent getSentIntent() {
251       return sentIntent;
252     }
253 
getDeliveryIntent()254     public PendingIntent getDeliveryIntent() {
255       return deliveryIntent;
256     }
257 
getMessageId()258     public long getMessageId() {
259       return messageId;
260     }
261   }
262 
263   public static class TextMultipartParams {
264     private final String destinationAddress;
265     private final String scAddress;
266     private final List<String> parts;
267     private final List<PendingIntent> sentIntents;
268     private final List<PendingIntent> deliveryIntents;
269     private final long messageId;
270 
TextMultipartParams( String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents)271     public TextMultipartParams(
272         String destinationAddress,
273         String scAddress,
274         ArrayList<String> parts,
275         ArrayList<PendingIntent> sentIntents,
276         ArrayList<PendingIntent> deliveryIntents) {
277       this(destinationAddress, scAddress, parts, sentIntents, deliveryIntents, 0L);
278     }
279 
TextMultipartParams( String destinationAddress, String scAddress, List<String> parts, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents, long messageId)280     public TextMultipartParams(
281         String destinationAddress,
282         String scAddress,
283         List<String> parts,
284         List<PendingIntent> sentIntents,
285         List<PendingIntent> deliveryIntents,
286         long messageId) {
287       this.destinationAddress = destinationAddress;
288       this.scAddress = scAddress;
289       this.parts = parts;
290       this.sentIntents = sentIntents;
291       this.deliveryIntents = deliveryIntents;
292       this.messageId = messageId;
293     }
294 
getDestinationAddress()295     public String getDestinationAddress() {
296       return destinationAddress;
297     }
298 
getScAddress()299     public String getScAddress() {
300       return scAddress;
301     }
302 
getParts()303     public List<String> getParts() {
304       return parts;
305     }
306 
getSentIntents()307     public List<android.app.PendingIntent> getSentIntents() {
308       return sentIntents;
309     }
310 
getDeliveryIntents()311     public List<android.app.PendingIntent> getDeliveryIntents() {
312       return deliveryIntents;
313     }
314 
getMessageId()315     public long getMessageId() {
316       return messageId;
317     }
318   }
319 
320   // MMS functionality
321 
322   protected SendMultimediaMessageParams lastSentMultimediaMessageParams;
323   protected DownloadMultimediaMessageParams lastDownloadedMultimediaMessageParams;
324 
325   @Implementation
sendMultimediaMessage( Context context, Uri contentUri, @Nullable String locationUrl, @Nullable Bundle configOverrides, @Nullable PendingIntent sentIntent)326   protected void sendMultimediaMessage(
327       Context context,
328       Uri contentUri,
329       @Nullable String locationUrl,
330       @Nullable Bundle configOverrides,
331       @Nullable PendingIntent sentIntent) {
332     if (contentUri == null || TextUtils.isEmpty(contentUri.getHost())) {
333       throw new IllegalArgumentException("Invalid contentUri");
334     }
335 
336     lastSentMultimediaMessageParams =
337         new SendMultimediaMessageParams(contentUri, locationUrl, configOverrides, sentIntent, 0L);
338   }
339 
340   @Implementation(minSdk = S)
sendMultimediaMessage( Context context, Uri contentUri, @Nullable String locationUrl, @Nullable Bundle configOverrides, @Nullable PendingIntent sentIntent, long messageId)341   protected void sendMultimediaMessage(
342       Context context,
343       Uri contentUri,
344       @Nullable String locationUrl,
345       @Nullable Bundle configOverrides,
346       @Nullable PendingIntent sentIntent,
347       long messageId) {
348     if (contentUri == null || TextUtils.isEmpty(contentUri.getHost())) {
349       throw new IllegalArgumentException("Invalid contentUri");
350     }
351 
352     lastSentMultimediaMessageParams =
353         new SendMultimediaMessageParams(
354             contentUri, locationUrl, configOverrides, sentIntent, messageId);
355   }
356 
357   @Implementation
downloadMultimediaMessage( Context context, String locationUrl, Uri contentUri, @Nullable Bundle configOverrides, @Nullable PendingIntent sentIntent)358   protected void downloadMultimediaMessage(
359       Context context,
360       String locationUrl,
361       Uri contentUri,
362       @Nullable Bundle configOverrides,
363       @Nullable PendingIntent sentIntent) {
364     if (contentUri == null || TextUtils.isEmpty(contentUri.getHost())) {
365       throw new IllegalArgumentException("Invalid contentUri");
366     }
367 
368     if (TextUtils.isEmpty(locationUrl)) {
369       throw new IllegalArgumentException("Invalid locationUrl");
370     }
371 
372     lastDownloadedMultimediaMessageParams =
373         new DownloadMultimediaMessageParams(
374             contentUri, locationUrl, configOverrides, sentIntent, 0L);
375   }
376 
377   @Implementation(minSdk = S)
downloadMultimediaMessage( Context context, String locationUrl, Uri contentUri, @Nullable Bundle configOverrides, @Nullable PendingIntent sentIntent, long messageId)378   protected void downloadMultimediaMessage(
379       Context context,
380       String locationUrl,
381       Uri contentUri,
382       @Nullable Bundle configOverrides,
383       @Nullable PendingIntent sentIntent,
384       long messageId) {
385     if (contentUri == null || TextUtils.isEmpty(contentUri.getHost())) {
386       throw new IllegalArgumentException("Invalid contentUri");
387     }
388 
389     if (TextUtils.isEmpty(locationUrl)) {
390       throw new IllegalArgumentException("Invalid locationUrl");
391     }
392 
393     lastDownloadedMultimediaMessageParams =
394         new org.robolectric.shadows.ShadowSmsManager.DownloadMultimediaMessageParams(
395             contentUri, locationUrl, configOverrides, sentIntent, messageId);
396   }
397 
398   /**
399    * @return Parameters for last call to {@link #sendMultimediaMessage}.
400    */
getLastSentMultimediaMessageParams()401   public SendMultimediaMessageParams getLastSentMultimediaMessageParams() {
402     return lastSentMultimediaMessageParams;
403   }
404 
405   /** Clear last recorded parameters for {@link #sendMultimediaMessage}. */
clearLastSentMultimediaMessageParams()406   public void clearLastSentMultimediaMessageParams() {
407     lastSentMultimediaMessageParams = null;
408   }
409 
410   /**
411    * @return Parameters for last call to {@link #downloadMultimediaMessage}.
412    */
getLastDownloadedMultimediaMessageParams()413   public DownloadMultimediaMessageParams getLastDownloadedMultimediaMessageParams() {
414     return lastDownloadedMultimediaMessageParams;
415   }
416 
417   /** Clear last recorded parameters for {@link #downloadMultimediaMessage}. */
clearLastDownloadedMultimediaMessageParams()418   public void clearLastDownloadedMultimediaMessageParams() {
419     lastDownloadedMultimediaMessageParams = null;
420   }
421 
422   @Implementation(minSdk = R)
sendMultipartTextMessage( String destinationAddress, String scAddress, List<String> parts, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents, long messageId)423   protected void sendMultipartTextMessage(
424       String destinationAddress,
425       String scAddress,
426       List<String> parts,
427       List<PendingIntent> sentIntents,
428       List<PendingIntent> deliveryIntents,
429       long messageId) {
430     if (TextUtils.isEmpty(destinationAddress)) {
431       throw new IllegalArgumentException("Invalid destinationAddress");
432     }
433 
434     if (parts == null) {
435       throw new IllegalArgumentException("Invalid message parts");
436     }
437 
438     lastTextMultipartParams =
439         new TextMultipartParams(
440             destinationAddress, scAddress, parts, sentIntents, deliveryIntents, messageId);
441   }
442 
443   /**
444    * Base class for testable parameters from calls to either {@link #downloadMultimediaMessage} or
445    * {@link #downloadMultimediaMessage}.
446    */
447   public abstract static class MultimediaMessageParams {
448     private final Uri contentUri;
449     protected final String locationUrl;
450     @Nullable private final Bundle configOverrides;
451     @Nullable protected final PendingIntent pendingIntent;
452     protected final long messageId;
453 
MultimediaMessageParams( Uri contentUri, String locationUrl, @Nullable Bundle configOverrides, @Nullable PendingIntent pendingIntent, long messageId)454     protected MultimediaMessageParams(
455         Uri contentUri,
456         String locationUrl,
457         @Nullable Bundle configOverrides,
458         @Nullable PendingIntent pendingIntent,
459         long messageId) {
460       this.contentUri = contentUri;
461       this.locationUrl = locationUrl;
462       this.configOverrides = configOverrides;
463       this.pendingIntent = pendingIntent;
464       this.messageId = messageId;
465     }
466 
getContentUri()467     public Uri getContentUri() {
468       return contentUri;
469     }
470 
471     @Nullable
getConfigOverrides()472     public Bundle getConfigOverrides() {
473       return configOverrides;
474     }
475 
getMessageId()476     public long getMessageId() {
477       return messageId;
478     }
479   }
480 
481   /** Testable parameters from calls to {@link #sendMultimediaMessage}. */
482   public static final class SendMultimediaMessageParams extends MultimediaMessageParams {
SendMultimediaMessageParams( Uri contentUri, @Nullable String locationUrl, @Nullable Bundle configOverrides, @Nullable PendingIntent pendingIntent, long messageId)483     public SendMultimediaMessageParams(
484         Uri contentUri,
485         @Nullable String locationUrl,
486         @Nullable Bundle configOverrides,
487         @Nullable PendingIntent pendingIntent,
488         long messageId) {
489       super(contentUri, locationUrl, configOverrides, pendingIntent, messageId);
490     }
491 
492     @Nullable
getLocationUrl()493     public String getLocationUrl() {
494       return locationUrl;
495     }
496 
497     @Nullable
getSentIntent()498     public PendingIntent getSentIntent() {
499       return pendingIntent;
500     }
501   }
502 
503   /** Testable parameters from calls to {@link #downloadMultimediaMessage}. */
504   public static final class DownloadMultimediaMessageParams extends MultimediaMessageParams {
DownloadMultimediaMessageParams( Uri contentUri, String locationUrl, @Nullable Bundle configOverrides, @Nullable PendingIntent pendingIntent, long messageId)505     public DownloadMultimediaMessageParams(
506         Uri contentUri,
507         String locationUrl,
508         @Nullable Bundle configOverrides,
509         @Nullable PendingIntent pendingIntent,
510         long messageId) {
511       super(contentUri, locationUrl, configOverrides, pendingIntent, messageId);
512     }
513 
getLocationUrl()514     public String getLocationUrl() {
515       return locationUrl;
516     }
517 
518     @Nullable
getDownloadedIntent()519     public PendingIntent getDownloadedIntent() {
520       return pendingIntent;
521     }
522   }
523 
524   /**
525    * Sets a boolean value to simulate whether or not the required permissions to call {@link
526    * #getSmscAddress()} have been granted.
527    */
setSmscAddressPermission(boolean smscAddressPermission)528   public void setSmscAddressPermission(boolean smscAddressPermission) {
529     this.hasSmscAddressPermission = smscAddressPermission;
530   }
531 
532   /**
533    * Returns {@code null} by default or the value specified via {@link #setSmscAddress(String)}.
534    * Required permission is set by {@link #setSmscAddressPermission(boolean)}.
535    */
536   @Implementation(minSdk = R)
getSmscAddress()537   protected String getSmscAddress() {
538     if (!hasSmscAddressPermission) {
539       throw new SecurityException();
540     }
541     return smscAddress;
542   }
543 
544   /** Sets the value to be returned by {@link #getDefaultSmsSubscriptionId()}. */
setDefaultSmsSubscriptionId(int id)545   public static void setDefaultSmsSubscriptionId(int id) {
546     defaultSmsSubscriptionId = id;
547   }
548 
549   /**
550    * Returns {@code -1} by default or the value specified in {@link
551    * #setDefaultSmsSubscriptionId(int)}.
552    */
553   @Implementation(minSdk = R)
getDefaultSmsSubscriptionId()554   protected static int getDefaultSmsSubscriptionId() {
555     return defaultSmsSubscriptionId;
556   }
557 
558   /** Sets the value returned by {@link SmsManager#getSmscAddress()}. */
setSmscAddress(String smscAddress)559   public void setSmscAddress(String smscAddress) {
560     this.smscAddress = smscAddress;
561   }
562 }
563