1/*
2**
3** Copyright (C) 2014, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18/*
19 * This class is used to create, load tables for HBPCD
20 * HBPCD means 'Handset Based Plus Code Dialing', for CDMA network, most of network
21 * couldn't handle international dialing number with '+', it need to be converted
22 * to a IDD (International Direct Dialing) number, and some CDMA network won't
23 * broadcast operator numeric, we need CDMA system ID and timezone etc. information
24 * to get right MCC part of numeric, MNC part of numeric has no way to get in this
25 * case, but for HBPCD, the MCC is enough.
26 *
27 * Table TABLE_MCC_LOOKUP_TABLE
28 * This table has country name, country code, time zones for each MCC
29 *
30 * Table TABLE_MCC_IDD
31 * This table has the IDDs for each MCC, some countries have multiple IDDs.
32 *
33 * Table TABLE_MCC_SID_RANGE
34 * This table are SIDs assigned to each MCC
35 *
36 * Table TABLE_MCC_SID_CONFLICT
37 * This table shows those SIDs are assigned to more than 1 MCC entry,
38 * if the SID is here, it means the SID couldn't be matched to a single MCC,
39 * it need to check the time zone and SID in TABLE_MCC_LOOKUP_TABLE to get
40 * right MCC.
41 *
42 * Table TABLE_ARBITRARY_MCC_SID_MATCH
43 * The SID listed in this table technically have operators in multiple MCC,
44 * but conveniently only have *active* operators in a single MCC allowing a
45 * unique SID->MCC lookup.  Lookup by Timezone however would be complicatedi
46 * as there will be multiple matches, and those matched entries have same
47 * time zone, which can not tell which MCC is right. Conventionaly it is known
48 * that SID is used only by the *active* operators in that MCC.
49 *
50 * Table TABLE_NANP_AREA_CODE
51 * This table has NANP(North America Number Planning) area code, this is used
52 * to check if a dialing number is a NANP number.
53 */
54
55package com.android.providers.telephony;
56
57import android.content.ContentValues;
58import android.content.Context;
59import android.content.res.Resources;
60import android.content.res.XmlResourceParser;
61import android.database.Cursor;
62import android.database.SQLException;
63import android.database.sqlite.SQLiteDatabase;
64import android.database.sqlite.SQLiteOpenHelper;
65import android.database.sqlite.SQLiteQueryBuilder;
66import android.util.Log;
67import android.util.Xml;
68import com.android.internal.util.XmlUtils;
69
70import org.xmlpull.v1.XmlPullParser;
71import org.xmlpull.v1.XmlPullParserException;
72
73import java.io.IOException;
74
75import com.android.internal.telephony.HbpcdLookup;
76import com.android.internal.telephony.HbpcdLookup.MccIdd;
77import com.android.internal.telephony.HbpcdLookup.MccLookup;
78import com.android.internal.telephony.HbpcdLookup.MccSidConflicts;
79import com.android.internal.telephony.HbpcdLookup.MccSidRange;
80import com.android.internal.telephony.HbpcdLookup.ArbitraryMccSidMatch;
81import com.android.internal.telephony.HbpcdLookup.NanpAreaCode;
82
83public class HbpcdLookupDatabaseHelper extends SQLiteOpenHelper {
84    private static final String TAG = "HbpcdLockupDatabaseHelper";
85    private static final boolean DBG = true;
86
87    private static final String DATABASE_NAME = "HbpcdLookup.db";
88    private static final int DATABASE_VERSION = 1;
89    private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
90
91    // Context to access resources with
92    private Context mContext;
93
94    /**
95     * DatabaseHelper helper class for loading apns into a database.
96     *
97     * @param context of the user.
98     */
99    public HbpcdLookupDatabaseHelper(Context context) {
100        super(context, DATABASE_NAME, null, DATABASE_VERSION);
101
102        mContext = context;
103        // Memory optimization - close idle connections after 30s of inactivity
104        setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
105    }
106
107    @Override
108    public void onCreate(SQLiteDatabase db) {
109        //set up the database schema
110        // 1 MCC may has more IDDs
111        db.execSQL("CREATE TABLE " + HbpcdLookupProvider.TABLE_MCC_IDD +
112            "(_id INTEGER PRIMARY KEY," +
113                "MCC INTEGER," +
114                "IDD TEXT);");
115
116        db.execSQL("CREATE TABLE " + HbpcdLookupProvider.TABLE_MCC_LOOKUP_TABLE +
117            "(_id INTEGER PRIMARY KEY," +
118                "MCC INTEGER," +
119                "Country_Code TEXT," +
120                "Country_Name TEXT," +
121                "NDD TEXT," +
122                "NANPS BOOLEAN," +
123                "GMT_Offset_Low REAL," +
124                "GMT_Offset_High REAL," +
125                "GMT_DST_Low REAL," +
126                "GMT_DST_High REAL);");
127
128        db.execSQL("CREATE TABLE " + HbpcdLookupProvider.TABLE_MCC_SID_CONFLICT +
129            "(_id INTEGER PRIMARY KEY," +
130                "MCC INTEGER," +
131                "SID_Conflict INTEGER);");
132
133        db.execSQL("CREATE TABLE " + HbpcdLookupProvider.TABLE_MCC_SID_RANGE +
134            "(_id INTEGER PRIMARY KEY," +
135                "MCC INTEGER," +
136                "SID_Range_Low INTEGER," +
137                "SID_Range_High INTEGER);");
138
139        db.execSQL("CREATE TABLE " + HbpcdLookupProvider.TABLE_NANP_AREA_CODE +
140            "(_id INTEGER PRIMARY KEY," +
141                "AREA_CODE INTEGER UNIQUE);");
142
143        db.execSQL("CREATE TABLE " + HbpcdLookupProvider.TABLE_ARBITRARY_MCC_SID_MATCH +
144            "(_id INTEGER PRIMARY KEY," +
145                "MCC INTEGER," +
146                "SID INTEGER UNIQUE);");
147
148        initDatabase(db);
149    }
150
151    @Override
152    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
153        // do nothing
154    }
155
156    private void initDatabase (SQLiteDatabase db) {
157        // Read internal data from xml
158        Resources r = mContext.getResources();
159        XmlResourceParser parser = r.getXml(R.xml.hbpcd_lookup_tables);
160
161        if (parser == null) {
162           Log.e (TAG, "error to load the HBPCD resource");
163        } else {
164            try {
165                db.beginTransaction();
166                XmlUtils.beginDocument(parser, "hbpcd_info");
167
168                int eventType = parser.getEventType();
169                String tagName = parser.getName();
170
171                while (eventType != XmlPullParser.END_DOCUMENT) {
172                    if (eventType == XmlPullParser.START_TAG
173                            && tagName.equalsIgnoreCase("table")) {
174                        String tableName = parser.getAttributeValue(null, "name");
175                        loadTable(db, parser, tableName);
176                    }
177                    parser.next();
178                    eventType = parser.getEventType();
179                    tagName = parser.getName();
180                }
181                db.setTransactionSuccessful();
182            } catch (XmlPullParserException e) {
183                Log.e (TAG, "Got XmlPullParserException when load hbpcd info");
184            } catch (IOException e) {
185                Log.e (TAG, "Got IOException when load hbpcd info");
186            } catch (SQLException e) {
187                Log.e (TAG, "Got SQLException when load hbpcd info");
188            } finally {
189                db.endTransaction();
190                parser.close();
191            }
192        }
193    }
194
195    private void loadTable(SQLiteDatabase db, XmlPullParser parser, String tableName)
196            throws XmlPullParserException, IOException {
197        int eventType = parser.getEventType();
198        String tagName = parser.getName();
199        while (!(eventType == XmlPullParser.END_TAG
200                && tagName.equalsIgnoreCase("table"))) {
201            ContentValues row = null;
202            if (tableName.equalsIgnoreCase(HbpcdLookupProvider.TABLE_MCC_IDD)) {
203                row = getTableMccIddRow(parser);
204            } else if (tableName.equalsIgnoreCase(HbpcdLookupProvider.TABLE_MCC_LOOKUP_TABLE)) {
205                row = getTableMccLookupTableRow(parser);
206            } else if (tableName.equalsIgnoreCase(HbpcdLookupProvider.TABLE_MCC_SID_CONFLICT)) {
207                row = getTableMccSidConflictRow(parser);
208            } else if (tableName.equalsIgnoreCase(HbpcdLookupProvider.TABLE_MCC_SID_RANGE)) {
209                row = getTableMccSidRangeRow(parser);
210            } else if (tableName.equalsIgnoreCase(HbpcdLookupProvider.TABLE_NANP_AREA_CODE)) {
211                row = getTableNanpAreaCodeRow(parser);
212            } else if (tableName.equalsIgnoreCase(
213                    HbpcdLookupProvider.TABLE_ARBITRARY_MCC_SID_MATCH)) {
214                row = getTableArbitraryMccSidMatch(parser);
215            } else {
216                Log.e(TAG, "unrecognized table name"  + tableName);
217                break;
218            }
219            if (row != null) {
220                db.insert(tableName, null, row);
221            }
222            parser.next();
223            eventType = parser.getEventType();
224            tagName = parser.getName();
225        }
226    }
227
228    private ContentValues getTableMccIddRow(XmlPullParser parser)
229            throws XmlPullParserException, IOException {
230        int eventType = parser.getEventType();
231        String tagName = parser.getName();
232        ContentValues row = new ContentValues();
233
234        while (!(eventType == XmlPullParser.END_TAG && tagName.equalsIgnoreCase("row"))) {
235            if (eventType == XmlPullParser.START_TAG) {
236                if (tagName.equalsIgnoreCase(MccIdd.MCC)) {
237                    row.put(MccIdd.MCC, Integer.parseInt(parser.nextText()));
238                } else if (tagName.equalsIgnoreCase(MccIdd.IDD)) {
239                    row.put(MccIdd.IDD, parser.nextText());
240                }
241            }
242            parser.next();
243            eventType = parser.getEventType();
244            tagName = parser.getName();
245        }
246        return row;
247    }
248
249    private ContentValues getTableMccLookupTableRow(XmlPullParser parser)
250            throws XmlPullParserException, IOException {
251        int eventType = parser.getEventType();
252        String tagName = parser.getName();
253        ContentValues row = new ContentValues();
254
255        while (!(eventType == XmlPullParser.END_TAG && tagName.equalsIgnoreCase("row"))) {
256            if (eventType == XmlPullParser.START_TAG) {
257                if (tagName.equalsIgnoreCase(MccLookup.MCC)) {
258                    row.put(MccLookup.MCC, Integer.parseInt(parser.nextText()));
259                } else if (tagName.equalsIgnoreCase(MccLookup.COUNTRY_CODE)) {
260                    row.put(MccLookup.COUNTRY_CODE, Integer.parseInt(parser.nextText()));
261                } else if (tagName.equalsIgnoreCase(MccLookup.COUNTRY_NAME)) {
262                    row.put(MccLookup.COUNTRY_NAME, parser.nextText());
263                } else if (tagName.equalsIgnoreCase(MccLookup.NDD)) {
264                    row.put(MccLookup.NDD, parser.nextText());
265                } else if (tagName.equalsIgnoreCase(MccLookup.NANPS)) {
266                    row.put(MccLookup.NANPS, Boolean.parseBoolean(parser.nextText()));
267                } else if (tagName.equalsIgnoreCase(MccLookup.GMT_OFFSET_LOW)) {
268                    row.put(MccLookup.GMT_OFFSET_LOW, Float.parseFloat(parser.nextText()));
269                } else if (tagName.equalsIgnoreCase(MccLookup.GMT_OFFSET_HIGH)) {
270                    row.put(MccLookup.GMT_OFFSET_HIGH, Float.parseFloat(parser.nextText()));
271                } else if (tagName.equalsIgnoreCase(MccLookup.GMT_DST_LOW)) {
272                    row.put(MccLookup.GMT_DST_LOW, Float.parseFloat(parser.nextText()));
273                } else if (tagName.equalsIgnoreCase(MccLookup.GMT_DST_HIGH)) {
274                    row.put(MccLookup.GMT_DST_HIGH, Float.parseFloat(parser.nextText()));
275                }
276            }
277            parser.next();
278            eventType = parser.getEventType();
279            tagName = parser.getName();
280        }
281        return row;
282    }
283
284    private ContentValues getTableMccSidConflictRow(XmlPullParser parser)
285            throws XmlPullParserException, IOException {
286        int eventType = parser.getEventType();
287        String tagName = parser.getName();
288        ContentValues row = new ContentValues();
289
290        while (!(eventType == XmlPullParser.END_TAG && tagName.equalsIgnoreCase("row"))) {
291            if (eventType == XmlPullParser.START_TAG) {
292                if (tagName.equalsIgnoreCase(MccSidConflicts.MCC)) {
293                    row.put(MccSidConflicts.MCC, Integer.parseInt(parser.nextText()));
294                } else if (tagName.equalsIgnoreCase(MccSidConflicts.SID_CONFLICT)) {
295                    row.put(MccSidConflicts.SID_CONFLICT, Integer.parseInt(parser.nextText()));
296                }
297            }
298            parser.next();
299            eventType = parser.getEventType();
300            tagName = parser.getName();
301        }
302        return row;
303    }
304
305    private ContentValues getTableMccSidRangeRow(XmlPullParser parser)
306            throws XmlPullParserException, IOException {
307        int eventType = parser.getEventType();
308        String tagName = parser.getName();
309        ContentValues row = new ContentValues();
310
311        while (!(eventType == XmlPullParser.END_TAG && tagName.equalsIgnoreCase("row"))) {
312            if (eventType == XmlPullParser.START_TAG) {
313                if (tagName.equalsIgnoreCase(MccSidRange.MCC)) {
314                    row.put(MccSidRange.MCC, Integer.parseInt(parser.nextText()));
315                } else if (tagName.equalsIgnoreCase(MccSidRange.RANGE_LOW)) {
316                    row.put(MccSidRange.RANGE_LOW, Integer.parseInt(parser.nextText()));
317                } else if (tagName.equalsIgnoreCase(MccSidRange.RANGE_HIGH)) {
318                    row.put(MccSidRange.RANGE_HIGH, Integer.parseInt(parser.nextText()));
319                }
320            }
321            parser.next();
322            eventType = parser.getEventType();
323            tagName = parser.getName();
324       }
325       return row;
326    }
327
328    private ContentValues getTableNanpAreaCodeRow(XmlPullParser parser)
329            throws XmlPullParserException, IOException {
330        int eventType = parser.getEventType();
331        String tagName = parser.getName();
332        ContentValues row = new ContentValues();
333
334        while (!(eventType == XmlPullParser.END_TAG && tagName.equalsIgnoreCase("row"))) {
335            if (eventType == XmlPullParser.START_TAG) {
336                if (tagName.equalsIgnoreCase(NanpAreaCode.AREA_CODE)) {
337                    row.put(NanpAreaCode.AREA_CODE, Integer.parseInt(parser.nextText()));
338                }
339            }
340            parser.next();
341            eventType = parser.getEventType();
342            tagName = parser.getName();
343        }
344        return row;
345    }
346
347    private ContentValues getTableArbitraryMccSidMatch(XmlPullParser parser)
348            throws XmlPullParserException, IOException {
349        int eventType = parser.getEventType();
350        String tagName = parser.getName();
351        ContentValues row = new ContentValues();
352
353        while (!(eventType == XmlPullParser.END_TAG && tagName.equalsIgnoreCase("row"))) {
354            if (eventType == XmlPullParser.START_TAG) {
355                if (tagName.equalsIgnoreCase(ArbitraryMccSidMatch.MCC)) {
356                    row.put(ArbitraryMccSidMatch.MCC, Integer.parseInt(parser.nextText()));
357                } else if (tagName.equalsIgnoreCase(ArbitraryMccSidMatch.SID)) {
358                    row.put(ArbitraryMccSidMatch.SID, Integer.parseInt(parser.nextText()));
359                }
360            }
361            parser.next();
362            eventType = parser.getEventType();
363            tagName = parser.getName();
364        }
365        return row;
366    }
367}
368