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 */ 36 public abstract void format(ByteArrayOutputStream buf); 37 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 id; 52 53 public SelectItemResponseData(int id) { 54 super(); 55 this.id = id; 56 } 57 58 @Override 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(id); // 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 80 public GetInkeyInputResponseData(String inData, boolean ucs2, boolean packed) { 81 super(); 82 this.mIsUcs2 = ucs2; 83 this.mIsPacked = packed; 84 this.mInData = inData; 85 this.mIsYesNo = false; 86 } 87 88 public GetInkeyInputResponseData(boolean yesNoResponse) { 89 super(); 90 this.mIsUcs2 = false; 91 this.mIsPacked = false; 92 this.mInData = ""; 93 this.mIsYesNo = true; 94 this.mYesNoResponse = yesNoResponse; 95 } 96 97 @Override 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 if (mIsUcs2) { 115 data = mInData.getBytes("UTF-16"); 116 } else if (mIsPacked) { 117 int size = mInData.length(); 118 119 byte[] tempData = GsmAlphabet 120 .stringToGsm7BitPacked(mInData, 0, 0); 121 data = new byte[size]; 122 // Since stringToGsm7BitPacked() set byte 0 in the 123 // returned byte array to the count of septets used... 124 // copy to a new array without byte 0. 125 System.arraycopy(tempData, 1, data, 0, size); 126 } else { 127 data = GsmAlphabet.stringToGsm8BitPacked(mInData); 128 } 129 } catch (UnsupportedEncodingException e) { 130 data = new byte[0]; 131 } catch (EncodeException e) { 132 data = new byte[0]; 133 } 134 } else { 135 data = new byte[0]; 136 } 137 138 // length - one more for data coding scheme. 139 writeLength(buf, data.length + 1); 140 141 // data coding scheme 142 if (mIsUcs2) { 143 buf.write(0x08); // UCS2 144 } else if (mIsPacked) { 145 buf.write(0x00); // 7 bit packed 146 } else { 147 buf.write(0x04); // 8 bit unpacked 148 } 149 150 for (byte b : data) { 151 buf.write(b); 152 } 153 } 154 } 155 156 // For "PROVIDE LOCAL INFORMATION" command. 157 // See TS 31.111 section 6.4.15/ETSI TS 102 223 158 // TS 31.124 section 27.22.4.15 for test spec 159 class LanguageResponseData extends ResponseData { 160 private String lang; 161 162 public LanguageResponseData(String lang) { 163 super(); 164 this.lang = lang; 165 } 166 167 @Override 168 public void format(ByteArrayOutputStream buf) { 169 if (buf == null) { 170 return; 171 } 172 173 // Text string object 174 int tag = 0x80 | ComprehensionTlvTag.LANGUAGE.value(); 175 buf.write(tag); // tag 176 177 byte[] data; 178 179 if (lang != null && lang.length() > 0) { 180 data = GsmAlphabet.stringToGsm8BitPacked(lang); 181 } 182 else { 183 data = new byte[0]; 184 } 185 186 buf.write(data.length); 187 188 for (byte b : data) { 189 buf.write(b); 190 } 191 } 192 } 193 194 // For "PROVIDE LOCAL INFORMATION" command. 195 // See TS 31.111 section 6.4.15/ETSI TS 102 223 196 // TS 31.124 section 27.22.4.15 for test spec 197 class DTTZResponseData extends ResponseData { 198 private Calendar calendar; 199 200 public DTTZResponseData(Calendar cal) { 201 super(); 202 calendar = cal; 203 } 204 205 @Override 206 public void format(ByteArrayOutputStream buf) { 207 if (buf == null) { 208 return; 209 } 210 211 // DTTZ object 212 int tag = 0x80 | CommandType.PROVIDE_LOCAL_INFORMATION.value(); 213 buf.write(tag); // tag 214 215 byte[] data = new byte[8]; 216 217 data[0] = 0x07; // Write length of DTTZ data 218 219 if (calendar == null) { 220 calendar = Calendar.getInstance(); 221 } 222 // Fill year byte 223 data[1] = byteToBCD(calendar.get(java.util.Calendar.YEAR) % 100); 224 225 // Fill month byte 226 data[2] = byteToBCD(calendar.get(java.util.Calendar.MONTH) + 1); 227 228 // Fill day byte 229 data[3] = byteToBCD(calendar.get(java.util.Calendar.DATE)); 230 231 // Fill hour byte 232 data[4] = byteToBCD(calendar.get(java.util.Calendar.HOUR_OF_DAY)); 233 234 // Fill minute byte 235 data[5] = byteToBCD(calendar.get(java.util.Calendar.MINUTE)); 236 237 // Fill second byte 238 data[6] = byteToBCD(calendar.get(java.util.Calendar.SECOND)); 239 240 String tz = SystemProperties.get("persist.sys.timezone", ""); 241 if (TextUtils.isEmpty(tz)) { 242 data[7] = (byte) 0xFF; // set FF in terminal response 243 } else { 244 TimeZone zone = TimeZone.getTimeZone(tz); 245 int zoneOffset = zone.getRawOffset() + zone.getDSTSavings(); 246 data[7] = getTZOffSetByte(zoneOffset); 247 } 248 249 for (byte b : data) { 250 buf.write(b); 251 } 252 } 253 254 private byte byteToBCD(int value) { 255 if (value < 0 && value > 99) { 256 CatLog.d(this, "Err: byteToBCD conversion Value is " + value + 257 " Value has to be between 0 and 99"); 258 return 0; 259 } 260 261 return (byte) ((value / 10) | ((value % 10) << 4)); 262 } 263 264 private byte getTZOffSetByte(long offSetVal) { 265 boolean isNegative = (offSetVal < 0); 266 267 /* 268 * The 'offSetVal' is in milliseconds. Convert it to hours and compute 269 * offset While sending T.R to UICC, offset is expressed is 'quarters of 270 * hours' 271 */ 272 273 long tzOffset = offSetVal / (15 * 60 * 1000); 274 tzOffset = (isNegative ? -1 : 1) * tzOffset; 275 byte bcdVal = byteToBCD((int) tzOffset); 276 // For negative offsets, put '1' in the msb 277 return isNegative ? (bcdVal |= 0x08) : bcdVal; 278 } 279 280 } 281 282