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