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 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                     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 
    176     public LanguageResponseData(String lang) {
    177         super();
    178         mLang = lang;
    179     }
    180 
    181     @Override
    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 
    214     public DTTZResponseData(Calendar cal) {
    215         super();
    216         mCalendar = cal;
    217     }
    218 
    219     @Override
    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 
    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 
    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