AdnRecord.java revision d720945f2be5ea5fe0faf67e67d9ea0e184eba67
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 = "GSM";
39
40    //***** Instance Variables
41
42    String alphaTag = null;
43    String number = null;
44    String[] emails;
45    int extRecord = 0xff;
46    int efid;                   // or 0 if none
47    int recordNumber;           // 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        public AdnRecord createFromParcel(Parcel source) {
77            int efid;
78            int recordNumber;
79            String alphaTag;
80            String number;
81            String[] emails;
82
83            efid = source.readInt();
84            recordNumber = source.readInt();
85            alphaTag = source.readString();
86            number = source.readString();
87            emails = source.readStringArray();
88
89            return new AdnRecord(efid, recordNumber, alphaTag, number, emails);
90        }
91
92        public AdnRecord[] newArray(int size) {
93            return new AdnRecord[size];
94        }
95    };
96
97
98    //***** Constructor
99    public AdnRecord (byte[] record) {
100        this(0, 0, record);
101    }
102
103    public AdnRecord (int efid, int recordNumber, byte[] record) {
104        this.efid = efid;
105        this.recordNumber = recordNumber;
106        parseRecord(record);
107    }
108
109    public AdnRecord (String alphaTag, String number) {
110        this(0, 0, alphaTag, number);
111    }
112
113    public AdnRecord (String alphaTag, String number, String[] emails) {
114        this(0, 0, alphaTag, number, emails);
115    }
116
117    public AdnRecord (int efid, int recordNumber, String alphaTag, String number, String[] emails) {
118        this.efid = efid;
119        this.recordNumber = recordNumber;
120        this.alphaTag = alphaTag;
121        this.number = number;
122        this.emails = emails;
123    }
124
125    public AdnRecord(int efid, int recordNumber, String alphaTag, String number) {
126        this.efid = efid;
127        this.recordNumber = recordNumber;
128        this.alphaTag = alphaTag;
129        this.number = number;
130        this.emails = null;
131    }
132
133    //***** Instance Methods
134
135    public String getAlphaTag() {
136        return alphaTag;
137    }
138
139    public String getNumber() {
140        return number;
141    }
142
143    public String[] getEmails() {
144        return emails;
145    }
146
147    public void setEmails(String[] emails) {
148        this.emails = emails;
149    }
150
151    public String toString() {
152        return "ADN Record '" + alphaTag + "' '" + number + " " + emails + "'";
153    }
154
155    public boolean isEmpty() {
156        return TextUtils.isEmpty(alphaTag) && TextUtils.isEmpty(number) && emails == null;
157    }
158
159    public boolean hasExtendedRecord() {
160        return extRecord != 0 && extRecord != 0xff;
161    }
162
163    /** Helper function for {@link #isEqual}. */
164    private static boolean stringCompareNullEqualsEmpty(String s1, String s2) {
165        if (s1 == s2) {
166            return true;
167        }
168        if (s1 == null) {
169            s1 = "";
170        }
171        if (s2 == null) {
172            s2 = "";
173        }
174        return (s1.equals(s2));
175    }
176
177    public boolean isEqual(AdnRecord adn) {
178        return ( stringCompareNullEqualsEmpty(alphaTag, adn.alphaTag) &&
179                stringCompareNullEqualsEmpty(number, adn.number) &&
180                Arrays.equals(emails, adn.emails));
181    }
182    //***** Parcelable Implementation
183
184    public int describeContents() {
185        return 0;
186    }
187
188    public void writeToParcel(Parcel dest, int flags) {
189        dest.writeInt(efid);
190        dest.writeInt(recordNumber);
191        dest.writeString(alphaTag);
192        dest.writeString(number);
193        dest.writeStringArray(emails);
194    }
195
196    /**
197     * Build adn hex byte array based on record size
198     * The format of byte array is defined in 51.011 10.5.1
199     *
200     * @param recordSize is the size X of EF record
201     * @return hex byte[recordSize] to be written to EF record
202     *          return null for wrong format of dialing number or tag
203     */
204    public byte[] buildAdnString(int recordSize) {
205        byte[] bcdNumber;
206        byte[] byteTag;
207        byte[] adnString;
208        int footerOffset = recordSize - FOOTER_SIZE_BYTES;
209
210        // create an empty record
211        adnString = new byte[recordSize];
212        for (int i = 0; i < recordSize; i++) {
213            adnString[i] = (byte) 0xFF;
214        }
215
216        if (TextUtils.isEmpty(number)) {
217            Rlog.w(LOG_TAG, "[buildAdnString] Empty dialing number");
218            return adnString;   // return the empty record (for delete)
219        } else if (number.length()
220                > (ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1) * 2) {
221            Rlog.w(LOG_TAG,
222                    "[buildAdnString] Max length of dialing number is 20");
223            return null;
224        } else if (alphaTag != null && alphaTag.length() > footerOffset) {
225            Rlog.w(LOG_TAG,
226                    "[buildAdnString] Max length of tag is " + footerOffset);
227            return null;
228        } else {
229            bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(number);
230
231            System.arraycopy(bcdNumber, 0, adnString,
232                    footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
233
234            adnString[footerOffset + ADN_BCD_NUMBER_LENGTH]
235                    = (byte) (bcdNumber.length);
236            adnString[footerOffset + ADN_CAPABILITY_ID]
237                    = (byte) 0xFF; // Capability Id
238            adnString[footerOffset + ADN_EXTENSION_ID]
239                    = (byte) 0xFF; // Extension Record Id
240
241            if (!TextUtils.isEmpty(alphaTag)) {
242                byteTag = GsmAlphabet.stringToGsm8BitPacked(alphaTag);
243                System.arraycopy(byteTag, 0, adnString, 0, byteTag.length);
244            }
245
246            return adnString;
247        }
248    }
249
250    /**
251     * See TS 51.011 10.5.10
252     */
253    public void
254    appendExtRecord (byte[] extRecord) {
255        try {
256            if (extRecord.length != EXT_RECORD_LENGTH_BYTES) {
257                return;
258            }
259
260            if ((extRecord[0] & EXT_RECORD_TYPE_MASK)
261                    != EXT_RECORD_TYPE_ADDITIONAL_DATA) {
262                return;
263            }
264
265            if ((0xff & extRecord[1]) > MAX_EXT_CALLED_PARTY_LENGTH) {
266                // invalid or empty record
267                return;
268            }
269
270            number += PhoneNumberUtils.calledPartyBCDFragmentToString(
271                                        extRecord, 2, 0xff & extRecord[1]);
272
273            // We don't support ext record chaining.
274
275        } catch (RuntimeException ex) {
276            Rlog.w(LOG_TAG, "Error parsing AdnRecord ext record", ex);
277        }
278    }
279
280    //***** Private Methods
281
282    /**
283     * alphaTag and number are set to null on invalid format
284     */
285    private void
286    parseRecord(byte[] record) {
287        try {
288            alphaTag = IccUtils.adnStringFieldToString(
289                            record, 0, record.length - FOOTER_SIZE_BYTES);
290
291            int footerOffset = record.length - FOOTER_SIZE_BYTES;
292
293            int numberLength = 0xff & record[footerOffset];
294
295            if (numberLength > MAX_NUMBER_SIZE_BYTES) {
296                // Invalid number length
297                number = "";
298                return;
299            }
300
301            // Please note 51.011 10.5.1:
302            //
303            // "If the Dialling Number/SSC String does not contain
304            // a dialling number, e.g. a control string deactivating
305            // a service, the TON/NPI byte shall be set to 'FF' by
306            // the ME (see note 2)."
307
308            number = PhoneNumberUtils.calledPartyBCDToString(
309                            record, footerOffset + 1, numberLength);
310
311
312            extRecord = 0xff & record[record.length - 1];
313
314            emails = null;
315
316        } catch (RuntimeException ex) {
317            Rlog.w(LOG_TAG, "Error parsing AdnRecord", ex);
318            number = "";
319            alphaTag = "";
320            emails = null;
321        }
322    }
323}
324