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