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