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