• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 package com.android.providers.telephony;
19 
20 import android.content.ContentProvider;
21 import android.content.ContentUris;
22 import android.content.ContentValues;
23 import android.content.Context;
24 import android.content.SharedPreferences;
25 import android.content.UriMatcher;
26 import android.content.res.Resources;
27 import android.content.res.XmlResourceParser;
28 import android.database.Cursor;
29 import android.database.sqlite.SQLiteDatabase;
30 import android.database.sqlite.SQLiteOpenHelper;
31 import android.database.sqlite.SQLiteQueryBuilder;
32 import android.net.Uri;
33 import android.os.Environment;
34 import android.os.FileUtils;
35 import android.provider.Telephony;
36 import android.util.Log;
37 import android.util.Xml;
38 
39 import com.android.internal.telephony.BaseCommands;
40 import com.android.internal.telephony.Phone;
41 import com.android.internal.util.XmlUtils;
42 
43 import org.xmlpull.v1.XmlPullParser;
44 import org.xmlpull.v1.XmlPullParserException;
45 
46 import java.io.File;
47 import java.io.FileNotFoundException;
48 import java.io.FileReader;
49 import java.io.IOException;
50 
51 
52 public 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          */
DatabaseHelper(Context context)101         public DatabaseHelper(Context context) {
102             super(context, DATABASE_NAME, null, getVersion(context));
103             mContext = context;
104         }
105 
getVersion(Context context)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
onCreate(SQLiteDatabase db)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 
initDatabase(SQLiteDatabase db)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
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)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          */
getRow(XmlPullParser parser)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          */
loadApns(SQLiteDatabase db, XmlPullParser parser)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 
insertAddingDefaults(SQLiteDatabase db, String table, ContentValues row)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
onCreate()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 
isLteOnCdma()393     private boolean isLteOnCdma() {
394         return BaseCommands.getLteOnCdmaModeStatic() == Phone.LTE_ON_CDMA_TRUE;
395     }
396 
setPreferredApnId(Long id)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 
getPreferredApnId()404     private long getPreferredApnId() {
405         SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
406         return sp.getLong(COLUMN_APN_ID, -1);
407     }
408 
getAPNConfigCheckSum()409     private long getAPNConfigCheckSum() {
410         SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
411         return sp.getLong(APN_CONFIG_CHECKSUM, -1);
412     }
413 
setAPNConfigCheckSum(long id)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
query(Uri url, String[] projectionIn, String selection, String[] selectionArgs, String sort)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
getType(Uri url)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
insert(Uri url, ContentValues initialValues)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
delete(Uri url, String where, String[] whereArgs)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
update(Uri url, ContentValues values, String where, String[] whereArgs)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 
checkPermission()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 
restoreDefaultAPN()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