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