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