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.SystemApi;
22 import android.annotation.TestApi;
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 
32 /**
33  * Base implementation for SMS over IMS.
34  *
35  * Any service wishing to provide SMS over IMS should extend this class and implement all methods
36  * that the service supports.
37  *
38  * @hide
39  */
40 @SystemApi
41 @TestApi
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 
134     /**
135      * Registers a listener responsible for handling tasks like delivering messages.
136      *
137      * @param listener listener to register.
138      *
139      * @hide
140      */
registerSmsListener(IImsSmsListener listener)141     public final void registerSmsListener(IImsSmsListener listener) {
142         synchronized (mLock) {
143             mListener = listener;
144         }
145     }
146 
147     /**
148      * This method will be triggered by the platform when the user attempts to send an SMS. This
149      * method should be implemented by the IMS providers to provide implementation of sending an SMS
150      * over IMS.
151      *
152      * @param token unique token generated by the platform that should be used when triggering
153      *             callbacks for this specific message.
154      * @param messageRef the message reference, which may be 1 byte if it is in
155      *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
156      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
157      * @param format the format of the message.
158      * @param smsc the Short Message Service Center address.
159      * @param isRetry whether it is a retry of an already attempted message or not.
160      * @param pdu PDU representing the contents of the message.
161      */
sendSms(int token, @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format, String smsc, boolean isRetry, byte[] pdu)162     public void sendSms(int token, @IntRange(from = 0, to = 65535) int messageRef,
163             @SmsMessage.Format String format, String smsc, boolean isRetry,
164             byte[] pdu) {
165         // Base implementation returns error. Should be overridden.
166         try {
167             onSendSmsResult(token, messageRef, SEND_STATUS_ERROR,
168                     SmsManager.RESULT_ERROR_GENERIC_FAILURE);
169         } catch (RuntimeException e) {
170             Log.e(LOG_TAG, "Can not send sms: " + e.getMessage());
171         }
172     }
173 
174     /**
175      * This method will be triggered by the platform after
176      * {@link #onSmsReceived(int, String, byte[])} has been called to deliver the result to the IMS
177      * provider.
178      *
179      * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
180      * @param messageRef the message reference, which may be 1 byte if it is in
181      *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
182      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
183      * @param result result of delivering the message.
184      */
acknowledgeSms(int token, @IntRange(from = 0, to = 65535) int messageRef, @DeliverStatusResult int result)185     public void acknowledgeSms(int token, @IntRange(from = 0, to = 65535)  int messageRef,
186             @DeliverStatusResult int result) {
187         Log.e(LOG_TAG, "acknowledgeSms() not implemented.");
188     }
189 
190     /**
191      * This method will be triggered by the platform after
192      * {@link #onSmsStatusReportReceived(int, int, String, byte[])} or
193      * {@link #onSmsStatusReportReceived(int, String, byte[])} has been called to provide the
194      * result to the IMS provider.
195      *
196      * @param token token provided in {@link #onSmsStatusReportReceived(int, int, String, byte[])}
197      *              or {@link #onSmsStatusReportReceived(int, String, byte[])}
198      * @param messageRef the message reference, which may be 1 byte if it is in
199      *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
200      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
201      * @param result result of delivering the message.
202      */
acknowledgeSmsReport(int token, @IntRange(from = 0, to = 65535) int messageRef, @StatusReportResult int result)203     public void acknowledgeSmsReport(int token, @IntRange(from = 0, to = 65535) int messageRef,
204             @StatusReportResult int result) {
205         Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented.");
206     }
207 
208     /**
209      * This method should be triggered by the IMS providers when there is an incoming message. The
210      * platform will deliver the message to the messages database and notify the IMS provider of the
211      * result by calling {@link #acknowledgeSms(int, int, int)}.
212      *
213      * This method must not be called before {@link #onReady()} is called or the call will fail. If
214      * the platform is not available, {@link #acknowledgeSms(int, int, int)} will be called with the
215      * {@link #DELIVER_STATUS_ERROR_GENERIC} result code.
216      * @param token unique token generated by IMS providers that the platform will use to trigger
217      *              callbacks for this message.
218      * @param format the format of the message.
219      * @param pdu PDU representing the contents of the message.
220      * @throws RuntimeException if called before {@link #onReady()} is triggered.
221      */
onSmsReceived(int token, @SmsMessage.Format String format, byte[] pdu)222     public final void onSmsReceived(int token, @SmsMessage.Format String format, byte[] pdu)
223             throws RuntimeException {
224         synchronized (mLock) {
225             if (mListener == null) {
226                 throw new RuntimeException("Feature not ready.");
227             }
228             try {
229                 mListener.onSmsReceived(token, format, pdu);
230             } catch (RemoteException e) {
231                 Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
232                 SmsMessage message = SmsMessage.createFromPdu(pdu, format);
233                 if (message != null && message.mWrappedSmsMessage != null) {
234                     acknowledgeSms(token, message.mWrappedSmsMessage.mMessageRef,
235                             DELIVER_STATUS_ERROR_GENERIC);
236                 } else {
237                     Log.w(LOG_TAG, "onSmsReceived: Invalid pdu entered.");
238                     acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC);
239                 }
240             }
241         }
242     }
243 
244     /**
245      * This method should be triggered by the IMS providers when an outgoing SMS message has been
246      * sent successfully.
247      *
248      * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
249      * @param messageRef the message reference, which may be 1 byte if it is in
250      *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
251      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
252      *
253      * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
254      * connection to the framework is not available. If this happens attempting to send the SMS
255      * should be aborted.
256      */
onSendSmsResultSuccess(int token, @IntRange(from = 0, to = 65535) int messageRef)257     public final void onSendSmsResultSuccess(int token,
258             @IntRange(from = 0, to = 65535) int messageRef) throws RuntimeException {
259         synchronized (mLock) {
260             if (mListener == null) {
261                 throw new RuntimeException("Feature not ready.");
262             }
263             try {
264                 mListener.onSendSmsResult(token, messageRef, SEND_STATUS_OK,
265                         SmsManager.RESULT_ERROR_NONE, RESULT_NO_NETWORK_ERROR);
266             } catch (RemoteException e) {
267                 e.rethrowFromSystemServer();
268             }
269         }
270     }
271 
272     /**
273      * This method should be triggered by the IMS providers to pass the result of the sent message
274      * to the platform.
275      *
276      * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
277      * @param messageRef the message reference, which may be 1 byte if it is in
278      *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
279      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
280      * @param status result of sending the SMS.
281      * @param reason reason in case status is failure.
282      *
283      * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
284      * connection to the framework is not available. If this happens attempting to send the SMS
285      * should be aborted.
286      * @deprecated Use {@link #onSendSmsResultSuccess(int, int)} or
287      * {@link #onSendSmsResultError(int, int, int, int, int)} to notify the framework of the SMS
288      * send result.
289      */
290     @Deprecated
onSendSmsResult(int token, @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status, @SmsManager.Result int reason)291     public final void onSendSmsResult(int token, @IntRange(from = 0, to = 65535) int messageRef,
292             @SendStatusResult int status, @SmsManager.Result int reason) throws RuntimeException {
293         synchronized (mLock) {
294             if (mListener == null) {
295                 throw new RuntimeException("Feature not ready.");
296             }
297             try {
298                 mListener.onSendSmsResult(token, messageRef, status, reason,
299                         RESULT_NO_NETWORK_ERROR);
300             } catch (RemoteException e) {
301                 e.rethrowFromSystemServer();
302             }
303         }
304     }
305 
306     /**
307      * This method should be triggered by the IMS providers when an outgoing message fails to be
308      * sent due to an error generated while processing the message or after being sent to the
309      * network.
310      *
311      * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
312      * @param messageRef the message reference, which may be 1 byte if it is in
313      *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
314      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
315      * @param status result of sending the SMS.
316      * @param networkErrorCode the error code reported by the carrier network if sending this SMS
317      *  has resulted in an error or {@link #RESULT_NO_NETWORK_ERROR} if no network error was
318      *  generated. See 3GPP TS 24.011 Section 7.3.4 for valid error codes and more information.
319      *
320      * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
321      * connection to the framework is not available. If this happens attempting to send the SMS
322      * should be aborted.
323      */
onSendSmsResultError(int token, @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status, @SmsManager.Result int reason, int networkErrorCode)324     public final void onSendSmsResultError(int token,
325             @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status,
326             @SmsManager.Result int reason, int networkErrorCode) throws RuntimeException {
327         synchronized (mLock) {
328             if (mListener == null) {
329                 throw new RuntimeException("Feature not ready.");
330             }
331             try {
332                 mListener.onSendSmsResult(token, messageRef, status, reason, networkErrorCode);
333             } catch (RemoteException e) {
334                 e.rethrowFromSystemServer();
335             }
336         }
337     }
338 
339     /**
340      * This method should be triggered by the IMS providers when the status report of the sent
341      * message is received. The platform will handle the report and notify the IMS provider of the
342      * result by calling {@link #acknowledgeSmsReport(int, int, int)}.
343      *
344      * This method must not be called before {@link #onReady()} is called or the call will fail. If
345      * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called
346      * with the {@link #STATUS_REPORT_STATUS_ERROR} result code.
347      * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
348      * @param messageRef the message reference, which may be 1 byte if it is in
349      *     {@link SmsMessage#FORMAT_3GPP} format or 2 bytes if it is in
350      *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
351      * @param format the format of the message.
352      * @param pdu PDU representing the content of the status report.
353      * @throws RuntimeException if called before {@link #onReady()} is triggered
354      *
355      * @deprecated Use {@link #onSmsStatusReportReceived(int, String, byte[])} instead without the
356      * message reference.
357      */
358     @Deprecated
onSmsStatusReportReceived(int token, @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format, byte[] pdu)359     public final void onSmsStatusReportReceived(int token,
360             @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format,
361             byte[] pdu) throws RuntimeException {
362         synchronized (mLock) {
363             if (mListener == null) {
364                 throw new RuntimeException("Feature not ready.");
365             }
366             try {
367                 mListener.onSmsStatusReportReceived(token, format, pdu);
368             } catch (RemoteException e) {
369                 Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
370                 acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR);
371             }
372         }
373     }
374 
375     /**
376      * This method should be triggered by the IMS providers when the status report of the sent
377      * message is received. The platform will handle the report and notify the IMS provider of the
378      * result by calling {@link #acknowledgeSmsReport(int, int, int)}.
379      *
380      * This method must not be called before {@link #onReady()} is called or the call will fail. If
381      * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called
382      * with the {@link #STATUS_REPORT_STATUS_ERROR} result code.
383      * @param token unique token generated by IMS providers that the platform will use to trigger
384      *              callbacks for this message.
385      * @param format the format of the message.
386      * @param pdu PDU representing the content of the status report.
387      * @throws RuntimeException if called before {@link #onReady()} is triggered
388      */
onSmsStatusReportReceived(int token, @SmsMessage.Format String format, byte[] pdu)389     public final void onSmsStatusReportReceived(int token, @SmsMessage.Format String format,
390             byte[] pdu) throws RuntimeException {
391         synchronized (mLock) {
392             if (mListener == null) {
393                 throw new RuntimeException("Feature not ready.");
394             }
395             try {
396                 mListener.onSmsStatusReportReceived(token, format, pdu);
397             } catch (RemoteException e) {
398                 Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
399                 SmsMessage message = SmsMessage.createFromPdu(pdu, format);
400                 if (message != null && message.mWrappedSmsMessage != null) {
401                     acknowledgeSmsReport(
402                             token,
403                             message.mWrappedSmsMessage.mMessageRef,
404                             STATUS_REPORT_STATUS_ERROR);
405                 } else {
406                     Log.w(LOG_TAG, "onSmsStatusReportReceived: Invalid pdu entered.");
407                     acknowledgeSmsReport(token, 0, STATUS_REPORT_STATUS_ERROR);
408                 }
409             }
410         }
411     }
412 
413     /**
414      * Returns the SMS format that the ImsService expects.
415      *
416      * @return  The expected format of the SMS messages.
417      */
getSmsFormat()418     public @SmsMessage.Format String getSmsFormat() {
419       return SmsMessage.FORMAT_3GPP;
420     }
421 
422     /**
423      * Called when ImsSmsImpl has been initialized and communication with the framework is set up.
424      * Any attempt by this class to access the framework before this method is called will return
425      * with a {@link RuntimeException}.
426      */
onReady()427     public void onReady() {
428         // Base Implementation - Should be overridden
429     }
430 }
431