• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.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