[automerger skipped] [automerger] Fixed Invalid Pdu Issue am: 4b938358de am: fc2a2d071a am: ca00d5d151 am: 23de93c197 am: 909c1606d3 am: bfb0076f03 skipped: c8ec5c3e94 am: 63d433cd2b am: 2c39575764 -s ours
am: 6e89e1ecdf -s ours
Change-Id: I0c46301fd221b50b88c204280009d4067e2655e5
diff --git a/Android.mk b/Android.mk
index 70a88db..b440b28 100644
--- a/Android.mk
+++ b/Android.mk
@@ -25,7 +25,7 @@
$(call all-logtags-files-under, src/java) \
$(call all-proto-files-under, proto)
-LOCAL_JAVA_LIBRARIES := voip-common ims-common services
+LOCAL_JAVA_LIBRARIES := voip-common ims-common services bouncycastle
LOCAL_STATIC_JAVA_LIBRARIES := android.hardware.radio-V1.1-java-static \
android.hardware.radio.deprecated-V1.0-java-static
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..b215563
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,7 @@
+amitmahajan@google.com
+breadley@google.com
+fionaxu@google.com
+jackyu@google.com
+jsh@google.com
+rgreenwalt@google.com
+tgunn@google.com
diff --git a/src/java/com/android/internal/telephony/CallForwardInfo.java b/src/java/com/android/internal/telephony/CallForwardInfo.java
index dccf306..e40028f 100644
--- a/src/java/com/android/internal/telephony/CallForwardInfo.java
+++ b/src/java/com/android/internal/telephony/CallForwardInfo.java
@@ -16,12 +16,16 @@
package com.android.internal.telephony;
+import android.telecom.Log;
+
/**
* See also RIL_CallForwardInfo in include/telephony/ril.h
*
* {@hide}
*/
public class CallForwardInfo {
+ private static final String TAG = "CallForwardInfo";
+
public int status; /*1 = active, 0 = not active */
public int reason; /* from TS 27.007 7.11 "reason" */
public int serviceClass; /* Saum of CommandsInterface.SERVICE_CLASS */
@@ -31,9 +35,9 @@
@Override
public String toString() {
- return super.toString() + (status == 0 ? " not active " : " active ")
- + " reason: " + reason
- + " serviceClass: " + serviceClass + " " + timeSeconds + " seconds";
-
+ return "[CallForwardInfo: status=" + (status == 0 ? " not active " : " active ")
+ + ", reason= " + reason
+ + ", serviceClass= " + serviceClass + ", timeSec= " + timeSeconds + " seconds"
+ + ", number=" + Log.pii(number) + "]";
}
}
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java
index 96dd8c4..ebf04e8 100644
--- a/src/java/com/android/internal/telephony/CarrierInfoManager.java
+++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -16,26 +16,133 @@
package com.android.internal.telephony;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteConstraintException;
+import android.provider.Telephony;
import android.telephony.ImsiEncryptionInfo;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
- /**
+import java.util.Date;
+
+/**
* This class provides methods to retreive information from the CarrierKeyProvider.
*/
public class CarrierInfoManager {
- private static final String TAG = "CarrierInfoManager";
+ private static final String LOG_TAG = "CarrierInfoManager";
/**
* Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
* @param keyType whether the key is being used for WLAN or ePDG.
- * @return ImsiEncryptionInfo which contains the information including the public key to be
+ * @param mContext
+ * @return ImsiEncryptionInfo which contains the information, including the public key, to be
* used for encryption.
*/
- public static ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
- //TODO implementation will be done in subsequent CL.
+ public static ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType,
+ Context mContext) {
+ String mcc = "";
+ String mnc = "";
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ String networkOperator = telephonyManager.getNetworkOperator();
+ if (!TextUtils.isEmpty(networkOperator)) {
+ mcc = networkOperator.substring(0, 3);
+ mnc = networkOperator.substring(3);
+ Log.i(LOG_TAG, "using values for mnc, mcc: " + mnc + "," + mcc);
+ } else {
+ Log.e(LOG_TAG, "Invalid networkOperator: " + networkOperator);
+ return null;
+ }
+ Cursor findCursor = null;
+ try {
+ // In the current design, MVNOs are not supported. If we decide to support them,
+ // we'll need to add to this CL.
+ ContentResolver mContentResolver = mContext.getContentResolver();
+ String[] columns = {Telephony.CarrierColumns.PUBLIC_KEY,
+ Telephony.CarrierColumns.EXPIRATION_TIME,
+ Telephony.CarrierColumns.KEY_IDENTIFIER};
+ findCursor = mContentResolver.query(Telephony.CarrierColumns.CONTENT_URI, columns,
+ "mcc=? and mnc=? and key_type=?",
+ new String[]{mcc, mnc, String.valueOf(keyType)}, null);
+ if (findCursor == null || !findCursor.moveToFirst()) {
+ Log.d(LOG_TAG, "No rows found for keyType: " + keyType);
+ return null;
+ }
+ if (findCursor.getCount() > 1) {
+ Log.e(LOG_TAG, "More than 1 row found for the keyType: " + keyType);
+ }
+ byte[] carrier_key = findCursor.getBlob(0);
+ Date expirationTime = new Date(findCursor.getLong(1));
+ String keyIdentifier = findCursor.getString(2);
+ return new ImsiEncryptionInfo(mcc, mnc, keyType, keyIdentifier, carrier_key,
+ expirationTime);
+ } catch (IllegalArgumentException e) {
+ Log.e(LOG_TAG, "Bad arguments:" + e);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Query failed:" + e);
+ } finally {
+ if (findCursor != null) {
+ findCursor.close();
+ }
+ }
return null;
}
/**
+ * Inserts or update the Carrier Key in the database
+ * @param imsiEncryptionInfo ImsiEncryptionInfo object.
+ * @param mContext Context.
+ */
+ public static void updateOrInsertCarrierKey(ImsiEncryptionInfo imsiEncryptionInfo,
+ Context mContext) {
+ byte[] keyBytes = imsiEncryptionInfo.getPublicKey().getEncoded();
+ ContentResolver mContentResolver = mContext.getContentResolver();
+ // In the current design, MVNOs are not supported. If we decide to support them,
+ // we'll need to add to this CL.
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Telephony.CarrierColumns.MCC, imsiEncryptionInfo.getMcc());
+ contentValues.put(Telephony.CarrierColumns.MNC, imsiEncryptionInfo.getMnc());
+ contentValues.put(Telephony.CarrierColumns.KEY_TYPE,
+ imsiEncryptionInfo.getKeyType());
+ contentValues.put(Telephony.CarrierColumns.KEY_IDENTIFIER,
+ imsiEncryptionInfo.getKeyIdentifier());
+ contentValues.put(Telephony.CarrierColumns.PUBLIC_KEY, keyBytes);
+ contentValues.put(Telephony.CarrierColumns.EXPIRATION_TIME,
+ imsiEncryptionInfo.getExpirationTime().getTime());
+ try {
+ Log.i(LOG_TAG, "Inserting imsiEncryptionInfo into db");
+ mContentResolver.insert(Telephony.CarrierColumns.CONTENT_URI, contentValues);
+ } catch (SQLiteConstraintException e) {
+ Log.i(LOG_TAG, "Insert failed, updating imsiEncryptionInfo into db");
+ ContentValues updatedValues = new ContentValues();
+ updatedValues.put(Telephony.CarrierColumns.PUBLIC_KEY, keyBytes);
+ updatedValues.put(Telephony.CarrierColumns.EXPIRATION_TIME,
+ imsiEncryptionInfo.getExpirationTime().getTime());
+ updatedValues.put(Telephony.CarrierColumns.KEY_IDENTIFIER,
+ imsiEncryptionInfo.getKeyIdentifier());
+ try {
+ int nRows = mContentResolver.update(Telephony.CarrierColumns.CONTENT_URI,
+ updatedValues,
+ "mcc=? and mnc=? and key_type=?", new String[]{
+ imsiEncryptionInfo.getMcc(),
+ imsiEncryptionInfo.getMnc(),
+ String.valueOf(imsiEncryptionInfo.getKeyType())});
+ if (nRows == 0) {
+ Log.d(LOG_TAG, "Error updating values:" + imsiEncryptionInfo);
+ }
+ } catch (Exception ex) {
+ Log.d(LOG_TAG, "Error updating values:" + imsiEncryptionInfo + ex);
+ }
+ } catch (Exception e) {
+ Log.d(LOG_TAG, "Error inserting/updating values:" + imsiEncryptionInfo + e);
+ }
+ }
+
+ /**
* Sets the Carrier specific information that will be used to encrypt the IMSI and IMPI.
* This includes the public key and the key identifier. This information will be stored in the
* device keystore.
@@ -43,10 +150,12 @@
* {@link java.security.PublicKey} and the Key Identifier.
* The keyIdentifier Attribute value pair that helps a server locate
* the private key to decrypt the permanent identity.
+ * @param mContext Context.
*/
- public static void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
- //TODO implementation will be done in subsequent CL.
- return;
+ public static void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
+ Context mContext) {
+ Log.i(LOG_TAG, "inserting carrier key: " + imsiEncryptionInfo);
+ updateOrInsertCarrierKey(imsiEncryptionInfo, mContext);
+ //todo send key to modem. Will be done in a subsequent CL.
}
-}
-
+}
\ No newline at end of file
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..66bc529
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -0,0 +1,551 @@
+/*
+ * 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 static java.nio.charset.StandardCharsets.UTF_8;
+
+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.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.org.bouncycastle.util.io.pem.PemReader;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.security.PublicKey;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * 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";
+
+ @VisibleForTesting
+ public 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_CERTIFICATE = "certificate";
+ // This is a hack to accommodate certain Carriers who insists on using the public-key
+ // field to store the certificate. We'll just use which-ever is not null.
+ private static final String JSON_CERTIFICATE_ALTERNATE = "public-key";
+ private static final String JSON_TYPE = "key-type";
+ private static final String JSON_IDENTIFIER = "key-identifier";
+ 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;
+ public 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 returns the date to be used to decide on when to start downloading the key.
+ * from the carrier.
+ **/
+ @VisibleForTesting
+ public long getExpirationDate() {
+ 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;
+ }
+ return minExpirationDate;
+ }
+
+ /**
+ * 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.
+ **/
+ @VisibleForTesting
+ public void resetRenewalAlarm() {
+ cleanupRenewalAlarms();
+ int slotId = mPhone.getPhoneId();
+ long minExpirationDate = getExpirationDate();
+ 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);
+ }
+
+ /**
+ * Returns the sim operator.
+ **/
+ @VisibleForTesting
+ public String getSimOperator() {
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ return telephonyManager.getSimOperator(mPhone.getSubId());
+ }
+
+ /**
+ * 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.
+ **/
+ @VisibleForTesting
+ public boolean isValidDownload(String mccMnc) {
+ String mccCurrent = "";
+ String mncCurrent = "";
+ String mccSource = "";
+ String mncSource = "";
+
+ String simOperator = getSimOperator();
+ if (TextUtils.isEmpty(simOperator) || TextUtils.isEmpty(mccMnc)) {
+ Log.e(LOG_TAG, "simOperator 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 = simOperator.substring(0, 3);
+ mncCurrent = simOperator.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);
+ InputStream source = null;
+
+ if (cursor == null) {
+ return;
+ }
+ if (cursor.moveToFirst()) {
+ int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
+ if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(columnIndex)) {
+ try {
+ 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);
+ try {
+ source.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ 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) {
+ try {
+ // The current implementation at certain Carriers has the data gzipped, which requires
+ // us to unzip the contents. Longer term, we want to add a flag in carrier config which
+ // determines if the data needs to be zipped or not.
+ GZIPInputStream gunzip = new GZIPInputStream(is);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(gunzip, UTF_8));
+ StringBuilder sb = new StringBuilder();
+
+ String line;
+ while ((line = reader.readLine()) != null) {
+ sb.append(line).append('\n');
+ }
+ return sb.toString();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 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": [ { "certificate": "",
+ * "key-type": "WLAN",
+ * "key-identifier": ""
+ * } ]
+ * @param jsonStr the json string.
+ * @param mccMnc contains the mcc, mnc.
+ */
+ @VisibleForTesting
+ public void parseJsonAndPersistKey(String jsonStr, String mccMnc) {
+ if (TextUtils.isEmpty(jsonStr) || TextUtils.isEmpty(mccMnc)) {
+ Log.e(LOG_TAG, "jsonStr or mcc, mnc: is empty");
+ return;
+ }
+ PemReader reader = null;
+ 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);
+ // This is a hack to accommodate certain carriers who insist on using the public-key
+ // field to store the certificate. We'll just use which-ever is not null.
+ String cert = null;
+ if (key.has(JSON_CERTIFICATE)) {
+ cert = key.getString(JSON_CERTIFICATE);
+ } else {
+ cert = key.getString(JSON_CERTIFICATE_ALTERNATE);
+ }
+ 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;
+ }
+ String identifier = key.getString(JSON_IDENTIFIER);
+ ByteArrayInputStream inStream = new ByteArrayInputStream(cert.getBytes());
+ Reader fReader = new BufferedReader(new InputStreamReader(inStream));
+ reader = new PemReader(fReader);
+ Pair<PublicKey, Long> keyInfo =
+ getKeyInformation(reader.readPemObject().getContent());
+ reader.close();
+ savePublicKey(keyInfo.first, type, identifier, keyInfo.second, mcc, mnc);
+ }
+ } catch (final JSONException e) {
+ Log.e(LOG_TAG, "Json parsing error: " + e.getMessage());
+ } catch (final Exception e) {
+ Log.e(LOG_TAG, "Exception getting certificate: " + e);
+ } finally {
+ try {
+ if (reader != null) {
+ reader.close();
+ }
+ } catch (final Exception e) {
+ Log.e(LOG_TAG, "Exception getting certificate: " + e);
+ }
+ }
+ }
+
+ /**
+ * introspects the mKeyAvailability bitmask
+ * @return true if the digit at position k is 1, else false.
+ */
+ @VisibleForTesting
+ public 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);
+ String mcc = "";
+ String mnc = "";
+ String simOperator = getSimOperator();
+
+ if (!TextUtils.isEmpty(simOperator)) {
+ mcc = simOperator.substring(0, 3);
+ mnc = simOperator.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;
+ }
+
+ /**
+ * Save the public key
+ * @param certificate certificate that contains the public key.
+ * @return Pair containing the Public Key and the expiration date.
+ **/
+ @VisibleForTesting
+ public static Pair<PublicKey, Long> getKeyInformation(byte[] certificate) throws Exception {
+ InputStream inStream = new ByteArrayInputStream(certificate);
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
+ Pair<PublicKey, Long> keyInformation =
+ new Pair(cert.getPublicKey(), cert.getNotAfter().getTime());
+ return keyInformation;
+ }
+
+ /**
+ * Save the public key
+ * @param publicKey public key.
+ * @param type key-type.
+ * @param identifier which is an opaque string.
+ * @param expirationDate expiration date of the key.
+ * @param mcc
+ * @param mnc
+ **/
+ @VisibleForTesting
+ public void savePublicKey(PublicKey publicKey, int type, String identifier, long expirationDate,
+ String mcc, String mnc) {
+ ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo(mcc, mnc, type, identifier,
+ publicKey, new Date(expirationDate));
+ mPhone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index 4acb6e3..77a39eb 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -16,9 +16,9 @@
package com.android.internal.telephony;
-import android.app.PendingIntent;
import android.app.Notification;
import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -31,8 +31,13 @@
import android.telephony.Rlog;
import android.telephony.ServiceState;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.util.NotificationChannelController;
+import java.util.HashMap;
+import java.util.Map;
+
+
/**
* This contains Carrier specific logic based on the states/events
* managed in ServiceStateTracker.
@@ -45,19 +50,28 @@
protected static final int CARRIER_EVENT_VOICE_DEREGISTRATION = CARRIER_EVENT_BASE + 2;
protected static final int CARRIER_EVENT_DATA_REGISTRATION = CARRIER_EVENT_BASE + 3;
protected static final int CARRIER_EVENT_DATA_DEREGISTRATION = CARRIER_EVENT_BASE + 4;
- private static final int SHOW_NOTIFICATION = 200;
- private static final int NOTIFICATION_ID = 1000;
private static final int UNINITIALIZED_DELAY_VALUE = -1;
- private int mDelay = UNINITIALIZED_DELAY_VALUE;
private Phone mPhone;
- private boolean mIsPhoneRegistered = false;
private ServiceStateTracker mSST;
+ public static final int NOTIFICATION_PREF_NETWORK = 1000;
+ public static final int NOTIFICATION_EMERGENCY_NETWORK = 1001;
+
+ private final Map<Integer, NotificationType> mNotificationTypeMap = new HashMap<>();
+
public CarrierServiceStateTracker(Phone phone, ServiceStateTracker sst) {
this.mPhone = phone;
this.mSST = sst;
phone.getContext().registerReceiver(mBroadcastReceiver, new IntentFilter(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ registerNotificationTypes();
+ }
+
+ private void registerNotificationTypes() {
+ mNotificationTypeMap.put(NOTIFICATION_PREF_NETWORK,
+ new PrefNetworkNotification(NOTIFICATION_PREF_NETWORK));
+ mNotificationTypeMap.put(NOTIFICATION_EMERGENCY_NETWORK,
+ new EmergencyNetworkNotification(NOTIFICATION_EMERGENCY_NETWORK));
}
@Override
@@ -65,19 +79,17 @@
switch (msg.what) {
case CARRIER_EVENT_VOICE_REGISTRATION:
case CARRIER_EVENT_DATA_REGISTRATION:
- mIsPhoneRegistered = true;
- handleConfigChanges();
- break;
case CARRIER_EVENT_VOICE_DEREGISTRATION:
case CARRIER_EVENT_DATA_DEREGISTRATION:
- if (isGlobalModeOrRadioOffOrAirplaneMode() || isPhoneStillRegistered()) {
- break;
- }
- mIsPhoneRegistered = false;
handleConfigChanges();
break;
- case SHOW_NOTIFICATION:
- sendNotification();
+ case NOTIFICATION_EMERGENCY_NETWORK:
+ case NOTIFICATION_PREF_NETWORK:
+ Rlog.d(LOG_TAG, "sending notification after delay: " + msg.what);
+ NotificationType notificationType = mNotificationTypeMap.get(msg.what);
+ if (notificationType != null) {
+ sendNotification(notificationType);
+ }
break;
}
}
@@ -90,48 +102,97 @@
|| mSST.mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE);
}
+ private boolean isPhoneVoiceRegistered() {
+ if (mSST.mSS == null) {
+ return true; //something has gone wrong, return true and not show the notification.
+ }
+ return (mSST.mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE);
+ }
+
+ private boolean isPhoneRegisteredForWifiCalling() {
+ Rlog.d(LOG_TAG, "isPhoneRegisteredForWifiCalling: " + mPhone.isWifiCallingEnabled());
+ return mPhone.isWifiCallingEnabled();
+ }
+
/**
- * Returns true if the preferred network is set to 'Global' or the radio is off or in
- * Airplane Mode else returns false.
+ * Returns true if the radio is off or in Airplane Mode else returns false.
*/
- private boolean isGlobalModeOrRadioOffOrAirplaneMode() {
+ @VisibleForTesting
+ public boolean isRadioOffOrAirplaneMode() {
+ Context context = mPhone.getContext();
+ int airplaneMode = -1;
+ try {
+ airplaneMode = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0);
+ } catch (Exception e) {
+ Rlog.e(LOG_TAG, "Unable to get AIRPLACE_MODE_ON.");
+ return true;
+ }
+ return (!mSST.isRadioOn() || (airplaneMode != 0));
+ }
+
+ /**
+ * Returns true if the preferred network is set to 'Global'.
+ */
+ private boolean isGlobalMode() {
Context context = mPhone.getContext();
int preferredNetworkSetting = -1;
- int airplaneMode = -1;
- int subId = mPhone.getSubId();
try {
preferredNetworkSetting =
android.provider.Settings.Global.getInt(context.getContentResolver(),
- android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId,
- Phone.PREFERRED_NT_MODE);
- airplaneMode = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0);
+ android.provider.Settings.Global.PREFERRED_NETWORK_MODE
+ + mPhone.getSubId(), Phone.PREFERRED_NT_MODE);
} catch (Exception e) {
Rlog.e(LOG_TAG, "Unable to get PREFERRED_NETWORK_MODE.");
return true;
}
- return ((preferredNetworkSetting == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA) ||
- !mSST.isRadioOn() || (airplaneMode != 0));
+ return (preferredNetworkSetting == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
+ }
+
+ private void handleConfigChanges() {
+ for (Map.Entry<Integer, NotificationType> entry : mNotificationTypeMap.entrySet()) {
+ NotificationType notificationType = entry.getValue();
+ if (evaluateSendingMessage(notificationType)) {
+ Message notificationMsg = obtainMessage(notificationType.getTypeId(), null);
+ Rlog.i(LOG_TAG, "starting timer for notifications." + notificationType.getTypeId());
+ sendMessageDelayed(notificationMsg, getDelay(notificationType));
+ } else {
+ cancelNotification(notificationType.getTypeId());
+ Rlog.i(LOG_TAG, "canceling notifications: " + notificationType.getTypeId());
+ }
+ }
}
/**
- * Contains logic to decide when to create/cancel notifications.
- */
- private void handleConfigChanges() {
- if (mDelay == UNINITIALIZED_DELAY_VALUE) {
- cancelNotification();
- return;
- }
- // send a notification if the device is registerd to a network.
- if (mIsPhoneRegistered) {
- cancelNotification();
- Rlog.i(LOG_TAG, "canceling all notifications. ");
- } else {
- Message notificationMsg;
- notificationMsg = obtainMessage(SHOW_NOTIFICATION, null);
- Rlog.i(LOG_TAG, "starting timer for notifications. ");
- sendMessageDelayed(notificationMsg, mDelay);
- }
+ * This method adds a level of indirection, and was created so we can unit the class.
+ **/
+ @VisibleForTesting
+ public boolean evaluateSendingMessage(NotificationType notificationType) {
+ return notificationType.sendMessage();
+ }
+
+ /**
+ * This method adds a level of indirection, and was created so we can unit the class.
+ **/
+ @VisibleForTesting
+ public int getDelay(NotificationType notificationType) {
+ return notificationType.getDelay();
+ }
+
+ /**
+ * This method adds a level of indirection, and was created so we can unit the class.
+ **/
+ @VisibleForTesting
+ public Notification.Builder getNotificationBuilder(NotificationType notificationType) {
+ return notificationType.getNotificationBuilder();
+ }
+
+ /**
+ * This method adds a level of indirection, and was created so we can unit the class.
+ **/
+ @VisibleForTesting
+ public NotificationManager getNotificationManager(Context context) {
+ return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -139,9 +200,12 @@
public void onReceive(Context context, Intent intent) {
CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- PersistableBundle b = carrierConfigManager.getConfig();
- mDelay = b.getInt(CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT);
- Rlog.i(LOG_TAG, "reading time to delay notification: " + mDelay);
+ PersistableBundle b = carrierConfigManager.getConfigForSubId(mPhone.getSubId());
+
+ for (Map.Entry<Integer, NotificationType> entry : mNotificationTypeMap.entrySet()) {
+ NotificationType notificationType = entry.getValue();
+ notificationType.setDelay(b);
+ }
handleConfigChanges();
}
};
@@ -149,56 +213,195 @@
/**
* Post a notification to the NotificationManager for changing network type.
*/
- private void sendNotification() {
- Context context = mPhone.getContext();
-
- Rlog.i(LOG_TAG, "w/values: " + "," + mIsPhoneRegistered + "," + mDelay
- + "," + isGlobalModeOrRadioOffOrAirplaneMode() + "," + mSST.isRadioOn());
-
- // exit if the network preference is set to Global or if the phone is registered.
- if (isGlobalModeOrRadioOffOrAirplaneMode() || mIsPhoneRegistered) {
+ @VisibleForTesting
+ public void sendNotification(NotificationType notificationType) {
+ if (!evaluateSendingMessage(notificationType)) {
return;
}
- NotificationManager notificationManager = (NotificationManager)
- context.getSystemService(Context.NOTIFICATION_SERVICE);
-
-
- Intent notificationIntent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
- PendingIntent settingsIntent = PendingIntent.getActivity(context, 0, notificationIntent,
- PendingIntent.FLAG_ONE_SHOT);
-
- CharSequence title =
- context.getText(com.android.internal.R.string.NetworkPreferenceSwitchTitle);
- CharSequence details =
- context.getText(com.android.internal.R.string.NetworkPreferenceSwitchSummary);
-
-
- Notification mNotification = new Notification.Builder(context)
- .setWhen(System.currentTimeMillis())
+ Context context = mPhone.getContext();
+ Notification.Builder builder = getNotificationBuilder(notificationType);
+ // set some common attributes
+ builder.setWhen(System.currentTimeMillis())
.setAutoCancel(true)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
- .setContentTitle(title)
.setColor(context.getResources().getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setStyle(new Notification.BigTextStyle().bigText(details))
- .setContentText(details)
- .setContentIntent(settingsIntent)
- .setChannel(NotificationChannelController.CHANNEL_ID_ALERT)
- .build();
+ com.android.internal.R.color.system_notification_accent_color));
- notificationManager.notify(NOTIFICATION_ID, mNotification);
+ getNotificationManager(context).notify(notificationType.getTypeId(), builder.build());
}
/**
* Cancel notifications if a registration is pending or has been sent.
- */
- private void cancelNotification() {
+ **/
+ public void cancelNotification(int notificationId) {
Context context = mPhone.getContext();
- mIsPhoneRegistered = true;
- removeMessages(SHOW_NOTIFICATION);
- NotificationManager notificationManager = (NotificationManager)
- context.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(NOTIFICATION_ID);
+ removeMessages(notificationId);
+ getNotificationManager(context).cancel(notificationId);
+ }
+
+ /**
+ * Class that defines the different types of notifications.
+ */
+ public interface NotificationType {
+
+ /**
+ * decides if the message should be sent, Returns boolean
+ **/
+ boolean sendMessage();
+
+ /**
+ * returns the interval by which the message is delayed.
+ **/
+ int getDelay();
+
+ /** sets the interval by which the message is delayed.
+ * @param bundle PersistableBundle
+ **/
+ void setDelay(PersistableBundle bundle);
+
+ /**
+ * returns notification type id.
+ **/
+ int getTypeId();
+
+ /**
+ * returns the notification builder, for the notification to be displayed.
+ **/
+ Notification.Builder getNotificationBuilder();
+ }
+
+ /**
+ * Class that defines the network notification, which is shown when the phone cannot camp on
+ * a network, and has 'preferred mode' set to global.
+ */
+ public class PrefNetworkNotification implements NotificationType {
+
+ private final int mTypeId;
+ private int mDelay = UNINITIALIZED_DELAY_VALUE;
+
+ PrefNetworkNotification(int typeId) {
+ this.mTypeId = typeId;
+ }
+
+ /** sets the interval by which the message is delayed.
+ * @param bundle PersistableBundle
+ **/
+ public void setDelay(PersistableBundle bundle) {
+ if (bundle == null) {
+ Rlog.e(LOG_TAG, "bundle is null");
+ return;
+ }
+ this.mDelay = bundle.getInt(
+ CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT);
+ Rlog.i(LOG_TAG, "reading time to delay notification emergency: " + mDelay);
+ }
+
+ public int getDelay() {
+ return mDelay;
+ }
+
+ public int getTypeId() {
+ return mTypeId;
+ }
+
+ /**
+ * Contains logic on sending notifications.
+ */
+ public boolean sendMessage() {
+ Rlog.i(LOG_TAG, "PrefNetworkNotification: sendMessage() w/values: "
+ + "," + isPhoneStillRegistered() + "," + mDelay + "," + isGlobalMode()
+ + "," + mSST.isRadioOn());
+ if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered() || isGlobalMode()
+ || isRadioOffOrAirplaneMode()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Builds a partial notificaiton builder, and returns it.
+ */
+ public Notification.Builder getNotificationBuilder() {
+ Context context = mPhone.getContext();
+ Intent notificationIntent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
+ PendingIntent settingsIntent = PendingIntent.getActivity(context, 0, notificationIntent,
+ PendingIntent.FLAG_ONE_SHOT);
+ CharSequence title = context.getText(
+ com.android.internal.R.string.NetworkPreferenceSwitchTitle);
+ CharSequence details = context.getText(
+ com.android.internal.R.string.NetworkPreferenceSwitchSummary);
+ return new Notification.Builder(context)
+ .setContentTitle(title)
+ .setStyle(new Notification.BigTextStyle().bigText(details))
+ .setContentText(details)
+ .setChannel(NotificationChannelController.CHANNEL_ID_ALERT)
+ .setContentIntent(settingsIntent);
+ }
+ }
+
+ /**
+ * Class that defines the emergency notification, which is shown when the user is out of cell
+ * connectivity, but has wifi enabled.
+ */
+ public class EmergencyNetworkNotification implements NotificationType {
+
+ private final int mTypeId;
+ private int mDelay = UNINITIALIZED_DELAY_VALUE;
+
+ EmergencyNetworkNotification(int typeId) {
+ this.mTypeId = typeId;
+ }
+
+ /** sets the interval by which the message is delayed.
+ * @param bundle PersistableBundle
+ **/
+ public void setDelay(PersistableBundle bundle) {
+ if (bundle == null) {
+ Rlog.e(LOG_TAG, "bundle is null");
+ return;
+ }
+ this.mDelay = bundle.getInt(
+ CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT);
+ Rlog.i(LOG_TAG, "reading time to delay notification emergency: " + mDelay);
+ }
+
+ public int getDelay() {
+ return mDelay;
+ }
+
+ public int getTypeId() {
+ return mTypeId;
+ }
+
+ /**
+ * Contains logic on sending notifications,
+ */
+ public boolean sendMessage() {
+ Rlog.i(LOG_TAG, "EmergencyNetworkNotification: sendMessage() w/values: "
+ + "," + isPhoneVoiceRegistered() + "," + mDelay + ","
+ + isPhoneRegisteredForWifiCalling() + "," + mSST.isRadioOn());
+ if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneVoiceRegistered()
+ || !isPhoneRegisteredForWifiCalling()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Builds a partial notificaiton builder, and returns it.
+ */
+ public Notification.Builder getNotificationBuilder() {
+ Context context = mPhone.getContext();
+ CharSequence title = context.getText(
+ com.android.internal.R.string.EmergencyCallWarningTitle);
+ CharSequence details = context.getText(
+ com.android.internal.R.string.EmergencyCallWarningSummary);
+ return new Notification.Builder(context)
+ .setContentTitle(title)
+ .setStyle(new Notification.BigTextStyle().bigText(details))
+ .setContentText(details)
+ .setChannel(NotificationChannelController.CHANNEL_ID_WFC);
+ }
}
}
diff --git a/src/java/com/android/internal/telephony/ClientWakelockTracker.java b/src/java/com/android/internal/telephony/ClientWakelockTracker.java
index 5bec60b..fa71e76 100644
--- a/src/java/com/android/internal/telephony/ClientWakelockTracker.java
+++ b/src/java/com/android/internal/telephony/ClientWakelockTracker.java
@@ -18,10 +18,10 @@
import android.os.SystemClock;
import android.telephony.ClientRequestStats;
-import android.telephony.Rlog;
import com.android.internal.annotations.VisibleForTesting;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -119,13 +119,13 @@
return false;
}
- void dumpClientRequestTracker() {
- Rlog.d(RIL.RILJ_LOG_TAG, "-------mClients---------------");
+ void dumpClientRequestTracker(PrintWriter pw) {
+ pw.println("-------mClients---------------");
synchronized (mClients) {
for (String key : mClients.keySet()) {
- Rlog.d(RIL.RILJ_LOG_TAG, "Client : " + key);
- Rlog.d(RIL.RILJ_LOG_TAG, mClients.get(key).toString());
+ pw.println("Client : " + key);
+ pw.println(mClients.get(key).toString());
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index bb92692..245f76c 100644
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -311,6 +311,15 @@
}
/**
+ * Sets the Connection connect time in {@link SystemClock#elapsedRealtime()} format.
+ *
+ * @param connectTimeReal the new connect time.
+ */
+ public void setConnectTimeReal(long connectTimeReal) {
+ mConnectTimeReal = connectTimeReal;
+ }
+
+ /**
* Connection connect time in elapsedRealtime() format.
* For outgoing calls: Begins at (DIALING|ALERTING) -> ACTIVE transition.
* For incoming calls: Begins at (INCOMING|WAITING) -> ACTIVE transition.
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index c13e540..98c0a32 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -125,6 +125,9 @@
int subId = sender.getSubId();
try {
if (mRegistry != null) {
+ Rlog.d(LOG_TAG, "notifyCallForwardingChanged: subId=" + subId + ", isCFActive="
+ + sender.getCallForwardingIndicator());
+
mRegistry.notifyCallForwardingChangedForSubscriber(subId,
sender.getCallForwardingIndicator());
}
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index b91f452..e4e7f35 100755
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -40,6 +40,7 @@
import android.text.TextUtils;
import android.util.EventLog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
import com.android.internal.telephony.metrics.TelephonyMetrics;
@@ -68,7 +69,8 @@
private static final int MAX_CONNECTIONS_PER_CALL_CDMA = 1; //only 1 connection allowed per call
//***** Instance Variables
- private GsmCdmaConnection mConnections[];
+ @VisibleForTesting
+ public GsmCdmaConnection[] mConnections;
private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
@@ -168,6 +170,11 @@
if (mPhone.isPhoneTypeGsm()) {
mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_GSM];
mCi.unregisterForCallWaitingInfo(this);
+ // Prior to phone switch to GSM, if CDMA has any emergency call
+ // data will be in disabled state, after switching to GSM enable data.
+ if (mIsInEmergencyCall) {
+ mPhone.mDcTracker.setInternalDataEnabled(true);
+ }
} else {
mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_CDMA];
mPendingCallInEcm = false;
@@ -182,10 +189,9 @@
private void reset() {
Rlog.d(LOG_TAG, "reset");
- clearDisconnected();
-
for (GsmCdmaConnection gsmCdmaConnection : mConnections) {
if (gsmCdmaConnection != null) {
+ gsmCdmaConnection.onDisconnect(DisconnectCause.ERROR_UNSPECIFIED);
gsmCdmaConnection.dispose();
}
}
@@ -196,7 +202,7 @@
mConnections = null;
mPendingMO = null;
- mState = PhoneConstants.State.IDLE;
+ clearDisconnected();
}
@Override
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 4251567..5053341 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) {
@@ -1524,12 +1525,12 @@
@Override
public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
- return CarrierInfoManager.getCarrierInfoForImsiEncryption(keyType);
+ return CarrierInfoManager.getCarrierInfoForImsiEncryption(keyType, mContext);
}
@Override
public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
- CarrierInfoManager.setCarrierInfoForImsiEncryption(imsiEncryptionInfo);
+ CarrierInfoManager.setCarrierInfoForImsiEncryption(imsiEncryptionInfo, mContext);
}
@Override
@@ -1853,6 +1854,15 @@
}
@Override
+ public void setTTYMode(int ttyMode, Message onComplete) {
+ // Send out the TTY Mode change over RIL as well
+ super.setTTYMode(ttyMode, onComplete);
+ if (mImsPhone != null) {
+ mImsPhone.setTTYMode(ttyMode, onComplete);
+ }
+ }
+
+ @Override
public void setUiTTYMode(int uiTtyMode, Message onComplete) {
if (mImsPhone != null) {
mImsPhone.setUiTTYMode(uiTtyMode, onComplete);
@@ -2539,6 +2549,7 @@
private void processIccRecordEvents(int eventCode) {
switch (eventCode) {
case IccRecords.EVENT_CFI:
+ logi("processIccRecordEvents: EVENT_CFI");
notifyCallForwardingIndicator();
break;
}
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 934ecff..99fd965 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -159,6 +159,17 @@
/** New SMS received as an AsyncResult. */
public static final int EVENT_INJECT_SMS = 8;
+ /** Update tracker object; used only in waiting state */
+ private static final int EVENT_UPDATE_TRACKER = 9;
+
+ /** Timeout in case state machine is stuck in a state for too long; used only in waiting
+ * state */
+ private static final int EVENT_STATE_TIMEOUT = 10;
+
+ /** Timeout duration for EVENT_STATE_TIMEOUT (5 minutes) */
+ @VisibleForTesting
+ public static final int STATE_TIMEOUT = 5 * 60 * 1000;
+
/** Wakelock release delay when returning to idle state. */
private static final int WAKELOCK_TIMEOUT = 3000;
@@ -451,6 +462,7 @@
// if any broadcasts were sent, transition to waiting state
InboundSmsTracker inboundSmsTracker = (InboundSmsTracker) msg.obj;
if (processMessagePart(inboundSmsTracker)) {
+ sendMessage(EVENT_UPDATE_TRACKER, inboundSmsTracker);
transitionTo(mWaitingState);
} else {
// if event is sent from SmsBroadcastUndelivered.broadcastSms(), and
@@ -494,18 +506,41 @@
* {@link IdleState} after any deferred {@link #EVENT_BROADCAST_SMS} messages are handled.
*/
private class WaitingState extends State {
+ private InboundSmsTracker mTracker;
+
+ @Override
+ public void enter() {
+ if (DBG) log("entering Waiting state");
+ mTracker = null;
+ sendMessageDelayed(EVENT_STATE_TIMEOUT, STATE_TIMEOUT);
+ }
+
@Override
public void exit() {
if (DBG) log("exiting Waiting state");
// Before moving to idle state, set wakelock timeout to WAKE_LOCK_TIMEOUT milliseconds
// to give any receivers time to take their own wake locks
setWakeLockTimeout(WAKELOCK_TIMEOUT);
+ if (VDBG) {
+ if (hasMessages(EVENT_STATE_TIMEOUT)) {
+ log("exiting Waiting state: removing EVENT_STATE_TIMEOUT from message queue");
+ }
+ if (hasMessages(EVENT_UPDATE_TRACKER)) {
+ log("exiting Waiting state: removing EVENT_UPDATE_TRACKER from message queue");
+ }
+ }
+ removeMessages(EVENT_STATE_TIMEOUT);
+ removeMessages(EVENT_UPDATE_TRACKER);
}
@Override
public boolean processMessage(Message msg) {
log("WaitingState.processMessage:" + msg.what);
switch (msg.what) {
+ case EVENT_UPDATE_TRACKER:
+ mTracker = (InboundSmsTracker) msg.obj;
+ return HANDLED;
+
case EVENT_BROADCAST_SMS:
// defer until the current broadcast completes
deferMessage(msg);
@@ -521,6 +556,18 @@
// not ready to return to idle; ignore
return HANDLED;
+ case EVENT_STATE_TIMEOUT:
+ // stuck in WaitingState for too long; drop the message and exit this state
+ if (mTracker != null) {
+ log("WaitingState.processMessage: EVENT_STATE_TIMEOUT; dropping message");
+ dropSms(new SmsBroadcastReceiver(mTracker));
+ } else {
+ log("WaitingState.processMessage: EVENT_STATE_TIMEOUT; mTracker is null "
+ + "- sending EVENT_BROADCAST_COMPLETE");
+ sendMessage(EVENT_BROADCAST_COMPLETE);
+ }
+ return HANDLED;
+
default:
// parent state handles the other message types
return NOT_HANDLED;
diff --git a/src/java/com/android/internal/telephony/MccTable.java b/src/java/com/android/internal/telephony/MccTable.java
index 51ae81e..5714b29 100644
--- a/src/java/com/android/internal/telephony/MccTable.java
+++ b/src/java/com/android/internal/telephony/MccTable.java
@@ -376,7 +376,14 @@
*/
private static void setTimezoneFromMccIfNeeded(Context context, int mcc) {
String timezone = SystemProperties.get(ServiceStateTracker.TIMEZONE_PROPERTY);
- if (timezone == null || timezone.length() == 0) {
+ // timezone.equals("GMT") will be true and only true if the timezone was
+ // set to a default value by the system server (when starting, system server.
+ // sets the persist.sys.timezone to "GMT" if it's not set)."GMT" is not used by
+ // any code that sets it explicitly (in case where something sets GMT explicitly,
+ // "Etc/GMT" Olsen ID would be used).
+ // TODO(b/64056758): Remove "timezone.equals("GMT")" hack when there's a
+ // better way of telling if the value has been defaulted.
+ if (timezone == null || timezone.length() == 0 || timezone.equals("GMT")) {
String zoneId = defaultTimeZoneForMcc(mcc);
if (zoneId != null && zoneId.length() > 0) {
// Set time zone based on MCC
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 0238793..8b0c677 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -56,11 +56,13 @@
import android.telephony.SubscriptionManager;
import android.telephony.VoLteServiceState;
import android.text.TextUtils;
+import android.util.Log;
import com.android.ims.ImsCall;
import com.android.ims.ImsConfig;
import com.android.ims.ImsManager;
import com.android.internal.R;
+import com.android.internal.telephony.dataconnection.DataConnectionReasons;
import com.android.internal.telephony.dataconnection.DcTracker;
import com.android.internal.telephony.imsphone.ImsPhoneCall;
import com.android.internal.telephony.test.SimulatedRadioControl;
@@ -1788,7 +1790,7 @@
int status = enable ? IccRecords.CALL_FORWARDING_STATUS_ENABLED :
IccRecords.CALL_FORWARDING_STATUS_DISABLED;
int subId = getSubId();
- Rlog.d(LOG_TAG, "setCallForwardingIndicatorInSharedPref: Storing status = " + status +
+ Rlog.i(LOG_TAG, "setCallForwardingIndicatorInSharedPref: Storing status = " + status +
" in pref " + CF_STATUS + subId);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
@@ -1830,6 +1832,9 @@
if (callForwardingIndicator == IccRecords.CALL_FORWARDING_STATUS_UNKNOWN) {
callForwardingIndicator = getCallForwardingIndicatorFromSharedPref();
}
+ Rlog.v(LOG_TAG, "getCallForwardingIndicator: iccForwardingFlag=" + (r != null
+ ? r.getVoiceCallForwardingFlag() : "null") + ", sharedPrefFlag="
+ + getCallForwardingIndicatorFromSharedPref());
return (callForwardingIndicator == IccRecords.CALL_FORWARDING_STATUS_ENABLED);
}
@@ -2790,12 +2795,25 @@
/**
* Report on whether data connectivity is allowed.
+ *
+ * @return True if data is allowed to be established.
*/
public boolean isDataAllowed() {
return ((mDcTracker != null) && (mDcTracker.isDataAllowed(null)));
}
/**
+ * Report on whether data connectivity is allowed.
+ *
+ * @param reasons The reasons that data can/can't be established. This is an output param.
+ * @return True if data is allowed to be established
+ */
+ public boolean isDataAllowed(DataConnectionReasons reasons) {
+ return ((mDcTracker != null) && (mDcTracker.isDataAllowed(reasons)));
+ }
+
+
+ /**
* Action set from carrier signalling broadcast receivers to enable/disable metered apns.
*/
public void carrierActionSetMeteredApnsEnabled(boolean enabled) {
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 3ed591f..226ee8e 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -4910,7 +4910,7 @@
}
pw.println(" mLastNITZTimeInfo=" + Arrays.toString(mLastNITZTimeInfo));
pw.println(" mTestingEmergencyCall=" + mTestingEmergencyCall.get());
- mClientWakelockTracker.dumpClientRequestTracker();
+ mClientWakelockTracker.dumpClientRequestTracker(pw);
}
public List<ClientRequestStats> getClientRequestStats() {
diff --git a/src/java/com/android/internal/telephony/RetryManager.java b/src/java/com/android/internal/telephony/RetryManager.java
index 684de9a..23c3498 100644
--- a/src/java/com/android/internal/telephony/RetryManager.java
+++ b/src/java/com/android/internal/telephony/RetryManager.java
@@ -27,8 +27,6 @@
import com.android.internal.telephony.dataconnection.ApnSetting;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Random;
@@ -112,6 +110,11 @@
private static final long DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING = 3000;
/**
+ * The default value (in milliseconds) for retrying APN after disconnect
+ */
+ private static final long DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY = 10000;
+
+ /**
* The value indicating no retry is needed
*/
public static final long NO_RETRY = -1;
@@ -141,6 +144,12 @@
private long mFailFastInterApnDelay;
/**
+ * The delay (in milliseconds) for APN retrying after disconnect (e.g. Modem suddenly reports
+ * data call lost)
+ */
+ private long mApnRetryAfterDisconnectDelay;
+
+ /**
* Modem suggested delay for retrying the current APN
*/
private long mModemSuggestedDelay = NO_SUGGESTED_RETRY_DELAY;
@@ -337,6 +346,9 @@
mFailFastInterApnDelay = b.getLong(
CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG,
DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING);
+ mApnRetryAfterDisconnectDelay = b.getLong(
+ CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG,
+ DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY);
// Load all retry patterns for all different APNs.
String[] allConfigStrings = b.getStringArray(
@@ -645,44 +657,21 @@
}
/**
- * Get the delay between APN setting trying. This is the fixed delay used for APN setting trying
- * within the same round, comparing to the exponential delay used for different rounds.
- * @param failFastEnabled True if fail fast mode enabled, which a shorter delay will be used
+ * Get the delay in milliseconds for APN retry after disconnect
* @return The delay in milliseconds
*/
- public long getInterApnDelay(boolean failFastEnabled) {
- return (failFastEnabled) ? mFailFastInterApnDelay : mInterApnDelay;
+ public long getRetryAfterDisconnectDelay() {
+ return mApnRetryAfterDisconnectDelay;
}
public String toString() {
- return "mApnType=" + mApnType + " mRetryCount=" + mRetryCount +
- " mMaxRetryCount=" + mMaxRetryCount + " mCurrentApnIndex=" + mCurrentApnIndex +
- " mSameApnRtryCount=" + mSameApnRetryCount + " mModemSuggestedDelay=" +
- mModemSuggestedDelay + " mRetryForever=" + mRetryForever +
- " mConfig={" + mConfig + "}";
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(" RetryManager");
- pw.println("***************************************");
-
- pw.println(" config = " + mConfig);
- pw.println(" mApnType = " + mApnType);
- pw.println(" mCurrentApnIndex = " + mCurrentApnIndex);
- pw.println(" mRetryCount = " + mRetryCount);
- pw.println(" mMaxRetryCount = " + mMaxRetryCount);
- pw.println(" mSameApnRetryCount = " + mSameApnRetryCount);
- pw.println(" mModemSuggestedDelay = " + mModemSuggestedDelay);
-
- if (mWaitingApns != null) {
- pw.println(" APN list: ");
- for (int i = 0; i < mWaitingApns.size(); i++) {
- pw.println(" [" + i + "]=" + mWaitingApns.get(i));
- }
- }
-
- pw.println("***************************************");
- pw.flush();
+ if (mConfig == null) return "";
+ return "RetryManager: mApnType=" + mApnType + " mRetryCount=" + mRetryCount
+ + " mMaxRetryCount=" + mMaxRetryCount + " mCurrentApnIndex=" + mCurrentApnIndex
+ + " mSameApnRtryCount=" + mSameApnRetryCount + " mModemSuggestedDelay="
+ + mModemSuggestedDelay + " mRetryForever=" + mRetryForever + " mInterApnDelay="
+ + mInterApnDelay + " mApnRetryAfterDisconnectDelay=" + mApnRetryAfterDisconnectDelay
+ + " mConfig={" + mConfig + "}";
}
private void log(String s) {
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index d1e8a0c..2ec5101 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -23,6 +23,8 @@
import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
+import static android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
+import static android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -74,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;
@@ -313,7 +316,25 @@
case EVENT_STOP_SENDING:
{
SmsTracker tracker = (SmsTracker) msg.obj;
- tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/);
+ if (msg.arg1 == ConfirmDialogListener.SHORT_CODE_MSG) {
+ if (msg.arg2 == ConfirmDialogListener.NEVER_ALLOW) {
+ tracker.onFailed(mContext,
+ RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, 0/*errorCode*/);
+ Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+ + "sending SHORT_CODE_NEVER_ALLOWED error code.");
+ } else {
+ tracker.onFailed(mContext,
+ RESULT_ERROR_SHORT_CODE_NOT_ALLOWED, 0/*errorCode*/);
+ Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+ + "sending SHORT_CODE_NOT_ALLOWED error code.");
+ }
+ } else if (msg.arg1 == ConfirmDialogListener.RATE_LIMIT) {
+ tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/);
+ Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+ + "sending LIMIT_EXCEEDED error code.");
+ } else {
+ Rlog.e(TAG, "SMSDispatcher: EVENT_STOP_SENDING - unexpected cases.");
+ }
mPendingTrackerCount--;
break;
}
@@ -950,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");
@@ -1055,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;
@@ -1068,7 +1090,10 @@
case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW:
Rlog.w(TAG, "User denied this app from sending to premium SMS");
- sendMessage(obtainMessage(EVENT_STOP_SENDING, tracker));
+ Message msg = obtainMessage(EVENT_STOP_SENDING, tracker);
+ msg.arg1 = ConfirmDialogListener.SHORT_CODE_MSG;
+ msg.arg2 = ConfirmDialogListener.NEVER_ALLOW;
+ sendMessage(msg);
return false; // reject this message
case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER:
@@ -1127,11 +1152,13 @@
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));
- ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null);
+ // Construct ConfirmDialogListenter for Rate Limit handling
+ ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null,
+ ConfirmDialogListener.RATE_LIMIT);
AlertDialog d = new AlertDialog.Builder(mContext)
.setTitle(R.string.sms_control_title)
@@ -1163,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));
@@ -1172,8 +1199,10 @@
Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.sms_short_code_confirmation_dialog, null);
+ // Construct ConfirmDialogListenter for short code message sending
ConfirmDialogListener listener = new ConfirmDialogListener(tracker,
- (TextView)layout.findViewById(R.id.sms_short_code_remember_undo_instruction));
+ (TextView) layout.findViewById(R.id.sms_short_code_remember_undo_instruction),
+ ConfirmDialogListener.SHORT_CODE_MSG);
TextView messageView = (TextView) layout.findViewById(R.id.sms_short_code_confirm_message);
@@ -1374,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) {
@@ -1634,10 +1671,15 @@
private Button mNegativeButton;
private boolean mRememberChoice; // default is unchecked
private final TextView mRememberUndoInstruction;
+ private int mConfirmationType; // 0 - Short Code Msg Sending; 1 - Rate Limit Exceeded
+ private static final int SHORT_CODE_MSG = 0; // Short Code Msg
+ private static final int RATE_LIMIT = 1; // Rate Limit Exceeded
+ private static final int NEVER_ALLOW = 1; // Never Allow
- ConfirmDialogListener(SmsTracker tracker, TextView textView) {
+ ConfirmDialogListener(SmsTracker tracker, TextView textView, int confirmationType) {
mTracker = tracker;
mRememberUndoInstruction = textView;
+ mConfirmationType = confirmationType;
}
void setPositiveButton(Button button) {
@@ -1670,18 +1712,23 @@
EventLog.writeEvent(EventLogTags.EXP_DET_SMS_DENIED_BY_USER,
mTracker.mAppInfo.applicationInfo == null ?
-1 : mTracker.mAppInfo.applicationInfo.uid);
- sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
+ Message msg = obtainMessage(EVENT_STOP_SENDING, mTracker);
+ msg.arg1 = mConfirmationType;
if (mRememberChoice) {
newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW;
+ msg.arg2 = ConfirmDialogListener.NEVER_ALLOW;
}
+ sendMessage(msg);
}
- setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission);
+ setPremiumSmsPermission(mTracker.getAppPackageName(), newSmsPermission);
}
@Override
public void onCancel(DialogInterface dialog) {
Rlog.d(TAG, "dialog dismissed: don't send SMS");
- sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
+ Message msg = obtainMessage(EVENT_STOP_SENDING, mTracker);
+ msg.arg1 = mConfirmationType;
+ sendMessage(msg);
}
@Override
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index c4d048d..d3c17dd 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -87,6 +87,7 @@
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.util.NotificationChannelController;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor;
@@ -209,6 +210,7 @@
protected static final int EVENT_ALL_DATA_DISCONNECTED = 49;
protected static final int EVENT_PHONE_TYPE_SWITCHED = 50;
protected static final int EVENT_RADIO_POWER_FROM_CARRIER = 51;
+ protected static final int EVENT_SIM_NOT_INSERTED = 52;
protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
@@ -353,6 +355,14 @@
}
// update voicemail count and notify message waiting changed
mPhone.updateVoiceMail();
+
+ // cancel notifications if we see SIM_NOT_INSERTED (This happens on bootup before
+ // the SIM is first detected and then subsequently on SIM removals)
+ if (mSubscriptionController.getSlotIndex(subId)
+ == SubscriptionManager.SIM_NOT_INSERTED) {
+ // this is handled on the main thread to mitigate racing with setNotification().
+ sendMessage(obtainMessage(EVENT_SIM_NOT_INSERTED));
+ }
}
}
};
@@ -445,7 +455,6 @@
public static final int CS_NORMAL_ENABLED = 1005; // Access Control blocks normal voice/sms service
public static final int CS_EMERGENCY_ENABLED = 1006; // Access Control blocks emergency call service
public static final int CS_REJECT_CAUSE_ENABLED = 2001; // Notify MM rejection cause
- public static final int CS_REJECT_CAUSE_DISABLED = 2002; // Cancel MM rejection cause
/** Notification id. */
public static final int PS_NOTIFICATION = 888; // Id to update and cancel PS restricted
public static final int CS_NOTIFICATION = 999; // Id to update and cancel CS restricted
@@ -1296,6 +1305,14 @@
}
break;
+ case EVENT_SIM_NOT_INSERTED:
+ if (DBG) log("EVENT_SIM_NOT_INSERTED");
+ cancelAllNotifications();
+ mMdn = null;
+ mMin = null;
+ mIsMinInfoReady = false;
+ break;
+
case EVENT_ALL_DATA_DISCONNECTED:
int dds = SubscriptionManager.getDefaultDataSubscriptionId();
ProxyController.getInstance().unregisterForAllDataDisconnected(dds, this);
@@ -2819,7 +2836,7 @@
}
if (hasRejectCauseChanged) {
- setNotification(mRejectCode == 0 ? CS_REJECT_CAUSE_DISABLED : CS_REJECT_CAUSE_ENABLED);
+ setNotification(CS_REJECT_CAUSE_ENABLED);
}
if (hasChanged) {
@@ -3181,8 +3198,6 @@
+ mNeedFixZoneAfterNitz + " zone=" + (zone != null ? zone.getID() : "NULL");
mTimeZoneLog.log(tmpLog);
- mNeedFixZoneAfterNitz = false;
-
if (zone != null) {
log("fixTimeZone: zone != null zone.getID=" + zone.getID());
if (getAutoTimeZone()) {
@@ -3190,10 +3205,13 @@
} else {
log("fixTimeZone: skip changing zone as getAutoTimeZone was false");
}
- saveNitzTimeZone(zone.getID());
+ if (mNeedFixZoneAfterNitz) {
+ saveNitzTimeZone(zone.getID());
+ }
} else {
log("fixTimeZone: zone == null, do nothing for zone");
}
+ mNeedFixZoneAfterNitz = false;
}
/**
@@ -3312,7 +3330,8 @@
/**
* Do not set roaming state in case of oprators considered non-roaming.
*
- * Can use mcc or mcc+mnc as item of config_operatorConsideredNonRoaming.
+ * Can use mcc or mcc+mnc as item of
+ * {@link CarrierConfigManager#KEY_NON_ROAMING_OPERATOR_STRING_ARRAY}.
* For example, 302 or 21407. If mcc or mcc+mnc match with operator,
* don't set roaming state.
*
@@ -3321,10 +3340,17 @@
*/
private boolean isOperatorConsideredNonRoaming(ServiceState s) {
String operatorNumeric = s.getOperatorNumeric();
- String[] numericArray = mPhone.getContext().getResources().getStringArray(
- com.android.internal.R.array.config_operatorConsideredNonRoaming);
-
- if (numericArray.length == 0 || operatorNumeric == null) {
+ final CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ String[] numericArray = null;
+ if (configManager != null) {
+ PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId());
+ if (config != null) {
+ numericArray = config.getStringArray(
+ CarrierConfigManager.KEY_NON_ROAMING_OPERATOR_STRING_ARRAY);
+ }
+ }
+ if (ArrayUtils.isEmpty(numericArray) || operatorNumeric == null) {
return false;
}
@@ -3338,10 +3364,17 @@
private boolean isOperatorConsideredRoaming(ServiceState s) {
String operatorNumeric = s.getOperatorNumeric();
- String[] numericArray = mPhone.getContext().getResources().getStringArray(
- com.android.internal.R.array.config_sameNamedOperatorConsideredRoaming);
-
- if (numericArray.length == 0 || operatorNumeric == null) {
+ final CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ String[] numericArray = null;
+ if (configManager != null) {
+ PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId());
+ if (config != null) {
+ numericArray = config.getStringArray(
+ CarrierConfigManager.KEY_ROAMING_OPERATOR_STRING_ARRAY);
+ }
+ }
+ if (ArrayUtils.isEmpty(numericArray) || operatorNumeric == null) {
return false;
}
@@ -3829,6 +3862,18 @@
}
/**
+ * Cancels all notifications posted to NotificationManager. These notifications for restricted
+ * state and rejection cause for cs registration are no longer valid after the SIM has been
+ * removed.
+ */
+ private void cancelAllNotifications() {
+ if (DBG) log("setNotification: cancelAllNotifications");
+ NotificationManager notificationManager = (NotificationManager)
+ mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.cancelAll();
+ }
+
+ /**
* Post a notification to NotificationManager for restricted state and
* rejection cause for cs registration
*
@@ -3903,17 +3948,14 @@
notificationId = CS_REJECT_CAUSE_NOTIFICATION;
int resId = selectResourceForRejectCode(mRejectCode);
if (0 == resId) {
- // cancel notification because current reject code is not handled.
- notifyType = CS_REJECT_CAUSE_DISABLED;
+ loge("setNotification: mRejectCode=" + mRejectCode + " is not handled.");
+ return;
} else {
icon = com.android.internal.R.drawable.stat_notify_mmcc_indication_icn;
title = Resources.getSystem().getString(resId);
details = null;
}
break;
- case CS_REJECT_CAUSE_DISABLED:
- notificationId = CS_REJECT_CAUSE_NOTIFICATION;
- break;
}
if (DBG) {
@@ -3929,6 +3971,7 @@
.setColor(context.getResources().getColor(
com.android.internal.R.color.system_notification_accent_color))
.setContentTitle(title)
+ .setStyle(new Notification.BigTextStyle().bigText(details))
.setContentText(details)
.setChannel(NotificationChannelController.CHANNEL_ID_ALERT)
.build();
@@ -3936,8 +3979,7 @@
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
- if (notifyType == PS_DISABLED || notifyType == CS_DISABLED
- || notifyType == CS_REJECT_CAUSE_DISABLED) {
+ if (notifyType == PS_DISABLED || notifyType == CS_DISABLED) {
// cancel previous post notification
notificationManager.cancel(notificationId);
} else {
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/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 4a41926..f122cc0 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -61,6 +61,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
@@ -84,9 +85,13 @@
static final String LOG_TAG = "SubscriptionController";
static final boolean DBG = true;
static final boolean VDBG = false;
+ static final boolean DBG_CACHE = false;
static final int MAX_LOCAL_LOG_LINES = 500; // TODO: Reduce to 100 when 17678050 is fixed
private ScLocalLog mLocalLog = new ScLocalLog(MAX_LOCAL_LOG_LINES);
+ /* The Cache of Active SubInfoRecord(s) list of currently in use SubInfoRecord(s) */
+ private AtomicReference<List<SubscriptionInfo>> mCacheActiveSubInfoList = new AtomicReference();
+
/**
* Copied from android.util.LocalLog with flush() adding flush and line number
* TODO: Update LocalLog
@@ -590,6 +595,43 @@
return null;
}
+ // Get the active subscription info list from the cache if the cache is not null
+ List<SubscriptionInfo> tmpCachedSubList = mCacheActiveSubInfoList.get();
+ if (tmpCachedSubList != null) {
+ if (DBG_CACHE) {
+ for (SubscriptionInfo si : tmpCachedSubList) {
+ logd("[getActiveSubscriptionInfoList] Getting Cached subInfo=" + si);
+ }
+ }
+ return new ArrayList<SubscriptionInfo>(tmpCachedSubList);
+ } else {
+ if (DBG_CACHE) {
+ logd("[getActiveSubscriptionInfoList] Cached subInfo is null");
+ }
+ return null;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Refresh the cache of SubInfoRecord(s) of the currently inserted SIM(s)
+ */
+ @VisibleForTesting
+ protected void refreshCachedActiveSubscriptionInfoList() {
+
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (!isSubInfoReady()) {
+ if (DBG_CACHE) {
+ logdl("[refreshCachedActiveSubscriptionInfoList] "
+ + "Sub Controller not ready ");
+ }
+ return;
+ }
+
List<SubscriptionInfo> subList = getSubInfo(
SubscriptionManager.SIM_SLOT_INDEX + ">=0", null);
@@ -597,12 +639,21 @@
// FIXME: Unnecessary when an insertion sort is used!
subList.sort(SUBSCRIPTION_INFO_COMPARATOR);
- if (VDBG) logdl("[getActiveSubInfoList]- " + subList.size() + " infos return");
+ if (DBG_CACHE) {
+ logdl("[refreshCachedActiveSubscriptionInfoList]- " + subList.size()
+ + " infos return");
+ }
} else {
- if (DBG) logdl("[getActiveSubInfoList]- no info return");
+ if (DBG_CACHE) logdl("[refreshCachedActiveSubscriptionInfoList]- no info return");
}
- return subList;
+ if (DBG_CACHE) {
+ for (SubscriptionInfo si : subList) {
+ logd("[refreshCachedActiveSubscriptionInfoList] Setting Cached subInfo=" + si);
+ }
+ }
+ mCacheActiveSubInfoList.set(subList);
+
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -615,8 +666,6 @@
*/
@Override
public int getActiveSubInfoCount(String callingPackage) {
- if (DBG) logd("[getActiveSubInfoCount]+");
-
if (!canReadPhoneState(callingPackage, "getActiveSubInfoCount")) {
return 0;
}
@@ -627,10 +676,10 @@
List<SubscriptionInfo> records = getActiveSubscriptionInfoList(
mContext.getOpPackageName());
if (records == null) {
- if (DBG) logd("[getActiveSubInfoCount] records null");
+ if (VDBG) logd("[getActiveSubInfoCount] records null");
return 0;
}
- if (DBG) logd("[getActiveSubInfoCount]- count: " + records.size());
+ if (VDBG) logd("[getActiveSubInfoCount]- count: " + records.size());
return records.size();
} finally {
Binder.restoreCallingIdentity(identity);
@@ -883,6 +932,9 @@
resolver.update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
"=" + Long.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
}
if (DBG) logdl("[addSubInfoRecord] Record already exists");
@@ -976,6 +1028,9 @@
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
"=" + Long.toString(subId), null);
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet);
}
@@ -1008,7 +1063,13 @@
value.put(SubscriptionManager.COLOR, color);
value.put(SubscriptionManager.SIM_SLOT_INDEX, slotIndex);
value.put(SubscriptionManager.CARRIER_NAME, "");
- return resolver.insert(SubscriptionManager.CONTENT_URI, value);
+
+ Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
+ return uri;
}
/**
@@ -1074,6 +1135,10 @@
int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
Long.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
notifySubscriptionInfoChanged();
return result;
@@ -1105,6 +1170,10 @@
int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
Long.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
notifySubscriptionInfoChanged();
return result;
@@ -1165,6 +1234,10 @@
int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
Long.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
notifySubscriptionInfoChanged();
return result;
@@ -1207,6 +1280,10 @@
result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
+ "=" + Long.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
if (DBG) logd("[setDisplayNumber]- update result :" + result);
notifySubscriptionInfoChanged();
@@ -1243,6 +1320,10 @@
int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
Long.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
notifySubscriptionInfoChanged();
return result;
@@ -1273,6 +1354,10 @@
int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
notifySubscriptionInfoChanged();
return result;
@@ -1919,6 +2004,10 @@
resolver.update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
"=" + Integer.toString(subId), null);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
Binder.restoreCallingIdentity(token);
}
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index 519a57b..7e8f51e 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -388,18 +388,18 @@
}
private void handleSimLoaded(int slotId) {
- logd("handleSimStateLoadedInternal: slotId: " + slotId);
+ logd("handleSimLoaded: slotId: " + slotId);
// The SIM should be loaded at this state, but it is possible in cases such as SIM being
// removed or a refresh RESET that the IccRecords could be null. The right behavior is to
// not broadcast the SIM loaded.
IccRecords records = mPhone[slotId].getIccCard().getIccRecords();
if (records == null) { // Possibly a race condition.
- logd("onRecieve: IccRecords null");
+ logd("handleSimLoaded: IccRecords null");
return;
}
if (records.getIccId() == null) {
- logd("onRecieve: IccID null");
+ logd("handleSimLoaded: IccID null");
return;
}
mIccId[slotId] = records.getIccId();
@@ -431,6 +431,9 @@
contentResolver.update(SubscriptionManager.CONTENT_URI, number,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
+ Long.toString(subId), null);
+
+ // refresh Cached Active Subscription Info List
+ SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
}
SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
@@ -450,6 +453,9 @@
contentResolver.update(SubscriptionManager.CONTENT_URI, name,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
+ "=" + Long.toString(subId), null);
+
+ // refresh Cached Active Subscription Info List
+ SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
}
/* Update preferred network type and network selection mode on SIM change.
@@ -581,6 +587,9 @@
contentResolver.update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
+ Integer.toString(oldSubInfo.get(0).getSubscriptionId()), null);
+
+ // refresh Cached Active Subscription Info List
+ SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
}
} else {
if (mInsertSimState[i] == SIM_NOT_CHANGE) {
@@ -658,6 +667,9 @@
contentResolver.update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
+ Integer.toString(temp.getSubscriptionId()), null);
+
+ // refresh Cached Active Subscription Info List
+ SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
}
}
@@ -745,6 +757,9 @@
hasChanges = true;
contentResolver.update(SubscriptionManager.CONTENT_URI, values,
SubscriptionManager.ICC_ID + "=\"" + embeddedProfile.iccid + "\"", null);
+
+ // refresh Cached Active Subscription Info List
+ SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
}
// Remove all remaining subscriptions which have embedded = true. We set embedded to false
@@ -765,6 +780,9 @@
values.put(SubscriptionManager.IS_EMBEDDED, 0);
hasChanges = true;
contentResolver.update(SubscriptionManager.CONTENT_URI, values, whereClause, null);
+
+ // refresh Cached Active Subscription Info List
+ SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
}
return hasChanges;
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
index 7865bc4..3cd804d 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
@@ -17,7 +17,6 @@
package com.android.internal.telephony.dataconnection;
import android.app.PendingIntent;
-import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
@@ -536,8 +535,8 @@
return mConnectionGeneration.get();
}
- public long getInterApnDelay(boolean failFastEnabled) {
- return mRetryManager.getInterApnDelay(failFastEnabled || isFastRetryReason());
+ long getRetryAfterDisconnectDelay() {
+ return mRetryManager.getRetryAfterDisconnectDelay();
}
public static int apnIdForType(int networkType) {
@@ -726,7 +725,7 @@
l.dump(fd, pw, args);
}
pw.decreaseIndent();
- pw.println("mRetryManager={" + mRetryManager.toString() + "}");
+ pw.println(mRetryManager);
}
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 7c5f503..2898b24 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -411,9 +411,6 @@
networkType, NETWORK_TYPE, TelephonyManager.getNetworkTypeName(networkType));
mNetworkInfo.setRoaming(ss.getDataRoaming());
mNetworkInfo.setIsAvailable(true);
- // The network should be by default metered until we find it has NET_CAPABILITY_NOT_METERED
- // capability.
- mNetworkInfo.setMetered(true);
addState(mDefaultState);
addState(mInactiveState, mDefaultState);
@@ -865,6 +862,7 @@
result.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
if (mApnSetting != null) {
+ ApnSetting securedDunApn = mDct.fetchDunApn();
for (String type : mApnSetting.types) {
if (!mRestrictedNetworkOverride
&& (mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
@@ -881,6 +879,11 @@
result.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
result.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
result.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
+ // check if this is the DUN apn as well as returned by fetchDunApn().
+ // If yes, add DUN capability too.
+ if (mApnSetting.equals(securedDunApn)) {
+ result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
+ }
break;
}
case PhoneConstants.APN_TYPE_DEFAULT: {
@@ -896,7 +899,6 @@
break;
}
case PhoneConstants.APN_TYPE_DUN: {
- ApnSetting securedDunApn = mDct.fetchDunApn();
if (securedDunApn == null || securedDunApn.equals(mApnSetting)) {
result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
}
@@ -933,10 +935,8 @@
&& !mRestrictedNetworkOverride)
|| !mApnSetting.isMetered(mPhone)) {
result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- mNetworkInfo.setMetered(false);
} else {
result.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- mNetworkInfo.setMetered(true);
}
result.maybeMarkCapabilitiesRestricted();
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
index e949acf..e7afdff 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
@@ -74,6 +74,16 @@
return mDataDisallowedReasonSet.contains(reason);
}
+ /**
+ * Check if only one disallowed reason prevent data connection.
+ *
+ * @param reason The given reason to check
+ * @return True if the given reason is the only one that prevents data connection
+ */
+ public boolean containsOnly(DataDisallowedReasonType reason) {
+ return mDataDisallowedReasonSet.size() == 1 && contains(reason);
+ }
+
boolean contains(DataAllowedReasonType reason) {
return reason == mDataAllowedReason;
}
@@ -88,7 +98,7 @@
}
// Disallowed reasons. There could be multiple reasons if data connection is not allowed.
- enum DataDisallowedReasonType {
+ public enum DataDisallowedReasonType {
// Soft failure reasons. Normally the reasons from users or policy settings.
DATA_DISABLED(false), // Data is disabled by the user or policy.
ROAMING_DISABLED(false), // Data roaming is disabled by the user.
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 6daff83..850ccaa 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -84,6 +84,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.SettingsObserver;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataAllowedReasonType;
import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
@@ -216,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) {
@@ -316,46 +319,6 @@
}
};
- private static class SettingsObserver extends ContentObserver {
- final private HashMap<Uri, Integer> mUriEventMap;
- final private Context mContext;
- final private Handler mHandler;
- final private static String TAG = "DcTracker.SettingsObserver";
-
- SettingsObserver(Context context, Handler handler) {
- super(null);
- mUriEventMap = new HashMap<Uri, Integer>();
- mContext = context;
- mHandler = handler;
- }
-
- void observe(Uri uri, int what) {
- mUriEventMap.put(uri, what);
- final ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(uri, false, this);
- }
-
- void unobserve() {
- final ContentResolver resolver = mContext.getContentResolver();
- resolver.unregisterContentObserver(this);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- Rlog.e(TAG, "Should never be reached.");
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- final Integer what = mUriEventMap.get(uri);
- if (what != null) {
- mHandler.obtainMessage(what.intValue()).sendToTarget();
- } else {
- Rlog.e(TAG, "No matching event to send for URI=" + uri);
- }
- }
- }
-
private final SettingsObserver mSettingsObserver;
private void registerSettingsObserver() {
@@ -367,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);
@@ -2813,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()) {
@@ -2822,6 +2787,8 @@
return;
}
+ checkDataRoamingStatus(settingChanged);
+
if (getDataRoamingEnabled()) {
if (DBG) log("onDataRoamingOnOrSettingsChanged: setup data on roaming");
@@ -2837,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) {
@@ -2966,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) {
@@ -2990,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) {
@@ -3217,7 +3202,7 @@
// we're not tying up the RIL command channel.
// This also helps in any external dependency to turn off the context.
if (DBG) log("onDisconnectDone: attached, ready and retry after disconnect");
- long delay = apnContext.getInterApnDelay(mFailFast);
+ long delay = apnContext.getRetryAfterDisconnectDelay();
if (delay > 0) {
// Data connection is in IDLE state, so when we reconnect later, we'll rebuild
// the waiting APN list, which will also reset/reconfigure the retry manager.
@@ -3801,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:
@@ -4215,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/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
index 00ab0f4..6489014 100755
--- a/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
+++ b/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
@@ -523,6 +523,7 @@
switch(msg.what) {
case EVENT_PBR_LOAD_DONE:
+ log("Loading PBR records done");
ar = (AsyncResult) msg.obj;
if (ar.exception == null) {
createPbrFile((ArrayList<byte[]>)ar.result);
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 734a155..45dc0b27 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -712,6 +712,11 @@
}
@Override
+ public void setTTYMode(int ttyMode, Message onComplete) {
+ mCT.setTtyMode(ttyMode);
+ }
+
+ @Override
public void setUiTTYMode(int uiTtyMode, Message onComplete) {
mCT.setUiTTYMode(uiTtyMode, onComplete);
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
index 87b96d8..c165b03 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
@@ -496,9 +496,6 @@
return false;
}
- public void saveClirSetting(int commandInterfaceCLIRMode) {
- }
-
@Override
public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){
return null;
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
index a011f757..52636f5 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
@@ -267,6 +267,7 @@
long conferenceConnectTime = imsPhoneConnection.getConferenceConnectTime();
if (conferenceConnectTime > 0) {
imsPhoneConnection.setConnectTime(conferenceConnectTime);
+ imsPhoneConnection.setConnectTimeReal(imsPhoneConnection.getConnectTimeReal());
} else {
if (DBG) {
Rlog.d(LOG_TAG, "merge: conference connect time is 0");
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 7cf720d..bdb1e11 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -24,7 +24,10 @@
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.net.NetworkRequest;
import android.net.NetworkStats;
import android.net.Uri;
import android.os.AsyncResult;
@@ -227,6 +230,24 @@
}
};
+ /**
+ * Tracks whether we are currently monitoring network connectivity for the purpose of warning
+ * the user of an inability to handover from LTE to WIFI for video calls.
+ */
+ private boolean mIsMonitoringConnectivity = false;
+
+ /**
+ * Network callback used to schedule the handover check when a wireless network connects.
+ */
+ private ConnectivityManager.NetworkCallback mNetworkCallback =
+ new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ Rlog.i(LOG_TAG, "Network available: " + network);
+ scheduleHandoverCheck();
+ }
+ };
+
//***** Constants
static final int MAX_CONNECTIONS = 7;
@@ -306,6 +327,7 @@
private ImsCall mCallExpectedToResume = null;
private boolean mAllowEmergencyVideoCalls = false;
private boolean mIgnoreDataEnabledChangedForVideoCalls = false;
+ private boolean mIsViLteDataMetered = false;
/**
* Listeners to changes in the phone state. Intended for use by other interested IMS components
@@ -579,6 +601,22 @@
private boolean mNotifyHandoverVideoFromWifiToLTE = false;
/**
+ * Carrier configuration option which determines whether the carrier wants to inform the user
+ * when a video call is handed over from LTE to WIFI.
+ * See {@link CarrierConfigManager#KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL} for more
+ * information.
+ */
+ private boolean mNotifyHandoverVideoFromLTEToWifi = false;
+
+ /**
+ * When {@code} false, indicates that no handover from LTE to WIFI has occurred during the start
+ * of the call.
+ * When {@code true}, indicates that the start of call handover from LTE to WIFI has been
+ * attempted (it may have suceeded or failed).
+ */
+ private boolean mHasPerformedStartOfCallHandover = false;
+
+ /**
* Carrier configuration option which determines whether the carrier supports the
* {@link VideoProfile#STATE_PAUSED} signalling.
* See {@link CarrierConfigManager#KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL} for more information.
@@ -985,6 +1023,16 @@
return;
}
+ updateCarrierConfigCache(carrierConfig);
+ }
+
+ /**
+ * Updates the local carrier config cache from a bundle obtained from the carrier config
+ * manager. Also supports unit testing by injecting configuration at test time.
+ * @param carrierConfig The config bundle.
+ */
+ @VisibleForTesting
+ public void updateCarrierConfigCache(PersistableBundle carrierConfig) {
mAllowEmergencyVideoCalls =
carrierConfig.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL);
mTreatDowngradedVideoCallsAsVideoCalls =
@@ -1001,9 +1049,13 @@
mSupportDowngradeVtToAudio = carrierConfig.getBoolean(
CarrierConfigManager.KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL);
mNotifyHandoverVideoFromWifiToLTE = carrierConfig.getBoolean(
- CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL);
+ CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL);
+ mNotifyHandoverVideoFromLTEToWifi = carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL);
mIgnoreDataEnabledChangedForVideoCalls = carrierConfig.getBoolean(
CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
+ mIsViLteDataMetered = carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_VILTE_DATA_IS_METERED_BOOL);
mSupportPauseVideo = carrierConfig.getBoolean(
CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
@@ -1449,7 +1501,27 @@
}
//***** Called from ImsPhone
+ /**
+ * Set the TTY mode. This is the actual tty mode (varies depending on peripheral status)
+ */
+ public void setTtyMode(int ttyMode) {
+ if (mImsManager == null) {
+ Log.w(LOG_TAG, "ImsManager is null when setting TTY mode");
+ return;
+ }
+ try {
+ mImsManager.setTtyMode(ttyMode);
+ } catch (ImsException e) {
+ loge("setTtyMode : " + e);
+ retryGetImsService();
+ }
+ }
+
+ /**
+ * Sets the UI TTY mode. This is the preferred TTY mode that the user sets in the call
+ * settings screen.
+ */
public void setUiTTYMode(int uiTtyMode, Message onComplete) {
if (mImsManager == null) {
mPhone.sendErrorResponse(onComplete, getImsManagerIsNullException());
@@ -1459,7 +1531,7 @@
try {
mImsManager.setUiTTYMode(mPhone.getContext(), uiTtyMode, onComplete);
} catch (ImsException e) {
- loge("setTTYMode : " + e);
+ loge("setUITTYMode : " + e);
mPhone.sendErrorResponse(onComplete, e);
retryGetImsService();
}
@@ -1825,10 +1897,17 @@
return code;
}
- private int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) {
+ /**
+ * Maps an {@link ImsReasonInfo} reason code to a {@link DisconnectCause} cause code.
+ * The {@link Call.State} provided is the state of the call prior to disconnection.
+ * @param reasonInfo the {@link ImsReasonInfo} for the disconnection.
+ * @param callState The {@link Call.State} prior to disconnection.
+ * @return The {@link DisconnectCause} code.
+ */
+ @VisibleForTesting
+ public int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo, Call.State callState) {
int cause = DisconnectCause.ERROR_UNSPECIFIED;
- //int type = reasonInfo.getReasonType();
int code = maybeRemapReasonCode(reasonInfo);
switch (code) {
case ImsReasonInfo.CODE_SIP_BAD_ADDRESS:
@@ -1884,10 +1963,18 @@
case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE:
return DisconnectCause.TIMED_OUT;
- case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY:
case ImsReasonInfo.CODE_LOCAL_POWER_OFF:
return DisconnectCause.POWER_OFF;
+ case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY:
+ case ImsReasonInfo.CODE_LOW_BATTERY: {
+ if (callState == Call.State.DIALING) {
+ return DisconnectCause.DIAL_LOW_BATTERY;
+ } else {
+ return DisconnectCause.LOW_BATTERY;
+ }
+ }
+
case ImsReasonInfo.CODE_FDN_BLOCKED:
return DisconnectCause.FDN_BLOCKED;
@@ -1978,13 +2065,18 @@
processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE,
DisconnectCause.NOT_DISCONNECTED);
- if (mNotifyVtHandoverToWifiFail &&
- !imsCall.isWifiCall() && imsCall.isVideoCall() && isWifiConnected()) {
- // Schedule check to see if handover succeeded.
- sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, imsCall),
- HANDOVER_TO_WIFI_TIMEOUT_MS);
+ if (mNotifyVtHandoverToWifiFail && imsCall.isVideoCall() && !imsCall.isWifiCall()) {
+ if (isWifiConnected()) {
+ // Schedule check to see if handover succeeded.
+ sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, imsCall),
+ HANDOVER_TO_WIFI_TIMEOUT_MS);
+ } else {
+ // No wifi connectivity, so keep track of network availability for potential
+ // handover.
+ registerForConnectivityChanges();
+ }
}
-
+ mHasPerformedStartOfCallHandover = false;
mMetrics.writeOnImsCallStarted(mPhone.getPhoneId(), imsCall.getCallSession());
}
@@ -2035,8 +2127,18 @@
return;
} else {
mPendingMO = null;
- int cause = getDisconnectCauseFromReasonInfo(reasonInfo);
ImsPhoneConnection conn = findConnection(imsCall);
+ Call.State callState;
+ if (conn != null) {
+ callState = conn.getState();
+ } else {
+ // Need to fall back in case connection is null; it shouldn't be, but a sane
+ // fallback is to assume we're dialing. This state is only used to
+ // determine which disconnect string to show in the case of a low battery
+ // disconnect.
+ callState = Call.State.DIALING;
+ }
+ int cause = getDisconnectCauseFromReasonInfo(reasonInfo, callState);
if(conn != null) {
conn.setPreciseDisconnectCause(
@@ -2054,8 +2156,18 @@
public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode());
- int cause = getDisconnectCauseFromReasonInfo(reasonInfo);
ImsPhoneConnection conn = findConnection(imsCall);
+ Call.State callState;
+ if (conn != null) {
+ callState = conn.getState();
+ } else {
+ // Connection shouldn't be null, but if it is, we can assume the call was active.
+ // This call state is only used for determining which disconnect message to show in
+ // the case of the device's battery being low resulting in a call drop.
+ callState = Call.State.ACTIVE;
+ }
+ int cause = getDisconnectCauseFromReasonInfo(reasonInfo, callState);
+
if (DBG) log("cause = " + cause + " conn = " + conn);
if (conn != null) {
@@ -2473,33 +2585,70 @@
ImsReasonInfo reasonInfo) {
if (DBG) {
log("onCallHandover :: srcAccessTech=" + srcAccessTech + ", targetAccessTech=" +
- targetAccessTech + ", reasonInfo=" + reasonInfo);
+ targetAccessTech + ", reasonInfo=" + reasonInfo);
}
// Only consider it a valid handover to WIFI if the source radio tech is known.
boolean isHandoverToWifi = srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
&& srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
&& targetAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
- if (isHandoverToWifi) {
- // If we handed over to wifi successfully, don't check for failure in the future.
- removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
- }
-
// Only consider it a handover from WIFI if the source and target radio tech is known.
- boolean isHandoverFromWifi = srcAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
- && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
- && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
- if (mNotifyHandoverVideoFromWifiToLTE && isHandoverFromWifi && imsCall.isVideoCall()) {
- log("onCallHandover :: notifying of WIFI to LTE handover.");
- ImsPhoneConnection conn = findConnection(imsCall);
- if (conn != null) {
- conn.onConnectionEvent(
- TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE, null);
- } else {
- loge("onCallHandover :: failed to notify of handover; connection is null.");
+ boolean isHandoverFromWifi =
+ srcAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
+ && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+
+ ImsPhoneConnection conn = findConnection(imsCall);
+ if (conn != null) {
+ if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
+ if (isHandoverToWifi) {
+ removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
+
+ if (mNotifyHandoverVideoFromLTEToWifi && mHasPerformedStartOfCallHandover) {
+ // This is a handover which happened mid-call (ie not the start of call
+ // handover from LTE to WIFI), so we'll notify the InCall UI.
+ conn.onConnectionEvent(
+ TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI, null);
+ }
+
+ // We are on WIFI now so no need to get notified of network availability.
+ unregisterForConnectivityChanges();
+ } else if (isHandoverFromWifi && imsCall.isVideoCall()) {
+ // A video call just dropped from WIFI to LTE; we want to be informed if a
+ // new WIFI
+ // network comes into range.
+ registerForConnectivityChanges();
+ }
}
+
+ if (isHandoverFromWifi && imsCall.isVideoCall()) {
+ if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) {
+ if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
+ log("onCallHandover :: notifying of WIFI to LTE handover.");
+ conn.onConnectionEvent(
+ TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE, null);
+ } else {
+ // Call has already had a disconnect request issued by the user or is
+ // in the process of disconnecting; do not inform the UI of this as it
+ // is not relevant.
+ log("onCallHandover :: skip notify of WIFI to LTE handover for "
+ + "disconnected call.");
+ }
+ }
+
+ if (!mIsDataEnabled && mIsViLteDataMetered) {
+ // Call was downgraded from WIFI to LTE and data is metered; downgrade the
+ // call now.
+ downgradeVideoCall(ImsReasonInfo.CODE_WIFI_LOST, conn);
+ }
+ }
+ } else {
+ loge("onCallHandover :: connection null.");
}
+ if (!mHasPerformedStartOfCallHandover) {
+ mHasPerformedStartOfCallHandover = true;
+ }
mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(),
TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER, imsCall.getCallSession(),
srcAccessTech, targetAccessTech, reasonInfo);
@@ -2525,11 +2674,20 @@
// If we know we failed to handover, don't check for failure in the future.
removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
+ if (imsCall.isVideoCall()
+ && conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
+ // Start listening for a WIFI network to come into range for potential handover.
+ registerForConnectivityChanges();
+ }
+
if (mNotifyVtHandoverToWifiFail) {
// Only notify others if carrier config indicates to do so.
conn.onHandoverToWifiFailed();
}
}
+ if (!mHasPerformedStartOfCallHandover) {
+ mHasPerformedStartOfCallHandover = true;
+ }
}
@Override
@@ -2607,6 +2765,8 @@
public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode());
removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
+ mHasPerformedStartOfCallHandover = false;
+ unregisterForConnectivityChanges();
if (imsCall == mUssdSession) {
mUssdSession = null;
@@ -2875,12 +3035,24 @@
case EVENT_CHECK_FOR_WIFI_HANDOVER:
if (msg.obj instanceof ImsCall) {
ImsCall imsCall = (ImsCall) msg.obj;
+ if (imsCall != mForegroundCall.getImsCall()) {
+ Rlog.i(LOG_TAG, "handoverCheck: no longer FG; check skipped.");
+ unregisterForConnectivityChanges();
+ // Handover check and its not the foreground call any more.
+ return;
+ }
if (!imsCall.isWifiCall()) {
// Call did not handover to wifi, notify of handover failure.
ImsPhoneConnection conn = findConnection(imsCall);
if (conn != null) {
+ Rlog.i(LOG_TAG, "handoverCheck: handover failed.");
conn.onHandoverToWifiFailed();
}
+
+ if (imsCall.isVideoCall()
+ && conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
+ registerForConnectivityChanges();
+ }
}
}
break;
@@ -3332,27 +3504,46 @@
ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId()).setDataEnabled(enabled);
mIsDataEnabled = enabled;
- if (mIgnoreDataEnabledChangedForVideoCalls) {
- log("Ignore data " + ((enabled) ? "enabled" : "disabled") + " due to carrier policy.");
+ if (!mIsViLteDataMetered) {
+ log("Ignore data " + ((enabled) ? "enabled" : "disabled") + " - carrier policy "
+ + "indicates that data is not metered for ViLTE calls.");
return;
}
- if (mIgnoreDataEnabledChangedForVideoCalls) {
- log("Ignore data " + ((enabled) ? "enabled" : "disabled") + " due to carrier policy.");
- return;
+ // Inform connections that data has been disabled to ensure we turn off video capability
+ // if this is an LTE call.
+ for (ImsPhoneConnection conn : mConnections) {
+ conn.handleDataEnabledChange(enabled);
}
+ int reasonCode;
+ if (reason == DataEnabledSettings.REASON_POLICY_DATA_ENABLED) {
+ reasonCode = ImsReasonInfo.CODE_DATA_LIMIT_REACHED;
+ } else if (reason == DataEnabledSettings.REASON_USER_DATA_ENABLED) {
+ reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
+ } else {
+ // Unexpected code, default to data disabled.
+ reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
+ }
+
+ // Potentially send connection events so the InCall UI knows that video calls are being
+ // downgraded due to data being enabled/disabled.
+ maybeNotifyDataDisabled(enabled, reasonCode);
+ // Handle video state changes required as a result of data being enabled/disabled.
+ handleDataEnabledChange(enabled, reasonCode);
+
+ // We do not want to update the ImsConfig for REASON_REGISTERED, since it can happen before
+ // the carrier config has loaded and will deregister IMS.
+ if (!mShouldUpdateImsConfigOnDisconnect
+ && reason != DataEnabledSettings.REASON_REGISTERED) {
+ // This will call into updateVideoCallFeatureValue and eventually all clients will be
+ // asynchronously notified that the availability of VT over LTE has changed.
+ ImsManager.updateImsServiceConfig(mPhone.getContext(), mPhone.getPhoneId(), true);
+ }
+ }
+
+ private void maybeNotifyDataDisabled(boolean enabled, int reasonCode) {
if (!enabled) {
- int reasonCode;
- if (reason == DataEnabledSettings.REASON_POLICY_DATA_ENABLED) {
- reasonCode = ImsReasonInfo.CODE_DATA_LIMIT_REACHED;
- } else if (reason == DataEnabledSettings.REASON_USER_DATA_ENABLED) {
- reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
- } else {
- // Unexpected code, default to data disabled.
- reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
- }
-
// If data is disabled while there are ongoing VT calls which are not taking place over
// wifi, then they should be disconnected to prevent the user from incurring further
// data charges.
@@ -3372,27 +3563,38 @@
conn.onConnectionEvent(
TelephonyManager.EVENT_DOWNGRADE_DATA_LIMIT_REACHED, null);
}
- modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY);
- } else if (mSupportPauseVideo) {
- // The carrier supports video pause signalling, so pause the video.
- mShouldUpdateImsConfigOnDisconnect = true;
- conn.pauseVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
- } else {
- // At this point the only choice we have is to terminate the call.
- try {
- imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED, reasonCode);
- } catch (ImsException ie) {
- loge("Couldn't terminate call " + imsCall);
- }
}
}
}
+ }
+ }
+
+ /**
+ * Handles changes to the enabled state of mobile data.
+ * When data is disabled, handles auto-downgrade of video calls over LTE.
+ * When data is enabled, handled resuming of video calls paused when data was disabled.
+ * @param enabled {@code true} if mobile data is enabled, {@code false} if mobile data is
+ * disabled.
+ * @param reasonCode The {@link ImsReasonInfo} code for the data enabled state change.
+ */
+ private void handleDataEnabledChange(boolean enabled, int reasonCode) {
+ if (!enabled) {
+ // If data is disabled while there are ongoing VT calls which are not taking place over
+ // wifi, then they should be disconnected to prevent the user from incurring further
+ // data charges.
+ for (ImsPhoneConnection conn : mConnections) {
+ ImsCall imsCall = conn.getImsCall();
+ if (imsCall != null && imsCall.isVideoCall() && !imsCall.isWifiCall()) {
+ log("handleDataEnabledChange - downgrading " + conn);
+ downgradeVideoCall(reasonCode, conn);
+ }
+ }
} else if (mSupportPauseVideo) {
// Data was re-enabled, so un-pause previously paused video calls.
for (ImsPhoneConnection conn : mConnections) {
// If video is paused, check to see if there are any pending pauses due to enabled
// state of data changing.
- log("onDataEnabledChanged - resuming " + conn);
+ log("handleDataEnabledChange - resuming " + conn);
if (VideoProfile.isPaused(conn.getVideoState()) &&
conn.wasVideoPausedFromSource(VideoPauseTracker.SOURCE_DATA_ENABLED)) {
// The data enabled state was a cause of a pending pause, so potentially
@@ -3402,15 +3604,40 @@
}
mShouldUpdateImsConfigOnDisconnect = false;
}
+ }
+ /**
+ * Handles downgrading a video call. The behavior depends on carrier capabilities; we will
+ * attempt to take one of the following actions (in order of precedence):
+ * 1. If supported by the carrier, the call will be downgraded to an audio-only call.
+ * 2. If the carrier supports video pause signalling, the video will be paused.
+ * 3. The call will be disconnected.
+ * @param reasonCode The {@link ImsReasonInfo} reason code for the downgrade.
+ * @param conn The {@link ImsPhoneConnection} to downgrade.
+ */
+ private void downgradeVideoCall(int reasonCode, ImsPhoneConnection conn) {
+ ImsCall imsCall = conn.getImsCall();
+ if (imsCall != null) {
+ if (conn.hasCapabilities(
+ Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL |
+ Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE)) {
- // We do not want to update the ImsConfig for REASON_REGISTERED, since it can happen before
- // the carrier config has loaded and will deregister IMS.
- if (!mShouldUpdateImsConfigOnDisconnect
- && reason != DataEnabledSettings.REASON_REGISTERED) {
- // This will call into updateVideoCallFeatureValue and eventually all clients will be
- // asynchronously notified that the availability of VT over LTE has changed.
- ImsManager.updateImsServiceConfig(mPhone.getContext(), mPhone.getPhoneId(), true);
+ // If the carrier supports downgrading to voice, then we can simply issue a
+ // downgrade to voice instead of terminating the call.
+ modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY);
+ } else if (mSupportPauseVideo && reasonCode != ImsReasonInfo.CODE_WIFI_LOST) {
+ // The carrier supports video pause signalling, so pause the video if we didn't just
+ // lose wifi; in that case just disconnect.
+ mShouldUpdateImsConfigOnDisconnect = true;
+ conn.pauseVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
+ } else {
+ // At this point the only choice we have is to terminate the call.
+ try {
+ imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED, reasonCode);
+ } catch (ImsException ie) {
+ loge("Couldn't terminate call " + imsCall);
+ }
+ }
}
}
@@ -3437,12 +3664,75 @@
}
/**
+ * Registers for changes to network connectivity. Specifically requests the availability of new
+ * WIFI networks which an IMS video call could potentially hand over to.
+ */
+ private void registerForConnectivityChanges() {
+ if (mIsMonitoringConnectivity || !mNotifyVtHandoverToWifiFail) {
+ return;
+ }
+ ConnectivityManager cm = (ConnectivityManager) mPhone.getContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (cm != null) {
+ Rlog.i(LOG_TAG, "registerForConnectivityChanges");
+ NetworkCapabilities capabilities = new NetworkCapabilities();
+ capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ NetworkRequest.Builder builder = new NetworkRequest.Builder();
+ builder.setCapabilities(capabilities);
+ cm.registerNetworkCallback(builder.build(), mNetworkCallback);
+ mIsMonitoringConnectivity = true;
+ }
+ }
+
+ /**
+ * Unregister for connectivity changes. Will be called when a call disconnects or if the call
+ * ends up handing over to WIFI.
+ */
+ private void unregisterForConnectivityChanges() {
+ if (!mIsMonitoringConnectivity || !mNotifyVtHandoverToWifiFail) {
+ return;
+ }
+ ConnectivityManager cm = (ConnectivityManager) mPhone.getContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (cm != null) {
+ Rlog.i(LOG_TAG, "unregisterForConnectivityChanges");
+ cm.unregisterNetworkCallback(mNetworkCallback);
+ mIsMonitoringConnectivity = false;
+ }
+ }
+
+ /**
+ * If the foreground call is a video call, schedule a handover check if one is not already
+ * scheduled. This method is intended ONLY for use when scheduling to watch for mid-call
+ * handovers.
+ */
+ private void scheduleHandoverCheck() {
+ ImsCall fgCall = mForegroundCall.getImsCall();
+ ImsPhoneConnection conn = mForegroundCall.getFirstConnection();
+ if (!mNotifyVtHandoverToWifiFail || fgCall == null || !fgCall.isVideoCall() || conn == null
+ || conn.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED) {
+ return;
+ }
+
+ if (!hasMessages(EVENT_CHECK_FOR_WIFI_HANDOVER)) {
+ Rlog.i(LOG_TAG, "scheduleHandoverCheck: schedule");
+ sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, fgCall),
+ HANDOVER_TO_WIFI_TIMEOUT_MS);
+ }
+ }
+
+ /**
* @return {@code true} if downgrading of a video call to audio is supported.
*/
public boolean isCarrierDowngradeOfVtCallSupported() {
return mSupportDowngradeVtToAudio;
}
+ @VisibleForTesting
+ public void setDataEnabled(boolean isDataEnabled) {
+ mIsDataEnabled = isDataEnabled;
+ }
+
private void handleFeatureCapabilityChanged(int serviceClass,
int[] enabledFeatures, int[] disabledFeatures) {
if (serviceClass == ImsServiceClass.MMTEL) {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index a180862..9800a44 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -111,6 +111,14 @@
*/
private boolean mIsMergeInProcess = false;
+ /**
+ * Used as an override to determine whether video is locally available for this call.
+ * This allows video availability to be overridden in the case that the modem says video is
+ * currently available, but mobile data is off and the carrier is metering data for video
+ * calls.
+ */
+ private boolean mIsVideoEnabled = true;
+
//***** Event Constants
private static final int EVENT_DTMF_DONE = 1;
private static final int EVENT_PAUSE_DONE = 2;
@@ -246,11 +254,15 @@
return (a == null) ? (b == null) : (b != null && a.startsWith (b));
}
- private static int applyLocalCallCapabilities(ImsCallProfile localProfile, int capabilities) {
- Rlog.w(LOG_TAG, "applyLocalCallCapabilities - localProfile = "+localProfile);
+ private int applyLocalCallCapabilities(ImsCallProfile localProfile, int capabilities) {
+ Rlog.i(LOG_TAG, "applyLocalCallCapabilities - localProfile = " + localProfile);
capabilities = removeCapability(capabilities,
Connection.Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
+ if (!mIsVideoEnabled) {
+ Rlog.i(LOG_TAG, "applyLocalCallCapabilities - disabling video (overidden)");
+ return capabilities;
+ }
switch (localProfile.mCallType) {
case ImsCallProfile.CALL_TYPE_VT:
// Fall-through
@@ -1229,4 +1241,15 @@
updateVideoState(newVideoState);
mShouldIgnoreVideoStateChanges = false;
}
+
+ public void handleDataEnabledChange(boolean isDataEnabled) {
+ mIsVideoEnabled = isDataEnabled;
+ Rlog.i(LOG_TAG, "handleDataEnabledChange: isDataEnabled=" + isDataEnabled
+ + "; updating local video availability.");
+ updateMediaCapabilities(getImsCall());
+ if (mImsVideoCallProviderWrapper != null) {
+ mImsVideoCallProviderWrapper.setIsVideoEnabled(
+ hasCapabilities(Connection.Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL));
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index 1be0ffa..4e3957e 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -722,7 +722,6 @@
boolean
isSupportedOverImsPhone() {
if (isShortCode()) return true;
- else if (mDialingNumber != null) return false;
else if (isServiceCodeCallForwarding(mSc)
|| isServiceCodeCallBarring(mSc)
|| (mSc != null && mSc.equals(SC_WAIT))
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
index 9dd4c62..241f211 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
@@ -47,7 +47,6 @@
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
import com.android.internal.telephony.uicc.IccCardStatus.PinState;
-import com.android.internal.telephony.uicc.UiccController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -88,7 +87,7 @@
private static final int EVENT_ICC_RECORD_EVENTS = 500;
private static final int EVENT_SUBSCRIPTION_ACTIVATED = 501;
private static final int EVENT_SUBSCRIPTION_DEACTIVATED = 502;
- private static final int EVENT_CARRIER_PRIVILIGES_LOADED = 503;
+ private static final int EVENT_CARRIER_PRIVILEGES_LOADED = 503;
private Integer mPhoneId = null;
@@ -168,17 +167,31 @@
*/
private void updateQuietMode() {
synchronized (mLock) {
+ boolean oldQuietMode = mQuietMode;
boolean newQuietMode;
int cdmaSource = Phone.CDMA_SUBSCRIPTION_UNKNOWN;
+ boolean isLteOnCdmaMode = TelephonyManager.getLteOnCdmaModeStatic()
+ == PhoneConstants.LTE_ON_CDMA_TRUE;
if (mCurrentAppType == UiccController.APP_FAM_3GPP) {
newQuietMode = false;
if (DBG) log("updateQuietMode: 3GPP subscription -> newQuietMode=" + newQuietMode);
} else {
+ if (isLteOnCdmaMode) {
+ log("updateQuietMode: is cdma/lte device, force IccCardProxy into 3gpp mode");
+ mCurrentAppType = UiccController.APP_FAM_3GPP;
+ }
cdmaSource = mCdmaSSM != null ?
mCdmaSSM.getCdmaSubscriptionSource() : Phone.CDMA_SUBSCRIPTION_UNKNOWN;
newQuietMode = (cdmaSource == Phone.CDMA_SUBSCRIPTION_NV)
- && (mCurrentAppType == UiccController.APP_FAM_3GPP2);
+ && (mCurrentAppType == UiccController.APP_FAM_3GPP2)
+ && !isLteOnCdmaMode;
+ if (DBG) {
+ log("updateQuietMode: cdmaSource=" + cdmaSource
+ + " mCurrentAppType=" + mCurrentAppType
+ + " isLteOnCdmaMode=" + isLteOnCdmaMode
+ + " newQuietMode=" + newQuietMode);
+ }
}
if (mQuietMode == false && newQuietMode == true) {
@@ -199,7 +212,8 @@
}
if (DBG) {
log("updateQuietMode: QuietMode is " + mQuietMode + " (app_type="
- + mCurrentAppType + " cdmaSource=" + cdmaSource + ")");
+ + mCurrentAppType + " isLteOnCdmaMode=" + isLteOnCdmaMode
+ + " cdmaSource=" + cdmaSource + ")");
}
mInitialized = true;
sendMessage(obtainMessage(EVENT_ICC_CHANGED));
@@ -260,7 +274,7 @@
}
if (mUiccCard != null && !mUiccCard.areCarrierPriviligeRulesLoaded()) {
mUiccCard.registerForCarrierPrivilegeRulesLoaded(
- this, EVENT_CARRIER_PRIVILIGES_LOADED, null);
+ this, EVENT_CARRIER_PRIVILEGES_LOADED, null);
} else {
onRecordsLoaded();
}
@@ -296,7 +310,7 @@
}
break;
- case EVENT_CARRIER_PRIVILIGES_LOADED:
+ case EVENT_CARRIER_PRIVILEGES_LOADED:
log("EVENT_CARRIER_PRIVILEGES_LOADED");
if (mUiccCard != null) {
mUiccCard.unregisterForCarrierPrivilegeRulesLoaded(this);
@@ -526,7 +540,8 @@
intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, value);
intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
intent.putExtra(PhoneConstants.PHONE_KEY, mPhoneId); // SubId may not be valid.
- log("Sending intent ACTION_INTERNAL_SIM_STATE_CHANGED" + " for mPhoneId : " + mPhoneId);
+ log("Sending intent ACTION_INTERNAL_SIM_STATE_CHANGED value=" + value
+ + " for mPhoneId : " + mPhoneId);
ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
index 4750814..33dd381 100644
--- a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
@@ -471,6 +471,7 @@
response = lc.mOnLoaded;
if (processException(response, (AsyncResult) msg.obj)) {
+ loge("exception caught from EVENT_GET_RECORD_SIZE");
break;
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCard.java b/src/java/com/android/internal/telephony/uicc/UiccCard.java
index 8cd6696..baad60b 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCard.java
@@ -94,7 +94,7 @@
private static final int EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE = 17;
private static final int EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE = 18;
private static final int EVENT_SIM_IO_DONE = 19;
- private static final int EVENT_CARRIER_PRIVILIGES_LOADED = 20;
+ private static final int EVENT_CARRIER_PRIVILEGES_LOADED = 20;
private static final LocalLog mLocalLog = new LocalLog(100);
@@ -158,7 +158,7 @@
log("Before privilege rules: " + mCarrierPrivilegeRules + " : " + mCardState);
if (mCarrierPrivilegeRules == null && mCardState == CardState.CARDSTATE_PRESENT) {
mCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(this,
- mHandler.obtainMessage(EVENT_CARRIER_PRIVILIGES_LOADED));
+ mHandler.obtainMessage(EVENT_CARRIER_PRIVILEGES_LOADED));
} else if (mCarrierPrivilegeRules != null
&& mCardState != CardState.CARDSTATE_PRESENT) {
mCarrierPrivilegeRules = null;
@@ -384,7 +384,7 @@
AsyncResult.forMessage((Message)ar.userObj, ar.result, ar.exception);
((Message)ar.userObj).sendToTarget();
break;
- case EVENT_CARRIER_PRIVILIGES_LOADED:
+ case EVENT_CARRIER_PRIVILEGES_LOADED:
onCarrierPriviligesLoadedMessage();
break;
default:
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
index 26c8aea..e50f40c 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
@@ -92,6 +92,8 @@
private static final String TAG_PKG_REF_DO = "CA";
private static final String TAG_AR_DO = "E3";
private static final String TAG_PERM_AR_DO = "DB";
+ private static final String TAG_AID_REF_DO = "4F";
+ private static final String CARRIER_PRIVILEGE_AID = "FFFFFFFFFFFF";
private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 1;
private static final int EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE = 2;
@@ -541,37 +543,52 @@
if (rule.startsWith(TAG_REF_DO)) {
TLV refDo = new TLV(TAG_REF_DO); //E1
rule = refDo.parse(rule, false);
-
- // Skip unrelated rules.
- if (!refDo.value.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
+ // Allow 4F tag with a default value "FF FF FF FF FF FF" to be compatible with
+ // devices having GP access control enforcer:
+ // - If no 4F tag is present, it's a CP rule.
+ // - If 4F tag has value "FF FF FF FF FF FF", it's a CP rule.
+ // - If 4F tag has other values, it's not a CP rule and Android should ignore it.
+ TLV deviceDo = new TLV(TAG_DEVICE_APP_ID_REF_DO); //C1
+ if (refDo.value.startsWith(TAG_AID_REF_DO)) {
+ TLV cpDo = new TLV(TAG_AID_REF_DO); //4F
+ String remain = cpDo.parse(refDo.value, false);
+ if (!cpDo.lengthBytes.equals("06") || !cpDo.value.equals(CARRIER_PRIVILEGE_AID)
+ || remain.isEmpty() || !remain.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
+ return null;
+ }
+ tmp = deviceDo.parse(remain, false);
+ certificateHash = deviceDo.value;
+ } else if (refDo.value.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
+ tmp = deviceDo.parse(refDo.value, false);
+ certificateHash = deviceDo.value;
+ } else {
return null;
}
-
- TLV deviceDo = new TLV(TAG_DEVICE_APP_ID_REF_DO); //C1
- tmp = deviceDo.parse(refDo.value, false);
- certificateHash = deviceDo.value;
-
if (!tmp.isEmpty()) {
- if (!tmp.startsWith(TAG_PKG_REF_DO)) {
- return null;
- }
- TLV pkgDo = new TLV(TAG_PKG_REF_DO); //CA
- pkgDo.parse(tmp, true);
- packageName = new String(IccUtils.hexStringToBytes(pkgDo.value));
+ if (!tmp.startsWith(TAG_PKG_REF_DO)) {
+ return null;
+ }
+ TLV pkgDo = new TLV(TAG_PKG_REF_DO); //CA
+ pkgDo.parse(tmp, true);
+ packageName = new String(IccUtils.hexStringToBytes(pkgDo.value));
} else {
- packageName = null;
+ packageName = null;
}
} else if (rule.startsWith(TAG_AR_DO)) {
TLV arDo = new TLV(TAG_AR_DO); //E3
rule = arDo.parse(rule, false);
-
- // Skip unrelated rules.
- if (!arDo.value.startsWith(TAG_PERM_AR_DO)) {
+ // Skip all the irrelevant tags (All the optional tags here are two bytes
+ // according to the spec GlobalPlatform Secure Element Access Control).
+ String remain = arDo.value;
+ while (!remain.isEmpty() && !remain.startsWith(TAG_PERM_AR_DO)) {
+ TLV tmpDo = new TLV(remain.substring(0, 2));
+ remain = tmpDo.parse(remain, false);
+ }
+ if (remain.isEmpty()) {
return null;
}
-
TLV permDo = new TLV(TAG_PERM_AR_DO); //DB
- permDo.parse(arDo.value, true);
+ permDo.parse(remain, true);
} else {
// Spec requires it must be either TAG_REF_DO or TAG_AR_DO.
throw new RuntimeException("Invalid Rule type");
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 65aacd1..a948b75 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -89,8 +89,6 @@
private static final int EVENT_RADIO_UNAVAILABLE = 3;
private static final int EVENT_SIM_REFRESH = 4;
- private static final String DECRYPT_STATE = "trigger_restart_framework";
-
private CommandsInterface[] mCis;
private UiccCard[] mUiccCards = new UiccCard[TelephonyManager.getDefault().getPhoneCount()];
@@ -125,12 +123,11 @@
Integer index = new Integer(i);
mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);
// TODO remove this once modem correctly notifies the unsols
- // If the device has been decrypted or FBE is supported, read SIM when radio state is
- // available.
+ // If the device is unencrypted or has been decrypted or FBE is supported,
+ // i.e. not in cryptkeeper bounce, read SIM when radio state isavailable.
// Else wait for radio to be on. This is needed for the scenario when SIM is locked --
// to avoid overlap of CryptKeeper and SIM unlock screen.
- if (DECRYPT_STATE.equals(SystemProperties.get("vold.decrypt")) ||
- StorageManager.isFileEncryptedNativeOrEmulated()) {
+ if (!StorageManager.inCryptKeeperBounce()) {
mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index);
} else {
mCis[i].registerForOn(this, EVENT_ICC_STATUS_CHANGED, index);
@@ -342,7 +339,7 @@
if (requirePowerOffOnSimRefreshReset) {
mCis[index].setRadioPower(false, null);
} else {
- mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
+ mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
}
mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
}
diff --git a/src/java/com/android/internal/telephony/util/NotificationChannelController.java b/src/java/com/android/internal/telephony/util/NotificationChannelController.java
index 88ff4cf..54d7d1a 100644
--- a/src/java/com/android/internal/telephony/util/NotificationChannelController.java
+++ b/src/java/com/android/internal/telephony/util/NotificationChannelController.java
@@ -38,12 +38,12 @@
*/
public static final String CHANNEL_ID_ALERT = "alert";
public static final String CHANNEL_ID_CALL_FORWARD = "callForward";
- public static final String CHANNEL_ID_MOBILE_DATA_ALERT = "mobileDataAlertNew";
+ public static final String CHANNEL_ID_MOBILE_DATA_STATUS = "mobileDataAlertNew";
public static final String CHANNEL_ID_SMS = "sms";
public static final String CHANNEL_ID_VOICE_MAIL = "voiceMail";
public static final String CHANNEL_ID_WFC = "wfc";
- /** deprecated channel, replaced with @see #CHANNEL_ID_MOBILE_DATA_ALERT */
+ /** deprecated channel, replaced with @see #CHANNEL_ID_MOBILE_DATA_STATUS */
private static final String CHANNEL_ID_MOBILE_DATA_ALERT_DEPRECATED = "mobileDataAlert";
/**
@@ -58,21 +58,26 @@
alertChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
+ final NotificationChannel mobileDataStatusChannel = new NotificationChannel(
+ CHANNEL_ID_MOBILE_DATA_STATUS,
+ context.getText(R.string.notification_channel_mobile_data_status),
+ NotificationManager.IMPORTANCE_LOW);
+ // allow users to block notifications from system
+ mobileDataStatusChannel.setBlockableSystem(true);
+
context.getSystemService(NotificationManager.class)
.createNotificationChannels(Arrays.asList(
new NotificationChannel(CHANNEL_ID_CALL_FORWARD,
context.getText(R.string.notification_channel_call_forward),
NotificationManager.IMPORTANCE_LOW),
- new NotificationChannel(CHANNEL_ID_MOBILE_DATA_ALERT,
- context.getText(R.string.notification_channel_mobile_data_alert),
- NotificationManager.IMPORTANCE_LOW),
new NotificationChannel(CHANNEL_ID_SMS,
context.getText(R.string.notification_channel_sms),
NotificationManager.IMPORTANCE_HIGH),
new NotificationChannel(CHANNEL_ID_WFC,
context.getText(R.string.notification_channel_wfc),
NotificationManager.IMPORTANCE_LOW),
- alertChannel));
+ alertChannel,
+ mobileDataStatusChannel));
// only for update
if (getChannel(CHANNEL_ID_VOICE_MAIL, context) != null) {
migrateVoicemailNotificationSettings(context);
diff --git a/tests/telephonytests/Android.mk b/tests/telephonytests/Android.mk
index fd2cbb2..b777fac 100644
--- a/tests/telephonytests/Android.mk
+++ b/tests/telephonytests/Android.mk
@@ -7,7 +7,7 @@
#LOCAL_STATIC_JAVA_LIBRARIES := librilproto-java
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common ims-common services.core
+LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common ims-common services.core bouncycastle
LOCAL_STATIC_JAVA_LIBRARIES := guava \
frameworks-base-testutils \
mockito-target-minus-junit4 \
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
new file mode 100644
index 0000000..4b99cbe
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
@@ -0,0 +1,321 @@
+/*
+ * 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 android.app.DownloadManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.HandlerThread;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ImsiEncryptionInfo;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+
+import com.android.org.bouncycastle.util.io.pem.PemReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.mockito.MockitoAnnotations;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.security.PublicKey;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import static android.preference.PreferenceManager.getDefaultSharedPreferences;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class CarrierKeyDownloadMgrTest extends TelephonyTest {
+
+ private static final String LOG_TAG = "CarrierKeyDownloadManager";
+
+ private CarrierKeyDownloadManager mCarrierKeyDM;
+ private CarrierActionAgentHandler mCarrierActionAgentHandler;
+
+ private String mURL = "http://www.google.com";
+
+ private static final String CERT = "-----BEGIN CERTIFICATE-----\r\nMIIFjzCCBHegAwIBAgIUPxj3SLif82Ky1RlUy8p2EWJCh8MwDQYJKoZIhvcNAQELBQAwgY0xCzAJBgNVBAYTAk5MMRIwEAYDVQQHEwlBbXN0ZXJkYW0xJTAjBgNVBAoTHFZlcml6b24gRW50ZXJwcmlzZSBTb2x1dGlvbnMxEzARBgNVBAsTCkN5YmVydHJ1c3QxLjAsBgNVBAMTJVZlcml6b24gUHVibGljIFN1cmVTZXJ2ZXIgQ0EgRzE0LVNIQTIwHhcNMTcwODE0MTc0MzM4WhcNMTkwODE0MTc0MzM4WjCBmTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFjAUBgNVBAcTDUJhc2tpbmcgUmlkZ2UxIjAgBgNVBAoTGVZlcml6b24gRGF0YSBTZXJ2aWNlcyBMTEMxHzAdBgNVBAsTFk5ldHdvcmsgU3lzdGVtIFN1cHBvcnQxGDAWBgNVBAMTD3ZpMWx2Lmltc3ZtLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALUQKWTHi4Hjpd1LQwJ87RXa0Rs3rVonvVevliqdUH5BikjhAzvIqwPSXeRQqkaRTFIyp0NKcNqGdjAaHRo43gdHeWSH331sS6CMZDg988gZznskzCqJJo6ii5FuLC8qe2YDsHxT+CefXev2rn6Bj1ei2X74uZsy5KlkBRZfFHtPdK6/EK5TpzrvcXfDyOK1rn8FTno1bQOTAhL39GPcLhdrXV7AN+lu+EBpdCqlTdcoDxsqavi/91MwUIVEzxJmycKloT6OWfU44r7+L5SYYgc88NTaGL/BvCFwHRIa1ZgYSGeAPes45792MGG7tfr/ttAGp9UEwTv2zWTxzWnRP/UCAwEAAaOCAdcwggHTMAwGA1UdEwEB/wQCMAAwTAYDVR0gBEUwQzBBBgkrBgEEAbE+ATIwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly9zZWN1cmUub21uaXJvb3QuY29tL3JlcG9zaXRvcnkwgakGCCsGAQUFBwEBBIGcMIGZMC0GCCsGAQUFBzABhiFodHRwOi8vdnBzc2cxNDIub2NzcC5vbW5pcm9vdC5jb20wMwYIKwYBBQUHMAKGJ2h0dHA6Ly9jYWNlcnQub21uaXJvb3QuY29tL3Zwc3NnMTQyLmNydDAzBggrBgEFBQcwAoYnaHR0cDovL2NhY2VydC5vbW5pcm9vdC5jb20vdnBzc2cxNDIuZGVyMBoGA1UdEQQTMBGCD3ZpMWx2Lmltc3ZtLmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFOQtu5EBZSYftHo/oxUlpM6MRDM7MD4GA1UdHwQ3MDUwM6AxoC+GLWh0dHA6Ly92cHNzZzE0Mi5jcmwub21uaXJvb3QuY29tL3Zwc3NnMTQyLmNybDAdBgNVHQ4EFgQUv5SaSyNM/yXw1v0N9TNpjsFCaPcwDQYJKoZIhvcNAQELBQADggEBACNJusTULj1KyV4RwiskKfp4wI9Hsz3ESbZS/ijF9D57BQ0UwkELU9r6rEAhsYLUvMq4sDhDbYIdupgP4MBzFnjkKult7VQm5W3nCcuHgXYFAJ9Y1a4OZAo/4hrHj70W9TsQ1ioSMjUT4F8bDUYZI0kcyH8e/+2DaTsLUpHw3L+Keu8PsJVBLnvcKJjWrZD/Bgd6JuaTX2G84i0rY0GJuO9CxLNJa6n61Mz5cqLYIuwKgiVgTA2n71YITyFICOFPFX1vSx35AWvD6aVYblxtC8mpCdF2h4s1iyrpXeji2GCJLwsNVtTtNQ4zWX3Gnq683wzkYZeyOHUyftIgAQZ+HsY=\r\n-----END CERTIFICATE-----";
+
+
+ private String mJsonStr = "{ \"carrier-keys\": [ { \"certificate\": \"" + CERT + "\", \"key-type\": \"WLAN\", \"key-identifier\": \"key1=value\", \"expiration-date\": 1502577746000 }, { \"certificate\": \"" + CERT + "\", \"key-type\": \"WLAN\", \"key-identifier\": \"key1=value\", \"expiration-date\": 1502577746000 }]}";
+
+ private String mJsonStr1 = "{ \"carrier-keys\": [ { \"public-key\": \"" + CERT + "\", \"key-type\": \"WLAN\", \"key-identifier\": \"key1=value\", \"expiration-date\": 1502577746000 }, { \"public-key\": \"" + CERT + "\", \"key-type\": \"WLAN\", \"key-identifier\": \"key1=value\", \"expiration-date\": 1502577746000 }]}";
+
+ private class CarrierActionAgentHandler extends HandlerThread {
+
+ private CarrierActionAgentHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mCarrierKeyDM = new CarrierKeyDownloadManager(mPhone);
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ logd("CarrierActionAgentTest +Setup!");
+ MockitoAnnotations.initMocks(this);
+ super.setUp(getClass().getSimpleName());
+ mCarrierActionAgentHandler = new CarrierActionAgentHandler(getClass().getSimpleName());
+ mCarrierActionAgentHandler.start();
+ waitUntilReady();
+ logd("CarrierActionAgentTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mCarrierActionAgentHandler.quit();
+ super.tearDown();
+ }
+
+ /* Checks if the expiration date is calculated correctly
+ * In this case the expiration date should be the next day.
+ */
+ @Test
+ @SmallTest
+ public void testExpirationDate1Day() {
+ java.security.PublicKey publicKey = null;
+ mCarrierKeyDM.mKeyAvailability = 3;
+ SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd");
+ Calendar cal = new GregorianCalendar();
+ cal.add(Calendar.DATE, 6);
+ Date date = cal.getTime();
+ Calendar expectedCal = new GregorianCalendar();
+ expectedCal.add(Calendar.DATE, 1);
+ String dateExpected = dt.format(expectedCal.getTime());
+ ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("mcc", "mnc", 1,
+ "keyIdentifier", publicKey, date);
+ when(mPhone.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(imsiEncryptionInfo);
+ Date expirationDate = new Date(mCarrierKeyDM.getExpirationDate());
+ assertTrue(dt.format(expirationDate).equals(dateExpected));
+ }
+
+ /**
+ * Checks if the expiration date is calculated correctly
+ * In this case the expiration date should be the expiration date of the key.
+ **/
+ @Test
+ @SmallTest
+ public void testExpirationDate7Day() {
+ java.security.PublicKey publicKey = null;
+ mCarrierKeyDM.mKeyAvailability = 3;
+ SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd");
+ Calendar cal = new GregorianCalendar();
+ cal.add(Calendar.DATE, 10);
+ Date date = cal.getTime();
+ Calendar expectedCal = new GregorianCalendar();
+ expectedCal.add(Calendar.DATE, 3);
+ String dateExpected = dt.format(expectedCal.getTime());
+ ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("mcc", "mnc", 1,
+ "keyIdentifier", publicKey, date);
+ when(mPhone.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(imsiEncryptionInfo);
+ Date expirationDate = new Date(mCarrierKeyDM.getExpirationDate());
+ assertTrue(dt.format(expirationDate).equals(dateExpected));
+ }
+
+ /**
+ * Checks if the json is parse correctly.
+ * Verify that setCarrierInfoForImsiEncryption is called with the right params
+ **/
+ @Test
+ @SmallTest
+ public void testParseJson() {
+ ByteArrayInputStream certBytes = new ByteArrayInputStream(CERT.getBytes());
+ Reader fRd = new BufferedReader(new InputStreamReader(certBytes));
+ PemReader reader = new PemReader(fRd);
+ Pair<PublicKey, Long> keyInfo = null;
+ try {
+ keyInfo = mCarrierKeyDM.getKeyInformation(reader.readPemObject().getContent());
+ } catch (Exception e) {
+ fail(LOG_TAG + "exception creating public key");
+ }
+ ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("310", "270", 2,
+ "key1=value", keyInfo.first, new Date(keyInfo.second));
+ String mccMnc = "310:270";
+ mCarrierKeyDM.parseJsonAndPersistKey(mJsonStr, mccMnc);
+ verify(mPhone, times(2)).setCarrierInfoForImsiEncryption(
+ (Matchers.refEq(imsiEncryptionInfo)));
+ }
+
+ /**
+ * Checks if the json is parse correctly.
+ * Same as testParseJason, except that the test looks for the "public-key" field.
+ **/
+ @Test
+ @SmallTest
+ public void testParseJsonPublicKey() {
+ ByteArrayInputStream certBytes = new ByteArrayInputStream(CERT.getBytes());
+ Reader fRd = new BufferedReader(new InputStreamReader(certBytes));
+ PemReader reader = new PemReader(fRd);
+ Pair<PublicKey, Long> keyInfo = null;
+ try {
+ keyInfo = mCarrierKeyDM.getKeyInformation(reader.readPemObject().getContent());
+ } catch (Exception e) {
+ fail(LOG_TAG + "exception creating public key");
+ }
+ ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("310", "270", 2,
+ "key1=value", keyInfo.first, new Date(keyInfo.second));
+ String mccMnc = "310:270";
+ mCarrierKeyDM.parseJsonAndPersistKey(mJsonStr1, mccMnc);
+ verify(mPhone, times(2)).setCarrierInfoForImsiEncryption(
+ (Matchers.refEq(imsiEncryptionInfo)));
+ }
+
+ /**
+ * Checks if the json is parse correctly.
+ * Since the json is bad, we want to verify that savePublicKey is not called.
+ **/
+ @Test
+ @SmallTest
+ public void testParseBadJsonFail() {
+ String mccMnc = "310:290";
+ String badJsonStr = "{badJsonString}";
+ mCarrierKeyDM.parseJsonAndPersistKey(badJsonStr, mccMnc);
+ verify(mPhone, times(0)).setCarrierInfoForImsiEncryption(any());
+ }
+
+ /**
+ * Checks if the download is valid.
+ * returns true since the mnc/mcc is valid.
+ **/
+ @Test
+ @SmallTest
+ public void testIsValidDownload() {
+ String mccMnc = "310:260";
+ when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
+ assertTrue(mCarrierKeyDM.isValidDownload(mccMnc));
+ }
+
+ /**
+ * Checks if the download is valid.
+ * returns false since the mnc/mcc is in-valid.
+ **/
+ @Test
+ @SmallTest
+ public void testIsValidDownloadFail() {
+ String mccMnc = "310:290";
+ when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
+ assertFalse(mCarrierKeyDM.isValidDownload(mccMnc));
+ }
+
+ /**
+ * Tests if the key is enabled.
+ * tests for all bit-mask value.
+ **/
+ @Test
+ @SmallTest
+ public void testIsKeyEnabled() {
+ mCarrierKeyDM.mKeyAvailability = 3;
+ assertTrue(mCarrierKeyDM.isKeyEnabled(1));
+ assertTrue(mCarrierKeyDM.isKeyEnabled(2));
+ mCarrierKeyDM.mKeyAvailability = 2;
+ assertFalse(mCarrierKeyDM.isKeyEnabled(1));
+ assertTrue(mCarrierKeyDM.isKeyEnabled(2));
+ mCarrierKeyDM.mKeyAvailability = 1;
+ assertTrue(mCarrierKeyDM.isKeyEnabled(1));
+ assertFalse(mCarrierKeyDM.isKeyEnabled(2));
+ }
+
+ /**
+ * Tests sending the ACTION_DOWNLOAD_COMPLETE intent.
+ * Verify that the alarm will kick-off the next day.
+ **/
+ @Test
+ @SmallTest
+ public void testDownloadComplete() {
+ SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit();
+ String mccMnc = "310:260";
+ int slotId = mPhone.getPhoneId();
+ editor.putString("CARRIER_KEY_DM_MCC_MNC" + slotId, mccMnc);
+ editor.commit();
+
+ SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd");
+ Calendar expectedCal = new GregorianCalendar();
+ expectedCal.add(Calendar.DATE, 1);
+ String dateExpected = dt.format(expectedCal.getTime());
+
+ when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
+ Intent mIntent = new Intent(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mContext.sendBroadcast(mIntent);
+ Date expirationDate = new Date(mCarrierKeyDM.getExpirationDate());
+ assertTrue(dt.format(expirationDate).equals(dateExpected));
+ }
+
+ /**
+ * Test sending the ACTION_CARRIER_CONFIG_CHANGED intent.
+ * Verify that the right mnc/mcc gets stored in the preferences.
+ **/
+ @Test
+ @SmallTest
+ public void testCarrierConfigChanged() {
+ CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ int slotId = mPhone.getPhoneId();
+ PersistableBundle bundle = carrierConfigManager.getConfigForSubId(slotId);
+ bundle.putInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, 3);
+ bundle.putString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, mURL);
+
+ when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
+ Intent mIntent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
+ mContext.sendBroadcast(mIntent);
+ SharedPreferences preferences = getDefaultSharedPreferences(mContext);
+ String mccMnc = preferences.getString("CARRIER_KEY_DM_MCC_MNC" + slotId, null);
+ assertTrue(mccMnc.equals("310:260"));
+ }
+
+ /**
+ * Tests sending the INTENT_KEY_RENEWAL_ALARM_PREFIX intent.
+ * Verify that the right mnc/mcc gets stored in the preferences.
+ **/
+ @Test
+ @SmallTest
+ public void testAlarmRenewal() {
+ CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ int slotId = mPhone.getPhoneId();
+ PersistableBundle bundle = carrierConfigManager.getConfigForSubId(slotId);
+ bundle.putInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, 3);
+ bundle.putString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, mURL);
+
+ when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
+ Intent mIntent = new Intent("com.android.internal.telephony.carrier_key_download_alarm"
+ + slotId);
+ mContext.sendBroadcast(mIntent);
+ SharedPreferences preferences = getDefaultSharedPreferences(mContext);
+ String mccMnc = preferences.getString("CARRIER_KEY_DM_MCC_MNC" + slotId, null);
+ assertTrue(mccMnc.equals("310:260"));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
new file mode 100644
index 0000000..8be2f60
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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 android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Resources;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit tests for {@link com.android.internal.telephony.CarrierServiceStateTracker}.
+ */
+@SmallTest
+public class CarrierServiceStateTrackerTest extends TelephonyTest {
+ public static final String LOG_TAG = "CSST";
+ public static final int TEST_TIMEOUT = 5000;
+
+ private CarrierServiceStateTracker mCarrierSST;
+ private CarrierServiceStateTrackerTestHandler mCarrierServiceStateTrackerTestHandler;
+ private CarrierServiceStateTracker.PrefNetworkNotification mPrefNetworkNotification;
+ private CarrierServiceStateTracker.EmergencyNetworkNotification mEmergencyNetworkNotification;
+
+ @Mock Context mContext;
+ @Mock ServiceStateTracker mServiceStateTracker;
+ @Mock NotificationManager mNotificationManager;
+ @Mock Resources mResources;
+
+ private class CarrierServiceStateTrackerTestHandler extends HandlerThread {
+
+ private CarrierServiceStateTrackerTestHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mCarrierSST = spy(new CarrierServiceStateTracker(mPhone, mServiceStateTracker));
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ logd(LOG_TAG + "Setup!");
+ super.setUp(getClass().getSimpleName());
+ mCarrierServiceStateTrackerTestHandler =
+ new CarrierServiceStateTrackerTestHandler(getClass().getSimpleName());
+ mCarrierServiceStateTrackerTestHandler.start();
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+ waitUntilReady();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mCarrierServiceStateTrackerTestHandler.quit();
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testCancelBothNotifications() {
+ logd(LOG_TAG + ":testCancelBothNotifications()");
+ Message notificationMsg = mCarrierSST.obtainMessage(
+ CarrierServiceStateTracker.CARRIER_EVENT_DATA_REGISTRATION, null);
+ doReturn(false).when(mCarrierSST).evaluateSendingMessage(any());
+ doReturn(mNotificationManager).when(mCarrierSST).getNotificationManager(any());
+ mCarrierSST.handleMessage(notificationMsg);
+ waitForHandlerAction(mCarrierSST, TEST_TIMEOUT);
+ verify(mNotificationManager).cancel(
+ CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK);
+ verify(mNotificationManager).cancel(
+ CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendBothNotifications() {
+ logd(LOG_TAG + ":testSendBothNotifications()");
+ Notification.Builder mNotificationBuilder = new Notification.Builder(mContext);
+ Message notificationMsg = mCarrierSST.obtainMessage(
+ CarrierServiceStateTracker.CARRIER_EVENT_DATA_DEREGISTRATION, null);
+ doReturn(true).when(mCarrierSST).evaluateSendingMessage(any());
+ doReturn(false).when(mCarrierSST).isRadioOffOrAirplaneMode();
+ doReturn(0).when(mCarrierSST).getDelay(any());
+ doReturn(mNotificationBuilder).when(mCarrierSST).getNotificationBuilder(any());
+ doReturn(mNotificationManager).when(mCarrierSST).getNotificationManager(any());
+ mCarrierSST.handleMessage(notificationMsg);
+ waitForHandlerAction(mCarrierSST, TEST_TIMEOUT);
+ verify(mNotificationManager).notify(
+ eq(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK), isA(Notification.class));
+ verify(mNotificationManager).notify(
+ eq(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK), any());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ClientWakelockAccountantTest.java b/tests/telephonytests/src/com/android/internal/telephony/ClientWakelockAccountantTest.java
index 2167c13..f7132b8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ClientWakelockAccountantTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ClientWakelockAccountantTest.java
@@ -18,7 +18,6 @@
import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import android.test.TestRunner;
import android.os.Build;
import android.util.Log;
import android.telephony.Rlog;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 9a03c44..2afdcf9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -27,6 +27,7 @@
import android.app.AlarmManager;
import android.app.AppOpsManager;
+import android.app.DownloadManager;
import android.app.NotificationManager;
import android.app.usage.UsageStatsManager;
import android.content.BroadcastReceiver;
@@ -240,6 +241,8 @@
return mEuiccManager;
case Context.TELECOM_SERVICE:
return mTelecomManager;
+ case Context.DOWNLOAD_SERVICE:
+ return mDownloadManager;
case Context.DISPLAY_SERVICE:
case Context.POWER_SERVICE:
// PowerManager and DisplayManager are final classes so cannot be mocked,
@@ -442,7 +445,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
@@ -489,6 +499,7 @@
private final Resources mResources = mock(Resources.class);
private final PackageManager mPackageManager = mock(PackageManager.class);
private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
+ private final DownloadManager mDownloadManager = mock(DownloadManager.class);
private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class);
private final NotificationManager mNotificationManager = mock(NotificationManager.class);
private final UserManager mUserManager = mock(UserManager.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
index 30122f9..f3066f4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
@@ -53,7 +53,7 @@
private GsmCdmaCallTracker mCTUT;
private GsmCdmaCTHandlerThread mGsmCdmaCTHandlerThread;
@Mock
- GsmCdmaCall mCall;
+ GsmCdmaConnection mConnection;
@Mock
private Handler mHandler;
@@ -166,8 +166,8 @@
assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
assertEquals(1, mCTUT.mForegroundCall.getConnections().size());
/* get the reference of the connection before reject */
- Connection mConnection = mCTUT.mForegroundCall.getConnections().get(0);
- assertEquals(DisconnectCause.NOT_DISCONNECTED, mConnection.getDisconnectCause());
+ Connection connection = mCTUT.mForegroundCall.getConnections().get(0);
+ assertEquals(DisconnectCause.NOT_DISCONNECTED, connection.getDisconnectCause());
logd("hang up MO call after pickup");
try {
mCTUT.hangup(mCTUT.mForegroundCall);
@@ -180,11 +180,12 @@
assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mForegroundCall.getState());
assertEquals(0, mCTUT.mForegroundCall.getConnections().size());
assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
- assertEquals(DisconnectCause.LOCAL, mConnection.getDisconnectCause());
+ assertEquals(DisconnectCause.LOCAL, connection.getDisconnectCause());
}
@FlakyTest
+ @Ignore
@Test
@MediumTest
public void testMOCallPendingHangUp() {
@@ -276,9 +277,9 @@
testMTCallRinging();
logd("MT call ringing and rejected ");
/* get the reference of the connection before reject */
- Connection mConnection = mCTUT.mRingingCall.getConnections().get(0);
- assertNotNull(mConnection);
- assertEquals(DisconnectCause.NOT_DISCONNECTED, mConnection.getDisconnectCause());
+ Connection connection = mCTUT.mRingingCall.getConnections().get(0);
+ assertNotNull(connection);
+ assertEquals(DisconnectCause.NOT_DISCONNECTED, connection.getDisconnectCause());
try {
mCTUT.rejectCall();
} catch(Exception ex) {
@@ -290,11 +291,12 @@
assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mForegroundCall.getState());
assertEquals(0, mCTUT.mForegroundCall.getConnections().size());
/* ? why rejectCall didnt -> hang up locally to set the cause to LOCAL? */
- assertEquals(DisconnectCause.INCOMING_MISSED, mConnection.getDisconnectCause());
+ assertEquals(DisconnectCause.INCOMING_MISSED, connection.getDisconnectCause());
}
@FlakyTest
+ @Ignore
@Test
@MediumTest
public void testMOCallSwitchHangupForeGround() {
@@ -353,7 +355,8 @@
testMOCallPickUp();
ArgumentCaptor<Message> mCaptorMessage = ArgumentCaptor.forClass(Message.class);
ArgumentCaptor<Long> mCaptorLong = ArgumentCaptor.forClass(Long.class);
- verify(mHandler,times(1)).sendMessageAtTime(mCaptorMessage.capture(), mCaptorLong.capture());
+ verify(mHandler, times(1))
+ .sendMessageAtTime(mCaptorMessage.capture(), mCaptorLong.capture());
assertEquals(VOICE_CALL_STARTED_EVENT, mCaptorMessage.getValue().what);
}
@@ -367,7 +370,8 @@
ArgumentCaptor<Message> mCaptorMessage = ArgumentCaptor.forClass(Message.class);
ArgumentCaptor<Long> mCaptorLong = ArgumentCaptor.forClass(Long.class);
testMOCallHangup();
- verify(mHandler,times(1)).sendMessageAtTime(mCaptorMessage.capture(), mCaptorLong.capture());
+ verify(mHandler, times(1))
+ .sendMessageAtTime(mCaptorMessage.capture(), mCaptorLong.capture());
assertEquals(VOICE_CALL_ENDED_EVENT, mCaptorMessage.getValue().what);
}
@@ -407,5 +411,28 @@
assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mBackgroundCall.getState());
assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mRingingCall.getState());
}
+
+ @Test
+ @SmallTest
+ public void testUpdatePhoneTypeWithActiveCall() {
+ // verify getCurrentCalls is called on init
+ verify(mSimulatedCommandsVerifier).getCurrentCalls(any(Message.class));
+
+ // fake connection
+ mCTUT.mConnections[0] = mConnection;
+
+ // update phone type (call the function on same thread as the call tracker)
+ Handler updatePhoneTypeHandler = new Handler(mCTUT.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ mCTUT.updatePhoneType();
+ }
+ };
+ updatePhoneTypeHandler.sendEmptyMessage(0);
+ waitForMs(100);
+
+ // verify that the active call is disconnected
+ verify(mConnection).onDisconnect(DisconnectCause.ERROR_UNSPECIFIED);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 309568a..e1e3cd2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -513,6 +513,7 @@
* received when obj is created and that are received on phone type switch
*/
@FlakyTest
+ @Ignore
@Test
@SmallTest
public void testHandleInitialMessages() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index f4caf48..5e3f948 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -26,6 +26,7 @@
import android.telephony.PhoneNumberUtils;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.SpannableStringBuilder;
+import android.text.style.TtsSpan;
import org.junit.Ignore;
import org.junit.Test;
@@ -428,6 +429,7 @@
// To run this test, the device has to be registered with network
@FlakyTest
+ @Ignore
public void testCheckAndProcessPlusCode() {
assertEquals("0118475797000",
PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+8475797000"));
@@ -750,4 +752,21 @@
assertEquals("tim_123", PhoneNumberUtils.getUsernameFromUriNumber("tim_123@zzz.org"));
assertEquals("5103331245", PhoneNumberUtils.getUsernameFromUriNumber("5103331245"));
}
+
+ @SmallTest
+ @Test
+ public void testCreateTtsSpan() {
+ checkTtsNumber("650 555 1212", "650-555-1212");
+ checkTtsNumber("6505551212", "+1-650-555-1212");
+ checkTtsNumber("232", "232");
+ checkTtsNumber("*232", "*232");
+ checkTtsNumber("*232#", "*232#");
+ checkTtsNumber("*650 555 1212#", "*650-555-1212#");
+ }
+
+ private void checkTtsNumber(String expected, String sourceNumber) {
+ TtsSpan ttsSpan = PhoneNumberUtils.createTtsSpan(sourceNumber);
+ assertEquals(TtsSpan.TYPE_TELEPHONE, ttsSpan.getType());
+ assertEquals(expected, ttsSpan.getArgs().getString(TtsSpan.ARG_NUMBER_PARTS));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 2096231..795cb98 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -45,10 +45,12 @@
import android.os.HandlerThread;
import android.os.Message;
import android.os.Parcel;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
import android.os.WorkSource;
import android.support.test.filters.FlakyTest;
+import android.telephony.CarrierConfigManager;
import android.telephony.CellInfo;
import android.telephony.CellInfoGsm;
import android.telephony.ServiceState;
@@ -66,6 +68,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -87,6 +90,7 @@
private ServiceStateTracker sst;
private ServiceStateTrackerTestHandler mSSTTestHandler;
+ private PersistableBundle mBundle;
private static final int EVENT_REGISTERED_TO_NETWORK = 1;
private static final int EVENT_SUBSCRIPTION_INFO_READY = 2;
@@ -123,14 +127,12 @@
mPhone.mDcTracker = mDct;
replaceInstance(ProxyController.class, "sProxyController", null, mProxyController);
+ mBundle = mContextFixture.getCarrierConfigBundle();
+ mBundle.putStringArray(
+ CarrierConfigManager.KEY_ROAMING_OPERATOR_STRING_ARRAY, new String[]{"123456"});
- mContextFixture.putStringArrayResource(
- com.android.internal.R.array.config_sameNamedOperatorConsideredRoaming,
- new String[]{"123456"});
-
- mContextFixture.putStringArrayResource(
- com.android.internal.R.array.config_operatorConsideredNonRoaming,
- new String[]{"123456"});
+ mBundle.putStringArray(
+ CarrierConfigManager.KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, new String[]{"123456"});
mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
mSimulatedCommands.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_HSPA);
@@ -238,6 +240,7 @@
}
@FlakyTest
+ @Ignore
@Test
@MediumTest
public void testSpnUpdateShowPlmnOnly() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/Sms7BitEncodingTranslatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/Sms7BitEncodingTranslatorTest.java
index 283b170..cf11d1b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/Sms7BitEncodingTranslatorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/Sms7BitEncodingTranslatorTest.java
@@ -16,21 +16,23 @@
package com.android.internal.telephony;
-import android.support.test.filters.FlakyTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-
-import java.io.UnsupportedEncodingException;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
+import android.support.test.filters.FlakyTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.io.UnsupportedEncodingException;
+
+@Ignore
public class Sms7BitEncodingTranslatorTest extends TelephonyTest {
@Mock
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
index 141ca45..66742dd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
@@ -243,6 +243,33 @@
}
@Test @SmallTest
+ public void testSetGetDisplayNameSrc() {
+ testInsertSim();
+
+ /* Get SUB ID */
+ int[] subIds = mSubscriptionControllerUT.getActiveSubIdList();
+ assertTrue(subIds != null && subIds.length != 0);
+ int subID = subIds[0];
+
+ /* Setting */
+ String disName = "TESTING";
+ long nameSource = 1;
+ mSubscriptionControllerUT.setDisplayNameUsingSrc(disName, subID, nameSource);
+ SubscriptionInfo subInfo = mSubscriptionControllerUT
+ .getActiveSubscriptionInfo(subID, mCallingPackage);
+ assertNotNull(subInfo);
+ assertEquals(disName, subInfo.getDisplayName());
+ assertEquals(nameSource, subInfo.getNameSource());
+
+ /* verify broadcast intent */
+ ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, atLeast(1)).sendBroadcast(captorIntent.capture());
+ assertEquals(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED,
+ captorIntent.getValue().getAction());
+
+ }
+
+ @Test @SmallTest
public void testCleanUpSIM() {
testInsertSim();
assertFalse(mSubscriptionControllerUT.isActiveSubId(1));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
index ab7fd7f..55036ad 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
@@ -17,27 +17,24 @@
package com.android.internal.telephony.cdma;
import android.hardware.radio.V1_0.CdmaSmsMessage;
-import android.os.Parcel;
import android.support.test.filters.FlakyTest;
+import android.telephony.Rlog;
import android.telephony.SmsCbCmasInfo;
import android.telephony.SmsCbMessage;
import android.telephony.cdma.CdmaSmsCbProgramData;
import android.test.AndroidTestCase;
-import android.telephony.Rlog;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.cdma.SmsMessageConverter;
import com.android.internal.telephony.cdma.sms.BearerData;
import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import com.android.internal.telephony.cdma.sms.UserData;
-import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.util.BitwiseOutputStream;
+import org.junit.Ignore;
import org.junit.Test;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
@@ -482,7 +479,9 @@
// VZW requirement is to discard message with unsupported charset. Verify that we return null
// for this unsupported character set.
@FlakyTest
- @Test @SmallTest
+ @Ignore
+ @Test
+ @SmallTest
public void testCmasUnsupportedCharSet() throws Exception {
SmsMessage msg = createCmasSmsMessage(SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT,
12345, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
index 1e34910..21048ff 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -296,7 +296,6 @@
assertFalse(getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
- assertTrue(getNetworkInfo().isMetered());
}
@Test
@@ -312,6 +311,5 @@
assertTrue(getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
- assertFalse(getNetworkInfo().isMetered());
}
}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
index 1ca9acc..0983c27 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -849,6 +849,7 @@
// Test for API carrierActionSetMeteredApnsEnabled.
@FlakyTest
+ @Ignore
@Test
@MediumTest
public void testCarrierActionSetMeteredApnsEnabled() throws Exception {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
index a60b502..6d1cf8b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
@@ -22,7 +22,6 @@
import android.net.NetworkRequest;
import android.net.StringNetworkSpecifier;
import android.os.Binder;
-import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
@@ -39,6 +38,7 @@
import com.android.internal.telephony.mocks.SubscriptionMonitorMock;
import com.android.internal.telephony.mocks.TelephonyRegistryMock;
+import org.junit.Ignore;
public class TelephonyNetworkFactoryTest extends AndroidTestCase {
private final static String LOG_TAG = "TelephonyNetworkFactoryTest";
@@ -65,18 +65,29 @@
final Looper looper;
DcTrackerMock dcTrackerMock;
final Context contextMock;
+ private Object mLock = new Object();
+ private static final int MAX_INIT_WAIT_MS = 30000; // 30 seconds
TestSetup(int numPhones) {
- handlerThread = new HandlerThread("TelephonyNetworkFactoryTest");
- handlerThread.start();
- looper = handlerThread.getLooper();
-
- Handler myHandler = new Handler(looper) {
- public void handleMessage(Message msg) {
- if (dcTrackerMock == null) dcTrackerMock = new DcTrackerMock();
+ handlerThread = new HandlerThread("TelephonyNetworkFactoryTest") {
+ @Override
+ public void onLooperPrepared() {
+ synchronized (mLock) {
+ if (dcTrackerMock == null) dcTrackerMock = new DcTrackerMock();
+ mLock.notifyAll();
+ }
}
};
- myHandler.obtainMessage(0).sendToTarget();
+ handlerThread.start();
+ // wait until dct created
+ synchronized (mLock) {
+ try {
+ mLock.wait(MAX_INIT_WAIT_MS);
+ } catch (InterruptedException ie) {
+ }
+ if (dcTrackerMock == null) fail("failed to initialize dct");
+ }
+ looper = handlerThread.getLooper();
final ContextFixture contextFixture = new ContextFixture();
String[] networkConfigString = getContext().getResources().getStringArray(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index 61b4b4c..6e14aca 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -47,8 +47,9 @@
import android.os.UserManager;
import android.provider.Telephony;
import android.support.test.filters.FlakyTest;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
import android.test.mock.MockContentResolver;
-import android.test.suitebuilder.annotation.MediumTest;
import com.android.internal.telephony.FakeSmsContentProvider;
import com.android.internal.telephony.InboundSmsHandler;
@@ -64,6 +65,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -225,6 +227,8 @@
assertEquals("IdleState", getCurrentState().getName());
}
+ @FlakyTest
+ @Ignore
@Test
@MediumTest
public void testNewSms() {
@@ -297,6 +301,8 @@
verifyDataSmsIntentBroadcasts(1);
}
+ @FlakyTest
+ @Ignore
@Test
@MediumTest
public void testInjectSms() {
@@ -483,6 +489,7 @@
}
@FlakyTest
+ @Ignore
@Test
@MediumTest
public void testMultiPartSms() {
@@ -752,6 +759,7 @@
}
@FlakyTest
+ @Ignore
@Test
@MediumTest
public void testBroadcastUndeliveredMultiPart() throws Exception {
@@ -774,4 +782,28 @@
verifySmsIntentBroadcasts(0);
}
+
+ @FlakyTest
+ @Ignore
+ @Test
+ @LargeTest
+ public void testWaitingStateTimeout() throws Exception {
+ transitionFromStartupToIdle();
+
+ // send new SMS to state machine and verify that triggers SMS_DELIVER_ACTION
+ mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS,
+ new AsyncResult(null, mSmsMessage, null));
+ waitForMs(100);
+
+ ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(1)).sendBroadcast(
+ intentArgumentCaptor.capture());
+ assertEquals(Telephony.Sms.Intents.SMS_DELIVER_ACTION,
+ intentArgumentCaptor.getAllValues().get(0).getAction());
+ assertEquals("WaitingState", getCurrentState().getName());
+
+ waitForMs(InboundSmsHandler.STATE_TIMEOUT + 300);
+
+ assertEquals("IdleState", getCurrentState().getName());
+ }
}
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 8ab749c..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,9 +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;
@@ -35,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;
@@ -42,17 +49,23 @@
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;
import org.junit.After;
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;
@@ -65,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;
@@ -143,6 +158,7 @@
}
@FlakyTest
+ @Ignore
@Test @MediumTest
public void testSendSmsToEmergencyNumber_notifiesBlockedNumberProvider() throws Exception {
setupMockPackagePermissionChecks();
@@ -170,7 +186,10 @@
}
}
- @Test @SmallTest
+ @Test
+ @SmallTest
+ @FlakyTest
+ @Ignore
public void testSendTextWithInvalidDestAddr() throws Exception {
// unmock ActivityManager to be able to register receiver, create real PendingIntent and
// receive TEST_INTENT
@@ -191,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());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java
index b01ae4a..8baa6cd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java
@@ -16,40 +16,32 @@
package com.android.internal.telephony.imsphone;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.verify;
+
+import android.net.Uri;
+import android.support.test.filters.FlakyTest;
+
import com.android.ims.ImsCallProfile;
import com.android.ims.ImsExternalCallState;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.Connection;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import android.net.Uri;
-import android.support.test.filters.FlakyTest;
import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
import java.util.List;
-import java.util.ListIterator;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
/**
* Unit tests for the {@link ImsExternalCallTracker}.
*/
+@Ignore
public class ImsExternalCallTrackerTest {
private static final Uri TEST_ADDRESS = Uri.parse("tel:6505551212");
private static final int CALL_ID = 1;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
index 4fc9411..5f88bb1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
@@ -16,6 +16,14 @@
package com.android.internal.telephony.imsphone;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
import android.support.test.filters.FlakyTest;
import android.test.suitebuilder.annotation.SmallTest;
@@ -25,17 +33,10 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mock;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
public class ImsPhoneCallTest extends TelephonyTest {
@Mock
ImsPhoneConnection mConnection1;
@@ -61,6 +62,7 @@
}
@FlakyTest
+ @Ignore
@Test
@SmallTest
public void testAttachDetach() {
@@ -84,6 +86,7 @@
}
@FlakyTest
+ @Ignore
@Test
@SmallTest
public void testConnectionDisconnected() {
@@ -102,6 +105,7 @@
}
@FlakyTest
+ @Ignore
@Test
@SmallTest
public void testHangup() {
@@ -114,6 +118,7 @@
}
@FlakyTest
+ @Ignore
@Test
@SmallTest
public void testUpdateRingBackTone() {
@@ -146,6 +151,7 @@
}
@FlakyTest
+ @Ignore
@Test
@SmallTest
public void testMultiParty() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index e3f265a..71f4cc4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.isNull;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -42,9 +43,14 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
+import android.os.PersistableBundle;
import android.support.test.filters.FlakyTest;
import android.telecom.VideoProfile;
+import android.telephony.CarrierConfigManager;
+import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
import android.telephony.ims.feature.ImsFeature;
import android.test.suitebuilder.annotation.SmallTest;
@@ -87,6 +93,8 @@
private ImsCallSession mImsCallSession;
@Mock
private SharedPreferences mSharedPreferences;
+ @Mock
+ private ImsPhoneConnection.Listener mImsPhoneConnectionListener;
private Handler mCTHander;
private class ImsCTHandlerThread extends HandlerThread {
@@ -102,6 +110,7 @@
ImsReasonInfo.CODE_ANSWERED_ELSEWHERE);
mCTUT.addReasonCodeRemapping(510, "Call answered elsewhere.",
ImsReasonInfo.CODE_ANSWERED_ELSEWHERE);
+ mCTUT.setDataEnabled(true);
mCTHander = new Handler(mCTUT.getLooper());
setReady(true);
}
@@ -155,7 +164,7 @@
}
}).when(mImsCall).hold();
- doReturn(mImsCallSession).when(mImsCall).getCallSession();
+ mImsCall.attachSession(mImsCallSession);
}
@Before
@@ -166,6 +175,7 @@
mImsManagerInstances.put(mImsPhone.getPhoneId(), mImsManager);
mImsCall = spy(new ImsCall(mContext, mImsCallProfile));
mSecondImsCall = spy(new ImsCall(mContext, mImsCallProfile));
+ mImsPhoneConnectionListener = mock(ImsPhoneConnection.Listener.class);
imsCallMocking(mImsCall);
imsCallMocking(mSecondImsCall);
doReturn(ImsFeature.STATE_READY).when(mImsManager).getImsServiceStatus();
@@ -188,6 +198,7 @@
public ImsCall answer(InvocationOnMock invocation) throws Throwable {
mImsCallListener =
(ImsCall.Listener) invocation.getArguments()[2];
+ mImsCall.setListener(mImsCallListener);
return mImsCall;
}
}).when(mImsManager).takeCall(eq(mServiceId), (Intent) any(), (ImsCall.Listener) any());
@@ -197,6 +208,7 @@
public ImsCall answer(InvocationOnMock invocation) throws Throwable {
mImsCallListener =
(ImsCall.Listener) invocation.getArguments()[3];
+ mSecondImsCall.setListener(mImsCallListener);
return mSecondImsCall;
}
}).when(mImsManager).makeCall(eq(mServiceId), eq(mImsCallProfile), (String []) any(),
@@ -261,6 +273,9 @@
assertEquals(PhoneConstants.State.RINGING, mCTUT.getState());
assertTrue(mCTUT.mRingingCall.isRinging());
assertEquals(1, mCTUT.mRingingCall.getConnections().size());
+ ImsPhoneConnection connection =
+ (ImsPhoneConnection) mCTUT.mRingingCall.getConnections().get(0);
+ connection.addListener(mImsPhoneConnectionListener);
}
@Test
@@ -419,6 +434,7 @@
}
@FlakyTest
+ @Ignore
@Test
@SmallTest
public void testImsMOCallDial() {
@@ -567,4 +583,71 @@
verify(mImsManager, times(2)).open(anyInt(), nullable(PendingIntent.class),
nullable(ImsConnectionStateListener.class));
}
+
+ /**
+ * Test notification of handover from LTE to WIFI and WIFI to LTE and ensure that the expected
+ * connection events are sent.
+ */
+ @Test
+ @SmallTest
+ public void testNotifyHandovers() {
+ setupCarrierConfig();
+
+ //establish a MT call
+ testImsMTCallAccept();
+ ImsPhoneConnection connection =
+ (ImsPhoneConnection) mCTUT.mForegroundCall.getConnections().get(0);
+ ImsCall call = connection.getImsCall();
+ // Needs to be a video call to see this signalling.
+ doReturn(true).when(mImsCallProfile).isVideoCall();
+
+ // First handover from LTE to WIFI; this takes us into a mid-call state.
+ call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(),
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN,
+ new ImsReasonInfo());
+ // Handover back to LTE.
+ call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(),
+ ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, ServiceState.RIL_RADIO_TECHNOLOGY_LTE,
+ new ImsReasonInfo());
+ verify(mImsPhoneConnectionListener).onConnectionEvent(eq(
+ TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE), isNull());
+
+ // Finally hand back to WIFI
+ call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(),
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN,
+ new ImsReasonInfo());
+ verify(mImsPhoneConnectionListener).onConnectionEvent(eq(
+ TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI), isNull());
+ }
+
+ /**
+ * Configure carrier config options relevant to the unit test.
+ */
+ public void setupCarrierConfig() {
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL,
+ true);
+ bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL,
+ true);
+ bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, true);
+ mCTUT.updateCarrierConfigCache(bundle);
+ }
+
+ @Test
+ @SmallTest
+ public void testLowBatteryDisconnectMidCall() {
+ assertEquals(DisconnectCause.LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY, 0), Call.State.ACTIVE));
+ assertEquals(DisconnectCause.LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_LOW_BATTERY, 0), Call.State.ACTIVE));
+ }
+
+ @Test
+ @SmallTest
+ public void testLowBatteryDisconnectDialing() {
+ assertEquals(DisconnectCause.DIAL_LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY, 0), Call.State.DIALING));
+ assertEquals(DisconnectCause.DIAL_LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
+ new ImsReasonInfo(ImsReasonInfo.CODE_LOW_BATTERY, 0), Call.State.DIALING));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
index 43763ec..9210a08 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
@@ -574,7 +574,7 @@
}
@Override
- public boolean isTetheringSupported() {
+ public boolean isTetheringSupported(String callerPkg) {
throw new RuntimeException("not implemented");
}
@@ -651,6 +651,11 @@
}
@Override
+ public boolean isAlwaysOnVpnPackageSupported(int userId, String packageName) {
+ throw new RuntimeException("not implemented");
+ }
+
+ @Override
public boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdownEnabled) {
throw new RuntimeException("not implemented");
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
new file mode 100644
index 0000000..e6ca407
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2016 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.uicc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doAnswer;
+
+import android.content.pm.Signature;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class UiccCarrierPrivilegeRulesTest extends TelephonyTest {
+ private UiccCarrierPrivilegeRules mUiccCarrierPrivilegeRules;
+ public UiccCarrierPrivilegeRulesTest() {
+ super();
+ }
+ private UiccCarrierPrivilegeRulesHandlerThread mTestHandlerThread;
+ private Handler mHandler;
+
+ private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 1;
+ private static final int EVENT_TEST_DONE = 2;
+
+ @Mock
+ private UiccCard mUiccCard;
+
+ private class UiccCarrierPrivilegeRulesHandlerThread extends HandlerThread {
+
+ private UiccCarrierPrivilegeRulesHandlerThread(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ /* create a custom handler for the Handler Thread */
+ mHandler = new Handler(mTestHandlerThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
+ /* Upon handling this event, new CarrierPrivilegeRule
+ will be created with the looper of HandlerThread */
+ mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(
+ mUiccCard, mHandler.obtainMessage(EVENT_TEST_DONE));
+ break;
+ case EVENT_TEST_DONE:
+ setReady(true);
+ break;
+ default:
+ logd("Unknown Event " + msg.what);
+ }
+ }
+ };
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mTestHandlerThread = new UiccCarrierPrivilegeRulesHandlerThread(TAG);
+ mTestHandlerThread.start();
+
+ waitUntilReady();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mTestHandlerThread.quit();
+ super.tearDown();
+ mUiccCarrierPrivilegeRules = null;
+ }
+
+ private void testHelper(String hexString) {
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[2];
+ AsyncResult ar = new AsyncResult(null, new int[]{0}, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+ anyInt(), anyInt(), anyString(), any(Message.class));
+
+
+ Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
+ setReady(false);
+ mCardOpenLogicalChannel.sendToTarget();
+ waitUntilReady();
+ }
+
+ @Test
+ @SmallTest
+ public void testHandleMessage_Normal() {
+ /**
+ * FF40 45
+ * E2 43
+ * E1 35
+ * C1 14 ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4
+ * CA 1D 636F6D2E676F6F676C652E616E64726F69642E617070732E6D79617070
+ * E3 0A
+ * DB 08 0000000000000001
+ */
+ final String hexString =
+ "FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+ + "C652E616E64726F69642E617070732E6D79617070E30ADB080000000000000001";
+
+ testHelper(hexString);
+
+ assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+ assertEquals(1, mUiccCarrierPrivilegeRules.getPackageNames().size());
+ assertEquals("com.google.android.apps.myapp",
+ mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+ Signature signature = new Signature("abcd92cbb156b280fa4e1429a6eceeb6e5c1bfe4");
+ assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature,
+ mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+ }
+
+ @Test
+ @SmallTest
+ public void testHandleMessage_With4FD0D1() {
+ /**
+ * FF40 34
+ * E2 32
+ * E1 1E
+ * 4F 06 FF FF FF FF FF FF
+ * C1 14 B6 1B E3 4A D2 C2 0D 7A FE D8 49 3C 31 3A 13 7F 89 FA 27 65
+ * E3 10
+ * D0 01 01
+ * D1 01 01
+ * DB 08 00 00 00 00 00 00 00 01
+ */
+ final String hexString = "FF4034E232E11E4F06FFFFFFFFFFFFC114B61BE34AD2C20D7AFED84"
+ + "93C313A137F89FA2765E310D00101D10101DB080000000000000001";
+
+ testHelper(hexString);
+
+ assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+ assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+ }
+
+ @Test
+ @SmallTest
+ public void testHandleMessage_With4FD0() {
+ /**
+ * FF40 31
+ * E2 2F
+ * E1 1E
+ * 4F 06 FF FF FF FF FF FF
+ * C1 14 B6 1B E3 4A D2 C2 0D 7A FE D8 49 3C 31 3A 13 7F 89 FA 27 65
+ * E3 0D
+ * D0 01 01
+ * DB 08 00 00 00 00 00 00 00 01
+ */
+ final String hexString = "FF4031E22FE11E4F06FFFFFFFFFFFFC114B61BE34AD2C20D7AFED8493C313A"
+ + "137F89FA2765E30DD00101DB080000000000000001";
+
+ testHelper(hexString);
+
+ assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+ assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+ }
+
+ @Test
+ @SmallTest
+ public void testHandleMessage_TwoMessages() {
+ /**
+ * FF40 68
+ * E2 39
+ * E1 2B
+ * 4F 06 FFFFFFFFFFFF
+ * C1 02 B61B
+ * CA 1D 636F6D2E676F6F676C652E616E64726F69642E617070732E6D79617070
+ * E3 0A
+ * D0 01 01
+ * D1 01 01
+ * DB 02 0001
+ * E2 2B
+ * E1 23
+ * C1 02 ABCD
+ * CA 1D 636F6D2E676F6F676C652E616E64726F69642E617070732E6D79617070
+ * E3 04
+ * DB 02 0001
+ */
+ final String hexString =
+ "FF4068E239E12B4F06FFFFFFFFFFFFC102B61BCA1D636F6D2E676F6F676C652E616E64726F69642"
+ + "E617070732E6D79617070E30AD00101D10101DB020001E22BE123C102ABCDCA1D636F"
+ + "6D2E676F6F676C652E616E64726F69642E617070732E6D79617070E304DB020001";
+
+ testHelper(hexString);
+
+ assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+ assertEquals(2, mUiccCarrierPrivilegeRules.getPackageNames().size());
+ assertEquals("com.google.android.apps.myapp",
+ mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+ Signature signature1 = new Signature("b61b");
+ assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature1,
+ mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+
+ assertEquals("com.google.android.apps.myapp",
+ mUiccCarrierPrivilegeRules.getPackageNames().get(1));
+ Signature signature2 = new Signature("abcd");
+ assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature2,
+ mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+ }
+
+ @Test
+ @SmallTest
+ public void testHandleMessage_InvalidRulesWith4F00() {
+ /**
+ * FF40 24
+ * E2 22
+ * E1 18
+ * 4F 00
+ * C1 14 75C073AFD219AEB221948E828F066E778ADFDF23
+ * E3 06
+ * D0 01 01
+ * D1 01 01
+ */
+ final String hexString = "FF4024E222E1184F00C11475C073AFD219AEB221948E828F066E778ADFDF23"
+ + "E306D00101D10101";
+
+ testHelper(hexString);
+
+ assertTrue(!mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+ assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+ }
+
+ @Test
+ @SmallTest
+ public void testHandleMessage_InvalidRulesWithoutDB() {
+ /**
+ * FF40 2A
+ * E2 28
+ * E1 1E
+ * 4F 06 FF FF FF FF FF FF
+ * C1 14 B6 1B E3 4A D2 C2 0D 7A FE D8 49 3C 31 3A 13 7F 89 FA 27 65
+ * E3 06
+ * D0 01 01
+ * D1 01 01
+ */
+ final String hexString = "FF402AE228E11E4F06FFFFFFFFFFFFC114B61BE34AD2C20D7AFED8493C313A"
+ + "137F89FA2765E306D00101D10101";
+
+ testHelper(hexString);
+
+ assertTrue(!mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+ assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+ }
+}