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 
17 package com.android.internal.telephony.gsm;
18 
19 import android.app.Activity;
20 import android.compat.annotation.UnsupportedAppUsage;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.os.AsyncResult;
26 import android.os.Message;
27 import android.os.SystemProperties;
28 import android.provider.Telephony.Sms.Intents;
29 
30 import com.android.internal.telephony.CommandsInterface;
31 import com.android.internal.telephony.InboundSmsHandler;
32 import com.android.internal.telephony.Phone;
33 import com.android.internal.telephony.SmsConstants;
34 import com.android.internal.telephony.SmsHeader;
35 import com.android.internal.telephony.SmsMessageBase;
36 import com.android.internal.telephony.SmsStorageMonitor;
37 import com.android.internal.telephony.VisualVoicemailSmsFilter;
38 import com.android.internal.telephony.uicc.UsimServiceTable;
39 
40 /**
41  * This class broadcasts incoming SMS messages to interested apps after storing them in
42  * the SmsProvider "raw" table and ACKing them to the SMSC. After each message has been
43  */
44 public class GsmInboundSmsHandler extends InboundSmsHandler {
45 
46     private static BroadcastReceiver sTestBroadcastReceiver;
47     /** Handler for SMS-PP data download messages to UICC. */
48     private final UsimDataDownloadHandler mDataDownloadHandler;
49 
50     // When TEST_MODE is on we allow the test intent to trigger an SMS CB alert
51     private static final boolean TEST_MODE = SystemProperties.getInt("ro.debuggable", 0) == 1;
52     private static final String TEST_ACTION = "com.android.internal.telephony.gsm"
53             + ".TEST_TRIGGER_CELL_BROADCAST";
54 
55     /**
56      * Create a new GSM inbound SMS handler.
57      */
GsmInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor, Phone phone)58     private GsmInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor,
59             Phone phone) {
60         super("GsmInboundSmsHandler", context, storageMonitor, phone);
61         phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);
62         mDataDownloadHandler = new UsimDataDownloadHandler(phone.mCi, phone.getPhoneId());
63         mCellBroadcastServiceManager.enable();
64 
65         if (TEST_MODE) {
66             if (sTestBroadcastReceiver == null) {
67                 sTestBroadcastReceiver = new GsmCbTestBroadcastReceiver();
68                 IntentFilter filter = new IntentFilter();
69                 filter.addAction(TEST_ACTION);
70                 context.registerReceiver(sTestBroadcastReceiver, filter);
71             }
72         }
73     }
74 
75 
76     /**
77      * A broadcast receiver used for testing emergency cell broadcasts. To trigger test GSM cell
78      * broadcasts with adb run e.g:
79      *
80      * adb shell am broadcast -a com.android.internal.telephony.gsm.TEST_TRIGGER_CELL_BROADCAST \
81      * --es pdu_string  0000110011010D0A5BAE57CE770C531790E85C716CBF3044573065B9306757309707767 \
82      * A751F30025F37304463FA308C306B5099304830664E0B30553044FF086C178C615E81FF09000000000000000 \
83      * 0000000000000
84      *
85      * adb shell am broadcast -a com.android.internal.telephony.gsm.TEST_TRIGGER_CELL_BROADCAST \
86      * --es pdu_string  0000110011010D0A5BAE57CE770C531790E85C716CBF3044573065B9306757309707767 \
87      * A751F30025F37304463FA308C306B5099304830664E0B30553044FF086C178C615E81FF09000000000000000 \
88      * 0000000000000 --ei phone_id 0
89      */
90     private class GsmCbTestBroadcastReceiver extends CbTestBroadcastReceiver {
91 
GsmCbTestBroadcastReceiver()92         GsmCbTestBroadcastReceiver() {
93             super(TEST_ACTION);
94         }
95 
96         @Override
handleTestAction(Intent intent)97         protected void handleTestAction(Intent intent) {
98             byte[] smsPdu = intent.getByteArrayExtra("pdu");
99             if (smsPdu == null) {
100                 String pduString = intent.getStringExtra("pdu_string");
101                 smsPdu = decodeHexString(pduString);
102             }
103             if (smsPdu == null) {
104                 log("No pdu or pdu_string extra, ignoring CB test intent");
105                 return;
106             }
107 
108             // Return early if phone_id is explicilty included and does not match mPhone.
109             // If phone_id extra is not included, continue.
110             int phoneId = mPhone.getPhoneId();
111             if (intent.getIntExtra("phone_id", phoneId) != phoneId) {
112                 return;
113             }
114             Message m = Message.obtain();
115             AsyncResult.forMessage(m, smsPdu, null);
116             mCellBroadcastServiceManager.sendGsmMessageToHandler(m);
117         }
118     }
119 
120     /**
121      * Unregister for GSM SMS.
122      */
123     @Override
onQuitting()124     protected void onQuitting() {
125         mPhone.mCi.unSetOnNewGsmSms(getHandler());
126 
127         if (DBG) log("unregistered for 3GPP SMS");
128         super.onQuitting();     // release wakelock
129     }
130 
131     /**
132      * Wait for state machine to enter startup state. We can't send any messages until then.
133      */
makeInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor, Phone phone)134     public static GsmInboundSmsHandler makeInboundSmsHandler(Context context,
135             SmsStorageMonitor storageMonitor, Phone phone) {
136         GsmInboundSmsHandler handler = new GsmInboundSmsHandler(context, storageMonitor, phone);
137         handler.start();
138         return handler;
139     }
140 
141     /**
142      * Return true if this handler is for 3GPP2 messages; false for 3GPP format.
143      *
144      * @return false (3GPP)
145      */
146     @Override
is3gpp2()147     protected boolean is3gpp2() {
148         return false;
149     }
150 
151     /**
152      * Handle type zero, SMS-PP data download, and 3GPP/CPHS MWI type SMS. Normal SMS messages
153      * are handled by {@link #dispatchNormalMessage} in parent class.
154      *
155      * @param smsb the SmsMessageBase object from the RIL
156      * @return a result code from {@link android.provider.Telephony.Sms.Intents},
157      * or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
158      */
159     @Override
dispatchMessageRadioSpecific(SmsMessageBase smsb)160     protected int dispatchMessageRadioSpecific(SmsMessageBase smsb) {
161         SmsMessage sms = (SmsMessage) smsb;
162 
163         if (sms.isTypeZero()) {
164             // Some carriers will send visual voicemail SMS as type zero.
165             int destPort = -1;
166             SmsHeader smsHeader = sms.getUserDataHeader();
167             if (smsHeader != null && smsHeader.portAddrs != null) {
168                 // The message was sent to a port.
169                 destPort = smsHeader.portAddrs.destPort;
170             }
171             VisualVoicemailSmsFilter
172                     .filter(mContext, new byte[][]{sms.getPdu()}, SmsConstants.FORMAT_3GPP,
173                             destPort, mPhone.getSubId());
174             // As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be
175             // Displayed/Stored/Notified. They should only be acknowledged.
176             log("Received short message type 0, Don't display or store it. Send Ack");
177             addSmsTypeZeroToMetrics();
178             return Intents.RESULT_SMS_HANDLED;
179         }
180 
181         // Send SMS-PP data download messages to UICC. See 3GPP TS 31.111 section 7.1.1.
182         if (sms.isUsimDataDownload()) {
183             UsimServiceTable ust = mPhone.getUsimServiceTable();
184             return mDataDownloadHandler.handleUsimDataDownload(ust, sms);
185         }
186 
187         boolean handled = false;
188         if (sms.isMWISetMessage()) {
189             updateMessageWaitingIndicator(sms.getNumOfVoicemails());
190             handled = sms.isMwiDontStore();
191             if (DBG) log("Received voice mail indicator set SMS shouldStore=" + !handled);
192         } else if (sms.isMWIClearMessage()) {
193             updateMessageWaitingIndicator(0);
194             handled = sms.isMwiDontStore();
195             if (DBG) log("Received voice mail indicator clear SMS shouldStore=" + !handled);
196         }
197         if (handled) {
198             addVoicemailSmsToMetrics();
199             return Intents.RESULT_SMS_HANDLED;
200         }
201 
202         if (!mStorageMonitor.isStorageAvailable() &&
203                 sms.getMessageClass() != SmsConstants.MessageClass.CLASS_0) {
204             // It's a storable message and there's no storage available.  Bail.
205             // (See TS 23.038 for a description of class 0 messages.)
206             return Intents.RESULT_SMS_OUT_OF_MEMORY;
207         }
208 
209         return dispatchNormalMessage(smsb);
210     }
211 
updateMessageWaitingIndicator(int voicemailCount)212     private void updateMessageWaitingIndicator(int voicemailCount) {
213         // range check
214         if (voicemailCount < 0) {
215             voicemailCount = -1;
216         } else if (voicemailCount > 0xff) {
217             // TS 23.040 9.2.3.24.2
218             // "The value 255 shall be taken to mean 255 or greater"
219             voicemailCount = 0xff;
220         }
221         // update voice mail count in Phone
222         mPhone.setVoiceMessageCount(voicemailCount);
223     }
224 
225     /**
226      * Send an acknowledge message.
227      *
228      * @param success indicates that last message was successfully received.
229      * @param result result code indicating any error
230      * @param response callback message sent when operation completes.
231      */
232     @UnsupportedAppUsage
233     @Override
acknowledgeLastIncomingSms(boolean success, int result, Message response)234     protected void acknowledgeLastIncomingSms(boolean success, int result, Message response) {
235         mPhone.mCi.acknowledgeLastIncomingGsmSms(success, resultToCause(result), response);
236     }
237 
238     /**
239      * Convert Android result code to 3GPP SMS failure cause.
240      *
241      * @param rc the Android SMS intent result value
242      * @return 0 for success, or a 3GPP SMS failure cause value
243      */
resultToCause(int rc)244     private static int resultToCause(int rc) {
245         switch (rc) {
246             case Activity.RESULT_OK:
247             case Intents.RESULT_SMS_HANDLED:
248                 // Cause code is ignored on success.
249                 return 0;
250             case Intents.RESULT_SMS_OUT_OF_MEMORY:
251                 return CommandsInterface.GSM_SMS_FAIL_CAUSE_MEMORY_CAPACITY_EXCEEDED;
252             case Intents.RESULT_SMS_GENERIC_ERROR:
253             default:
254                 return CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR;
255         }
256     }
257 
258     /**
259      * Add SMS of type 0 to metrics.
260      */
addSmsTypeZeroToMetrics()261     private void addSmsTypeZeroToMetrics() {
262         mMetrics.writeIncomingSmsTypeZero(mPhone.getPhoneId(),
263                 android.telephony.SmsMessage.FORMAT_3GPP);
264     }
265 
266     /**
267      * Add voicemail indication SMS 0 to metrics.
268      */
addVoicemailSmsToMetrics()269     private void addVoicemailSmsToMetrics() {
270         mMetrics.writeIncomingVoiceMailSms(mPhone.getPhoneId(),
271                 android.telephony.SmsMessage.FORMAT_3GPP);
272     }
273 }
274