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