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 
17 package com.android.internal.telephony.cat;
18 
19 import android.telephony.Rlog;
20 
21 import java.util.ArrayList;
22 import 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  */
32 public class 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      */
ComprehensionTlv(int tag, boolean cr, int length, byte[] data, int valueIndex)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 
getTag()60     public int getTag() {
61         return mTag;
62     }
63 
isComprehensionRequired()64     public boolean isComprehensionRequired() {
65         return mCr;
66     }
67 
getLength()68     public int getLength() {
69         return mLength;
70     }
71 
getValueIndex()72     public int getValueIndex() {
73         return mValueIndex;
74     }
75 
getRawValue()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      */
decodeMany(byte[] data, int startIndex)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      */
decode(byte[] data, int startIndex)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