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