Home | History | Annotate | Download | only in telephony
      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.internal.telephony;
     18 
     19 import static android.preference.PreferenceManager.getDefaultSharedPreferences;
     20 
     21 import static java.nio.charset.StandardCharsets.UTF_8;
     22 
     23 import android.app.AlarmManager;
     24 import android.app.DownloadManager;
     25 import android.app.PendingIntent;
     26 import android.content.BroadcastReceiver;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.content.IntentFilter;
     30 import android.content.SharedPreferences;
     31 import android.database.Cursor;
     32 import android.net.Uri;
     33 import android.os.PersistableBundle;
     34 import android.telephony.CarrierConfigManager;
     35 import android.telephony.ImsiEncryptionInfo;
     36 import android.telephony.SubscriptionManager;
     37 import android.telephony.TelephonyManager;
     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.org.bouncycastle.util.io.pem.PemReader;
     44 
     45 import org.json.JSONArray;
     46 import org.json.JSONException;
     47 import org.json.JSONObject;
     48 
     49 import java.io.BufferedReader;
     50 import java.io.ByteArrayInputStream;
     51 import java.io.FileInputStream;
     52 import java.io.IOException;
     53 import java.io.InputStream;
     54 import java.io.InputStreamReader;
     55 import java.io.Reader;
     56 import java.security.PublicKey;
     57 import java.security.cert.CertificateFactory;
     58 import java.security.cert.X509Certificate;
     59 import java.util.Date;
     60 import java.util.Random;
     61 import java.util.zip.GZIPInputStream;
     62 
     63 /**
     64  * This class contains logic to get Certificates and keep them current.
     65  * The class will be instantiated by various Phone implementations.
     66  */
     67 public class CarrierKeyDownloadManager {
     68     private static final String LOG_TAG = "CarrierKeyDownloadManager";
     69 
     70     private static final String MCC_MNC_PREF_TAG = "CARRIER_KEY_DM_MCC_MNC";
     71 
     72     private static final int DAY_IN_MILLIS = 24 * 3600 * 1000;
     73 
     74     // Create a window prior to the key expiration, during which the cert will be
     75     // downloaded. Defines the start date of that window. So if the key expires on
     76     // Dec  21st, the start of the renewal window will be Dec 1st.
     77     private static final int START_RENEWAL_WINDOW_DAYS = 21;
     78 
     79     // This will define the end date of the window.
     80     private static final int END_RENEWAL_WINDOW_DAYS = 7;
     81 
     82 
     83 
     84     /* Intent for downloading the public key */
     85     private static final String INTENT_KEY_RENEWAL_ALARM_PREFIX =
     86             "com.android.internal.telephony.carrier_key_download_alarm";
     87 
     88     @VisibleForTesting
     89     public int mKeyAvailability = 0;
     90 
     91     public static final String MNC = "MNC";
     92     public static final String MCC = "MCC";
     93     private static final String SEPARATOR = ":";
     94 
     95     private static final String JSON_CERTIFICATE = "certificate";
     96     // This is a hack to accommodate certain Carriers who insists on using the public-key
     97     // field to store the certificate. We'll just use which-ever is not null.
     98     private static final String JSON_CERTIFICATE_ALTERNATE = "public-key";
     99     private static final String JSON_TYPE = "key-type";
    100     private static final String JSON_IDENTIFIER = "key-identifier";
    101     private static final String JSON_CARRIER_KEYS = "carrier-keys";
    102     private static final String JSON_TYPE_VALUE_WLAN = "WLAN";
    103     private static final String JSON_TYPE_VALUE_EPDG = "EPDG";
    104 
    105 
    106     private static final int[] CARRIER_KEY_TYPES = {TelephonyManager.KEY_TYPE_EPDG,
    107             TelephonyManager.KEY_TYPE_WLAN};
    108     private static final int UNINITIALIZED_KEY_TYPE = -1;
    109 
    110     private final Phone mPhone;
    111     private final Context mContext;
    112     public final DownloadManager mDownloadManager;
    113     private String mURL;
    114 
    115     public CarrierKeyDownloadManager(Phone phone) {
    116         mPhone = phone;
    117         mContext = phone.getContext();
    118         IntentFilter filter = new IntentFilter();
    119         filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
    120         filter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
    121         filter.addAction(INTENT_KEY_RENEWAL_ALARM_PREFIX + mPhone.getPhoneId());
    122         filter.addAction(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
    123         mContext.registerReceiver(mBroadcastReceiver, filter, null, phone);
    124         mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
    125     }
    126 
    127     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    128         @Override
    129         public void onReceive(Context context, Intent intent) {
    130             String action = intent.getAction();
    131             int slotId = mPhone.getPhoneId();
    132             if (action.equals(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId)) {
    133                 Log.d(LOG_TAG, "Handling key renewal alarm: " + action);
    134                 handleAlarmOrConfigChange();
    135             } else if (action.equals(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD)) {
    136                 if (slotId == intent.getIntExtra(PhoneConstants.PHONE_KEY,
    137                         SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
    138                     Log.d(LOG_TAG, "Handling reset intent: " + action);
    139                     handleAlarmOrConfigChange();
    140                 }
    141             } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
    142                 if (slotId == intent.getIntExtra(PhoneConstants.PHONE_KEY,
    143                         SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
    144                     Log.d(LOG_TAG, "Carrier Config changed: " + action);
    145                     handleAlarmOrConfigChange();
    146                 }
    147             } else if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
    148                 Log.d(LOG_TAG, "Download Complete");
    149                 long carrierKeyDownloadIdentifier =
    150                         intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
    151                 String mccMnc = getMccMncSetFromPref();
    152                 if (isValidDownload(mccMnc)) {
    153                     onDownloadComplete(carrierKeyDownloadIdentifier, mccMnc);
    154                     onPostDownloadProcessing(carrierKeyDownloadIdentifier);
    155                 }
    156             }
    157         }
    158     };
    159 
    160     private void onPostDownloadProcessing(long carrierKeyDownloadIdentifier) {
    161         resetRenewalAlarm();
    162         cleanupDownloadPreferences(carrierKeyDownloadIdentifier);
    163     }
    164 
    165     private void handleAlarmOrConfigChange() {
    166         if (carrierUsesKeys()) {
    167             if (areCarrierKeysAbsentOrExpiring()) {
    168                 boolean downloadStartedSuccessfully = downloadKey();
    169                 // if the download was attemped, but not started successfully, and if carriers uses
    170                 // keys, we'll still want to renew the alarms, and try downloading the key a day
    171                 // later.
    172                 if (!downloadStartedSuccessfully) {
    173                     resetRenewalAlarm();
    174                 }
    175             } else {
    176                 return;
    177             }
    178         } else {
    179             // delete any existing alarms.
    180             cleanupRenewalAlarms();
    181         }
    182     }
    183 
    184     private void cleanupDownloadPreferences(long carrierKeyDownloadIdentifier) {
    185         Log.d(LOG_TAG, "Cleaning up download preferences: " + carrierKeyDownloadIdentifier);
    186         SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit();
    187         editor.remove(String.valueOf(carrierKeyDownloadIdentifier));
    188         editor.commit();
    189     }
    190 
    191     private void cleanupRenewalAlarms() {
    192         Log.d(LOG_TAG, "Cleaning up existing renewal alarms");
    193         int slotId = mPhone.getPhoneId();
    194         Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId);
    195         PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent,
    196                 PendingIntent.FLAG_UPDATE_CURRENT);
    197         AlarmManager alarmManager =
    198                 (AlarmManager) mContext.getSystemService(mContext.ALARM_SERVICE);
    199         alarmManager.cancel(carrierKeyDownloadIntent);
    200     }
    201 
    202     /**
    203      * this method returns the date to be used to decide on when to start downloading the key.
    204      * from the carrier.
    205      **/
    206     @VisibleForTesting
    207     public long getExpirationDate()  {
    208         long minExpirationDate = Long.MAX_VALUE;
    209         for (int key_type : CARRIER_KEY_TYPES) {
    210             if (!isKeyEnabled(key_type)) {
    211                 continue;
    212             }
    213             ImsiEncryptionInfo imsiEncryptionInfo =
    214                     mPhone.getCarrierInfoForImsiEncryption(key_type);
    215             if (imsiEncryptionInfo != null && imsiEncryptionInfo.getExpirationTime() != null) {
    216                 if (minExpirationDate > imsiEncryptionInfo.getExpirationTime().getTime()) {
    217                     minExpirationDate = imsiEncryptionInfo.getExpirationTime().getTime();
    218                 }
    219             }
    220         }
    221 
    222         // if there are no keys, or expiration date is in the past, or within 7 days, then we
    223         // set the alarm to run in a day. Else, we'll set the alarm to run 7 days prior to
    224         // expiration.
    225         if (minExpirationDate == Long.MAX_VALUE || (minExpirationDate
    226                 < System.currentTimeMillis() + END_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS)) {
    227             minExpirationDate = System.currentTimeMillis() + DAY_IN_MILLIS;
    228         } else {
    229             // We don't want all the phones to download the certs simultaneously, so
    230             // we pick a random time during the download window to avoid this situation.
    231             Random random = new Random();
    232             int max = START_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS;
    233             int min = END_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS;
    234             int randomTime = random.nextInt(max - min) + min;
    235             minExpirationDate = minExpirationDate - randomTime;
    236         }
    237         return minExpirationDate;
    238     }
    239 
    240     /**
    241      * this method resets the alarm. Starts by cleaning up the existing alarms.
    242      * We look at the earliest expiration date, and setup an alarms X days prior.
    243      * If the expiration date is in the past, we'll setup an alarm to run the next day. This
    244      * could happen if the download has failed.
    245      **/
    246     @VisibleForTesting
    247     public void resetRenewalAlarm() {
    248         cleanupRenewalAlarms();
    249         int slotId = mPhone.getPhoneId();
    250         long minExpirationDate = getExpirationDate();
    251         Log.d(LOG_TAG, "minExpirationDate: " + new Date(minExpirationDate));
    252         final AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
    253                 Context.ALARM_SERVICE);
    254         Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId);
    255         PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent,
    256                 PendingIntent.FLAG_UPDATE_CURRENT);
    257         alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, minExpirationDate,
    258                 carrierKeyDownloadIntent);
    259         Log.d(LOG_TAG, "setRenewelAlarm: action=" + intent.getAction() + " time="
    260                 + new Date(minExpirationDate));
    261     }
    262 
    263     private String getMccMncSetFromPref() {
    264         // check if this is a download that we had created. We do this by checking if the
    265         // downloadId is stored in the shared prefs.
    266         int slotId = mPhone.getPhoneId();
    267         SharedPreferences preferences = getDefaultSharedPreferences(mContext);
    268         return preferences.getString(MCC_MNC_PREF_TAG + slotId, null);
    269     }
    270 
    271     /**
    272      * Returns the sim operator.
    273      **/
    274     @VisibleForTesting
    275     public String getSimOperator() {
    276         final TelephonyManager telephonyManager =
    277                 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
    278         return telephonyManager.getSimOperator(mPhone.getSubId());
    279     }
    280 
    281     /**
    282      *  checks if the download was sent by this particular instance. We do this by including the
    283      *  slot id in the key. If no value is found, we know that the download was not for this
    284      *  instance of the phone.
    285      **/
    286     @VisibleForTesting
    287     public boolean isValidDownload(String mccMnc) {
    288         String mccCurrent = "";
    289         String mncCurrent = "";
    290         String mccSource = "";
    291         String mncSource = "";
    292 
    293         String simOperator = getSimOperator();
    294         if (TextUtils.isEmpty(simOperator) || TextUtils.isEmpty(mccMnc)) {
    295             Log.e(LOG_TAG, "simOperator or mcc/mnc is empty");
    296             return false;
    297         }
    298 
    299         String[] splitValue = mccMnc.split(SEPARATOR);
    300         mccSource = splitValue[0];
    301         mncSource = splitValue[1];
    302         Log.d(LOG_TAG, "values from sharedPrefs mcc, mnc: " + mccSource + "," + mncSource);
    303 
    304         mccCurrent = simOperator.substring(0, 3);
    305         mncCurrent = simOperator.substring(3);
    306         Log.d(LOG_TAG, "using values for mcc, mnc: " + mccCurrent + "," + mncCurrent);
    307 
    308         if (TextUtils.equals(mncSource, mncCurrent) &&  TextUtils.equals(mccSource, mccCurrent)) {
    309             return true;
    310         }
    311         return false;
    312     }
    313 
    314     /**
    315      * This method will try to parse the downloaded information, and persist it in the database.
    316      **/
    317     private void onDownloadComplete(long carrierKeyDownloadIdentifier, String mccMnc) {
    318         Log.d(LOG_TAG, "onDownloadComplete: " + carrierKeyDownloadIdentifier);
    319         String jsonStr;
    320         DownloadManager.Query query = new DownloadManager.Query();
    321         query.setFilterById(carrierKeyDownloadIdentifier);
    322         Cursor cursor = mDownloadManager.query(query);
    323         InputStream source = null;
    324 
    325         if (cursor == null) {
    326             return;
    327         }
    328         if (cursor.moveToFirst()) {
    329             int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
    330             if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(columnIndex)) {
    331                 try {
    332                     source = new FileInputStream(
    333                             mDownloadManager.openDownloadedFile(carrierKeyDownloadIdentifier)
    334                                     .getFileDescriptor());
    335                     jsonStr = convertToString(source);
    336                     parseJsonAndPersistKey(jsonStr, mccMnc);
    337                 } catch (Exception e) {
    338                     Log.e(LOG_TAG, "Error in download:" + carrierKeyDownloadIdentifier
    339                             + ". " + e);
    340                 } finally {
    341                     mDownloadManager.remove(carrierKeyDownloadIdentifier);
    342                     try {
    343                         source.close();
    344                     } catch (IOException e) {
    345                         e.printStackTrace();
    346                     }
    347                 }
    348             }
    349             Log.d(LOG_TAG, "Completed downloading keys");
    350         }
    351         cursor.close();
    352         return;
    353     }
    354 
    355     /**
    356      * This method checks if the carrier requires key. We'll read the carrier config to make that
    357      * determination.
    358      * @return boolean returns true if carrier requires keys, else false.
    359      **/
    360     private boolean carrierUsesKeys() {
    361         CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
    362                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
    363         if (carrierConfigManager == null) {
    364             return false;
    365         }
    366         int subId = mPhone.getSubId();
    367         PersistableBundle b = carrierConfigManager.getConfigForSubId(subId);
    368         if (b == null) {
    369             return false;
    370         }
    371         mKeyAvailability = b.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT);
    372         mURL = b.getString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING);
    373         if (TextUtils.isEmpty(mURL) || mKeyAvailability == 0) {
    374             Log.d(LOG_TAG, "Carrier not enabled or invalid values");
    375             return false;
    376         }
    377         for (int key_type : CARRIER_KEY_TYPES) {
    378             if (isKeyEnabled(key_type)) {
    379                 return true;
    380             }
    381         }
    382         return false;
    383     }
    384 
    385     private static String convertToString(InputStream is) {
    386         try {
    387             // The current implementation at certain Carriers has the data gzipped, which requires
    388             // us to unzip the contents. Longer term, we want to add a flag in carrier config which
    389             // determines if the data needs to be zipped or not.
    390             GZIPInputStream gunzip = new GZIPInputStream(is);
    391             BufferedReader reader = new BufferedReader(new InputStreamReader(gunzip, UTF_8));
    392             StringBuilder sb = new StringBuilder();
    393 
    394             String line;
    395             while ((line = reader.readLine()) != null) {
    396                 sb.append(line).append('\n');
    397             }
    398             return sb.toString();
    399         } catch (IOException e) {
    400             e.printStackTrace();
    401         }
    402         return null;
    403     }
    404 
    405     /**
    406      * Converts the string into a json object to retreive the nodes. The Json should have 3 nodes,
    407      * including the Carrier public key, the key type and the key identifier. Once the nodes have
    408      * been extracted, they get persisted to the database. Sample:
    409      *      "carrier-keys": [ { "certificate": "",
    410      *                         "key-type": "WLAN",
    411      *                         "key-identifier": ""
    412      *                        } ]
    413      * @param jsonStr the json string.
    414      * @param mccMnc contains the mcc, mnc.
    415      */
    416     @VisibleForTesting
    417     public void parseJsonAndPersistKey(String jsonStr, String mccMnc) {
    418         if (TextUtils.isEmpty(jsonStr) || TextUtils.isEmpty(mccMnc)) {
    419             Log.e(LOG_TAG, "jsonStr or mcc, mnc: is empty");
    420             return;
    421         }
    422         PemReader reader = null;
    423         try {
    424             String mcc = "";
    425             String mnc = "";
    426             String[] splitValue = mccMnc.split(SEPARATOR);
    427             mcc = splitValue[0];
    428             mnc = splitValue[1];
    429             JSONObject jsonObj = new JSONObject(jsonStr);
    430             JSONArray keys = jsonObj.getJSONArray(JSON_CARRIER_KEYS);
    431             for (int i = 0; i < keys.length(); i++) {
    432                 JSONObject key = keys.getJSONObject(i);
    433                 // This is a hack to accommodate certain carriers who insist on using the public-key
    434                 // field to store the certificate. We'll just use which-ever is not null.
    435                 String cert = null;
    436                 if (key.has(JSON_CERTIFICATE)) {
    437                     cert = key.getString(JSON_CERTIFICATE);
    438                 } else {
    439                     cert = key.getString(JSON_CERTIFICATE_ALTERNATE);
    440                 }
    441                 String typeString = key.getString(JSON_TYPE);
    442                 int type = UNINITIALIZED_KEY_TYPE;
    443                 if (typeString.equals(JSON_TYPE_VALUE_WLAN)) {
    444                     type = TelephonyManager.KEY_TYPE_WLAN;
    445                 } else if (typeString.equals(JSON_TYPE_VALUE_EPDG)) {
    446                     type = TelephonyManager.KEY_TYPE_EPDG;
    447                 }
    448                 String identifier = key.getString(JSON_IDENTIFIER);
    449                 ByteArrayInputStream inStream = new ByteArrayInputStream(cert.getBytes());
    450                 Reader fReader = new BufferedReader(new InputStreamReader(inStream));
    451                 reader = new PemReader(fReader);
    452                 Pair<PublicKey, Long> keyInfo =
    453                         getKeyInformation(reader.readPemObject().getContent());
    454                 reader.close();
    455                 savePublicKey(keyInfo.first, type, identifier, keyInfo.second, mcc, mnc);
    456             }
    457         } catch (final JSONException e) {
    458             Log.e(LOG_TAG, "Json parsing error: " + e.getMessage());
    459         } catch (final Exception e) {
    460             Log.e(LOG_TAG, "Exception getting certificate: " + e);
    461         } finally {
    462             try {
    463                 if (reader != null) {
    464                     reader.close();
    465                 }
    466             } catch (final Exception e) {
    467                 Log.e(LOG_TAG, "Exception getting certificate: " + e);
    468             }
    469         }
    470     }
    471 
    472     /**
    473      * introspects the mKeyAvailability bitmask
    474      * @return true if the digit at position k is 1, else false.
    475      */
    476     @VisibleForTesting
    477     public boolean isKeyEnabled(int keyType) {
    478         //since keytype has values of 1, 2.... we need to subtract 1 from the keytype.
    479         int returnValue = (mKeyAvailability >> (keyType - 1)) & 1;
    480         return (returnValue == 1) ? true : false;
    481     }
    482 
    483     /**
    484      * Checks whether is the keys are absent or close to expiration. Returns true, if either of
    485      * those conditions are true.
    486      * @return boolean returns true when keys are absent or close to expiration, else false.
    487      */
    488     @VisibleForTesting
    489     public boolean areCarrierKeysAbsentOrExpiring() {
    490         for (int key_type : CARRIER_KEY_TYPES) {
    491             if (!isKeyEnabled(key_type)) {
    492                 continue;
    493             }
    494             ImsiEncryptionInfo imsiEncryptionInfo =
    495                     mPhone.getCarrierInfoForImsiEncryption(key_type);
    496             if (imsiEncryptionInfo == null) {
    497                 Log.d(LOG_TAG, "Key not found for: " + key_type);
    498                 return true;
    499             }
    500             Date imsiDate = imsiEncryptionInfo.getExpirationTime();
    501             long timeToExpire = imsiDate.getTime() - System.currentTimeMillis();
    502             return (timeToExpire < START_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS) ? true : false;
    503         }
    504         return false;
    505     }
    506 
    507     private boolean downloadKey() {
    508         Log.d(LOG_TAG, "starting download from: " + mURL);
    509         String mcc = "";
    510         String mnc = "";
    511         String simOperator = getSimOperator();
    512 
    513         if (!TextUtils.isEmpty(simOperator)) {
    514             mcc = simOperator.substring(0, 3);
    515             mnc = simOperator.substring(3);
    516             Log.d(LOG_TAG, "using values for mcc, mnc: " + mcc + "," + mnc);
    517         } else {
    518             Log.e(LOG_TAG, "mcc, mnc: is empty");
    519             return false;
    520         }
    521         try {
    522             DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mURL));
    523             request.setAllowedOverMetered(false);
    524             request.setVisibleInDownloadsUi(false);
    525             request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
    526             Long carrierKeyDownloadRequestId = mDownloadManager.enqueue(request);
    527             SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit();
    528 
    529             String mccMnc = mcc + SEPARATOR + mnc;
    530             int slotId = mPhone.getPhoneId();
    531             Log.d(LOG_TAG, "storing values in sharedpref mcc, mnc, days: " + mcc + "," + mnc
    532                     + "," + carrierKeyDownloadRequestId);
    533             editor.putString(MCC_MNC_PREF_TAG + slotId, mccMnc);
    534             editor.commit();
    535         } catch (Exception e) {
    536             Log.e(LOG_TAG, "exception trying to dowload key from url: " + mURL);
    537             return false;
    538         }
    539         return true;
    540     }
    541 
    542     /**
    543      * Save the public key
    544      * @param certificate certificate that contains the public key.
    545      * @return Pair containing the Public Key and the expiration date.
    546      **/
    547     @VisibleForTesting
    548     public static Pair<PublicKey, Long> getKeyInformation(byte[] certificate) throws Exception {
    549         InputStream inStream = new ByteArrayInputStream(certificate);
    550         CertificateFactory cf = CertificateFactory.getInstance("X.509");
    551         X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
    552         Pair<PublicKey, Long> keyInformation =
    553                 new Pair(cert.getPublicKey(), cert.getNotAfter().getTime());
    554         return keyInformation;
    555     }
    556 
    557     /**
    558      * Save the public key
    559      * @param publicKey public key.
    560      * @param type key-type.
    561      * @param identifier which is an opaque string.
    562      * @param expirationDate expiration date of the key.
    563      * @param mcc
    564      * @param mnc
    565      **/
    566     @VisibleForTesting
    567     public void savePublicKey(PublicKey publicKey, int type, String identifier, long expirationDate,
    568                                String mcc, String mnc) {
    569         ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo(mcc, mnc, type, identifier,
    570                 publicKey, new Date(expirationDate));
    571         mPhone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo);
    572     }
    573 }
    574