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