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