1/*
2 * Copyright (C) 2006 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 com.android.internal.telephony;
18
19import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
20
21import java.util.ArrayList;
22import java.util.HashMap;
23
24import android.app.PendingIntent;
25import android.app.PendingIntent.CanceledException;
26import android.os.AsyncResult;
27import android.os.Message;
28import android.provider.Telephony.Sms.Intents;
29import android.telephony.Rlog;
30
31import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
32import com.android.internal.telephony.gsm.GsmSMSDispatcher;
33import com.android.internal.telephony.InboundSmsHandler;
34import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
35import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
36import com.android.internal.telephony.SmsBroadcastUndelivered;
37
38public final class ImsSMSDispatcher extends SMSDispatcher {
39    private static final String TAG = "RIL_ImsSms";
40
41    private SMSDispatcher mCdmaDispatcher;
42    private SMSDispatcher mGsmDispatcher;
43
44    private GsmInboundSmsHandler mGsmInboundSmsHandler;
45    private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
46
47
48    /** true if IMS is registered and sms is supported, false otherwise.*/
49    private boolean mIms = false;
50    private String mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN;
51
52    public ImsSMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor,
53            SmsUsageMonitor usageMonitor) {
54        super(phone, usageMonitor, null);
55        Rlog.d(TAG, "ImsSMSDispatcher created");
56
57        // Create dispatchers, inbound SMS handlers and
58        // broadcast undelivered messages in raw table.
59        mCdmaDispatcher = new CdmaSMSDispatcher(phone, usageMonitor, this);
60        mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
61                storageMonitor, phone);
62        mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
63                storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher);
64        mGsmDispatcher = new GsmSMSDispatcher(phone, usageMonitor, this, mGsmInboundSmsHandler);
65        Thread broadcastThread = new Thread(new SmsBroadcastUndelivered(phone.getContext(),
66                mGsmInboundSmsHandler, mCdmaInboundSmsHandler));
67        broadcastThread.start();
68
69        mCi.registerForOn(this, EVENT_RADIO_ON, null);
70        mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
71    }
72
73    /* Updates the phone object when there is a change */
74    @Override
75    protected void updatePhoneObject(PhoneBase phone) {
76        Rlog.d(TAG, "In IMS updatePhoneObject ");
77        super.updatePhoneObject(phone);
78        mCdmaDispatcher.updatePhoneObject(phone);
79        mGsmDispatcher.updatePhoneObject(phone);
80        mGsmInboundSmsHandler.updatePhoneObject(phone);
81        mCdmaInboundSmsHandler.updatePhoneObject(phone);
82    }
83
84    public void dispose() {
85        mCi.unregisterForOn(this);
86        mCi.unregisterForImsNetworkStateChanged(this);
87        mGsmDispatcher.dispose();
88        mCdmaDispatcher.dispose();
89        mGsmInboundSmsHandler.dispose();
90        mCdmaInboundSmsHandler.dispose();
91    }
92
93    /**
94     * Handles events coming from the phone stack. Overridden from handler.
95     *
96     * @param msg the message to handle
97     */
98    @Override
99    public void handleMessage(Message msg) {
100        AsyncResult ar;
101
102        switch (msg.what) {
103        case EVENT_RADIO_ON:
104        case EVENT_IMS_STATE_CHANGED: // received unsol
105            mCi.getImsRegistrationState(this.obtainMessage(EVENT_IMS_STATE_DONE));
106            break;
107
108        case EVENT_IMS_STATE_DONE:
109            ar = (AsyncResult) msg.obj;
110
111            if (ar.exception == null) {
112                updateImsInfo(ar);
113            } else {
114                Rlog.e(TAG, "IMS State query failed with exp "
115                        + ar.exception);
116            }
117            break;
118
119        default:
120            super.handleMessage(msg);
121        }
122    }
123
124    private void setImsSmsFormat(int format) {
125        // valid format?
126        switch (format) {
127            case PhoneConstants.PHONE_TYPE_GSM:
128                mImsSmsFormat = "3gpp";
129                break;
130            case PhoneConstants.PHONE_TYPE_CDMA:
131                mImsSmsFormat = "3gpp2";
132                break;
133            default:
134                mImsSmsFormat = "unknown";
135                break;
136        }
137    }
138
139    private void updateImsInfo(AsyncResult ar) {
140        int[] responseArray = (int[])ar.result;
141
142        mIms = false;
143        if (responseArray[0] == 1) {  // IMS is registered
144            Rlog.d(TAG, "IMS is registered!");
145            mIms = true;
146        } else {
147            Rlog.d(TAG, "IMS is NOT registered!");
148        }
149
150        setImsSmsFormat(responseArray[1]);
151
152        if (("unknown".equals(mImsSmsFormat))) {
153            Rlog.e(TAG, "IMS format was unknown!");
154            // failed to retrieve valid IMS SMS format info, set IMS to unregistered
155            mIms = false;
156        }
157    }
158
159    @Override
160    protected void sendData(String destAddr, String scAddr, int destPort,
161            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
162        if (isCdmaMo()) {
163            mCdmaDispatcher.sendData(destAddr, scAddr, destPort,
164                    data, sentIntent, deliveryIntent);
165        } else {
166            mGsmDispatcher.sendData(destAddr, scAddr, destPort,
167                    data, sentIntent, deliveryIntent);
168        }
169    }
170
171    @Override
172    protected void sendMultipartText(String destAddr, String scAddr,
173            ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
174            ArrayList<PendingIntent> deliveryIntents) {
175        if (isCdmaMo()) {
176            mCdmaDispatcher.sendMultipartText(destAddr, scAddr,
177                    parts, sentIntents, deliveryIntents);
178        } else {
179            mGsmDispatcher.sendMultipartText(destAddr, scAddr,
180                    parts, sentIntents, deliveryIntents);
181        }
182    }
183
184    @Override
185    protected void sendSms(SmsTracker tracker) {
186        //  sendSms is a helper function to other send functions, sendText/Data...
187        //  it is not part of ISms.stub
188        Rlog.e(TAG, "sendSms should never be called from here!");
189    }
190
191    @Override
192    protected void sendText(String destAddr, String scAddr, String text,
193            PendingIntent sentIntent, PendingIntent deliveryIntent) {
194        Rlog.d(TAG, "sendText");
195        if (isCdmaMo()) {
196            mCdmaDispatcher.sendText(destAddr, scAddr,
197                    text, sentIntent, deliveryIntent);
198        } else {
199            mGsmDispatcher.sendText(destAddr, scAddr,
200                    text, sentIntent, deliveryIntent);
201        }
202    }
203
204    @Override
205    public void sendRetrySms(SmsTracker tracker) {
206        String oldFormat = tracker.mFormat;
207
208        // newFormat will be based on voice technology
209        String newFormat =
210            (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()) ?
211                    mCdmaDispatcher.getFormat() :
212                        mGsmDispatcher.getFormat();
213
214        // was previously sent sms format match with voice tech?
215        if (oldFormat.equals(newFormat)) {
216            if (isCdmaFormat(newFormat)) {
217                Rlog.d(TAG, "old format matched new format (cdma)");
218                mCdmaDispatcher.sendSms(tracker);
219                return;
220            } else {
221                Rlog.d(TAG, "old format matched new format (gsm)");
222                mGsmDispatcher.sendSms(tracker);
223                return;
224            }
225        }
226
227        // format didn't match, need to re-encode.
228        HashMap map = tracker.mData;
229
230        // to re-encode, fields needed are:  scAddr, destAddr, and
231        //   text if originally sent as sendText or
232        //   data and destPort if originally sent as sendData.
233        if (!( map.containsKey("scAddr") && map.containsKey("destAddr") &&
234               ( map.containsKey("text") ||
235                       (map.containsKey("data") && map.containsKey("destPort"))))) {
236            // should never come here...
237            Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!");
238            if (tracker.mSentIntent != null) {
239                int error = RESULT_ERROR_GENERIC_FAILURE;
240                // Done retrying; return an error to the app.
241                try {
242                    tracker.mSentIntent.send(mContext, error, null);
243                } catch (CanceledException ex) {}
244            }
245            return;
246        }
247        String scAddr = (String)map.get("scAddr");
248        String destAddr = (String)map.get("destAddr");
249
250        SmsMessageBase.SubmitPduBase pdu = null;
251        //    figure out from tracker if this was sendText/Data
252        if (map.containsKey("text")) {
253            Rlog.d(TAG, "sms failed was text");
254            String text = (String)map.get("text");
255
256            if (isCdmaFormat(newFormat)) {
257                Rlog.d(TAG, "old format (gsm) ==> new format (cdma)");
258                pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
259                        scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
260            } else {
261                Rlog.d(TAG, "old format (cdma) ==> new format (gsm)");
262                pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
263                        scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
264            }
265        } else if (map.containsKey("data")) {
266            Rlog.d(TAG, "sms failed was data");
267            byte[] data = (byte[])map.get("data");
268            Integer destPort = (Integer)map.get("destPort");
269
270            if (isCdmaFormat(newFormat)) {
271                Rlog.d(TAG, "old format (gsm) ==> new format (cdma)");
272                pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
273                            scAddr, destAddr, destPort.intValue(), data,
274                            (tracker.mDeliveryIntent != null));
275            } else {
276                Rlog.d(TAG, "old format (cdma) ==> new format (gsm)");
277                pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
278                            scAddr, destAddr, destPort.intValue(), data,
279                            (tracker.mDeliveryIntent != null));
280            }
281        }
282
283        // replace old smsc and pdu with newly encoded ones
284        map.put("smsc", pdu.encodedScAddress);
285        map.put("pdu", pdu.encodedMessage);
286
287        SMSDispatcher dispatcher = (isCdmaFormat(newFormat)) ?
288                mCdmaDispatcher : mGsmDispatcher;
289
290        tracker.mFormat = dispatcher.getFormat();
291        dispatcher.sendSms(tracker);
292    }
293
294    @Override
295    protected String getFormat() {
296        // this function should be defined in Gsm/CdmaDispatcher.
297        Rlog.e(TAG, "getFormat should never be called from here!");
298        return "unknown";
299    }
300
301    @Override
302    protected GsmAlphabet.TextEncodingDetails calculateLength(
303            CharSequence messageBody, boolean use7bitOnly) {
304        Rlog.e(TAG, "Error! Not implemented for IMS.");
305        return null;
306    }
307
308    @Override
309    protected void sendNewSubmitPdu(String destinationAddress, String scAddress, String message,
310            SmsHeader smsHeader, int format, PendingIntent sentIntent,
311            PendingIntent deliveryIntent, boolean lastPart) {
312        Rlog.e(TAG, "Error! Not implemented for IMS.");
313    }
314
315    @Override
316    public boolean isIms() {
317        return mIms;
318    }
319
320    @Override
321    public String getImsSmsFormat() {
322        return mImsSmsFormat;
323    }
324
325    /**
326     * Determines whether or not to use CDMA format for MO SMS.
327     * If SMS over IMS is supported, then format is based on IMS SMS format,
328     * otherwise format is based on current phone type.
329     *
330     * @return true if Cdma format should be used for MO SMS, false otherwise.
331     */
332    private boolean isCdmaMo() {
333        if (!isIms()) {
334            // IMS is not registered, use Voice technology to determine SMS format.
335            return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType());
336        }
337        // IMS is registered with SMS support
338        return isCdmaFormat(mImsSmsFormat);
339    }
340
341    /**
342     * Determines whether or not format given is CDMA format.
343     *
344     * @param format
345     * @return true if format given is CDMA format, false otherwise.
346     */
347    private boolean isCdmaFormat(String format) {
348        return (mCdmaDispatcher.getFormat().equals(format));
349    }
350}
351