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