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 java.nio.charset.StandardCharsets.UTF_8; 20 21 import android.annotation.NonNull; 22 import android.app.AlarmManager; 23 import android.app.DownloadManager; 24 import android.app.KeyguardManager; 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.database.Cursor; 31 import android.net.ConnectivityManager; 32 import android.net.Network; 33 import android.net.Uri; 34 import android.os.Handler; 35 import android.os.Message; 36 import android.os.PersistableBundle; 37 import android.os.UserManager; 38 import android.telephony.CarrierConfigManager; 39 import android.telephony.ImsiEncryptionInfo; 40 import android.telephony.SubscriptionInfo; 41 import android.telephony.SubscriptionManager; 42 import android.telephony.TelephonyManager; 43 import android.text.TextUtils; 44 import android.util.Log; 45 import android.util.Pair; 46 47 48 import com.android.internal.annotations.VisibleForTesting; 49 import com.android.internal.telephony.flags.Flags; 50 51 import org.json.JSONArray; 52 import org.json.JSONException; 53 import org.json.JSONObject; 54 55 56 import java.io.BufferedReader; 57 import java.io.ByteArrayInputStream; 58 import java.io.FileInputStream; 59 import java.io.IOException; 60 import java.io.InputStream; 61 import java.io.InputStreamReader; 62 import java.security.PublicKey; 63 import java.security.cert.CertificateFactory; 64 import java.security.cert.X509Certificate; 65 import java.util.Date; 66 import java.util.List; 67 import java.util.Random; 68 import java.util.zip.GZIPInputStream; 69 import java.util.zip.ZipException; 70 71 /** 72 * This class contains logic to get Certificates and keep them current. 73 * The class will be instantiated by various Phone implementations. 74 */ 75 public class CarrierKeyDownloadManager extends Handler { 76 private static final String LOG_TAG = "CarrierKeyDownloadManager"; 77 78 private static final String CERT_BEGIN_STRING = "-----BEGIN CERTIFICATE-----"; 79 80 private static final String CERT_END_STRING = "-----END CERTIFICATE-----"; 81 82 private static final int DAY_IN_MILLIS = 24 * 3600 * 1000; 83 84 // Create a window prior to the key expiration, during which the cert will be 85 // downloaded. Defines the start date of that window. So if the key expires on 86 // Dec 21st, the start of the renewal window will be Dec 1st. 87 private static final int START_RENEWAL_WINDOW_DAYS = 21; 88 89 // This will define the end date of the window. 90 private static final int END_RENEWAL_WINDOW_DAYS = 7; 91 92 /* Intent for downloading the public key */ 93 private static final String INTENT_KEY_RENEWAL_ALARM_PREFIX = 94 "com.android.internal.telephony.carrier_key_download_alarm"; 95 96 @VisibleForTesting 97 public int mKeyAvailability = 0; 98 99 private static final String JSON_CERTIFICATE = "certificate"; 100 private static final String JSON_CERTIFICATE_ALTERNATE = "public-key"; 101 private static final String JSON_TYPE = "key-type"; 102 private static final String JSON_IDENTIFIER = "key-identifier"; 103 private static final String JSON_CARRIER_KEYS = "carrier-keys"; 104 private static final String JSON_TYPE_VALUE_WLAN = "WLAN"; 105 private static final String JSON_TYPE_VALUE_EPDG = "EPDG"; 106 107 private static final int EVENT_ALARM_OR_CONFIG_CHANGE = 0; 108 private static final int EVENT_DOWNLOAD_COMPLETE = 1; 109 private static final int EVENT_NETWORK_AVAILABLE = 2; 110 private static final int EVENT_SCREEN_UNLOCKED = 3; 111 112 113 private static final int[] CARRIER_KEY_TYPES = {TelephonyManager.KEY_TYPE_EPDG, 114 TelephonyManager.KEY_TYPE_WLAN}; 115 116 private final Phone mPhone; 117 private final Context mContext; 118 public final DownloadManager mDownloadManager; 119 private String mURL; 120 private boolean mAllowedOverMeteredNetwork = false; 121 private boolean mDeleteOldKeyAfterDownload = false; 122 private boolean mIsRequiredToHandleUnlock; 123 private final TelephonyManager mTelephonyManager; 124 private UserManager mUserManager; 125 @VisibleForTesting 126 public String mMccMncForDownload = ""; 127 public int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; 128 @VisibleForTesting 129 public long mDownloadId; 130 private DefaultNetworkCallback mDefaultNetworkCallback; 131 private ConnectivityManager mConnectivityManager; 132 private KeyguardManager mKeyguardManager; 133 CarrierKeyDownloadManager(Phone phone)134 public CarrierKeyDownloadManager(Phone phone) { 135 mPhone = phone; 136 mContext = phone.getContext(); 137 IntentFilter filter = new IntentFilter(); 138 filter.addAction(INTENT_KEY_RENEWAL_ALARM_PREFIX); 139 filter.addAction(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD); 140 if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { 141 filter.addAction(Intent.ACTION_USER_UNLOCKED); 142 } 143 mContext.registerReceiver(mBroadcastReceiver, filter, null, phone); 144 mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); 145 mTelephonyManager = mContext.getSystemService(TelephonyManager.class) 146 .createForSubscriptionId(mPhone.getSubId()); 147 if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { 148 mKeyguardManager = mContext.getSystemService(KeyguardManager.class); 149 } else { 150 mUserManager = mContext.getSystemService(UserManager.class); 151 } 152 CarrierConfigManager carrierConfigManager = mContext.getSystemService( 153 CarrierConfigManager.class); 154 // Callback which directly handle config change should be executed on handler thread 155 carrierConfigManager.registerCarrierConfigChangeListener(this::post, 156 (slotIndex, subId, carrierId, specificCarrierId) -> { 157 if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { 158 logd("CarrierConfig changed slotIndex = " + slotIndex + " subId = " + subId 159 + " CarrierId = " + carrierId + " phoneId = " 160 + mPhone.getPhoneId()); 161 // Below checks are necessary to optimise the logic. 162 if ((slotIndex == mPhone.getPhoneId()) && (carrierId > 0 163 || !TextUtils.isEmpty( 164 mMccMncForDownload))) { 165 mCarrierId = carrierId; 166 updateSimOperator(); 167 // If device is screen locked do not proceed to handle 168 // EVENT_ALARM_OR_CONFIG_CHANGE 169 if (mKeyguardManager.isDeviceLocked()) { 170 logd("Device is Locked"); 171 mIsRequiredToHandleUnlock = true; 172 } else { 173 logd("Carrier Config changed: slotIndex=" + slotIndex); 174 sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE); 175 } 176 } 177 } else { 178 boolean isUserUnlocked = mUserManager.isUserUnlocked(); 179 180 if (isUserUnlocked && slotIndex == mPhone.getPhoneId()) { 181 Log.d(LOG_TAG, "Carrier Config changed: slotIndex=" + slotIndex); 182 handleAlarmOrConfigChange(); 183 } else { 184 Log.d(LOG_TAG, "User is locked"); 185 mContext.registerReceiver(mUserUnlockedReceiver, new IntentFilter( 186 Intent.ACTION_USER_UNLOCKED)); 187 } 188 } 189 }); 190 mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); 191 } 192 193 // TODO remove this method upon imsiKeyRetryDownloadOnPhoneUnlock enabled. 194 private final BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() { 195 @Override 196 public void onReceive(Context context, Intent intent) { 197 if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { 198 Log.d(LOG_TAG, "Received UserUnlockedReceiver"); 199 handleAlarmOrConfigChange(); 200 } 201 } 202 }; 203 204 private final BroadcastReceiver mDownloadReceiver = new BroadcastReceiver() { 205 @Override 206 public void onReceive(Context context, Intent intent) { 207 String action = intent.getAction(); 208 if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) { 209 logd("Download Complete"); 210 sendMessage(obtainMessage(EVENT_DOWNLOAD_COMPLETE, 211 intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0))); 212 } 213 } 214 }; 215 216 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 217 @Override 218 public void onReceive(Context context, Intent intent) { 219 String action = intent.getAction(); 220 int slotIndex = SubscriptionManager.getSlotIndex(mPhone.getSubId()); 221 int phoneId = mPhone.getPhoneId(); 222 switch (action) { 223 case INTENT_KEY_RENEWAL_ALARM_PREFIX -> { 224 int slotIndexExtra = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 225 -1); 226 if (slotIndexExtra == slotIndex) { 227 logd("Handling key renewal alarm: " + action); 228 if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { 229 updateSimOperator(); 230 } 231 sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE); 232 } 233 } 234 case TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD -> { 235 if (phoneId == intent.getIntExtra(PhoneConstants.PHONE_KEY, 236 SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { 237 logd("Handling reset intent: " + action); 238 sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE); 239 } 240 } 241 case Intent.ACTION_USER_UNLOCKED -> { 242 // The Carrier key download fails when SIM is inserted while device is locked 243 // hence adding a retry logic when device is unlocked. 244 logd("device fully unlocked, isRequiredToHandleUnlock = " 245 + mIsRequiredToHandleUnlock 246 + ", slotIndex = " + slotIndex + " hasActiveDataNetwork = " + ( 247 mConnectivityManager.getActiveNetwork() != null)); 248 if (mIsRequiredToHandleUnlock) { 249 mIsRequiredToHandleUnlock = false; 250 sendEmptyMessage(EVENT_SCREEN_UNLOCKED); 251 } 252 } 253 } 254 } 255 }; 256 257 @Override handleMessage(Message msg)258 public void handleMessage (Message msg) { 259 switch (msg.what) { 260 case EVENT_ALARM_OR_CONFIG_CHANGE, EVENT_NETWORK_AVAILABLE, EVENT_SCREEN_UNLOCKED -> 261 handleAlarmOrConfigChange(); 262 case EVENT_DOWNLOAD_COMPLETE -> { 263 long carrierKeyDownloadIdentifier = (long) msg.obj; 264 String currentMccMnc = Flags.imsiKeyRetryDownloadOnPhoneUnlock() 265 ? mTelephonyManager.getSimOperator(mPhone.getSubId()) : getSimOperator(); 266 int carrierId = Flags.imsiKeyRetryDownloadOnPhoneUnlock() 267 ? mTelephonyManager.getSimCarrierId() : getSimCarrierId(); 268 if (isValidDownload(currentMccMnc, carrierKeyDownloadIdentifier, carrierId)) { 269 onDownloadComplete(carrierKeyDownloadIdentifier, currentMccMnc, carrierId); 270 onPostDownloadProcessing(); 271 } 272 } 273 } 274 } 275 onPostDownloadProcessing()276 private void onPostDownloadProcessing() { 277 resetRenewalAlarm(); 278 if(Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { 279 mDownloadId = -1; 280 } else { 281 cleanupDownloadInfo(); 282 } 283 // unregister from DOWNLOAD_COMPLETE 284 mContext.unregisterReceiver(mDownloadReceiver); 285 } 286 handleAlarmOrConfigChange()287 private void handleAlarmOrConfigChange() { 288 if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { 289 if (carrierUsesKeys()) { 290 if (areCarrierKeysAbsentOrExpiring()) { 291 boolean hasActiveDataNetwork = 292 (mConnectivityManager.getActiveNetwork() != null); 293 boolean downloadStartedSuccessfully = hasActiveDataNetwork && downloadKey(); 294 // if the download was attempted, but not started successfully, and if 295 // carriers uses keys, we'll still want to renew the alarms, and try 296 // downloading the key a day later. 297 int slotIndex = SubscriptionManager.getSlotIndex(mPhone.getSubId()); 298 if (downloadStartedSuccessfully) { 299 unregisterDefaultNetworkCb(slotIndex); 300 } else { 301 // If download fails due to the device lock, we will reattempt once the 302 // device is unlocked. 303 mIsRequiredToHandleUnlock = mKeyguardManager.isDeviceLocked(); 304 loge("hasActiveDataConnection = " + hasActiveDataNetwork 305 + " isDeviceLocked = " + mIsRequiredToHandleUnlock); 306 if (!hasActiveDataNetwork) { 307 registerDefaultNetworkCb(slotIndex); 308 } 309 resetRenewalAlarm(); 310 } 311 } 312 logd("handleAlarmOrConfigChange :: areCarrierKeysAbsentOrExpiring returned false"); 313 } else { 314 cleanupRenewalAlarms(); 315 if (!isOtherSlotHasCarrier()) { 316 // delete any existing alarms. 317 mPhone.deleteCarrierInfoForImsiEncryption(getSimCarrierId(), getSimOperator()); 318 } 319 cleanupDownloadInfo(); 320 } 321 } else { 322 if (carrierUsesKeys()) { 323 if (areCarrierKeysAbsentOrExpiring()) { 324 boolean downloadStartedSuccessfully = downloadKey(); 325 // if the download was attempted, but not started successfully, and if 326 // carriers uses keys, we'll still want to renew the alarms, and try 327 // downloading the key a day later. 328 if (!downloadStartedSuccessfully) { 329 resetRenewalAlarm(); 330 } 331 } 332 } else { 333 // delete any existing alarms. 334 cleanupRenewalAlarms(); 335 mPhone.deleteCarrierInfoForImsiEncryption(getSimCarrierId()); 336 } 337 } 338 } 339 isOtherSlotHasCarrier()340 private boolean isOtherSlotHasCarrier() { 341 SubscriptionManager subscriptionManager = mPhone.getContext().getSystemService( 342 SubscriptionManager.class); 343 List<SubscriptionInfo> subscriptionInfoList = 344 subscriptionManager.getActiveSubscriptionInfoList(); 345 loge("handleAlarmOrConfigChange ActiveSubscriptionInfoList = " + ( 346 (subscriptionInfoList != null) ? subscriptionInfoList.size() : null)); 347 for (SubscriptionInfo subInfo : subscriptionInfoList) { 348 if (mPhone.getSubId() != subInfo.getSubscriptionId() 349 && subInfo.getCarrierId() == mPhone.getCarrierId()) { 350 // We do not proceed to remove the Key from the DB as another slot contains 351 // same operator sim which is in active state. 352 loge("handleAlarmOrConfigChange same operator sim in another slot"); 353 return true; 354 } 355 } 356 return false; 357 } 358 cleanupDownloadInfo()359 private void cleanupDownloadInfo() { 360 logd("Cleaning up download info"); 361 mDownloadId = -1; 362 if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { 363 mMccMncForDownload = ""; 364 } else { 365 mMccMncForDownload = null; 366 } 367 mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; 368 } 369 cleanupRenewalAlarms()370 private void cleanupRenewalAlarms() { 371 logd("Cleaning up existing renewal alarms"); 372 int slotIndex = SubscriptionManager.getSlotIndex(mPhone.getSubId()); 373 Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX); 374 intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, slotIndex); 375 PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent, 376 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); 377 AlarmManager alarmManager =mContext.getSystemService(AlarmManager.class); 378 alarmManager.cancel(carrierKeyDownloadIntent); 379 } 380 381 /** 382 * this method returns the date to be used to decide on when to start downloading the key. 383 * from the carrier. 384 **/ 385 @VisibleForTesting getExpirationDate()386 public long getExpirationDate() { 387 long minExpirationDate = Long.MAX_VALUE; 388 for (int key_type : CARRIER_KEY_TYPES) { 389 if (!isKeyEnabled(key_type)) { 390 continue; 391 } 392 ImsiEncryptionInfo imsiEncryptionInfo = 393 mPhone.getCarrierInfoForImsiEncryption(key_type, false); 394 if (imsiEncryptionInfo != null && imsiEncryptionInfo.getExpirationTime() != null) { 395 if (minExpirationDate > imsiEncryptionInfo.getExpirationTime().getTime()) { 396 minExpirationDate = imsiEncryptionInfo.getExpirationTime().getTime(); 397 } 398 } 399 } 400 401 // if there are no keys, or expiration date is in the past, or within 7 days, then we 402 // set the alarm to run in a day. Else, we'll set the alarm to run 7 days prior to 403 // expiration. 404 if (minExpirationDate == Long.MAX_VALUE || (minExpirationDate 405 < System.currentTimeMillis() + END_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS)) { 406 minExpirationDate = System.currentTimeMillis() + DAY_IN_MILLIS; 407 } else { 408 // We don't want all the phones to download the certs simultaneously, so 409 // we pick a random time during the download window to avoid this situation. 410 Random random = new Random(); 411 int max = START_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS; 412 int min = END_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS; 413 int randomTime = random.nextInt(max - min) + min; 414 minExpirationDate = minExpirationDate - randomTime; 415 } 416 return minExpirationDate; 417 } 418 419 /** 420 * this method resets the alarm. Starts by cleaning up the existing alarms. 421 * We look at the earliest expiration date, and setup an alarms X days prior. 422 * If the expiration date is in the past, we'll setup an alarm to run the next day. This 423 * could happen if the download has failed. 424 **/ 425 @VisibleForTesting resetRenewalAlarm()426 public void resetRenewalAlarm() { 427 cleanupRenewalAlarms(); 428 int slotIndex = SubscriptionManager.getSlotIndex(mPhone.getSubId()); 429 long minExpirationDate = getExpirationDate(); 430 logd("minExpirationDate: " + new Date(minExpirationDate)); 431 final AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( 432 Context.ALARM_SERVICE); 433 Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX); 434 intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, slotIndex); 435 PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent, 436 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); 437 alarmManager.set(AlarmManager.RTC_WAKEUP, minExpirationDate, carrierKeyDownloadIntent); 438 logd("setRenewalAlarm: action=" + intent.getAction() + " time=" 439 + new Date(minExpirationDate)); 440 } 441 442 /** 443 * Read the store the sim operetor value and update the value in case of change in the sim 444 * operetor. 445 */ updateSimOperator()446 public void updateSimOperator() { 447 if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { 448 String simOperator = mPhone.getOperatorNumeric(); 449 if (!TextUtils.isEmpty(simOperator) && !simOperator.equals(mMccMncForDownload)) { 450 mMccMncForDownload = simOperator; 451 logd("updateSimOperator, Initialized mMccMncForDownload = " + mMccMncForDownload); 452 } 453 } 454 } 455 456 /** 457 * Returns the sim operator. 458 **/ 459 @VisibleForTesting getSimOperator()460 public String getSimOperator() { 461 if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { 462 updateSimOperator(); 463 return mMccMncForDownload; 464 } else { 465 return mTelephonyManager.getSimOperator(mPhone.getSubId()); 466 } 467 } 468 469 /** 470 * Returns the sim operator. 471 **/ 472 @VisibleForTesting getSimCarrierId()473 public int getSimCarrierId() { 474 if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { 475 return (mCarrierId > 0) ? mCarrierId : mPhone.getCarrierId(); 476 } else { 477 return mTelephonyManager.getSimCarrierId(); 478 } 479 } 480 481 /** 482 * checks if the download was sent by this particular instance. We do this by including the 483 * slot id in the key. If no value is found, we know that the download was not for this 484 * instance of the phone. 485 **/ 486 @VisibleForTesting isValidDownload(String currentMccMnc, long currentDownloadId, int carrierId)487 public boolean isValidDownload(String currentMccMnc, long currentDownloadId, int carrierId) { 488 if (currentDownloadId != mDownloadId) { 489 loge( "download ID=" + currentDownloadId 490 + " for completed download does not match stored id=" + mDownloadId); 491 return false; 492 } 493 494 if (TextUtils.isEmpty(currentMccMnc) || TextUtils.isEmpty(mMccMncForDownload) 495 || !TextUtils.equals(currentMccMnc, mMccMncForDownload) 496 || mCarrierId != carrierId) { 497 loge( "currentMccMnc=" + currentMccMnc + " storedMccMnc =" + mMccMncForDownload 498 + "currentCarrierId = " + carrierId + " storedCarrierId = " + mCarrierId); 499 return false; 500 } 501 502 logd("Matched MccMnc = " + currentMccMnc + ", carrierId = " + carrierId 503 + ", downloadId: " + currentDownloadId); 504 return true; 505 } 506 507 /** 508 * This method will try to parse the downloaded information, and persist it in the database. 509 **/ onDownloadComplete(long carrierKeyDownloadIdentifier, String mccMnc, int carrierId)510 private void onDownloadComplete(long carrierKeyDownloadIdentifier, String mccMnc, 511 int carrierId) { 512 logd("onDownloadComplete: " + carrierKeyDownloadIdentifier); 513 String jsonStr; 514 DownloadManager.Query query = new DownloadManager.Query(); 515 query.setFilterById(carrierKeyDownloadIdentifier); 516 Cursor cursor = mDownloadManager.query(query); 517 518 if (cursor == null) { 519 return; 520 } 521 if (cursor.moveToFirst()) { 522 int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); 523 if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(columnIndex)) { 524 try { 525 jsonStr = convertToString(mDownloadManager, carrierKeyDownloadIdentifier); 526 if (TextUtils.isEmpty(jsonStr)) { 527 logd("fallback to no gzip"); 528 jsonStr = convertToStringNoGZip(mDownloadManager, 529 carrierKeyDownloadIdentifier); 530 } 531 parseJsonAndPersistKey(jsonStr, mccMnc, carrierId); 532 } catch (Exception e) { 533 loge( "Error in download:" + carrierKeyDownloadIdentifier 534 + ". " + e); 535 } finally { 536 mDownloadManager.remove(carrierKeyDownloadIdentifier); 537 } 538 } 539 logd("Completed downloading keys"); 540 } 541 cursor.close(); 542 } 543 544 /** 545 * This method checks if the carrier requires key. We'll read the carrier config to make that 546 * determination. 547 * @return boolean returns true if carrier requires keys, else false. 548 **/ carrierUsesKeys()549 private boolean carrierUsesKeys() { 550 CarrierConfigManager carrierConfigManager = (CarrierConfigManager) 551 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); 552 if (carrierConfigManager == null) { 553 return false; 554 } 555 int subId = mPhone.getSubId(); 556 PersistableBundle b = null; 557 try { 558 b = carrierConfigManager.getConfigForSubId(subId, 559 CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, 560 CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, 561 CarrierConfigManager.KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL); 562 } catch (RuntimeException e) { 563 loge( "CarrierConfigLoader is not available."); 564 } 565 if (b == null || b.isEmpty()) { 566 return false; 567 } 568 569 mKeyAvailability = b.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT); 570 mURL = b.getString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING); 571 mAllowedOverMeteredNetwork = b.getBoolean( 572 CarrierConfigManager.KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL); 573 574 if (mKeyAvailability == 0 || TextUtils.isEmpty(mURL)) { 575 logd("Carrier not enabled or invalid values. mKeyAvailability=" + mKeyAvailability 576 + " mURL=" + mURL); 577 return false; 578 } 579 for (int key_type : CARRIER_KEY_TYPES) { 580 if (isKeyEnabled(key_type)) { 581 return true; 582 } 583 } 584 return false; 585 } 586 convertToStringNoGZip(DownloadManager downloadManager, long downloadId)587 private static String convertToStringNoGZip(DownloadManager downloadManager, long downloadId) { 588 StringBuilder sb = new StringBuilder(); 589 try (InputStream source = new FileInputStream( 590 downloadManager.openDownloadedFile(downloadId).getFileDescriptor())) { 591 // If the carrier does not have the data gzipped, fallback to assuming it is not zipped. 592 // parseJsonAndPersistKey may still fail if the data is malformed, so we won't be 593 // persisting random bogus strings thinking it's the cert 594 BufferedReader reader = new BufferedReader(new InputStreamReader(source, UTF_8)); 595 596 String line; 597 while ((line = reader.readLine()) != null) { 598 sb.append(line).append('\n'); 599 } 600 } catch (IOException e) { 601 e.printStackTrace(); 602 return null; 603 } 604 return sb.toString(); 605 } 606 convertToString(DownloadManager downloadManager, long downloadId)607 private static String convertToString(DownloadManager downloadManager, long downloadId) { 608 try (InputStream source = new FileInputStream( 609 downloadManager.openDownloadedFile(downloadId).getFileDescriptor()); 610 InputStream gzipIs = new GZIPInputStream(source)) { 611 BufferedReader reader = new BufferedReader(new InputStreamReader(gzipIs, UTF_8)); 612 StringBuilder sb = new StringBuilder(); 613 614 String line; 615 while ((line = reader.readLine()) != null) { 616 sb.append(line).append('\n'); 617 } 618 return sb.toString(); 619 } catch (ZipException e) { 620 // GZIPInputStream constructor will throw exception if stream is not GZIP 621 Log.d(LOG_TAG, "Stream is not gzipped e=" + e); 622 return null; 623 } catch (IOException e) { 624 Log.e(LOG_TAG, "Unexpected exception in convertToString e=" + e); 625 return null; 626 } 627 } 628 629 /** 630 * Converts the string into a json object to retreive the nodes. The Json should have 3 nodes, 631 * including the Carrier public key, the key type and the key identifier. Once the nodes have 632 * been extracted, they get persisted to the database. Sample: 633 * "carrier-keys": [ { "certificate": "", 634 * "key-type": "WLAN", 635 * "key-identifier": "" 636 * } ] 637 * @param jsonStr the json string. 638 * @param mccMnc contains the mcc, mnc. 639 */ 640 @VisibleForTesting parseJsonAndPersistKey(String jsonStr, String mccMnc, int carrierId)641 public void parseJsonAndPersistKey(String jsonStr, String mccMnc, int carrierId) { 642 if (TextUtils.isEmpty(jsonStr) || TextUtils.isEmpty(mccMnc) 643 || carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { 644 loge( "jsonStr or mcc, mnc: is empty or carrierId is UNKNOWN_CARRIER_ID"); 645 return; 646 } 647 try { 648 String mcc = mccMnc.substring(0, 3); 649 String mnc = mccMnc.substring(3); 650 JSONObject jsonObj = new JSONObject(jsonStr); 651 JSONArray keys = jsonObj.getJSONArray(JSON_CARRIER_KEYS); 652 for (int i = 0; i < keys.length(); i++) { 653 JSONObject key = keys.getJSONObject(i); 654 // Support both "public-key" and "certificate" String property. 655 String cert = null; 656 if (key.has(JSON_CERTIFICATE)) { 657 cert = key.getString(JSON_CERTIFICATE); 658 } else { 659 cert = key.getString(JSON_CERTIFICATE_ALTERNATE); 660 } 661 // The key-type property is optional, therefore, the default value is WLAN type if 662 // not specified. 663 int type = TelephonyManager.KEY_TYPE_WLAN; 664 if (key.has(JSON_TYPE)) { 665 String typeString = key.getString(JSON_TYPE); 666 if (typeString.equals(JSON_TYPE_VALUE_EPDG)) { 667 type = TelephonyManager.KEY_TYPE_EPDG; 668 } else if (!typeString.equals(JSON_TYPE_VALUE_WLAN)) { 669 loge( "Invalid key-type specified: " + typeString); 670 } 671 } 672 String identifier = key.getString(JSON_IDENTIFIER); 673 Pair<PublicKey, Long> keyInfo = 674 getKeyInformation(cleanCertString(cert).getBytes()); 675 if (mDeleteOldKeyAfterDownload) { 676 if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { 677 mPhone.deleteCarrierInfoForImsiEncryption( 678 TelephonyManager.UNKNOWN_CARRIER_ID, null); 679 } else { 680 mPhone.deleteCarrierInfoForImsiEncryption( 681 TelephonyManager.UNKNOWN_CARRIER_ID); 682 } 683 mDeleteOldKeyAfterDownload = false; 684 } 685 savePublicKey(keyInfo.first, type, identifier, keyInfo.second, mcc, mnc, carrierId); 686 } 687 } catch (final JSONException e) { 688 loge( "Json parsing error: " + e.getMessage()); 689 } catch (final Exception e) { 690 loge( "Exception getting certificate: " + e); 691 } 692 } 693 694 /** 695 * introspects the mKeyAvailability bitmask 696 * @return true if the digit at position k is 1, else false. 697 */ 698 @VisibleForTesting isKeyEnabled(int keyType)699 public boolean isKeyEnabled(int keyType) { 700 // since keytype has values of 1, 2.... we need to subtract 1 from the keytype. 701 return isKeyEnabled(keyType, mKeyAvailability); 702 } 703 704 /** 705 * introspects the mKeyAvailability bitmask 706 * @return true if the digit at position k is 1, else false. 707 */ isKeyEnabled(int keyType, int keyAvailability)708 public static boolean isKeyEnabled(int keyType, int keyAvailability) { 709 // since keytype has values of 1, 2.... we need to subtract 1 from the keytype. 710 int returnValue = (keyAvailability >> (keyType - 1)) & 1; 711 return returnValue == 1; 712 } 713 714 /** 715 * Checks whether is the keys are absent or close to expiration. Returns true, if either of 716 * those conditions are true. 717 * @return boolean returns true when keys are absent or close to expiration, else false. 718 */ 719 @VisibleForTesting areCarrierKeysAbsentOrExpiring()720 public boolean areCarrierKeysAbsentOrExpiring() { 721 for (int key_type : CARRIER_KEY_TYPES) { 722 if (!isKeyEnabled(key_type)) { 723 continue; 724 } 725 // get encryption info with fallback=false so that we attempt a download even if there's 726 // backup info stored in carrier config 727 ImsiEncryptionInfo imsiEncryptionInfo = 728 mPhone.getCarrierInfoForImsiEncryption(key_type, false); 729 if (imsiEncryptionInfo == null) { 730 logd("Key not found for: " + key_type); 731 return true; 732 } else if (imsiEncryptionInfo.getCarrierId() == TelephonyManager.UNKNOWN_CARRIER_ID) { 733 logd("carrier key is unknown carrier, so prefer to reDownload"); 734 mDeleteOldKeyAfterDownload = true; 735 return true; 736 } 737 Date imsiDate = imsiEncryptionInfo.getExpirationTime(); 738 long timeToExpire = imsiDate.getTime() - System.currentTimeMillis(); 739 return timeToExpire < START_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS; 740 } 741 return false; 742 } 743 downloadKey()744 private boolean downloadKey() { 745 logd("starting download from: " + mURL); 746 String mccMnc = null; 747 int carrierId = -1; 748 if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { 749 if (TextUtils.isEmpty(mMccMncForDownload) 750 || mCarrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { 751 loge("mccmnc or carrierId is UnKnown"); 752 return false; 753 } 754 } else { 755 mccMnc = getSimOperator(); 756 carrierId = getSimCarrierId(); 757 if (!TextUtils.isEmpty(mccMnc) || carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) { 758 Log.d(LOG_TAG, "downloading key for mccmnc : " + mccMnc + ", carrierId : " 759 + carrierId); 760 } else { 761 Log.e(LOG_TAG, "mccmnc or carrierId is UnKnown"); 762 return false; 763 } 764 } 765 766 try { 767 // register the broadcast receiver to listen for download complete 768 IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE); 769 mContext.registerReceiver(mDownloadReceiver, filter, null, mPhone, 770 Context.RECEIVER_EXPORTED); 771 772 DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mURL)); 773 774 // TODO(b/128550341): Implement the logic to minimize using metered network such as 775 // LTE for downloading a certificate. 776 request.setAllowedOverMetered(mAllowedOverMeteredNetwork); 777 request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN); 778 request.addRequestHeader("Accept-Encoding", "gzip"); 779 long carrierKeyDownloadRequestId = mDownloadManager.enqueue(request); 780 if (!Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { 781 mMccMncForDownload = mccMnc; 782 mCarrierId = carrierId; 783 } 784 mDownloadId = carrierKeyDownloadRequestId; 785 logd("saving values mccmnc: " + mMccMncForDownload + ", downloadId: " 786 + carrierKeyDownloadRequestId + ", carrierId: " + mCarrierId); 787 } catch (Exception e) { 788 loge( "exception trying to download key from url: " + mURL + ", Exception = " 789 + e.getMessage()); 790 return false; 791 } 792 return true; 793 } 794 795 /** 796 * Save the public key 797 * @param certificate certificate that contains the public key. 798 * @return Pair containing the Public Key and the expiration date. 799 **/ 800 @VisibleForTesting getKeyInformation(byte[] certificate)801 public static Pair<PublicKey, Long> getKeyInformation(byte[] certificate) throws Exception { 802 InputStream inStream = new ByteArrayInputStream(certificate); 803 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 804 X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream); 805 Pair<PublicKey, Long> keyInformation = 806 new Pair<>(cert.getPublicKey(), cert.getNotAfter().getTime()); 807 return keyInformation; 808 } 809 810 /** 811 * Save the public key 812 * @param publicKey public key. 813 * @param type key-type. 814 * @param identifier which is an opaque string. 815 * @param expirationDate expiration date of the key. 816 * @param mcc 817 * @param mnc 818 **/ 819 @VisibleForTesting savePublicKey(PublicKey publicKey, int type, String identifier, long expirationDate, String mcc, String mnc, int carrierId)820 public void savePublicKey(PublicKey publicKey, int type, String identifier, long expirationDate, 821 String mcc, String mnc, int carrierId) { 822 ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo(mcc, mnc, 823 type, identifier, publicKey, new Date(expirationDate), carrierId); 824 mPhone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo); 825 } 826 827 /** 828 * Remove potential extraneous text in a certificate string 829 * @param cert certificate string 830 * @return Cleaned up version of the certificate string 831 */ 832 @VisibleForTesting cleanCertString(String cert)833 public static String cleanCertString(String cert) { 834 return cert.substring( 835 cert.indexOf(CERT_BEGIN_STRING), 836 cert.indexOf(CERT_END_STRING) + CERT_END_STRING.length()); 837 } 838 839 /** 840 * Registering the callback to listen on data connection availability. 841 * 842 * @param slotId Sim slotIndex that tries to download the key. 843 */ registerDefaultNetworkCb(int slotId)844 private void registerDefaultNetworkCb(int slotId) { 845 logd("RegisterDefaultNetworkCb for slotId = " + slotId); 846 if (mDefaultNetworkCallback == null 847 && slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX) { 848 mDefaultNetworkCallback = new DefaultNetworkCallback(slotId); 849 mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this); 850 } 851 } 852 853 /** 854 * Unregister the data connection monitor listener. 855 */ unregisterDefaultNetworkCb(int slotId)856 private void unregisterDefaultNetworkCb(int slotId) { 857 logd("unregisterDefaultNetworkCb for slotId = " + slotId); 858 if (mDefaultNetworkCallback != null) { 859 mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback); 860 mDefaultNetworkCallback = null; 861 } 862 } 863 864 final class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback { 865 final int mSlotIndex; 866 DefaultNetworkCallback(int slotId)867 public DefaultNetworkCallback(int slotId) { 868 mSlotIndex = slotId; 869 } 870 871 /** Called when the framework connects and has declared a new network ready for use. */ 872 @Override onAvailable(@onNull Network network)873 public void onAvailable(@NonNull Network network) { 874 logd("Data network connected, slotId = " + mSlotIndex); 875 if (mConnectivityManager.getActiveNetwork() != null && SubscriptionManager.getSlotIndex( 876 mPhone.getSubId()) == mSlotIndex) { 877 mIsRequiredToHandleUnlock = false; 878 unregisterDefaultNetworkCb(mSlotIndex); 879 sendEmptyMessage(EVENT_NETWORK_AVAILABLE); 880 } 881 } 882 } 883 loge(String logStr)884 private void loge(String logStr) { 885 String TAG = LOG_TAG + " [" + mPhone.getPhoneId() + "]"; 886 Log.e(TAG, logStr); 887 } 888 logd(String logStr)889 private void logd(String logStr) { 890 String TAG = LOG_TAG + " [" + mPhone.getPhoneId() + "]"; 891 Log.d(TAG, logStr); 892 } 893 } 894