1 /*
2  * Copyright (C) 2018 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.telephony.ims.stub;
18 
19 import android.annotation.IntDef;
20 import android.annotation.IntRange;
21 import android.annotation.NonNull;
22 import android.annotation.SystemApi;
23 import android.os.RemoteException;
24 import android.telephony.SmsManager;
25 import android.telephony.SmsMessage;
26 import android.telephony.ims.aidl.IImsSmsListener;
27 import android.util.Log;
28 
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import java.util.concurrent.Executor;
32 
33 /**
34  * Base implementation for SMS over IMS.
35  *
36  * Any service wishing to provide SMS over IMS should extend this class and implement all methods
37  * that the service supports.
38  *
39  * @hide
40  */
41 @SystemApi
42 public class ImsSmsImplBase {
43     private static final String LOG_TAG = "SmsImplBase";
44 
45     /** @hide */
46     @IntDef({
47             SEND_STATUS_OK,
48             SEND_STATUS_ERROR,
49             SEND_STATUS_ERROR_RETRY,
50             SEND_STATUS_ERROR_FALLBACK
51         })
52     @Retention(RetentionPolicy.SOURCE)
53     public @interface SendStatusResult {}
54     /**
55      * Message was sent successfully.
56      */
57     public static final int SEND_STATUS_OK = 1;
58 
59     /**
60      * IMS provider failed to send the message and platform should not retry falling back to sending
61      * the message using the radio.
62      */
63     public static final int SEND_STATUS_ERROR = 2;
64 
65     /**
66      * IMS provider failed to send the message and platform should retry again after setting TP-RD
67      * bit to high.
68      */
69     public static final int SEND_STATUS_ERROR_RETRY = 3;
70 
71     /**
72      * IMS provider failed to send the message and platform should retry falling back to sending
73      * the message using the radio.
74      */
75     public static final int SEND_STATUS_ERROR_FALLBACK = 4;
76 
77     /** @hide */
78     @IntDef({
79             DELIVER_STATUS_OK,
80             DELIVER_STATUS_ERROR_GENERIC,
81             DELIVER_STATUS_ERROR_NO_MEMORY,
82             DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED
83         })
84     @Retention(RetentionPolicy.SOURCE)
85     public @interface DeliverStatusResult {}
86     /**
87      * Message was delivered successfully.
88      */
89     public static final int DELIVER_STATUS_OK = 1;
90 
91     /**
92      * Message was not delivered.
93      */
94     public static final int DELIVER_STATUS_ERROR_GENERIC = 2;
95 
96     /**
97      * Message was not delivered due to lack of memory.
98      */
99     public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3;
100 
101     /**
102      * Message was not delivered as the request is not supported.
103      */
104     public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4;
105 
106     /** @hide */
107     @IntDef({
108             STATUS_REPORT_STATUS_OK,
109             STATUS_REPORT_STATUS_ERROR
110         })
111     @Retention(RetentionPolicy.SOURCE)
112     public @interface StatusReportResult {}
113 
114     /**
115      * Status Report was set successfully.
116      */
117     public static final int STATUS_REPORT_STATUS_OK = 1;
118 
119     /**
120      * Error while setting status report.
121      */
122     public static final int STATUS_REPORT_STATUS_ERROR = 2;
123 
124     /**
125      * No network error was generated while processing the SMS message.
126      */
127     // Should match SmsResponse.NO_ERROR_CODE
128     public static final int RESULT_NO_NETWORK_ERROR = -1;
129 
130     // Lock for feature synchronization
131     private final Object mLock = new Object();
132     private IImsSmsListener mListener;
133     private Executor mExecutor;
134 
135     /**
136      * Create a new ImsSmsImplBase using the Executor set in MmTelFeature
137      */
ImsSmsImplBase()138     public ImsSmsImplBase() {
139     }
140 
141     /**
142      * Create a new ImsSmsImplBase with specified executor.
143      * <p>
144      * @param executor Default executor for ImsSmsImplBase
145      */
ImsSmsImplBase(@onNull Executor executor)146     public ImsSmsImplBase(@NonNull Executor executor) {
147         mExecutor = executor;
148     }
149 
150     /**
151      * Registers a listener responsible for handling tasks like delivering messages.
152      *
153      * @param listener listener to register.
154      *
155      * @hide
156      */
registerSmsListener(IImsSmsListener listener)157     public final void registerSmsListener(IImsSmsListener listener) {
158         synchronized (mLock) {
159             mListener = listener;
160         }
161     }
162 
163     /**
164      * This method will be triggered by the platform when the user attempts to send an SMS. This
165      * method should be implemented by the IMS providers to provide implementation of sending an SMS
166      * over IMS.
167      *
168      * @param token unique token generated by the platform that should be used when triggering
169      *             callbacks for this specific message.
170      * @param messageRef the message reference, which may be 1 byte if it is in
171      *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
172      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
173      * @param format the format of the message.
174      * @param smsc the Short Message Service Center address.
175      * @param isRetry whether it is a retry of an already attempted message or not.
176      * @param pdu PDU representing the contents of the message.
177      */
sendSms(int token, @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format, String smsc, boolean isRetry, byte[] pdu)178     public void sendSms(int token, @IntRange(from = 0, to = 65535) int messageRef,
179             @SmsMessage.Format String format, String smsc, boolean isRetry,
180             byte[] pdu) {
181         // Base implementation returns error. Should be overridden.
182         try {
183             onSendSmsResult(token, messageRef, SEND_STATUS_ERROR,
184                     SmsManager.RESULT_ERROR_GENERIC_FAILURE);
185         } catch (RuntimeException e) {
186             Log.e(LOG_TAG, "Can not send sms: " + e.getMessage());
187         }
188     }
189 
190     /**
191      * This method will be triggered by the platform when memory becomes available to receive SMS
192      * after a memory full event. This method should be implemented by IMS providers to
193      * send RP-SMMA notification from SMS Relay Layer to server over IMS as per section 7.3.2 of
194      * TS 124.11. Once the RP-SMMA Notification is sent to the network. The network will deliver all
195      * the pending messages which failed due to Unavailability of Memory.
196      *
197      * @param token unique token generated in {@link ImsSmsDispatcher#onMemoryAvailable(void)} that
198      *  should be used when triggering callbacks for this specific message.
199      *
200      * @hide
201      */
onMemoryAvailable(int token)202     public void onMemoryAvailable(int token) {
203         // Base Implementation - Should be overridden
204     }
205 
206     /**
207      * This method will be triggered by the platform after
208      * {@link #onSmsReceived(int, String, byte[])} has been called to deliver the result to the IMS
209      * provider.
210      *
211      * If the framework needs to provide the PDU used to acknowledge the SMS,
212      * {@link #acknowledgeSms(int, int, int, byte[])} will be called.
213      *
214      * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
215      * @param messageRef the message reference, which may be 1 byte if it is in
216      *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
217      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
218      * @param result result of delivering the message.
219      */
acknowledgeSms(int token, @IntRange(from = 0, to = 65535) int messageRef, @DeliverStatusResult int result)220     public void acknowledgeSms(int token, @IntRange(from = 0, to = 65535)  int messageRef,
221             @DeliverStatusResult int result) {
222         Log.e(LOG_TAG, "acknowledgeSms() not implemented.");
223     }
224 
225     /**
226      * This method will be called by the platform after
227      * {@link #onSmsReceived(int, String, byte[])} has been called to acknowledge an incoming SMS.
228      *
229      * This method is only called in cases where the framework needs to provide the PDU such as the
230      * case where we provide the Short Message Transfer Layer PDU (see 3GPP TS 23.040). Otherwise,
231      * {@link #acknowledgeSms(int, int, int)} will be used.
232      *
233      * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
234      * @param messageRef the message reference, which may be 1 byte if it is in
235      *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
236      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
237      * @param result result of delivering the message.
238      * @param pdu PDU representing the contents of the message.
239      */
acknowledgeSms(int token, @IntRange(from = 0, to = 65535) int messageRef, @DeliverStatusResult int result, @NonNull byte[] pdu)240     public void acknowledgeSms(int token, @IntRange(from = 0, to = 65535)  int messageRef,
241             @DeliverStatusResult int result, @NonNull byte[] pdu) {
242         Log.e(LOG_TAG, "acknowledgeSms() not implemented. acknowledgeSms(int, int, int) called.");
243         acknowledgeSms(token, messageRef, result);
244     }
245 
246     /**
247      * This method will be triggered by the platform after
248      * {@link #onSmsStatusReportReceived(int, int, String, byte[])} or
249      * {@link #onSmsStatusReportReceived(int, String, byte[])} has been called to provide the
250      * result to the IMS provider.
251      *
252      * @param token token provided in {@link #onSmsStatusReportReceived(int, int, String, byte[])}
253      *              or {@link #onSmsStatusReportReceived(int, String, byte[])}
254      * @param messageRef the message reference, which may be 1 byte if it is in
255      *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
256      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
257      * @param result result of delivering the message.
258      */
acknowledgeSmsReport(int token, @IntRange(from = 0, to = 65535) int messageRef, @StatusReportResult int result)259     public void acknowledgeSmsReport(int token, @IntRange(from = 0, to = 65535) int messageRef,
260             @StatusReportResult int result) {
261         Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented.");
262     }
263 
264     /**
265      * This method should be triggered by the IMS providers when there is an incoming message. The
266      * platform will deliver the message to the messages database and notify the IMS provider of the
267      * result by calling {@link #acknowledgeSms(int, int, int)}.
268      *
269      * This method must not be called before {@link #onReady()} is called or the call will fail. If
270      * the platform is not available, {@link #acknowledgeSms(int, int, int)} will be called with the
271      * {@link #DELIVER_STATUS_ERROR_GENERIC} result code.
272      * @param token unique token generated by IMS providers that the platform will use to trigger
273      *              callbacks for this message.
274      * @param format the format of the message.
275      * @param pdu PDU representing the contents of the message.
276      * @throws RuntimeException if called before {@link #onReady()} is triggered.
277      */
onSmsReceived(int token, @SmsMessage.Format String format, byte[] pdu)278     public final void onSmsReceived(int token, @SmsMessage.Format String format, byte[] pdu)
279             throws RuntimeException {
280         IImsSmsListener listener = null;
281         synchronized (mLock) {
282             listener = mListener;
283         }
284 
285         if (listener == null) {
286             throw new RuntimeException("Feature not ready.");
287         }
288         try {
289             listener.onSmsReceived(token, format, pdu);
290         } catch (RemoteException e) {
291             Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
292             SmsMessage message = SmsMessage.createFromPdu(pdu, format);
293             if (message != null && message.mWrappedSmsMessage != null) {
294                 acknowledgeSms(token, message.mWrappedSmsMessage.mMessageRef,
295                         DELIVER_STATUS_ERROR_GENERIC);
296             } else {
297                 Log.w(LOG_TAG, "onSmsReceived: Invalid pdu entered.");
298                 acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC);
299             }
300         }
301     }
302 
303     /**
304      * This method should be triggered by the IMS providers when an outgoing SMS message has been
305      * sent successfully.
306      *
307      * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
308      * @param messageRef the message reference, which may be 1 byte if it is in
309      *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
310      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
311      *
312      * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
313      * connection to the framework is not available. If this happens attempting to send the SMS
314      * should be aborted.
315      */
onSendSmsResultSuccess(int token, @IntRange(from = 0, to = 65535) int messageRef)316     public final void onSendSmsResultSuccess(int token,
317             @IntRange(from = 0, to = 65535) int messageRef) throws RuntimeException {
318         IImsSmsListener listener = null;
319         synchronized (mLock) {
320             listener = mListener;
321         }
322 
323         if (listener == null) {
324             throw new RuntimeException("Feature not ready.");
325         }
326         try {
327             listener.onSendSmsResult(token, messageRef, SEND_STATUS_OK,
328                     SmsManager.RESULT_ERROR_NONE, RESULT_NO_NETWORK_ERROR);
329         } catch (RemoteException e) {
330             e.rethrowFromSystemServer();
331         }
332     }
333 
334     /**
335      * This method should be triggered by the IMS providers to pass the result of the sent message
336      * to the platform.
337      *
338      * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
339      * @param messageRef the message reference, which may be 1 byte if it is in
340      *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
341      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
342      * @param status result of sending the SMS.
343      * @param reason reason in case status is failure.
344      *
345      * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
346      * connection to the framework is not available. If this happens attempting to send the SMS
347      * should be aborted.
348      * @deprecated Use {@link #onSendSmsResultSuccess(int, int)} or
349      * {@link #onSendSmsResultError(int, int, int, int, int)} to notify the framework of the SMS
350      * send result.
351      */
352     @Deprecated
onSendSmsResult(int token, @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status, @SmsManager.Result int reason)353     public final void onSendSmsResult(int token, @IntRange(from = 0, to = 65535) int messageRef,
354             @SendStatusResult int status, @SmsManager.Result int reason) throws RuntimeException {
355         IImsSmsListener listener = null;
356         synchronized (mLock) {
357             listener = mListener;
358         }
359 
360         if (listener == null) {
361             throw new RuntimeException("Feature not ready.");
362         }
363         try {
364             listener.onSendSmsResult(token, messageRef, status, reason,
365                     RESULT_NO_NETWORK_ERROR);
366         } catch (RemoteException e) {
367             e.rethrowFromSystemServer();
368         }
369     }
370 
371     /**
372      * This method should be triggered by the IMS providers when an outgoing message fails to be
373      * sent due to an error generated while processing the message or after being sent to the
374      * network.
375      *
376      * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
377      * @param messageRef the message reference, which may be 1 byte if it is in
378      *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
379      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
380      * @param status result of sending the SMS.
381      * @param networkErrorCode the error code reported by the carrier network if sending this SMS
382      *  has resulted in an error or {@link #RESULT_NO_NETWORK_ERROR} if no network error was
383      *  generated. See 3GPP TS 24.011 Section 7.3.4 for valid error codes and more information.
384      *
385      * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
386      * connection to the framework is not available. If this happens attempting to send the SMS
387      * should be aborted.
388      */
onSendSmsResultError(int token, @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status, @SmsManager.Result int reason, int networkErrorCode)389     public final void onSendSmsResultError(int token,
390             @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status,
391             @SmsManager.Result int reason, int networkErrorCode) throws RuntimeException {
392         IImsSmsListener listener = null;
393         synchronized (mLock) {
394             listener = mListener;
395         }
396 
397         if (listener == null) {
398             throw new RuntimeException("Feature not ready.");
399         }
400         try {
401             listener.onSendSmsResult(token, messageRef, status, reason, networkErrorCode);
402         } catch (RemoteException e) {
403             e.rethrowFromSystemServer();
404         }
405     }
406 
407     /**
408      * This API is used to report the result of sending
409      * RP-SMMA to framework based on received network responses(RP-ACK,
410      * RP-ERROR or SIP error).
411      *
412      * @param token provided in {@link #onMemoryAvailable()}.
413      * @param result based on RP-ACK or RP_ERROR
414      * @param networkErrorCode the error code reported by the carrier
415      * network if sending this SMS has resulted in an error or
416      * {@link #RESULT_NO_NETWORK_ERROR} if no network error was generated. See
417      * 3GPP TS 24.011 Section 7.3.4 for valid error codes and more
418      * information.
419      *
420      * @hide
421      */
onMemoryAvailableResult(int token, @SendStatusResult int result, int networkErrorCode)422     public final void onMemoryAvailableResult(int token, @SendStatusResult int result,
423             int networkErrorCode) throws RuntimeException {
424         IImsSmsListener listener = null;
425         synchronized (mLock) {
426             listener = mListener;
427         }
428 
429         if (listener == null) {
430             throw new RuntimeException("Feature not ready.");
431         }
432         try {
433             listener.onMemoryAvailableResult(token, result, networkErrorCode);
434         } catch (RemoteException e) {
435             e.rethrowFromSystemServer();
436         }
437     }
438 
439     /**
440      * This method should be triggered by the IMS providers when the status report of the sent
441      * message is received. The platform will handle the report and notify the IMS provider of the
442      * result by calling {@link #acknowledgeSmsReport(int, int, int)}.
443      *
444      * This method must not be called before {@link #onReady()} is called or the call will fail. If
445      * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called
446      * with the {@link #STATUS_REPORT_STATUS_ERROR} result code.
447      * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
448      * @param messageRef the message reference, which may be 1 byte if it is in
449      *     {@link SmsMessage#FORMAT_3GPP} format or 2 bytes if it is in
450      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
451      * @param format the format of the message.
452      * @param pdu PDU representing the content of the status report.
453      * @throws RuntimeException if called before {@link #onReady()} is triggered
454      *
455      * @deprecated Use {@link #onSmsStatusReportReceived(int, String, byte[])} instead without the
456      * message reference.
457      */
458     @Deprecated
onSmsStatusReportReceived(int token, @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format, byte[] pdu)459     public final void onSmsStatusReportReceived(int token,
460             @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format,
461             byte[] pdu) throws RuntimeException {
462         IImsSmsListener listener = null;
463         synchronized (mLock) {
464             listener = mListener;
465         }
466 
467         if (listener == null) {
468             throw new RuntimeException("Feature not ready.");
469         }
470         try {
471             listener.onSmsStatusReportReceived(token, format, pdu);
472         } catch (RemoteException e) {
473             Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
474             acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR);
475         }
476     }
477 
478     /**
479      * This method should be triggered by the IMS providers when the status report of the sent
480      * message is received. The platform will handle the report and notify the IMS provider of the
481      * result by calling {@link #acknowledgeSmsReport(int, int, int)}.
482      *
483      * This method must not be called before {@link #onReady()} is called or the call will fail. If
484      * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called
485      * with the {@link #STATUS_REPORT_STATUS_ERROR} result code.
486      * @param token unique token generated by IMS providers that the platform will use to trigger
487      *              callbacks for this message.
488      * @param format the format of the message.
489      * @param pdu PDU representing the content of the status report.
490      * @throws RuntimeException if called before {@link #onReady()} is triggered
491      */
onSmsStatusReportReceived(int token, @SmsMessage.Format String format, byte[] pdu)492     public final void onSmsStatusReportReceived(int token, @SmsMessage.Format String format,
493             byte[] pdu) throws RuntimeException {
494         IImsSmsListener listener = null;
495         synchronized (mLock) {
496             listener = mListener;
497         }
498 
499         if (listener == null) {
500             throw new RuntimeException("Feature not ready.");
501         }
502         try {
503             listener.onSmsStatusReportReceived(token, format, pdu);
504         } catch (RemoteException e) {
505             Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
506             SmsMessage message = SmsMessage.createFromPdu(pdu, format);
507             if (message != null && message.mWrappedSmsMessage != null) {
508                 acknowledgeSmsReport(
509                         token,
510                         message.mWrappedSmsMessage.mMessageRef,
511                         STATUS_REPORT_STATUS_ERROR);
512             } else {
513                 Log.w(LOG_TAG, "onSmsStatusReportReceived: Invalid pdu entered.");
514                 acknowledgeSmsReport(token, 0, STATUS_REPORT_STATUS_ERROR);
515             }
516         }
517     }
518 
519     /**
520      * Returns the SMS format that the ImsService expects.
521      *
522      * @return  The expected format of the SMS messages.
523      */
getSmsFormat()524     public @SmsMessage.Format String getSmsFormat() {
525       return SmsMessage.FORMAT_3GPP;
526     }
527 
528     /**
529      * Called when ImsSmsImpl has been initialized and communication with the framework is set up.
530      * Any attempt by this class to access the framework before this method is called will return
531      * with a {@link RuntimeException}.
532      */
onReady()533     public void onReady() {
534         // Base Implementation - Should be overridden
535     }
536 
537     /**
538      * Set default Executor for ImsSmsImplBase.
539      *
540      * @param executor The default executor for the framework to use when executing the methods
541      * overridden by the implementation of ImsSms.
542      * @hide
543      */
setDefaultExecutor(@onNull Executor executor)544     public final void setDefaultExecutor(@NonNull Executor executor) {
545         if (mExecutor == null) {
546             mExecutor = executor;
547         }
548     }
549 
550     /**
551      * Get Executor from ImsSmsImplBase.
552      * If there is no settings for the executor, all ImsSmsImplBase method calls will use
553      * Runnable::run as default
554      *
555      * @return an Executor used to execute methods in ImsSms called remotely by the framework.
556      * @hide
557      */
getExecutor()558     public @NonNull Executor getExecutor() {
559         return mExecutor != null ? mExecutor : Runnable::run;
560     }
561 }
562