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