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.cdma;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.res.Resources;
24 import android.os.Looper;
25 import android.os.Message;
26 import android.os.RemoteCallback;
27 import android.os.SystemProperties;
28 import android.provider.Telephony.Sms.Intents;
29 import android.telephony.PhoneNumberUtils;
30 import android.telephony.cdma.CdmaSmsCbProgramResults;
31 
32 import com.android.internal.telephony.CommandsInterface;
33 import com.android.internal.telephony.InboundSmsHandler;
34 import com.android.internal.telephony.InboundSmsTracker;
35 import com.android.internal.telephony.Phone;
36 import com.android.internal.telephony.SmsConstants;
37 import com.android.internal.telephony.SmsMessageBase;
38 import com.android.internal.telephony.SmsStorageMonitor;
39 import com.android.internal.telephony.TelephonyComponentFactory;
40 import com.android.internal.telephony.WspTypeDecoder;
41 import com.android.internal.telephony.cdma.sms.BearerData;
42 import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
43 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
44 import com.android.internal.util.HexDump;
45 
46 import java.io.ByteArrayOutputStream;
47 import java.io.DataOutputStream;
48 import java.io.IOException;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 
52 /**
53  * Subclass of {@link InboundSmsHandler} for 3GPP2 type messages.
54  */
55 public class CdmaInboundSmsHandler extends InboundSmsHandler {
56 
57     private final CdmaSMSDispatcher mSmsDispatcher;
58     private static CdmaCbTestBroadcastReceiver sTestBroadcastReceiver;
59     private static CdmaScpTestBroadcastReceiver sTestScpBroadcastReceiver;
60 
61     private byte[] mLastDispatchedSmsFingerprint;
62     private byte[] mLastAcknowledgedSmsFingerprint;
63 
64     // Callback used to process the result of an SCP message
65     private RemoteCallback mScpCallback;
66 
67     private final boolean mCheckForDuplicatePortsInOmadmWapPush = Resources.getSystem().getBoolean(
68             com.android.internal.R.bool.config_duplicate_port_omadm_wappush);
69 
70     // When TEST_MODE is on we allow the test intent to trigger an SMS CB alert
71     private static final boolean TEST_MODE = SystemProperties.getInt("ro.debuggable", 0) == 1;
72     private static final String TEST_ACTION = "com.android.internal.telephony.cdma"
73             + ".TEST_TRIGGER_CELL_BROADCAST";
74     private static final String SCP_TEST_ACTION = "com.android.internal.telephony.cdma"
75             + ".TEST_TRIGGER_SCP_MESSAGE";
76 
77     /**
78      * Create a new inbound SMS handler for CDMA.
79      */
CdmaInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor, Phone phone, CdmaSMSDispatcher smsDispatcher, Looper looper)80     private CdmaInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor,
81             Phone phone, CdmaSMSDispatcher smsDispatcher, Looper looper) {
82         super("CdmaInboundSmsHandler", context, storageMonitor, phone, looper);
83         mSmsDispatcher = smsDispatcher;
84         phone.mCi.setOnNewCdmaSms(getHandler(), EVENT_NEW_SMS, null);
85 
86         mCellBroadcastServiceManager.enable();
87         mScpCallback = new RemoteCallback(result -> {
88             if (result == null) {
89                 loge("SCP results error: missing extras");
90                 return;
91             }
92             String sender = result.getString("sender");
93             if (sender == null) {
94                 loge("SCP results error: missing sender extra.");
95                 return;
96             }
97             ArrayList<CdmaSmsCbProgramResults> results = result.getParcelableArrayList("results");
98             if (results == null) {
99                 loge("SCP results error: missing results extra.");
100                 return;
101             }
102 
103             BearerData bData = new BearerData();
104             bData.messageType = BearerData.MESSAGE_TYPE_SUBMIT;
105             bData.messageId = SmsMessage.getNextMessageId();
106             bData.serviceCategoryProgramResults = results;
107             byte[] encodedBearerData = BearerData.encode(bData);
108 
109             ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
110             DataOutputStream dos = new DataOutputStream(baos);
111             try {
112                 dos.writeInt(SmsEnvelope.TELESERVICE_SCPT);
113                 dos.writeInt(0); //servicePresent
114                 dos.writeInt(0); //serviceCategory
115                 CdmaSmsAddress destAddr = CdmaSmsAddress.parse(
116                         PhoneNumberUtils.cdmaCheckAndProcessPlusCodeForSms(sender));
117                 dos.write(destAddr.digitMode);
118                 dos.write(destAddr.numberMode);
119                 dos.write(destAddr.ton); // number_type
120                 dos.write(destAddr.numberPlan);
121                 dos.write(destAddr.numberOfDigits);
122                 dos.write(destAddr.origBytes, 0, destAddr.origBytes.length); // digits
123                 // Subaddress is not supported.
124                 dos.write(0); //subaddressType
125                 dos.write(0); //subaddr_odd
126                 dos.write(0); //subaddr_nbr_of_digits
127                 dos.write(encodedBearerData.length);
128                 dos.write(encodedBearerData, 0, encodedBearerData.length);
129                 // Ignore the RIL response. TODO: implement retry if SMS send fails.
130                 mPhone.mCi.sendCdmaSms(baos.toByteArray(), null);
131             } catch (IOException e) {
132                 loge("exception creating SCP results PDU", e);
133             } finally {
134                 try {
135                     dos.close();
136                 } catch (IOException ignored) {
137                 }
138             }
139         });
140         if (TEST_MODE) {
141             if (sTestBroadcastReceiver == null) {
142                 sTestBroadcastReceiver = new CdmaCbTestBroadcastReceiver();
143                 IntentFilter filter = new IntentFilter();
144                 filter.addAction(TEST_ACTION);
145                 context.registerReceiver(sTestBroadcastReceiver, filter,
146                         Context.RECEIVER_EXPORTED);
147             }
148             if (sTestScpBroadcastReceiver == null) {
149                 sTestScpBroadcastReceiver = new CdmaScpTestBroadcastReceiver();
150                 IntentFilter filter = new IntentFilter();
151                 filter.addAction(SCP_TEST_ACTION);
152                 context.registerReceiver(sTestScpBroadcastReceiver, filter,
153                         Context.RECEIVER_EXPORTED);
154             }
155         }
156     }
157 
158     /**
159      * Unregister for CDMA SMS.
160      */
161     @Override
onQuitting()162     protected void onQuitting() {
163         mPhone.mCi.unSetOnNewCdmaSms(getHandler());
164 
165         if (DBG) log("unregistered for 3GPP2 SMS");
166         super.onQuitting();
167     }
168 
169     /**
170      * Wait for state machine to enter startup state. We can't send any messages until then.
171      */
makeInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor, Phone phone, CdmaSMSDispatcher smsDispatcher, Looper looper)172     public static CdmaInboundSmsHandler makeInboundSmsHandler(Context context,
173             SmsStorageMonitor storageMonitor, Phone phone, CdmaSMSDispatcher smsDispatcher,
174             Looper looper) {
175         CdmaInboundSmsHandler handler = new CdmaInboundSmsHandler(context, storageMonitor,
176                 phone, smsDispatcher, looper);
177         handler.start();
178         return handler;
179     }
180 
181     /**
182      * Return true if this handler is for 3GPP2 messages; false for 3GPP format.
183      *
184      * @return true (3GPP2)
185      */
186     @Override
is3gpp2()187     protected boolean is3gpp2() {
188         return true;
189     }
190 
191     /**
192      * Process Cell Broadcast, Voicemail Notification, and other 3GPP/3GPP2-specific messages.
193      *
194      * @param smsb the SmsMessageBase object from the RIL
195      * @param smsSource the source of the SMS message
196      * @return true if the message was handled here; false to continue processing
197      */
198     @Override
dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource, int token)199     protected int dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource,
200             int token) {
201         SmsMessage sms = (SmsMessage) smsb;
202         boolean isBroadcastType = (SmsEnvelope.MESSAGE_TYPE_BROADCAST == sms.getMessageType());
203 
204         // Handle CMAS emergency broadcast messages.
205         if (isBroadcastType) {
206             log("Broadcast type message");
207             mCellBroadcastServiceManager.sendCdmaMessageToHandler(sms);
208             return Intents.RESULT_SMS_HANDLED;
209         }
210 
211         // Initialize fingerprint field, and see if we have a network duplicate SMS.
212         mLastDispatchedSmsFingerprint = sms.getIncomingSmsFingerprint();
213         if (mLastAcknowledgedSmsFingerprint != null &&
214                 Arrays.equals(mLastDispatchedSmsFingerprint, mLastAcknowledgedSmsFingerprint)) {
215             return Intents.RESULT_SMS_HANDLED;
216         }
217 
218         // Decode BD stream and set sms variables.
219         sms.parseSms();
220         int teleService = sms.getTeleService();
221 
222         switch (teleService) {
223             case SmsEnvelope.TELESERVICE_VMN:
224             case SmsEnvelope.TELESERVICE_MWI:
225                 // handle voicemail indication
226                 handleVoicemailTeleservice(sms, smsSource);
227                 return Intents.RESULT_SMS_HANDLED;
228 
229             case SmsEnvelope.TELESERVICE_WMT:
230             case SmsEnvelope.TELESERVICE_WEMT:
231                 if (sms.isStatusReportMessage()) {
232                     mSmsDispatcher.sendStatusReportMessage(sms);
233                     return Intents.RESULT_SMS_HANDLED;
234                 }
235                 break;
236 
237             case SmsEnvelope.TELESERVICE_SCPT:
238                 mCellBroadcastServiceManager.sendCdmaScpMessageToHandler(sms, mScpCallback);
239                 return Intents.RESULT_SMS_HANDLED;
240 
241             case SmsEnvelope.TELESERVICE_FDEA_WAP:
242                 if (!sms.preprocessCdmaFdeaWap()) {
243                     return Intents.RESULT_SMS_HANDLED;
244                 }
245                 teleService = SmsEnvelope.TELESERVICE_WAP;
246                 // fall through
247             case SmsEnvelope.TELESERVICE_WAP:
248                 // handled below, after storage check
249                 break;
250 
251             default:
252                 loge("unsupported teleservice 0x" + Integer.toHexString(teleService));
253                 return Intents.RESULT_SMS_UNSUPPORTED;
254         }
255 
256         if (!mStorageMonitor.isStorageAvailable() &&
257                 sms.getMessageClass() != SmsConstants.MessageClass.CLASS_0) {
258             // It's a storable message and there's no storage available.  Bail.
259             // (See C.S0015-B v2.0 for a description of "Immediate Display"
260             // messages, which we represent as CLASS_0.)
261             return Intents.RESULT_SMS_OUT_OF_MEMORY;
262         }
263 
264         if (SmsEnvelope.TELESERVICE_WAP == teleService) {
265             return processCdmaWapPdu(sms.getUserData(), sms.mMessageRef,
266                     sms.getOriginatingAddress(), sms.getDisplayOriginatingAddress(),
267                     sms.getTimestampMillis(), smsSource);
268         }
269 
270         return dispatchNormalMessage(smsb, smsSource);
271     }
272 
273     /**
274      * Send an acknowledge message.
275      *
276      * @param success  indicates that last message was successfully received.
277      * @param result   result code indicating any error
278      * @param response callback message sent when operation completes.
279      */
280     @Override
acknowledgeLastIncomingSms(boolean success, int result, Message response)281     protected void acknowledgeLastIncomingSms(boolean success, int result, Message response) {
282         int causeCode = resultToCause(result);
283         mPhone.mCi.acknowledgeLastIncomingCdmaSms(success, causeCode, response);
284 
285         if (causeCode == 0) {
286             mLastAcknowledgedSmsFingerprint = mLastDispatchedSmsFingerprint;
287         }
288         mLastDispatchedSmsFingerprint = null;
289     }
290 
291     /**
292      * Convert Android result code to CDMA SMS failure cause.
293      *
294      * @param rc the Android SMS intent result value
295      * @return 0 for success, or a CDMA SMS failure cause value
296      */
resultToCause(int rc)297     private static int resultToCause(int rc) {
298         switch (rc) {
299             case Activity.RESULT_OK:
300             case Intents.RESULT_SMS_HANDLED:
301                 // Cause code is ignored on success.
302                 return 0;
303             case Intents.RESULT_SMS_OUT_OF_MEMORY:
304                 return CommandsInterface.CDMA_SMS_FAIL_CAUSE_RESOURCE_SHORTAGE;
305             case Intents.RESULT_SMS_UNSUPPORTED:
306                 return CommandsInterface.CDMA_SMS_FAIL_CAUSE_INVALID_TELESERVICE_ID;
307             case Intents.RESULT_SMS_GENERIC_ERROR:
308             default:
309                 return CommandsInterface.CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM;
310         }
311     }
312 
313     /**
314      * Handle {@link SmsEnvelope#TELESERVICE_VMN} and {@link SmsEnvelope#TELESERVICE_MWI}.
315      *
316      * @param sms the message to process
317      */
handleVoicemailTeleservice(SmsMessage sms, @SmsSource int smsSource)318     private void handleVoicemailTeleservice(SmsMessage sms, @SmsSource int smsSource) {
319         int voicemailCount = sms.getNumOfVoicemails();
320         if (DBG) log("Voicemail count=" + voicemailCount);
321 
322         // range check
323         if (voicemailCount < 0) {
324             voicemailCount = -1;
325         } else if (voicemailCount > 99) {
326             // C.S0015-B v2, 4.5.12
327             // range: 0-99
328             voicemailCount = 99;
329         }
330         // update voice mail count in phone
331         mPhone.setVoiceMessageCount(voicemailCount);
332         // update metrics
333         addVoicemailSmsToMetrics(smsSource);
334     }
335 
336     /**
337      * Processes inbound messages that are in the WAP-WDP PDU format. See
338      * wap-259-wdp-20010614-a section 6.5 for details on the WAP-WDP PDU format.
339      * WDP segments are gathered until a datagram completes and gets dispatched.
340      *
341      * @param pdu The WAP-WDP PDU segment
342      * @return a result code from {@link android.provider.Telephony.Sms.Intents}, or
343      * {@link Activity#RESULT_OK} if the message has been broadcast
344      * to applications
345      */
processCdmaWapPdu(byte[] pdu, int referenceNumber, String address, String dispAddr, long timestamp, @SmsSource int smsSource)346     private int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address, String dispAddr,
347             long timestamp, @SmsSource int smsSource) {
348         int index = 0;
349 
350         int msgType = (0xFF & pdu[index++]);
351         if (msgType != 0) {
352             log("Received a WAP SMS which is not WDP. Discard.");
353             return Intents.RESULT_SMS_HANDLED;
354         }
355         int totalSegments = (0xFF & pdu[index++]);   // >= 1
356         int segment = (0xFF & pdu[index++]);         // >= 0
357 
358         if (segment >= totalSegments) {
359             loge("WDP bad segment #" + segment + " expecting 0-" + (totalSegments - 1));
360             return Intents.RESULT_SMS_HANDLED;
361         }
362 
363         // Only the first segment contains sourcePort and destination Port
364         int sourcePort = 0;
365         int destinationPort = 0;
366         if (segment == 0) {
367             //process WDP segment
368             sourcePort = (0xFF & pdu[index++]) << 8;
369             sourcePort |= 0xFF & pdu[index++];
370             destinationPort = (0xFF & pdu[index++]) << 8;
371             destinationPort |= 0xFF & pdu[index++];
372             // Some carriers incorrectly send duplicate port fields in omadm wap pushes.
373             // If configured, check for that here
374             if (mCheckForDuplicatePortsInOmadmWapPush) {
375                 if (checkDuplicatePortOmadmWapPush(pdu, index)) {
376                     index = index + 4; // skip duplicate port fields
377                 }
378             }
379         }
380 
381         // Lookup all other related parts
382         log("Received WAP PDU. Type = " + msgType + ", originator = " + address
383                 + ", src-port = " + sourcePort + ", dst-port = " + destinationPort
384                 + ", ID = " + referenceNumber + ", segment# = " + segment + '/' + totalSegments);
385 
386         // pass the user data portion of the PDU to the shared handler in SMSDispatcher
387         byte[] userData = new byte[pdu.length - index];
388         System.arraycopy(pdu, index, userData, 0, pdu.length - index);
389         InboundSmsTracker tracker = TelephonyComponentFactory.getInstance()
390                 .inject(InboundSmsTracker.class.getName()).makeInboundSmsTracker(mContext,
391                         userData, timestamp, destinationPort, true, address, dispAddr,
392                         referenceNumber,
393                         segment, totalSegments, true, HexDump.toHexString(userData),
394                         false /* isClass0 */,
395                         mPhone.getSubId(),
396                         smsSource);
397 
398         // de-duping is done only for text messages
399         return addTrackerToRawTableAndSendMessage(tracker, false /* don't de-dup */);
400     }
401 
402     /**
403      * Optional check to see if the received WapPush is an OMADM notification with erroneous
404      * extra port fields.
405      * - Some carriers make this mistake.
406      * ex: MSGTYPE-TotalSegments-CurrentSegment
407      * -SourcePortDestPort-SourcePortDestPort-OMADM PDU
408      *
409      * @param origPdu The WAP-WDP PDU segment
410      * @param index   Current Index while parsing the PDU.
411      * @return True if OrigPdu is OmaDM Push Message which has duplicate ports.
412      * False if OrigPdu is NOT OmaDM Push Message which has duplicate ports.
413      */
checkDuplicatePortOmadmWapPush(byte[] origPdu, int index)414     private static boolean checkDuplicatePortOmadmWapPush(byte[] origPdu, int index) {
415         index += 4;
416         byte[] omaPdu = new byte[origPdu.length - index];
417         System.arraycopy(origPdu, index, omaPdu, 0, omaPdu.length);
418 
419         WspTypeDecoder pduDecoder = new WspTypeDecoder(omaPdu);
420         int wspIndex = 2;
421 
422         // Process header length field
423         if (!pduDecoder.decodeUintvarInteger(wspIndex)) {
424             return false;
425         }
426 
427         wspIndex += pduDecoder.getDecodedDataLength();  // advance to next field
428 
429         // Process content type field
430         if (!pduDecoder.decodeContentType(wspIndex)) {
431             return false;
432         }
433 
434         String mimeType = pduDecoder.getValueString();
435         return (WspTypeDecoder.CONTENT_TYPE_B_PUSH_SYNCML_NOTI.equals(mimeType));
436     }
437 
438     /**
439      * Add voicemail indication SMS 0 to metrics.
440      */
addVoicemailSmsToMetrics(@msSource int smsSource)441     private void addVoicemailSmsToMetrics(@SmsSource int smsSource) {
442         mMetrics.writeIncomingVoiceMailSms(mPhone.getPhoneId(),
443                 android.telephony.SmsMessage.FORMAT_3GPP2);
444         mPhone.getSmsStats().onIncomingSmsVoicemail(true /* is3gpp2 */, smsSource);
445     }
446 
447     /**
448      * A broadcast receiver used for testing emergency cell broadcasts. To trigger test CDMA cell
449      * broadcasts with adb run e.g:
450      *
451      * adb shell am broadcast -a com.android.internal.telephony.cdma.TEST_TRIGGER_CELL_BROADCAST \
452      * --ei service_category 4097 \
453      * --es bearer_data_string 0003104D200801C00D010101510278000260A34C834E4208D327CF8882BC1A53A \
454      * 4CE8E8234FA4829CFAB52420873E1CF9D2674F410E7D59D52CA05A8274FA5524208716754A506620834A4DA9F \
455      * 3A0A0AB3AA499881A316A8284D41369D40
456      *
457      * adb shell am broadcast -a com.android.internal.telephony.cdma.TEST_TRIGGER_CELL_BROADCAST \
458      * --ei service_category 4097 \
459      * --es bearer_data_string 0003104D200801C00D010101510278000260A34C834E4208D327CF8882BC1A53A \
460      * 4CE8E8234FA4829CFAB52420873E1CF9D2674F410E7D59D52CA05A8274FA5524208716754A506620834A4DA9F \
461      * 3A0A0AB3AA499881A316A8284D41369D40 \
462      * --ei phone_id 0
463      */
464     private class CdmaCbTestBroadcastReceiver extends CbTestBroadcastReceiver {
465 
CdmaCbTestBroadcastReceiver()466         CdmaCbTestBroadcastReceiver() {
467             super(TEST_ACTION);
468         }
469 
470         @Override
handleTestAction(Intent intent)471         protected void handleTestAction(Intent intent) {
472             SmsEnvelope envelope = new SmsEnvelope();
473             // the CdmaSmsAddress is not used for a test cell broadcast message, but needs to be
474             // supplied to avoid a null pointer exception in the platform
475             CdmaSmsAddress nonNullAddress = new CdmaSmsAddress();
476             nonNullAddress.origBytes = new byte[]{(byte) 0xFF};
477             envelope.origAddress = nonNullAddress;
478 
479             // parse service category from intent
480             envelope.serviceCategory = intent.getIntExtra("service_category", -1);
481             if (envelope.serviceCategory == -1) {
482                 log("No service category, ignoring CB test intent");
483                 return;
484             }
485 
486             // parse bearer data from intent
487             String bearerDataString = intent.getStringExtra("bearer_data_string");
488             envelope.bearerData = decodeHexString(bearerDataString);
489             if (envelope.bearerData == null) {
490                 log("No bearer data, ignoring CB test intent");
491                 return;
492             }
493 
494             SmsMessage sms = new SmsMessage(new CdmaSmsAddress(), envelope);
495                 mCellBroadcastServiceManager.sendCdmaMessageToHandler(sms);
496         }
497     }
498 
499     /**
500      * A broadcast receiver used for testing CDMA SCP messages. To trigger test CDMA SCP messages
501      * with adb run e.g:
502      *
503      * adb shell am broadcast -a com.android.internal.telephony.cdma.TEST_TRIGGER_SCP_MESSAGE \
504      * --es originating_address_string 1234567890 \
505      * --es bearer_data_string 00031007B0122610880080B2091C5F1D3965DB95054D1CB2E1E883A6F41334E \
506      * 6CA830EEC882872DFC32F2E9E40
507      */
508     private class CdmaScpTestBroadcastReceiver extends CbTestBroadcastReceiver {
509 
CdmaScpTestBroadcastReceiver()510         CdmaScpTestBroadcastReceiver() {
511             super(SCP_TEST_ACTION);
512         }
513 
514         @Override
handleTestAction(Intent intent)515         protected void handleTestAction(Intent intent) {
516             SmsEnvelope envelope = new SmsEnvelope();
517             // the CdmaSmsAddress is not used for a test SCP message, but needs to be supplied to
518             // avoid a null pointer exception in the platform
519             CdmaSmsAddress nonNullAddress = new CdmaSmsAddress();
520             nonNullAddress.origBytes = new byte[]{(byte) 0xFF};
521             envelope.origAddress = nonNullAddress;
522 
523             // parse bearer data from intent
524             String bearerDataString = intent.getStringExtra("bearer_data_string");
525             envelope.bearerData = decodeHexString(bearerDataString);
526             if (envelope.bearerData == null) {
527                 log("No bearer data, ignoring SCP test intent");
528                 return;
529             }
530 
531             CdmaSmsAddress origAddr = new CdmaSmsAddress();
532             String addressString = intent.getStringExtra("originating_address_string");
533             origAddr.origBytes = decodeHexString(addressString);
534             if (origAddr.origBytes == null) {
535                 log("No address data, ignoring SCP test intent");
536                 return;
537             }
538             SmsMessage sms = new SmsMessage(origAddr, envelope);
539             sms.parseSms();
540             mCellBroadcastServiceManager.sendCdmaScpMessageToHandler(sms, mScpCallback);
541         }
542     }
543 }
544