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