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