1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.telephony.cat;
18
19import android.telephony.Rlog;
20
21import java.util.ArrayList;
22import java.util.List;
23
24
25/**
26 * Class for representing COMPREHENSION-TLV objects.
27 *
28 * @see "ETSI TS 101 220 subsection 7.1.1"
29 *
30 * {@hide}
31 */
32class ComprehensionTlv {
33    private static final String LOG_TAG = "ComprehensionTlv";
34    private int mTag;
35    private boolean mCr;
36    private int mLength;
37    private int mValueIndex;
38    private byte[] mRawValue;
39
40    /**
41     * Constructor. Private on purpose. Use
42     * {@link #decodeMany(byte[], int) decodeMany} or
43     * {@link #decode(byte[], int) decode} method.
44     *
45     * @param tag The tag for this object
46     * @param cr Comprehension Required flag
47     * @param length Length of the value
48     * @param data Byte array containing the value
49     * @param valueIndex Index in data at which the value starts
50     */
51    protected ComprehensionTlv(int tag, boolean cr, int length, byte[] data,
52            int valueIndex) {
53        mTag = tag;
54        mCr = cr;
55        mLength = length;
56        mValueIndex = valueIndex;
57        mRawValue = data;
58    }
59
60    public int getTag() {
61        return mTag;
62    }
63
64    public boolean isComprehensionRequired() {
65        return mCr;
66    }
67
68    public int getLength() {
69        return mLength;
70    }
71
72    public int getValueIndex() {
73        return mValueIndex;
74    }
75
76    public byte[] getRawValue() {
77        return mRawValue;
78    }
79
80    /**
81     * Parses a list of COMPREHENSION-TLV objects from a byte array.
82     *
83     * @param data A byte array containing data to be parsed
84     * @param startIndex Index in data at which to start parsing
85     * @return A list of COMPREHENSION-TLV objects parsed
86     * @throws ResultException
87     */
88    public static List<ComprehensionTlv> decodeMany(byte[] data, int startIndex)
89            throws ResultException {
90        ArrayList<ComprehensionTlv> items = new ArrayList<ComprehensionTlv>();
91        int endIndex = data.length;
92        while (startIndex < endIndex) {
93            ComprehensionTlv ctlv = ComprehensionTlv.decode(data, startIndex);
94            if (ctlv != null) {
95                items.add(ctlv);
96                startIndex = ctlv.mValueIndex + ctlv.mLength;
97            } else {
98                CatLog.d(LOG_TAG, "decodeMany: ctlv is null, stop decoding");
99                break;
100            }
101        }
102
103        return items;
104    }
105
106    /**
107     * Parses an COMPREHENSION-TLV object from a byte array.
108     *
109     * @param data A byte array containing data to be parsed
110     * @param startIndex Index in data at which to start parsing
111     * @return A COMPREHENSION-TLV object parsed
112     * @throws ResultException
113     */
114    public static ComprehensionTlv decode(byte[] data, int startIndex)
115            throws ResultException {
116        int curIndex = startIndex;
117        int endIndex = data.length;
118
119        try {
120            /* tag */
121            int tag;
122            boolean cr; // Comprehension required flag
123            int temp = data[curIndex++] & 0xff;
124            switch (temp) {
125            case 0:
126            case 0xff:
127            case 0x80:
128                Rlog.d("CAT     ", "decode: unexpected first tag byte=" + Integer.toHexString(temp) +
129                        ", startIndex=" + startIndex + " curIndex=" + curIndex +
130                        " endIndex=" + endIndex);
131                // Return null which will stop decoding, this has occurred
132                // with Ghana MTN simcard and JDI simcard.
133                return null;
134
135            case 0x7f: // tag is in three-byte format
136                tag = ((data[curIndex] & 0xff) << 8)
137                        | (data[curIndex + 1] & 0xff);
138                cr = (tag & 0x8000) != 0;
139                tag &= ~0x8000;
140                curIndex += 2;
141                break;
142
143            default: // tag is in single-byte format
144                tag = temp;
145                cr = (tag & 0x80) != 0;
146                tag &= ~0x80;
147                break;
148            }
149
150            /* length */
151            int length;
152            temp = data[curIndex++] & 0xff;
153            if (temp < 0x80) {
154                length = temp;
155            } else if (temp == 0x81) {
156                length = data[curIndex++] & 0xff;
157                if (length < 0x80) {
158                    throw new ResultException(
159                            ResultCode.CMD_DATA_NOT_UNDERSTOOD,
160                            "length < 0x80 length=" + Integer.toHexString(length) +
161                            " startIndex=" + startIndex + " curIndex=" + curIndex +
162                            " endIndex=" + endIndex);
163                }
164            } else if (temp == 0x82) {
165                length = ((data[curIndex] & 0xff) << 8)
166                        | (data[curIndex + 1] & 0xff);
167                curIndex += 2;
168                if (length < 0x100) {
169                    throw new ResultException(
170                            ResultCode.CMD_DATA_NOT_UNDERSTOOD,
171                            "two byte length < 0x100 length=" + Integer.toHexString(length) +
172                            " startIndex=" + startIndex + " curIndex=" + curIndex +
173                            " endIndex=" + endIndex);
174                }
175            } else if (temp == 0x83) {
176                length = ((data[curIndex] & 0xff) << 16)
177                        | ((data[curIndex + 1] & 0xff) << 8)
178                        | (data[curIndex + 2] & 0xff);
179                curIndex += 3;
180                if (length < 0x10000) {
181                    throw new ResultException(
182                            ResultCode.CMD_DATA_NOT_UNDERSTOOD,
183                            "three byte length < 0x10000 length=0x" + Integer.toHexString(length) +
184                            " startIndex=" + startIndex + " curIndex=" + curIndex +
185                            " endIndex=" + endIndex);
186                }
187            } else {
188                throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD,
189                        "Bad length modifer=" + temp +
190                        " startIndex=" + startIndex + " curIndex=" + curIndex +
191                        " endIndex=" + endIndex);
192
193            }
194
195            return new ComprehensionTlv(tag, cr, length, data, curIndex);
196
197        } catch (IndexOutOfBoundsException e) {
198            throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD,
199                    "IndexOutOfBoundsException" + " startIndex=" + startIndex +
200                    " curIndex=" + curIndex + " endIndex=" + endIndex);
201        }
202    }
203}
204