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.SystemApi;
21 import android.os.RemoteException;
22 import android.telephony.SmsManager;
23 import android.telephony.SmsMessage;
24 import android.telephony.ims.aidl.IImsSmsListener;
25 import android.util.Log;
26 
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 
30 /**
31  * Base implementation for SMS over IMS.
32  *
33  * Any service wishing to provide SMS over IMS should extend this class and implement all methods
34  * that the service supports.
35  *
36  * @hide
37  */
38 @SystemApi
39 public class ImsSmsImplBase {
40     private static final String LOG_TAG = "SmsImplBase";
41 
42     /** @hide */
43     @IntDef({
44             SEND_STATUS_OK,
45             SEND_STATUS_ERROR,
46             SEND_STATUS_ERROR_RETRY,
47             SEND_STATUS_ERROR_FALLBACK
48         })
49     @Retention(RetentionPolicy.SOURCE)
50     public @interface SendStatusResult {}
51     /**
52      * Message was sent successfully.
53      */
54     public static final int SEND_STATUS_OK = 1;
55 
56     /**
57      * IMS provider failed to send the message and platform should not retry falling back to sending
58      * the message using the radio.
59      */
60     public static final int SEND_STATUS_ERROR = 2;
61 
62     /**
63      * IMS provider failed to send the message and platform should retry again after setting TP-RD
64      * bit to high.
65      */
66     public static final int SEND_STATUS_ERROR_RETRY = 3;
67 
68     /**
69      * IMS provider failed to send the message and platform should retry falling back to sending
70      * the message using the radio.
71      */
72     public static final int SEND_STATUS_ERROR_FALLBACK = 4;
73 
74     /** @hide */
75     @IntDef({
76             DELIVER_STATUS_OK,
77             DELIVER_STATUS_ERROR_GENERIC,
78             DELIVER_STATUS_ERROR_NO_MEMORY,
79             DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED
80         })
81     @Retention(RetentionPolicy.SOURCE)
82     public @interface DeliverStatusResult {}
83     /**
84      * Message was delivered successfully.
85      */
86     public static final int DELIVER_STATUS_OK = 1;
87 
88     /**
89      * Message was not delivered.
90      */
91     public static final int DELIVER_STATUS_ERROR_GENERIC = 2;
92 
93     /**
94      * Message was not delivered due to lack of memory.
95      */
96     public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3;
97 
98     /**
99      * Message was not delivered as the request is not supported.
100      */
101     public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4;
102 
103     /** @hide */
104     @IntDef({
105             STATUS_REPORT_STATUS_OK,
106             STATUS_REPORT_STATUS_ERROR
107         })
108     @Retention(RetentionPolicy.SOURCE)
109     public @interface StatusReportResult {}
110 
111     /**
112      * Status Report was set successfully.
113      */
114     public static final int STATUS_REPORT_STATUS_OK = 1;
115 
116     /**
117      * Error while setting status report.
118      */
119     public static final int STATUS_REPORT_STATUS_ERROR = 2;
120 
121     // Lock for feature synchronization
122     private final Object mLock = new Object();
123     private IImsSmsListener mListener;
124 
125     /**
126      * Registers a listener responsible for handling tasks like delivering messages.
127      *
128      * @param listener listener to register.
129      *
130      * @hide
131      */
registerSmsListener(IImsSmsListener listener)132     public final void registerSmsListener(IImsSmsListener listener) {
133         synchronized (mLock) {
134             mListener = listener;
135         }
136     }
137 
138     /**
139      * This method will be triggered by the platform when the user attempts to send an SMS. This
140      * method should be implemented by the IMS providers to provide implementation of sending an SMS
141      * over IMS.
142      *
143      * @param token unique token generated by the platform that should be used when triggering
144      *             callbacks for this specific message.
145      * @param messageRef the message reference.
146      * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
147      *               {@link SmsMessage#FORMAT_3GPP2}.
148      * @param smsc the Short Message Service Center address.
149      * @param isRetry whether it is a retry of an already attempted message or not.
150      * @param pdu PDUs representing the contents of the message.
151      */
sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, byte[] pdu)152     public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
153             byte[] pdu) {
154         // Base implementation returns error. Should be overridden.
155         try {
156             onSendSmsResult(token, messageRef, SEND_STATUS_ERROR,
157                     SmsManager.RESULT_ERROR_GENERIC_FAILURE);
158         } catch (RuntimeException e) {
159             Log.e(LOG_TAG, "Can not send sms: " + e.getMessage());
160         }
161     }
162 
163     /**
164      * This method will be triggered by the platform after
165      * {@link #onSmsReceived(int, String, byte[])} has been called to deliver the result to the IMS
166      * provider.
167      *
168      * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
169      * @param result result of delivering the message. Valid values are:
170      *  {@link #DELIVER_STATUS_OK},
171      *  {@link #DELIVER_STATUS_ERROR_GENERIC},
172      *  {@link #DELIVER_STATUS_ERROR_NO_MEMORY},
173      *  {@link #DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED}
174      * @param messageRef the message reference
175      */
acknowledgeSms(int token, @DeliverStatusResult int messageRef, int result)176     public void acknowledgeSms(int token, @DeliverStatusResult int messageRef, int result) {
177         Log.e(LOG_TAG, "acknowledgeSms() not implemented.");
178     }
179 
180     /**
181      * This method will be triggered by the platform after
182      * {@link #onSmsStatusReportReceived(int, int, String, byte[])} has been called to provide the
183      * result to the IMS provider.
184      *
185      * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
186      * @param result result of delivering the message. Valid values are:
187      *  {@link #STATUS_REPORT_STATUS_OK},
188      *  {@link #STATUS_REPORT_STATUS_ERROR}
189      * @param messageRef the message reference
190      */
acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result)191     public void acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result) {
192         Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented.");
193     }
194 
195     /**
196      * This method should be triggered by the IMS providers when there is an incoming message. The
197      * platform will deliver the message to the messages database and notify the IMS provider of the
198      * result by calling {@link #acknowledgeSms(int, int, int)}.
199      *
200      * This method must not be called before {@link #onReady()} is called or the call will fail. If
201      * the platform is not available, {@link #acknowledgeSms(int, int, int)} will be called with the
202      * {@link #DELIVER_STATUS_ERROR_GENERIC} result code.
203      * @param token unique token generated by IMS providers that the platform will use to trigger
204      *              callbacks for this message.
205      * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
206      * {@link SmsMessage#FORMAT_3GPP2}.
207      * @param pdu PDUs representing the contents of the message.
208      * @throws RuntimeException if called before {@link #onReady()} is triggered.
209      */
onSmsReceived(int token, String format, byte[] pdu)210     public final void onSmsReceived(int token, String format, byte[] pdu) throws RuntimeException {
211         synchronized (mLock) {
212             if (mListener == null) {
213                 throw new RuntimeException("Feature not ready.");
214             }
215             try {
216                 mListener.onSmsReceived(token, format, pdu);
217             } catch (RemoteException e) {
218                 Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
219                 SmsMessage message = SmsMessage.createFromPdu(pdu, format);
220                 if (message != null && message.mWrappedSmsMessage != null) {
221                     acknowledgeSms(token, message.mWrappedSmsMessage.mMessageRef,
222                             DELIVER_STATUS_ERROR_GENERIC);
223                 } else {
224                     Log.w(LOG_TAG, "onSmsReceived: Invalid pdu entered.");
225                     acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC);
226                 }
227             }
228         }
229     }
230 
231     /**
232      * This method should be triggered by the IMS providers to pass the result of the sent message
233      * to the platform.
234      *
235      * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
236      * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
237      * @param status result of sending the SMS. Valid values are:
238      *  {@link #SEND_STATUS_OK},
239      *  {@link #SEND_STATUS_ERROR},
240      *  {@link #SEND_STATUS_ERROR_RETRY},
241      *  {@link #SEND_STATUS_ERROR_FALLBACK},
242      * @param reason reason in case status is failure. Valid values are:
243      *  {@link SmsManager#RESULT_ERROR_NONE},
244      *  {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
245      *  {@link SmsManager#RESULT_ERROR_RADIO_OFF},
246      *  {@link SmsManager#RESULT_ERROR_NULL_PDU},
247      *  {@link SmsManager#RESULT_ERROR_NO_SERVICE},
248      *  {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED},
249      *  {@link SmsManager#RESULT_ERROR_FDN_CHECK_FAILURE},
250      *  {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED},
251      *  {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED},
252      *  {@link SmsManager#RESULT_RADIO_NOT_AVAILABLE},
253      *  {@link SmsManager#RESULT_NETWORK_REJECT},
254      *  {@link SmsManager#RESULT_INVALID_ARGUMENTS},
255      *  {@link SmsManager#RESULT_INVALID_STATE},
256      *  {@link SmsManager#RESULT_NO_MEMORY},
257      *  {@link SmsManager#RESULT_INVALID_SMS_FORMAT},
258      *  {@link SmsManager#RESULT_SYSTEM_ERROR},
259      *  {@link SmsManager#RESULT_MODEM_ERROR},
260      *  {@link SmsManager#RESULT_NETWORK_ERROR},
261      *  {@link SmsManager#RESULT_ENCODING_ERROR},
262      *  {@link SmsManager#RESULT_INVALID_SMSC_ADDRESS},
263      *  {@link SmsManager#RESULT_OPERATION_NOT_ALLOWED},
264      *  {@link SmsManager#RESULT_INTERNAL_ERROR},
265      *  {@link SmsManager#RESULT_NO_RESOURCES},
266      *  {@link SmsManager#RESULT_CANCELLED},
267      *  {@link SmsManager#RESULT_REQUEST_NOT_SUPPORTED}
268      *
269      * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
270      * connection to the framework is not available. If this happens attempting to send the SMS
271      * should be aborted.
272      */
onSendSmsResult(int token, int messageRef, @SendStatusResult int status, int reason)273     public final void onSendSmsResult(int token, int messageRef,  @SendStatusResult int status,
274             int reason) throws RuntimeException {
275         synchronized (mLock) {
276             if (mListener == null) {
277                 throw new RuntimeException("Feature not ready.");
278             }
279             try {
280                 mListener.onSendSmsResult(token, messageRef, status, reason);
281             } catch (RemoteException e) {
282                 e.rethrowFromSystemServer();
283             }
284         }
285     }
286 
287     /**
288      * Sets the status report of the sent message.
289      *
290      * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
291      * @param messageRef the message reference.
292      * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
293      * {@link SmsMessage#FORMAT_3GPP2}.
294      * @param pdu PDUs representing the content of the status report.
295      * @throws RuntimeException if called before {@link #onReady()} is triggered
296      */
onSmsStatusReportReceived(int token, int messageRef, String format, byte[] pdu)297     public final void onSmsStatusReportReceived(int token, int messageRef, String format,
298             byte[] pdu) throws RuntimeException{
299         synchronized (mLock) {
300             if (mListener == null) {
301                 throw new RuntimeException("Feature not ready.");
302             }
303             try {
304                 mListener.onSmsStatusReportReceived(token, messageRef, format, pdu);
305             } catch (RemoteException e) {
306                 Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
307                 acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR);
308             }
309         }
310     }
311 
312     /**
313      * Returns the SMS format. Default is {@link SmsMessage#FORMAT_3GPP} unless overridden by IMS
314      * Provider.
315      *
316      * @return  the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
317      * {@link SmsMessage#FORMAT_3GPP2}.
318      */
getSmsFormat()319     public String getSmsFormat() {
320       return SmsMessage.FORMAT_3GPP;
321     }
322 
323     /**
324      * Called when ImsSmsImpl has been initialized and communication with the framework is set up.
325      * Any attempt by this class to access the framework before this method is called will return
326      * with a {@link RuntimeException}.
327      */
onReady()328     public void onReady() {
329         // Base Implementation - Should be overridden
330     }
331 }
332