TelephonyProvider.java revision bae4b07ec2b83007aa24eb86d28cdbb347ddec0e
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.res.Resources;
27import android.content.res.XmlResourceParser;
28import android.database.Cursor;
29import android.database.sqlite.SQLiteDatabase;
30import android.database.sqlite.SQLiteOpenHelper;
31import android.database.sqlite.SQLiteQueryBuilder;
32import android.net.Uri;
33import android.os.Environment;
34import android.os.FileUtils;
35import android.provider.Telephony;
36import android.util.Log;
37import android.util.Xml;
38
39import com.android.internal.telephony.BaseCommands;
40import com.android.internal.telephony.Phone;
41import com.android.internal.util.XmlUtils;
42
43import org.xmlpull.v1.XmlPullParser;
44import org.xmlpull.v1.XmlPullParserException;
45
46import java.io.File;
47import java.io.FileNotFoundException;
48import java.io.FileReader;
49import java.io.IOException;
50
51
52public class TelephonyProvider extends ContentProvider
53{
54    private static final String DATABASE_NAME = "telephony.db";
55    private static final boolean DBG = true;
56
57    private static final int DATABASE_VERSION = 7 << 16;
58    private static final int URL_TELEPHONY = 1;
59    private static final int URL_CURRENT = 2;
60    private static final int URL_ID = 3;
61    private static final int URL_RESTOREAPN = 4;
62    private static final int URL_PREFERAPN = 5;
63
64    private static final String TAG = "TelephonyProvider";
65    private static final String CARRIERS_TABLE = "carriers";
66
67    private static final String PREF_FILE = "preferred-apn";
68    private static final String COLUMN_APN_ID = "apn_id";
69    private static final String APN_CONFIG_CHECKSUM = "apn_conf_checksum";
70
71    private static final String PARTNER_APNS_PATH = "etc/apns-conf.xml";
72
73    private static final UriMatcher s_urlMatcher = new UriMatcher(UriMatcher.NO_MATCH);
74
75    private static final ContentValues s_currentNullMap;
76    private static final ContentValues s_currentSetMap;
77
78    static {
79        s_urlMatcher.addURI("telephony", "carriers", URL_TELEPHONY);
80        s_urlMatcher.addURI("telephony", "carriers/current", URL_CURRENT);
81        s_urlMatcher.addURI("telephony", "carriers/#", URL_ID);
82        s_urlMatcher.addURI("telephony", "carriers/restore", URL_RESTOREAPN);
83        s_urlMatcher.addURI("telephony", "carriers/preferapn", URL_PREFERAPN);
84
85        s_currentNullMap = new ContentValues(1);
86        s_currentNullMap.put("current", (Long) null);
87
88        s_currentSetMap = new ContentValues(1);
89        s_currentSetMap.put("current", "1");
90    }
91
92    private static class DatabaseHelper extends SQLiteOpenHelper {
93        // Context to access resources with
94        private Context mContext;
95
96        /**
97         * DatabaseHelper helper class for loading apns into a database.
98         *
99         * @param context of the user.
100         */
101        public DatabaseHelper(Context context) {
102            super(context, DATABASE_NAME, null, getVersion(context));
103            mContext = context;
104        }
105
106        private static int getVersion(Context context) {
107            // Get the database version, combining a static schema version and the XML version
108            Resources r = context.getResources();
109            XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns);
110            try {
111                XmlUtils.beginDocument(parser, "apns");
112                int publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));
113                return DATABASE_VERSION | publicversion;
114            } catch (Exception e) {
115                Log.e(TAG, "Can't get version of APN database", e);
116                return DATABASE_VERSION;
117            } finally {
118                parser.close();
119            }
120        }
121
122        @Override
123        public void onCreate(SQLiteDatabase db) {
124            // Set up the database schema
125            db.execSQL("CREATE TABLE " + CARRIERS_TABLE +
126                "(_id INTEGER PRIMARY KEY," +
127                    "name TEXT," +
128                    "numeric TEXT," +
129                    "mcc TEXT," +
130                    "mnc TEXT," +
131                    "apn TEXT," +
132                    "user TEXT," +
133                    "server TEXT," +
134                    "password TEXT," +
135                    "proxy TEXT," +
136                    "port TEXT," +
137                    "mmsproxy TEXT," +
138                    "mmsport TEXT," +
139                    "mmsc TEXT," +
140                    "authtype INTEGER," +
141                    "type TEXT," +
142                    "current INTEGER," +
143                    "protocol TEXT," +
144                    "roaming_protocol TEXT," +
145                    "carrier_enabled BOOLEAN," +
146                    "bearer INTEGER);");
147
148            initDatabase(db);
149        }
150
151        private void initDatabase(SQLiteDatabase db) {
152            // Read internal APNS data
153            Resources r = mContext.getResources();
154            XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns);
155            int publicversion = -1;
156            try {
157                XmlUtils.beginDocument(parser, "apns");
158                publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));
159                loadApns(db, parser);
160            } catch (Exception e) {
161                Log.e(TAG, "Got exception while loading APN database.", e);
162            } finally {
163                parser.close();
164            }
165
166           // Read external APNS data (partner-provided)
167            XmlPullParser confparser = null;
168            // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
169            File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH);
170            FileReader confreader = null;
171            try {
172                confreader = new FileReader(confFile);
173                confparser = Xml.newPullParser();
174                confparser.setInput(confreader);
175                XmlUtils.beginDocument(confparser, "apns");
176
177                // Sanity check. Force internal version and confidential versions to agree
178                int confversion = Integer.parseInt(confparser.getAttributeValue(null, "version"));
179                if (publicversion != confversion) {
180                    throw new IllegalStateException("Internal APNS file version doesn't match "
181                            + confFile.getAbsolutePath());
182                }
183
184                loadApns(db, confparser);
185            } catch (FileNotFoundException e) {
186                // It's ok if the file isn't found. It means there isn't a confidential file
187                // Log.e(TAG, "File not found: '" + confFile.getAbsolutePath() + "'");
188            } catch (Exception e) {
189                Log.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
190            } finally {
191                try { if (confreader != null) confreader.close(); } catch (IOException e) { }
192            }
193        }
194
195        @Override
196        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
197            if (oldVersion < (5 << 16 | 6)) {
198                // 5 << 16 is the Database version and 6 in the xml version.
199
200                // This change adds a new authtype column to the database.
201                // The auth type column can have 4 values: 0 (None), 1 (PAP), 2 (CHAP)
202                // 3 (PAP or CHAP). To avoid breaking compatibility, with already working
203                // APNs, the unset value (-1) will be used. If the value is -1.
204                // the authentication will default to 0 (if no user / password) is specified
205                // or to 3. Currently, there have been no reported problems with
206                // pre-configured APNs and hence it is set to -1 for them. Similarly,
207                // if the user, has added a new APN, we set the authentication type
208                // to -1.
209
210                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
211                        " ADD COLUMN authtype INTEGER DEFAULT -1;");
212
213                oldVersion = 5 << 16 | 6;
214            }
215            if (oldVersion < (6 << 16 | 6)) {
216                // Add protcol fields to the APN. The XML file does not change.
217                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
218                        " ADD COLUMN protocol TEXT DEFAULT IP;");
219                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
220                        " ADD COLUMN roaming_protocol TEXT DEFAULT IP;");
221                oldVersion = 6 << 16 | 6;
222            }
223            if (oldVersion < (7 << 16 | 6)) {
224                // Add protcol fields to the APN. The XML file does not change.
225                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
226                        " ADD COLUMN carrier_enabled BOOLEAN DEFAULT 1;");
227                db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
228                        " ADD COLUMN bearer INTEGER DEFAULT 0;");
229                oldVersion = 7 << 16 | 6;
230            }
231        }
232
233        /**
234         * Gets the next row of apn values.
235         *
236         * @param parser the parser
237         * @return the row or null if it's not an apn
238         */
239        private ContentValues getRow(XmlPullParser parser) {
240            if (!"apn".equals(parser.getName())) {
241                return null;
242            }
243
244            ContentValues map = new ContentValues();
245
246            String mcc = parser.getAttributeValue(null, "mcc");
247            String mnc = parser.getAttributeValue(null, "mnc");
248            String numeric = mcc + mnc;
249
250            map.put(Telephony.Carriers.NUMERIC,numeric);
251            map.put(Telephony.Carriers.MCC, mcc);
252            map.put(Telephony.Carriers.MNC, mnc);
253            map.put(Telephony.Carriers.NAME, parser.getAttributeValue(null, "carrier"));
254            map.put(Telephony.Carriers.APN, parser.getAttributeValue(null, "apn"));
255            map.put(Telephony.Carriers.USER, parser.getAttributeValue(null, "user"));
256            map.put(Telephony.Carriers.SERVER, parser.getAttributeValue(null, "server"));
257            map.put(Telephony.Carriers.PASSWORD, parser.getAttributeValue(null, "password"));
258
259            // do not add NULL to the map so that insert() will set the default value
260            String proxy = parser.getAttributeValue(null, "proxy");
261            if (proxy != null) {
262                map.put(Telephony.Carriers.PROXY, proxy);
263            }
264            String port = parser.getAttributeValue(null, "port");
265            if (port != null) {
266                map.put(Telephony.Carriers.PORT, port);
267            }
268            String mmsproxy = parser.getAttributeValue(null, "mmsproxy");
269            if (mmsproxy != null) {
270                map.put(Telephony.Carriers.MMSPROXY, mmsproxy);
271            }
272            String mmsport = parser.getAttributeValue(null, "mmsport");
273            if (mmsport != null) {
274                map.put(Telephony.Carriers.MMSPORT, mmsport);
275            }
276            map.put(Telephony.Carriers.MMSC, parser.getAttributeValue(null, "mmsc"));
277            String type = parser.getAttributeValue(null, "type");
278            if (type != null) {
279                map.put(Telephony.Carriers.TYPE, type);
280            }
281
282            String auth = parser.getAttributeValue(null, "authtype");
283            if (auth != null) {
284                map.put(Telephony.Carriers.AUTH_TYPE, Integer.parseInt(auth));
285            }
286
287            String protocol = parser.getAttributeValue(null, "protocol");
288            if (protocol != null) {
289                map.put(Telephony.Carriers.PROTOCOL, protocol);
290            }
291
292            String roamingProtocol = parser.getAttributeValue(null, "roaming_protocol");
293            if (roamingProtocol != null) {
294                map.put(Telephony.Carriers.ROAMING_PROTOCOL, roamingProtocol);
295            }
296
297            String carrierEnabled = parser.getAttributeValue(null, "carrier_enabled");
298            if (carrierEnabled != null) {
299                map.put(Telephony.Carriers.CARRIER_ENABLED, Boolean.parseBoolean(carrierEnabled));
300            }
301
302            String bearer = parser.getAttributeValue(null, "bearer");
303            if (bearer != null) {
304                map.put(Telephony.Carriers.BEARER, Integer.parseInt(bearer));
305            }
306            return map;
307        }
308
309        /*
310         * Loads apns from xml file into the database
311         *
312         * @param db the sqlite database to write to
313         * @param parser the xml parser
314         *
315         */
316        private void loadApns(SQLiteDatabase db, XmlPullParser parser) {
317            if (parser != null) {
318                try {
319                    while (true) {
320                        XmlUtils.nextElement(parser);
321                        ContentValues row = getRow(parser);
322                        if (row != null) {
323                            insertAddingDefaults(db, CARRIERS_TABLE, row);
324                        } else {
325                            break;  // do we really want to skip the rest of the file?
326                        }
327                    }
328                } catch (XmlPullParserException e)  {
329                    Log.e(TAG, "Got execption while getting perferred time zone.", e);
330                } catch (IOException e) {
331                    Log.e(TAG, "Got execption while getting perferred time zone.", e);
332                }
333            }
334        }
335
336        private void insertAddingDefaults(SQLiteDatabase db, String table, ContentValues row) {
337            // Initialize defaults if any
338            if (row.containsKey(Telephony.Carriers.AUTH_TYPE) == false) {
339                row.put(Telephony.Carriers.AUTH_TYPE, -1);
340            }
341            if (row.containsKey(Telephony.Carriers.PROTOCOL) == false) {
342                row.put(Telephony.Carriers.PROTOCOL, "IP");
343            }
344            if (row.containsKey(Telephony.Carriers.ROAMING_PROTOCOL) == false) {
345                row.put(Telephony.Carriers.ROAMING_PROTOCOL, "IP");
346            }
347            if (row.containsKey(Telephony.Carriers.CARRIER_ENABLED) == false) {
348                row.put(Telephony.Carriers.CARRIER_ENABLED, true);
349            }
350            if (row.containsKey(Telephony.Carriers.BEARER) == false) {
351                row.put(Telephony.Carriers.BEARER, 0);
352            }
353            db.insert(CARRIERS_TABLE, null, row);
354        }
355    }
356
357    @Override
358    public boolean onCreate() {
359        long oldCheckSum = getAPNConfigCheckSum();
360        File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH);
361        long newCheckSum = -1L;
362
363        if (DBG) {
364            Log.w(TAG, "onCreate: confFile=" + confFile.getAbsolutePath() +
365                    " oldCheckSum=" + oldCheckSum);
366        }
367        mOpenHelper = new DatabaseHelper(getContext());
368
369        if (isLteOnCdma()) {
370            // Check to see if apns-conf.xml file changed. If so, generate db again.
371            //
372            // TODO: Generalize so we can handle apns-conf.xml updates
373            // and preserve any modifications the user might make. For
374            // now its safe on LteOnCdma devices because the user cannot
375            // make changes.
376            try {
377                newCheckSum = FileUtils.checksumCrc32(confFile);
378                if (DBG) Log.w(TAG, "onCreate: newCheckSum=" + newCheckSum);
379                if (oldCheckSum != newCheckSum) {
380                    Log.w(TAG, "Rebuilding Telephony.db");
381                    restoreDefaultAPN();
382                    setAPNConfigCheckSum(newCheckSum);
383                }
384            } catch (FileNotFoundException e) {
385                Log.e(TAG, "FileNotFoundException: '" + confFile.getAbsolutePath() + "'", e);
386            } catch (IOException e) {
387                Log.e(TAG, "IOException: '" + confFile.getAbsolutePath() + "'", e);
388            }
389        }
390        return true;
391    }
392
393    private boolean isLteOnCdma() {
394        return BaseCommands.getLteOnCdmaModeStatic() == Phone.LTE_ON_CDMA_TRUE;
395    }
396
397    private void setPreferredApnId(Long id) {
398        SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
399        SharedPreferences.Editor editor = sp.edit();
400        editor.putLong(COLUMN_APN_ID, id != null ? id.longValue() : -1);
401        editor.apply();
402    }
403
404    private long getPreferredApnId() {
405        SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
406        return sp.getLong(COLUMN_APN_ID, -1);
407    }
408
409    private long getAPNConfigCheckSum() {
410        SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
411        return sp.getLong(APN_CONFIG_CHECKSUM, -1);
412    }
413
414    private void setAPNConfigCheckSum(long id) {
415        SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
416        SharedPreferences.Editor editor = sp.edit();
417        editor.putLong(APN_CONFIG_CHECKSUM, id);
418        editor.apply();
419    }
420
421    @Override
422    public Cursor query(Uri url, String[] projectionIn, String selection,
423            String[] selectionArgs, String sort) {
424        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
425        qb.setTables("carriers");
426
427        int match = s_urlMatcher.match(url);
428        switch (match) {
429            // do nothing
430            case URL_TELEPHONY: {
431                break;
432            }
433
434
435            case URL_CURRENT: {
436                qb.appendWhere("current IS NOT NULL");
437                // do not ignore the selection since MMS may use it.
438                //selection = null;
439                break;
440            }
441
442            case URL_ID: {
443                qb.appendWhere("_id = " + url.getPathSegments().get(1));
444                break;
445            }
446
447            case URL_PREFERAPN: {
448                qb.appendWhere("_id = " + getPreferredApnId());
449                break;
450            }
451
452            default: {
453                return null;
454            }
455        }
456
457        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
458        Cursor ret = qb.query(db, projectionIn, selection, selectionArgs, null, null, sort);
459        ret.setNotificationUri(getContext().getContentResolver(), url);
460        return ret;
461    }
462
463    @Override
464    public String getType(Uri url)
465    {
466        switch (s_urlMatcher.match(url)) {
467        case URL_TELEPHONY:
468            return "vnd.android.cursor.dir/telephony-carrier";
469
470        case URL_ID:
471            return "vnd.android.cursor.item/telephony-carrier";
472
473        case URL_PREFERAPN:
474            return "vnd.android.cursor.item/telephony-carrier";
475
476        default:
477            throw new IllegalArgumentException("Unknown URL " + url);
478        }
479    }
480
481    @Override
482    public Uri insert(Uri url, ContentValues initialValues)
483    {
484        Uri result = null;
485
486        checkPermission();
487
488        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
489        int match = s_urlMatcher.match(url);
490        boolean notify = false;
491        switch (match)
492        {
493            case URL_TELEPHONY:
494            {
495                ContentValues values;
496                if (initialValues != null) {
497                    values = new ContentValues(initialValues);
498                } else {
499                    values = new ContentValues();
500                }
501
502                // TODO Review this. This code should probably not bet here.
503                // It is valid for the database to return a null string.
504                if (!values.containsKey(Telephony.Carriers.NAME)) {
505                    values.put(Telephony.Carriers.NAME, "");
506                }
507                if (!values.containsKey(Telephony.Carriers.APN)) {
508                    values.put(Telephony.Carriers.APN, "");
509                }
510                if (!values.containsKey(Telephony.Carriers.PORT)) {
511                    values.put(Telephony.Carriers.PORT, "");
512                }
513                if (!values.containsKey(Telephony.Carriers.PROXY)) {
514                    values.put(Telephony.Carriers.PROXY, "");
515                }
516                if (!values.containsKey(Telephony.Carriers.USER)) {
517                    values.put(Telephony.Carriers.USER, "");
518                }
519                if (!values.containsKey(Telephony.Carriers.SERVER)) {
520                    values.put(Telephony.Carriers.SERVER, "");
521                }
522                if (!values.containsKey(Telephony.Carriers.PASSWORD)) {
523                    values.put(Telephony.Carriers.PASSWORD, "");
524                }
525                if (!values.containsKey(Telephony.Carriers.MMSPORT)) {
526                    values.put(Telephony.Carriers.MMSPORT, "");
527                }
528                if (!values.containsKey(Telephony.Carriers.MMSPROXY)) {
529                    values.put(Telephony.Carriers.MMSPROXY, "");
530                }
531                if (!values.containsKey(Telephony.Carriers.AUTH_TYPE)) {
532                    values.put(Telephony.Carriers.AUTH_TYPE, -1);
533                }
534                if (!values.containsKey(Telephony.Carriers.PROTOCOL)) {
535                    values.put(Telephony.Carriers.PROTOCOL, "IP");
536                }
537                if (!values.containsKey(Telephony.Carriers.ROAMING_PROTOCOL)) {
538                    values.put(Telephony.Carriers.ROAMING_PROTOCOL, "IP");
539                }
540                if (!values.containsKey(Telephony.Carriers.CARRIER_ENABLED)) {
541                    values.put(Telephony.Carriers.CARRIER_ENABLED, true);
542                }
543                if (!values.containsKey(Telephony.Carriers.BEARER)) {
544                    values.put(Telephony.Carriers.BEARER, 0);
545                }
546
547                long rowID = db.insert(CARRIERS_TABLE, null, values);
548                if (rowID > 0)
549                {
550                    result = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, rowID);
551                    notify = true;
552                }
553
554                if (false) Log.d(TAG, "inserted " + values.toString() + " rowID = " + rowID);
555                break;
556            }
557
558            case URL_CURRENT:
559            {
560                // null out the previous operator
561                db.update("carriers", s_currentNullMap, "current IS NOT NULL", null);
562
563                String numeric = initialValues.getAsString("numeric");
564                int updated = db.update("carriers", s_currentSetMap,
565                        "numeric = '" + numeric + "'", null);
566
567                if (updated > 0)
568                {
569                    if (false) {
570                        Log.d(TAG, "Setting numeric '" + numeric + "' to be the current operator");
571                    }
572                }
573                else
574                {
575                    Log.e(TAG, "Failed setting numeric '" + numeric + "' to the current operator");
576                }
577                break;
578            }
579
580            case URL_PREFERAPN:
581            {
582                if (initialValues != null) {
583                    if(initialValues.containsKey(COLUMN_APN_ID)) {
584                        setPreferredApnId(initialValues.getAsLong(COLUMN_APN_ID));
585                    }
586                }
587                break;
588            }
589        }
590
591        if (notify) {
592            getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null);
593        }
594
595        return result;
596    }
597
598    @Override
599    public int delete(Uri url, String where, String[] whereArgs)
600    {
601        int count;
602
603        checkPermission();
604
605        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
606        int match = s_urlMatcher.match(url);
607        switch (match)
608        {
609            case URL_TELEPHONY:
610            {
611                count = db.delete(CARRIERS_TABLE, where, whereArgs);
612                break;
613            }
614
615            case URL_CURRENT:
616            {
617                count = db.delete(CARRIERS_TABLE, where, whereArgs);
618                break;
619            }
620
621            case URL_ID:
622            {
623                count = db.delete(CARRIERS_TABLE, Telephony.Carriers._ID + "=?",
624                        new String[] { url.getLastPathSegment() });
625                break;
626            }
627
628            case URL_RESTOREAPN: {
629                count = 1;
630                restoreDefaultAPN();
631                break;
632            }
633
634            case URL_PREFERAPN:
635            {
636                setPreferredApnId((long)-1);
637                count = 1;
638                break;
639            }
640
641            default: {
642                throw new UnsupportedOperationException("Cannot delete that URL: " + url);
643            }
644        }
645
646        if (count > 0) {
647            getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null);
648        }
649
650        return count;
651    }
652
653    @Override
654    public int update(Uri url, ContentValues values, String where, String[] whereArgs)
655    {
656        int count = 0;
657
658        checkPermission();
659
660        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
661        int match = s_urlMatcher.match(url);
662        switch (match)
663        {
664            case URL_TELEPHONY:
665            {
666                count = db.update(CARRIERS_TABLE, values, where, whereArgs);
667                break;
668            }
669
670            case URL_CURRENT:
671            {
672                count = db.update(CARRIERS_TABLE, values, where, whereArgs);
673                break;
674            }
675
676            case URL_ID:
677            {
678                if (where != null || whereArgs != null) {
679                    throw new UnsupportedOperationException(
680                            "Cannot update URL " + url + " with a where clause");
681                }
682                count = db.update(CARRIERS_TABLE, values, Telephony.Carriers._ID + "=?",
683                        new String[] { url.getLastPathSegment() });
684                break;
685            }
686
687            case URL_PREFERAPN:
688            {
689                if (values != null) {
690                    if (values.containsKey(COLUMN_APN_ID)) {
691                        setPreferredApnId(values.getAsLong(COLUMN_APN_ID));
692                        count = 1;
693                    }
694                }
695                break;
696            }
697
698            default: {
699                throw new UnsupportedOperationException("Cannot update that URL: " + url);
700            }
701        }
702
703        if (count > 0) {
704            getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null);
705        }
706
707        return count;
708    }
709
710    private void checkPermission() {
711        // Check the permissions
712        getContext().enforceCallingOrSelfPermission("android.permission.WRITE_APN_SETTINGS",
713                "No permission to write APN settings");
714    }
715
716    private DatabaseHelper mOpenHelper;
717
718    private void restoreDefaultAPN() {
719        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
720
721        db.delete(CARRIERS_TABLE, null, null);
722        setPreferredApnId((long)-1);
723        mOpenHelper.initDatabase(db);
724    }
725}
726