ResponseData.java revision 0825495a331bb44df395a0cdb79fab85e68db5d5
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
17package com.android.internal.telephony.cat;
18
19import com.android.internal.telephony.EncodeException;
20import com.android.internal.telephony.GsmAlphabet;
21import java.util.Calendar;
22import java.util.TimeZone;
23import android.os.SystemProperties;
24import android.text.TextUtils;
25
26import com.android.internal.telephony.cat.AppInterface.CommandType;
27
28import java.io.ByteArrayOutputStream;
29import java.io.UnsupportedEncodingException;
30
31abstract 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
49class 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
68class 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
159class 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
197class 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