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