Loading src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java 0 → 100644 +471 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony; import static android.preference.PreferenceManager.getDefaultSharedPreferences; import android.app.AlarmManager; import android.app.DownloadManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; import android.telephony.ImsiEncryptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Base64; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Date; /** * This class contains logic to get Certificates and keep them current. * The class will be instantiated by various Phone implementations. */ public class CarrierKeyDownloadManager { private static final String LOG_TAG = "CarrierKeyDownloadManager"; private static final String MCC_MNC_PREF_TAG = "CARRIER_KEY_DM_MCC_MNC"; private static final int DAY_IN_MILLIS = 24 * 3600 * 1000; // Start trying to renew the cert X days before it expires. private static final int DEFAULT_RENEWAL_WINDOW_DAYS = 7; /* Intent for downloading the public key */ private static final String INTENT_KEY_RENEWAL_ALARM_PREFIX = "com.android.internal.telephony.carrier_key_download_alarm"; private int mKeyAvailability = 0; public static final String MNC = "MNC"; public static final String MCC = "MCC"; private static final String SEPARATOR = ":"; private static final String JSON_KEY = "key"; private static final String JSON_TYPE = "type"; private static final String JSON_IDENTIFIER = "identifier"; private static final String JSON_EXPIRATION_DATE = "expiration-date"; private static final String JSON_CARRIER_KEYS = "carrier-keys"; private static final String JSON_TYPE_VALUE_WLAN = "WLAN"; private static final String JSON_TYPE_VALUE_EPDG = "EPDG"; private static final int[] CARRIER_KEY_TYPES = {TelephonyManager.KEY_TYPE_EPDG, TelephonyManager.KEY_TYPE_WLAN}; private static final int UNINITIALIZED_KEY_TYPE = -1; private final Phone mPhone; private final Context mContext; private final DownloadManager mDownloadManager; private String mURL; public CarrierKeyDownloadManager(Phone phone) { mPhone = phone; mContext = phone.getContext(); IntentFilter filter = new IntentFilter(); filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); filter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE); filter.addAction(INTENT_KEY_RENEWAL_ALARM_PREFIX + mPhone.getPhoneId()); mContext.registerReceiver(mBroadcastReceiver, filter, null, phone); mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); } private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); int slotId = mPhone.getPhoneId(); if (action.equals(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId)) { Log.d(LOG_TAG, "Handling key renewal alarm: " + action); handleAlarmOrConfigChange(); } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { if (slotId == intent.getIntExtra(PhoneConstants.PHONE_KEY, SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { Log.d(LOG_TAG, "Carrier Config changed: " + action); handleAlarmOrConfigChange(); } } else if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) { Log.d(LOG_TAG, "Download Complete"); long carrierKeyDownloadIdentifier = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0); String mccMnc = getMccMncSetFromPref(); if (isValidDownload(mccMnc)) { onDownloadComplete(carrierKeyDownloadIdentifier, mccMnc); onPostDownloadProcessing(carrierKeyDownloadIdentifier); } } } }; private void onPostDownloadProcessing(long carrierKeyDownloadIdentifier) { resetRenewalAlarm(); cleanupDownloadPreferences(carrierKeyDownloadIdentifier); } private void handleAlarmOrConfigChange() { if (carrierUsesKeys()) { if (areCarrierKeysAbsentOrExpiring()) { boolean downloadStartedSuccessfully = downloadKey(); // if the download was attemped, but not started successfully, and if carriers uses // keys, we'll still want to renew the alarms, and try downloading the key a day // later. if (!downloadStartedSuccessfully) { resetRenewalAlarm(); } } else { return; } } else { // delete any existing alarms. cleanupRenewalAlarms(); } } private void cleanupDownloadPreferences(long carrierKeyDownloadIdentifier) { Log.d(LOG_TAG, "Cleaning up download preferences: " + carrierKeyDownloadIdentifier); SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit(); editor.remove(String.valueOf(carrierKeyDownloadIdentifier)); editor.commit(); } private void cleanupRenewalAlarms() { Log.d(LOG_TAG, "Cleaning up existing renewal alarms"); int slotId = mPhone.getPhoneId(); Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId); PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(mContext.ALARM_SERVICE); alarmManager.cancel(carrierKeyDownloadIntent); } /** * this method resets the alarm. Starts by cleaning up the existing alarms. * We look at the earliest expiration date, and setup an alarms X days prior. * If the expiration date is in the past, we'll setup an alarm to run the next day. This * could happen if the download has failed. **/ private void resetRenewalAlarm() { cleanupRenewalAlarms(); int slotId = mPhone.getPhoneId(); long minExpirationDate = Long.MAX_VALUE; for (int key_type : CARRIER_KEY_TYPES) { if (!isKeyEnabled(key_type)) { continue; } ImsiEncryptionInfo imsiEncryptionInfo = mPhone.getCarrierInfoForImsiEncryption(key_type); if (imsiEncryptionInfo != null && imsiEncryptionInfo.getExpirationTime() != null) { if (minExpirationDate > imsiEncryptionInfo.getExpirationTime().getTime()) { minExpirationDate = imsiEncryptionInfo.getExpirationTime().getTime(); } } } // if there are no keys, or expiration date is in the past, or within 7 days, then we // set the alarm to run in a day. Else, we'll set the alarm to run 7 days prior to // expiration. if (minExpirationDate == Long.MAX_VALUE || (minExpirationDate < System.currentTimeMillis() + DEFAULT_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS)) { minExpirationDate = System.currentTimeMillis() + DAY_IN_MILLIS; } else { minExpirationDate = minExpirationDate - DEFAULT_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS; } Log.d(LOG_TAG, "minExpirationDate: " + new Date(minExpirationDate)); final AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( Context.ALARM_SERVICE); Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId); PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, minExpirationDate, carrierKeyDownloadIntent); Log.d(LOG_TAG, "setRenewelAlarm: action=" + intent.getAction() + " time=" + new Date(minExpirationDate)); } private String getMccMncSetFromPref() { // check if this is a download that we had created. We do this by checking if the // downloadId is stored in the shared prefs. int slotId = mPhone.getPhoneId(); SharedPreferences preferences = getDefaultSharedPreferences(mContext); return preferences.getString(MCC_MNC_PREF_TAG + slotId, null); } /** * checks if the download was sent by this particular instance. We do this by including the * slot id in the key. If no value is found, we know that the download was not for this * instance of the phone. **/ private boolean isValidDownload(String mccMnc) { String mccCurrent = ""; String mncCurrent = ""; String mccSource = ""; String mncSource = ""; final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); String networkOperator = telephonyManager.getNetworkOperator(mPhone.getSubId()); if (TextUtils.isEmpty(networkOperator) || TextUtils.isEmpty(mccMnc)) { Log.e(LOG_TAG, "networkOperator or mcc/mnc is empty"); return false; } String[] splitValue = mccMnc.split(SEPARATOR); mccSource = splitValue[0]; mncSource = splitValue[1]; Log.d(LOG_TAG, "values from sharedPrefs mcc, mnc: " + mccSource + "," + mncSource); mccCurrent = networkOperator.substring(0, 3); mncCurrent = networkOperator.substring(3); Log.d(LOG_TAG, "using values for mcc, mnc: " + mccCurrent + "," + mncCurrent); if (TextUtils.equals(mncSource, mncCurrent) && TextUtils.equals(mccSource, mccCurrent)) { return true; } return false; } /** * This method will try to parse the downloaded information, and persist it in the database. **/ private void onDownloadComplete(long carrierKeyDownloadIdentifier, String mccMnc) { Log.d(LOG_TAG, "onDownloadComplete: " + carrierKeyDownloadIdentifier); String jsonStr; DownloadManager.Query query = new DownloadManager.Query(); query.setFilterById(carrierKeyDownloadIdentifier); Cursor cursor = mDownloadManager.query(query); if (cursor == null) { return; } if (cursor.moveToFirst()) { int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(columnIndex)) { try { final InputStream source = new FileInputStream( mDownloadManager.openDownloadedFile(carrierKeyDownloadIdentifier) .getFileDescriptor()); jsonStr = convertToString(source); parseJsonAndPersistKey(jsonStr, mccMnc); } catch (Exception e) { Log.e(LOG_TAG, "Error in download:" + carrierKeyDownloadIdentifier + ". " + e); } finally { mDownloadManager.remove(carrierKeyDownloadIdentifier); } } Log.d(LOG_TAG, "Completed downloading keys"); } cursor.close(); return; } /** * This method checks if the carrier requires key. We'll read the carrier config to make that * determination. * @return boolean returns true if carrier requires keys, else false. **/ private boolean carrierUsesKeys() { CarrierConfigManager carrierConfigManager = (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); if (carrierConfigManager == null) { return false; } int subId = mPhone.getSubId(); PersistableBundle b = carrierConfigManager.getConfigForSubId(subId); if (b == null) { return false; } mKeyAvailability = b.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT); mURL = b.getString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING); if (TextUtils.isEmpty(mURL) || mKeyAvailability == 0) { Log.d(LOG_TAG, "Carrier not enabled or invalid values"); return false; } for (int key_type : CARRIER_KEY_TYPES) { if (isKeyEnabled(key_type)) { return true; } } return false; } private static String convertToString(InputStream is) { BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder sb = new StringBuilder(); String line; try { while ((line = reader.readLine()) != null) { sb.append(line).append('\n'); } } catch (IOException e) { e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } return sb.toString(); } /** * Converts the string into a json object to retreive the nodes. The Json should have 3 nodes, * including the Carrier public key, the key type and the key identifier. Once the nodes have * been extracted, they get persisted to the database. Sample: * "carrier-keys": [ { "key": "", * "type": WLAN, * "identifier": "", * "expiration-date": 1502577746000 * } ] * @param jsonStr the json string. * @param mccMnc contains the mcc, mnc */ private void parseJsonAndPersistKey(String jsonStr, String mccMnc) { if (TextUtils.isEmpty(jsonStr) || TextUtils.isEmpty(mccMnc)) { Log.e(LOG_TAG, "jsonStr or mcc, mnc: is empty"); return; } try { String mcc = ""; String mnc = ""; String[] splitValue = mccMnc.split(SEPARATOR); mcc = splitValue[0]; mnc = splitValue[1]; JSONObject jsonObj = new JSONObject(jsonStr); JSONArray keys = jsonObj.getJSONArray(JSON_CARRIER_KEYS); for (int i = 0; i < keys.length(); i++) { JSONObject key = keys.getJSONObject(i); String carrierKey = key.getString(JSON_KEY); String typeString = key.getString(JSON_TYPE); int type = UNINITIALIZED_KEY_TYPE; if (typeString.equals(JSON_TYPE_VALUE_WLAN)) { type = TelephonyManager.KEY_TYPE_WLAN; } else if (typeString.equals(JSON_TYPE_VALUE_EPDG)) { type = TelephonyManager.KEY_TYPE_EPDG; } long expiration_date = key.getLong(JSON_EXPIRATION_DATE); String identifier = key.getString(JSON_IDENTIFIER); savePublicKey(carrierKey, type, identifier, expiration_date, mcc, mnc); } } catch (final JSONException e) { Log.e(LOG_TAG, "Json parsing error: " + e.getMessage()); } } /** * introspects the mKeyAvailability bitmask * @return true if the digit at position k is 1, else false. */ private boolean isKeyEnabled(int keyType) { //since keytype has values of 1, 2.... we need to subtract 1 from the keytype. int returnValue = (mKeyAvailability >> (keyType - 1)) & 1; return (returnValue == 1) ? true : false; } /** * Checks whether is the keys are absent or close to expiration. Returns true, if either of * those conditions are true. * @return boolean returns true when keys are absent or close to expiration, else false. */ @VisibleForTesting public boolean areCarrierKeysAbsentOrExpiring() { for (int key_type : CARRIER_KEY_TYPES) { if (!isKeyEnabled(key_type)) { continue; } ImsiEncryptionInfo imsiEncryptionInfo = mPhone.getCarrierInfoForImsiEncryption(key_type); if (imsiEncryptionInfo == null) { Log.d(LOG_TAG, "Key not found for: " + key_type); return true; } Date imsiDate = imsiEncryptionInfo.getExpirationTime(); long timeToExpire = imsiDate.getTime() - System.currentTimeMillis(); return (timeToExpire < DEFAULT_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS) ? true : false; } return false; } private boolean downloadKey() { Log.d(LOG_TAG, "starting download from: " + mURL); final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); String mcc = ""; String mnc = ""; String networkOperator = telephonyManager.getNetworkOperator(mPhone.getSubId()); if (!TextUtils.isEmpty(networkOperator)) { mcc = networkOperator.substring(0, 3); mnc = networkOperator.substring(3); Log.d(LOG_TAG, "using values for mcc, mnc: " + mcc + "," + mnc); } else { Log.e(LOG_TAG, "mcc, mnc: is empty"); return false; } try { DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mURL)); request.setAllowedOverMetered(false); request.setVisibleInDownloadsUi(false); Long carrierKeyDownloadRequestId = mDownloadManager.enqueue(request); SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit(); String mccMnc = mcc + SEPARATOR + mnc; int slotId = mPhone.getPhoneId(); Log.d(LOG_TAG, "storing values in sharedpref mcc, mnc, days: " + mcc + "," + mnc + "," + carrierKeyDownloadRequestId); editor.putString(MCC_MNC_PREF_TAG + slotId, mccMnc); editor.commit(); } catch (Exception e) { Log.e(LOG_TAG, "exception trying to dowload key from url: " + mURL); return false; } return true; } private void savePublicKey(String key, int type, String identifier, long expirationDate, String mcc, String mnc) { byte[] keyBytes = Base64.decode(key.getBytes(), Base64.DEFAULT); ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo(mcc, mnc, type, identifier, keyBytes, new Date(expirationDate)); mPhone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo); } } src/java/com/android/internal/telephony/GsmCdmaPhone.java +2 −1 Original line number Diff line number Diff line Loading @@ -187,7 +187,7 @@ public class GsmCdmaPhone extends Phone { private int mRilVersion; private boolean mBroadcastEmergencyCallStateChanges = false; private CarrierKeyDownloadManager mCDM; // Constructors public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId, Loading Loading @@ -269,6 +269,7 @@ public class GsmCdmaPhone extends Phone { mCi.registerForVoiceRadioTechChanged(this, EVENT_VOICE_RADIO_TECH_CHANGED, null); mContext.registerReceiver(mBroadcastReceiver, new IntentFilter( CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); mCDM = new CarrierKeyDownloadManager(this); } private void initRatSpecific(int precisePhoneType) { Loading Loading
src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java 0 → 100644 +471 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony; import static android.preference.PreferenceManager.getDefaultSharedPreferences; import android.app.AlarmManager; import android.app.DownloadManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; import android.telephony.ImsiEncryptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Base64; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Date; /** * This class contains logic to get Certificates and keep them current. * The class will be instantiated by various Phone implementations. */ public class CarrierKeyDownloadManager { private static final String LOG_TAG = "CarrierKeyDownloadManager"; private static final String MCC_MNC_PREF_TAG = "CARRIER_KEY_DM_MCC_MNC"; private static final int DAY_IN_MILLIS = 24 * 3600 * 1000; // Start trying to renew the cert X days before it expires. private static final int DEFAULT_RENEWAL_WINDOW_DAYS = 7; /* Intent for downloading the public key */ private static final String INTENT_KEY_RENEWAL_ALARM_PREFIX = "com.android.internal.telephony.carrier_key_download_alarm"; private int mKeyAvailability = 0; public static final String MNC = "MNC"; public static final String MCC = "MCC"; private static final String SEPARATOR = ":"; private static final String JSON_KEY = "key"; private static final String JSON_TYPE = "type"; private static final String JSON_IDENTIFIER = "identifier"; private static final String JSON_EXPIRATION_DATE = "expiration-date"; private static final String JSON_CARRIER_KEYS = "carrier-keys"; private static final String JSON_TYPE_VALUE_WLAN = "WLAN"; private static final String JSON_TYPE_VALUE_EPDG = "EPDG"; private static final int[] CARRIER_KEY_TYPES = {TelephonyManager.KEY_TYPE_EPDG, TelephonyManager.KEY_TYPE_WLAN}; private static final int UNINITIALIZED_KEY_TYPE = -1; private final Phone mPhone; private final Context mContext; private final DownloadManager mDownloadManager; private String mURL; public CarrierKeyDownloadManager(Phone phone) { mPhone = phone; mContext = phone.getContext(); IntentFilter filter = new IntentFilter(); filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); filter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE); filter.addAction(INTENT_KEY_RENEWAL_ALARM_PREFIX + mPhone.getPhoneId()); mContext.registerReceiver(mBroadcastReceiver, filter, null, phone); mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); } private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); int slotId = mPhone.getPhoneId(); if (action.equals(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId)) { Log.d(LOG_TAG, "Handling key renewal alarm: " + action); handleAlarmOrConfigChange(); } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { if (slotId == intent.getIntExtra(PhoneConstants.PHONE_KEY, SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { Log.d(LOG_TAG, "Carrier Config changed: " + action); handleAlarmOrConfigChange(); } } else if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) { Log.d(LOG_TAG, "Download Complete"); long carrierKeyDownloadIdentifier = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0); String mccMnc = getMccMncSetFromPref(); if (isValidDownload(mccMnc)) { onDownloadComplete(carrierKeyDownloadIdentifier, mccMnc); onPostDownloadProcessing(carrierKeyDownloadIdentifier); } } } }; private void onPostDownloadProcessing(long carrierKeyDownloadIdentifier) { resetRenewalAlarm(); cleanupDownloadPreferences(carrierKeyDownloadIdentifier); } private void handleAlarmOrConfigChange() { if (carrierUsesKeys()) { if (areCarrierKeysAbsentOrExpiring()) { boolean downloadStartedSuccessfully = downloadKey(); // if the download was attemped, but not started successfully, and if carriers uses // keys, we'll still want to renew the alarms, and try downloading the key a day // later. if (!downloadStartedSuccessfully) { resetRenewalAlarm(); } } else { return; } } else { // delete any existing alarms. cleanupRenewalAlarms(); } } private void cleanupDownloadPreferences(long carrierKeyDownloadIdentifier) { Log.d(LOG_TAG, "Cleaning up download preferences: " + carrierKeyDownloadIdentifier); SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit(); editor.remove(String.valueOf(carrierKeyDownloadIdentifier)); editor.commit(); } private void cleanupRenewalAlarms() { Log.d(LOG_TAG, "Cleaning up existing renewal alarms"); int slotId = mPhone.getPhoneId(); Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId); PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(mContext.ALARM_SERVICE); alarmManager.cancel(carrierKeyDownloadIntent); } /** * this method resets the alarm. Starts by cleaning up the existing alarms. * We look at the earliest expiration date, and setup an alarms X days prior. * If the expiration date is in the past, we'll setup an alarm to run the next day. This * could happen if the download has failed. **/ private void resetRenewalAlarm() { cleanupRenewalAlarms(); int slotId = mPhone.getPhoneId(); long minExpirationDate = Long.MAX_VALUE; for (int key_type : CARRIER_KEY_TYPES) { if (!isKeyEnabled(key_type)) { continue; } ImsiEncryptionInfo imsiEncryptionInfo = mPhone.getCarrierInfoForImsiEncryption(key_type); if (imsiEncryptionInfo != null && imsiEncryptionInfo.getExpirationTime() != null) { if (minExpirationDate > imsiEncryptionInfo.getExpirationTime().getTime()) { minExpirationDate = imsiEncryptionInfo.getExpirationTime().getTime(); } } } // if there are no keys, or expiration date is in the past, or within 7 days, then we // set the alarm to run in a day. Else, we'll set the alarm to run 7 days prior to // expiration. if (minExpirationDate == Long.MAX_VALUE || (minExpirationDate < System.currentTimeMillis() + DEFAULT_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS)) { minExpirationDate = System.currentTimeMillis() + DAY_IN_MILLIS; } else { minExpirationDate = minExpirationDate - DEFAULT_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS; } Log.d(LOG_TAG, "minExpirationDate: " + new Date(minExpirationDate)); final AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( Context.ALARM_SERVICE); Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId); PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, minExpirationDate, carrierKeyDownloadIntent); Log.d(LOG_TAG, "setRenewelAlarm: action=" + intent.getAction() + " time=" + new Date(minExpirationDate)); } private String getMccMncSetFromPref() { // check if this is a download that we had created. We do this by checking if the // downloadId is stored in the shared prefs. int slotId = mPhone.getPhoneId(); SharedPreferences preferences = getDefaultSharedPreferences(mContext); return preferences.getString(MCC_MNC_PREF_TAG + slotId, null); } /** * checks if the download was sent by this particular instance. We do this by including the * slot id in the key. If no value is found, we know that the download was not for this * instance of the phone. **/ private boolean isValidDownload(String mccMnc) { String mccCurrent = ""; String mncCurrent = ""; String mccSource = ""; String mncSource = ""; final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); String networkOperator = telephonyManager.getNetworkOperator(mPhone.getSubId()); if (TextUtils.isEmpty(networkOperator) || TextUtils.isEmpty(mccMnc)) { Log.e(LOG_TAG, "networkOperator or mcc/mnc is empty"); return false; } String[] splitValue = mccMnc.split(SEPARATOR); mccSource = splitValue[0]; mncSource = splitValue[1]; Log.d(LOG_TAG, "values from sharedPrefs mcc, mnc: " + mccSource + "," + mncSource); mccCurrent = networkOperator.substring(0, 3); mncCurrent = networkOperator.substring(3); Log.d(LOG_TAG, "using values for mcc, mnc: " + mccCurrent + "," + mncCurrent); if (TextUtils.equals(mncSource, mncCurrent) && TextUtils.equals(mccSource, mccCurrent)) { return true; } return false; } /** * This method will try to parse the downloaded information, and persist it in the database. **/ private void onDownloadComplete(long carrierKeyDownloadIdentifier, String mccMnc) { Log.d(LOG_TAG, "onDownloadComplete: " + carrierKeyDownloadIdentifier); String jsonStr; DownloadManager.Query query = new DownloadManager.Query(); query.setFilterById(carrierKeyDownloadIdentifier); Cursor cursor = mDownloadManager.query(query); if (cursor == null) { return; } if (cursor.moveToFirst()) { int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(columnIndex)) { try { final InputStream source = new FileInputStream( mDownloadManager.openDownloadedFile(carrierKeyDownloadIdentifier) .getFileDescriptor()); jsonStr = convertToString(source); parseJsonAndPersistKey(jsonStr, mccMnc); } catch (Exception e) { Log.e(LOG_TAG, "Error in download:" + carrierKeyDownloadIdentifier + ". " + e); } finally { mDownloadManager.remove(carrierKeyDownloadIdentifier); } } Log.d(LOG_TAG, "Completed downloading keys"); } cursor.close(); return; } /** * This method checks if the carrier requires key. We'll read the carrier config to make that * determination. * @return boolean returns true if carrier requires keys, else false. **/ private boolean carrierUsesKeys() { CarrierConfigManager carrierConfigManager = (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); if (carrierConfigManager == null) { return false; } int subId = mPhone.getSubId(); PersistableBundle b = carrierConfigManager.getConfigForSubId(subId); if (b == null) { return false; } mKeyAvailability = b.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT); mURL = b.getString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING); if (TextUtils.isEmpty(mURL) || mKeyAvailability == 0) { Log.d(LOG_TAG, "Carrier not enabled or invalid values"); return false; } for (int key_type : CARRIER_KEY_TYPES) { if (isKeyEnabled(key_type)) { return true; } } return false; } private static String convertToString(InputStream is) { BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder sb = new StringBuilder(); String line; try { while ((line = reader.readLine()) != null) { sb.append(line).append('\n'); } } catch (IOException e) { e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } return sb.toString(); } /** * Converts the string into a json object to retreive the nodes. The Json should have 3 nodes, * including the Carrier public key, the key type and the key identifier. Once the nodes have * been extracted, they get persisted to the database. Sample: * "carrier-keys": [ { "key": "", * "type": WLAN, * "identifier": "", * "expiration-date": 1502577746000 * } ] * @param jsonStr the json string. * @param mccMnc contains the mcc, mnc */ private void parseJsonAndPersistKey(String jsonStr, String mccMnc) { if (TextUtils.isEmpty(jsonStr) || TextUtils.isEmpty(mccMnc)) { Log.e(LOG_TAG, "jsonStr or mcc, mnc: is empty"); return; } try { String mcc = ""; String mnc = ""; String[] splitValue = mccMnc.split(SEPARATOR); mcc = splitValue[0]; mnc = splitValue[1]; JSONObject jsonObj = new JSONObject(jsonStr); JSONArray keys = jsonObj.getJSONArray(JSON_CARRIER_KEYS); for (int i = 0; i < keys.length(); i++) { JSONObject key = keys.getJSONObject(i); String carrierKey = key.getString(JSON_KEY); String typeString = key.getString(JSON_TYPE); int type = UNINITIALIZED_KEY_TYPE; if (typeString.equals(JSON_TYPE_VALUE_WLAN)) { type = TelephonyManager.KEY_TYPE_WLAN; } else if (typeString.equals(JSON_TYPE_VALUE_EPDG)) { type = TelephonyManager.KEY_TYPE_EPDG; } long expiration_date = key.getLong(JSON_EXPIRATION_DATE); String identifier = key.getString(JSON_IDENTIFIER); savePublicKey(carrierKey, type, identifier, expiration_date, mcc, mnc); } } catch (final JSONException e) { Log.e(LOG_TAG, "Json parsing error: " + e.getMessage()); } } /** * introspects the mKeyAvailability bitmask * @return true if the digit at position k is 1, else false. */ private boolean isKeyEnabled(int keyType) { //since keytype has values of 1, 2.... we need to subtract 1 from the keytype. int returnValue = (mKeyAvailability >> (keyType - 1)) & 1; return (returnValue == 1) ? true : false; } /** * Checks whether is the keys are absent or close to expiration. Returns true, if either of * those conditions are true. * @return boolean returns true when keys are absent or close to expiration, else false. */ @VisibleForTesting public boolean areCarrierKeysAbsentOrExpiring() { for (int key_type : CARRIER_KEY_TYPES) { if (!isKeyEnabled(key_type)) { continue; } ImsiEncryptionInfo imsiEncryptionInfo = mPhone.getCarrierInfoForImsiEncryption(key_type); if (imsiEncryptionInfo == null) { Log.d(LOG_TAG, "Key not found for: " + key_type); return true; } Date imsiDate = imsiEncryptionInfo.getExpirationTime(); long timeToExpire = imsiDate.getTime() - System.currentTimeMillis(); return (timeToExpire < DEFAULT_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS) ? true : false; } return false; } private boolean downloadKey() { Log.d(LOG_TAG, "starting download from: " + mURL); final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); String mcc = ""; String mnc = ""; String networkOperator = telephonyManager.getNetworkOperator(mPhone.getSubId()); if (!TextUtils.isEmpty(networkOperator)) { mcc = networkOperator.substring(0, 3); mnc = networkOperator.substring(3); Log.d(LOG_TAG, "using values for mcc, mnc: " + mcc + "," + mnc); } else { Log.e(LOG_TAG, "mcc, mnc: is empty"); return false; } try { DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mURL)); request.setAllowedOverMetered(false); request.setVisibleInDownloadsUi(false); Long carrierKeyDownloadRequestId = mDownloadManager.enqueue(request); SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit(); String mccMnc = mcc + SEPARATOR + mnc; int slotId = mPhone.getPhoneId(); Log.d(LOG_TAG, "storing values in sharedpref mcc, mnc, days: " + mcc + "," + mnc + "," + carrierKeyDownloadRequestId); editor.putString(MCC_MNC_PREF_TAG + slotId, mccMnc); editor.commit(); } catch (Exception e) { Log.e(LOG_TAG, "exception trying to dowload key from url: " + mURL); return false; } return true; } private void savePublicKey(String key, int type, String identifier, long expirationDate, String mcc, String mnc) { byte[] keyBytes = Base64.decode(key.getBytes(), Base64.DEFAULT); ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo(mcc, mnc, type, identifier, keyBytes, new Date(expirationDate)); mPhone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo); } }
src/java/com/android/internal/telephony/GsmCdmaPhone.java +2 −1 Original line number Diff line number Diff line Loading @@ -187,7 +187,7 @@ public class GsmCdmaPhone extends Phone { private int mRilVersion; private boolean mBroadcastEmergencyCallStateChanges = false; private CarrierKeyDownloadManager mCDM; // Constructors public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId, Loading Loading @@ -269,6 +269,7 @@ public class GsmCdmaPhone extends Phone { mCi.registerForVoiceRadioTechChanged(this, EVENT_VOICE_RADIO_TECH_CHANGED, null); mContext.registerReceiver(mBroadcastReceiver, new IntentFilter( CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); mCDM = new CarrierKeyDownloadManager(this); } private void initRatSpecific(int precisePhoneType) { Loading