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 
17 package com.android.internal.telephony.cat;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.os.Build;
21 import android.telephony.SmsMessage;
22 
23 import com.android.internal.telephony.GsmAlphabet;
24 import com.android.internal.telephony.cat.Duration.TimeUnit;
25 import com.android.internal.telephony.uicc.IccUtils;
26 
27 import java.io.ByteArrayOutputStream;
28 import java.io.UnsupportedEncodingException;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.List;
32 
33 
34 /**
35  *  Util class that parses different entities from the ctlvs ComprehensionTlv List
36  * @hide
37  */
38 public abstract class ValueParser {
39 
40     /**
41      * Search for a Command Details object from a list.
42      *
43      * @param ctlv List of ComprehensionTlv objects used for search
44      * @return An CtlvCommandDetails object found from the objects. If no
45      *         Command Details object is found, ResultException is thrown.
46      * @throws ResultException
47      */
retrieveCommandDetails(ComprehensionTlv ctlv)48     static CommandDetails retrieveCommandDetails(ComprehensionTlv ctlv)
49             throws ResultException {
50 
51         CommandDetails cmdDet = new CommandDetails();
52         byte[] rawValue = ctlv.getRawValue();
53         int valueIndex = ctlv.getValueIndex();
54         try {
55             cmdDet.compRequired = ctlv.isComprehensionRequired();
56             cmdDet.commandNumber = rawValue[valueIndex] & 0xff;
57             cmdDet.typeOfCommand = rawValue[valueIndex + 1] & 0xff;
58             cmdDet.commandQualifier = rawValue[valueIndex + 2] & 0xff;
59             return cmdDet;
60         } catch (IndexOutOfBoundsException e) {
61             throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
62         }
63     }
64 
65     /**
66      * Search for a Device Identities object from a list.
67      *
68      * @param ctlv List of ComprehensionTlv objects used for search
69      * @return An CtlvDeviceIdentities object found from the objects. If no
70      *         Command Details object is found, ResultException is thrown.
71      * @throws ResultException
72      */
73     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
retrieveDeviceIdentities(ComprehensionTlv ctlv)74     static DeviceIdentities retrieveDeviceIdentities(ComprehensionTlv ctlv)
75             throws ResultException {
76 
77         DeviceIdentities devIds = new DeviceIdentities();
78         byte[] rawValue = ctlv.getRawValue();
79         int valueIndex = ctlv.getValueIndex();
80         try {
81             devIds.sourceId = rawValue[valueIndex] & 0xff;
82             devIds.destinationId = rawValue[valueIndex + 1] & 0xff;
83             return devIds;
84         } catch (IndexOutOfBoundsException e) {
85             throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING);
86         }
87     }
88 
89     /**
90      * Retrieves Duration information from the Duration COMPREHENSION-TLV
91      * object.
92      *
93      * @param ctlv A Text Attribute COMPREHENSION-TLV object
94      * @return A Duration object
95      * @throws ResultException
96      */
retrieveDuration(ComprehensionTlv ctlv)97     static Duration retrieveDuration(ComprehensionTlv ctlv) throws ResultException {
98         int timeInterval = 0;
99         TimeUnit timeUnit = TimeUnit.SECOND;
100 
101         byte[] rawValue = ctlv.getRawValue();
102         int valueIndex = ctlv.getValueIndex();
103 
104         try {
105             timeUnit = TimeUnit.values()[(rawValue[valueIndex] & 0xff)];
106             timeInterval = rawValue[valueIndex + 1] & 0xff;
107         } catch (IndexOutOfBoundsException e) {
108             throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
109         }
110         return new Duration(timeInterval, timeUnit);
111     }
112 
113     /**
114      * Retrieves Item information from the COMPREHENSION-TLV object.
115      *
116      * @param ctlv A Text Attribute COMPREHENSION-TLV object
117      * @return An Item
118      * @throws ResultException
119      */
retrieveItem(ComprehensionTlv ctlv)120     static Item retrieveItem(ComprehensionTlv ctlv) throws ResultException {
121         Item item = null;
122 
123         byte[] rawValue = ctlv.getRawValue();
124         int valueIndex = ctlv.getValueIndex();
125         int length = ctlv.getLength();
126 
127         if (length != 0) {
128             int textLen = length - 1;
129 
130             try {
131                 int id = rawValue[valueIndex] & 0xff;
132                 String text = IccUtils.adnStringFieldToString(rawValue,
133                         valueIndex + 1, textLen);
134                 item = new Item(id, text);
135             } catch (IndexOutOfBoundsException e) {
136                 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
137             }
138         }
139 
140         return item;
141     }
142 
143     /**
144      * Retrieves Item id information from the COMPREHENSION-TLV object.
145      *
146      * @param ctlv A Text Attribute COMPREHENSION-TLV object
147      * @return An Item id
148      * @throws ResultException
149      */
retrieveItemId(ComprehensionTlv ctlv)150     static int retrieveItemId(ComprehensionTlv ctlv) throws ResultException {
151         int id = 0;
152 
153         byte[] rawValue = ctlv.getRawValue();
154         int valueIndex = ctlv.getValueIndex();
155 
156         try {
157             id = rawValue[valueIndex] & 0xff;
158         } catch (IndexOutOfBoundsException e) {
159             throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
160         }
161 
162         return id;
163     }
164 
165     /**
166      * Retrieves icon id from an Icon Identifier COMPREHENSION-TLV object
167      *
168      * @param ctlv An Icon Identifier COMPREHENSION-TLV object
169      * @return IconId instance
170      * @throws ResultException
171      */
retrieveIconId(ComprehensionTlv ctlv)172     static IconId retrieveIconId(ComprehensionTlv ctlv) throws ResultException {
173         IconId id = new IconId();
174 
175         byte[] rawValue = ctlv.getRawValue();
176         int valueIndex = ctlv.getValueIndex();
177         try {
178             id.selfExplanatory = (rawValue[valueIndex++] & 0xff) == 0x00;
179             id.recordNumber = rawValue[valueIndex] & 0xff;
180         } catch (IndexOutOfBoundsException e) {
181             throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
182         }
183 
184         return id;
185     }
186 
187     /**
188      * Retrieves item icons id from an Icon Identifier List COMPREHENSION-TLV
189      * object
190      *
191      * @param ctlv An Item Icon List Identifier COMPREHENSION-TLV object
192      * @return ItemsIconId instance
193      * @throws ResultException
194      */
retrieveItemsIconId(ComprehensionTlv ctlv)195     static ItemsIconId retrieveItemsIconId(ComprehensionTlv ctlv)
196             throws ResultException {
197         CatLog.d("ValueParser", "retrieveItemsIconId:");
198         ItemsIconId id = new ItemsIconId();
199 
200         byte[] rawValue = ctlv.getRawValue();
201         int valueIndex = ctlv.getValueIndex();
202         int numOfItems = ctlv.getLength() - 1;
203         id.recordNumbers = new int[numOfItems];
204 
205         try {
206             // get icon self-explanatory
207             id.selfExplanatory = (rawValue[valueIndex++] & 0xff) == 0x00;
208 
209             for (int index = 0; index < numOfItems;) {
210                 id.recordNumbers[index++] = rawValue[valueIndex++];
211             }
212         } catch (IndexOutOfBoundsException e) {
213             throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
214         }
215         return id;
216     }
217 
218     /**
219      * Retrieves text attribute information from the Text Attribute
220      * COMPREHENSION-TLV object.
221      *
222      * @param ctlv A Text Attribute COMPREHENSION-TLV object
223      * @return A list of TextAttribute objects
224      * @throws ResultException
225      */
226     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
retrieveTextAttribute(ComprehensionTlv ctlv)227     static List<TextAttribute> retrieveTextAttribute(ComprehensionTlv ctlv)
228             throws ResultException {
229         ArrayList<TextAttribute> lst = new ArrayList<TextAttribute>();
230 
231         byte[] rawValue = ctlv.getRawValue();
232         int valueIndex = ctlv.getValueIndex();
233         int length = ctlv.getLength();
234 
235         if (length != 0) {
236             // Each attribute is consisted of four bytes
237             int itemCount = length / 4;
238 
239             try {
240                 for (int i = 0; i < itemCount; i++, valueIndex += 4) {
241                     int start = rawValue[valueIndex] & 0xff;
242                     int textLength = rawValue[valueIndex + 1] & 0xff;
243                     int format = rawValue[valueIndex + 2] & 0xff;
244                     int colorValue = rawValue[valueIndex + 3] & 0xff;
245 
246                     int alignValue = format & 0x03;
247                     TextAlignment align = TextAlignment.fromInt(alignValue);
248 
249                     int sizeValue = (format >> 2) & 0x03;
250                     FontSize size = FontSize.fromInt(sizeValue);
251                     if (size == null) {
252                         // Font size value is not defined. Use default.
253                         size = FontSize.NORMAL;
254                     }
255 
256                     boolean bold = (format & 0x10) != 0;
257                     boolean italic = (format & 0x20) != 0;
258                     boolean underlined = (format & 0x40) != 0;
259                     boolean strikeThrough = (format & 0x80) != 0;
260 
261                     TextColor color = TextColor.fromInt(colorValue);
262 
263                     TextAttribute attr = new TextAttribute(start, textLength,
264                             align, size, bold, italic, underlined,
265                             strikeThrough, color);
266                     lst.add(attr);
267                 }
268 
269                 return lst;
270 
271             } catch (IndexOutOfBoundsException e) {
272                 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
273             }
274         }
275         return null;
276     }
277 
278 
279     /**
280      * Retrieves alpha identifier from an Alpha Identifier COMPREHENSION-TLV
281      * object.
282      *
283      * @param ctlv An Alpha Identifier COMPREHENSION-TLV object
284      * @return String corresponding to the alpha identifier
285      * @throws ResultException
286      */
287     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
retrieveAlphaId(ComprehensionTlv ctlv, boolean noAlphaUsrCnf)288     static String retrieveAlphaId(ComprehensionTlv ctlv, boolean noAlphaUsrCnf)
289             throws ResultException {
290 
291         if (ctlv != null) {
292             byte[] rawValue = ctlv.getRawValue();
293             int valueIndex = ctlv.getValueIndex();
294             int length = ctlv.getLength();
295             if (length != 0) {
296                 try {
297                     return IccUtils.adnStringFieldToString(rawValue, valueIndex,
298                             length);
299                 } catch (IndexOutOfBoundsException e) {
300                     throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
301                 }
302             } else {
303                 CatLog.d("ValueParser", "Alpha Id length=" + length);
304                 return null;
305             }
306         } else {
307             /* Per 3GPP specification 102.223,
308              * if the alpha identifier is not provided by the UICC,
309              * the terminal MAY give information to the user
310              * noAlphaUsrCnf defines if you need to show user confirmation or not
311              */
312             return (noAlphaUsrCnf ? null : CatService.STK_DEFAULT);
313         }
314     }
315 
316     /**
317      * Retrieves text from the Text COMPREHENSION-TLV object, and decodes it
318      * into a Java String.
319      *
320      * @param ctlv A Text COMPREHENSION-TLV object
321      * @return A Java String object decoded from the Text object
322      * @throws ResultException
323      */
324     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
retrieveTextString(ComprehensionTlv ctlv)325     static String retrieveTextString(ComprehensionTlv ctlv) throws ResultException {
326         byte[] rawValue = ctlv.getRawValue();
327         int valueIndex = ctlv.getValueIndex();
328         byte codingScheme = 0x00;
329         String text = null;
330         int textLen = ctlv.getLength();
331 
332         // In case the text length is 0, return a null string.
333         if (textLen == 0) {
334             return text;
335         } else {
336             // one byte is coding scheme
337             textLen -= 1;
338         }
339 
340         try {
341             codingScheme = (byte) (rawValue[valueIndex] & 0x0c);
342 
343             if (codingScheme == 0x00) { // GSM 7-bit packed
344                 text = GsmAlphabet.gsm7BitPackedToString(rawValue,
345                         valueIndex + 1, (textLen * 8) / 7);
346             } else if (codingScheme == 0x04) { // GSM 8-bit unpacked
347                 text = GsmAlphabet.gsm8BitUnpackedToString(rawValue,
348                         valueIndex + 1, textLen);
349             } else if (codingScheme == 0x08) { // UCS2
350                 text = new String(rawValue, valueIndex + 1, textLen, "UTF-16");
351             } else {
352                 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
353             }
354 
355             return text;
356         } catch (IndexOutOfBoundsException e) {
357             throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
358         } catch (UnsupportedEncodingException e) {
359             // This should never happen.
360             throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
361         }
362     }
363 
364     /**
365      * Retrieve's the tpdu from the ctlv and creates the SmsMessage from pdu.
366      * @param ctlv  ComprehensionTlv value
367      * @return message SmsMessage to retrieve the destAddress and Text
368      * @throws ResultException
369      * @hide
370      */
retrieveTpduAsSmsMessage(ComprehensionTlv ctlv)371     public static SmsMessage retrieveTpduAsSmsMessage(ComprehensionTlv ctlv)
372             throws ResultException {
373         if (ctlv != null) {
374             byte[] rawValue = ctlv.getRawValue();
375             int valueIndex = ctlv.getValueIndex();
376             int length = ctlv.getLength();
377             if (length != 0) {
378                 try {
379                     byte[] pdu = Arrays.copyOfRange(rawValue, valueIndex, (valueIndex + length));
380                     ByteArrayOutputStream bo = new ByteArrayOutputStream(pdu.length + 1);
381                     /* Framework's TPdu Parser expects the TPdu be prepended with SC-Address.
382                      * else the parser will throw an exception. So prepending TPdu with 0,
383                      * which indicates that there is no SC address and its length is 0.
384                      * This way Parser will skip parsing for SC-Address
385                      */
386                     bo.write(0x00);
387                     bo.write(pdu, 0, pdu.length);
388                     byte[] frameworkPdu = bo.toByteArray();
389                     //ToDO handle for 3GPP2 format bug: b/243123533
390                     SmsMessage message = SmsMessage.createFromPdu(frameworkPdu,
391                             SmsMessage.FORMAT_3GPP);
392                     return message;
393                 } catch (IndexOutOfBoundsException e) {
394                     throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
395                 }
396             }
397         }
398         return null;
399     }
400 
401 }
402