TelephonyProvider.java revision 6de10270199e49235eb381f8f478d4b7a1c72fbe
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.SQLiteException; 32import android.database.sqlite.SQLiteOpenHelper; 33import android.database.sqlite.SQLiteQueryBuilder; 34import android.net.Uri; 35import android.os.Environment; 36import android.provider.Telephony; 37import android.telephony.SubscriptionManager; 38import android.telephony.TelephonyManager; 39import android.util.Log; 40import android.util.Xml; 41 42import com.android.internal.telephony.BaseCommands; 43import com.android.internal.telephony.Phone; 44import com.android.internal.telephony.PhoneConstants; 45import com.android.internal.util.XmlUtils; 46 47import org.xmlpull.v1.XmlPullParser; 48import org.xmlpull.v1.XmlPullParserException; 49 50import java.io.File; 51import java.io.FileNotFoundException; 52import java.io.FileReader; 53import java.io.IOException; 54 55 56public class TelephonyProvider extends ContentProvider 57{ 58 private static final String DATABASE_NAME = "telephony.db"; 59 private static final boolean DBG = true; 60 private static final boolean VDBG = false; 61 62 private static final int DATABASE_VERSION = 9 << 16; 63 private static final int URL_UNKNOWN = 0; 64 private static final int URL_TELEPHONY = 1; 65 private static final int URL_CURRENT = 2; 66 private static final int URL_ID = 3; 67 private static final int URL_RESTOREAPN = 4; 68 private static final int URL_PREFERAPN = 5; 69 private static final int URL_PREFERAPN_NO_UPDATE = 6; 70 private static final int URL_SIMINFO = 7; 71 private static final int URL_TELEPHONY_USING_SUBID = 8; 72 private static final int URL_CURRENT_USING_SUBID = 9; 73 private static final int URL_RESTOREAPN_USING_SUBID = 10; 74 private static final int URL_PREFERAPN_USING_SUBID = 11; 75 private static final int URL_PREFERAPN_NO_UPDATE_USING_SUBID = 12; 76 private static final int URL_SIMINFO_USING_SUBID = 13; 77 78 private static final String TAG = "TelephonyProvider"; 79 private static final String CARRIERS_TABLE = "carriers"; 80 private static final String SIMINFO_TABLE = "siminfo"; 81 82 private static final String PREF_FILE = "preferred-apn"; 83 private static final String COLUMN_APN_ID = "apn_id"; 84 85 private static final String PARTNER_APNS_PATH = "etc/apns-conf.xml"; 86 87 private static final UriMatcher s_urlMatcher = new UriMatcher(UriMatcher.NO_MATCH); 88 89 private static final ContentValues s_currentNullMap; 90 private static final ContentValues s_currentSetMap; 91 92 static { 93 s_urlMatcher.addURI("telephony", "carriers", URL_TELEPHONY); 94 s_urlMatcher.addURI("telephony", "carriers/current", URL_CURRENT); 95 s_urlMatcher.addURI("telephony", "carriers/#", URL_ID); 96 s_urlMatcher.addURI("telephony", "carriers/restore", URL_RESTOREAPN); 97 s_urlMatcher.addURI("telephony", "carriers/preferapn", URL_PREFERAPN); 98 s_urlMatcher.addURI("telephony", "carriers/preferapn_no_update", URL_PREFERAPN_NO_UPDATE); 99 100 s_urlMatcher.addURI("telephony", "siminfo", URL_SIMINFO); 101 102 s_urlMatcher.addURI("telephony", "carriers/subId/*", URL_TELEPHONY_USING_SUBID); 103 s_urlMatcher.addURI("telephony", "carriers/current/subId/*", URL_CURRENT_USING_SUBID); 104 s_urlMatcher.addURI("telephony", "carriers/restore/subId/*", URL_RESTOREAPN_USING_SUBID); 105 s_urlMatcher.addURI("telephony", "carriers/preferapn/subId/*", URL_PREFERAPN_USING_SUBID); 106 s_urlMatcher.addURI("telephony", "carriers/preferapn_no_update/subId/*", 107 URL_PREFERAPN_NO_UPDATE_USING_SUBID); 108 109 110 s_currentNullMap = new ContentValues(1); 111 s_currentNullMap.put("current", (Long) null); 112 113 s_currentSetMap = new ContentValues(1); 114 s_currentSetMap.put("current", "1"); 115 } 116 117 private static class DatabaseHelper extends SQLiteOpenHelper { 118 // Context to access resources with 119 private Context mContext; 120 121 /** 122 * DatabaseHelper helper class for loading apns into a database. 123 * 124 * @param context of the user. 125 */ 126 public DatabaseHelper(Context context) { 127 super(context, DATABASE_NAME, null, getVersion(context)); 128 mContext = context; 129 } 130 131 private static int getVersion(Context context) { 132 if (VDBG) log("getVersion:+"); 133 // Get the database version, combining a static schema version and the XML version 134 Resources r = context.getResources(); 135 XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns); 136 try { 137 XmlUtils.beginDocument(parser, "apns"); 138 int publicversion = Integer.parseInt(parser.getAttributeValue(null, "version")); 139 int version = DATABASE_VERSION | publicversion; 140 if (VDBG) log("getVersion:- version=0x" + Integer.toHexString(version)); 141 return version; 142 } catch (Exception e) { 143 loge("Can't get version of APN database" + e + " return version=" + 144 Integer.toHexString(DATABASE_VERSION)); 145 return DATABASE_VERSION; 146 } finally { 147 parser.close(); 148 } 149 } 150 151 @Override 152 public void onCreate(SQLiteDatabase db) { 153 if (DBG) log("dbh.onCreate:+ db=" + db); 154 createSimInfoTable(db); 155 createCarriersTable(db); 156 initDatabase(db); 157 if (DBG) log("dbh.onCreate:- db=" + db); 158 } 159 160 @Override 161 public void onOpen(SQLiteDatabase db) { 162 if (VDBG) log("dbh.onOpen:+ db=" + db); 163 try { 164 // Try to access the table and create it if "no such table" 165 db.query(SIMINFO_TABLE, null, null, null, null, null, null); 166 if (DBG) log("dbh.onOpen: ok, queried table=" + SIMINFO_TABLE); 167 } catch (SQLiteException e) { 168 loge("Exception " + SIMINFO_TABLE + "e=" + e); 169 if (e.getMessage().startsWith("no such table")) { 170 createSimInfoTable(db); 171 } 172 } 173 try { 174 db.query(CARRIERS_TABLE, null, null, null, null, null, null); 175 if (DBG) log("dbh.onOpen: ok, queried table=" + CARRIERS_TABLE); 176 } catch (SQLiteException e) { 177 loge("Exception " + CARRIERS_TABLE + " e=" + e); 178 if (e.getMessage().startsWith("no such table")) { 179 createCarriersTable(db); 180 } 181 } 182 if (VDBG) log("dbh.onOpen:- db=" + db); 183 } 184 185 private void createSimInfoTable(SQLiteDatabase db) { 186 if (DBG) log("dbh.createSimInfoTable:+"); 187 db.execSQL("CREATE TABLE " + SIMINFO_TABLE + "(" 188 + "_id INTEGER PRIMARY KEY AUTOINCREMENT," 189 + SubscriptionManager.ICC_ID + " TEXT NOT NULL," 190 + SubscriptionManager.SIM_ID + " INTEGER DEFAULT " + SubscriptionManager.SIM_NOT_INSERTED + "," 191 + SubscriptionManager.DISPLAY_NAME + " TEXT," 192 + SubscriptionManager.NAME_SOURCE + " INTEGER DEFAULT " + SubscriptionManager.DEFAULT_SOURCE + "," 193 + SubscriptionManager.COLOR + " INTEGER DEFAULT " + SubscriptionManager.COLOR_DEFAULT + "," 194 + SubscriptionManager.NUMBER + " TEXT," 195 + SubscriptionManager.DISPLAY_NUMBER_FORMAT + " INTEGER NOT NULL DEFAULT " + SubscriptionManager.DISLPAY_NUMBER_DEFAULT + "," 196 + SubscriptionManager.DATA_ROAMING + " INTEGER DEFAULT " + SubscriptionManager.DATA_ROAMING_DEFAULT 197 + ");"); 198 if (DBG) log("dbh.createSimInfoTable:-"); 199 } 200 201 private void createCarriersTable(SQLiteDatabase db) { 202 // Set up the database schema 203 if (DBG) log("dbh.createCarriersTable:+"); 204 db.execSQL("CREATE TABLE " + CARRIERS_TABLE + 205 "(_id INTEGER PRIMARY KEY," + 206 "name TEXT," + 207 "numeric TEXT," + 208 "mcc TEXT," + 209 "mnc TEXT," + 210 "apn TEXT," + 211 "user TEXT," + 212 "server TEXT," + 213 "password TEXT," + 214 "proxy TEXT," + 215 "port TEXT," + 216 "mmsproxy TEXT," + 217 "mmsport TEXT," + 218 "mmsc TEXT," + 219 "authtype INTEGER," + 220 "type TEXT," + 221 "current INTEGER," + 222 "protocol TEXT," + 223 "roaming_protocol TEXT," + 224 "carrier_enabled BOOLEAN," + 225 "bearer INTEGER," + 226 "mvno_type TEXT," + 227 "mvno_match_data TEXT," + 228 "sub_id LONG DEFAULT -1);"); 229 /* FIXME Currenlty sub_id is column is not used for query purpose. 230 This would be modified to more appropriate default value later. */ 231 if (DBG) log("dbh.createCarriersTable:-"); 232 } 233 private void initDatabase(SQLiteDatabase db) { 234 if (VDBG) log("dbh.initDatabase:+ db=" + db); 235 // Read internal APNS data 236 Resources r = mContext.getResources(); 237 XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns); 238 int publicversion = -1; 239 try { 240 XmlUtils.beginDocument(parser, "apns"); 241 publicversion = Integer.parseInt(parser.getAttributeValue(null, "version")); 242 loadApns(db, parser); 243 } catch (Exception e) { 244 loge("Got exception while loading APN database." + e); 245 } finally { 246 parser.close(); 247 } 248 249 // Read external APNS data (partner-provided) 250 XmlPullParser confparser = null; 251 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system". 252 File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH); 253 FileReader confreader = null; 254 try { 255 confreader = new FileReader(confFile); 256 confparser = Xml.newPullParser(); 257 confparser.setInput(confreader); 258 XmlUtils.beginDocument(confparser, "apns"); 259 260 // Sanity check. Force internal version and confidential versions to agree 261 int confversion = Integer.parseInt(confparser.getAttributeValue(null, "version")); 262 if (publicversion != confversion) { 263 throw new IllegalStateException("Internal APNS file version doesn't match " 264 + confFile.getAbsolutePath()); 265 } 266 267 loadApns(db, confparser); 268 } catch (FileNotFoundException e) { 269 // It's ok if the file isn't found. It means there isn't a confidential file 270 // Log.e(TAG, "File not found: '" + confFile.getAbsolutePath() + "'"); 271 } catch (Exception e) { 272 loge("Exception while parsing '" + confFile.getAbsolutePath() + "'" + e); 273 } finally { 274 try { if (confreader != null) confreader.close(); } catch (IOException e) { } 275 } 276 if (VDBG) log("dbh.initDatabase:- db=" + db); 277 } 278 279 @Override 280 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 281 if (DBG) { 282 log("dbh.onUpgrade:+ db=" + db + " oldV=" + oldVersion + " newV=" + newVersion); 283 } 284 285 if (oldVersion < (5 << 16 | 6)) { 286 // 5 << 16 is the Database version and 6 in the xml version. 287 288 // This change adds a new authtype column to the database. 289 // The auth type column can have 4 values: 0 (None), 1 (PAP), 2 (CHAP) 290 // 3 (PAP or CHAP). To avoid breaking compatibility, with already working 291 // APNs, the unset value (-1) will be used. If the value is -1. 292 // the authentication will default to 0 (if no user / password) is specified 293 // or to 3. Currently, there have been no reported problems with 294 // pre-configured APNs and hence it is set to -1 for them. Similarly, 295 // if the user, has added a new APN, we set the authentication type 296 // to -1. 297 298 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 299 " ADD COLUMN authtype INTEGER DEFAULT -1;"); 300 301 oldVersion = 5 << 16 | 6; 302 } 303 if (oldVersion < (6 << 16 | 6)) { 304 // Add protcol fields to the APN. The XML file does not change. 305 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 306 " ADD COLUMN protocol TEXT DEFAULT IP;"); 307 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 308 " ADD COLUMN roaming_protocol TEXT DEFAULT IP;"); 309 oldVersion = 6 << 16 | 6; 310 } 311 if (oldVersion < (7 << 16 | 6)) { 312 // Add carrier_enabled, bearer fields to the APN. The XML file does not change. 313 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 314 " ADD COLUMN carrier_enabled BOOLEAN DEFAULT 1;"); 315 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 316 " ADD COLUMN bearer INTEGER DEFAULT 0;"); 317 oldVersion = 7 << 16 | 6; 318 } 319 if (oldVersion < (8 << 16 | 6)) { 320 // Add mvno_type, mvno_match_data fields to the APN. 321 // The XML file does not change. 322 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 323 " ADD COLUMN mvno_type TEXT DEFAULT '';"); 324 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 325 " ADD COLUMN mvno_match_data TEXT DEFAULT '';"); 326 oldVersion = 8 << 16 | 6; 327 } 328 if (oldVersion < (9 << 16 | 6)) { 329 db.execSQL("ALTER TABLE " + CARRIERS_TABLE + 330 " ADD COLUMN sub_id LONG DEFAULT -1;"); 331 oldVersion = 9 << 16 | 6; 332 } 333 if (DBG) { 334 log("dbh.onUpgrade:- db=" + db + " oldV=" + oldVersion + " newV=" + newVersion); 335 } 336 } 337 338 /** 339 * Gets the next row of apn values. 340 * 341 * @param parser the parser 342 * @return the row or null if it's not an apn 343 */ 344 private ContentValues getRow(XmlPullParser parser) { 345 if (!"apn".equals(parser.getName())) { 346 return null; 347 } 348 349 ContentValues map = new ContentValues(); 350 351 String mcc = parser.getAttributeValue(null, "mcc"); 352 String mnc = parser.getAttributeValue(null, "mnc"); 353 String numeric = mcc + mnc; 354 355 map.put(Telephony.Carriers.NUMERIC,numeric); 356 map.put(Telephony.Carriers.MCC, mcc); 357 map.put(Telephony.Carriers.MNC, mnc); 358 map.put(Telephony.Carriers.NAME, parser.getAttributeValue(null, "carrier")); 359 map.put(Telephony.Carriers.APN, parser.getAttributeValue(null, "apn")); 360 map.put(Telephony.Carriers.USER, parser.getAttributeValue(null, "user")); 361 map.put(Telephony.Carriers.SERVER, parser.getAttributeValue(null, "server")); 362 map.put(Telephony.Carriers.PASSWORD, parser.getAttributeValue(null, "password")); 363 364 // do not add NULL to the map so that insert() will set the default value 365 String proxy = parser.getAttributeValue(null, "proxy"); 366 if (proxy != null) { 367 map.put(Telephony.Carriers.PROXY, proxy); 368 } 369 String port = parser.getAttributeValue(null, "port"); 370 if (port != null) { 371 map.put(Telephony.Carriers.PORT, port); 372 } 373 String mmsproxy = parser.getAttributeValue(null, "mmsproxy"); 374 if (mmsproxy != null) { 375 map.put(Telephony.Carriers.MMSPROXY, mmsproxy); 376 } 377 String mmsport = parser.getAttributeValue(null, "mmsport"); 378 if (mmsport != null) { 379 map.put(Telephony.Carriers.MMSPORT, mmsport); 380 } 381 map.put(Telephony.Carriers.MMSC, parser.getAttributeValue(null, "mmsc")); 382 String type = parser.getAttributeValue(null, "type"); 383 if (type != null) { 384 map.put(Telephony.Carriers.TYPE, type); 385 } 386 387 String auth = parser.getAttributeValue(null, "authtype"); 388 if (auth != null) { 389 map.put(Telephony.Carriers.AUTH_TYPE, Integer.parseInt(auth)); 390 } 391 392 String protocol = parser.getAttributeValue(null, "protocol"); 393 if (protocol != null) { 394 map.put(Telephony.Carriers.PROTOCOL, protocol); 395 } 396 397 String roamingProtocol = parser.getAttributeValue(null, "roaming_protocol"); 398 if (roamingProtocol != null) { 399 map.put(Telephony.Carriers.ROAMING_PROTOCOL, roamingProtocol); 400 } 401 402 String carrierEnabled = parser.getAttributeValue(null, "carrier_enabled"); 403 if (carrierEnabled != null) { 404 map.put(Telephony.Carriers.CARRIER_ENABLED, Boolean.parseBoolean(carrierEnabled)); 405 } 406 407 String bearer = parser.getAttributeValue(null, "bearer"); 408 if (bearer != null) { 409 map.put(Telephony.Carriers.BEARER, Integer.parseInt(bearer)); 410 } 411 412 String mvno_type = parser.getAttributeValue(null, "mvno_type"); 413 if (mvno_type != null) { 414 String mvno_match_data = parser.getAttributeValue(null, "mvno_match_data"); 415 if (mvno_match_data != null) { 416 map.put(Telephony.Carriers.MVNO_TYPE, mvno_type); 417 map.put(Telephony.Carriers.MVNO_MATCH_DATA, mvno_match_data); 418 } 419 } 420 return map; 421 } 422 423 /* 424 * Loads apns from xml file into the database 425 * 426 * @param db the sqlite database to write to 427 * @param parser the xml parser 428 * 429 */ 430 private void loadApns(SQLiteDatabase db, XmlPullParser parser) { 431 if (parser != null) { 432 try { 433 db.beginTransaction(); 434 XmlUtils.nextElement(parser); 435 while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 436 ContentValues row = getRow(parser); 437 if (row == null) { 438 throw new XmlPullParserException("Expected 'apn' tag", parser, null); 439 } 440 insertAddingDefaults(db, CARRIERS_TABLE, row); 441 XmlUtils.nextElement(parser); 442 } 443 db.setTransactionSuccessful(); 444 } catch (XmlPullParserException e) { 445 loge("Got XmlPullParserException while loading apns." + e); 446 } catch (IOException e) { 447 loge("Got IOException while loading apns." + e); 448 } catch (SQLException e) { 449 loge("Got SQLException while loading apns." + e); 450 } finally { 451 db.endTransaction(); 452 } 453 } 454 } 455 456 private void insertAddingDefaults(SQLiteDatabase db, String table, ContentValues row) { 457 // Initialize defaults if any 458 if (row.containsKey(Telephony.Carriers.AUTH_TYPE) == false) { 459 row.put(Telephony.Carriers.AUTH_TYPE, -1); 460 } 461 if (row.containsKey(Telephony.Carriers.PROTOCOL) == false) { 462 row.put(Telephony.Carriers.PROTOCOL, "IP"); 463 } 464 if (row.containsKey(Telephony.Carriers.ROAMING_PROTOCOL) == false) { 465 row.put(Telephony.Carriers.ROAMING_PROTOCOL, "IP"); 466 } 467 if (row.containsKey(Telephony.Carriers.CARRIER_ENABLED) == false) { 468 row.put(Telephony.Carriers.CARRIER_ENABLED, true); 469 } 470 if (row.containsKey(Telephony.Carriers.BEARER) == false) { 471 row.put(Telephony.Carriers.BEARER, 0); 472 } 473 if (row.containsKey(Telephony.Carriers.MVNO_TYPE) == false) { 474 row.put(Telephony.Carriers.MVNO_TYPE, ""); 475 } 476 if (row.containsKey(Telephony.Carriers.MVNO_MATCH_DATA) == false) { 477 row.put(Telephony.Carriers.MVNO_MATCH_DATA, ""); 478 } 479 db.insert(CARRIERS_TABLE, null, row); 480 } 481 } 482 483 @Override 484 public boolean onCreate() { 485 if (VDBG) log("onCreate:+"); 486 mOpenHelper = new DatabaseHelper(getContext()); 487 if (VDBG) log("onCreate:- ret true"); 488 return true; 489 } 490 491 private void setPreferredApnId(Long id, long subId) { 492 SharedPreferences sp = getContext().getSharedPreferences( 493 PREF_FILE + subId, Context.MODE_PRIVATE); 494 SharedPreferences.Editor editor = sp.edit(); 495 editor.putLong(COLUMN_APN_ID, id != null ? id.longValue() : -1); 496 editor.apply(); 497 } 498 499 private long getPreferredApnId(long subId) { 500 SharedPreferences sp = getContext().getSharedPreferences( 501 PREF_FILE + subId, Context.MODE_PRIVATE); 502 return sp.getLong(COLUMN_APN_ID, -1); 503 } 504 505 @Override 506 public Cursor query(Uri url, String[] projectionIn, String selection, 507 String[] selectionArgs, String sort) { 508 TelephonyManager mTelephonyManager = 509 (TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE); 510 long subId = SubscriptionManager.getDefaultSubId(); 511 String subIdString; 512 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 513 qb.setStrict(true); // a little protection from injection attacks 514 qb.setTables("carriers"); 515 516 int match = s_urlMatcher.match(url); 517 switch (match) { 518 case URL_TELEPHONY_USING_SUBID: { 519 subIdString = url.getLastPathSegment(); 520 try { 521 subId = Long.parseLong(subIdString); 522 } catch (NumberFormatException e) { 523 loge("NumberFormatException" + e); 524 return null; 525 } 526 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 527 qb.appendWhere("numeric = " + mTelephonyManager.getSimOperator(subId)); 528 // FIXME alter the selection to pass subId 529 // selection = selection + "and subId = " 530 } 531 //intentional fall through from above case 532 // do nothing 533 case URL_TELEPHONY: { 534 break; 535 } 536 537 case URL_CURRENT_USING_SUBID: { 538 subIdString = url.getLastPathSegment(); 539 try { 540 subId = Long.parseLong(subIdString); 541 } catch (NumberFormatException e) { 542 loge("NumberFormatException" + e); 543 return null; 544 } 545 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 546 // FIXME alter the selection to pass subId 547 // selection = selection + "and subId = " 548 } 549 //intentional fall through from above case 550 case URL_CURRENT: { 551 qb.appendWhere("current IS NOT NULL"); 552 // do not ignore the selection since MMS may use it. 553 //selection = null; 554 break; 555 } 556 557 case URL_ID: { 558 qb.appendWhere("_id = " + url.getPathSegments().get(1)); 559 break; 560 } 561 562 case URL_PREFERAPN_USING_SUBID: 563 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: { 564 subIdString = url.getLastPathSegment(); 565 try { 566 subId = Long.parseLong(subIdString); 567 } catch (NumberFormatException e) { 568 loge("NumberFormatException" + e); 569 return null; 570 } 571 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 572 } 573 //intentional fall through from above case 574 case URL_PREFERAPN: 575 case URL_PREFERAPN_NO_UPDATE: { 576 qb.appendWhere("_id = " + getPreferredApnId(subId)); 577 break; 578 } 579 580 case URL_SIMINFO: { 581 qb.setTables(SIMINFO_TABLE); 582 break; 583 } 584 585 default: { 586 return null; 587 } 588 } 589 590 if (match != URL_SIMINFO) { 591 if (projectionIn != null) { 592 for (String column : projectionIn) { 593 if (Telephony.Carriers.TYPE.equals(column) || 594 Telephony.Carriers.MMSC.equals(column) || 595 Telephony.Carriers.MMSPROXY.equals(column) || 596 Telephony.Carriers.MMSPORT.equals(column) || 597 Telephony.Carriers.APN.equals(column)) { 598 // noop 599 } else { 600 checkPermission(); 601 break; 602 } 603 } 604 } else { 605 // null returns all columns, so need permission check 606 checkPermission(); 607 } 608 } 609 610 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 611 Cursor ret = null; 612 try { 613 ret = qb.query(db, projectionIn, selection, selectionArgs, null, null, sort); 614 } catch (SQLException e) { 615 loge("got exception when querying: " + e); 616 } 617 if (ret != null) 618 ret.setNotificationUri(getContext().getContentResolver(), url); 619 return ret; 620 } 621 622 @Override 623 public String getType(Uri url) 624 { 625 switch (s_urlMatcher.match(url)) { 626 case URL_TELEPHONY: 627 case URL_TELEPHONY_USING_SUBID: 628 return "vnd.android.cursor.dir/telephony-carrier"; 629 630 case URL_ID: 631 return "vnd.android.cursor.item/telephony-carrier"; 632 633 case URL_PREFERAPN_USING_SUBID: 634 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: 635 case URL_PREFERAPN: 636 case URL_PREFERAPN_NO_UPDATE: 637 return "vnd.android.cursor.item/telephony-carrier"; 638 639 default: 640 throw new IllegalArgumentException("Unknown URL " + url); 641 } 642 } 643 644 @Override 645 public Uri insert(Uri url, ContentValues initialValues) 646 { 647 Uri result = null; 648 long subId = SubscriptionManager.getDefaultSubId(); 649 650 checkPermission(); 651 652 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 653 int match = s_urlMatcher.match(url); 654 boolean notify = false; 655 switch (match) 656 { 657 case URL_TELEPHONY_USING_SUBID: 658 { 659 String subIdString = url.getLastPathSegment(); 660 try { 661 subId = Long.parseLong(subIdString); 662 } catch (NumberFormatException e) { 663 loge("NumberFormatException" + e); 664 return result; 665 } 666 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 667 } 668 //intentional fall through from above case 669 670 case URL_TELEPHONY: 671 { 672 ContentValues values; 673 if (initialValues != null) { 674 values = new ContentValues(initialValues); 675 } else { 676 values = new ContentValues(); 677 } 678 679 // TODO Review this. This code should probably not bet here. 680 // It is valid for the database to return a null string. 681 if (!values.containsKey(Telephony.Carriers.NAME)) { 682 values.put(Telephony.Carriers.NAME, ""); 683 } 684 if (!values.containsKey(Telephony.Carriers.APN)) { 685 values.put(Telephony.Carriers.APN, ""); 686 } 687 if (!values.containsKey(Telephony.Carriers.PORT)) { 688 values.put(Telephony.Carriers.PORT, ""); 689 } 690 if (!values.containsKey(Telephony.Carriers.PROXY)) { 691 values.put(Telephony.Carriers.PROXY, ""); 692 } 693 if (!values.containsKey(Telephony.Carriers.USER)) { 694 values.put(Telephony.Carriers.USER, ""); 695 } 696 if (!values.containsKey(Telephony.Carriers.SERVER)) { 697 values.put(Telephony.Carriers.SERVER, ""); 698 } 699 if (!values.containsKey(Telephony.Carriers.PASSWORD)) { 700 values.put(Telephony.Carriers.PASSWORD, ""); 701 } 702 if (!values.containsKey(Telephony.Carriers.MMSPORT)) { 703 values.put(Telephony.Carriers.MMSPORT, ""); 704 } 705 if (!values.containsKey(Telephony.Carriers.MMSPROXY)) { 706 values.put(Telephony.Carriers.MMSPROXY, ""); 707 } 708 if (!values.containsKey(Telephony.Carriers.AUTH_TYPE)) { 709 values.put(Telephony.Carriers.AUTH_TYPE, -1); 710 } 711 if (!values.containsKey(Telephony.Carriers.PROTOCOL)) { 712 values.put(Telephony.Carriers.PROTOCOL, "IP"); 713 } 714 if (!values.containsKey(Telephony.Carriers.ROAMING_PROTOCOL)) { 715 values.put(Telephony.Carriers.ROAMING_PROTOCOL, "IP"); 716 } 717 if (!values.containsKey(Telephony.Carriers.CARRIER_ENABLED)) { 718 values.put(Telephony.Carriers.CARRIER_ENABLED, true); 719 } 720 if (!values.containsKey(Telephony.Carriers.BEARER)) { 721 values.put(Telephony.Carriers.BEARER, 0); 722 } 723 if (!values.containsKey(Telephony.Carriers.MVNO_TYPE)) { 724 values.put(Telephony.Carriers.MVNO_TYPE, ""); 725 } 726 if (!values.containsKey(Telephony.Carriers.MVNO_MATCH_DATA)) { 727 values.put(Telephony.Carriers.MVNO_MATCH_DATA, ""); 728 } 729 730 if (!values.containsKey(Telephony.Carriers.SUB_ID)) { 731 values.put(Telephony.Carriers.SUB_ID, subId); 732 } 733 734 long rowID = db.insert(CARRIERS_TABLE, null, values); 735 if (rowID > 0) 736 { 737 result = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, rowID); 738 notify = true; 739 } 740 741 if (VDBG) log("inserted " + values.toString() + " rowID = " + rowID); 742 break; 743 } 744 745 case URL_CURRENT_USING_SUBID: 746 { 747 String subIdString = url.getLastPathSegment(); 748 try { 749 subId = Long.parseLong(subIdString); 750 } catch (NumberFormatException e) { 751 loge("NumberFormatException" + e); 752 return result; 753 } 754 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 755 // FIXME use subId in the query 756 } 757 //intentional fall through from above case 758 759 case URL_CURRENT: 760 { 761 // null out the previous operator 762 db.update("carriers", s_currentNullMap, "current IS NOT NULL", null); 763 764 String numeric = initialValues.getAsString("numeric"); 765 int updated = db.update("carriers", s_currentSetMap, 766 "numeric = '" + numeric + "'", null); 767 768 if (updated > 0) 769 { 770 if (VDBG) log("Setting numeric '" + numeric + "' to be the current operator"); 771 } 772 else 773 { 774 loge("Failed setting numeric '" + numeric + "' to the current operator"); 775 } 776 break; 777 } 778 779 case URL_PREFERAPN_USING_SUBID: 780 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: 781 { 782 String subIdString = url.getLastPathSegment(); 783 try { 784 subId = Long.parseLong(subIdString); 785 } catch (NumberFormatException e) { 786 loge("NumberFormatException" + e); 787 return result; 788 } 789 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 790 } 791 //intentional fall through from above case 792 793 case URL_PREFERAPN: 794 case URL_PREFERAPN_NO_UPDATE: 795 { 796 if (initialValues != null) { 797 if(initialValues.containsKey(COLUMN_APN_ID)) { 798 setPreferredApnId(initialValues.getAsLong(COLUMN_APN_ID), subId); 799 } 800 } 801 break; 802 } 803 804 case URL_SIMINFO: { 805 long id = db.insert(SIMINFO_TABLE, null, initialValues); 806 result = ContentUris.withAppendedId(SubscriptionManager.CONTENT_URI, id); 807 break; 808 } 809 } 810 811 if (notify) { 812 getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null); 813 } 814 815 return result; 816 } 817 818 @Override 819 public int delete(Uri url, String where, String[] whereArgs) 820 { 821 int count = 0; 822 long subId = SubscriptionManager.getDefaultSubId(); 823 824 checkPermission(); 825 826 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 827 int match = s_urlMatcher.match(url); 828 switch (match) 829 { 830 case URL_TELEPHONY_USING_SUBID: 831 { 832 String subIdString = url.getLastPathSegment(); 833 try { 834 subId = Long.parseLong(subIdString); 835 } catch (NumberFormatException e) { 836 loge("NumberFormatException" + e); 837 throw new IllegalArgumentException("Invalid subId " + url); 838 } 839 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 840 // FIXME use subId in query 841 } 842 //intentional fall through from above case 843 844 case URL_TELEPHONY: 845 { 846 count = db.delete(CARRIERS_TABLE, where, whereArgs); 847 break; 848 } 849 850 case URL_CURRENT_USING_SUBID: { 851 String subIdString = url.getLastPathSegment(); 852 try { 853 subId = Long.parseLong(subIdString); 854 } catch (NumberFormatException e) { 855 loge("NumberFormatException" + e); 856 throw new IllegalArgumentException("Invalid subId " + url); 857 } 858 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 859 // FIXME use subId in query 860 } 861 //intentional fall through from above case 862 863 case URL_CURRENT: 864 { 865 count = db.delete(CARRIERS_TABLE, where, whereArgs); 866 break; 867 } 868 869 case URL_ID: 870 { 871 count = db.delete(CARRIERS_TABLE, Telephony.Carriers._ID + "=?", 872 new String[] { url.getLastPathSegment() }); 873 break; 874 } 875 876 case URL_RESTOREAPN_USING_SUBID: { 877 String subIdString = url.getLastPathSegment(); 878 try { 879 subId = Long.parseLong(subIdString); 880 } catch (NumberFormatException e) { 881 loge("NumberFormatException" + e); 882 throw new IllegalArgumentException("Invalid subId " + url); 883 } 884 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 885 // FIXME use subId in query 886 } 887 case URL_RESTOREAPN: { 888 count = 1; 889 restoreDefaultAPN(subId); 890 break; 891 } 892 893 case URL_PREFERAPN_USING_SUBID: 894 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: { 895 String subIdString = url.getLastPathSegment(); 896 try { 897 subId = Long.parseLong(subIdString); 898 } catch (NumberFormatException e) { 899 loge("NumberFormatException" + e); 900 throw new IllegalArgumentException("Invalid subId " + url); 901 } 902 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 903 } 904 //intentional fall through from above case 905 906 case URL_PREFERAPN: 907 case URL_PREFERAPN_NO_UPDATE: 908 { 909 setPreferredApnId((long)-1, subId); 910 if ((match == URL_PREFERAPN) || (match == URL_PREFERAPN_USING_SUBID)) count = 1; 911 break; 912 } 913 914 case URL_SIMINFO: { 915 count = db.delete(SIMINFO_TABLE, where, whereArgs); 916 break; 917 } 918 919 default: { 920 throw new UnsupportedOperationException("Cannot delete that URL: " + url); 921 } 922 } 923 924 if (count > 0) { 925 getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null); 926 } 927 928 return count; 929 } 930 931 @Override 932 public int update(Uri url, ContentValues values, String where, String[] whereArgs) 933 { 934 int count = 0; 935 int uriType = URL_UNKNOWN; 936 long subId = SubscriptionManager.getDefaultSubId(); 937 938 checkPermission(); 939 940 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 941 int match = s_urlMatcher.match(url); 942 switch (match) 943 { 944 case URL_TELEPHONY_USING_SUBID: 945 { 946 String subIdString = url.getLastPathSegment(); 947 try { 948 subId = Long.parseLong(subIdString); 949 } catch (NumberFormatException e) { 950 loge("NumberFormatException" + e); 951 throw new IllegalArgumentException("Invalid subId " + url); 952 } 953 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 954 //FIXME use subId in the query 955 } 956 //intentional fall through from above case 957 958 case URL_TELEPHONY: 959 { 960 count = db.update(CARRIERS_TABLE, values, where, whereArgs); 961 break; 962 } 963 964 case URL_CURRENT_USING_SUBID: 965 { 966 String subIdString = url.getLastPathSegment(); 967 try { 968 subId = Long.parseLong(subIdString); 969 } catch (NumberFormatException e) { 970 loge("NumberFormatException" + e); 971 throw new IllegalArgumentException("Invalid subId " + url); 972 } 973 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 974 //FIXME use subId in the query 975 } 976 //intentional fall through from above case 977 978 case URL_CURRENT: 979 { 980 count = db.update(CARRIERS_TABLE, values, where, whereArgs); 981 break; 982 } 983 984 case URL_ID: 985 { 986 if (where != null || whereArgs != null) { 987 throw new UnsupportedOperationException( 988 "Cannot update URL " + url + " with a where clause"); 989 } 990 count = db.update(CARRIERS_TABLE, values, Telephony.Carriers._ID + "=?", 991 new String[] { url.getLastPathSegment() }); 992 break; 993 } 994 995 case URL_PREFERAPN_USING_SUBID: 996 case URL_PREFERAPN_NO_UPDATE_USING_SUBID: 997 { 998 String subIdString = url.getLastPathSegment(); 999 try { 1000 subId = Long.parseLong(subIdString); 1001 } catch (NumberFormatException e) { 1002 loge("NumberFormatException" + e); 1003 throw new IllegalArgumentException("Invalid subId " + url); 1004 } 1005 if (DBG) log("subIdString = " + subIdString + " subId = " + subId); 1006 } 1007 1008 case URL_PREFERAPN: 1009 case URL_PREFERAPN_NO_UPDATE: 1010 { 1011 if (values != null) { 1012 if (values.containsKey(COLUMN_APN_ID)) { 1013 setPreferredApnId(values.getAsLong(COLUMN_APN_ID), subId); 1014 if ((match == URL_PREFERAPN) || 1015 (match == URL_PREFERAPN_USING_SUBID)) { 1016 count = 1; 1017 } 1018 } 1019 } 1020 break; 1021 } 1022 1023 case URL_SIMINFO: { 1024 count = db.update(SIMINFO_TABLE, values, where, whereArgs); 1025 uriType = URL_SIMINFO; 1026 break; 1027 } 1028 1029 default: { 1030 throw new UnsupportedOperationException("Cannot update that URL: " + url); 1031 } 1032 } 1033 1034 if (count > 0) { 1035 switch (uriType) { 1036 case URL_SIMINFO: 1037 getContext().getContentResolver().notifyChange( 1038 SubscriptionManager.CONTENT_URI, null); 1039 break; 1040 default: 1041 getContext().getContentResolver().notifyChange( 1042 Telephony.Carriers.CONTENT_URI, null); 1043 } 1044 } 1045 1046 return count; 1047 } 1048 1049 private void checkPermission() { 1050 getContext().enforceCallingOrSelfPermission("android.permission.WRITE_APN_SETTINGS", 1051 "No permission to write APN settings"); 1052 } 1053 1054 private DatabaseHelper mOpenHelper; 1055 1056 private void restoreDefaultAPN(long subId) { 1057 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 1058 1059 try { 1060 db.delete(CARRIERS_TABLE, null, null); 1061 } catch (SQLException e) { 1062 loge("got exception when deleting to restore: " + e); 1063 } 1064 setPreferredApnId((long)-1, subId); 1065 mOpenHelper.initDatabase(db); 1066 } 1067 1068 /** 1069 * Log with debug 1070 * 1071 * @param s is string log 1072 */ 1073 private static void log(String s) { 1074 Log.d(TAG, s); 1075 } 1076 1077 private static void loge(String s) { 1078 Log.e(TAG, s); 1079 } 1080} 1081