• 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.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