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