TelephonyProvider.java revision a1045b0285e275d583d94ff70863cefae3e2333a
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                try {
364                    // Try to update the siminfo table. It might not be there.
365                    db.execSQL("ALTER TABLE " + SIMINFO_TABLE +
366                            " ADD COLUMN " + SubscriptionManager.MCC + " INTEGER DEFAULT 0;");
367                    db.execSQL("ALTER TABLE " + SIMINFO_TABLE +
368                            " ADD COLUMN " + SubscriptionManager.MNC + " INTEGER DEFAULT 0;");
369                } catch (SQLiteException e) {
370                    if (DBG) {
371                        log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
372                                " The table will get created in onOpen.");
373                    }
374                }
375                oldVersion = 12 << 16 | 6;
376            }
377            if (DBG) {
378                log("dbh.onUpgrade:- db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
379            }
380        }
381
382        /**
383         * Gets the next row of apn values.
384         *
385         * @param parser the parser
386         * @return the row or null if it's not an apn
387         */
388        private ContentValues getRow(XmlPullParser parser) {
389            if (!"apn".equals(parser.getName())) {
390                return null;
391            }
392
393            ContentValues map = new ContentValues();
394
395            String mcc = parser.getAttributeValue(null, "mcc");
396            String mnc = parser.getAttributeValue(null, "mnc");
397            String numeric = mcc + mnc;
398
399            map.put(Telephony.Carriers.NUMERIC,numeric);
400            map.put(Telephony.Carriers.MCC, mcc);
401            map.put(Telephony.Carriers.MNC, mnc);
402            map.put(Telephony.Carriers.NAME, parser.getAttributeValue(null, "carrier"));
403            map.put(Telephony.Carriers.APN, parser.getAttributeValue(null, "apn"));
404            map.put(Telephony.Carriers.USER, parser.getAttributeValue(null, "user"));
405            map.put(Telephony.Carriers.SERVER, parser.getAttributeValue(null, "server"));
406            map.put(Telephony.Carriers.PASSWORD, parser.getAttributeValue(null, "password"));
407
408            // do not add NULL to the map so that insert() will set the default value
409            String proxy = parser.getAttributeValue(null, "proxy");
410            if (proxy != null) {
411                map.put(Telephony.Carriers.PROXY, proxy);
412            }
413            String port = parser.getAttributeValue(null, "port");
414            if (port != null) {
415                map.put(Telephony.Carriers.PORT, port);
416            }
417            String mmsproxy = parser.getAttributeValue(null, "mmsproxy");
418            if (mmsproxy != null) {
419                map.put(Telephony.Carriers.MMSPROXY, mmsproxy);
420            }
421            String mmsport = parser.getAttributeValue(null, "mmsport");
422            if (mmsport != null) {
423                map.put(Telephony.Carriers.MMSPORT, mmsport);
424            }
425            map.put(Telephony.Carriers.MMSC, parser.getAttributeValue(null, "mmsc"));
426            String type = parser.getAttributeValue(null, "type");
427            if (type != null) {
428                map.put(Telephony.Carriers.TYPE, type);
429            }
430
431            String auth = parser.getAttributeValue(null, "authtype");
432            if (auth != null) {
433                map.put(Telephony.Carriers.AUTH_TYPE, Integer.parseInt(auth));
434            }
435
436            String protocol = parser.getAttributeValue(null, "protocol");
437            if (protocol != null) {
438                map.put(Telephony.Carriers.PROTOCOL, protocol);
439            }
440
441            String roamingProtocol = parser.getAttributeValue(null, "roaming_protocol");
442            if (roamingProtocol != null) {
443                map.put(Telephony.Carriers.ROAMING_PROTOCOL, roamingProtocol);
444            }
445
446            String carrierEnabled = parser.getAttributeValue(null, "carrier_enabled");
447            if (carrierEnabled != null) {
448                map.put(Telephony.Carriers.CARRIER_ENABLED, Boolean.parseBoolean(carrierEnabled));
449            }
450
451            String bearer = parser.getAttributeValue(null, "bearer");
452            if (bearer != null) {
453                map.put(Telephony.Carriers.BEARER, Integer.parseInt(bearer));
454            }
455
456            String mvno_type = parser.getAttributeValue(null, "mvno_type");
457            if (mvno_type != null) {
458                String mvno_match_data = parser.getAttributeValue(null, "mvno_match_data");
459                if (mvno_match_data != null) {
460                    map.put(Telephony.Carriers.MVNO_TYPE, mvno_type);
461                    map.put(Telephony.Carriers.MVNO_MATCH_DATA, mvno_match_data);
462                }
463            }
464
465            String profileId = parser.getAttributeValue(null, "profile_id");
466            if (profileId != null) {
467                map.put(Telephony.Carriers.PROFILE_ID, Integer.parseInt(profileId));
468            }
469
470            String modemCognitive = parser.getAttributeValue(null, "modem_cognitive");
471            if (modemCognitive != null) {
472                map.put(Telephony.Carriers.MODEM_COGNITIVE, Boolean.parseBoolean(modemCognitive));
473            }
474
475            String maxConns = parser.getAttributeValue(null, "max_conns");
476            if (maxConns != null) {
477                map.put(Telephony.Carriers.MAX_CONNS, Integer.parseInt(maxConns));
478            }
479
480            String waitTime = parser.getAttributeValue(null, "wait_time");
481            if (waitTime != null) {
482                map.put(Telephony.Carriers.WAIT_TIME, Integer.parseInt(waitTime));
483            }
484
485            String maxConnsTime = parser.getAttributeValue(null, "max_conns_time");
486            if (maxConnsTime != null) {
487                map.put(Telephony.Carriers.MAX_CONNS_TIME, Integer.parseInt(maxConnsTime));
488            }
489
490            String mtu = parser.getAttributeValue(null, "mtu");
491            if (mtu != null) {
492                map.put(Telephony.Carriers.MTU, Integer.parseInt(mtu));
493            }
494
495            return map;
496        }
497
498        /*
499         * Loads apns from xml file into the database
500         *
501         * @param db the sqlite database to write to
502         * @param parser the xml parser
503         *
504         */
505        private void loadApns(SQLiteDatabase db, XmlPullParser parser) {
506            if (parser != null) {
507                try {
508                    db.beginTransaction();
509                    XmlUtils.nextElement(parser);
510                    while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
511                        ContentValues row = getRow(parser);
512                        if (row == null) {
513                            throw new XmlPullParserException("Expected 'apn' tag", parser, null);
514                        }
515                        insertAddingDefaults(db, CARRIERS_TABLE, row);
516                        XmlUtils.nextElement(parser);
517                    }
518                    db.setTransactionSuccessful();
519                } catch (XmlPullParserException e) {
520                    loge("Got XmlPullParserException while loading apns." + e);
521                } catch (IOException e) {
522                    loge("Got IOException while loading apns." + e);
523                } catch (SQLException e) {
524                    loge("Got SQLException while loading apns." + e);
525                } finally {
526                    db.endTransaction();
527                }
528            }
529        }
530
531        static public ContentValues setDefaultValue(ContentValues values) {
532            if (!values.containsKey(Telephony.Carriers.NAME)) {
533                values.put(Telephony.Carriers.NAME, "");
534            }
535            if (!values.containsKey(Telephony.Carriers.APN)) {
536                values.put(Telephony.Carriers.APN, "");
537            }
538            if (!values.containsKey(Telephony.Carriers.PORT)) {
539                values.put(Telephony.Carriers.PORT, "");
540            }
541            if (!values.containsKey(Telephony.Carriers.PROXY)) {
542                values.put(Telephony.Carriers.PROXY, "");
543            }
544            if (!values.containsKey(Telephony.Carriers.USER)) {
545                values.put(Telephony.Carriers.USER, "");
546            }
547            if (!values.containsKey(Telephony.Carriers.SERVER)) {
548                values.put(Telephony.Carriers.SERVER, "");
549            }
550            if (!values.containsKey(Telephony.Carriers.PASSWORD)) {
551                values.put(Telephony.Carriers.PASSWORD, "");
552            }
553            if (!values.containsKey(Telephony.Carriers.MMSPORT)) {
554                values.put(Telephony.Carriers.MMSPORT, "");
555            }
556            if (!values.containsKey(Telephony.Carriers.MMSPROXY)) {
557                values.put(Telephony.Carriers.MMSPROXY, "");
558            }
559            if (!values.containsKey(Telephony.Carriers.AUTH_TYPE)) {
560                values.put(Telephony.Carriers.AUTH_TYPE, -1);
561            }
562            if (!values.containsKey(Telephony.Carriers.PROTOCOL)) {
563                values.put(Telephony.Carriers.PROTOCOL, "IP");
564            }
565            if (!values.containsKey(Telephony.Carriers.ROAMING_PROTOCOL)) {
566                values.put(Telephony.Carriers.ROAMING_PROTOCOL, "IP");
567            }
568            if (!values.containsKey(Telephony.Carriers.CARRIER_ENABLED)) {
569                values.put(Telephony.Carriers.CARRIER_ENABLED, true);
570            }
571            if (!values.containsKey(Telephony.Carriers.BEARER)) {
572                values.put(Telephony.Carriers.BEARER, 0);
573            }
574            if (!values.containsKey(Telephony.Carriers.MVNO_TYPE)) {
575                values.put(Telephony.Carriers.MVNO_TYPE, "");
576            }
577            if (!values.containsKey(Telephony.Carriers.MVNO_MATCH_DATA)) {
578                values.put(Telephony.Carriers.MVNO_MATCH_DATA, "");
579            }
580
581            long subId = SubscriptionManager.getDefaultSubId();
582            if (!values.containsKey(Telephony.Carriers.SUB_ID)) {
583                values.put(Telephony.Carriers.SUB_ID, subId);
584            }
585
586            if (!values.containsKey(Telephony.Carriers.PROFILE_ID)) {
587                values.put(Telephony.Carriers.PROFILE_ID, 0);
588            }
589            if (!values.containsKey(Telephony.Carriers.MODEM_COGNITIVE)) {
590                values.put(Telephony.Carriers.MODEM_COGNITIVE, false);
591            }
592            if (!values.containsKey(Telephony.Carriers.MAX_CONNS)) {
593                values.put(Telephony.Carriers.MAX_CONNS, 0);
594            }
595            if (!values.containsKey(Telephony.Carriers.WAIT_TIME)) {
596                values.put(Telephony.Carriers.WAIT_TIME, 0);
597            }
598            if (!values.containsKey(Telephony.Carriers.MAX_CONNS_TIME)) {
599                values.put(Telephony.Carriers.MAX_CONNS_TIME, 0);
600            }
601
602            return values;
603        }
604
605        private void insertAddingDefaults(SQLiteDatabase db, String table, ContentValues row) {
606            row = setDefaultValue(row);
607            db.insert(CARRIERS_TABLE, null, row);
608        }
609    }
610
611    @Override
612    public boolean onCreate() {
613        if (VDBG) log("onCreate:+");
614        mOpenHelper = new DatabaseHelper(getContext());
615        if (VDBG) log("onCreate:- ret true");
616        return true;
617    }
618
619    private void setPreferredApnId(Long id, long subId) {
620        SharedPreferences sp = getContext().getSharedPreferences(
621                PREF_FILE + subId, Context.MODE_PRIVATE);
622        SharedPreferences.Editor editor = sp.edit();
623        editor.putLong(COLUMN_APN_ID, id != null ? id.longValue() : -1);
624        editor.apply();
625    }
626
627    private long getPreferredApnId(long subId) {
628        SharedPreferences sp = getContext().getSharedPreferences(
629                PREF_FILE + subId, Context.MODE_PRIVATE);
630        return sp.getLong(COLUMN_APN_ID, -1);
631    }
632
633    @Override
634    public Cursor query(Uri url, String[] projectionIn, String selection,
635            String[] selectionArgs, String sort) {
636        TelephonyManager mTelephonyManager =
637                (TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);
638        long subId = SubscriptionManager.getDefaultSubId();
639        String subIdString;
640        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
641        qb.setStrict(true); // a little protection from injection attacks
642        qb.setTables("carriers");
643
644        int match = s_urlMatcher.match(url);
645        switch (match) {
646            case URL_TELEPHONY_USING_SUBID: {
647                subIdString = url.getLastPathSegment();
648                try {
649                    subId = Long.parseLong(subIdString);
650                } catch (NumberFormatException e) {
651                    loge("NumberFormatException" + e);
652                    return null;
653                }
654                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
655                qb.appendWhere("numeric = '" + mTelephonyManager.getSimOperator(subId)+"'");
656                // FIXME alter the selection to pass subId
657                // selection = selection + "and subId = "
658            }
659            //intentional fall through from above case
660            // do nothing
661            case URL_TELEPHONY: {
662                break;
663            }
664
665            case URL_CURRENT_USING_SUBID: {
666                subIdString = url.getLastPathSegment();
667                try {
668                    subId = Long.parseLong(subIdString);
669                } catch (NumberFormatException e) {
670                    loge("NumberFormatException" + e);
671                    return null;
672                }
673                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
674                // FIXME alter the selection to pass subId
675                // selection = selection + "and subId = "
676            }
677            //intentional fall through from above case
678            case URL_CURRENT: {
679                qb.appendWhere("current IS NOT NULL");
680                // do not ignore the selection since MMS may use it.
681                //selection = null;
682                break;
683            }
684
685            case URL_ID: {
686                qb.appendWhere("_id = " + url.getPathSegments().get(1));
687                break;
688            }
689
690            case URL_PREFERAPN_USING_SUBID:
691            case URL_PREFERAPN_NO_UPDATE_USING_SUBID: {
692                subIdString = url.getLastPathSegment();
693                try {
694                    subId = Long.parseLong(subIdString);
695                } catch (NumberFormatException e) {
696                    loge("NumberFormatException" + e);
697                    return null;
698                }
699                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
700            }
701            //intentional fall through from above case
702            case URL_PREFERAPN:
703            case URL_PREFERAPN_NO_UPDATE: {
704                qb.appendWhere("_id = " + getPreferredApnId(subId));
705                break;
706            }
707
708            case URL_SIMINFO: {
709                qb.setTables(SIMINFO_TABLE);
710                break;
711            }
712
713            default: {
714                return null;
715            }
716        }
717
718        if (match != URL_SIMINFO) {
719            if (projectionIn != null) {
720                for (String column : projectionIn) {
721                    if (Telephony.Carriers.TYPE.equals(column) ||
722                            Telephony.Carriers.MMSC.equals(column) ||
723                            Telephony.Carriers.MMSPROXY.equals(column) ||
724                            Telephony.Carriers.MMSPORT.equals(column) ||
725                            Telephony.Carriers.APN.equals(column)) {
726                        // noop
727                    } else {
728                        checkPermission();
729                        break;
730                    }
731                }
732            } else {
733                // null returns all columns, so need permission check
734                checkPermission();
735            }
736        }
737
738        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
739        Cursor ret = null;
740        try {
741            ret = qb.query(db, projectionIn, selection, selectionArgs, null, null, sort);
742        } catch (SQLException e) {
743            loge("got exception when querying: " + e);
744        }
745        if (ret != null)
746            ret.setNotificationUri(getContext().getContentResolver(), url);
747        return ret;
748    }
749
750    @Override
751    public String getType(Uri url)
752    {
753        switch (s_urlMatcher.match(url)) {
754        case URL_TELEPHONY:
755        case URL_TELEPHONY_USING_SUBID:
756            return "vnd.android.cursor.dir/telephony-carrier";
757
758        case URL_ID:
759            return "vnd.android.cursor.item/telephony-carrier";
760
761        case URL_PREFERAPN_USING_SUBID:
762        case URL_PREFERAPN_NO_UPDATE_USING_SUBID:
763        case URL_PREFERAPN:
764        case URL_PREFERAPN_NO_UPDATE:
765            return "vnd.android.cursor.item/telephony-carrier";
766
767        default:
768            throw new IllegalArgumentException("Unknown URL " + url);
769        }
770    }
771
772    @Override
773    public Uri insert(Uri url, ContentValues initialValues)
774    {
775        Uri result = null;
776        long subId = SubscriptionManager.getDefaultSubId();
777
778        checkPermission();
779
780        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
781        int match = s_urlMatcher.match(url);
782        boolean notify = false;
783        switch (match)
784        {
785            case URL_TELEPHONY_USING_SUBID:
786            {
787                String subIdString = url.getLastPathSegment();
788                try {
789                    subId = Long.parseLong(subIdString);
790                } catch (NumberFormatException e) {
791                    loge("NumberFormatException" + e);
792                    return result;
793                }
794                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
795            }
796            //intentional fall through from above case
797
798            case URL_TELEPHONY:
799            {
800                ContentValues values;
801                if (initialValues != null) {
802                    values = new ContentValues(initialValues);
803                } else {
804                    values = new ContentValues();
805                }
806
807                values = DatabaseHelper.setDefaultValue(values);
808
809                long rowID = db.insert(CARRIERS_TABLE, null, values);
810                if (rowID > 0)
811                {
812                    result = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, rowID);
813                    notify = true;
814                }
815
816                if (VDBG) log("inserted " + values.toString() + " rowID = " + rowID);
817                break;
818            }
819
820            case URL_CURRENT_USING_SUBID:
821            {
822                String subIdString = url.getLastPathSegment();
823                try {
824                    subId = Long.parseLong(subIdString);
825                } catch (NumberFormatException e) {
826                    loge("NumberFormatException" + e);
827                    return result;
828                }
829                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
830                // FIXME use subId in the query
831            }
832            //intentional fall through from above case
833
834            case URL_CURRENT:
835            {
836                // null out the previous operator
837                db.update("carriers", s_currentNullMap, "current IS NOT NULL", null);
838
839                String numeric = initialValues.getAsString("numeric");
840                int updated = db.update("carriers", s_currentSetMap,
841                        "numeric = '" + numeric + "'", null);
842
843                if (updated > 0)
844                {
845                    if (VDBG) log("Setting numeric '" + numeric + "' to be the current operator");
846                }
847                else
848                {
849                    loge("Failed setting numeric '" + numeric + "' to the current operator");
850                }
851                break;
852            }
853
854            case URL_PREFERAPN_USING_SUBID:
855            case URL_PREFERAPN_NO_UPDATE_USING_SUBID:
856            {
857                String subIdString = url.getLastPathSegment();
858                try {
859                    subId = Long.parseLong(subIdString);
860                } catch (NumberFormatException e) {
861                    loge("NumberFormatException" + e);
862                    return result;
863                }
864                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
865            }
866            //intentional fall through from above case
867
868            case URL_PREFERAPN:
869            case URL_PREFERAPN_NO_UPDATE:
870            {
871                if (initialValues != null) {
872                    if(initialValues.containsKey(COLUMN_APN_ID)) {
873                        setPreferredApnId(initialValues.getAsLong(COLUMN_APN_ID), subId);
874                    }
875                }
876                break;
877            }
878
879            case URL_SIMINFO: {
880               long id = db.insert(SIMINFO_TABLE, null, initialValues);
881               result = ContentUris.withAppendedId(SubscriptionManager.CONTENT_URI, id);
882               break;
883            }
884        }
885
886        if (notify) {
887            getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null);
888        }
889
890        return result;
891    }
892
893    @Override
894    public int delete(Uri url, String where, String[] whereArgs)
895    {
896        int count = 0;
897        long subId = SubscriptionManager.getDefaultSubId();
898
899        checkPermission();
900
901        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
902        int match = s_urlMatcher.match(url);
903        switch (match)
904        {
905            case URL_TELEPHONY_USING_SUBID:
906            {
907                 String subIdString = url.getLastPathSegment();
908                 try {
909                     subId = Long.parseLong(subIdString);
910                 } catch (NumberFormatException e) {
911                     loge("NumberFormatException" + e);
912                     throw new IllegalArgumentException("Invalid subId " + url);
913                 }
914                 if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
915                // FIXME use subId in query
916            }
917            //intentional fall through from above case
918
919            case URL_TELEPHONY:
920            {
921                count = db.delete(CARRIERS_TABLE, where, whereArgs);
922                break;
923            }
924
925            case URL_CURRENT_USING_SUBID: {
926                String subIdString = url.getLastPathSegment();
927                try {
928                    subId = Long.parseLong(subIdString);
929                } catch (NumberFormatException e) {
930                    loge("NumberFormatException" + e);
931                    throw new IllegalArgumentException("Invalid subId " + url);
932                }
933                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
934                // FIXME use subId in query
935            }
936            //intentional fall through from above case
937
938            case URL_CURRENT:
939            {
940                count = db.delete(CARRIERS_TABLE, where, whereArgs);
941                break;
942            }
943
944            case URL_ID:
945            {
946                count = db.delete(CARRIERS_TABLE, Telephony.Carriers._ID + "=?",
947                        new String[] { url.getLastPathSegment() });
948                break;
949            }
950
951            case URL_RESTOREAPN_USING_SUBID: {
952                String subIdString = url.getLastPathSegment();
953                try {
954                    subId = Long.parseLong(subIdString);
955                } catch (NumberFormatException e) {
956                    loge("NumberFormatException" + e);
957                    throw new IllegalArgumentException("Invalid subId " + url);
958                }
959                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
960                // FIXME use subId in query
961            }
962            case URL_RESTOREAPN: {
963                count = 1;
964                restoreDefaultAPN(subId);
965                break;
966            }
967
968            case URL_PREFERAPN_USING_SUBID:
969            case URL_PREFERAPN_NO_UPDATE_USING_SUBID: {
970                String subIdString = url.getLastPathSegment();
971                try {
972                    subId = Long.parseLong(subIdString);
973                } catch (NumberFormatException e) {
974                    loge("NumberFormatException" + e);
975                    throw new IllegalArgumentException("Invalid subId " + url);
976                }
977                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
978            }
979            //intentional fall through from above case
980
981            case URL_PREFERAPN:
982            case URL_PREFERAPN_NO_UPDATE:
983            {
984                setPreferredApnId((long)-1, subId);
985                if ((match == URL_PREFERAPN) || (match == URL_PREFERAPN_USING_SUBID)) count = 1;
986                break;
987            }
988
989            case URL_SIMINFO: {
990                count = db.delete(SIMINFO_TABLE, where, whereArgs);
991                break;
992            }
993
994            default: {
995                throw new UnsupportedOperationException("Cannot delete that URL: " + url);
996            }
997        }
998
999        if (count > 0) {
1000            getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null);
1001        }
1002
1003        return count;
1004    }
1005
1006    @Override
1007    public int update(Uri url, ContentValues values, String where, String[] whereArgs)
1008    {
1009        int count = 0;
1010        int uriType = URL_UNKNOWN;
1011        long subId = SubscriptionManager.getDefaultSubId();
1012
1013        checkPermission();
1014
1015        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1016        int match = s_urlMatcher.match(url);
1017        switch (match)
1018        {
1019            case URL_TELEPHONY_USING_SUBID:
1020            {
1021                 String subIdString = url.getLastPathSegment();
1022                 try {
1023                     subId = Long.parseLong(subIdString);
1024                 } catch (NumberFormatException e) {
1025                     loge("NumberFormatException" + e);
1026                     throw new IllegalArgumentException("Invalid subId " + url);
1027                 }
1028                 if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
1029                //FIXME use subId in the query
1030            }
1031            //intentional fall through from above case
1032
1033            case URL_TELEPHONY:
1034            {
1035                count = db.update(CARRIERS_TABLE, values, where, whereArgs);
1036                break;
1037            }
1038
1039            case URL_CURRENT_USING_SUBID:
1040            {
1041                String subIdString = url.getLastPathSegment();
1042                try {
1043                    subId = Long.parseLong(subIdString);
1044                } catch (NumberFormatException e) {
1045                    loge("NumberFormatException" + e);
1046                    throw new IllegalArgumentException("Invalid subId " + url);
1047                }
1048                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
1049                //FIXME use subId in the query
1050            }
1051            //intentional fall through from above case
1052
1053            case URL_CURRENT:
1054            {
1055                count = db.update(CARRIERS_TABLE, values, where, whereArgs);
1056                break;
1057            }
1058
1059            case URL_ID:
1060            {
1061                if (where != null || whereArgs != null) {
1062                    throw new UnsupportedOperationException(
1063                            "Cannot update URL " + url + " with a where clause");
1064                }
1065                count = db.update(CARRIERS_TABLE, values, Telephony.Carriers._ID + "=?",
1066                        new String[] { url.getLastPathSegment() });
1067                break;
1068            }
1069
1070            case URL_PREFERAPN_USING_SUBID:
1071            case URL_PREFERAPN_NO_UPDATE_USING_SUBID:
1072            {
1073                String subIdString = url.getLastPathSegment();
1074                try {
1075                    subId = Long.parseLong(subIdString);
1076                } catch (NumberFormatException e) {
1077                    loge("NumberFormatException" + e);
1078                    throw new IllegalArgumentException("Invalid subId " + url);
1079                }
1080                if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
1081            }
1082
1083            case URL_PREFERAPN:
1084            case URL_PREFERAPN_NO_UPDATE:
1085            {
1086                if (values != null) {
1087                    if (values.containsKey(COLUMN_APN_ID)) {
1088                        setPreferredApnId(values.getAsLong(COLUMN_APN_ID), subId);
1089                        if ((match == URL_PREFERAPN) ||
1090                                (match == URL_PREFERAPN_USING_SUBID)) {
1091                            count = 1;
1092                        }
1093                    }
1094                }
1095                break;
1096            }
1097
1098            case URL_SIMINFO: {
1099                count = db.update(SIMINFO_TABLE, values, where, whereArgs);
1100                uriType = URL_SIMINFO;
1101                break;
1102            }
1103
1104            default: {
1105                throw new UnsupportedOperationException("Cannot update that URL: " + url);
1106            }
1107        }
1108
1109        if (count > 0) {
1110            switch (uriType) {
1111                case URL_SIMINFO:
1112                    getContext().getContentResolver().notifyChange(
1113                            SubscriptionManager.CONTENT_URI, null);
1114                    break;
1115                default:
1116                    getContext().getContentResolver().notifyChange(
1117                            Telephony.Carriers.CONTENT_URI, null);
1118            }
1119        }
1120
1121        return count;
1122    }
1123
1124    private void checkPermission() {
1125        int status = getContext().checkCallingOrSelfPermission(
1126                "android.permission.WRITE_APN_SETTINGS");
1127        if (status == PackageManager.PERMISSION_GRANTED) {
1128            return;
1129        }
1130
1131        PackageManager packageManager = getContext().getPackageManager();
1132        String[] packages = packageManager.getPackagesForUid(Binder.getCallingUid());
1133
1134        TelephonyManager telephonyManager =
1135                (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
1136        for (String pkg : packages) {
1137            if (telephonyManager.checkCarrierPrivilegesForPackage(pkg) ==
1138                    TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
1139                return;
1140            }
1141        }
1142        throw new SecurityException("No permission to write APN settings");
1143    }
1144
1145    private DatabaseHelper mOpenHelper;
1146
1147    private void restoreDefaultAPN(long subId) {
1148        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1149
1150        try {
1151            db.delete(CARRIERS_TABLE, null, null);
1152        } catch (SQLException e) {
1153            loge("got exception when deleting to restore: " + e);
1154        }
1155        setPreferredApnId((long)-1, subId);
1156        mOpenHelper.initDatabase(db);
1157    }
1158
1159    /**
1160     * Log with debug
1161     *
1162     * @param s is string log
1163     */
1164    private static void log(String s) {
1165        Log.d(TAG, s);
1166    }
1167
1168    private static void loge(String s) {
1169        Log.e(TAG, s);
1170    }
1171}
1172