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