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.os.FileUtils;
     36 import android.provider.Telephony;
     37 import android.telephony.TelephonyManager;
     38 import android.util.Log;
     39 import android.util.Xml;
     40 
     41 import com.android.internal.telephony.BaseCommands;
     42 import com.android.internal.telephony.Phone;
     43 import com.android.internal.telephony.PhoneConstants;
     44 import com.android.internal.util.XmlUtils;
     45 
     46 import org.xmlpull.v1.XmlPullParser;
     47 import org.xmlpull.v1.XmlPullParserException;
     48 
     49 import java.io.File;
     50 import java.io.FileNotFoundException;
     51 import java.io.FileReader;
     52 import java.io.IOException;
     53 
     54 
     55 public 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         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
    462         qb.setStrict(true); // a little protection from injection attacks
    463         qb.setTables("carriers");
    464 
    465         int match = s_urlMatcher.match(url);
    466         switch (match) {
    467             // do nothing
    468             case URL_TELEPHONY: {
    469                 break;
    470             }
    471 
    472 
    473             case URL_CURRENT: {
    474                 qb.appendWhere("current IS NOT NULL");
    475                 // do not ignore the selection since MMS may use it.
    476                 //selection = null;
    477                 break;
    478             }
    479 
    480             case URL_ID: {
    481                 qb.appendWhere("_id = " + url.getPathSegments().get(1));
    482                 break;
    483             }
    484 
    485             case URL_PREFERAPN:
    486             case URL_PREFERAPN_NO_UPDATE: {
    487                 qb.appendWhere("_id = " + getPreferredApnId());
    488                 break;
    489             }
    490 
    491             default: {
    492                 return null;
    493             }
    494         }
    495 
    496         if (projectionIn != null) {
    497             for (String column : projectionIn) {
    498                 if (Telephony.Carriers.TYPE.equals(column) ||
    499                         Telephony.Carriers.MMSC.equals(column) ||
    500                         Telephony.Carriers.MMSPROXY.equals(column) ||
    501                         Telephony.Carriers.MMSPORT.equals(column) ||
    502                         Telephony.Carriers.APN.equals(column)) {
    503                     // noop
    504                 } else {
    505                     checkPermission();
    506                     break;
    507                 }
    508             }
    509         } else {
    510             // null returns all columns, so need permission check
    511             checkPermission();
    512         }
    513 
    514         SQLiteDatabase db = mOpenHelper.getReadableDatabase();
    515         Cursor ret = null;
    516         try {
    517             ret = qb.query(db, projectionIn, selection, selectionArgs, null, null, sort);
    518         } catch (SQLException e) {
    519             Log.e(TAG, "got exception when querying: " + e);
    520         }
    521         if (ret != null)
    522             ret.setNotificationUri(getContext().getContentResolver(), url);
    523         return ret;
    524     }
    525 
    526     @Override
    527     public String getType(Uri url)
    528     {
    529         switch (s_urlMatcher.match(url)) {
    530         case URL_TELEPHONY:
    531             return "vnd.android.cursor.dir/telephony-carrier";
    532 
    533         case URL_ID:
    534             return "vnd.android.cursor.item/telephony-carrier";
    535 
    536         case URL_PREFERAPN:
    537         case URL_PREFERAPN_NO_UPDATE:
    538             return "vnd.android.cursor.item/telephony-carrier";
    539 
    540         default:
    541             throw new IllegalArgumentException("Unknown URL " + url);
    542         }
    543     }
    544 
    545     @Override
    546     public Uri insert(Uri url, ContentValues initialValues)
    547     {
    548         Uri result = null;
    549 
    550         checkPermission();
    551 
    552         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    553         int match = s_urlMatcher.match(url);
    554         boolean notify = false;
    555         switch (match)
    556         {
    557             case URL_TELEPHONY:
    558             {
    559                 ContentValues values;
    560                 if (initialValues != null) {
    561                     values = new ContentValues(initialValues);
    562                 } else {
    563                     values = new ContentValues();
    564                 }
    565 
    566                 // TODO Review this. This code should probably not bet here.
    567                 // It is valid for the database to return a null string.
    568                 if (!values.containsKey(Telephony.Carriers.NAME)) {
    569                     values.put(Telephony.Carriers.NAME, "");
    570                 }
    571                 if (!values.containsKey(Telephony.Carriers.APN)) {
    572                     values.put(Telephony.Carriers.APN, "");
    573                 }
    574                 if (!values.containsKey(Telephony.Carriers.PORT)) {
    575                     values.put(Telephony.Carriers.PORT, "");
    576                 }
    577                 if (!values.containsKey(Telephony.Carriers.PROXY)) {
    578                     values.put(Telephony.Carriers.PROXY, "");
    579                 }
    580                 if (!values.containsKey(Telephony.Carriers.USER)) {
    581                     values.put(Telephony.Carriers.USER, "");
    582                 }
    583                 if (!values.containsKey(Telephony.Carriers.SERVER)) {
    584                     values.put(Telephony.Carriers.SERVER, "");
    585                 }
    586                 if (!values.containsKey(Telephony.Carriers.PASSWORD)) {
    587                     values.put(Telephony.Carriers.PASSWORD, "");
    588                 }
    589                 if (!values.containsKey(Telephony.Carriers.MMSPORT)) {
    590                     values.put(Telephony.Carriers.MMSPORT, "");
    591                 }
    592                 if (!values.containsKey(Telephony.Carriers.MMSPROXY)) {
    593                     values.put(Telephony.Carriers.MMSPROXY, "");
    594                 }
    595                 if (!values.containsKey(Telephony.Carriers.AUTH_TYPE)) {
    596                     values.put(Telephony.Carriers.AUTH_TYPE, -1);
    597                 }
    598                 if (!values.containsKey(Telephony.Carriers.PROTOCOL)) {
    599                     values.put(Telephony.Carriers.PROTOCOL, "IP");
    600                 }
    601                 if (!values.containsKey(Telephony.Carriers.ROAMING_PROTOCOL)) {
    602                     values.put(Telephony.Carriers.ROAMING_PROTOCOL, "IP");
    603                 }
    604                 if (!values.containsKey(Telephony.Carriers.CARRIER_ENABLED)) {
    605                     values.put(Telephony.Carriers.CARRIER_ENABLED, true);
    606                 }
    607                 if (!values.containsKey(Telephony.Carriers.BEARER)) {
    608                     values.put(Telephony.Carriers.BEARER, 0);
    609                 }
    610                 if (!values.containsKey(Telephony.Carriers.MVNO_TYPE)) {
    611                     values.put(Telephony.Carriers.MVNO_TYPE, "");
    612                 }
    613                 if (!values.containsKey(Telephony.Carriers.MVNO_MATCH_DATA)) {
    614                     values.put(Telephony.Carriers.MVNO_MATCH_DATA, "");
    615                 }
    616 
    617                 long rowID = db.insert(CARRIERS_TABLE, null, values);
    618                 if (rowID > 0)
    619                 {
    620                     result = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, rowID);
    621                     notify = true;
    622                 }
    623 
    624                 if (false) Log.d(TAG, "inserted " + values.toString() + " rowID = " + rowID);
    625                 break;
    626             }
    627 
    628             case URL_CURRENT:
    629             {
    630                 // null out the previous operator
    631                 db.update("carriers", s_currentNullMap, "current IS NOT NULL", null);
    632 
    633                 String numeric = initialValues.getAsString("numeric");
    634                 int updated = db.update("carriers", s_currentSetMap,
    635                         "numeric = '" + numeric + "'", null);
    636 
    637                 if (updated > 0)
    638                 {
    639                     if (false) {
    640                         Log.d(TAG, "Setting numeric '" + numeric + "' to be the current operator");
    641                     }
    642                 }
    643                 else
    644                 {
    645                     Log.e(TAG, "Failed setting numeric '" + numeric + "' to the current operator");
    646                 }
    647                 break;
    648             }
    649 
    650             case URL_PREFERAPN:
    651             case URL_PREFERAPN_NO_UPDATE:
    652             {
    653                 if (initialValues != null) {
    654                     if(initialValues.containsKey(COLUMN_APN_ID)) {
    655                         setPreferredApnId(initialValues.getAsLong(COLUMN_APN_ID));
    656                     }
    657                 }
    658                 break;
    659             }
    660         }
    661 
    662         if (notify) {
    663             getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null);
    664         }
    665 
    666         return result;
    667     }
    668 
    669     @Override
    670     public int delete(Uri url, String where, String[] whereArgs)
    671     {
    672         int count = 0;
    673 
    674         checkPermission();
    675 
    676         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    677         int match = s_urlMatcher.match(url);
    678         switch (match)
    679         {
    680             case URL_TELEPHONY:
    681             {
    682                 count = db.delete(CARRIERS_TABLE, where, whereArgs);
    683                 break;
    684             }
    685 
    686             case URL_CURRENT:
    687             {
    688                 count = db.delete(CARRIERS_TABLE, where, whereArgs);
    689                 break;
    690             }
    691 
    692             case URL_ID:
    693             {
    694                 count = db.delete(CARRIERS_TABLE, Telephony.Carriers._ID + "=?",
    695                         new String[] { url.getLastPathSegment() });
    696                 break;
    697             }
    698 
    699             case URL_RESTOREAPN: {
    700                 count = 1;
    701                 restoreDefaultAPN();
    702                 break;
    703             }
    704 
    705             case URL_PREFERAPN:
    706             case URL_PREFERAPN_NO_UPDATE:
    707             {
    708                 setPreferredApnId((long)-1);
    709                 if (match == URL_PREFERAPN) count = 1;
    710                 break;
    711             }
    712 
    713             default: {
    714                 throw new UnsupportedOperationException("Cannot delete that URL: " + url);
    715             }
    716         }
    717 
    718         if (count > 0) {
    719             getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null);
    720         }
    721 
    722         return count;
    723     }
    724 
    725     @Override
    726     public int update(Uri url, ContentValues values, String where, String[] whereArgs)
    727     {
    728         int count = 0;
    729 
    730         checkPermission();
    731 
    732         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    733         int match = s_urlMatcher.match(url);
    734         switch (match)
    735         {
    736             case URL_TELEPHONY:
    737             {
    738                 count = db.update(CARRIERS_TABLE, values, where, whereArgs);
    739                 break;
    740             }
    741 
    742             case URL_CURRENT:
    743             {
    744                 count = db.update(CARRIERS_TABLE, values, where, whereArgs);
    745                 break;
    746             }
    747 
    748             case URL_ID:
    749             {
    750                 if (where != null || whereArgs != null) {
    751                     throw new UnsupportedOperationException(
    752                             "Cannot update URL " + url + " with a where clause");
    753                 }
    754                 count = db.update(CARRIERS_TABLE, values, Telephony.Carriers._ID + "=?",
    755                         new String[] { url.getLastPathSegment() });
    756                 break;
    757             }
    758 
    759             case URL_PREFERAPN:
    760             case URL_PREFERAPN_NO_UPDATE:
    761             {
    762                 if (values != null) {
    763                     if (values.containsKey(COLUMN_APN_ID)) {
    764                         setPreferredApnId(values.getAsLong(COLUMN_APN_ID));
    765                         if (match == URL_PREFERAPN) count = 1;
    766                     }
    767                 }
    768                 break;
    769             }
    770 
    771             default: {
    772                 throw new UnsupportedOperationException("Cannot update that URL: " + url);
    773             }
    774         }
    775 
    776         if (count > 0) {
    777             getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null);
    778         }
    779 
    780         return count;
    781     }
    782 
    783     private void checkPermission() {
    784         getContext().enforceCallingOrSelfPermission("android.permission.WRITE_APN_SETTINGS",
    785                 "No permission to write APN settings");
    786     }
    787 
    788     private DatabaseHelper mOpenHelper;
    789 
    790     private void restoreDefaultAPN() {
    791         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    792 
    793         try {
    794             db.delete(CARRIERS_TABLE, null, null);
    795         } catch (SQLException e) {
    796             Log.e(TAG, "got exception when deleting to restore: " + e);
    797         }
    798         setPreferredApnId((long)-1);
    799         mOpenHelper.initDatabase(db);
    800     }
    801 }
    802