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