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
17package android.telephony.ims.stub;
18
19import android.annotation.IntDef;
20import android.annotation.SystemApi;
21import android.os.RemoteException;
22import android.telephony.SmsManager;
23import android.telephony.SmsMessage;
24import android.telephony.ims.aidl.IImsSmsListener;
25import android.util.Log;
26
27import java.lang.annotation.Retention;
28import 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
39public 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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
328    public void onReady() {
329        // Base Implementation - Should be overridden
330    }
331}
332