release-request-ee600ee1-fadc-4a92-9feb-e22548c84a75-for-git_oc-mr1-release-4318546 snap-temp-L23800000099760172
Change-Id: Ib36217cc33f4c3f8264dfcb7e5a875c1349ec237
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
new file mode 100644
index 0000000..bca337d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -0,0 +1,471 @@
+/*
+ * 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);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index afaa431..35a2d41 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -187,7 +187,7 @@
private int mRilVersion;
private boolean mBroadcastEmergencyCallStateChanges = false;
-
+ private CarrierKeyDownloadManager mCDM;
// Constructors
public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId,
@@ -269,6 +269,7 @@
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) {
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 4598b0c..2ec5101 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -76,6 +76,7 @@
import android.widget.TextView;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccController;
@@ -970,7 +971,8 @@
* raw pdu of the status report is in the extended data ("pdu").
* -param destAddr the destination phone number (for short code confirmation)
*/
- protected void sendRawPdu(SmsTracker tracker) {
+ @VisibleForTesting
+ public void sendRawPdu(SmsTracker tracker) {
HashMap map = tracker.getData();
byte pdu[] = (byte[]) map.get("pdu");
@@ -1075,7 +1077,7 @@
// Wait for user confirmation unless the user has set permission to always allow/deny
int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission(
- tracker.mAppInfo.packageName);
+ tracker.getAppPackageName());
if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
// First time trying to send to premium SMS.
premiumSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
@@ -1150,7 +1152,7 @@
return; // queue limit reached; error was returned to caller
}
- CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName, tracker.mUserId);
+ CharSequence appLabel = getAppLabel(tracker.getAppPackageName(), tracker.mUserId);
Resources r = Resources.getSystem();
Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel));
@@ -1188,7 +1190,7 @@
detailsId = R.string.sms_short_code_details;
}
- CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName, tracker.mUserId);
+ CharSequence appLabel = getAppLabel(tracker.getAppPackageName(), tracker.mUserId);
Resources r = Resources.getSystem();
Spanned messageText = Html.fromHtml(r.getString(R.string.sms_short_code_confirm_message,
appLabel, tracker.mDestAddress));
@@ -1401,6 +1403,14 @@
}
/**
+ * Get the App package name
+ * @return App package name info
+ */
+ public String getAppPackageName() {
+ return mAppInfo != null ? mAppInfo.packageName : null;
+ }
+
+ /**
* Update the status of this message if we persisted it
*/
public void updateSentMessageStatus(Context context, int status) {
@@ -1710,7 +1720,7 @@
}
sendMessage(msg);
}
- setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission);
+ setPremiumSmsPermission(mTracker.getAppPackageName(), newSmsPermission);
}
@Override
diff --git a/src/java/com/android/internal/telephony/SmsUsageMonitor.java b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
index 73e9a42..402a5ef 100644
--- a/src/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -29,8 +29,8 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.PhoneNumberUtils;
-import android.util.AtomicFile;
import android.telephony.Rlog;
+import android.util.AtomicFile;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
@@ -48,10 +48,10 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
/**
@@ -85,7 +85,7 @@
static final int CATEGORY_STANDARD_SHORT_CODE = 2;
/** Return value from {@link #checkDestination} for possible premium short codes. */
- static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
+ public static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
/** Return value from {@link #checkDestination} for premium short codes. */
static final int CATEGORY_PREMIUM_SHORT_CODE = 4;
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java b/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
index 67d91d3..8273dee 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
@@ -80,6 +80,8 @@
sCmdToString[RSP_RESET - BASE] = "RSP_RESET";
}
+ ConnectionParams mLastConnectionParams;
+
// Convert cmd to string or null if unknown
protected static String cmdToString(int cmd) {
cmd -= BASE;
@@ -377,9 +379,9 @@
log("bringUp: apnContext=" + apnContext + "unmeteredUseOnly=" + unmeteredUseOnly
+ " onCompletedMsg=" + onCompletedMsg);
}
- sendMessage(DataConnection.EVENT_CONNECT,
- new ConnectionParams(apnContext, profileId, rilRadioTechnology, unmeteredUseOnly,
- onCompletedMsg, connectionGeneration));
+ mLastConnectionParams = new ConnectionParams(apnContext, profileId, rilRadioTechnology,
+ unmeteredUseOnly, onCompletedMsg, connectionGeneration);
+ sendMessage(DataConnection.EVENT_CONNECT, mLastConnectionParams);
}
/**
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 54c8bea..850ccaa 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -217,6 +217,8 @@
private AsyncChannel mReplyAc = new AsyncChannel();
+ private final LocalLog mDataRoamingLeakageLog = new LocalLog(50);
+
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver () {
@Override
public void onReceive(Context context, Intent intent) {
@@ -328,7 +330,7 @@
mSettingsObserver.observe(
Settings.Global.getUriFor(Settings.Global.DATA_ROAMING + simSuffix),
- DctConstants.EVENT_ROAMING_ON);
+ DctConstants.EVENT_ROAMING_SETTING_CHANGE);
mSettingsObserver.observe(
Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE);
@@ -2774,8 +2776,10 @@
// This method is called
// 1. When the data roaming status changes from non-roaming to roaming.
// 2. When allowed data roaming settings is changed by the user.
- private void onDataRoamingOnOrSettingsChanged() {
+ private void onDataRoamingOnOrSettingsChanged(int messageType) {
if (DBG) log("onDataRoamingOnOrSettingsChanged");
+ // Used to differentiate data roaming turned on vs settings changed.
+ boolean settingChanged = (messageType == DctConstants.EVENT_ROAMING_SETTING_CHANGE);
// Check if the device is actually data roaming
if (!mPhone.getServiceState().getDataRoaming()) {
@@ -2783,6 +2787,8 @@
return;
}
+ checkDataRoamingStatus(settingChanged);
+
if (getDataRoamingEnabled()) {
if (DBG) log("onDataRoamingOnOrSettingsChanged: setup data on roaming");
@@ -2798,6 +2804,22 @@
}
}
+ // We want to track possible roaming data leakage. Which is, if roaming setting
+ // is disabled, yet we still setup a roaming data connection or have a connected ApnContext
+ // switched to roaming. When this happens, we log it in a local log.
+ private void checkDataRoamingStatus(boolean settingChanged) {
+ if (!settingChanged && !getDataRoamingEnabled()
+ && mPhone.getServiceState().getDataRoaming()) {
+ for (ApnContext apnContext : mApnContexts.values()) {
+ if (apnContext.getState() == DctConstants.State.CONNECTED) {
+ mDataRoamingLeakageLog.log("PossibleRoamingLeakage "
+ + " connection params: " + (apnContext.getDcAc() != null
+ ? apnContext.getDcAc().mLastConnectionParams : ""));
+ }
+ }
+ }
+ }
+
private void onRadioAvailable() {
if (DBG) log("onRadioAvailable");
if (mPhone.getSimulatedRadioControl() != null) {
@@ -2927,7 +2949,7 @@
}
// everything is setup
- if(TextUtils.equals(apnContext.getApnType(),PhoneConstants.APN_TYPE_DEFAULT)) {
+ if (TextUtils.equals(apnContext.getApnType(), PhoneConstants.APN_TYPE_DEFAULT)) {
try {
SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "true");
} catch (RuntimeException ex) {
@@ -2951,6 +2973,8 @@
// A connection is setup
apnContext.setState(DctConstants.State.CONNECTED);
+ checkDataRoamingStatus(false);
+
boolean isProvApn = apnContext.isProvisioningApn();
final ConnectivityManager cm = ConnectivityManager.from(mPhone.getContext());
if (mProvisionBroadcastReceiver != null) {
@@ -3762,7 +3786,8 @@
break;
case DctConstants.EVENT_ROAMING_ON:
- onDataRoamingOnOrSettingsChanged();
+ case DctConstants.EVENT_ROAMING_SETTING_CHANGE:
+ onDataRoamingOnOrSettingsChanged(msg.what);
break;
case DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE:
@@ -4176,6 +4201,8 @@
pw.println(" mAutoAttachOnCreation=" + mAutoAttachOnCreation.get());
pw.println(" mIsScreenOn=" + mIsScreenOn);
pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator);
+ pw.println(" mDataRoamingLeakageLog= ");
+ mDataRoamingLeakageLog.dump(fd, pw, args);
pw.flush();
pw.println(" ***************************************");
DcController dcc = mDcc;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 9a03c44..7e8a439 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -442,7 +442,14 @@
@Override
public int checkCallingOrSelfPermission(String permission) {
- return PackageManager.PERMISSION_GRANTED;
+ if (mPermissionTable.contains(permission)
+ || mPermissionTable.contains(PERMISSION_ENABLE_ALL)) {
+ logd("checkCallingOrSelfPermission: " + permission + " return GRANTED");
+ return PackageManager.PERMISSION_GRANTED;
+ } else {
+ logd("checkCallingOrSelfPermission: " + permission + " return DENIED");
+ return PackageManager.PERMISSION_DENIED;
+ }
}
@Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
index c488348..0774e8e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -16,10 +16,15 @@
package com.android.internal.telephony.gsm;
+import static android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
+
+import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
+import static com.android.internal.telephony.SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW;
import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -36,6 +41,7 @@
import android.os.HandlerThread;
import android.os.Message;
import android.os.SystemProperties;
+import android.provider.Settings;
import android.provider.Telephony;
import android.support.test.filters.FlakyTest;
import android.telephony.SmsManager;
@@ -43,8 +49,10 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Singleton;
+import com.android.internal.telephony.ContextFixture;
import com.android.internal.telephony.ISub;
import com.android.internal.telephony.ImsSMSDispatcher;
+import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.TelephonyTestUtils;
import com.android.internal.telephony.TestApplication;
@@ -53,8 +61,11 @@
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import java.util.HashMap;
+
public class GsmSmsDispatcherTest extends TelephonyTest {
@Mock
private android.telephony.SmsMessage mSmsMessage;
@@ -67,6 +78,8 @@
@Mock
private CountryDetector mCountryDetector;
@Mock
+ private SMSDispatcher.SmsTracker mSmsTracker;
+ @Mock
private ISub.Stub mISubStub;
private Object mLock = new Object();
private boolean mReceivedTestIntent = false;
@@ -197,4 +210,35 @@
assertEquals(SmsManager.RESULT_ERROR_NULL_PDU, mTestReceiver.getResultCode());
}
}
+
+ @Test
+ public void testSendRawPduWithEventStopSending() throws Exception {
+ setupMockPackagePermissionChecks();
+ mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+
+ // return a fake value to pass getData()
+ HashMap data = new HashMap<String, String>();
+ data.put("pdu", new byte[1]);
+ when(mSmsTracker.getData()).thenReturn(data);
+
+ // Set values to return to simulate EVENT_STOP_SENDING
+ when(mSmsUsageMonitor.checkDestination(any(), any()))
+ .thenReturn(CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE);
+ when(mSmsUsageMonitor.getPremiumSmsPermission(any()))
+ .thenReturn(PREMIUM_SMS_PERMISSION_NEVER_ALLOW);
+ when(mSmsTracker.getAppPackageName()).thenReturn("");
+
+ // Settings.Global.DEVICE_PROVISIONED to 1
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 1);
+
+ mGsmSmsDispatcher.sendRawPdu(mSmsTracker);
+
+ verify(mSmsUsageMonitor, times(1)).checkDestination(any(), any());
+ verify(mSmsUsageMonitor, times(1)).getPremiumSmsPermission(any());
+ ArgumentCaptor<Integer> argumentCaptor = ArgumentCaptor
+ .forClass(Integer.class);
+ verify(mSmsTracker, times(1)).onFailed(any(), argumentCaptor.capture(), anyInt());
+ assertEquals(RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, (int) argumentCaptor.getValue());
+ }
}