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