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