• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006-2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.android.internal.telephony.cat;
18 
19 import com.android.internal.telephony.EncodeException;
20 import com.android.internal.telephony.GsmAlphabet;
21 import java.util.Calendar;
22 import java.util.TimeZone;
23 import android.os.SystemProperties;
24 import android.text.TextUtils;
25 
26 import com.android.internal.telephony.cat.AppInterface.CommandType;
27 
28 import android.annotation.UnsupportedAppUsage;
29 import java.io.ByteArrayOutputStream;
30 import java.io.UnsupportedEncodingException;
31 
32 abstract class ResponseData {
33     /**
34      * Format the data appropriate for TERMINAL RESPONSE and write it into
35      * the ByteArrayOutputStream object.
36      */
37     @UnsupportedAppUsage
format(ByteArrayOutputStream buf)38     public abstract void format(ByteArrayOutputStream buf);
39 
writeLength(ByteArrayOutputStream buf, int length)40     public static void writeLength(ByteArrayOutputStream buf, int length) {
41         // As per ETSI 102.220 Sec7.1.2, if the total length is greater
42         // than 0x7F, it should be coded in two bytes and the first byte
43         // should be 0x81.
44         if (length > 0x7F) {
45             buf.write(0x81);
46         }
47         buf.write(length);
48     }
49 }
50 
51 class SelectItemResponseData extends ResponseData {
52     // members
53     private int mId;
54 
SelectItemResponseData(int id)55     public SelectItemResponseData(int id) {
56         super();
57         mId = id;
58     }
59 
60     @Override
format(ByteArrayOutputStream buf)61     public void format(ByteArrayOutputStream buf) {
62         // Item identifier object
63         int tag = 0x80 | ComprehensionTlvTag.ITEM_ID.value();
64         buf.write(tag); // tag
65         buf.write(1); // length
66         buf.write(mId); // identifier of item chosen
67     }
68 }
69 
70 class GetInkeyInputResponseData extends ResponseData {
71     // members
72     private boolean mIsUcs2;
73     private boolean mIsPacked;
74     private boolean mIsYesNo;
75     private boolean mYesNoResponse;
76     public String mInData;
77 
78     // GetInKey Yes/No response characters constants.
79     protected static final byte GET_INKEY_YES = 0x01;
80     protected static final byte GET_INKEY_NO = 0x00;
81 
GetInkeyInputResponseData(String inData, boolean ucs2, boolean packed)82     public GetInkeyInputResponseData(String inData, boolean ucs2, boolean packed) {
83         super();
84         mIsUcs2 = ucs2;
85         mIsPacked = packed;
86         mInData = inData;
87         mIsYesNo = false;
88     }
89 
GetInkeyInputResponseData(boolean yesNoResponse)90     public GetInkeyInputResponseData(boolean yesNoResponse) {
91         super();
92         mIsUcs2 = false;
93         mIsPacked = false;
94         mInData = "";
95         mIsYesNo = true;
96         mYesNoResponse = yesNoResponse;
97     }
98 
99     @Override
format(ByteArrayOutputStream buf)100     public void format(ByteArrayOutputStream buf) {
101         if (buf == null) {
102             return;
103         }
104 
105         // Text string object
106         int tag = 0x80 | ComprehensionTlvTag.TEXT_STRING.value();
107         buf.write(tag); // tag
108 
109         byte[] data;
110 
111         if (mIsYesNo) {
112             data = new byte[1];
113             data[0] = mYesNoResponse ? GET_INKEY_YES : GET_INKEY_NO;
114         } else if (mInData != null && mInData.length() > 0) {
115             try {
116                 // ETSI TS 102 223 8.15, should use the same format as in SMS messages
117                 // on the network.
118                 if (mIsUcs2) {
119                     // ucs2 is by definition big endian.
120                     data = mInData.getBytes("UTF-16BE");
121                 } else if (mIsPacked) {
122                     byte[] tempData = GsmAlphabet
123                             .stringToGsm7BitPacked(mInData, 0, 0);
124                     // The size of the new buffer will be smaller than the original buffer
125                     // since 7-bit GSM packed only requires ((mInData.length * 7) + 7) / 8 bytes.
126                     // And we don't need to copy/store the first byte from the returned array
127                     // because it is used to store the count of septets used.
128                     data = new byte[tempData.length - 1];
129                     System.arraycopy(tempData, 1, data, 0, tempData.length - 1);
130                 } else {
131                     data = GsmAlphabet.stringToGsm8BitPacked(mInData);
132                 }
133             } catch (UnsupportedEncodingException e) {
134                 data = new byte[0];
135             } catch (EncodeException e) {
136                 data = new byte[0];
137             }
138         } else {
139             data = new byte[0];
140         }
141 
142         // length - one more for data coding scheme.
143 
144         // ETSI TS 102 223 Annex C (normative): Structure of CAT communications
145         // Any length within the APDU limits (up to 255 bytes) can thus be encoded on two bytes.
146         // This coding is chosen to remain compatible with TS 101.220.
147         // Note that we need to reserve one more byte for coding scheme thus the maximum APDU
148         // size would be 254 bytes.
149         if (data.length + 1 <= 255) {
150             writeLength(buf, data.length + 1);
151         }
152         else {
153             data = new byte[0];
154         }
155 
156 
157         // data coding scheme
158         if (mIsUcs2) {
159             buf.write(0x08); // UCS2
160         } else if (mIsPacked) {
161             buf.write(0x00); // 7 bit packed
162         } else {
163             buf.write(0x04); // 8 bit unpacked
164         }
165 
166         for (byte b : data) {
167             buf.write(b);
168         }
169     }
170 }
171 
172 // For "PROVIDE LOCAL INFORMATION" command.
173 // See TS 31.111 section 6.4.15/ETSI TS 102 223
174 // TS 31.124 section 27.22.4.15 for test spec
175 class LanguageResponseData extends ResponseData {
176     private String mLang;
177 
LanguageResponseData(String lang)178     public LanguageResponseData(String lang) {
179         super();
180         mLang = lang;
181     }
182 
183     @Override
format(ByteArrayOutputStream buf)184     public void format(ByteArrayOutputStream buf) {
185         if (buf == null) {
186             return;
187         }
188 
189         // Text string object
190         int tag = 0x80 | ComprehensionTlvTag.LANGUAGE.value();
191         buf.write(tag); // tag
192 
193         byte[] data;
194 
195         if (mLang != null && mLang.length() > 0) {
196             data = GsmAlphabet.stringToGsm8BitPacked(mLang);
197         }
198         else {
199             data = new byte[0];
200         }
201 
202         buf.write(data.length);
203 
204         for (byte b : data) {
205             buf.write(b);
206         }
207     }
208 }
209 
210 // For "PROVIDE LOCAL INFORMATION" command.
211 // See TS 31.111 section 6.4.15/ETSI TS 102 223
212 // TS 31.124 section 27.22.4.15 for test spec
213 class DTTZResponseData extends ResponseData {
214     private Calendar mCalendar;
215 
DTTZResponseData(Calendar cal)216     public DTTZResponseData(Calendar cal) {
217         super();
218         mCalendar = cal;
219     }
220 
221     @Override
format(ByteArrayOutputStream buf)222     public void format(ByteArrayOutputStream buf) {
223         if (buf == null) {
224             return;
225         }
226 
227         // DTTZ object
228         int tag = 0x80 | CommandType.PROVIDE_LOCAL_INFORMATION.value();
229         buf.write(tag); // tag
230 
231         byte[] data = new byte[8];
232 
233         data[0] = 0x07; // Write length of DTTZ data
234 
235         if (mCalendar == null) {
236             mCalendar = Calendar.getInstance();
237         }
238         // Fill year byte
239         data[1] = byteToBCD(mCalendar.get(java.util.Calendar.YEAR) % 100);
240 
241         // Fill month byte
242         data[2] = byteToBCD(mCalendar.get(java.util.Calendar.MONTH) + 1);
243 
244         // Fill day byte
245         data[3] = byteToBCD(mCalendar.get(java.util.Calendar.DATE));
246 
247         // Fill hour byte
248         data[4] = byteToBCD(mCalendar.get(java.util.Calendar.HOUR_OF_DAY));
249 
250         // Fill minute byte
251         data[5] = byteToBCD(mCalendar.get(java.util.Calendar.MINUTE));
252 
253         // Fill second byte
254         data[6] = byteToBCD(mCalendar.get(java.util.Calendar.SECOND));
255 
256         String tz = SystemProperties.get("persist.sys.timezone", "");
257         if (TextUtils.isEmpty(tz)) {
258             data[7] = (byte) 0xFF;    // set FF in terminal response
259         } else {
260             TimeZone zone = TimeZone.getTimeZone(tz);
261             int zoneOffset = zone.getRawOffset() + zone.getDSTSavings();
262             data[7] = getTZOffSetByte(zoneOffset);
263         }
264 
265         for (byte b : data) {
266             buf.write(b);
267         }
268     }
269 
byteToBCD(int value)270     private byte byteToBCD(int value) {
271         if (value < 0 && value > 99) {
272             CatLog.d(this, "Err: byteToBCD conversion Value is " + value +
273                            " Value has to be between 0 and 99");
274             return 0;
275         }
276 
277         return (byte) ((value / 10) | ((value % 10) << 4));
278     }
279 
getTZOffSetByte(long offSetVal)280     private byte getTZOffSetByte(long offSetVal) {
281         boolean isNegative = (offSetVal < 0);
282 
283         /*
284          * The 'offSetVal' is in milliseconds. Convert it to hours and compute
285          * offset While sending T.R to UICC, offset is expressed is 'quarters of
286          * hours'
287          */
288 
289          long tzOffset = offSetVal / (15 * 60 * 1000);
290          tzOffset = (isNegative ? -1 : 1) * tzOffset;
291          byte bcdVal = byteToBCD((int) tzOffset);
292          // For negative offsets, put '1' in the msb
293          return isNegative ?  (bcdVal |= 0x08) : bcdVal;
294     }
295 
296 }
297 
298