TelephonyProvider.java revision 7c6edbe10dbbf1744b2b631ae23a5648f307fa14
1/* //device/content/providers/telephony/TelephonyProvider.java
2**
3** Copyright 2006, 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
18package com.android.providers.telephony;
19
20import android.content.ContentProvider;
21import android.content.ContentUris;
22import android.content.ContentValues;
23import android.content.Context;
24import android.content.SharedPreferences;
25import android.content.UriMatcher;
26import android.content.pm.PackageManager;
27import android.content.res.Resources;
28import android.content.res.XmlResourceParser;
29import android.database.Cursor;
30import android.database.SQLException;
31import android.database.sqlite.SQLiteDatabase;
32import android.database.sqlite.SQLiteException;
33import android.database.sqlite.SQLiteOpenHelper;
34import android.database.sqlite.SQLiteQueryBuilder;
35import android.net.Uri;
36import android.os.Binder;
37import android.os.Environment;
38import android.provider.Telephony;
39import android.telephony.SubscriptionManager;
40import android.telephony.TelephonyManager;
41import android.util.Log;
42import android.util.Xml;
43
44import com.android.internal.telephony.BaseCommands;
45import com.android.internal.telephony.Phone;
46import com.android.internal.telephony.PhoneConstants;
47import com.android.internal.util.XmlUtils;
48
49import org.xmlpull.v1.XmlPullParser;
50import org.xmlpull.v1.XmlPullParserException;
51
52import java.io.File;
53import java.io.FileNotFoundException;
54import java.io.FileReader;
55import java.io.IOException;
56import java.lang.NumberFormatException;
57
58public class TelephonyProvider extends ContentProvider
59{
60    private static final String DATABASE_NAME = "telephony.db";
61    private static final boolean DBG = true;
62    private static final boolean VDBG = false;
63
64    private static final int DATABASE_VERSION = 12 << 16;
65    private static final int URL_UNKNOWN = 0;
66    private static final int URL_TELEPHONY = 1;
67    private static final int URL_CURRENT = 2;
68    private static final int URL_ID = 3;
69    private static final int URL_RESTOREAPN = 4;
70    private static final int URL_PREFERAPN = 5;
71    private static final int URL_PREFERAPN_NO_UPDATE = 6;
72    private static final int URL_SIMINFO = 7;
73    private static final int URL_TELEPHONY_USING_SUBID = 8;
74    private static final int URL_CURRENT_USING_SUBID = 9;
75    private static final int URL_RESTOREAPN_USING_SUBID = 10;
76    private static final int URL_PREFERAPN_USING_SUBID = 11;
77    private static final int URL_PREFERAPN_NO_UPDATE_USING_SUBID = 12;
78    private static final int URL_SIMINFO_USING_SUBID = 13;
79
80    private static final String TAG = "TelephonyProvider";
81    private static final String CARRIERS_TABLE = "carriers";
82    private static final String SIMINFO_TABLE = "siminfo";
83
84    private static final String PREF_FILE = "preferred-apn";
85    private static final String COLUMN_APN_ID = "apn_id";
86
87    private static final String PARTNER_APNS_PATH = "etc/apns-conf.xml";
88
89    private static final UriMatcher s_urlMatcher = new UriMatcher(UriMatcher.NO_MATCH);
90
91    private static final ContentValues s_currentNullMap;
92    private static final ContentValues s_currentSetMap;
93
94    static {
95        s_urlMatcher.addURI("telephony", "carriers", URL_TELEPHONY);
96        s_urlMatcher.addURI("telephony", "carriers/current", URL_CURRENT);
97        s_urlMatcher.addURI("telephony", "carriers/#", URL_ID);
98        s_urlMatcher.addURI("telephony", "carriers/restore", URL_RESTOREAPN);
99        s_urlMatcher.addURI("telephony", "carriers/preferapn", URL_PREFERAPN);
100        s_urlMatcher.addURI("telephony", "carriers/preferapn_no_update", URL_PREFERAPN_NO_UPDATE);
101
102        s_urlMatcher.addURI("telephony", "siminfo", URL_SIMINFO);
103
104        s_urlMatcher.addURI("telephony", "carriers/subId/*", URL_TELEPHONY_USING_SUBID);
105        s_urlMatcher.addURI("telephony", "carriers/current/subId/*", URL_CURRENT_USING_SUBID);
106        s_urlMatcher.addURI("telephony", "carriers/restore/subId/*", URL_RESTOREAPN_USING_SUBID);
107        s_urlMatcher.addURI("telephony", "carriers/preferapn/subId/*", URL_PREFERAPN_USING_SUBID);
108        s_urlMatcher.addURI("telephony", "carriers/preferapn_no_update/subId/*",
109                URL_PREFERAPN_NO_UPDATE_USING_SUBID);
110
111
112        s_currentNullMap = new ContentValues(1);
113        s_currentNullMap.put("current", (Long) null);
114
115        s_currentSetMap = new ContentValues(1);
116        s_currentSetMap.put("current", "1");
117    }
118
119    private static class DatabaseHelper extends SQLiteOpenHelper {
120        // Context to access resources with
121        private Context mContext;
122
123        /**
124         * DatabaseHelper helper class for loading apns into a database.
125         *
126         * @param context of the user.
127         */
128        public DatabaseHelper(Context context) {
129            super(context, DATABASE_NAME, null, getVersion(context));
130            mContext = context;
131        }
132
133        private static int getVersion(Context context) {
134            if (VDBG) log("getVersion:+");
135            // Get the database version, combining a static schema version and the XML version
136            Resources r = context.getResources();
137            XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns);
138            try {
139                XmlUtils.beginDocument(parser, "apns");
140                int publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));
141                int version = DATABASE_VERSION | publicversion;
142                if (VDBG) log("getVersion:- version=0x" + Integer.toHexString(version));
143                return version;
144            } catch (Exception e) {
145                loge("Can't get version of APN database" + e + " return version=" +
146                        Integer.toHexString(DATABASE_VERSION));
147                return DATABASE_VERSION;
148            } finally {
149                parser.close();
150            }
151        }
152
153        @Override
154        public void onCreate(SQLiteDatabase db) {
155            if (DBG) log("dbh.onCreate:+ db=" + db);
156            createSimInfoTable(db);
157            createCarriersTable(db);
158            initDatabase(db);
159            if (DBG) log("dbh.onCreate:- db=" + db);
160        }
161
162        @Override
163        public void onOpen(SQLiteDatabase db) {
164            if (VDBG) log("dbh.onOpen:+ db=" + db);
165            try {
166                // Try to access the table and create it if "no such table"
167                db.query(SIMINFO_TABLE, null, null, null, null, null, null);
168                if (DBG) log("dbh.onOpen: ok, queried table=" + SIMINFO_TABLE);
169            } catch (SQLiteException e) {
170                loge("Exception " + SIMINFO_TABLE + "e=" + e);
171                if (e.getMessage().startsWith("no such table")) {
172                    createSimInfoTable(db);
173                }
174            }
175            try {
176                db.query(CARRIERS_TABLE, null, null, null, null, null, null);
177                if (DBG) log("dbh.onOpen: ok, queried table=" + CARRIERS_TABLE);
178            } catch (SQLiteException e) {
179                loge("Exception " + CARRIERS_TABLE + " e=" + e);
180                if (e.getMessage().startsWith("no such table")) {
181                    createCarriersTable(db);
182                }
183            }
184            if (VDBG) log("dbh.onOpen:- db=" + db);
185        }
186
187        private void createSimInfoTable(SQLiteDatabase db) {
188            if (DBG) log("dbh.createSimInfoTable:+");
189            db.execSQL("CREATE TABLE " + SIMINFO_TABLE + "("
190                    + "_id INTEGER PRIMARY KEY AUTOINCREMENT,"
191                    + SubscriptionManager.ICC_ID + " TEXT NOT NULL,"
192                    + SubscriptionManager.SIM_ID + " INTEGER DEFAULT " + SubscriptionManager.SIM_NOT_INSERTED + ","
193                    + SubscriptionManager.DISPLAY_NAME + " TEXT,"
194                    + SubscriptionManager.NAME_SOURCE + " INTEGER DEFAULT " + SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE + ","
195                    + SubscriptionManager.COLOR + " INTEGER DEFAULT " + SubscriptionManager.COLOR_DEFAULT + ","
196                    + SubscriptionManager.NUMBER + " TEXT,"
197                    + SubscriptionManager.DISPLAY_NUMBER_FORMAT + " INTEGER NOT NULL DEFAULT " + SubscriptionManager.DISLPAY_NUMBER_DEFAULT + ","
198                    + SubscriptionManager.DATA_ROAMING + " INTEGER DEFAULT " + SubscriptionManager.DATA_ROAMING_DEFAULT + ","
199                    + SubscriptionManager.MCC + " INTEGER DEFAULT 0,"
200                    + SubscriptionManager.MNC + " INTEGER DEFAULT 0"
201                    + ");");
202            if (DBG) log("dbh.createSimInfoTable:-");
203        }
204
205        private void createCarriersTable(SQLiteDatabase db) {
206            // Set up the database schema
207            if (DBG) log("dbh.createCarriersTable:+");
208            db.execSQL("CREATE TABLE " + CARRIERS_TABLE +
209                "(_id INTEGER PRIMARY KEY," +
210                    "name TEXT," +
211                    "numeric TEXT," +
212                    "mcc TEXT," +
213                    "mnc TEXT," +
214                    "apn TEXT," +
215                    "user TEXT," +
216                    "server TEXT," +
217                    "password TEXT," +
218                    "proxy TEXT," +
219                    "port TEXT," +
220                    "mmsproxy TEXT," +
221                    "mmsport TEXT," +
222                    "mmsc TEXT," +
223                    "authtype INTEGER," +
224                    "type TEXT," +
225                    "current INTEGER," +
226                    "protocol TEXT," +
227                    "roaming_protocol TEXT," +
228                    "carrier_enabled BOOLEAN," +
229                    "bearer INTEGER," +
230                    "mvno_type TEXT," +
231                    "mvno_match_data TEXT," +
232                    "sub_id LONG DEFAULT -1," +
233                    "profile_id INTEGER default 0," +
234                    "modem_cognitive BOOLEAN default 0," +
235                    "max_conns INTEGER default 0," +
236                    "wait_time INTEGER default 0," +
237                    "max_conns_time INTEGER default 0," +
238                    "mtu INTEGER);");
239             /* FIXME Currenlty sub_id is column is not used for query purpose.
240             This would be modified to more appropriate default value later. */
241            if (DBG) log("dbh.createCarriersTable:-");
242        }
243        private void initDatabase(SQLiteDatabase db) {
244            if (VDBG) log("dbh.initDatabase:+ db=" + db);
245            // Read internal APNS data
246            Resources r = mContext.getResources();
247            XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns);
248            int publicversion = -1;
249            try {
250                XmlUtils.beginDocument(parser, "apns");
251                publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));
252                loadApns(db, parser);
253            } catch (Exception e) {
254                loge("Got exception while loading APN database." + e);
255            } finally {
256                parser.close();
257            }
258
259            // Read external APNS data (partner-provided)
260            XmlPullParser confparser = null;
261            // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
262            File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH);
263            FileReader confreader = null;
264            try {
265                confreader = new FileReader(confFile);
266                confparser = Xml.newPullParser();
267                confparser.setInput(confreader);
268                XmlUtils.beginDocument(confparser, "apns");
269
270                // Sanity check. Force internal version and confidential versions to agree
271                int confversion = Integer.parseInt(confparser.getAttributeValue(null, "version"));
272                if (publicversion != confversion) {
273                    throw new IllegalStateException("Internal APNS file version doesn't match "
274                            + confFile.getAbsolutePath());
275                }
276
277                loadApns(db, confparser);
278            } catch (FileNotFoundException e) {
279                // It's ok if the file isn't found. It means there isn't a confidential file
280                // Log.e(TAG, "File not found: '" + confFile.getAbsolutePath() + "'");
281            } catch (Exception e) {
282                loge("Exception while parsing '" + confFile.getAbsolutePath() + "'" + e);
283            } finally {
284                try { if (confreader != null) confreader.close(); } catch (IOException e) { }
285            }
286            if (VDBG) log("dbh.initDatabase:- db=" + db);
287
288        }
289
290        @Override
291        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
292            if (DBG) {
293                log("dbh.onUpgrade:+ db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
294            }
295
296            if (oldVersion < (5 << 16 | 6)) {
297                // 5 << 16 is the Database version and 6 in the xml version.
298
299                // This change adds a new authtype column to the database.
300                // The auth type column can have 4 values: 0 (None), 1 (PAP), 2 (CHAP)
301                // 3 (PAP or CHAP). To avoid breaking compatibility, with already working
302                // APNs, the unset value (-1) will be used. If the value is -1.
303                // the authentication will default to 0 (if no user / password) is specified
304                // or to 3. Currently, there have been no reported problems with
305                // pre-configured APNs and hence it is set to -1 for them. Similarly,
306                // if the user, has added a new APN, we set the authentication type
307                // to -1.
308
309                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
310                        " ADD COLUMN authtype INTEGER DEFAULT -1;");
311
312                oldVersion = 5 << 16 | 6;
313            }
314            if (oldVersion < (6 << 16 | 6)) {
315                // Add protcol fields to the APN. The XML file does not change.
316                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
317                        " ADD COLUMN protocol TEXT DEFAULT IP;");
318                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
319                        " ADD COLUMN roaming_protocol TEXT DEFAULT IP;");
320                oldVersion = 6 << 16 | 6;
321            }
322            if (oldVersion < (7 << 16 | 6)) {
323                // Add carrier_enabled, bearer fields to the APN. The XML file does not change.
324                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
325                        " ADD COLUMN carrier_enabled BOOLEAN DEFAULT 1;");
326                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
327                        " ADD COLUMN bearer INTEGER DEFAULT 0;");
328                oldVersion = 7 << 16 | 6;
329            }
330            if (oldVersion < (8 << 16 | 6)) {
331                // Add mvno_type, mvno_match_data fields to the APN.
332                // The XML file does not change.
333                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
334                        " ADD COLUMN mvno_type TEXT DEFAULT '';");
335                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
336                        " ADD COLUMN mvno_match_data TEXT DEFAULT '';");
337                oldVersion = 8 << 16 | 6;
338            }
339            if (oldVersion < (9 << 16 | 6)) {
340                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
341                        " ADD COLUMN sub_id LONG DEFAULT -1;");
342                oldVersion = 9 << 16 | 6;
343            }
344            if (oldVersion < (10 << 16 | 6)) {
345                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
346                        " ADD COLUMN profile_id INTEGER DEFAULT 0;");
347                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
348                        " ADD COLUMN modem_cognitive BOOLEAN DEFAULT 0;");
349                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
350                        " ADD COLUMN max_conns INTEGER DEFAULT 0;");
351                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
352                        " ADD COLUMN wait_time INTEGER DEFAULT 0;");
353                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
354                        " ADD COLUMN max_conns_time INTEGER DEFAULT 0;");
355                oldVersion = 10 << 16 | 6;
356            }
357            if (oldVersion < (11 << 16 | 6)) {
358                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
359                        " ADD COLUMN mtu INTEGER DEFAULT 0;");
360                oldVersion = 11 << 16 | 6;
361            }
362            if (oldVersion < (12 << 16 | 6)) {
363                db.execSQL("ALTER TABLE " + SIMINFO_TABLE +
364                        " ADD COLUMN " + SubscriptionManager.MCC + " INTEGER DEFAULT 0;");
365                db.execSQL("ALTER TABLE " + SIMINFO_TABLE +
366                        " ADD COLUMN " + SubscriptionManager.MNC + " INTEGER DEFAULT 0;");
367                oldVersion = 12 << 16 | 6;
368            }
369            if (DBG) {
370                log("dbh.onUpgrade:- db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
371            }
372        }
373
374        /**
375         * Gets the next row of apn values.
376         *
377         * @param parser the parser
378         * @return the row or null if it's not an apn
379         */
380        private ContentValues getRow(XmlPullParser parser) {
381            if (!"apn".equals(parser.getName())) {
382                return null;
383            }
384
385            ContentValues map = new ContentValues();
386
387            String mcc = parser.getAttributeValue(null, "mcc");
388            String mnc = parser.getAttributeValue(null, "mnc");
389            String numeric = mcc + mnc;
390
391            map.put(Telephony.Carriers.NUMERIC,numeric);
392            map.put(Telephony.Carriers.MCC, mcc);
393            map.put(Telephony.Carriers.MNC, mnc);
394            map.put(Telephony.Carriers.NAME, parser.getAttributeValue(null, "carrier"));
395            map.put(Telephony.Carriers.APN, parser.getAttributeValue(null, "apn"));
396            map.put(Telephony.Carriers.USER, parser.getAttributeValue(null, "user"));
397            map.put(Telephony.Carriers.SERVER, parser.getAttributeValue(null, "server"));
398            map.put(Telephony.Carriers.PASSWORD, parser.getAttributeValue(null, "password"));
399
400            // do not add NULL to the map so that insert() will set the default value
401            String proxy = parser.getAttributeValue(null, "proxy");
402            if (proxy != null) {
403                map.put(Telephony.Carriers.PROXY, proxy);
404            }
405            String port = parser.getAttributeValue(null, "port");
406            if (port != null) {
407                map.put(Telephony.Carriers.PORT, port);
408            }
409            String mmsproxy = parser.getAttributeValue(null, "mmsproxy");
410            if (mmsproxy != null) {
411                map.put(Telephony.Carriers.MMSPROXY, mmsproxy);
412            }
413            String mmsport = parser.getAttributeValue(null, "mmsport");
414            if (mmsport != null) {
415                map.put(Telephony.Carriers.MMSPORT, mmsport);
416            }
417            map.put(Telephony.Carriers.MMSC, parser.getAttributeValue(null, "mmsc"));
418            String type = parser.getAttributeValue(null, "type");
419            if (type != null) {
420                map.put(Telephony.Carriers.TYPE, type);
421            }
422
423            String auth = parser.getAttributeValue(null, "authtype");
424            if (auth != null) {
425                map.put(Telephony.Carriers.AUTH_TYPE, Integer.parseInt(auth));
426            }
427
428            String protocol = parser.getAttributeValue(null, "protocol");
429            if (protocol != null) {
430                map.put(Telephony.Carriers.PROTOCOL, protocol);
431            }
432
433            String roamingProtocol = parser.getAttributeValue(null, "roaming_protocol");
434            if (roamingProtocol != null) {
435                map.put(Telephony.Carriers.ROAMING_PROTOCOL, roamingProtocol);
436            }
437
438            String carrierEnabled = parser.getAttributeValue(null, "carrier_enabled");
439            if (carrierEnabled != null) {
440                map.put(Telephony.Carriers.CARRIER_ENABLED, Boolean.parseBoolean(carrierEnabled));
441            }
442
443            String bearer = parser.getAttributeValue(null, "bearer");
444            if (bearer != null) {
445                map.put(Telephony.Carriers.BEARER, Integer.parseInt(bearer));
446            }
447
448            String mvno_type = parser.getAttributeValue(null, "mvno_type");
449            if (mvno_type != null) {
450                String mvno_match_data = parser.getAttributeValue(null, "mvno_match_data");
451                if (mvno_match_data != null) {
452                    map.put(Telephony.Carriers.MVNO_TYPE, mvno_type);
453                    map.put(Telephony.Carriers.MVNO_MATCH_DATA, mvno_match_data);
454                }
455            }
456
457            String profileId = parser.getAttributeValue(null, "profile_id");
458            if (profileId != null) {
459                map.put(Telephony.Carriers.PROFILE_ID, Integer.parseInt(profileId));
460            }
461
462            String modemCognitive = parser.getAttributeValue(null, "modem_cognitive");
463            if (carrierEnabled != null) {
464                map.put(Telephony.Carriers.MODEM_COGNITIVE, Boolean.parseBoolean(modemCognitive));
465            }
466
467            String maxConns = parser.getAttributeValue(null, "max_conns");
468            if (maxConns != null) {
469                map.put(Telephony.Carriers.MAX_CONNS, Integer.parseInt(maxConns));
470            }
471
472            String waitTime = parser.getAttributeValue(null, "wait_time");
473            if (waitTime != null) {
474                map.put(Telephony.Carriers.WAIT_TIME, Integer.parseInt(waitTime));
475            }
476
477            String maxConnsTime = parser.getAttributeValue(null, "max_conns_time");
478            if (maxConnsTime != null) {
479                map.put(Telephony.Carriers.MAX_CONNS_TIME, Integer.parseInt(maxConnsTime));
480            }
481
482            String mtu = parser.getAttributeValue(null, "mtu");
483            if (mtu != null) {
484                map.put(Telephony.Carriers.MTU, Integer.parseInt(mtu));
485            }
486
487            return map;
488        }
489
490        /*
491         * Loads apns from xml file into the database
492         *
493         * @param db the sqlite database to write to
494         * @param parser the xml parser
495         *
496         */
497        private void loadApns(SQLiteDatabase db, XmlPullParser parser) {
498            if (parser != null) {
499                try {
500                    db.beginTransaction();
501                    XmlUtils.nextElement(parser);
502                    while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
503                        ContentValues row = getRow(parser);
504                        if (row == null) {
505                            throw new XmlPullParserException("Expected 'apn' tag", parser, null);
506                        }
507                        insertAddingDefaults(db, CARRIERS_TABLE, row);
508                        XmlUtils.nextElement(parser);
509                    }
510                    db.setTransactionSuccessful();
511                } catch (XmlPullParserException e) {
512                    loge("Got XmlPullParserException while loading apns." + e);
513                } catch (IOException e) {
514                    loge("Got IOException while loading apns." + e);
515                } catch (SQLException e) {
516                    loge("Got SQLException while loading apns." + e);
517                } finally {
518                    db.endTransaction();
519                }
520            }
521        }
522
523        static public ContentValues setDefaultValue(ContentValues values) {
524            if (!values.containsKey(Telephony.Carriers.NAME)) {
525                values.put(Telephony.Carriers.NAME, "");
526            }
527            if (!values.containsKey(Telephony.Carriers.APN)) {
528                values.put(Telephony.Carriers.APN, "");
529            }
530            if (!values.containsKey(Telephony.Carriers.PORT)) {
531                values.put(Telephony.Carriers.PORT, "");
532            }
533            if (!values.containsKey(Telephony.Carriers.PROXY)) {
534                values.put(Telephony.Carriers.PROXY, "");
535            }
536            if (!values.containsKey(Telephony.Carriers.USER)) {
537                values.put(Telephony.Carriers.USER, "");
538            }
539            if (!values.containsKey(Telephony.Carriers.SERVER)) {
540                values.put(Telephony.Carriers.SERVER, "");
541            }
542            if (!values.containsKey(Telephony.Carriers.PASSWORD)) {
543                values.put(Telephony.Carriers.PASSWORD, "");
544            }
545            if (!values.containsKey(Telephony.Carriers.MMSPORT)) {
546                values.put(Telephony.Carriers.MMSPORT, "");
547            }
548            if (!values.containsKey(Telephony.Carriers.MMSPROXY)) {
549                values.put(Telephony.Carriers.MMSPROXY, "");
550            }
551            if (!values.containsKey(Telephony.Carriers.AUTH_TYPE)) {
552                values.put(Telephony.Carriers.AUTH_TYPE, -1);
553            }
554            if (!values.containsKey(Telephony.Carriers.PROTOCOL)) {
555                values.put(Telephony.Carriers.PROTOCOL, "IP");
556            }
557            if (!values.containsKey(Telephony.Carriers.ROAMING_PROTOCOL)) {
558                values.put(Telephony.Carriers.ROAMING_PROTOCOL, "IP");
559            }
560            if (!values.containsKey(Telephony.Carriers.CARRIER_ENABLED)) {
561                values.put(Telephony.Carriers.CARRIER_ENABLED, true);
562            }
563            if (!values.containsKey(Telephony.Carriers.BEARER)) {
564                values.put(Telephony.Carriers.BEARER, 0);
565            }
566            if (!values.containsKey(Telephony.Carriers.MVNO_TYPE)) {
567                values.put(Telephony.Carriers.MVNO_TYPE, "");
568            }
569            if (!values.containsKey(Telephony.Carriers.MVNO_MATCH_DATA)) {
570                values.put(Telephony.Carriers.MVNO_MATCH_DATA, "");
571            }
572
573            long subId = SubscriptionManager.getDefaultSubId();
574            if (!values.containsKey(Telephony.Carriers.SUB_ID)) {
575                values.put(Telephony.Carriers.SUB_ID, subId);
576            }
577
578            if (!values.containsKey(Telephony.Carriers.PROFILE_ID)) {
579                values.put(Telephony.Carriers.PROFILE_ID, 0);
580            }
581            if (!values.containsKey(Telephony.Carriers.MODEM_COGNITIVE)) {
582                values.put(Telephony.Carriers.MODEM_COGNITIVE, false);
583            }
584            if (!values.containsKey(Telephony.Carriers.MAX_CONNS)) {
585                values.put(Telephony.Carriers.MAX_CONNS, 0);
586            }
587            if (!values.containsKey(Telephony.Carriers.WAIT_TIME)) {
588                values.put(Telephony.Carriers.WAIT_TIME, 0);
589            }
590            if (!values.containsKey(Telephony.Carriers.MAX_CONNS_TIME)) {
591                values.put(Telephony.Carriers.MAX_CONNS_TIME, 0);
592            }
593
594            return values;
595        }
596
597        private void insertAddingDefaults(SQLiteDatabase db, String table, ContentValues row) {
598            row = setDefaultValue(row);
599            db.insert(CARRIERS_TABLE, null, row);
600        }
601    }
602
603    @Override
604    public boolean onCreate() {
605        if (VDBG) log("onCreate:+");
606        mOpenHelper = new DatabaseHelper(getContext());
607        if (VDBG) log("onCreate:- ret true");
608        return true;
609    }
610
611    private void setPreferredApnId(Long id, long subId) {
612        SharedPreferences sp = getContext().getSharedPreferences(
613                PREF_FILE + subId, Context.MODE_PRIVATE);
614        SharedPreferences.Editor editor = sp.edit();
615        editor.putLong(COLUMN_APN_ID, id != null ? id.longValue() : -1);
616        editor.apply();
617    }
618
619    private long getPreferredApnId(long subId) {
620        SharedPreferences sp = getContext().getSharedPreferences(
621                PREF_FILE + subId, Context.MODE_PRIVATE);
622        return sp.getLong(COLUMN_APN_ID, -1);
623    }
624
625    @Override
626    public Cursor query(Uri url, String[] projectionIn, String selection,
627            String[] selectionArgs, String sort) {
628        TelephonyManager mTelephonyManager =
629                (TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);
630        long subId = SubscriptionManager.getDefaultSubId();
631        String subIdString;
632        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
633        qb.setStrict(true); // a little protection from injection attacks
634        qb.setTables("carriers");
635
636        int match = s_urlMatcher.match(url);
637        switch (match) {
638            case URL_TELEPHONY_USING_SUBID: {
639                subIdString = url.getLastPathSegment();
640                try {
641                    subId = Long.parseLong(subIdString);
642                } catch (NumberFormatException e) {
643                    loge("NumberFormatException" + e);
644                    return null;
645                }
646                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
647                qb.appendWhere("numeric = " + mTelephonyManager.getSimOperator(subId));
648                // FIXME alter the selection to pass subId
649                // selection = selection + "and subId = "
650            }
651            //intentional fall through from above case
652            // do nothing
653            case URL_TELEPHONY: {
654                break;
655            }
656
657            case URL_CURRENT_USING_SUBID: {
658                subIdString = url.getLastPathSegment();
659                try {
660                    subId = Long.parseLong(subIdString);
661                } catch (NumberFormatException e) {
662                    loge("NumberFormatException" + e);
663                    return null;
664                }
665                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
666                // FIXME alter the selection to pass subId
667                // selection = selection + "and subId = "
668            }
669            //intentional fall through from above case
670            case URL_CURRENT: {
671                qb.appendWhere("current IS NOT NULL");
672                // do not ignore the selection since MMS may use it.
673                //selection = null;
674                break;
675            }
676
677            case URL_ID: {
678                qb.appendWhere("_id = " + url.getPathSegments().get(1));
679                break;
680            }
681
682            case URL_PREFERAPN_USING_SUBID:
683            case URL_PREFERAPN_NO_UPDATE_USING_SUBID: {
684                subIdString = url.getLastPathSegment();
685                try {
686                    subId = Long.parseLong(subIdString);
687                } catch (NumberFormatException e) {
688                    loge("NumberFormatException" + e);
689                    return null;
690                }
691                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
692            }
693            //intentional fall through from above case
694            case URL_PREFERAPN:
695            case URL_PREFERAPN_NO_UPDATE: {
696                qb.appendWhere("_id = " + getPreferredApnId(subId));
697                break;
698            }
699
700            case URL_SIMINFO: {
701                qb.setTables(SIMINFO_TABLE);
702                break;
703            }
704
705            default: {
706                return null;
707            }
708        }
709
710        if (match != URL_SIMINFO) {
711            if (projectionIn != null) {
712                for (String column : projectionIn) {
713                    if (Telephony.Carriers.TYPE.equals(column) ||
714                            Telephony.Carriers.MMSC.equals(column) ||
715                            Telephony.Carriers.MMSPROXY.equals(column) ||
716                            Telephony.Carriers.MMSPORT.equals(column) ||
717                            Telephony.Carriers.APN.equals(column)) {
718                        // noop
719                    } else {
720                        checkPermission();
721                        break;
722                    }
723                }
724            } else {
725                // null returns all columns, so need permission check
726                checkPermission();
727            }
728        }
729
730        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
731        Cursor ret = null;
732        try {
733            ret = qb.query(db, projectionIn, selection, selectionArgs, null, null, sort);
734        } catch (SQLException e) {
735            loge("got exception when querying: " + e);
736        }
737        if (ret != null)
738            ret.setNotificationUri(getContext().getContentResolver(), url);
739        return ret;
740    }
741
742    @Override
743    public String getType(Uri url)
744    {
745        switch (s_urlMatcher.match(url)) {
746        case URL_TELEPHONY:
747        case URL_TELEPHONY_USING_SUBID:
748            return "vnd.android.cursor.dir/telephony-carrier";
749
750        case URL_ID:
751            return "vnd.android.cursor.item/telephony-carrier";
752
753        case URL_PREFERAPN_USING_SUBID:
754        case URL_PREFERAPN_NO_UPDATE_USING_SUBID:
755        case URL_PREFERAPN:
756        case URL_PREFERAPN_NO_UPDATE:
757            return "vnd.android.cursor.item/telephony-carrier";
758
759        default:
760            throw new IllegalArgumentException("Unknown URL " + url);
761        }
762    }
763
764    @Override
765    public Uri insert(Uri url, ContentValues initialValues)
766    {
767        Uri result = null;
768        long subId = SubscriptionManager.getDefaultSubId();
769
770        checkPermission();
771
772        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
773        int match = s_urlMatcher.match(url);
774        boolean notify = false;
775        switch (match)
776        {
777            case URL_TELEPHONY_USING_SUBID:
778            {
779                String subIdString = url.getLastPathSegment();
780                try {
781                    subId = Long.parseLong(subIdString);
782                } catch (NumberFormatException e) {
783                    loge("NumberFormatException" + e);
784                    return result;
785                }
786                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
787            }
788            //intentional fall through from above case
789
790            case URL_TELEPHONY:
791            {
792                ContentValues values;
793                if (initialValues != null) {
794                    values = new ContentValues(initialValues);
795                } else {
796                    values = new ContentValues();
797                }
798
799                values = DatabaseHelper.setDefaultValue(values);
800
801                long rowID = db.insert(CARRIERS_TABLE, null, values);
802                if (rowID > 0)
803                {
804                    result = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, rowID);
805                    notify = true;
806                }
807
808                if (VDBG) log("inserted " + values.toString() + " rowID = " + rowID);
809                break;
810            }
811
812            case URL_CURRENT_USING_SUBID:
813            {
814                String subIdString = url.getLastPathSegment();
815                try {
816                    subId = Long.parseLong(subIdString);
817                } catch (NumberFormatException e) {
818                    loge("NumberFormatException" + e);
819                    return result;
820                }
821                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
822                // FIXME use subId in the query
823            }
824            //intentional fall through from above case
825
826            case URL_CURRENT:
827            {
828                // null out the previous operator
829                db.update("carriers", s_currentNullMap, "current IS NOT NULL", null);
830
831                String numeric = initialValues.getAsString("numeric");
832                int updated = db.update("carriers", s_currentSetMap,
833                        "numeric = '" + numeric + "'", null);
834
835                if (updated > 0)
836                {
837                    if (VDBG) log("Setting numeric '" + numeric + "' to be the current operator");
838                }
839                else
840                {
841                    loge("Failed setting numeric '" + numeric + "' to the current operator");
842                }
843                break;
844            }
845
846            case URL_PREFERAPN_USING_SUBID:
847            case URL_PREFERAPN_NO_UPDATE_USING_SUBID:
848            {
849                String subIdString = url.getLastPathSegment();
850                try {
851                    subId = Long.parseLong(subIdString);
852                } catch (NumberFormatException e) {
853                    loge("NumberFormatException" + e);
854                    return result;
855                }
856                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
857            }
858            //intentional fall through from above case
859
860            case URL_PREFERAPN:
861            case URL_PREFERAPN_NO_UPDATE:
862            {
863                if (initialValues != null) {
864                    if(initialValues.containsKey(COLUMN_APN_ID)) {
865                        setPreferredApnId(initialValues.getAsLong(COLUMN_APN_ID), subId);
866                    }
867                }
868                break;
869            }
870
871            case URL_SIMINFO: {
872               long id = db.insert(SIMINFO_TABLE, null, initialValues);
873               result = ContentUris.withAppendedId(SubscriptionManager.CONTENT_URI, id);
874               break;
875            }
876        }
877
878        if (notify) {
879            getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null);
880        }
881
882        return result;
883    }
884
885    @Override
886    public int delete(Uri url, String where, String[] whereArgs)
887    {
888        int count = 0;
889        long subId = SubscriptionManager.getDefaultSubId();
890
891        checkPermission();
892
893        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
894        int match = s_urlMatcher.match(url);
895        switch (match)
896        {
897            case URL_TELEPHONY_USING_SUBID:
898            {
899                 String subIdString = url.getLastPathSegment();
900                 try {
901                     subId = Long.parseLong(subIdString);
902                 } catch (NumberFormatException e) {
903                     loge("NumberFormatException" + e);
904                     throw new IllegalArgumentException("Invalid subId " + url);
905                 }
906                 if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
907                // FIXME use subId in query
908            }
909            //intentional fall through from above case
910
911            case URL_TELEPHONY:
912            {
913                count = db.delete(CARRIERS_TABLE, where, whereArgs);
914                break;
915            }
916
917            case URL_CURRENT_USING_SUBID: {
918                String subIdString = url.getLastPathSegment();
919                try {
920                    subId = Long.parseLong(subIdString);
921                } catch (NumberFormatException e) {
922                    loge("NumberFormatException" + e);
923                    throw new IllegalArgumentException("Invalid subId " + url);
924                }
925                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
926                // FIXME use subId in query
927            }
928            //intentional fall through from above case
929
930            case URL_CURRENT:
931            {
932                count = db.delete(CARRIERS_TABLE, where, whereArgs);
933                break;
934            }
935
936            case URL_ID:
937            {
938                count = db.delete(CARRIERS_TABLE, Telephony.Carriers._ID + "=?",
939                        new String[] { url.getLastPathSegment() });
940                break;
941            }
942
943            case URL_RESTOREAPN_USING_SUBID: {
944                String subIdString = url.getLastPathSegment();
945                try {
946                    subId = Long.parseLong(subIdString);
947                } catch (NumberFormatException e) {
948                    loge("NumberFormatException" + e);
949                    throw new IllegalArgumentException("Invalid subId " + url);
950                }
951                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
952                // FIXME use subId in query
953            }
954            case URL_RESTOREAPN: {
955                count = 1;
956                restoreDefaultAPN(subId);
957                break;
958            }
959
960            case URL_PREFERAPN_USING_SUBID:
961            case URL_PREFERAPN_NO_UPDATE_USING_SUBID: {
962                String subIdString = url.getLastPathSegment();
963                try {
964                    subId = Long.parseLong(subIdString);
965                } catch (NumberFormatException e) {
966                    loge("NumberFormatException" + e);
967                    throw new IllegalArgumentException("Invalid subId " + url);
968                }
969                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
970            }
971            //intentional fall through from above case
972
973            case URL_PREFERAPN:
974            case URL_PREFERAPN_NO_UPDATE:
975            {
976                setPreferredApnId((long)-1, subId);
977                if ((match == URL_PREFERAPN) || (match == URL_PREFERAPN_USING_SUBID)) count = 1;
978                break;
979            }
980
981            case URL_SIMINFO: {
982                count = db.delete(SIMINFO_TABLE, where, whereArgs);
983                break;
984            }
985
986            default: {
987                throw new UnsupportedOperationException("Cannot delete that URL: " + url);
988            }
989        }
990
991        if (count > 0) {
992            getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null);
993        }
994
995        return count;
996    }
997
998    @Override
999    public int update(Uri url, ContentValues values, String where, String[] whereArgs)
1000    {
1001        int count = 0;
1002        int uriType = URL_UNKNOWN;
1003        long subId = SubscriptionManager.getDefaultSubId();
1004
1005        checkPermission();
1006
1007        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1008        int match = s_urlMatcher.match(url);
1009        switch (match)
1010        {
1011            case URL_TELEPHONY_USING_SUBID:
1012            {
1013                 String subIdString = url.getLastPathSegment();
1014                 try {
1015                     subId = Long.parseLong(subIdString);
1016                 } catch (NumberFormatException e) {
1017                     loge("NumberFormatException" + e);
1018                     throw new IllegalArgumentException("Invalid subId " + url);
1019                 }
1020                 if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
1021                //FIXME use subId in the query
1022            }
1023            //intentional fall through from above case
1024
1025            case URL_TELEPHONY:
1026            {
1027                count = db.update(CARRIERS_TABLE, values, where, whereArgs);
1028                break;
1029            }
1030
1031            case URL_CURRENT_USING_SUBID:
1032            {
1033                String subIdString = url.getLastPathSegment();
1034                try {
1035                    subId = Long.parseLong(subIdString);
1036                } catch (NumberFormatException e) {
1037                    loge("NumberFormatException" + e);
1038                    throw new IllegalArgumentException("Invalid subId " + url);
1039                }
1040                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
1041                //FIXME use subId in the query
1042            }
1043            //intentional fall through from above case
1044
1045            case URL_CURRENT:
1046            {
1047                count = db.update(CARRIERS_TABLE, values, where, whereArgs);
1048                break;
1049            }
1050
1051            case URL_ID:
1052            {
1053                if (where != null || whereArgs != null) {
1054                    throw new UnsupportedOperationException(
1055                            "Cannot update URL " + url + " with a where clause");
1056                }
1057                count = db.update(CARRIERS_TABLE, values, Telephony.Carriers._ID + "=?",
1058                        new String[] { url.getLastPathSegment() });
1059                break;
1060            }
1061
1062            case URL_PREFERAPN_USING_SUBID:
1063            case URL_PREFERAPN_NO_UPDATE_USING_SUBID:
1064            {
1065                String subIdString = url.getLastPathSegment();
1066                try {
1067                    subId = Long.parseLong(subIdString);
1068                } catch (NumberFormatException e) {
1069                    loge("NumberFormatException" + e);
1070                    throw new IllegalArgumentException("Invalid subId " + url);
1071                }
1072                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
1073            }
1074
1075            case URL_PREFERAPN:
1076            case URL_PREFERAPN_NO_UPDATE:
1077            {
1078                if (values != null) {
1079                    if (values.containsKey(COLUMN_APN_ID)) {
1080                        setPreferredApnId(values.getAsLong(COLUMN_APN_ID), subId);
1081                        if ((match == URL_PREFERAPN) ||
1082                                (match == URL_PREFERAPN_USING_SUBID)) {
1083                            count = 1;
1084                        }
1085                    }
1086                }
1087                break;
1088            }
1089
1090            case URL_SIMINFO: {
1091                count = db.update(SIMINFO_TABLE, values, where, whereArgs);
1092                uriType = URL_SIMINFO;
1093                break;
1094            }
1095
1096            default: {
1097                throw new UnsupportedOperationException("Cannot update that URL: " + url);
1098            }
1099        }
1100
1101        if (count > 0) {
1102            switch (uriType) {
1103                case URL_SIMINFO:
1104                    getContext().getContentResolver().notifyChange(
1105                            SubscriptionManager.CONTENT_URI, null);
1106                    break;
1107                default:
1108                    getContext().getContentResolver().notifyChange(
1109                            Telephony.Carriers.CONTENT_URI, null);
1110            }
1111        }
1112
1113        return count;
1114    }
1115
1116    private void checkPermission() {
1117        int status = getContext().checkCallingOrSelfPermission(
1118                "android.permission.WRITE_APN_SETTINGS");
1119        if (status == PackageManager.PERMISSION_GRANTED) {
1120            return;
1121        }
1122
1123        PackageManager packageManager = getContext().getPackageManager();
1124        String[] packages = packageManager.getPackagesForUid(Binder.getCallingUid());
1125
1126        TelephonyManager telephonyManager =
1127                (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
1128        for (String pkg : packages) {
1129            if (telephonyManager.checkCarrierPrivilegesForPackage(pkg) ==
1130                    TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
1131                return;
1132            }
1133        }
1134        throw new SecurityException("No permission to write APN settings");
1135    }
1136
1137    private DatabaseHelper mOpenHelper;
1138
1139    private void restoreDefaultAPN(long subId) {
1140        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1141
1142        try {
1143            db.delete(CARRIERS_TABLE, null, null);
1144        } catch (SQLException e) {
1145            loge("got exception when deleting to restore: " + e);
1146        }
1147        setPreferredApnId((long)-1, subId);
1148        mOpenHelper.initDatabase(db);
1149    }
1150
1151    /**
1152     * Log with debug
1153     *
1154     * @param s is string log
1155     */
1156    private static void log(String s) {
1157        Log.d(TAG, s);
1158    }
1159
1160    private static void loge(String s) {
1161        Log.e(TAG, s);
1162    }
1163}
1164