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