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