1/*
2 * Copyright (C) 2006 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 java.util.List;
20
21/**
22 * Class for representing BER-TLV objects.
23 *
24 * @see "ETSI TS 102 223 Annex C" for more information.
25 *
26 * {@hide}
27 */
28class BerTlv {
29    private int mTag = BER_UNKNOWN_TAG;
30    private List<ComprehensionTlv> mCompTlvs = null;
31    private boolean mLengthValid = true;
32
33    public static final int BER_UNKNOWN_TAG             = 0x00;
34    public static final int BER_PROACTIVE_COMMAND_TAG   = 0xd0;
35    public static final int BER_MENU_SELECTION_TAG      = 0xd3;
36    public static final int BER_EVENT_DOWNLOAD_TAG      = 0xd6;
37
38    private BerTlv(int tag, List<ComprehensionTlv> ctlvs, boolean lengthValid) {
39        mTag = tag;
40        mCompTlvs = ctlvs;
41        mLengthValid = lengthValid;
42    }
43
44    /**
45     * Gets a list of ComprehensionTlv objects contained in this BER-TLV object.
46     *
47     * @return A list of COMPREHENSION-TLV object
48     */
49    public List<ComprehensionTlv> getComprehensionTlvs() {
50        return mCompTlvs;
51    }
52
53    /**
54     * Gets a tag id of the BER-TLV object.
55     *
56     * @return A tag integer.
57     */
58    public int getTag() {
59        return mTag;
60    }
61
62    /**
63     * Gets if the length of the BER-TLV object is valid
64     *
65     * @return if length valid
66     */
67     public boolean isLengthValid() {
68         return mLengthValid;
69     }
70
71    /**
72     * Decodes a BER-TLV object from a byte array.
73     *
74     * @param data A byte array to decode from
75     * @return A BER-TLV object decoded
76     * @throws ResultException
77     */
78    public static BerTlv decode(byte[] data) throws ResultException {
79        int curIndex = 0;
80        int endIndex = data.length;
81        int tag, length = 0;
82        boolean isLengthValid = true;
83
84        try {
85            /* tag */
86            tag = data[curIndex++] & 0xff;
87            if (tag == BER_PROACTIVE_COMMAND_TAG) {
88                /* length */
89                int temp = data[curIndex++] & 0xff;
90                if (temp < 0x80) {
91                    length = temp;
92                } else if (temp == 0x81) {
93                    temp = data[curIndex++] & 0xff;
94                    if (temp < 0x80) {
95                        throw new ResultException(
96                                ResultCode.CMD_DATA_NOT_UNDERSTOOD,
97                                "length < 0x80 length=" + Integer.toHexString(length) +
98                                " curIndex=" + curIndex + " endIndex=" + endIndex);
99
100                    }
101                    length = temp;
102                } else {
103                    throw new ResultException(
104                            ResultCode.CMD_DATA_NOT_UNDERSTOOD,
105                            "Expected first byte to be length or a length tag and < 0x81" +
106                            " byte= " + Integer.toHexString(temp) + " curIndex=" + curIndex +
107                            " endIndex=" + endIndex);
108                }
109            } else {
110                if (ComprehensionTlvTag.COMMAND_DETAILS.value() == (tag & ~0x80)) {
111                    tag = BER_UNKNOWN_TAG;
112                    curIndex = 0;
113                }
114            }
115        } catch (IndexOutOfBoundsException e) {
116            throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING,
117                    "IndexOutOfBoundsException " +
118                    " curIndex=" + curIndex + " endIndex=" + endIndex);
119        } catch (ResultException e) {
120            throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD, e.explanation());
121        }
122
123        /* COMPREHENSION-TLVs */
124        if (endIndex - curIndex < length) {
125            throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD,
126                    "Command had extra data endIndex=" + endIndex + " curIndex=" + curIndex +
127                    " length=" + length);
128        }
129
130        List<ComprehensionTlv> ctlvs = ComprehensionTlv.decodeMany(data,
131                curIndex);
132
133        if (tag == BER_PROACTIVE_COMMAND_TAG) {
134            int totalLength = 0;
135            for (ComprehensionTlv item : ctlvs) {
136                int itemLength = item.getLength();
137                if (itemLength >= 0x80 && itemLength <= 0xFF) {
138                    totalLength += itemLength + 3; //3: 'tag'(1 byte) and 'length'(2 bytes).
139                } else if (itemLength >= 0 && itemLength < 0x80) {
140                    totalLength += itemLength + 2; //2: 'tag'(1 byte) and 'length'(1 byte).
141                } else {
142                    isLengthValid = false;
143                    break;
144                }
145            }
146
147            // According to 3gpp11.14, chapter 6.10.6 "Length errors",
148
149            // If the total lengths of the SIMPLE-TLV data objects are not
150            // consistent with the length given in the BER-TLV data object,
151            // then the whole BER-TLV data object shall be rejected. The
152            // result field in the TERMINAL RESPONSE shall have the error
153            // condition "Command data not understood by ME".
154            if (length != totalLength) {
155                isLengthValid = false;
156            }
157        }
158
159        return new BerTlv(tag, ctlvs, isLengthValid);
160    }
161}
162