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