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