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.text.TextUtils;
23import android.telephony.Rlog;
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 + "' '" + mNumber + " " + mEmails + "'";
168    }
169
170    public boolean isEmpty() {
171        return TextUtils.isEmpty(mAlphaTag) && TextUtils.isEmpty(mNumber) && mEmails == null;
172    }
173
174    public boolean hasExtendedRecord() {
175        return mExtRecord != 0 && mExtRecord != 0xff;
176    }
177
178    /** Helper function for {@link #isEqual}. */
179    private static boolean stringCompareNullEqualsEmpty(String s1, String s2) {
180        if (s1 == s2) {
181            return true;
182        }
183        if (s1 == null) {
184            s1 = "";
185        }
186        if (s2 == null) {
187            s2 = "";
188        }
189        return (s1.equals(s2));
190    }
191
192    public boolean isEqual(AdnRecord adn) {
193        return ( stringCompareNullEqualsEmpty(mAlphaTag, adn.mAlphaTag) &&
194                stringCompareNullEqualsEmpty(mNumber, adn.mNumber) &&
195                Arrays.equals(mEmails, adn.mEmails));
196    }
197    //***** Parcelable Implementation
198
199    @Override
200    public int describeContents() {
201        return 0;
202    }
203
204    @Override
205    public void writeToParcel(Parcel dest, int flags) {
206        dest.writeInt(mEfid);
207        dest.writeInt(mRecordNumber);
208        dest.writeString(mAlphaTag);
209        dest.writeString(mNumber);
210        dest.writeStringArray(mEmails);
211    }
212
213    /**
214     * Build adn hex byte array based on record size
215     * The format of byte array is defined in 51.011 10.5.1
216     *
217     * @param recordSize is the size X of EF record
218     * @return hex byte[recordSize] to be written to EF record
219     *          return null for wrong format of dialing number or tag
220     */
221    public byte[] buildAdnString(int recordSize) {
222        byte[] bcdNumber;
223        byte[] byteTag;
224        byte[] adnString;
225        int footerOffset = recordSize - FOOTER_SIZE_BYTES;
226
227        // create an empty record
228        adnString = new byte[recordSize];
229        for (int i = 0; i < recordSize; i++) {
230            adnString[i] = (byte) 0xFF;
231        }
232
233        if (TextUtils.isEmpty(mNumber)) {
234            Rlog.w(LOG_TAG, "[buildAdnString] Empty dialing number");
235            return adnString;   // return the empty record (for delete)
236        } else if (mNumber.length()
237                > (ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1) * 2) {
238            Rlog.w(LOG_TAG,
239                    "[buildAdnString] Max length of dialing number is 20");
240            return null;
241        } else if (mAlphaTag != null && mAlphaTag.length() > footerOffset) {
242            Rlog.w(LOG_TAG,
243                    "[buildAdnString] Max length of tag is " + footerOffset);
244            return null;
245        } else {
246            bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(mNumber);
247
248            System.arraycopy(bcdNumber, 0, adnString,
249                    footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
250
251            adnString[footerOffset + ADN_BCD_NUMBER_LENGTH]
252                    = (byte) (bcdNumber.length);
253            adnString[footerOffset + ADN_CAPABILITY_ID]
254                    = (byte) 0xFF; // Capability Id
255            adnString[footerOffset + ADN_EXTENSION_ID]
256                    = (byte) 0xFF; // Extension Record Id
257
258            if (!TextUtils.isEmpty(mAlphaTag)) {
259                byteTag = GsmAlphabet.stringToGsm8BitPacked(mAlphaTag);
260                System.arraycopy(byteTag, 0, adnString, 0, byteTag.length);
261            }
262
263            return adnString;
264        }
265    }
266
267    /**
268     * See TS 51.011 10.5.10
269     */
270    public void
271    appendExtRecord (byte[] extRecord) {
272        try {
273            if (extRecord.length != EXT_RECORD_LENGTH_BYTES) {
274                return;
275            }
276
277            if ((extRecord[0] & EXT_RECORD_TYPE_MASK)
278                    != EXT_RECORD_TYPE_ADDITIONAL_DATA) {
279                return;
280            }
281
282            if ((0xff & extRecord[1]) > MAX_EXT_CALLED_PARTY_LENGTH) {
283                // invalid or empty record
284                return;
285            }
286
287            mNumber += PhoneNumberUtils.calledPartyBCDFragmentToString(
288                                        extRecord, 2, 0xff & extRecord[1]);
289
290            // We don't support ext record chaining.
291
292        } catch (RuntimeException ex) {
293            Rlog.w(LOG_TAG, "Error parsing AdnRecord ext record", ex);
294        }
295    }
296
297    //***** Private Methods
298
299    /**
300     * alphaTag and number are set to null on invalid format
301     */
302    private void
303    parseRecord(byte[] record) {
304        try {
305            mAlphaTag = IccUtils.adnStringFieldToString(
306                            record, 0, record.length - FOOTER_SIZE_BYTES);
307
308            int footerOffset = record.length - FOOTER_SIZE_BYTES;
309
310            int numberLength = 0xff & record[footerOffset];
311
312            if (numberLength > MAX_NUMBER_SIZE_BYTES) {
313                // Invalid number length
314                mNumber = "";
315                return;
316            }
317
318            // Please note 51.011 10.5.1:
319            //
320            // "If the Dialling Number/SSC String does not contain
321            // a dialling number, e.g. a control string deactivating
322            // a service, the TON/NPI byte shall be set to 'FF' by
323            // the ME (see note 2)."
324
325            mNumber = PhoneNumberUtils.calledPartyBCDToString(
326                            record, footerOffset + 1, numberLength);
327
328
329            mExtRecord = 0xff & record[record.length - 1];
330
331            mEmails = null;
332
333        } catch (RuntimeException ex) {
334            Rlog.w(LOG_TAG, "Error parsing AdnRecord", ex);
335            mNumber = "";
336            mAlphaTag = "";
337            mEmails = null;
338        }
339    }
340}
341