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