1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.providers.telephony; 18 19import android.content.ContentProvider; 20import android.content.ContentUris; 21import android.content.ContentValues; 22import android.content.Context; 23import android.content.SharedPreferences; 24import android.content.UriMatcher; 25import android.content.pm.PackageManager; 26import android.database.Cursor; 27import android.database.MatrixCursor; 28import android.database.sqlite.SQLiteDatabase; 29import android.database.sqlite.SQLiteOpenHelper; 30import android.database.sqlite.SQLiteQueryBuilder; 31import android.net.Uri; 32import android.os.Environment; 33import android.provider.Telephony.CarrierId; 34import android.telephony.SubscriptionManager; 35import android.text.TextUtils; 36import android.util.Log; 37import android.util.Pair; 38 39import com.android.internal.annotations.VisibleForTesting; 40import com.android.internal.telephony.SubscriptionController; 41import com.android.internal.telephony.nano.CarrierIdProto; 42 43import java.io.ByteArrayOutputStream; 44import java.io.File; 45import java.io.FileInputStream; 46import java.io.IOException; 47import java.io.InputStream; 48import java.util.ArrayList; 49import java.util.Arrays; 50import java.util.List; 51import java.util.Map; 52import java.util.concurrent.ConcurrentHashMap; 53 54import libcore.io.IoUtils; 55 56/** 57 * This class provides the ability to query the Carrier Identification databases 58 * (A.K.A. cid) which is stored in a SQLite database. 59 * 60 * Each row in carrier identification db consists of matching rule (e.g., MCCMNC, GID1, GID2, PLMN) 61 * and its matched carrier id & carrier name. Each carrier either MNO or MVNO could be 62 * identified by multiple matching rules but is assigned with a unique ID (cid). 63 * 64 * 65 * This class provides the ability to retrieve the cid of the current subscription. 66 * This is done atomically through a query. 67 * 68 * This class also provides a way to update carrier identifying attributes of an existing entry. 69 * Insert entries for new carriers or an existing carrier. 70 */ 71public class CarrierIdProvider extends ContentProvider { 72 73 private static final boolean VDBG = false; // STOPSHIP if true 74 private static final String TAG = CarrierIdProvider.class.getSimpleName(); 75 76 private static final String DATABASE_NAME = "carrierIdentification.db"; 77 private static final int DATABASE_VERSION = 3; 78 79 private static final String ASSETS_PB_FILE = "carrier_list.pb"; 80 private static final String VERSION_KEY = "version"; 81 private static final String OTA_UPDATED_PB_PATH = "misc/carrierid/" + ASSETS_PB_FILE; 82 private static final String PREF_FILE = CarrierIdProvider.class.getSimpleName(); 83 84 private static final UriMatcher s_urlMatcher = new UriMatcher(UriMatcher.NO_MATCH); 85 86 private static final int URL_ALL = 1; 87 private static final int URL_ALL_UPDATE_FROM_PB = 2; 88 private static final int URL_ALL_GET_VERSION = 3; 89 90 /** 91 * index 0: {@link CarrierId.All#MCCMNC} 92 */ 93 private static final int MCCMNC_INDEX = 0; 94 /** 95 * index 1: {@link CarrierId.All#IMSI_PREFIX_XPATTERN} 96 */ 97 private static final int IMSI_PREFIX_INDEX = 1; 98 /** 99 * index 2: {@link CarrierId.All#GID1} 100 */ 101 private static final int GID1_INDEX = 2; 102 /** 103 * index 3: {@link CarrierId.All#GID2} 104 */ 105 private static final int GID2_INDEX = 3; 106 /** 107 * index 4: {@link CarrierId.All#PLMN} 108 */ 109 private static final int PLMN_INDEX = 4; 110 /** 111 * index 5: {@link CarrierId.All#SPN} 112 */ 113 private static final int SPN_INDEX = 5; 114 /** 115 * index 6: {@link CarrierId.All#APN} 116 */ 117 private static final int APN_INDEX = 6; 118 /** 119 * index 7: {@link CarrierId.All#ICCID_PREFIX} 120 */ 121 private static final int ICCID_PREFIX_INDEX = 7; 122 /** 123 * ending index of carrier attribute list. 124 */ 125 private static final int CARRIER_ATTR_END_IDX = ICCID_PREFIX_INDEX; 126 /** 127 * The authority string for the CarrierIdProvider 128 */ 129 @VisibleForTesting 130 public static final String AUTHORITY = "carrier_id"; 131 132 public static final String CARRIER_ID_TABLE = "carrier_id"; 133 134 private static final List<String> CARRIERS_ID_UNIQUE_FIELDS = new ArrayList<>(Arrays.asList( 135 CarrierId.All.MCCMNC, 136 CarrierId.All.GID1, 137 CarrierId.All.GID2, 138 CarrierId.All.PLMN, 139 CarrierId.All.IMSI_PREFIX_XPATTERN, 140 CarrierId.All.SPN, 141 CarrierId.All.APN, 142 CarrierId.All.ICCID_PREFIX)); 143 144 private CarrierIdDatabaseHelper mDbHelper; 145 146 /** 147 * Stores carrier id information for the current active subscriptions. 148 * Key is the active subId and entryValue is a pair of carrier id(int) and Carrier Name(String). 149 */ 150 private final Map<Integer, Pair<Integer, String>> mCurrentSubscriptionMap = 151 new ConcurrentHashMap<>(); 152 153 @VisibleForTesting 154 public static String getStringForCarrierIdTableCreation(String tableName) { 155 return "CREATE TABLE " + tableName 156 + "(_id INTEGER PRIMARY KEY," 157 + CarrierId.All.MCCMNC + " TEXT NOT NULL," 158 + CarrierId.All.GID1 + " TEXT," 159 + CarrierId.All.GID2 + " TEXT," 160 + CarrierId.All.PLMN + " TEXT," 161 + CarrierId.All.IMSI_PREFIX_XPATTERN + " TEXT," 162 + CarrierId.All.SPN + " TEXT," 163 + CarrierId.All.APN + " TEXT," 164 + CarrierId.All.ICCID_PREFIX + " TEXT," 165 + CarrierId.CARRIER_NAME + " TEXT," 166 + CarrierId.CARRIER_ID + " INTEGER DEFAULT -1," 167 + "UNIQUE (" + TextUtils.join(", ", CARRIERS_ID_UNIQUE_FIELDS) + "));"; 168 } 169 170 @VisibleForTesting 171 public static String getStringForIndexCreation(String tableName) { 172 return "CREATE INDEX IF NOT EXISTS mccmncIndex ON " + tableName + " (" 173 + CarrierId.All.MCCMNC + ");"; 174 } 175 176 @Override 177 public boolean onCreate() { 178 Log.d(TAG, "onCreate"); 179 mDbHelper = new CarrierIdDatabaseHelper(getContext()); 180 mDbHelper.getReadableDatabase(); 181 s_urlMatcher.addURI(AUTHORITY, "all", URL_ALL); 182 s_urlMatcher.addURI(AUTHORITY, "all/update_db", URL_ALL_UPDATE_FROM_PB); 183 s_urlMatcher.addURI(AUTHORITY, "all/get_version", URL_ALL_GET_VERSION); 184 updateDatabaseFromPb(mDbHelper.getWritableDatabase()); 185 return true; 186 } 187 188 @Override 189 public String getType(Uri uri) { 190 Log.d(TAG, "getType"); 191 return null; 192 } 193 194 @Override 195 public Cursor query(Uri uri, String[] projectionIn, String selection, 196 String[] selectionArgs, String sortOrder) { 197 if (VDBG) { 198 Log.d(TAG, "query:" 199 + " uri=" + uri 200 + " values=" + Arrays.toString(projectionIn) 201 + " selection=" + selection 202 + " selectionArgs=" + Arrays.toString(selectionArgs)); 203 } 204 205 final int match = s_urlMatcher.match(uri); 206 switch (match) { 207 case URL_ALL_GET_VERSION: 208 checkReadPermission(); 209 final MatrixCursor cursor = new MatrixCursor(new String[] {VERSION_KEY}); 210 cursor.addRow(new Object[] {getAppliedVersion()}); 211 return cursor; 212 case URL_ALL: 213 checkReadPermission(); 214 SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 215 qb.setTables(CARRIER_ID_TABLE); 216 217 SQLiteDatabase db = getReadableDatabase(); 218 return qb.query(db, projectionIn, selection, selectionArgs, null, null, sortOrder); 219 default: 220 return queryCarrierIdForCurrentSubscription(uri, projectionIn); 221 } 222 } 223 224 @Override 225 public Uri insert(Uri uri, ContentValues values) { 226 checkWritePermission(); 227 final int match = s_urlMatcher.match(uri); 228 switch (match) { 229 case URL_ALL: 230 final long row = getWritableDatabase().insertOrThrow(CARRIER_ID_TABLE, null, 231 values); 232 if (row > 0) { 233 final Uri newUri = ContentUris.withAppendedId( 234 CarrierId.All.CONTENT_URI, row); 235 getContext().getContentResolver().notifyChange( 236 CarrierId.All.CONTENT_URI, null); 237 return newUri; 238 } 239 return null; 240 default: 241 throw new IllegalArgumentException("Cannot insert that URL: " + uri); 242 } 243 } 244 245 @Override 246 public int delete(Uri uri, String selection, String[] selectionArgs) { 247 checkWritePermission(); 248 if (VDBG) { 249 Log.d(TAG, "delete:" 250 + " uri=" + uri 251 + " selection={" + selection + "}" 252 + " selection=" + selection 253 + " selectionArgs=" + Arrays.toString(selectionArgs)); 254 } 255 final int match = s_urlMatcher.match(uri); 256 switch (match) { 257 case URL_ALL: 258 final int count = getWritableDatabase().delete(CARRIER_ID_TABLE, selection, 259 selectionArgs); 260 Log.d(TAG, " delete.count=" + count); 261 if (count > 0) { 262 getContext().getContentResolver().notifyChange( 263 CarrierId.All.CONTENT_URI, null); 264 } 265 return count; 266 default: 267 throw new IllegalArgumentException("Cannot delete that URL: " + uri); 268 } 269 } 270 271 @Override 272 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 273 checkWritePermission(); 274 if (VDBG) { 275 Log.d(TAG, "update:" 276 + " uri=" + uri 277 + " values={" + values + "}" 278 + " selection=" + selection 279 + " selectionArgs=" + Arrays.toString(selectionArgs)); 280 } 281 282 final int match = s_urlMatcher.match(uri); 283 switch (match) { 284 case URL_ALL_UPDATE_FROM_PB: 285 return updateDatabaseFromPb(getWritableDatabase()); 286 case URL_ALL: 287 final int count = getWritableDatabase().update(CARRIER_ID_TABLE, values, selection, 288 selectionArgs); 289 Log.d(TAG, " update.count=" + count); 290 if (count > 0) { 291 getContext().getContentResolver().notifyChange(CarrierId.All.CONTENT_URI, null); 292 } 293 return count; 294 default: 295 return updateCarrierIdForCurrentSubscription(uri, values); 296 297 } 298 } 299 300 /** 301 * These methods can be overridden in a subclass for testing CarrierIdProvider using an 302 * in-memory database. 303 */ 304 SQLiteDatabase getReadableDatabase() { 305 return mDbHelper.getReadableDatabase(); 306 } 307 SQLiteDatabase getWritableDatabase() { 308 return mDbHelper.getWritableDatabase(); 309 } 310 311 private class CarrierIdDatabaseHelper extends SQLiteOpenHelper { 312 private final String TAG = CarrierIdDatabaseHelper.class.getSimpleName(); 313 314 /** 315 * CarrierIdDatabaseHelper carrier identification database helper class. 316 * @param context of the user. 317 */ 318 public CarrierIdDatabaseHelper(Context context) { 319 super(context, DATABASE_NAME, null, DATABASE_VERSION); 320 } 321 322 @Override 323 public void onCreate(SQLiteDatabase db) { 324 Log.d(TAG, "onCreate"); 325 db.execSQL(getStringForCarrierIdTableCreation(CARRIER_ID_TABLE)); 326 db.execSQL(getStringForIndexCreation(CARRIER_ID_TABLE)); 327 } 328 329 public void createCarrierTable(SQLiteDatabase db) { 330 db.execSQL(getStringForCarrierIdTableCreation(CARRIER_ID_TABLE)); 331 db.execSQL(getStringForIndexCreation(CARRIER_ID_TABLE)); 332 } 333 334 public void dropCarrierTable(SQLiteDatabase db) { 335 db.execSQL("DROP TABLE IF EXISTS " + CARRIER_ID_TABLE + ";"); 336 } 337 338 @Override 339 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 340 Log.d(TAG, "dbh.onUpgrade:+ db=" + db + " oldV=" + oldVersion + " newV=" + newVersion); 341 if (oldVersion < DATABASE_VERSION) { 342 dropCarrierTable(db); 343 createCarrierTable(db); 344 } 345 } 346 } 347 348 /** 349 * Parse and persist pb file as database default values. 350 * Use version number to detect file update. 351 * Update database with data from assets or ota only if version jumps. 352 */ 353 private int updateDatabaseFromPb(SQLiteDatabase db) { 354 Log.d(TAG, "update database from pb file"); 355 int rows = 0; 356 CarrierIdProto.CarrierList carrierList = getUpdateCarrierList(); 357 // No update is needed 358 if (carrierList == null) return rows; 359 360 ContentValues cv; 361 List<ContentValues> cvs; 362 try { 363 // Batch all insertions in a single transaction to improve efficiency. 364 db.beginTransaction(); 365 db.delete(CARRIER_ID_TABLE, null, null); 366 for (CarrierIdProto.CarrierId id : carrierList.carrierId) { 367 for (CarrierIdProto.CarrierAttribute attr : id.carrierAttribute) { 368 cv = new ContentValues(); 369 cv.put(CarrierId.CARRIER_ID, id.canonicalId); 370 cv.put(CarrierId.CARRIER_NAME, id.carrierName); 371 cvs = new ArrayList<>(); 372 convertCarrierAttrToContentValues(cv, cvs, attr, 0); 373 for (ContentValues contentVal : cvs) { 374 // When a constraint violation occurs, the row that contains the violation 375 // is not inserted. But the command continues executing normally. 376 if (db.insertWithOnConflict(CARRIER_ID_TABLE, null, contentVal, 377 SQLiteDatabase.CONFLICT_IGNORE) > 0) { 378 rows++; 379 } else { 380 Log.e(TAG, "updateDatabaseFromPB insertion failure, row: " 381 + rows + "carrier id: " + id.canonicalId); 382 // TODO metrics 383 } 384 } 385 } 386 } 387 Log.d(TAG, "update database from pb. inserted rows = " + rows); 388 if (rows > 0) { 389 // Notify listener of DB change 390 getContext().getContentResolver().notifyChange(CarrierId.All.CONTENT_URI, null); 391 } 392 setAppliedVersion(carrierList.version); 393 db.setTransactionSuccessful(); 394 } finally { 395 db.endTransaction(); 396 } 397 return rows; 398 } 399 400 /** 401 * Recursively loop through carrier attribute list to get all combinations. 402 */ 403 private void convertCarrierAttrToContentValues(ContentValues cv, List<ContentValues> cvs, 404 CarrierIdProto.CarrierAttribute attr, int index) { 405 if (index > CARRIER_ATTR_END_IDX) { 406 cvs.add(new ContentValues(cv)); 407 return; 408 } 409 boolean found = false; 410 switch (index) { 411 case MCCMNC_INDEX: 412 for (String str : attr.mccmncTuple) { 413 cv.put(CarrierId.All.MCCMNC, str); 414 convertCarrierAttrToContentValues(cv, cvs, attr, index + 1); 415 cv.remove(CarrierId.All.MCCMNC); 416 found = true; 417 } 418 break; 419 case IMSI_PREFIX_INDEX: 420 for (String str : attr.imsiPrefixXpattern) { 421 cv.put(CarrierId.All.IMSI_PREFIX_XPATTERN, str); 422 convertCarrierAttrToContentValues(cv, cvs, attr, index + 1); 423 cv.remove(CarrierId.All.IMSI_PREFIX_XPATTERN); 424 found = true; 425 } 426 break; 427 case GID1_INDEX: 428 for (String str : attr.gid1) { 429 cv.put(CarrierId.All.GID1, str); 430 convertCarrierAttrToContentValues(cv, cvs, attr, index + 1); 431 cv.remove(CarrierId.All.GID1); 432 found = true; 433 } 434 break; 435 case GID2_INDEX: 436 for (String str : attr.gid2) { 437 cv.put(CarrierId.All.GID2, str); 438 convertCarrierAttrToContentValues(cv, cvs, attr, index + 1); 439 cv.remove(CarrierId.All.GID2); 440 found = true; 441 } 442 break; 443 case PLMN_INDEX: 444 for (String str : attr.plmn) { 445 cv.put(CarrierId.All.PLMN, str); 446 convertCarrierAttrToContentValues(cv, cvs, attr, index + 1); 447 cv.remove(CarrierId.All.PLMN); 448 found = true; 449 } 450 break; 451 case SPN_INDEX: 452 for (String str : attr.spn) { 453 cv.put(CarrierId.All.SPN, str); 454 convertCarrierAttrToContentValues(cv, cvs, attr, index + 1); 455 cv.remove(CarrierId.All.SPN); 456 found = true; 457 } 458 break; 459 case APN_INDEX: 460 for (String str : attr.preferredApn) { 461 cv.put(CarrierId.All.APN, str); 462 convertCarrierAttrToContentValues(cv, cvs, attr, index + 1); 463 cv.remove(CarrierId.All.APN); 464 found = true; 465 } 466 break; 467 case ICCID_PREFIX_INDEX: 468 for (String str : attr.iccidPrefix) { 469 cv.put(CarrierId.All.ICCID_PREFIX, str); 470 convertCarrierAttrToContentValues(cv, cvs, attr, index + 1); 471 cv.remove(CarrierId.All.ICCID_PREFIX); 472 found = true; 473 } 474 break; 475 default: 476 Log.e(TAG, "unsupported index: " + index); 477 break; 478 } 479 // if attribute at index is empty, move forward to the next attribute 480 if (!found) { 481 convertCarrierAttrToContentValues(cv, cvs, attr, index + 1); 482 } 483 } 484 485 /** 486 * Return the update carrierList. 487 * Get the latest version from the last applied, assets and ota file. if the latest version 488 * is newer than the last applied, update is required. Otherwise no update is required and 489 * the returned carrierList will be null. 490 */ 491 private CarrierIdProto.CarrierList getUpdateCarrierList() { 492 int version = getAppliedVersion(); 493 CarrierIdProto.CarrierList carrierList = null; 494 CarrierIdProto.CarrierList assets = null; 495 CarrierIdProto.CarrierList ota = null; 496 InputStream is = null; 497 498 try { 499 is = getContext().getAssets().open(ASSETS_PB_FILE); 500 assets = CarrierIdProto.CarrierList.parseFrom(readInputStreamToByteArray(is)); 501 } catch (IOException ex) { 502 Log.e(TAG, "read carrier list from assets pb failure: " + ex); 503 } finally { 504 IoUtils.closeQuietly(is); 505 } 506 try { 507 is = new FileInputStream(new File(Environment.getDataDirectory(), OTA_UPDATED_PB_PATH)); 508 ota = CarrierIdProto.CarrierList.parseFrom(readInputStreamToByteArray(is)); 509 } catch (IOException ex) { 510 Log.e(TAG, "read carrier list from ota pb failure: " + ex); 511 } finally { 512 IoUtils.closeQuietly(is); 513 } 514 515 // compare version 516 if (assets != null && assets.version > version) { 517 carrierList = assets; 518 version = assets.version; 519 } 520 if (ota != null && ota.version > version) { 521 carrierList = ota; 522 version = ota.version; 523 } 524 Log.d(TAG, "latest version: " + version + " need update: " + (carrierList != null)); 525 return carrierList; 526 } 527 528 private int getAppliedVersion() { 529 final SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, 530 Context.MODE_PRIVATE); 531 return sp.getInt(VERSION_KEY, -1); 532 } 533 534 private void setAppliedVersion(int version) { 535 final SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, 536 Context.MODE_PRIVATE); 537 SharedPreferences.Editor editor = sp.edit(); 538 editor.putInt(VERSION_KEY, version); 539 editor.apply(); 540 } 541 542 /** 543 * Util function to convert inputStream to byte array before parsing proto data. 544 */ 545 private static byte[] readInputStreamToByteArray(InputStream inputStream) throws IOException { 546 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 547 int nRead; 548 int size = 16 * 1024; // Read 16k chunks 549 byte[] data = new byte[size]; 550 while ((nRead = inputStream.read(data, 0, data.length)) != -1) { 551 buffer.write(data, 0, nRead); 552 } 553 buffer.flush(); 554 return buffer.toByteArray(); 555 } 556 557 private int updateCarrierIdForCurrentSubscription(Uri uri, ContentValues cv) { 558 // Parse the subId 559 int subId; 560 try { 561 subId = Integer.parseInt(uri.getLastPathSegment()); 562 } catch (NumberFormatException e) { 563 throw new IllegalArgumentException("invalid subid in provided uri " + uri); 564 } 565 Log.d(TAG, "updateCarrierIdForSubId: " + subId); 566 567 // Handle DEFAULT_SUBSCRIPTION_ID 568 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 569 subId = SubscriptionController.getInstance().getDefaultSubId(); 570 } 571 572 if (!SubscriptionController.getInstance().isActiveSubId(subId)) { 573 // Remove absent subId from the currentSubscriptionMap. 574 final List activeSubscriptions = Arrays.asList(SubscriptionController.getInstance() 575 .getActiveSubIdList()); 576 int count = 0; 577 for (int subscription : mCurrentSubscriptionMap.keySet()) { 578 if (!activeSubscriptions.contains(subscription)) { 579 count++; 580 Log.d(TAG, "updateCarrierIdForSubId: " + subscription); 581 mCurrentSubscriptionMap.remove(subscription); 582 getContext().getContentResolver().notifyChange(CarrierId.CONTENT_URI, null); 583 } 584 } 585 return count; 586 } else { 587 mCurrentSubscriptionMap.put(subId, 588 new Pair(cv.getAsInteger(CarrierId.CARRIER_ID), 589 cv.getAsString(CarrierId.CARRIER_NAME))); 590 getContext().getContentResolver().notifyChange(CarrierId.CONTENT_URI, null); 591 return 1; 592 } 593 } 594 595 private Cursor queryCarrierIdForCurrentSubscription(Uri uri, String[] projectionIn) { 596 // Parse the subId, using the default subId if subId is not provided 597 int subId = SubscriptionController.getInstance().getDefaultSubId(); 598 if (!TextUtils.isEmpty(uri.getLastPathSegment())) { 599 try { 600 subId = Integer.parseInt(uri.getLastPathSegment()); 601 } catch (NumberFormatException e) { 602 throw new IllegalArgumentException("invalid subid in provided uri" + uri); 603 } 604 } 605 Log.d(TAG, "queryCarrierIdForSubId: " + subId); 606 607 // Handle DEFAULT_SUBSCRIPTION_ID 608 if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { 609 subId = SubscriptionController.getInstance().getDefaultSubId(); 610 } 611 612 if (!mCurrentSubscriptionMap.containsKey(subId)) { 613 // Return an empty cursor if subId is not belonging to current subscriptions. 614 return new MatrixCursor(projectionIn, 0); 615 } 616 final MatrixCursor c = new MatrixCursor(projectionIn, 1); 617 final MatrixCursor.RowBuilder row = c.newRow(); 618 for (int i = 0; i < c.getColumnCount(); i++) { 619 final String columnName = c.getColumnName(i); 620 if (CarrierId.CARRIER_ID.equals(columnName)) { 621 row.add(mCurrentSubscriptionMap.get(subId).first); 622 } else if (CarrierId.CARRIER_NAME.equals(columnName)) { 623 row.add(mCurrentSubscriptionMap.get(subId).second); 624 } else { 625 throw new IllegalArgumentException("Invalid column " + projectionIn[i]); 626 } 627 } 628 return c; 629 } 630 631 private void checkReadPermission() { 632 int status = getContext().checkCallingOrSelfPermission( 633 "android.permission.READ_PRIVILEGED_PHONE_STATE"); 634 if (status == PackageManager.PERMISSION_GRANTED) { 635 return; 636 } 637 throw new SecurityException("No permission to read CarrierId provider"); 638 } 639 640 private void checkWritePermission() { 641 int status = getContext().checkCallingOrSelfPermission( 642 "android.permission.MODIFY_PHONE_STATE"); 643 if (status == PackageManager.PERMISSION_GRANTED) { 644 return; 645 } 646 throw new SecurityException("No permission to write CarrierId provider"); 647 } 648} 649