Home | History | Annotate | Download | only in cat
      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