TelephonyProvider.java revision cfdb743efa6b0cb5aafaaafbc7687ae41d761d65
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 = 7 << 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 153 initDatabase(db); 154 } 155 156 private void initDatabase(SQLiteDatabase db) { 157 // Read internal APNS data 158 Resources r = mContext.getResources(); 159 XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns); 160 int publicversion = -1; 161 try { 162 XmlUtils.beginDocument(parser, "apns"); 163 publicversion = Integer.parseInt(parser.getAttributeValue(null, "version")); 164 loadApns(db, parser); 165 } catch (Exception e) { 166 Log.e(TAG, "Got exception while loading APN database.", e); 167 } finally { 168 parser.close(); 169 } 170 171 // Read external APNS data (partner-provided) 172 XmlPullParser confparser = null; 173 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system". 174 File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH); 175 FileReader confreader = null; 176 try { 177 confreader = new FileReader(confFile); 178 confparser = Xml.newPullParser(); 179 confparser.setInput(confreader); 180 XmlUtils.beginDocument(confparser, "apns"); 181 182 // Sanity check. Force internal version and confidential versions to agree 183 int confversion = Integer.parseInt(confparser.getAttributeValue(null, "version")); 184 if (publicversion != confversion) { 185 throw new IllegalStateException("Internal APNS file version doesn't match " 186 + confFile.getAbsolutePath()); 187 } 188 189 loadApns(db, confparser); 190 } catch (FileNotFoundException e) { 191 // It's ok if the file isn't found. It means there isn't a confidential file 192 // Log.e(TAG, "File not found: '" + confFile.getAbsolutePath() + "'"); 193 } catch (Exception e) { 194 Log.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e); 195 } finally { 196 try { if (confreader != null) confreader.close(); } catch (IOException e) { } 197 } 198 } 199 200 @Override 201 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 202 if (oldVersion < (5 << 16 | 6)) { 203 // 5 << 16 is the Database version and 6 in the xml version. 204 205 // This change adds a new authtype column to the database. 206 // The auth type column can have 4 values: 0 (None), 1 (PAP), 2 (CHAP) 207 // 3 (PAP or CHAP). To avoid breaking compatibility, with already working 208 // APNs, the unset value (-1) will be used. If the value is -1. 209 // the authentication will default to 0 (if no user / password) is specified 210 // or to 3. Currently, there have been no reported problems with 211 // pre-configured APNs and hence it is set to -1 for them. Similarly, 212 // if the user, has added a new APN, we set the authentication type 213 // to -1. 214 215 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 216 " ADD COLUMN authtype INTEGER DEFAULT -1;"); 217 218 oldVersion = 5 << 16 | 6; 219 } 220 if (oldVersion < (6 << 16 | 6)) { 221 // Add protcol fields to the APN. The XML file does not change. 222 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 223 " ADD COLUMN protocol TEXT DEFAULT IP;"); 224 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 225 " ADD COLUMN roaming_protocol TEXT DEFAULT IP;"); 226 oldVersion = 6 << 16 | 6; 227 } 228 if (oldVersion < (7 << 16 | 6)) { 229 // Add carrier_enabled, bearer fields to the APN. The XML file does not change. 230 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 231 " ADD COLUMN carrier_enabled BOOLEAN DEFAULT 1;"); 232 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 233 " ADD COLUMN bearer INTEGER DEFAULT 0;"); 234 oldVersion = 7 << 16 | 6; 235 } 236 } 237 238 /** 239 * Gets the next row of apn values. 240 * 241 * @param parser the parser 242 * @return the row or null if it's not an apn 243 */ 244 private ContentValues getRow(XmlPullParser parser) { 245 if (!"apn".equals(parser.getName())) { 246 return null; 247 } 248 249 ContentValues map = new ContentValues(); 250 251 String mcc = parser.getAttributeValue(null, "mcc"); 252 String mnc = parser.getAttributeValue(null, "mnc"); 253 String numeric = mcc + mnc; 254 255 map.put(Telephony.Carriers.NUMERIC,numeric); 256 map.put(Telephony.Carriers.MCC, mcc); 257 map.put(Telephony.Carriers.MNC, mnc); 258 map.put(Telephony.Carriers.NAME, parser.getAttributeValue(null, "carrier")); 259 map.put(Telephony.Carriers.APN, parser.getAttributeValue(null, "apn")); 260 map.put(Telephony.Carriers.USER, parser.getAttributeValue(null, "user")); 261 map.put(Telephony.Carriers.SERVER, parser.getAttributeValue(null, "server")); 262 map.put(Telephony.Carriers.PASSWORD, parser.getAttributeValue(null, "password")); 263 264 // do not add NULL to the map so that insert() will set the default value 265 String proxy = parser.getAttributeValue(null, "proxy"); 266 if (proxy != null) { 267 map.put(Telephony.Carriers.PROXY, proxy); 268 } 269 String port = parser.getAttributeValue(null, "port"); 270 if (port != null) { 271 map.put(Telephony.Carriers.PORT, port); 272 } 273 String mmsproxy = parser.getAttributeValue(null, "mmsproxy"); 274 if (mmsproxy != null) { 275 map.put(Telephony.Carriers.MMSPROXY, mmsproxy); 276 } 277 String mmsport = parser.getAttributeValue(null, "mmsport"); 278 if (mmsport != null) { 279 map.put(Telephony.Carriers.MMSPORT, mmsport); 280 } 281 map.put(Telephony.Carriers.MMSC, parser.getAttributeValue(null, "mmsc")); 282 String type = parser.getAttributeValue(null, "type"); 283 if (type != null) { 284 map.put(Telephony.Carriers.TYPE, type); 285 } 286 287 String auth = parser.getAttributeValue(null, "authtype"); 288 if (auth != null) { 289 map.put(Telephony.Carriers.AUTH_TYPE, Integer.parseInt(auth)); 290 } 291 292 String protocol = parser.getAttributeValue(null, "protocol"); 293 if (protocol != null) { 294 map.put(Telephony.Carriers.PROTOCOL, protocol); 295 } 296 297 String roamingProtocol = parser.getAttributeValue(null, "roaming_protocol"); 298 if (roamingProtocol != null) { 299 map.put(Telephony.Carriers.ROAMING_PROTOCOL, roamingProtocol); 300 } 301 302 String carrierEnabled = parser.getAttributeValue(null, "carrier_enabled"); 303 if (carrierEnabled != null) { 304 map.put(Telephony.Carriers.CARRIER_ENABLED, Boolean.parseBoolean(carrierEnabled)); 305 } 306 307 String bearer = parser.getAttributeValue(null, "bearer"); 308 if (bearer != null) { 309 map.put(Telephony.Carriers.BEARER, Integer.parseInt(bearer)); 310 } 311 return map; 312 } 313 314 /* 315 * Loads apns from xml file into the database 316 * 317 * @param db the sqlite database to write to 318 * @param parser the xml parser 319 * 320 */ 321 private void loadApns(SQLiteDatabase db, XmlPullParser parser) { 322 if (parser != null) { 323 try { 324 db.beginTransaction(); 325 while (true) { 326 XmlUtils.nextElement(parser); 327 ContentValues row = getRow(parser); 328 if (row != null) { 329 insertAddingDefaults(db, CARRIERS_TABLE, row); 330 } else { 331 break; // do we really want to skip the rest of the file? 332 } 333 } 334 db.setTransactionSuccessful(); 335 } catch (XmlPullParserException e) { 336 Log.e(TAG, "Got execption while loading apns.", e); 337 } catch (IOException e) { 338 Log.e(TAG, "Got IOExecption while loading apns.", e); 339 } catch (SQLException e){ 340 Log.e(TAG, "Got SQLException while loading apns.", e); 341 } finally { 342 db.endTransaction(); 343 } 344 } 345 } 346 347 private void insertAddingDefaults(SQLiteDatabase db, String table, ContentValues row) { 348 // Initialize defaults if any 349 if (row.containsKey(Telephony.Carriers.AUTH_TYPE) == false) { 350 row.put(Telephony.Carriers.AUTH_TYPE, -1); 351 } 352 if (row.containsKey(Telephony.Carriers.PROTOCOL) == false) { 353 row.put(Telephony.Carriers.PROTOCOL, "IP"); 354 } 355 if (row.containsKey(Telephony.Carriers.ROAMING_PROTOCOL) == false) { 356 row.put(Telephony.Carriers.ROAMING_PROTOCOL, "IP"); 357 } 358 if (row.containsKey(Telephony.Carriers.CARRIER_ENABLED) == false) { 359 row.put(Telephony.Carriers.CARRIER_ENABLED, true); 360 } 361 if (row.containsKey(Telephony.Carriers.BEARER) == false) { 362 row.put(Telephony.Carriers.BEARER, 0); 363 } 364 db.insert(CARRIERS_TABLE, null, row); 365 } 366 } 367 368 @Override 369 public boolean onCreate() { 370 long oldCheckSum = getAPNConfigCheckSum(); 371 File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH); 372 long newCheckSum = -1L; 373 374 if (DBG) { 375 Log.w(TAG, "onCreate: confFile=" + confFile.getAbsolutePath() + 376 " oldCheckSum=" + oldCheckSum); 377 } 378 mOpenHelper = new DatabaseHelper(getContext()); 379 380 if (isLteOnCdma()) { 381 // Check to see if apns-conf.xml file changed. If so, generate db again. 382 // 383 // TODO: Generalize so we can handle apns-conf.xml updates 384 // and preserve any modifications the user might make. For 385 // now its safe on LteOnCdma devices because the user cannot 386 // make changes. 387 try { 388 newCheckSum = FileUtils.checksumCrc32(confFile); 389 if (DBG) Log.w(TAG, "onCreate: newCheckSum=" + newCheckSum); 390 if (oldCheckSum != newCheckSum) { 391 Log.w(TAG, "Rebuilding Telephony.db"); 392 restoreDefaultAPN(); 393 setAPNConfigCheckSum(newCheckSum); 394 } 395 } catch (FileNotFoundException e) { 396 Log.e(TAG, "FileNotFoundException: '" + confFile.getAbsolutePath() + "'", e); 397 } catch (IOException e) { 398 Log.e(TAG, "IOException: '" + confFile.getAbsolutePath() + "'", e); 399 } 400 } 401 return true; 402 } 403 404 private boolean isLteOnCdma() { 405 return TelephonyManager.getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE; 406 } 407 408 private void setPreferredApnId(Long id) { 409 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 410 SharedPreferences.Editor editor = sp.edit(); 411 editor.putLong(COLUMN_APN_ID, id != null ? id.longValue() : -1); 412 editor.apply(); 413 } 414 415 private long getPreferredApnId() { 416 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 417 return sp.getLong(COLUMN_APN_ID, -1); 418 } 419 420 private long getAPNConfigCheckSum() { 421 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 422 return sp.getLong(APN_CONFIG_CHECKSUM, -1); 423 } 424 425 private void setAPNConfigCheckSum(long id) { 426 SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); 427 SharedPreferences.Editor editor = sp.edit(); 428 editor.putLong(APN_CONFIG_CHECKSUM, id); 429 editor.apply(); 430 } 431 432 @Override 433 public Cursor query(Uri url, String[] projectionIn, String selection, 434 String[] selectionArgs, String sort) { 435 436 checkPermission(); 437 438 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 439 qb.setTables("carriers"); 440 441 int match = s_urlMatcher.match(url); 442 switch (match) { 443 // do nothing 444 case URL_TELEPHONY: { 445 break; 446 } 447 448 449 case URL_CURRENT: { 450 qb.appendWhere("current IS NOT NULL"); 451 // do not ignore the selection since MMS may use it. 452 //selection = null; 453 break; 454 } 455 456 case URL_ID: { 457 qb.appendWhere("_id = " + url.getPathSegments().get(1)); 458 break; 459 } 460 461 case URL_PREFERAPN: 462 case URL_PREFERAPN_NO_UPDATE: { 463 qb.appendWhere("_id = " + getPreferredApnId()); 464 break; 465 } 466 467 default: { 468 return null; 469 } 470 } 471 472 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 473 Cursor ret = null; 474 try { 475 ret = qb.query(db, projectionIn, selection, selectionArgs, null, null, sort); 476 } catch (SQLException e) { 477 Log.e(TAG, "got exception when querying: " + e); 478 } 479 if (ret != null) 480 ret.setNotificationUri(getContext().getContentResolver(), url); 481 return ret; 482 } 483 484 @Override 485 public String getType(Uri url) 486 { 487 switch (s_urlMatcher.match(url)) { 488 case URL_TELEPHONY: 489 return "vnd.android.cursor.dir/telephony-carrier"; 490 491 case URL_ID: 492 return "vnd.android.cursor.item/telephony-carrier"; 493 494 case URL_PREFERAPN: 495 case URL_PREFERAPN_NO_UPDATE: 496 return "vnd.android.cursor.item/telephony-carrier"; 497 498 default: 499 throw new IllegalArgumentException("Unknown URL " + url); 500 } 501 } 502 503 @Override 504 public Uri insert(Uri url, ContentValues initialValues) 505 { 506 Uri result = null; 507 508 checkPermission(); 509 510 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 511 int match = s_urlMatcher.match(url); 512 boolean notify = false; 513 switch (match) 514 { 515 case URL_TELEPHONY: 516 { 517 ContentValues values; 518 if (initialValues != null) { 519 values = new ContentValues(initialValues); 520 } else { 521 values = new ContentValues(); 522 } 523 524 // TODO Review this. This code should probably not bet here. 525 // It is valid for the database to return a null string. 526 if (!values.containsKey(Telephony.Carriers.NAME)) { 527 values.put(Telephony.Carriers.NAME, ""); 528 } 529 if (!values.containsKey(Telephony.Carriers.APN)) { 530 values.put(Telephony.Carriers.APN, ""); 531 } 532 if (!values.containsKey(Telephony.Carriers.PORT)) { 533 values.put(Telephony.Carriers.PORT, ""); 534 } 535 if (!values.containsKey(Telephony.Carriers.PROXY)) { 536 values.put(Telephony.Carriers.PROXY, ""); 537 } 538 if (!values.containsKey(Telephony.Carriers.USER)) { 539 values.put(Telephony.Carriers.USER, ""); 540 } 541 if (!values.containsKey(Telephony.Carriers.SERVER)) { 542 values.put(Telephony.Carriers.SERVER, ""); 543 } 544 if (!values.containsKey(Telephony.Carriers.PASSWORD)) { 545 values.put(Telephony.Carriers.PASSWORD, ""); 546 } 547 if (!values.containsKey(Telephony.Carriers.MMSPORT)) { 548 values.put(Telephony.Carriers.MMSPORT, ""); 549 } 550 if (!values.containsKey(Telephony.Carriers.MMSPROXY)) { 551 values.put(Telephony.Carriers.MMSPROXY, ""); 552 } 553 if (!values.containsKey(Telephony.Carriers.AUTH_TYPE)) { 554 values.put(Telephony.Carriers.AUTH_TYPE, -1); 555 } 556 if (!values.containsKey(Telephony.Carriers.PROTOCOL)) { 557 values.put(Telephony.Carriers.PROTOCOL, "IP"); 558 } 559 if (!values.containsKey(Telephony.Carriers.ROAMING_PROTOCOL)) { 560 values.put(Telephony.Carriers.ROAMING_PROTOCOL, "IP"); 561 } 562 if (!values.containsKey(Telephony.Carriers.CARRIER_ENABLED)) { 563 values.put(Telephony.Carriers.CARRIER_ENABLED, true); 564 } 565 if (!values.containsKey(Telephony.Carriers.BEARER)) { 566 values.put(Telephony.Carriers.BEARER, 0); 567 } 568 569 long rowID = db.insert(CARRIERS_TABLE, null, values); 570 if (rowID > 0) 571 { 572 result = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, rowID); 573 notify = true; 574 } 575 576 if (false) Log.d(TAG, "inserted " + values.toString() + " rowID = " + rowID); 577 break; 578 } 579 580 case URL_CURRENT: 581 { 582 // null out the previous operator 583 db.update("carriers", s_currentNullMap, "current IS NOT NULL", null); 584 585 String numeric = initialValues.getAsString("numeric"); 586 int updated = db.update("carriers", s_currentSetMap, 587 "numeric = '" + numeric + "'", null); 588 589 if (updated > 0) 590 { 591 if (false) { 592 Log.d(TAG, "Setting numeric '" + numeric + "' to be the current operator"); 593 } 594 } 595 else 596 { 597 Log.e(TAG, "Failed setting numeric '" + numeric + "' to the current operator"); 598 } 599 break; 600 } 601 602 case URL_PREFERAPN: 603 case URL_PREFERAPN_NO_UPDATE: 604 { 605 if (initialValues != null) { 606 if(initialValues.containsKey(COLUMN_APN_ID)) { 607 setPreferredApnId(initialValues.getAsLong(COLUMN_APN_ID)); 608 } 609 } 610 break; 611 } 612 } 613 614 if (notify) { 615 getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null); 616 } 617 618 return result; 619 } 620 621 @Override 622 public int delete(Uri url, String where, String[] whereArgs) 623 { 624 int count = 0; 625 626 checkPermission(); 627 628 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 629 int match = s_urlMatcher.match(url); 630 switch (match) 631 { 632 case URL_TELEPHONY: 633 { 634 count = db.delete(CARRIERS_TABLE, where, whereArgs); 635 break; 636 } 637 638 case URL_CURRENT: 639 { 640 count = db.delete(CARRIERS_TABLE, where, whereArgs); 641 break; 642 } 643 644 case URL_ID: 645 { 646 count = db.delete(CARRIERS_TABLE, Telephony.Carriers._ID + "=?", 647 new String[] { url.getLastPathSegment() }); 648 break; 649 } 650 651 case URL_RESTOREAPN: { 652 count = 1; 653 restoreDefaultAPN(); 654 break; 655 } 656 657 case URL_PREFERAPN: 658 case URL_PREFERAPN_NO_UPDATE: 659 { 660 setPreferredApnId((long)-1); 661 if (match == URL_PREFERAPN) count = 1; 662 break; 663 } 664 665 default: { 666 throw new UnsupportedOperationException("Cannot delete that URL: " + url); 667 } 668 } 669 670 if (count > 0) { 671 getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null); 672 } 673 674 return count; 675 } 676 677 @Override 678 public int update(Uri url, ContentValues values, String where, String[] whereArgs) 679 { 680 int count = 0; 681 682 checkPermission(); 683 684 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 685 int match = s_urlMatcher.match(url); 686 switch (match) 687 { 688 case URL_TELEPHONY: 689 { 690 count = db.update(CARRIERS_TABLE, values, where, whereArgs); 691 break; 692 } 693 694 case URL_CURRENT: 695 { 696 count = db.update(CARRIERS_TABLE, values, where, whereArgs); 697 break; 698 } 699 700 case URL_ID: 701 { 702 if (where != null || whereArgs != null) { 703 throw new UnsupportedOperationException( 704 "Cannot update URL " + url + " with a where clause"); 705 } 706 count = db.update(CARRIERS_TABLE, values, Telephony.Carriers._ID + "=?", 707 new String[] { url.getLastPathSegment() }); 708 break; 709 } 710 711 case URL_PREFERAPN: 712 case URL_PREFERAPN_NO_UPDATE: 713 { 714 if (values != null) { 715 if (values.containsKey(COLUMN_APN_ID)) { 716 setPreferredApnId(values.getAsLong(COLUMN_APN_ID)); 717 if (match == URL_PREFERAPN) count = 1; 718 } 719 } 720 break; 721 } 722 723 default: { 724 throw new UnsupportedOperationException("Cannot update that URL: " + url); 725 } 726 } 727 728 if (count > 0) { 729 getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null); 730 } 731 732 return count; 733 } 734 735 private void checkPermission() { 736 getContext().enforceCallingOrSelfPermission("android.permission.WRITE_APN_SETTINGS", 737 "No permission to write APN settings"); 738 } 739 740 private DatabaseHelper mOpenHelper; 741 742 private void restoreDefaultAPN() { 743 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 744 745 try { 746 db.delete(CARRIERS_TABLE, null, null); 747 } catch (SQLException e) { 748 Log.e(TAG, "got exception when deleting to restore: " + e); 749 } 750 setPreferredApnId((long)-1); 751 mOpenHelper.initDatabase(db); 752 } 753} 754