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