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