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