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 
17 package com.android.internal.telephony.uicc;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 import android.telephony.PhoneNumberUtils;
23 import android.text.TextUtils;
24 
25 import com.android.internal.telephony.GsmAlphabet;
26 import com.android.telephony.Rlog;
27 
28 import java.util.Arrays;
29 
30 /**
31  *
32  * Used to load or store ADNs (Abbreviated Dialing Numbers).
33  *
34  * {@hide}
35  *
36  */
37 public class AdnRecord implements Parcelable {
38     static final String LOG_TAG = "AdnRecord";
39 
40     //***** Instance Variables
41 
42     @UnsupportedAppUsage
43     String mAlphaTag = null;
44     @UnsupportedAppUsage
45     String mNumber = null;
46     @UnsupportedAppUsage
47     String[] mEmails;
48     @UnsupportedAppUsage
49     int mExtRecord = 0xff;
50     @UnsupportedAppUsage
51     int mEfid;                   // or 0 if none
52     @UnsupportedAppUsage
53     int mRecordNumber;           // or 0 if none
54 
55 
56     //***** Constants
57 
58     // In an ADN record, everything but the alpha identifier
59     // is in a footer that's 14 bytes
60     static final int FOOTER_SIZE_BYTES = 14;
61 
62     // Maximum size of the un-extended number field
63     static final int MAX_NUMBER_SIZE_BYTES = 11;
64 
65     static final int EXT_RECORD_LENGTH_BYTES = 13;
66     static final int EXT_RECORD_TYPE_ADDITIONAL_DATA = 2;
67     static final int EXT_RECORD_TYPE_MASK = 3;
68     static final int MAX_EXT_CALLED_PARTY_LENGTH = 0xa;
69 
70     // ADN offset
71     static final int ADN_BCD_NUMBER_LENGTH = 0;
72     static final int ADN_TON_AND_NPI = 1;
73     static final int ADN_DIALING_NUMBER_START = 2;
74     static final int ADN_DIALING_NUMBER_END = 11;
75     static final int ADN_CAPABILITY_ID = 12;
76     static final int ADN_EXTENSION_ID = 13;
77 
78     //***** Static Methods
79 
80     @UnsupportedAppUsage
81     public static final Parcelable.Creator<AdnRecord> CREATOR
82             = new Parcelable.Creator<AdnRecord>() {
83         @Override
84         public AdnRecord createFromParcel(Parcel source) {
85             int efid;
86             int recordNumber;
87             String alphaTag;
88             String number;
89             String[] emails;
90 
91             efid = source.readInt();
92             recordNumber = source.readInt();
93             alphaTag = source.readString();
94             number = source.readString();
95             final int len = source.readInt();
96             if (len > 0) {
97                 emails = new String[len];
98                 source.readStringArray(emails);
99                 return new AdnRecord(efid, recordNumber, alphaTag, number, emails);
100             } else {
101                 return new AdnRecord(efid, recordNumber, alphaTag, number, null);
102             }
103         }
104 
105         @Override
106         public AdnRecord[] newArray(int size) {
107             return new AdnRecord[size];
108         }
109     };
110 
111 
112     //***** Constructor
113     @UnsupportedAppUsage
AdnRecord(byte[] record)114     public AdnRecord (byte[] record) {
115         this(0, 0, record);
116     }
117 
118     @UnsupportedAppUsage
AdnRecord(int efid, int recordNumber, byte[] record)119     public AdnRecord (int efid, int recordNumber, byte[] record) {
120         this.mEfid = efid;
121         this.mRecordNumber = recordNumber;
122         parseRecord(record);
123     }
124 
125     @UnsupportedAppUsage
AdnRecord(String alphaTag, String number)126     public AdnRecord (String alphaTag, String number) {
127         this(0, 0, alphaTag, number);
128     }
129 
130     @UnsupportedAppUsage
AdnRecord(String alphaTag, String number, String[] emails)131     public AdnRecord (String alphaTag, String number, String[] emails) {
132         this(0, 0, alphaTag, number, emails);
133     }
134 
135     @UnsupportedAppUsage
AdnRecord(int efid, int recordNumber, String alphaTag, String number, String[] emails)136     public AdnRecord (int efid, int recordNumber, String alphaTag, String number, String[] emails) {
137         this.mEfid = efid;
138         this.mRecordNumber = recordNumber;
139         this.mAlphaTag = alphaTag;
140         this.mNumber = number;
141         this.mEmails = emails;
142     }
143 
144     @UnsupportedAppUsage
AdnRecord(int efid, int recordNumber, String alphaTag, String number)145     public AdnRecord(int efid, int recordNumber, String alphaTag, String number) {
146         this.mEfid = efid;
147         this.mRecordNumber = recordNumber;
148         this.mAlphaTag = alphaTag;
149         this.mNumber = number;
150         this.mEmails = null;
151     }
152 
153     //***** Instance Methods
154 
getAlphaTag()155     public String getAlphaTag() {
156         return mAlphaTag;
157     }
158 
getEfid()159     public int getEfid() {
160         return mEfid;
161     }
162 
getRecId()163     public int getRecId() {
164         return mRecordNumber;
165     }
166 
167     @UnsupportedAppUsage
getNumber()168     public String getNumber() {
169         return mNumber;
170     }
171 
setNumber(String number)172     public void setNumber(String number) {
173         mNumber = number;
174     }
175 
176     @UnsupportedAppUsage
getEmails()177     public String[] getEmails() {
178         return mEmails;
179     }
180 
181     @UnsupportedAppUsage
setEmails(String[] emails)182     public void setEmails(String[] emails) {
183         this.mEmails = emails;
184     }
185 
186     @Override
toString()187     public String toString() {
188         return "ADN Record '" + mAlphaTag + "' '" + Rlog.pii(LOG_TAG, mNumber) + " "
189                 + Rlog.pii(LOG_TAG, mEmails) + "'";
190     }
191 
192     @UnsupportedAppUsage
isEmpty()193     public boolean isEmpty() {
194         return TextUtils.isEmpty(mAlphaTag) && TextUtils.isEmpty(mNumber) && mEmails == null;
195     }
196 
hasExtendedRecord()197     public boolean hasExtendedRecord() {
198         return mExtRecord != 0 && mExtRecord != 0xff;
199     }
200 
201     /** Helper function for {@link #isEqual}. */
stringCompareNullEqualsEmpty(String s1, String s2)202     private static boolean stringCompareNullEqualsEmpty(String s1, String s2) {
203         if (s1 == s2) {
204             return true;
205         }
206         if (s1 == null) {
207             s1 = "";
208         }
209         if (s2 == null) {
210             s2 = "";
211         }
212         return (s1.equals(s2));
213     }
214 
isEqual(AdnRecord adn)215     public boolean isEqual(AdnRecord adn) {
216         return ( stringCompareNullEqualsEmpty(mAlphaTag, adn.mAlphaTag) &&
217                 stringCompareNullEqualsEmpty(mNumber, adn.mNumber) &&
218                 Arrays.equals(mEmails, adn.mEmails));
219     }
220     //***** Parcelable Implementation
221 
222     @Override
describeContents()223     public int describeContents() {
224         return 0;
225     }
226 
227     @Override
writeToParcel(Parcel dest, int flags)228     public void writeToParcel(Parcel dest, int flags) {
229         dest.writeInt(mEfid);
230         dest.writeInt(mRecordNumber);
231         dest.writeString(mAlphaTag);
232         dest.writeString(mNumber);
233         dest.writeStringArray(mEmails);
234     }
235 
236     /**
237      * Build adn hex byte array based on record size
238      * The format of byte array is defined in 51.011 10.5.1
239      *
240      * @param recordSize is the size X of EF record
241      * @return hex byte[recordSize] to be written to EF record
242      *          return null for wrong format of dialing number or tag
243      */
244     @UnsupportedAppUsage
buildAdnString(int recordSize)245     public byte[] buildAdnString(int recordSize) {
246         byte[] bcdNumber;
247         byte[] byteTag;
248         byte[] adnString;
249         int footerOffset = recordSize - FOOTER_SIZE_BYTES;
250 
251         // create an empty record
252         adnString = new byte[recordSize];
253         for (int i = 0; i < recordSize; i++) {
254             adnString[i] = (byte) 0xFF;
255         }
256 
257         if (TextUtils.isEmpty(mNumber)) {
258             Rlog.w(LOG_TAG, "[buildAdnString] Empty dialing number");
259             return adnString;   // return the empty record (for delete)
260         } else if (mNumber.length()
261                 > (ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1) * 2) {
262             Rlog.w(LOG_TAG,
263                     "[buildAdnString] Max length of dialing number is 20");
264             return null;
265         }
266 
267         byteTag = !TextUtils.isEmpty(mAlphaTag) ? GsmAlphabet.stringToGsm8BitPacked(mAlphaTag)
268                 : new byte[0];
269 
270         if (byteTag.length > footerOffset) {
271             Rlog.w(LOG_TAG, "[buildAdnString] Max length of tag is " + footerOffset);
272             return null;
273         } else {
274             bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(
275                     mNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
276 
277             System.arraycopy(bcdNumber, 0, adnString,
278                     footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
279 
280             adnString[footerOffset + ADN_BCD_NUMBER_LENGTH]
281                     = (byte) (bcdNumber.length);
282             adnString[footerOffset + ADN_CAPABILITY_ID]
283                     = (byte) 0xFF; // Capability Id
284             adnString[footerOffset + ADN_EXTENSION_ID]
285                     = (byte) 0xFF; // Extension Record Id
286 
287             if (byteTag.length > 0) {
288                 System.arraycopy(byteTag, 0, adnString, 0, byteTag.length);
289             }
290 
291             return adnString;
292         }
293     }
294 
295     /**
296      * See TS 51.011 10.5.10
297      */
298     public void
appendExtRecord(byte[] extRecord)299     appendExtRecord (byte[] extRecord) {
300         try {
301             if (extRecord.length != EXT_RECORD_LENGTH_BYTES) {
302                 return;
303             }
304 
305             if ((extRecord[0] & EXT_RECORD_TYPE_MASK)
306                     != EXT_RECORD_TYPE_ADDITIONAL_DATA) {
307                 return;
308             }
309 
310             if ((0xff & extRecord[1]) > MAX_EXT_CALLED_PARTY_LENGTH) {
311                 // invalid or empty record
312                 return;
313             }
314 
315             mNumber += PhoneNumberUtils.calledPartyBCDFragmentToString(
316                     extRecord,
317                     2,
318                     0xff & extRecord[1],
319                     PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
320 
321             // We don't support ext record chaining.
322 
323         } catch (RuntimeException ex) {
324             Rlog.w(LOG_TAG, "Error parsing AdnRecord ext record", ex);
325         }
326     }
327 
328     //***** Private Methods
329 
330     /**
331      * alphaTag and number are set to null on invalid format
332      */
333     private void
parseRecord(byte[] record)334     parseRecord(byte[] record) {
335         try {
336             mAlphaTag = IccUtils.adnStringFieldToString(
337                             record, 0, record.length - FOOTER_SIZE_BYTES);
338 
339             int footerOffset = record.length - FOOTER_SIZE_BYTES;
340 
341             int numberLength = 0xff & record[footerOffset];
342 
343             if (numberLength > MAX_NUMBER_SIZE_BYTES) {
344                 // Invalid number length
345                 mNumber = "";
346                 return;
347             }
348 
349             // Please note 51.011 10.5.1:
350             //
351             // "If the Dialling Number/SSC String does not contain
352             // a dialling number, e.g. a control string deactivating
353             // a service, the TON/NPI byte shall be set to 'FF' by
354             // the ME (see note 2)."
355 
356             mNumber = PhoneNumberUtils.calledPartyBCDToString(
357                     record,
358                     footerOffset + 1,
359                     numberLength,
360                     PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
361 
362 
363             mExtRecord = 0xff & record[record.length - 1];
364 
365             mEmails = null;
366 
367         } catch (RuntimeException ex) {
368             Rlog.w(LOG_TAG, "Error parsing AdnRecord", ex);
369             mNumber = "";
370             mAlphaTag = "";
371             mEmails = null;
372         }
373     }
374 }
375