TelephonyProvider.java revision 15fe736a6429ed6e4cc0138ce88b241807af207e
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.*; 21import android.content.res.Resources; 22import android.content.res.XmlResourceParser; 23import android.database.Cursor; 24import android.database.sqlite.SQLiteDatabase; 25import android.database.sqlite.SQLiteOpenHelper; 26import android.database.sqlite.SQLiteQueryBuilder; 27import android.net.Uri; 28import android.os.Environment; 29import android.provider.Telephony; 30import android.util.Config; 31import android.util.Log; 32import android.util.Xml; 33import com.android.internal.util.XmlUtils; 34 35import org.xmlpull.v1.XmlPullParser; 36import org.xmlpull.v1.XmlPullParserException; 37 38import java.io.File; 39import java.io.FileNotFoundException; 40import java.io.FileReader; 41import java.io.IOException; 42 43public class TelephonyProvider extends ContentProvider 44{ 45 private static final String DATABASE_NAME = "telephony.db"; 46 // DATABASE_VERSION needs to be in-sync with version in apns.xml. 47 private static final int DATABASE_VERSION = 4 << 16; 48 private static final int URL_TELEPHONY = 1; 49 private static final int URL_CURRENT = 2; 50 private static final int URL_ID = 3; 51 52 private static final String TAG = "TelephonyProvider"; 53 private static final String CARRIERS_TABLE = "carriers"; 54 private static final String PARTNER_APNS_PATH = "etc/apns-conf.xml"; 55 56 private static final UriMatcher s_urlMatcher = new UriMatcher(UriMatcher.NO_MATCH); 57 58 private static final ContentValues s_currentNullMap; 59 private static final ContentValues s_currentSetMap; 60 61 static { 62 s_urlMatcher.addURI("telephony", "carriers", URL_TELEPHONY); 63 s_urlMatcher.addURI("telephony", "carriers/current", URL_CURRENT); 64 s_urlMatcher.addURI("telephony", "carriers/#", URL_ID); 65 66 s_currentNullMap = new ContentValues(1); 67 s_currentNullMap.put("current", (Long) null); 68 69 s_currentSetMap = new ContentValues(1); 70 s_currentSetMap.put("current", "1"); 71 } 72 73 private static class DatabaseHelper extends SQLiteOpenHelper { 74 // Context to access resources with 75 private Context mContext; 76 77 /** 78 * DatabaseHelper helper class for loading apns into a database. 79 * 80 * @param parser the system-default parser for apns.xml 81 * @param confidential an optional parser for confidential APNS (stored separately) 82 */ 83 public DatabaseHelper(Context context) { 84 super(context, DATABASE_NAME, null, getVersion(context)); 85 mContext = context; 86 } 87 88 private static int getVersion(Context context) { 89 // Get the database version, combining a static schema version and the XML version 90 Resources r = context.getResources(); 91 XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns); 92 try { 93 XmlUtils.beginDocument(parser, "apns"); 94 int publicversion = Integer.parseInt(parser.getAttributeValue(null, "version")); 95 return DATABASE_VERSION | publicversion; 96 } catch (Exception e) { 97 Log.e(TAG, "Can't get version of APN database", e); 98 return DATABASE_VERSION; 99 } finally { 100 parser.close(); 101 } 102 } 103 104 @Override 105 public void onCreate(SQLiteDatabase db) { 106 // Set up the database schema 107 db.execSQL("CREATE TABLE " + CARRIERS_TABLE + 108 "(_id INTEGER PRIMARY KEY," + 109 "name TEXT," + 110 "numeric TEXT," + 111 "mcc TEXT," + 112 "mnc TEXT," + 113 "apn TEXT," + 114 "user TEXT," + 115 "server TEXT," + 116 "password TEXT," + 117 "proxy TEXT," + 118 "port TEXT," + 119 "mmsproxy TEXT," + 120 "mmsport TEXT," + 121 "mmsc TEXT," + 122 "type TEXT," + 123 "current INTEGER);"); 124 125 // Read internal APNS data 126 Resources r = mContext.getResources(); 127 XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns); 128 int publicversion = -1; 129 try { 130 XmlUtils.beginDocument(parser, "apns"); 131 publicversion = Integer.parseInt(parser.getAttributeValue(null, "version")); 132 loadApns(db, parser); 133 } catch (Exception e) { 134 Log.e(TAG, "Got execption while loading APN database.", e); 135 } finally { 136 parser.close(); 137 } 138 139 // Read external APNS data (partner-provided) 140 XmlPullParser confparser = null; 141 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system". 142 File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH); 143 FileReader confreader = null; 144 try { 145 confreader = new FileReader(confFile); 146 confparser = Xml.newPullParser(); 147 confparser.setInput(confreader); 148 XmlUtils.beginDocument(confparser, "apns"); 149 150 // Sanity check. Force internal version and confidential versions to agree 151 int confversion = Integer.parseInt(confparser.getAttributeValue(null, "version")); 152 if (publicversion != confversion) { 153 throw new IllegalStateException("Internal APNS file version doesn't match " 154 + confFile.getAbsolutePath()); 155 } 156 157 loadApns(db, confparser); 158 } catch (FileNotFoundException e) { 159 // It's ok if the file isn't found. It means there isn't a confidential file 160 // Log.e(TAG, "File not found: '" + confFile.getAbsolutePath() + "'"); 161 } catch (Exception e) { 162 Log.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e); 163 } finally { 164 try { if (confreader != null) confreader.close(); } catch (IOException e) { } 165 } 166 } 167 168 @Override 169 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 170 db.execSQL("DROP TABLE IF EXISTS " + CARRIERS_TABLE + ";"); 171 onCreate(db); 172 } 173 174 /** 175 * Gets the next row of apn values. 176 * 177 * @param parser the parser 178 * @return the row or null if it's not an apn 179 */ 180 private ContentValues getRow(XmlPullParser parser) { 181 if (!"apn".equals(parser.getName())) { 182 return null; 183 } 184 185 ContentValues map = new ContentValues(); 186 187 String mcc = parser.getAttributeValue(null, "mcc"); 188 String mnc = parser.getAttributeValue(null, "mnc"); 189 String numeric = mcc + mnc; 190 191 map.put(Telephony.Carriers.NUMERIC,numeric); 192 map.put(Telephony.Carriers.MCC, mcc); 193 map.put(Telephony.Carriers.MNC, mnc); 194 map.put(Telephony.Carriers.NAME, parser.getAttributeValue(null, "carrier")); 195 map.put(Telephony.Carriers.APN, parser.getAttributeValue(null, "apn")); 196 map.put(Telephony.Carriers.USER, parser.getAttributeValue(null, "user")); 197 map.put(Telephony.Carriers.SERVER, parser.getAttributeValue(null, "server")); 198 map.put(Telephony.Carriers.PASSWORD, parser.getAttributeValue(null, "password")); 199 200 // do not add NULL to the map so that insert() will set the default value 201 String proxy = parser.getAttributeValue(null, "proxy"); 202 if (proxy != null) { 203 map.put(Telephony.Carriers.PROXY, proxy); 204 } 205 String port = parser.getAttributeValue(null, "port"); 206 if (port != null) { 207 map.put(Telephony.Carriers.PORT, port); 208 } 209 String mmsproxy = parser.getAttributeValue(null, "mmsproxy"); 210 if (mmsproxy != null) { 211 map.put(Telephony.Carriers.MMSPROXY, mmsproxy); 212 } 213 String mmsport = parser.getAttributeValue(null, "mmsport"); 214 if (mmsport != null) { 215 map.put(Telephony.Carriers.MMSPORT, mmsport); 216 } 217 map.put(Telephony.Carriers.MMSC, parser.getAttributeValue(null, "mmsc")); 218 String type = parser.getAttributeValue(null, "type"); 219 if (type != null) { 220 map.put(Telephony.Carriers.TYPE, type); 221 } 222 223 return map; 224 } 225 226 /* 227 * Loads apns from xml file into the database 228 * 229 * @param db the sqlite database to write to 230 * @param parser the xml parser 231 * 232 */ 233 private void loadApns(SQLiteDatabase db, XmlPullParser parser) { 234 if (parser != null) { 235 try { 236 while (true) { 237 XmlUtils.nextElement(parser); 238 ContentValues row = getRow(parser); 239 if (row != null) { 240 db.insert(CARRIERS_TABLE, null, row); 241 } else { 242 break; // do we really want to skip the rest of the file? 243 } 244 } 245 } catch (XmlPullParserException e) { 246 Log.e(TAG, "Got execption while getting perferred time zone.", e); 247 } catch (IOException e) { 248 Log.e(TAG, "Got execption while getting perferred time zone.", e); 249 } 250 } 251 } 252 } 253 254 @Override 255 public boolean onCreate() { 256 mOpenHelper = new DatabaseHelper(getContext()); 257 return true; 258 } 259 260 @Override 261 public Cursor query(Uri url, String[] projectionIn, String selection, 262 String[] selectionArgs, String sort) { 263 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 264 qb.setTables("carriers"); 265 266 int match = s_urlMatcher.match(url); 267 switch (match) { 268 // do nothing 269 case URL_TELEPHONY: { 270 break; 271 } 272 273 274 case URL_CURRENT: { 275 qb.appendWhere("current IS NOT NULL"); 276 // ignore the selection 277 selection = null; 278 break; 279 } 280 281 case URL_ID: { 282 qb.appendWhere("_id = " + url.getPathSegments().get(1)); 283 break; 284 } 285 286 default: { 287 return null; 288 } 289 } 290 291 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 292 Cursor ret = qb.query(db, projectionIn, selection, selectionArgs, null, null, sort); 293 ret.setNotificationUri(getContext().getContentResolver(), url); 294 return ret; 295 } 296 297 @Override 298 public String getType(Uri url) 299 { 300 switch (s_urlMatcher.match(url)) { 301 case URL_TELEPHONY: 302 return "vnd.android.cursor.dir/telephony-carrier"; 303 304 case URL_ID: 305 return "vnd.android.cursor.item/telephony-carrier"; 306 307 default: 308 throw new IllegalArgumentException("Unknown URL " + url); 309 } 310 } 311 312 @Override 313 public Uri insert(Uri url, ContentValues initialValues) 314 { 315 Uri result = null; 316 317 checkPermission(); 318 319 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 320 int match = s_urlMatcher.match(url); 321 boolean notify = false; 322 switch (match) 323 { 324 case URL_TELEPHONY: 325 { 326 ContentValues values; 327 if (initialValues != null) { 328 values = new ContentValues(initialValues); 329 } else { 330 values = new ContentValues(); 331 } 332 333 // TODO Review this. This code should probably not bet here. 334 // It is valid for the database to return a null string. 335 if (values.containsKey(Telephony.Carriers.NAME) == false) { 336 values.put(Telephony.Carriers.NAME, ""); 337 } 338 if (values.containsKey(Telephony.Carriers.APN) == false) { 339 values.put(Telephony.Carriers.APN, ""); 340 } 341 if (values.containsKey(Telephony.Carriers.PORT) == false) { 342 values.put(Telephony.Carriers.PORT, ""); 343 } 344 if (values.containsKey(Telephony.Carriers.PROXY) == false) { 345 values.put(Telephony.Carriers.PROXY, ""); 346 } 347 if (values.containsKey(Telephony.Carriers.USER) == false) { 348 values.put(Telephony.Carriers.USER, ""); 349 } 350 if (values.containsKey(Telephony.Carriers.SERVER) == false) { 351 values.put(Telephony.Carriers.SERVER, ""); 352 } 353 if (values.containsKey(Telephony.Carriers.PASSWORD) == false) { 354 values.put(Telephony.Carriers.PASSWORD, ""); 355 } 356 if (values.containsKey(Telephony.Carriers.MMSPORT) == false) { 357 values.put(Telephony.Carriers.MMSPORT, ""); 358 } 359 if (values.containsKey(Telephony.Carriers.MMSPROXY) == false) { 360 values.put(Telephony.Carriers.MMSPROXY, ""); 361 } 362 363 long rowID = db.insert(CARRIERS_TABLE, null, values); 364 if (rowID > 0) 365 { 366 result = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, rowID); 367 notify = true; 368 } 369 370 if (Config.LOGD) Log.d(TAG, "inserted " + values.toString() + " rowID = " + rowID); 371 break; 372 } 373 374 case URL_CURRENT: 375 { 376 // null out the previous operator 377 db.update("carriers", s_currentNullMap, "current IS NOT NULL", null); 378 379 String numeric = initialValues.getAsString("numeric"); 380 int updated = db.update("carriers", s_currentSetMap, 381 "numeric = '" + numeric + "'", null); 382 383 if (updated > 0) 384 { 385 if (Config.LOGD) { 386 Log.d(TAG, "Setting numeric '" + numeric + "' to be the current operator"); 387 } 388 } 389 else 390 { 391 Log.e(TAG, "Failed setting numeric '" + numeric + "' to the current operator"); 392 } 393 break; 394 } 395 } 396 397 if (notify) { 398 getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null); 399 } 400 401 return result; 402 } 403 404 @Override 405 public int delete(Uri url, String where, String[] whereArgs) 406 { 407 int count; 408 409 checkPermission(); 410 411 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 412 int match = s_urlMatcher.match(url); 413 switch (match) 414 { 415 case URL_TELEPHONY: 416 { 417 count = db.delete(CARRIERS_TABLE, where, whereArgs); 418 break; 419 } 420 421 case URL_CURRENT: 422 { 423 count = db.delete(CARRIERS_TABLE, where, whereArgs); 424 break; 425 } 426 427 case URL_ID: 428 { 429 count = db.delete(CARRIERS_TABLE, Telephony.Carriers._ID + "=?", 430 new String[] { url.getLastPathSegment() }); 431 break; 432 } 433 434 default: { 435 throw new UnsupportedOperationException("Cannot delete that URL: " + url); 436 } 437 } 438 439 if (count > 0) { 440 getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null); 441 } 442 443 return count; 444 } 445 446 @Override 447 public int update(Uri url, ContentValues values, String where, String[] whereArgs) 448 { 449 int count; 450 451 checkPermission(); 452 453 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 454 int match = s_urlMatcher.match(url); 455 switch (match) 456 { 457 case URL_TELEPHONY: 458 { 459 count = db.update(CARRIERS_TABLE, values, where, whereArgs); 460 break; 461 } 462 463 case URL_CURRENT: 464 { 465 count = db.update(CARRIERS_TABLE, values, where, whereArgs); 466 break; 467 } 468 469 case URL_ID: 470 { 471 if (where != null || whereArgs != null) { 472 throw new UnsupportedOperationException( 473 "Cannot update URL " + url + " with a where clause"); 474 } 475 count = db.update(CARRIERS_TABLE, values, Telephony.Carriers._ID + "=?", 476 new String[] { url.getLastPathSegment() }); 477 break; 478 } 479 480 default: { 481 throw new UnsupportedOperationException("Cannot update that URL: " + url); 482 } 483 } 484 485 if (count > 0) { 486 getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null); 487 } 488 489 return count; 490 } 491 492 private void checkPermission() { 493 // Check the permissions 494 getContext().enforceCallingOrSelfPermission("android.permission.WRITE_APN_SETTINGS", 495 "No permission to write APN settings"); 496 } 497 498 private SQLiteOpenHelper mOpenHelper; 499} 500