1/*
2 * Copyright (C) 2013 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.gsm;
18
19import android.app.Activity;
20import android.content.Context;
21import android.os.Message;
22import android.provider.Telephony.Sms.Intents;
23
24import com.android.internal.telephony.CommandsInterface;
25import com.android.internal.telephony.InboundSmsHandler;
26import com.android.internal.telephony.Phone;
27import com.android.internal.telephony.SmsConstants;
28import com.android.internal.telephony.SmsHeader;
29import com.android.internal.telephony.SmsMessageBase;
30import com.android.internal.telephony.SmsStorageMonitor;
31import com.android.internal.telephony.VisualVoicemailSmsFilter;
32import com.android.internal.telephony.uicc.IccRecords;
33import com.android.internal.telephony.uicc.UiccController;
34import com.android.internal.telephony.uicc.UsimServiceTable;
35
36/**
37 * This class broadcasts incoming SMS messages to interested apps after storing them in
38 * the SmsProvider "raw" table and ACKing them to the SMSC. After each message has been
39 */
40public class GsmInboundSmsHandler extends InboundSmsHandler {
41
42    /** Handler for SMS-PP data download messages to UICC. */
43    private final UsimDataDownloadHandler mDataDownloadHandler;
44
45    /**
46     * Create a new GSM inbound SMS handler.
47     */
48    private GsmInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor,
49            Phone phone) {
50        super("GsmInboundSmsHandler", context, storageMonitor, phone,
51                GsmCellBroadcastHandler.makeGsmCellBroadcastHandler(context, phone));
52        phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);
53        mDataDownloadHandler = new UsimDataDownloadHandler(phone.mCi);
54    }
55
56    /**
57     * Unregister for GSM SMS.
58     */
59    @Override
60    protected void onQuitting() {
61        mPhone.mCi.unSetOnNewGsmSms(getHandler());
62        mCellBroadcastHandler.dispose();
63
64        if (DBG) log("unregistered for 3GPP SMS");
65        super.onQuitting();     // release wakelock
66    }
67
68    /**
69     * Wait for state machine to enter startup state. We can't send any messages until then.
70     */
71    public static GsmInboundSmsHandler makeInboundSmsHandler(Context context,
72            SmsStorageMonitor storageMonitor, Phone phone) {
73        GsmInboundSmsHandler handler = new GsmInboundSmsHandler(context, storageMonitor, phone);
74        handler.start();
75        return handler;
76    }
77
78    /**
79     * Return true if this handler is for 3GPP2 messages; false for 3GPP format.
80     * @return false (3GPP)
81     */
82    @Override
83    protected boolean is3gpp2() {
84        return false;
85    }
86
87    /**
88     * Handle type zero, SMS-PP data download, and 3GPP/CPHS MWI type SMS. Normal SMS messages
89     * are handled by {@link #dispatchNormalMessage} in parent class.
90     *
91     * @param smsb the SmsMessageBase object from the RIL
92     * @return a result code from {@link android.provider.Telephony.Sms.Intents},
93     *  or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
94     */
95    @Override
96    protected int dispatchMessageRadioSpecific(SmsMessageBase smsb) {
97        SmsMessage sms = (SmsMessage) smsb;
98
99        if (sms.isTypeZero()) {
100            // Some carriers will send visual voicemail SMS as type zero.
101            int destPort = -1;
102            SmsHeader smsHeader = sms.getUserDataHeader();
103            if (smsHeader != null && smsHeader.portAddrs != null) {
104                // The message was sent to a port.
105                destPort = smsHeader.portAddrs.destPort;
106            }
107            VisualVoicemailSmsFilter
108                    .filter(mContext, new byte[][]{sms.getPdu()}, SmsConstants.FORMAT_3GPP,
109                            destPort, mPhone.getSubId());
110            // As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be
111            // Displayed/Stored/Notified. They should only be acknowledged.
112            log("Received short message type 0, Don't display or store it. Send Ack");
113            return Intents.RESULT_SMS_HANDLED;
114        }
115
116        // Send SMS-PP data download messages to UICC. See 3GPP TS 31.111 section 7.1.1.
117        if (sms.isUsimDataDownload()) {
118            UsimServiceTable ust = mPhone.getUsimServiceTable();
119            return mDataDownloadHandler.handleUsimDataDownload(ust, sms);
120        }
121
122        boolean handled = false;
123        if (sms.isMWISetMessage()) {
124            updateMessageWaitingIndicator(sms.getNumOfVoicemails());
125            handled = sms.isMwiDontStore();
126            if (DBG) log("Received voice mail indicator set SMS shouldStore=" + !handled);
127        } else if (sms.isMWIClearMessage()) {
128            updateMessageWaitingIndicator(0);
129            handled = sms.isMwiDontStore();
130            if (DBG) log("Received voice mail indicator clear SMS shouldStore=" + !handled);
131        }
132        if (handled) {
133            return Intents.RESULT_SMS_HANDLED;
134        }
135
136        if (!mStorageMonitor.isStorageAvailable() &&
137                sms.getMessageClass() != SmsConstants.MessageClass.CLASS_0) {
138            // It's a storable message and there's no storage available.  Bail.
139            // (See TS 23.038 for a description of class 0 messages.)
140            return Intents.RESULT_SMS_OUT_OF_MEMORY;
141        }
142
143        return dispatchNormalMessage(smsb);
144    }
145
146    private void updateMessageWaitingIndicator(int voicemailCount) {
147        // range check
148        if (voicemailCount < 0) {
149            voicemailCount = -1;
150        } else if (voicemailCount > 0xff) {
151            // TS 23.040 9.2.3.24.2
152            // "The value 255 shall be taken to mean 255 or greater"
153            voicemailCount = 0xff;
154        }
155        // update voice mail count in Phone
156        mPhone.setVoiceMessageCount(voicemailCount);
157        // store voice mail count in SIM & shared preferences
158        IccRecords records = UiccController.getInstance().getIccRecords(
159                mPhone.getPhoneId(), UiccController.APP_FAM_3GPP);
160        if (records != null) {
161            log("updateMessageWaitingIndicator: updating SIM Records");
162            records.setVoiceMessageWaiting(1, voicemailCount);
163        } else {
164            log("updateMessageWaitingIndicator: SIM Records not found");
165        }
166    }
167
168    /**
169     * Send an acknowledge message.
170     * @param success indicates that last message was successfully received.
171     * @param result result code indicating any error
172     * @param response callback message sent when operation completes.
173     */
174    @Override
175    protected void acknowledgeLastIncomingSms(boolean success, int result, Message response) {
176        mPhone.mCi.acknowledgeLastIncomingGsmSms(success, resultToCause(result), response);
177    }
178
179    /**
180     * Called when the phone changes the default method updates mPhone
181     * mStorageMonitor and mCellBroadcastHandler.updatePhoneObject.
182     * Override if different or other behavior is desired.
183     *
184     * @param phone
185     */
186    @Override
187    protected void onUpdatePhoneObject(Phone phone) {
188        super.onUpdatePhoneObject(phone);
189        log("onUpdatePhoneObject: dispose of old CellBroadcastHandler and make a new one");
190        mCellBroadcastHandler.dispose();
191        mCellBroadcastHandler = GsmCellBroadcastHandler
192                .makeGsmCellBroadcastHandler(mContext, phone);
193    }
194
195    /**
196     * Convert Android result code to 3GPP SMS failure cause.
197     * @param rc the Android SMS intent result value
198     * @return 0 for success, or a 3GPP SMS failure cause value
199     */
200    private static int resultToCause(int rc) {
201        switch (rc) {
202            case Activity.RESULT_OK:
203            case Intents.RESULT_SMS_HANDLED:
204                // Cause code is ignored on success.
205                return 0;
206            case Intents.RESULT_SMS_OUT_OF_MEMORY:
207                return CommandsInterface.GSM_SMS_FAIL_CAUSE_MEMORY_CAPACITY_EXCEEDED;
208            case Intents.RESULT_SMS_GENERIC_ERROR:
209            default:
210                return CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR;
211        }
212    }
213}
214