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