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 
17 package android.service.carrier;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.SdkConstant;
22 import android.app.Service;
23 import android.content.Intent;
24 import android.net.Uri;
25 import android.os.IBinder;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.os.RemoteException;
29 
30 import java.util.ArrayList;
31 import java.util.List;
32 
33 /**
34  * A service that receives calls from the system when new SMS and MMS are
35  * sent or received.
36  * <p>To extend this class, you must declare the service in your manifest file with
37  * the {@link android.Manifest.permission#BIND_CARRIER_MESSAGING_SERVICE} permission
38  * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
39  * <pre>
40  * &lt;service android:name=".MyMessagingService"
41  *          android:label="&#64;string/service_name"
42  *          android:permission="android.permission.BIND_CARRIER_MESSAGING_SERVICE">
43  *     &lt;intent-filter>
44  *         &lt;action android:name="android.service.carrier.CarrierMessagingService" />
45  *     &lt;/intent-filter>
46  * &lt;/service></pre>
47  */
48 public abstract class CarrierMessagingService extends Service {
49     /**
50      * The {@link android.content.Intent} that must be declared as handled by the service.
51      */
52     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
53     public static final String SERVICE_INTERFACE
54             = "android.service.carrier.CarrierMessagingService";
55 
56     /**
57      * Indicates that an SMS or MMS message was successfully sent.
58      */
59     public static final int SEND_STATUS_OK = 0;
60 
61     /**
62      * SMS/MMS sending failed. We should retry via the carrier network.
63      */
64     public static final int SEND_STATUS_RETRY_ON_CARRIER_NETWORK = 1;
65 
66     /**
67      * SMS/MMS sending failed. We should not retry via the carrier network.
68      */
69     public static final int SEND_STATUS_ERROR = 2;
70 
71     /**
72      * Successfully downloaded an MMS message.
73      */
74     public static final int DOWNLOAD_STATUS_OK = 0;
75 
76     /**
77      * MMS downloading failed. We should retry via the carrier network.
78      */
79     public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1;
80 
81     /**
82      * MMS downloading failed. We should not retry via the carrier network.
83      */
84     public static final int DOWNLOAD_STATUS_ERROR = 2;
85 
86     private final ICarrierMessagingWrapper mWrapper = new ICarrierMessagingWrapper();
87 
88     /**
89      * Override this method to filter inbound SMS messages.
90      *
91      * @param pdu the PDUs of the message
92      * @param format the format of the PDUs, typically "3gpp" or "3gpp2"
93      * @param destPort the destination port of a binary SMS, this will be -1 for text SMS
94      * @param subId SMS subscription ID of the SIM
95      * @param callback result callback. Call with {@code true} to keep an inbound SMS message and
96      *        deliver to SMS apps, and {@code false} to drop the message.
97      */
onFilterSms(@onNull MessagePdu pdu, @NonNull String format, int destPort, int subId, @NonNull ResultCallback<Boolean> callback)98     public void onFilterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort,
99             int subId, @NonNull ResultCallback<Boolean> callback) {
100         // optional
101         try {
102             callback.onReceiveResult(true);
103         } catch (RemoteException ex) {
104         }
105     }
106 
107     /**
108      * Override this method to intercept text SMSs sent from the device.
109      *
110      * @param text the text to send
111      * @param subId SMS subscription ID of the SIM
112      * @param destAddress phone number of the recipient of the message
113      * @param callback result callback. Call with a {@link SendSmsResult}.
114      */
onSendTextSms( @onNull String text, int subId, @NonNull String destAddress, @NonNull ResultCallback<SendSmsResult> callback)115     public void onSendTextSms(
116             @NonNull String text, int subId, @NonNull String destAddress,
117             @NonNull ResultCallback<SendSmsResult> callback) {
118         // optional
119         try {
120             callback.onReceiveResult(new SendSmsResult(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, 0));
121         } catch (RemoteException ex) {
122         }
123     }
124 
125     /**
126      * Override this method to intercept binary SMSs sent from the device.
127      *
128      * @param data the binary content
129      * @param subId SMS subscription ID of the SIM
130      * @param destAddress phone number of the recipient of the message
131      * @param destPort the destination port
132      * @param callback result callback. Call with a {@link SendSmsResult}.
133      */
onSendDataSms(@onNull byte[] data, int subId, @NonNull String destAddress, int destPort, @NonNull ResultCallback<SendSmsResult> callback)134     public void onSendDataSms(@NonNull byte[] data, int subId,
135             @NonNull String destAddress, int destPort,
136             @NonNull ResultCallback<SendSmsResult> callback) {
137         // optional
138         try {
139             callback.onReceiveResult(new SendSmsResult(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, 0));
140         } catch (RemoteException ex) {
141         }
142     }
143 
144     /**
145      * Override this method to intercept long SMSs sent from the device.
146      *
147      * @param parts a {@link List} of the message parts
148      * @param subId SMS subscription ID of the SIM
149      * @param destAddress phone number of the recipient of the message
150      * @param callback result callback. Call with a {@link SendMultipartSmsResult}.
151      */
onSendMultipartTextSms(@onNull List<String> parts, int subId, @NonNull String destAddress, @NonNull ResultCallback<SendMultipartSmsResult> callback)152     public void onSendMultipartTextSms(@NonNull List<String> parts,
153             int subId, @NonNull String destAddress,
154             @NonNull ResultCallback<SendMultipartSmsResult> callback) {
155         // optional
156         try {
157             callback.onReceiveResult(
158                     new SendMultipartSmsResult(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, null));
159         } catch (RemoteException ex) {
160         }
161     }
162 
163     /**
164      * Override this method to intercept MMSs sent from the device.
165      *
166      * @param pduUri the content provider URI of the PDU to send
167      * @param subId SMS subscription ID of the SIM
168      * @param location the optional URI to send this MMS PDU. If this is {code null},
169      *        the PDU should be sent to the default MMSC URL.
170      * @param callback result callback. Call with a {@link SendMmsResult}.
171      */
onSendMms(@onNull Uri pduUri, int subId, @Nullable Uri location, @NonNull ResultCallback<SendMmsResult> callback)172     public void onSendMms(@NonNull Uri pduUri, int subId,
173             @Nullable Uri location, @NonNull ResultCallback<SendMmsResult> callback) {
174         // optional
175         try {
176             callback.onReceiveResult(new SendMmsResult(SEND_STATUS_RETRY_ON_CARRIER_NETWORK, null));
177         } catch (RemoteException ex) {
178         }
179     }
180 
181     /**
182      * Override this method to download MMSs received.
183      *
184      * @param contentUri the content provider URI of the PDU to be downloaded.
185      * @param subId SMS subscription ID of the SIM
186      * @param location the URI of the message to be downloaded.
187      * @param callback result callback. Call with a status code which is one of
188      *        {@link #DOWNLOAD_STATUS_OK},
189      *        {@link #DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK}, or {@link #DOWNLOAD_STATUS_ERROR}.
190      */
onDownloadMms(@onNull Uri contentUri, int subId, @NonNull Uri location, @NonNull ResultCallback<Integer> callback)191     public void onDownloadMms(@NonNull Uri contentUri, int subId, @NonNull Uri location,
192             @NonNull ResultCallback<Integer> callback) {
193         // optional
194         try {
195             callback.onReceiveResult(DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK);
196         } catch (RemoteException ex) {
197         }
198     }
199 
200     @Override
onBind(@onNull Intent intent)201     public @Nullable IBinder onBind(@NonNull Intent intent) {
202         if (!SERVICE_INTERFACE.equals(intent.getAction())) {
203             return null;
204         }
205         return mWrapper;
206     }
207 
208     /**
209      * The result of sending an MMS.
210      */
211     public static final class SendMmsResult {
212         private int mSendStatus;
213         private byte[] mSendConfPdu;
214 
215         /**
216          * Constructs a SendMmsResult with the MMS send result, and the SendConf PDU.
217          *
218          * @param sendStatus send status, one of {@link #SEND_STATUS_OK},
219          *        {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and
220          *        {@link #SEND_STATUS_ERROR}
221          * @param sendConfPdu a possibly {code null} SendConf PDU, which confirms that the message
222          *        was sent. sendConfPdu is ignored if the {@code result} is not
223          *        {@link #SEND_STATUS_OK}.
224          */
SendMmsResult(int sendStatus, @Nullable byte[] sendConfPdu)225         public SendMmsResult(int sendStatus, @Nullable byte[] sendConfPdu) {
226             mSendStatus = sendStatus;
227             mSendConfPdu = sendConfPdu;
228         }
229 
230         /**
231          * Returns the send status of the just-sent MMS.
232          *
233          * @return the send status which is one of {@link #SEND_STATUS_OK},
234          *         {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and {@link #SEND_STATUS_ERROR}
235          */
getSendStatus()236         public int getSendStatus() {
237             return mSendStatus;
238         }
239 
240         /**
241          * Returns the SendConf PDU, which confirms that the message was sent.
242          *
243          * @return the SendConf PDU
244          */
getSendConfPdu()245         public @Nullable byte[] getSendConfPdu() {
246             return mSendConfPdu;
247         }
248     }
249 
250     /**
251      * The result of sending an SMS.
252      */
253     public static final class SendSmsResult {
254         private final int mSendStatus;
255         private final int mMessageRef;
256 
257         /**
258          * Constructs a SendSmsResult with the send status and message reference for the
259          * just-sent SMS.
260          *
261          * @param sendStatus send status, one of {@link #SEND_STATUS_OK},
262          *        {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and {@link #SEND_STATUS_ERROR}.
263          * @param messageRef message reference of the just-sent SMS. This field is applicable only
264          *        if send status is {@link #SEND_STATUS_OK}.
265          */
SendSmsResult(int sendStatus, int messageRef)266         public SendSmsResult(int sendStatus, int messageRef) {
267             mSendStatus = sendStatus;
268             mMessageRef = messageRef;
269         }
270 
271         /**
272          * Returns the message reference of the just-sent SMS.
273          *
274          * @return the message reference
275          */
getMessageRef()276         public int getMessageRef() {
277             return mMessageRef;
278         }
279 
280         /**
281          * Returns the send status of the just-sent SMS.
282          *
283          * @return the send status
284          */
getSendStatus()285         public int getSendStatus() {
286             return mSendStatus;
287         }
288     }
289 
290     /**
291      * The result of sending a multipart SMS.
292      */
293     public static final class SendMultipartSmsResult {
294         private final int mSendStatus;
295         private final int[] mMessageRefs;
296 
297         /**
298          * Constructs a SendMultipartSmsResult with the send status and message references for the
299          * just-sent multipart SMS.
300          *
301          * @param sendStatus send status, one of {@link #SEND_STATUS_OK},
302          *        {@link #SEND_STATUS_RETRY_ON_CARRIER_NETWORK}, and {@link #SEND_STATUS_ERROR}.
303          * @param messageRefs an array of message references, one for each part of the
304          *        multipart SMS. This field is applicable only if send status is
305          *        {@link #SEND_STATUS_OK}.
306          */
SendMultipartSmsResult(int sendStatus, @Nullable int[] messageRefs)307         public SendMultipartSmsResult(int sendStatus, @Nullable int[] messageRefs) {
308             mSendStatus = sendStatus;
309             mMessageRefs = messageRefs;
310         }
311 
312         /**
313          * Returns the message references of the just-sent multipart SMS.
314          *
315          * @return the message references, one for each part of the multipart SMS
316          */
getMessageRefs()317         public @Nullable int[] getMessageRefs() {
318             return mMessageRefs;
319         }
320 
321         /**
322          * Returns the send status of the just-sent SMS.
323          *
324          * @return the send status
325          */
getSendStatus()326         public int getSendStatus() {
327             return mSendStatus;
328         }
329     }
330 
331     /**
332      * A callback interface used to provide results asynchronously.
333      */
334     public interface ResultCallback<T> {
335         /**
336          * Invoked when the result is available.
337          *
338          * @param result the result
339          */
onReceiveResult(@onNull T result)340         public void onReceiveResult(@NonNull T result) throws RemoteException;
341     };
342 
343     /**
344      * A wrapper around ICarrierMessagingService to enable the carrier messaging app to implement
345      * methods it cares about in the {@link ICarrierMessagingService} interface.
346      */
347     private class ICarrierMessagingWrapper extends ICarrierMessagingService.Stub {
348         @Override
filterSms(MessagePdu pdu, String format, int destPort, int subId, final ICarrierMessagingCallback callback)349         public void filterSms(MessagePdu pdu, String format, int destPort,
350                               int subId, final ICarrierMessagingCallback callback) {
351             onFilterSms(pdu, format, destPort, subId, new ResultCallback<Boolean>() {
352                     @Override
353                     public void onReceiveResult(final Boolean result) throws RemoteException {
354                         callback.onFilterComplete(result);
355                     }
356                 });
357         }
358 
359         @Override
sendTextSms(String text, int subId, String destAddress, final ICarrierMessagingCallback callback)360         public void sendTextSms(String text, int subId, String destAddress,
361                                 final ICarrierMessagingCallback callback) {
362             onSendTextSms(text, subId, destAddress, new ResultCallback<SendSmsResult>() {
363                     @Override
364                     public void onReceiveResult(final SendSmsResult result) throws RemoteException {
365                         callback.onSendSmsComplete(result.getSendStatus(), result.getMessageRef());
366                     }
367                 });
368         }
369 
370         @Override
sendDataSms(byte[] data, int subId, String destAddress, int destPort, final ICarrierMessagingCallback callback)371         public void sendDataSms(byte[] data, int subId, String destAddress, int destPort,
372                                 final ICarrierMessagingCallback callback) {
373             onSendDataSms(data, subId, destAddress, destPort, new ResultCallback<SendSmsResult>() {
374                     @Override
375                     public void onReceiveResult(final SendSmsResult result) throws RemoteException {
376                         callback.onSendSmsComplete(result.getSendStatus(), result.getMessageRef());
377                     }
378                 });
379         }
380 
381         @Override
sendMultipartTextSms(List<String> parts, int subId, String destAddress, final ICarrierMessagingCallback callback)382         public void sendMultipartTextSms(List<String> parts, int subId, String destAddress,
383                                          final ICarrierMessagingCallback callback) {
384                 onSendMultipartTextSms(parts, subId, destAddress,
385                         new ResultCallback<SendMultipartSmsResult>() {
386                                 @Override
387                                 public void onReceiveResult(final SendMultipartSmsResult result)
388                                         throws RemoteException {
389                                     callback.onSendMultipartSmsComplete(
390                                             result.getSendStatus(), result.getMessageRefs());
391                                 }
392                             });
393         }
394 
395         @Override
sendMms(Uri pduUri, int subId, Uri location, final ICarrierMessagingCallback callback)396         public void sendMms(Uri pduUri, int subId, Uri location,
397                 final ICarrierMessagingCallback callback) {
398             onSendMms(pduUri, subId, location, new ResultCallback<SendMmsResult>() {
399                     @Override
400                     public void onReceiveResult(final SendMmsResult result) throws RemoteException {
401                         callback.onSendMmsComplete(result.getSendStatus(), result.getSendConfPdu());
402                     }
403                 });
404         }
405 
406         @Override
downloadMms(Uri pduUri, int subId, Uri location, final ICarrierMessagingCallback callback)407         public void downloadMms(Uri pduUri, int subId, Uri location,
408                 final ICarrierMessagingCallback callback) {
409             onDownloadMms(pduUri, subId, location, new ResultCallback<Integer>() {
410                     @Override
411                     public void onReceiveResult(Integer result) throws RemoteException {
412                         callback.onDownloadMmsComplete(result);
413                     }
414                 });
415         }
416     }
417 }
418