1 /*
2  * Copyright (C) 2011 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.os.AsyncResult;
21 import android.os.Handler;
22 import android.os.Message;
23 import android.provider.Telephony.Sms.Intents;
24 import android.telephony.PhoneNumberUtils;
25 import android.telephony.SmsManager;
26 
27 import com.android.internal.telephony.CommandsInterface;
28 import com.android.internal.telephony.InboundSmsHandler;
29 import com.android.internal.telephony.PhoneFactory;
30 import com.android.internal.telephony.cat.ComprehensionTlvTag;
31 import com.android.internal.telephony.metrics.TelephonyMetrics;
32 import com.android.internal.telephony.uicc.IccIoResult;
33 import com.android.internal.telephony.uicc.IccUtils;
34 import com.android.internal.telephony.uicc.UsimServiceTable;
35 import com.android.telephony.Rlog;
36 
37 /**
38  * Handler for SMS-PP data download messages.
39  * See 3GPP TS 31.111 section 7.1.1
40  */
41 public class UsimDataDownloadHandler extends Handler {
42     private static final String TAG = "UsimDataDownloadHandler";
43 
44     /** BER-TLV tag for SMS-PP download. TS 31.111 section 9.1. */
45     private static final int BER_SMS_PP_DOWNLOAD_TAG      = 0xd1;
46 
47     /** Device identity value for UICC (destination). */
48     private static final int DEV_ID_UICC        = 0x81;
49 
50     /** Device identity value for network (source). */
51     private static final int DEV_ID_NETWORK     = 0x83;
52 
53     /** Message containing new SMS-PP message to process. */
54     private static final int EVENT_START_DATA_DOWNLOAD = 1;
55 
56     /** Response to SMS-PP download envelope command. */
57     private static final int EVENT_SEND_ENVELOPE_RESPONSE = 2;
58 
59     /** Result of writing SM to UICC (when SMS-PP service is not available). */
60     private static final int EVENT_WRITE_SMS_COMPLETE = 3;
61 
62     private final CommandsInterface mCi;
63     private final int mPhoneId;
64 
UsimDataDownloadHandler(CommandsInterface commandsInterface, int phoneId)65     public UsimDataDownloadHandler(CommandsInterface commandsInterface, int phoneId) {
66         mCi = commandsInterface;
67         mPhoneId = phoneId;
68     }
69 
70     /**
71      * Handle SMS-PP data download messages. Normally these are automatically handled by the
72      * radio, but we may have to deal with this type of SM arriving via the IMS stack. If the
73      * data download service is not enabled, try to write to the USIM as an SMS, and send the
74      * UICC response as the acknowledgment to the SMSC.
75      *
76      * @param ust the UsimServiceTable, to check if data download is enabled
77      * @param smsMessage the SMS message to process
78      * @param smsSource the source of the SMS message
79      * @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure
80      */
handleUsimDataDownload(UsimServiceTable ust, SmsMessage smsMessage, @InboundSmsHandler.SmsSource int smsSource)81     int handleUsimDataDownload(UsimServiceTable ust, SmsMessage smsMessage,
82             @InboundSmsHandler.SmsSource int smsSource) {
83         // If we receive an SMS-PP message before the UsimServiceTable has been loaded,
84         // assume that the data download service is not present. This is very unlikely to
85         // happen because the IMS connection will not be established until after the ISIM
86         // records have been loaded, after the USIM service table has been loaded.
87         if (ust != null && ust.isAvailable(
88                 UsimServiceTable.UsimService.DATA_DL_VIA_SMS_PP)) {
89             Rlog.d(TAG, "Received SMS-PP data download, sending to UICC.");
90             return startDataDownload(smsMessage, smsSource);
91         } else {
92             Rlog.d(TAG, "DATA_DL_VIA_SMS_PP service not available, storing message to UICC.");
93             String smsc = IccUtils.bytesToHexString(
94                     PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(
95                             smsMessage.getServiceCenterAddress()));
96             mCi.writeSmsToSim(SmsManager.STATUS_ON_ICC_UNREAD, smsc,
97                     IccUtils.bytesToHexString(smsMessage.getPdu()),
98                     obtainMessage(EVENT_WRITE_SMS_COMPLETE));
99             addUsimDataDownloadToMetrics(false, smsSource);
100             return Activity.RESULT_OK;  // acknowledge after response from write to USIM
101         }
102 
103     }
104 
105     /**
106      * Start an SMS-PP data download for the specified message. Can be called from a different
107      * thread than this Handler is running on.
108      *
109      * @param smsMessage the message to process
110      * @param smsSource the source of the SMS message
111      * @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure
112      */
startDataDownload(SmsMessage smsMessage, @InboundSmsHandler.SmsSource int smsSource)113     public int startDataDownload(SmsMessage smsMessage,
114             @InboundSmsHandler.SmsSource int smsSource) {
115         if (sendMessage(obtainMessage(EVENT_START_DATA_DOWNLOAD,
116                 smsSource, 0 /* unused */, smsMessage))) {
117             return Activity.RESULT_OK;  // we will send SMS ACK/ERROR based on UICC response
118         } else {
119             Rlog.e(TAG, "startDataDownload failed to send message to start data download.");
120             return Intents.RESULT_SMS_GENERIC_ERROR;
121         }
122     }
123 
handleDataDownload(SmsMessage smsMessage, @InboundSmsHandler.SmsSource int smsSource)124     private void handleDataDownload(SmsMessage smsMessage,
125             @InboundSmsHandler.SmsSource int smsSource) {
126         int dcs = smsMessage.getDataCodingScheme();
127         int pid = smsMessage.getProtocolIdentifier();
128         byte[] pdu = smsMessage.getPdu();           // includes SC address
129 
130         int scAddressLength = pdu[0] & 0xff;
131         int tpduIndex = scAddressLength + 1;        // start of TPDU
132         int tpduLength = pdu.length - tpduIndex;
133 
134         int bodyLength = getEnvelopeBodyLength(scAddressLength, tpduLength);
135 
136         // Add 1 byte for SMS-PP download tag and 1-2 bytes for BER-TLV length.
137         // See ETSI TS 102 223 Annex C for encoding of length and tags.
138         int totalLength = bodyLength + 1 + (bodyLength > 127 ? 2 : 1);
139 
140         byte[] envelope = new byte[totalLength];
141         int index = 0;
142 
143         // SMS-PP download tag and length (assumed to be < 256 bytes).
144         envelope[index++] = (byte) BER_SMS_PP_DOWNLOAD_TAG;
145         if (bodyLength > 127) {
146             envelope[index++] = (byte) 0x81;    // length 128-255 encoded as 0x81 + length
147         }
148         envelope[index++] = (byte) bodyLength;
149 
150         // Device identities TLV
151         envelope[index++] = (byte) (0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value());
152         envelope[index++] = (byte) 2;
153         envelope[index++] = (byte) DEV_ID_NETWORK;
154         envelope[index++] = (byte) DEV_ID_UICC;
155 
156         // Address TLV (if present). Encoded length is assumed to be < 127 bytes.
157         if (scAddressLength != 0) {
158             envelope[index++] = (byte) ComprehensionTlvTag.ADDRESS.value();
159             envelope[index++] = (byte) scAddressLength;
160             System.arraycopy(pdu, 1, envelope, index, scAddressLength);
161             index += scAddressLength;
162         }
163 
164         // SMS TPDU TLV. Length is assumed to be < 256 bytes.
165         envelope[index++] = (byte) (0x80 | ComprehensionTlvTag.SMS_TPDU.value());
166         if (tpduLength > 127) {
167             envelope[index++] = (byte) 0x81;    // length 128-255 encoded as 0x81 + length
168         }
169         envelope[index++] = (byte) tpduLength;
170         System.arraycopy(pdu, tpduIndex, envelope, index, tpduLength);
171         index += tpduLength;
172 
173         // Verify that we calculated the payload size correctly.
174         if (index != envelope.length) {
175             Rlog.e(TAG, "startDataDownload() calculated incorrect envelope length, aborting.");
176             acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR);
177             addUsimDataDownloadToMetrics(false, smsSource);
178             return;
179         }
180 
181         String encodedEnvelope = IccUtils.bytesToHexString(envelope);
182         mCi.sendEnvelopeWithStatus(encodedEnvelope, obtainMessage(
183                 EVENT_SEND_ENVELOPE_RESPONSE, new int[]{ dcs, pid }));
184 
185         addUsimDataDownloadToMetrics(true, smsSource);
186     }
187 
188     /**
189      * Return the size in bytes of the envelope to send to the UICC, excluding the
190      * SMS-PP download tag byte and length byte(s). If the size returned is <= 127,
191      * the BER-TLV length will be encoded in 1 byte, otherwise 2 bytes are required.
192      *
193      * @param scAddressLength the length of the SMSC address, or zero if not present
194      * @param tpduLength the length of the TPDU from the SMS-PP message
195      * @return the number of bytes to allocate for the envelope command
196      */
getEnvelopeBodyLength(int scAddressLength, int tpduLength)197     private static int getEnvelopeBodyLength(int scAddressLength, int tpduLength) {
198         // Add 4 bytes for device identities TLV + 1 byte for SMS TPDU tag byte
199         int length = tpduLength + 5;
200         // Add 1 byte for TPDU length, or 2 bytes if length > 127
201         length += (tpduLength > 127 ? 2 : 1);
202         // Add length of address tag, if present (+ 2 bytes for tag and length)
203         if (scAddressLength != 0) {
204             length = length + 2 + scAddressLength;
205         }
206         return length;
207     }
208 
209     /**
210      * Handle the response to the ENVELOPE command.
211      * @param response UICC response encoded as hexadecimal digits. First two bytes are the
212      *  UICC SW1 and SW2 status bytes.
213      */
sendSmsAckForEnvelopeResponse(IccIoResult response, int dcs, int pid)214     private void sendSmsAckForEnvelopeResponse(IccIoResult response, int dcs, int pid) {
215         int sw1 = response.sw1;
216         int sw2 = response.sw2;
217 
218         boolean success;
219         if ((sw1 == 0x90 && sw2 == 0x00) || sw1 == 0x91) {
220             Rlog.d(TAG, "USIM data download succeeded: " + response.toString());
221             success = true;
222         } else if (sw1 == 0x93 && sw2 == 0x00) {
223             Rlog.e(TAG, "USIM data download failed: Toolkit busy");
224             acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_APP_TOOLKIT_BUSY);
225             return;
226         } else if (sw1 == 0x62 || sw1 == 0x63) {
227             Rlog.e(TAG, "USIM data download failed: " + response.toString());
228             success = false;
229         } else {
230             Rlog.e(TAG, "Unexpected SW1/SW2 response from UICC: " + response.toString());
231             success = false;
232         }
233 
234         byte[] responseBytes = response.payload;
235         if (responseBytes == null || responseBytes.length == 0) {
236             if (success) {
237                 mCi.acknowledgeLastIncomingGsmSms(true, 0, null);
238             } else {
239                 acknowledgeSmsWithError(
240                         CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR);
241             }
242             return;
243         }
244 
245         byte[] smsAckPdu;
246         int index = 0;
247         if (success) {
248             smsAckPdu = new byte[responseBytes.length + 5];
249             smsAckPdu[index++] = 0x00;      // TP-MTI, TP-UDHI
250             smsAckPdu[index++] = 0x07;      // TP-PI: TP-PID, TP-DCS, TP-UDL present
251         } else {
252             smsAckPdu = new byte[responseBytes.length + 6];
253             smsAckPdu[index++] = 0x00;      // TP-MTI, TP-UDHI
254             smsAckPdu[index++] = (byte)
255                     CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR;  // TP-FCS
256             smsAckPdu[index++] = 0x07;      // TP-PI: TP-PID, TP-DCS, TP-UDL present
257         }
258 
259         smsAckPdu[index++] = (byte) pid;
260         smsAckPdu[index++] = (byte) dcs;
261 
262         if (is7bitDcs(dcs)) {
263             int septetCount = responseBytes.length * 8 / 7;
264             smsAckPdu[index++] = (byte) septetCount;
265         } else {
266             smsAckPdu[index++] = (byte) responseBytes.length;
267         }
268 
269         System.arraycopy(responseBytes, 0, smsAckPdu, index, responseBytes.length);
270 
271         mCi.acknowledgeIncomingGsmSmsWithPdu(success,
272                 IccUtils.bytesToHexString(smsAckPdu), null);
273     }
274 
acknowledgeSmsWithError(int cause)275     private void acknowledgeSmsWithError(int cause) {
276         mCi.acknowledgeLastIncomingGsmSms(false, cause, null);
277     }
278 
279     /**
280      * Returns whether the DCS is 7 bit. If so, set TP-UDL to the septet count of TP-UD;
281      * otherwise, set TP-UDL to the octet count of TP-UD.
282      * @param dcs the TP-Data-Coding-Scheme field from the original download SMS
283      * @return true if the DCS specifies 7 bit encoding; false otherwise
284      */
is7bitDcs(int dcs)285     private static boolean is7bitDcs(int dcs) {
286         // See 3GPP TS 23.038 section 4
287         return ((dcs & 0x8C) == 0x00) || ((dcs & 0xF4) == 0xF0);
288     }
289 
290     /**
291      * Add the SMS-PP data to the telephony metrics, indicating if the message was forwarded
292      * to the USIM. The metrics does not cover the case where the SMS-PP might be rejected
293      * by the USIM itself.
294      */
addUsimDataDownloadToMetrics(boolean result, @InboundSmsHandler.SmsSource int smsSource)295     private void addUsimDataDownloadToMetrics(boolean result,
296             @InboundSmsHandler.SmsSource int smsSource) {
297         TelephonyMetrics metrics = TelephonyMetrics.getInstance();
298         metrics.writeIncomingSMSPP(mPhoneId, android.telephony.SmsMessage.FORMAT_3GPP, result);
299         PhoneFactory.getPhone(mPhoneId).getSmsStats().onIncomingSmsPP(smsSource, result);
300     }
301 
302     /**
303      * Handle UICC envelope response and send SMS acknowledgement.
304      *
305      * @param msg the message to handle
306      */
307     @Override
handleMessage(Message msg)308     public void handleMessage(Message msg) {
309         AsyncResult ar;
310 
311         switch (msg.what) {
312             case EVENT_START_DATA_DOWNLOAD:
313                 handleDataDownload((SmsMessage) msg.obj, msg.arg1 /* smsSource */);
314                 break;
315 
316             case EVENT_SEND_ENVELOPE_RESPONSE:
317                 ar = (AsyncResult) msg.obj;
318 
319                 if (ar.exception != null) {
320                     Rlog.e(TAG, "UICC Send Envelope failure, exception: " + ar.exception);
321                     acknowledgeSmsWithError(
322                             CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR);
323                     return;
324                 }
325 
326                 int[] dcsPid = (int[]) ar.userObj;
327                 sendSmsAckForEnvelopeResponse((IccIoResult) ar.result, dcsPid[0], dcsPid[1]);
328                 break;
329 
330             case EVENT_WRITE_SMS_COMPLETE:
331                 ar = (AsyncResult) msg.obj;
332                 if (ar.exception == null) {
333                     Rlog.d(TAG, "Successfully wrote SMS-PP message to UICC");
334                     mCi.acknowledgeLastIncomingGsmSms(true, 0, null);
335                 } else {
336                     Rlog.d(TAG, "Failed to write SMS-PP message to UICC", ar.exception);
337                     mCi.acknowledgeLastIncomingGsmSms(false,
338                             CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR, null);
339                 }
340                 break;
341 
342             default:
343                 Rlog.e(TAG, "Ignoring unexpected message, what=" + msg.what);
344         }
345     }
346 }
347