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