1c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville/*
2c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * Copyright (C) 2006 The Android Open Source Project
3c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville *
4c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * Licensed under the Apache License, Version 2.0 (the "License");
5c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * you may not use this file except in compliance with the License.
6c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * You may obtain a copy of the License at
7c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville *
8c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville *      http://www.apache.org/licenses/LICENSE-2.0
9c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville *
10c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * Unless required by applicable law or agreed to in writing, software
11c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * distributed under the License is distributed on an "AS IS" BASIS,
12c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * See the License for the specific language governing permissions and
14c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville * limitations under the License.
15c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville */
16c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
17c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savillepackage com.android.internal.telephony.gsm;
18c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
19c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.app.Activity;
20c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.app.PendingIntent;
21c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.app.PendingIntent.CanceledException;
22c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.content.Intent;
23c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.os.AsyncResult;
24c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.os.Message;
25c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.os.SystemProperties;
26c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.provider.Telephony.Sms;
27c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.provider.Telephony.Sms.Intents;
28c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.telephony.PhoneNumberUtils;
29c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.telephony.SmsCbLocation;
30c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.telephony.SmsCbMessage;
31c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.telephony.SmsManager;
32c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.telephony.gsm.GsmCellLocation;
33c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport android.util.Log;
34c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
35c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.CommandsInterface;
36c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.GsmAlphabet;
37c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.IccUtils;
38c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.PhoneBase;
39c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.SmsConstants;
40c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.SMSDispatcher;
41c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.SmsHeader;
42c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.SmsMessageBase;
43c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.SmsStorageMonitor;
44c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.SmsUsageMonitor;
45c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport com.android.internal.telephony.TelephonyProperties;
46c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
47c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport java.util.HashMap;
48c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savilleimport java.util.Iterator;
49c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
50c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Savillepublic final class GsmSMSDispatcher extends SMSDispatcher {
51c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private static final String TAG = "GSM";
52c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
53c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** Status report received */
54c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private static final int EVENT_NEW_SMS_STATUS_REPORT = 100;
55c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
56c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** New broadcast SMS */
57c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private static final int EVENT_NEW_BROADCAST_SMS = 101;
58c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
59c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** Result of writing SM to UICC (when SMS-PP service is not available). */
60c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private static final int EVENT_WRITE_SMS_COMPLETE = 102;
61c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
62c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** Handler for SMS-PP data download messages to UICC. */
63c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private final UsimDataDownloadHandler mDataDownloadHandler;
64c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
65c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public GsmSMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor,
66c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            SmsUsageMonitor usageMonitor) {
67c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        super(phone, storageMonitor, usageMonitor);
68c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        mDataDownloadHandler = new UsimDataDownloadHandler(mCm);
69c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        mCm.setOnNewGsmSms(this, EVENT_NEW_SMS, null);
70c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
71c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        mCm.setOnNewGsmBroadcastSms(this, EVENT_NEW_BROADCAST_SMS, null);
72c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
73c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
74c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
75c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public void dispose() {
76c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        mCm.unSetOnNewGsmSms(this);
77c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        mCm.unSetOnSmsStatus(this);
78c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        mCm.unSetOnNewGsmBroadcastSms(this);
79c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
80c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
81c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
82c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    protected String getFormat() {
83c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return SmsConstants.FORMAT_3GPP;
84c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
85c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
86c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
87c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Handles 3GPP format-specific events coming from the phone stack.
88c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Other events are handled by {@link SMSDispatcher#handleMessage}.
89c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
90c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param msg the message to handle
91c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
92c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
93c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public void handleMessage(Message msg) {
94c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        switch (msg.what) {
95c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case EVENT_NEW_SMS_STATUS_REPORT:
96c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            handleStatusReport((AsyncResult) msg.obj);
97c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
98c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
99c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case EVENT_NEW_BROADCAST_SMS:
100c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            handleBroadcastSms((AsyncResult)msg.obj);
101c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
102c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
103c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        case EVENT_WRITE_SMS_COMPLETE:
104c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            AsyncResult ar = (AsyncResult) msg.obj;
105c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (ar.exception == null) {
106c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.d(TAG, "Successfully wrote SMS-PP message to UICC");
107c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                mCm.acknowledgeLastIncomingGsmSms(true, 0, null);
108c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
109c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.d(TAG, "Failed to write SMS-PP message to UICC", ar.exception);
110c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                mCm.acknowledgeLastIncomingGsmSms(false,
111c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR, null);
112c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
113c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            break;
114c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
115c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        default:
116c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            super.handleMessage(msg);
117c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
118c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
119c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
120c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
121c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Called when a status report is received.  This should correspond to
122c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * a previously successful SEND.
123c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *
124c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param ar AsyncResult passed into the message handler.  ar.result should
125c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     *           be a String representing the status report PDU, as ASCII hex.
126c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
127c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private void handleStatusReport(AsyncResult ar) {
128c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        String pduString = (String) ar.result;
129c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SmsMessage sms = SmsMessage.newFromCDS(pduString);
130c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
131c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (sms != null) {
132c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int tpStatus = sms.getStatus();
133c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int messageRef = sms.messageRef;
134c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
135c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                SmsTracker tracker = deliveryPendingList.get(i);
136c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                if (tracker.mMessageRef == messageRef) {
137c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    // Found it.  Remove from list and broadcast.
138c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) {
139c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                       deliveryPendingList.remove(i);
140c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    }
141c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    PendingIntent intent = tracker.mDeliveryIntent;
142c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    Intent fillIn = new Intent();
143c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    fillIn.putExtra("pdu", IccUtils.hexStringToBytes(pduString));
144c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    fillIn.putExtra("format", SmsConstants.FORMAT_3GPP);
145c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    try {
146c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        intent.send(mContext, Activity.RESULT_OK, fillIn);
147c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    } catch (CanceledException ex) {}
148c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
149c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    // Only expect to see one tracker matching this messageref
150c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    break;
151c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
152c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
153c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
154c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        acknowledgeLastIncomingSms(true, Intents.RESULT_SMS_HANDLED, null);
155c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
156c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
157c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
158c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
159c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    public int dispatchMessage(SmsMessageBase smsb) {
160c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
161c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // If sms is null, means there was a parsing error.
162c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (smsb == null) {
163c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Log.e(TAG, "dispatchMessage: message is null");
164c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return Intents.RESULT_SMS_GENERIC_ERROR;
165c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
166c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
167c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SmsMessage sms = (SmsMessage) smsb;
168c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
169c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (sms.isTypeZero()) {
170c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be
171c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Displayed/Stored/Notified. They should only be acknowledged.
172c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Log.d(TAG, "Received short message type 0, Don't display or store it. Send Ack");
173c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return Intents.RESULT_SMS_HANDLED;
174c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
175c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
176c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // Send SMS-PP data download messages to UICC. See 3GPP TS 31.111 section 7.1.1.
177c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (sms.isUsimDataDownload()) {
178c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            UsimServiceTable ust = mPhone.getUsimServiceTable();
179c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // If we receive an SMS-PP message before the UsimServiceTable has been loaded,
180c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // assume that the data download service is not present. This is very unlikely to
181c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // happen because the IMS connection will not be established until after the ISIM
182c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // records have been loaded, after the USIM service table has been loaded.
183c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (ust != null && ust.isAvailable(
184c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    UsimServiceTable.UsimService.DATA_DL_VIA_SMS_PP)) {
185c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.d(TAG, "Received SMS-PP data download, sending to UICC.");
186c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return mDataDownloadHandler.startDataDownload(sms);
187c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
188c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.d(TAG, "DATA_DL_VIA_SMS_PP service not available, storing message to UICC.");
189c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                String smsc = IccUtils.bytesToHexString(
190c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(
191c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                                sms.getServiceCenterAddress()));
192c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                mCm.writeSmsToSim(SmsManager.STATUS_ON_ICC_UNREAD, smsc,
193c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        IccUtils.bytesToHexString(sms.getPdu()),
194c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        obtainMessage(EVENT_WRITE_SMS_COMPLETE));
195c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return Activity.RESULT_OK;  // acknowledge after response from write to USIM
196c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
197c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
198c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
199c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (mSmsReceiveDisabled) {
200c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Device doesn't support SMS service,
201c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Log.d(TAG, "Received short message on device which doesn't support "
202c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    + "SMS service. Ignored.");
203c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return Intents.RESULT_SMS_HANDLED;
204c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
205c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
206c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        // Special case the message waiting indicator messages
207c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        boolean handled = false;
208c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (sms.isMWISetMessage()) {
209c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            mPhone.setVoiceMessageWaiting(1, -1);  // line 1: unknown number of msgs waiting
210c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            handled = sms.isMwiDontStore();
211c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (false) {
212c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled);
213c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
214c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else if (sms.isMWIClearMessage()) {
215c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            mPhone.setVoiceMessageWaiting(1, 0);   // line 1: no msgs waiting
216c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            handled = sms.isMwiDontStore();
217c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (false) {
218c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled);
219c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
220c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
221c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
222c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (handled) {
223c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return Intents.RESULT_SMS_HANDLED;
224c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
225c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
226c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (!mStorageMonitor.isStorageAvailable() &&
227c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                sms.getMessageClass() != SmsConstants.MessageClass.CLASS_0) {
228c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // It's a storable message and there's no storage available.  Bail.
229c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // (See TS 23.038 for a description of class 0 messages.)
230c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return Intents.RESULT_SMS_OUT_OF_MEMORY;
231c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
232c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
233c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return dispatchNormalMessage(smsb);
234c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
235c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
236c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
237c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
238c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    protected void sendData(String destAddr, String scAddr, int destPort,
239c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
240c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
241c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                scAddr, destAddr, destPort, data, (deliveryIntent != null));
242c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (pdu != null) {
243c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent,
244c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    destAddr);
245c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else {
246c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Log.e(TAG, "GsmSMSDispatcher.sendData(): getSubmitPdu() returned null");
247c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
248c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
249c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
250c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
251c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
252c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    protected void sendText(String destAddr, String scAddr, String text,
253c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            PendingIntent sentIntent, PendingIntent deliveryIntent) {
254c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
255c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                scAddr, destAddr, text, (deliveryIntent != null));
256c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (pdu != null) {
257c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent,
258c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    destAddr);
259c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else {
260c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Log.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");
261c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
262c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
263c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
264c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
265c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
266c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody,
267c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            boolean use7bitOnly) {
268c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        return SmsMessage.calculateLength(messageBody, use7bitOnly);
269c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
270c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
271c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
272c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
273c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    protected void sendNewSubmitPdu(String destinationAddress, String scAddress,
274c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String message, SmsHeader smsHeader, int encoding,
275c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart) {
276c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
277c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
278c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
279c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        if (pdu != null) {
280c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent,
281c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    destinationAddress);
282c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } else {
283c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Log.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
284c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
285c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
286c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
287c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
288c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
289c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    protected void sendSms(SmsTracker tracker) {
290c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        HashMap<String, Object> map = tracker.mData;
291c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
292c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte smsc[] = (byte[]) map.get("smsc");
293c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        byte pdu[] = (byte[]) map.get("pdu");
294c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
295c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
296c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        mCm.sendSMS(IccUtils.bytesToHexString(smsc), IccUtils.bytesToHexString(pdu), reply);
297c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
298c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
299c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /** {@inheritDoc} */
300c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    @Override
301c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    protected void acknowledgeLastIncomingSms(boolean success, int result, Message response) {
302c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        mCm.acknowledgeLastIncomingGsmSms(success, resultToCause(result), response);
303c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
304c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
305c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private static int resultToCause(int rc) {
306c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        switch (rc) {
307c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            case Activity.RESULT_OK:
308c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            case Intents.RESULT_SMS_HANDLED:
309c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Cause code is ignored on success.
310c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return 0;
311c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            case Intents.RESULT_SMS_OUT_OF_MEMORY:
312c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return CommandsInterface.GSM_SMS_FAIL_CAUSE_MEMORY_CAPACITY_EXCEEDED;
313c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            case Intents.RESULT_SMS_GENERIC_ERROR:
314c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            default:
315c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR;
316c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
317c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
318c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
319c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
320c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Holds all info about a message page needed to assemble a complete
321c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * concatenated message
322c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
323c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private static final class SmsCbConcatInfo {
324c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
325c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        private final SmsCbHeader mHeader;
326c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        private final SmsCbLocation mLocation;
327c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
328c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        public SmsCbConcatInfo(SmsCbHeader header, SmsCbLocation location) {
329c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            mHeader = header;
330c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            mLocation = location;
331c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
332c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
333c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        @Override
334c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        public int hashCode() {
335c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return (mHeader.getSerialNumber() * 31) + mLocation.hashCode();
336c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
337c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
338c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        @Override
339c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        public boolean equals(Object obj) {
340c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (obj instanceof SmsCbConcatInfo) {
341c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                SmsCbConcatInfo other = (SmsCbConcatInfo)obj;
342c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
343c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Two pages match if they have the same serial number (which includes the
344c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // geographical scope and update number), and both pages belong to the same
345c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // location (PLMN, plus LAC and CID if these are part of the geographical scope).
346c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                return mHeader.getSerialNumber() == other.mHeader.getSerialNumber()
347c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        && mLocation.equals(other.mLocation);
348c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
349c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
350c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return false;
351c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
352c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
353c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        /**
354c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * Compare the location code for this message to the current location code. The match is
355c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * relative to the geographical scope of the message, which determines whether the LAC
356c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * and Cell ID are saved in mLocation or set to -1 to match all values.
357c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         *
358c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @param plmn the current PLMN
359c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @param lac the current Location Area (GSM) or Service Area (UMTS)
360c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @param cid the current Cell ID
361c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         * @return true if this message is valid for the current location; false otherwise
362c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville         */
363c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        public boolean matchesLocation(String plmn, int lac, int cid) {
364c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            return mLocation.isInLocationArea(plmn, lac, cid);
365c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
366c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
367c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
368c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    // This map holds incomplete concatenated messages waiting for assembly
369c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private final HashMap<SmsCbConcatInfo, byte[][]> mSmsCbPageMap =
370c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            new HashMap<SmsCbConcatInfo, byte[][]>();
371c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
372c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    /**
373c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * Handle 3GPP format SMS-CB message.
374c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     * @param ar the AsyncResult containing the received PDUs
375c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville     */
376c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    private void handleBroadcastSms(AsyncResult ar) {
377c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        try {
378c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            byte[] receivedPdu = (byte[])ar.result;
379c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
380c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (false) {
381c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                for (int i = 0; i < receivedPdu.length; i += 8) {
382c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    StringBuilder sb = new StringBuilder("SMS CB pdu data: ");
383c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    for (int j = i; j < i + 8 && j < receivedPdu.length; j++) {
384c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        int b = receivedPdu[j] & 0xff;
385c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        if (b < 0x10) {
386c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                            sb.append('0');
387c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        }
388c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        sb.append(Integer.toHexString(b)).append(' ');
389c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    }
390c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    Log.d(TAG, sb.toString());
391c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
392c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
393c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
394c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            SmsCbHeader header = new SmsCbHeader(receivedPdu);
395c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            String plmn = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC);
3960fd49b6b31960a768702d202a066b1088ec489a1Alex Yakavenka            int lac = -1;
3970fd49b6b31960a768702d202a066b1088ec489a1Alex Yakavenka            int cid = -1;
3980fd49b6b31960a768702d202a066b1088ec489a1Alex Yakavenka            android.telephony.CellLocation cl = mPhone.getCellLocation();
3990fd49b6b31960a768702d202a066b1088ec489a1Alex Yakavenka            // Check if cell location is GsmCellLocation.  This is required to support
4000fd49b6b31960a768702d202a066b1088ec489a1Alex Yakavenka            // dual-mode devices such as CDMA/LTE devices that require support for
4010fd49b6b31960a768702d202a066b1088ec489a1Alex Yakavenka            // both 3GPP and 3GPP2 format messages
4020fd49b6b31960a768702d202a066b1088ec489a1Alex Yakavenka            if (cl instanceof GsmCellLocation) {
4030fd49b6b31960a768702d202a066b1088ec489a1Alex Yakavenka                GsmCellLocation cellLocation = (GsmCellLocation)cl;
4040fd49b6b31960a768702d202a066b1088ec489a1Alex Yakavenka                lac = cellLocation.getLac();
4050fd49b6b31960a768702d202a066b1088ec489a1Alex Yakavenka                cid = cellLocation.getCid();
4060fd49b6b31960a768702d202a066b1088ec489a1Alex Yakavenka            }
407c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
408c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            SmsCbLocation location;
409c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            switch (header.getGeographicalScope()) {
410c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                case SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE:
411c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    location = new SmsCbLocation(plmn, lac, -1);
412c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    break;
413c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
414c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE:
415c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE:
416c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    location = new SmsCbLocation(plmn, lac, cid);
417c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    break;
418c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
419c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                case SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE:
420c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                default:
421c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    location = new SmsCbLocation(plmn);
422c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    break;
423c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
424c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
425c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            byte[][] pdus;
426c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            int pageCount = header.getNumberOfPages();
427c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            if (pageCount > 1) {
428c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Multi-page message
429c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                SmsCbConcatInfo concatInfo = new SmsCbConcatInfo(header, location);
430c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
431c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Try to find other pages of the same message
432c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                pdus = mSmsCbPageMap.get(concatInfo);
433c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
434c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                if (pdus == null) {
435c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    // This is the first page of this message, make room for all
436c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    // pages and keep until complete
437c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    pdus = new byte[pageCount][];
438c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
439c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    mSmsCbPageMap.put(concatInfo, pdus);
440c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
441c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
442c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Page parameter is one-based
443c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                pdus[header.getPageIndex() - 1] = receivedPdu;
444c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
445c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                for (int i = 0; i < pdus.length; i++) {
446c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    if (pdus[i] == null) {
447c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        // Still missing pages, exit
448c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                        return;
449c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    }
450c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
451c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
452c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Message complete, remove and dispatch
453c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                mSmsCbPageMap.remove(concatInfo);
454c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            } else {
455c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                // Single page message
456c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                pdus = new byte[1][];
457c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                pdus[0] = receivedPdu;
458c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
459c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
460c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            SmsCbMessage message = GsmSmsCbMessage.createSmsCbMessage(header, location, pdus);
461c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            dispatchBroadcastMessage(message);
462c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
463c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // Remove messages that are out of scope to prevent the map from
464c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // growing indefinitely, containing incomplete messages that were
465c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            // never assembled
466c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Iterator<SmsCbConcatInfo> iter = mSmsCbPageMap.keySet().iterator();
467c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
468c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            while (iter.hasNext()) {
469c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                SmsCbConcatInfo info = iter.next();
470c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville
471c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                if (!info.matchesLocation(plmn, lac, cid)) {
472c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                    iter.remove();
473c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville                }
474c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            }
475c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        } catch (RuntimeException e) {
476c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville            Log.e(TAG, "Error in decoding SMS CB pdu", e);
477c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville        }
478c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville    }
479c38bb60d867c5d61d90b7179a9ed2b2d1848124fWink Saville}
480