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                     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