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