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