Merge "IMS-VT: Notify capability change to clients when reset"
diff --git a/proto/src/telephony.proto b/proto/src/telephony.proto
index b9243f7..215d14e 100644
--- a/proto/src/telephony.proto
+++ b/proto/src/telephony.proto
@@ -1199,6 +1199,9 @@
// Notification about received SMS
SMS_RECEIVED = 8;
+
+ // CB message received
+ CB_SMS_RECEIVED = 9;
}
// Formats used to encode SMS messages
@@ -1224,6 +1227,51 @@
SMS_IMS = 3;
}
+ message CBMessage {
+ // CB message format
+ optional Format msgFormat = 1;
+
+ // CB message priority
+ optional CBPriority msgPriority = 2;
+
+ // Type of CB msg
+ optional CBMessageType msgType = 3;
+
+ // Service category of CB message
+ optional int32 serviceCategory = 4;
+ }
+
+ enum CBMessageType {
+ // Unknown type
+ TYPE_UNKNOWN = 0;
+
+ // ETWS CB msg
+ ETWS = 1;
+
+ // CMAS CB msg
+ CMAS = 2;
+
+ // CB msg other than ETWS and CMAS
+ OTHER = 3;
+ }
+
+ enum CBPriority {
+ // Unknown priority
+ PRIORITY_UNKNOWN = 0;
+
+ // NORMAL priority
+ NORMAL = 1;
+
+ // Interactive priority
+ INTERACTIVE = 2;
+
+ // Urgent priority
+ URGENT = 3;
+
+ // Emergency priority
+ EMERGENCY = 4;
+ }
+
// Event type
optional Type type = 1;
@@ -1261,6 +1309,9 @@
// Numeric ID
optional int32 ril_request_id = 12;
+
+ // Cellbroadcast message content
+ optional CBMessage cell_broadcast_message = 13;
}
// Time when session has started, in minutes since epoch,
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index b44f58a..c8cea5a 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -48,6 +48,7 @@
protected RegistrantList mIccSlotStatusChangedRegistrants = new RegistrantList();
protected RegistrantList mVoicePrivacyOnRegistrants = new RegistrantList();
protected RegistrantList mVoicePrivacyOffRegistrants = new RegistrantList();
+ protected Registrant mUnsolOemHookRawRegistrant;
protected RegistrantList mOtaProvisionRegistrants = new RegistrantList();
protected RegistrantList mCallWaitingInfoRegistrants = new RegistrantList();
protected RegistrantList mDisplayInfoRegistrants = new RegistrantList();
@@ -596,6 +597,17 @@
mSignalInfoRegistrants.add(r);
}
+ public void setOnUnsolOemHookRaw(Handler h, int what, Object obj) {
+ mUnsolOemHookRawRegistrant = new Registrant (h, what, obj);
+ }
+
+ public void unSetOnUnsolOemHookRaw(Handler h) {
+ if (mUnsolOemHookRawRegistrant != null && mUnsolOemHookRawRegistrant.getHandler() == h) {
+ mUnsolOemHookRawRegistrant.clear();
+ mUnsolOemHookRawRegistrant = null;
+ }
+ }
+
@Override
public void unregisterForSignalInfo(Handler h) {
mSignalInfoRegistrants.remove(h);
diff --git a/src/java/com/android/internal/telephony/BlockChecker.java b/src/java/com/android/internal/telephony/BlockChecker.java
index 2a6392f..456be56 100644
--- a/src/java/com/android/internal/telephony/BlockChecker.java
+++ b/src/java/com/android/internal/telephony/BlockChecker.java
@@ -17,6 +17,23 @@
* <p>
* This method catches all underlying exceptions to ensure that this method never throws any
* exception.
+ * <p>
+ * @deprecated use {@link #isBlocked(Context, String, Bundle)} instead.
+ *
+ * @param context the context of the caller.
+ * @param phoneNumber the number to check.
+ * @return {@code true} if the number is blocked. {@code false} otherwise.
+ */
+ @Deprecated
+ public static boolean isBlocked(Context context, String phoneNumber) {
+ return isBlocked(context, phoneNumber, null /* extras */);
+ }
+
+ /**
+ * Returns {@code true} if {@code phoneNumber} is blocked according to {@code extras}.
+ * <p>
+ * This method catches all underlying exceptions to ensure that this method never throws any
+ * exception.
*
* @param context the context of the caller.
* @param phoneNumber the number to check.
diff --git a/src/java/com/android/internal/telephony/CarrierIdentifier.java b/src/java/com/android/internal/telephony/CarrierIdentifier.java
index 4c6e439..69131ca 100644
--- a/src/java/com/android/internal/telephony/CarrierIdentifier.java
+++ b/src/java/com/android/internal/telephony/CarrierIdentifier.java
@@ -20,13 +20,11 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
-import android.preference.PreferenceManager;
import android.provider.Telephony;
import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
@@ -38,6 +36,7 @@
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccProfile;
import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor;
@@ -85,12 +84,11 @@
private Context mContext;
private Phone mPhone;
private IccRecords mIccRecords;
+ private UiccProfile mUiccProfile;
private final LocalLog mCarrierIdLocalLog = new LocalLog(20);
private final TelephonyManager mTelephonyMgr;
private final SubscriptionsChangedListener mOnSubscriptionsChangedListener =
new SubscriptionsChangedListener();
- private final SharedPreferenceChangedListener mSharedPrefListener =
- new SharedPreferenceChangedListener();
private final ContentObserver mContentObserver = new ContentObserver(this) {
@Override
@@ -130,20 +128,6 @@
}
}
- private class SharedPreferenceChangedListener implements
- SharedPreferences.OnSharedPreferenceChangeListener {
- @Override
- public void onSharedPreferenceChanged(
- SharedPreferences sharedPreferences, String key) {
- if (TextUtils.equals(key, OPERATOR_BRAND_OVERRIDE_PREFIX
- + mPhone.getIccSerialNumber())) {
- // SPN override from carrier privileged apps
- logd("[onSharedPreferenceChanged]: " + key);
- sendEmptyMessage(SPN_OVERRIDE_EVENT);
- }
- }
- }
-
public CarrierIdentifier(Phone phone) {
logd("Creating CarrierIdentifier[" + phone.getPhoneId() + "]");
mContext = phone.getContext();
@@ -157,8 +141,6 @@
CarrierId.All.CONTENT_URI, false, mContentObserver);
SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
mOnSubscriptionsChangedListener);
- PreferenceManager.getDefaultSharedPreferences(mContext)
- .registerOnSharedPreferenceChangeListener(mSharedPrefListener);
UiccController.getInstance().registerForIccChanged(this, ICC_CHANGED_EVENT, null);
}
@@ -222,21 +204,36 @@
}
break;
case ICC_CHANGED_EVENT:
- final IccRecords newIccRecords = mPhone.getIccRecords();
+ // all records used for carrier identification are from SimRecord
+ final IccRecords newIccRecords = UiccController.getInstance().getIccRecords(
+ mPhone.getPhoneId(), UiccController.APP_FAM_3GPP);
if (mIccRecords != newIccRecords) {
if (mIccRecords != null) {
logd("Removing stale icc objects.");
- mIccRecords.unregisterForSpnUpdate(this);
mIccRecords.unregisterForRecordsLoaded(this);
mIccRecords = null;
}
if (newIccRecords != null) {
logd("new Icc object");
- newIccRecords.registerForSpnUpdate(this, SPN_OVERRIDE_EVENT, null);
newIccRecords.registerForRecordsLoaded(this, SIM_LOAD_EVENT, null);
mIccRecords = newIccRecords;
}
}
+ // check UICC profile
+ final UiccProfile uiccProfile = UiccController.getInstance()
+ .getUiccProfileForPhone(mPhone.getPhoneId());
+ if (mUiccProfile != uiccProfile) {
+ if (mUiccProfile != null) {
+ logd("unregister operatorBrandOverride");
+ mUiccProfile.unregisterForOperatorBrandOverride(this);
+ mUiccProfile = null;
+ }
+ if (uiccProfile != null) {
+ logd("register operatorBrandOverride");
+ uiccProfile.registerForOpertorBrandOverride(this, SPN_OVERRIDE_EVENT, null);
+ mUiccProfile = uiccProfile;
+ }
+ }
break;
default:
loge("invalid msg: " + msg.what);
@@ -532,6 +529,7 @@
if (VDBG) {
logd("[matchCarrier]"
+ + " mnnmnc:" + mccmnc
+ " gid1: " + gid1
+ " gid2: " + gid2
+ " imsi: " + Rlog.pii(LOG_TAG, imsi)
@@ -567,7 +565,7 @@
/*
* Write Carrier Identification Matching event, logging with the
- * carrierId, gid1 and carrier list version to differentiate below cases of metrics:
+ * carrierId, mccmnc, gid1 and carrier list version to differentiate below cases of metrics:
* 1) unknown mccmnc - the Carrier Id provider contains no rule that matches the
* read mccmnc.
* 2) the Carrier Id provider contains some rule(s) that match the read mccmnc,
@@ -577,14 +575,15 @@
*/
String unknownGid1ToLog = ((maxScore & CarrierMatchingRule.SCORE_GID1) == 0
&& !TextUtils.isEmpty(subscriptionRule.mGid1)) ? subscriptionRule.mGid1 : null;
- String unknownMccmncToLog = (maxScore == CarrierMatchingRule.SCORE_INVALID
+ String unknownMccmncToLog = ((maxScore == CarrierMatchingRule.SCORE_INVALID
+ || (maxScore & CarrierMatchingRule.SCORE_GID1) == 0)
&& !TextUtils.isEmpty(subscriptionRule.mMccMnc)) ? subscriptionRule.mMccMnc : null;
TelephonyMetrics.getInstance().writeCarrierIdMatchingEvent(
mPhone.getPhoneId(), getCarrierListVersion(), mCarrierId,
unknownMccmncToLog, unknownGid1ToLog);
}
- private int getCarrierListVersion() {
+ public int getCarrierListVersion() {
final Cursor cursor = mContext.getContentResolver().query(
Uri.withAppendedPath(CarrierId.All.CONTENT_URI,
"get_version"), null, null, null);
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java
index ebf04e8..6a6a065 100644
--- a/src/java/com/android/internal/telephony/CarrierInfoManager.java
+++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -19,8 +19,10 @@
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException;
+import android.os.UserHandle;
import android.provider.Telephony;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.TelephonyManager;
@@ -34,6 +36,16 @@
*/
public class CarrierInfoManager {
private static final String LOG_TAG = "CarrierInfoManager";
+ private static final String KEY_TYPE = "KEY_TYPE";
+
+ /*
+ * Rate limit (in milliseconds) the number of times the Carrier keys can be reset.
+ * Do it at most once every 12 hours.
+ */
+ private static final int RESET_CARRIER_KEY_RATE_LIMIT = 12 * 60 * 60 * 1000;
+
+ // Last time the resetCarrierKeysForImsiEncryption API was called successfully.
+ private long mLastAccessResetCarrierKey = 0;
/**
* Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
@@ -158,4 +170,69 @@
updateOrInsertCarrierKey(imsiEncryptionInfo, mContext);
//todo send key to modem. Will be done in a subsequent CL.
}
-}
\ No newline at end of file
+
+ /**
+ * Resets the Carrier Keys in the database. This involves 2 steps:
+ * 1. Delete the keys from the database.
+ * 2. Send an intent to download new Certificates.
+ * @param context Context
+ * @param mPhoneId phoneId
+ *
+ */
+ public void resetCarrierKeysForImsiEncryption(Context context, int mPhoneId) {
+ Log.i(LOG_TAG, "resetting carrier key");
+ // Check rate limit.
+ long now = System.currentTimeMillis();
+ if (now - mLastAccessResetCarrierKey < RESET_CARRIER_KEY_RATE_LIMIT) {
+ Log.i(LOG_TAG, "resetCarrierKeysForImsiEncryption: Access rate exceeded");
+ return;
+ }
+ mLastAccessResetCarrierKey = now;
+ deleteCarrierInfoForImsiEncryption(context);
+ Intent resetIntent = new Intent(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
+ resetIntent.putExtra(PhoneConstants.PHONE_KEY, mPhoneId);
+ context.sendBroadcastAsUser(resetIntent, UserHandle.ALL);
+ }
+
+ /**
+ * Deletes all the keys for a given Carrier from the device keystore.
+ * @param context Context
+ */
+ public static void deleteCarrierInfoForImsiEncryption(Context context) {
+ Log.i(LOG_TAG, "deleting carrier key from db");
+ String mcc = "";
+ String mnc = "";
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ String simOperator = telephonyManager.getSimOperator();
+ if (!TextUtils.isEmpty(simOperator)) {
+ mcc = simOperator.substring(0, 3);
+ mnc = simOperator.substring(3);
+ } else {
+ Log.e(LOG_TAG, "Invalid networkOperator: " + simOperator);
+ return;
+ }
+ ContentResolver mContentResolver = context.getContentResolver();
+ try {
+ String whereClause = "mcc=? and mnc=?";
+ String[] whereArgs = new String[] { mcc, mnc };
+ mContentResolver.delete(Telephony.CarrierColumns.CONTENT_URI, whereClause, whereArgs);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Delete failed" + e);
+ }
+ }
+
+ /**
+ * Deletes all the keys from the device keystore.
+ * @param context Context
+ */
+ public static void deleteAllCarrierKeysForImsiEncryption(Context context) {
+ Log.i(LOG_TAG, "deleting ALL carrier keys from db");
+ ContentResolver mContentResolver = context.getContentResolver();
+ try {
+ mContentResolver.delete(Telephony.CarrierColumns.CONTENT_URI, null, null);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Delete failed" + e);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
index 66bc529..1513a33 100644
--- a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
+++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -57,6 +57,7 @@
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;
+import java.util.Random;
import java.util.zip.GZIPInputStream;
/**
@@ -70,8 +71,15 @@
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;
+ // Create a window prior to the key expiration, during which the cert will be
+ // downloaded. Defines the start date of that window. So if the key expires on
+ // Dec 21st, the start of the renewal window will be Dec 1st.
+ private static final int START_RENEWAL_WINDOW_DAYS = 21;
+
+ // This will define the end date of the window.
+ private static final int END_RENEWAL_WINDOW_DAYS = 7;
+
+
/* Intent for downloading the public key */
private static final String INTENT_KEY_RENEWAL_ALARM_PREFIX =
@@ -111,6 +119,7 @@
filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
filter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
filter.addAction(INTENT_KEY_RENEWAL_ALARM_PREFIX + mPhone.getPhoneId());
+ filter.addAction(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
mContext.registerReceiver(mBroadcastReceiver, filter, null, phone);
mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
}
@@ -123,6 +132,12 @@
if (action.equals(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId)) {
Log.d(LOG_TAG, "Handling key renewal alarm: " + action);
handleAlarmOrConfigChange();
+ } else if (action.equals(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD)) {
+ if (slotId == intent.getIntExtra(PhoneConstants.PHONE_KEY,
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
+ Log.d(LOG_TAG, "Handling reset intent: " + action);
+ handleAlarmOrConfigChange();
+ }
} else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
if (slotId == intent.getIntExtra(PhoneConstants.PHONE_KEY,
SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
@@ -208,10 +223,16 @@
// 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)) {
+ < System.currentTimeMillis() + END_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS)) {
minExpirationDate = System.currentTimeMillis() + DAY_IN_MILLIS;
} else {
- minExpirationDate = minExpirationDate - DEFAULT_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS;
+ // We don't want all the phones to download the certs simultaneously, so
+ // we pick a random time during the download window to avoid this situation.
+ Random random = new Random();
+ int max = START_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS;
+ int min = END_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS;
+ int randomTime = random.nextInt(max - min) + min;
+ minExpirationDate = minExpirationDate - randomTime;
}
return minExpirationDate;
}
@@ -478,7 +499,7 @@
}
Date imsiDate = imsiEncryptionInfo.getExpirationTime();
long timeToExpire = imsiDate.getTime() - System.currentTimeMillis();
- return (timeToExpire < DEFAULT_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS) ? true : false;
+ return (timeToExpire < START_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS) ? true : false;
}
return false;
}
@@ -501,6 +522,7 @@
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mURL));
request.setAllowedOverMetered(false);
request.setVisibleInDownloadsUi(false);
+ request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
Long carrierKeyDownloadRequestId = mDownloadManager.enqueue(request);
SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit();
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index abf91c9..2f57838 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.database.ContentObserver;
import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
@@ -30,6 +31,8 @@
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.util.NotificationChannelController;
@@ -38,6 +41,7 @@
import java.util.Map;
+
/**
* This contains Carrier specific logic based on the states/events
* managed in ServiceStateTracker.
@@ -50,21 +54,75 @@
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;
+ protected static final int CARRIER_EVENT_IMS_CAPABILITIES_CHANGED = CARRIER_EVENT_BASE + 5;
+
private static final int UNINITIALIZED_DELAY_VALUE = -1;
private Phone mPhone;
private ServiceStateTracker mSST;
-
+ private final Map<Integer, NotificationType> mNotificationTypeMap = new HashMap<>();
+ private int mPreviousSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
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));
+ // Listen for subscriber changes
+ SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener(
+ new OnSubscriptionsChangedListener(this.getLooper()) {
+ @Override
+ public void onSubscriptionsChanged() {
+ int subId = mPhone.getSubId();
+ if (mPreviousSubId != subId) {
+ mPreviousSubId = subId;
+ registerPrefNetworkModeObserver();
+ }
+ }
+ });
+
registerNotificationTypes();
+ registerPrefNetworkModeObserver();
+ }
+
+ private ContentObserver mPrefNetworkModeObserver = new ContentObserver(this) {
+ @Override
+ public void onChange(boolean selfChange) {
+ handlePrefNetworkModeChanged();
+ }
+ };
+
+ /**
+ * Return preferred network mode observer
+ */
+ @VisibleForTesting
+ public ContentObserver getContentObserver() {
+ return mPrefNetworkModeObserver;
+ }
+
+ private void registerPrefNetworkModeObserver() {
+ int subId = mPhone.getSubId();
+ unregisterPrefNetworkModeObserver();
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ mPhone.getContext().getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.PREFERRED_NETWORK_MODE + subId),
+ true,
+ mPrefNetworkModeObserver);
+ }
+ }
+
+ private void unregisterPrefNetworkModeObserver() {
+ mPhone.getContext().getContentResolver().unregisterContentObserver(
+ mPrefNetworkModeObserver);
+ }
+
+ /**
+ * Returns mNotificationTypeMap
+ */
+ @VisibleForTesting
+ public Map<Integer, NotificationType> getNotificationTypeMap() {
+ return mNotificationTypeMap;
}
private void registerNotificationTypes() {
@@ -83,6 +141,9 @@
case CARRIER_EVENT_DATA_DEREGISTRATION:
handleConfigChanges();
break;
+ case CARRIER_EVENT_IMS_CAPABILITIES_CHANGED:
+ handleImsCapabilitiesChanged();
+ break;
case NOTIFICATION_EMERGENCY_NETWORK:
case NOTIFICATION_PREF_NETWORK:
Rlog.d(LOG_TAG, "sending notification after delay: " + msg.what);
@@ -152,14 +213,33 @@
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());
- }
+ evaluateSendingMessageOrCancelNotification(notificationType);
+ }
+ }
+
+ private void handlePrefNetworkModeChanged() {
+ NotificationType notificationType = mNotificationTypeMap.get(NOTIFICATION_PREF_NETWORK);
+ if (notificationType != null) {
+ evaluateSendingMessageOrCancelNotification(notificationType);
+ }
+ }
+
+ private void handleImsCapabilitiesChanged() {
+ NotificationType notificationType = mNotificationTypeMap
+ .get(NOTIFICATION_EMERGENCY_NETWORK);
+ if (notificationType != null) {
+ evaluateSendingMessageOrCancelNotification(notificationType);
+ }
+ }
+
+ private void evaluateSendingMessageOrCancelNotification(NotificationType notificationType) {
+ 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());
}
}
@@ -240,6 +320,13 @@
}
/**
+ * Dispose the CarrierServiceStateTracker.
+ */
+ public void dispose() {
+ unregisterPrefNetworkModeObserver();
+ }
+
+ /**
* Class that defines the different types of notifications.
*/
public interface NotificationType {
@@ -293,7 +380,7 @@
}
this.mDelay = bundle.getInt(
CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT);
- Rlog.i(LOG_TAG, "reading time to delay notification emergency: " + mDelay);
+ Rlog.i(LOG_TAG, "reading time to delay notification pref network: " + mDelay);
}
public int getDelay() {
@@ -311,7 +398,7 @@
Rlog.i(LOG_TAG, "PrefNetworkNotification: sendMessage() w/values: "
+ "," + isPhoneStillRegistered() + "," + mDelay + "," + isGlobalMode()
+ "," + mSST.isRadioOn());
- if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered() || isGlobalMode()
+ if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered() || isGlobalMode()
|| isRadioOffOrAirplaneMode()) {
return false;
}
@@ -324,6 +411,7 @@
public Notification.Builder getNotificationBuilder() {
Context context = mPhone.getContext();
Intent notificationIntent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
+ notificationIntent.putExtra("expandable", true);
PendingIntent settingsIntent = PendingIntent.getActivity(context, 0, notificationIntent,
PendingIntent.FLAG_ONE_SHOT);
CharSequence title = context.getText(
diff --git a/src/java/com/android/internal/telephony/CellBroadcastHandler.java b/src/java/com/android/internal/telephony/CellBroadcastHandler.java
index 4227148..8fc4642 100644
--- a/src/java/com/android/internal/telephony/CellBroadcastHandler.java
+++ b/src/java/com/android/internal/telephony/CellBroadcastHandler.java
@@ -30,6 +30,12 @@
import android.provider.Telephony;
import android.telephony.SmsCbMessage;
import android.telephony.SubscriptionManager;
+import android.util.LocalLog;
+
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
/**
* Dispatch new Cell Broadcasts to receivers. Acquires a private wakelock until the broadcast
@@ -37,6 +43,8 @@
*/
public class CellBroadcastHandler extends WakeLockStateMachine {
+ private final LocalLog mLocalLog = new LocalLog(100);
+
private CellBroadcastHandler(Context context, Phone phone) {
this("CellBroadcastHandler", context, phone);
}
@@ -82,9 +90,18 @@
String receiverPermission;
int appOp;
+ // Log Cellbroadcast msg received event
+ TelephonyMetrics metrics = TelephonyMetrics.getInstance();
+ metrics.writeNewCBSms(mPhone.getPhoneId(), message.getMessageFormat(),
+ message.getMessagePriority(), message.isCmasMessage(), message.isEtwsMessage(),
+ message.getServiceCategory());
+
+ String msg;
Intent intent;
if (message.isEmergencyMessage()) {
- log("Dispatching emergency SMS CB, SmsCbMessage is: " + message);
+ msg = "Dispatching emergency SMS CB, SmsCbMessage is: " + message;
+ log(msg);
+ mLocalLog.log(msg);
intent = new Intent(Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
// Explicitly send the intent to the default cell broadcast receiver.
intent.setPackage(mContext.getResources().getString(
@@ -92,7 +109,9 @@
receiverPermission = Manifest.permission.RECEIVE_EMERGENCY_BROADCAST;
appOp = AppOpsManager.OP_RECEIVE_EMERGECY_SMS;
} else {
- log("Dispatching SMS CB, SmsCbMessage is: " + message);
+ msg = "Dispatching SMS CB, SmsCbMessage is: " + message;
+ log(msg);
+ mLocalLog.log(msg);
intent = new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION);
// Send implicit intent since there are various 3rd party carrier apps listen to
// this intent.
@@ -121,4 +140,11 @@
mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, receiverPermission, appOp,
mReceiver, getHandler(), Activity.RESULT_OK, null, null);
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("CellBroadcastHandler:");
+ mLocalLog.dump(fd, pw, args);
+ pw.flush();
+ }
}
diff --git a/src/java/com/android/internal/telephony/CellularNetworkService.java b/src/java/com/android/internal/telephony/CellularNetworkService.java
index a89a15a..8b840de 100644
--- a/src/java/com/android/internal/telephony/CellularNetworkService.java
+++ b/src/java/com/android/internal/telephony/CellularNetworkService.java
@@ -18,9 +18,7 @@
import android.annotation.CallSuper;
import android.hardware.radio.V1_0.CellInfoType;
-import android.hardware.radio.V1_0.DataRegStateResult;
import android.hardware.radio.V1_0.RegState;
-import android.hardware.radio.V1_0.VoiceRegStateResult;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.HandlerThread;
@@ -202,8 +200,21 @@
// TODO: unify when voiceRegStateResult and DataRegStateResult are unified.
if (domain == NetworkRegistrationState.DOMAIN_CS) {
- VoiceRegStateResult voiceRegState = (VoiceRegStateResult) result;
- int transportType = TransportType.WWAN;
+ return createRegistrationStateFromVoiceRegState(result);
+ } else if (domain == NetworkRegistrationState.DOMAIN_PS) {
+ return createRegistrationStateFromDataRegState(result);
+ } else {
+ return null;
+ }
+ }
+
+ private NetworkRegistrationState createRegistrationStateFromVoiceRegState(Object result) {
+ int transportType = TransportType.WWAN;
+ int domain = NetworkRegistrationState.DOMAIN_CS;
+
+ if (result instanceof android.hardware.radio.V1_0.VoiceRegStateResult) {
+ android.hardware.radio.V1_0.VoiceRegStateResult voiceRegState =
+ (android.hardware.radio.V1_0.VoiceRegStateResult) result;
int regState = getRegStateFromHalRegState(voiceRegState.regState);
int accessNetworkTechnology = getAccessNetworkTechnologyFromRat(voiceRegState.rat);
int reasonForDenial = voiceRegState.reasonForDenial;
@@ -217,13 +228,42 @@
CellIdentity cellIdentity =
convertHalCellIdentityToCellIdentity(voiceRegState.cellIdentity);
- return new NetworkRegistrationState(transportType, domain, regState,
+ return new NetworkRegistrationState(domain, transportType, regState,
accessNetworkTechnology, reasonForDenial, emergencyOnly, availableServices,
cellIdentity, cssSupported, roamingIndicator, systemIsInPrl,
defaultRoamingIndicator);
- } else if (domain == NetworkRegistrationState.DOMAIN_PS) {
- DataRegStateResult dataRegState = (DataRegStateResult) result;
- int transportType = TransportType.WWAN;
+ } else if (result instanceof android.hardware.radio.V1_2.VoiceRegStateResult) {
+ android.hardware.radio.V1_2.VoiceRegStateResult voiceRegState =
+ (android.hardware.radio.V1_2.VoiceRegStateResult) result;
+ int regState = getRegStateFromHalRegState(voiceRegState.regState);
+ int accessNetworkTechnology = getAccessNetworkTechnologyFromRat(voiceRegState.rat);
+ int reasonForDenial = voiceRegState.reasonForDenial;
+ boolean emergencyOnly = isEmergencyOnly(voiceRegState.regState);
+ boolean cssSupported = voiceRegState.cssSupported;
+ int roamingIndicator = voiceRegState.roamingIndicator;
+ int systemIsInPrl = voiceRegState.systemIsInPrl;
+ int defaultRoamingIndicator = voiceRegState.defaultRoamingIndicator;
+ int[] availableServices = getAvailableServices(
+ regState, domain, emergencyOnly);
+ CellIdentity cellIdentity =
+ convertHalCellIdentityToCellIdentity(voiceRegState.cellIdentity);
+
+ return new NetworkRegistrationState(domain, transportType, regState,
+ accessNetworkTechnology, reasonForDenial, emergencyOnly, availableServices,
+ cellIdentity, cssSupported, roamingIndicator, systemIsInPrl,
+ defaultRoamingIndicator);
+ }
+
+ return null;
+ }
+
+ private NetworkRegistrationState createRegistrationStateFromDataRegState(Object result) {
+ int domain = NetworkRegistrationState.DOMAIN_PS;
+ int transportType = TransportType.WWAN;
+
+ if (result instanceof android.hardware.radio.V1_0.DataRegStateResult) {
+ android.hardware.radio.V1_0.DataRegStateResult dataRegState =
+ (android.hardware.radio.V1_0.DataRegStateResult) result;
int regState = getRegStateFromHalRegState(dataRegState.regState);
int accessNetworkTechnology = getAccessNetworkTechnologyFromRat(dataRegState.rat);
int reasonForDenial = dataRegState.reasonDataDenied;
@@ -232,12 +272,28 @@
int[] availableServices = getAvailableServices(regState, domain, emergencyOnly);
CellIdentity cellIdentity =
convertHalCellIdentityToCellIdentity(dataRegState.cellIdentity);
- return new NetworkRegistrationState(transportType, domain, regState,
+
+ return new NetworkRegistrationState(domain, transportType, regState,
accessNetworkTechnology, reasonForDenial, emergencyOnly, availableServices,
cellIdentity, maxDataCalls);
- } else {
- return null;
+ } else if (result instanceof android.hardware.radio.V1_2.DataRegStateResult) {
+ android.hardware.radio.V1_2.DataRegStateResult dataRegState =
+ (android.hardware.radio.V1_2.DataRegStateResult) result;
+ int regState = getRegStateFromHalRegState(dataRegState.regState);
+ int accessNetworkTechnology = getAccessNetworkTechnologyFromRat(dataRegState.rat);
+ int reasonForDenial = dataRegState.reasonDataDenied;
+ boolean emergencyOnly = isEmergencyOnly(dataRegState.regState);
+ int maxDataCalls = dataRegState.maxDataCalls;
+ int[] availableServices = getAvailableServices(regState, domain, emergencyOnly);
+ CellIdentity cellIdentity =
+ convertHalCellIdentityToCellIdentity(dataRegState.cellIdentity);
+
+ return new NetworkRegistrationState(domain, transportType, regState,
+ accessNetworkTechnology, reasonForDenial, emergencyOnly, availableServices,
+ cellIdentity, maxDataCalls);
}
+
+ return null;
}
private CellIdentity convertHalCellIdentityToCellIdentity(
@@ -274,7 +330,8 @@
cellIdentity.cellIdentityTdscdma.get(0);
result = new CellIdentityTdscdma(cellIdentityTdscdma.mcc,
cellIdentityTdscdma.mnc, cellIdentityTdscdma.lac,
- cellIdentityTdscdma.cid, cellIdentityTdscdma.cpid);
+ cellIdentityTdscdma.cid, cellIdentityTdscdma.cpid,
+ Integer.MAX_VALUE, null, null);
}
break;
}
@@ -308,6 +365,107 @@
return result;
}
+ private CellIdentity convertHalCellIdentityToCellIdentity(
+ android.hardware.radio.V1_2.CellIdentity cellIdentity) {
+ if (cellIdentity == null) {
+ return null;
+ }
+
+ CellIdentity result = null;
+ switch(cellIdentity.cellInfoType) {
+ case CellInfoType.GSM: {
+ if (cellIdentity.cellIdentityGsm.size() == 1) {
+ android.hardware.radio.V1_2.CellIdentityGsm cellIdentityGsm =
+ cellIdentity.cellIdentityGsm.get(0);
+
+ result = new CellIdentityGsm(
+ cellIdentityGsm.base.lac,
+ cellIdentityGsm.base.cid,
+ cellIdentityGsm.base.arfcn,
+ cellIdentityGsm.base.bsic,
+ cellIdentityGsm.base.mcc,
+ cellIdentityGsm.base.mnc,
+ cellIdentityGsm.operatorNames.alphaLong,
+ cellIdentityGsm.operatorNames.alphaShort);
+ }
+ break;
+ }
+ case CellInfoType.WCDMA: {
+ if (cellIdentity.cellIdentityWcdma.size() == 1) {
+ android.hardware.radio.V1_2.CellIdentityWcdma cellIdentityWcdma =
+ cellIdentity.cellIdentityWcdma.get(0);
+
+ result = new CellIdentityWcdma(
+ cellIdentityWcdma.base.lac,
+ cellIdentityWcdma.base.cid,
+ cellIdentityWcdma.base.psc,
+ cellIdentityWcdma.base.uarfcn,
+ cellIdentityWcdma.base.mcc,
+ cellIdentityWcdma.base.mnc,
+ cellIdentityWcdma.operatorNames.alphaLong,
+ cellIdentityWcdma.operatorNames.alphaShort);
+ }
+ break;
+ }
+ case CellInfoType.TD_SCDMA: {
+ if (cellIdentity.cellIdentityTdscdma.size() == 1) {
+ android.hardware.radio.V1_2.CellIdentityTdscdma cellIdentityTdscdma =
+ cellIdentity.cellIdentityTdscdma.get(0);
+
+ result = new CellIdentityTdscdma(
+ cellIdentityTdscdma.base.mcc,
+ cellIdentityTdscdma.base.mnc,
+ cellIdentityTdscdma.base.lac,
+ cellIdentityTdscdma.base.cid,
+ cellIdentityTdscdma.base.cpid,
+ cellIdentityTdscdma.uarfcn,
+ cellIdentityTdscdma.operatorNames.alphaLong,
+ cellIdentityTdscdma.operatorNames.alphaShort);
+ }
+ break;
+ }
+ case CellInfoType.LTE: {
+ if (cellIdentity.cellIdentityLte.size() == 1) {
+ android.hardware.radio.V1_2.CellIdentityLte cellIdentityLte =
+ cellIdentity.cellIdentityLte.get(0);
+
+ result = new CellIdentityLte(
+ cellIdentityLte.base.ci,
+ cellIdentityLte.base.pci,
+ cellIdentityLte.base.tac,
+ cellIdentityLte.base.earfcn,
+ cellIdentityLte.bandwidth,
+ cellIdentityLte.base.mcc,
+ cellIdentityLte.base.mnc,
+ cellIdentityLte.operatorNames.alphaLong,
+ cellIdentityLte.operatorNames.alphaShort);
+ }
+ break;
+ }
+ case CellInfoType.CDMA: {
+ if (cellIdentity.cellIdentityCdma.size() == 1) {
+ android.hardware.radio.V1_2.CellIdentityCdma cellIdentityCdma =
+ cellIdentity.cellIdentityCdma.get(0);
+
+ result = new CellIdentityCdma(
+ cellIdentityCdma.base.networkId,
+ cellIdentityCdma.base.systemId,
+ cellIdentityCdma.base.baseStationId,
+ cellIdentityCdma.base.longitude,
+ cellIdentityCdma.base.latitude,
+ cellIdentityCdma.operatorNames.alphaLong,
+ cellIdentityCdma.operatorNames.alphaShort);
+ }
+ break;
+ }
+ case CellInfoType.NONE:
+ default:
+ break;
+ }
+
+ return result;
+ }
+
@Override
public void getNetworkRegistrationState(int domain, NetworkServiceCallback callback) {
if (DBG) log("getNetworkRegistrationState for domain " + domain);
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index edf8359..9f6ce5e 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -1456,6 +1456,8 @@
*/
void reportStkServiceIsRunning(Message result);
+ void invokeOemRilRequestRaw(byte[] data, Message response);
+
/**
* Sends carrier specific information to the vendor ril that can be used to
* encrypt the IMSI and IMPI.
@@ -1468,6 +1470,14 @@
void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
Message response);
+ void invokeOemRilRequestStrings(String[] strings, Message response);
+
+ /**
+ * Fires when RIL_UNSOL_OEM_HOOK_RAW is received from the RIL.
+ */
+ void setOnUnsolOemHookRaw(Handler h, int what, Object obj);
+ void unSetOnUnsolOemHookRaw(Handler h);
+
/**
* Send TERMINAL RESPONSE to the SIM, after processing a proactive command
* sent by the SIM.
@@ -2126,6 +2136,38 @@
void setUnsolResponseFilter(int filter, Message result);
/**
+ * Send the signal strength reporting criteria to the modem.
+ *
+ * @param hysteresisMs A hysteresis time in milliseconds. A value of 0 disables hysteresis.
+ * @param hysteresisDb An interval in dB defining the required magnitude change between reports.
+ * A value of 0 disables hysteresis.
+ * @param thresholdsDbm An array of trigger thresholds in dBm. A size of 0 disables thresholds.
+ * @param ran RadioAccessNetwork for which to apply criteria.
+ * @param result callback message contains the information of SUCCESS/FAILURE
+ */
+ void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb, int[] thresholdsDbm,
+ int ran, Message result);
+
+ /**
+ * Send the link capacity reporting criteria to the modem
+ *
+ * @param hysteresisMs A hysteresis time in milliseconds. A value of 0 disables hysteresis.
+ * @param hysteresisDlKbps An interval in kbps defining the required magnitude change between DL
+ * reports. A value of 0 disables hysteresis.
+ * @param hysteresisUlKbps An interval in kbps defining the required magnitude change between UL
+ * reports. A value of 0 disables hysteresis.
+ * @param thresholdsDlKbps An array of trigger thresholds in kbps for downlink reports. A size
+ * of 0 disables thresholds.
+ * @param thresholdsUlKbps An array of trigger thresholds in kbps for uplink reports. A size
+ * of 0 disables thresholds.
+ * @param ran RadioAccessNetwork for which to apply criteria.
+ * @param result callback message contains the information of SUCCESS/FAILURE
+ */
+ void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
+ int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
+ Message result);
+
+ /**
* Set SIM card power up or down
*
* @param state State of SIM (power down, power up, pass through)
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index 5a8a0a1..8f1f078 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -333,6 +333,15 @@
}
}
+ @Override
+ public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) {
+ try {
+ mRegistry.notifyOemHookRawEventForSubscriber(subId, rawData);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
/**
* Convert the {@link Phone.DataActivityState} enum into the TelephonyManager.DATA_* constants
* for the public API.
diff --git a/src/java/com/android/internal/telephony/DeviceStateMonitor.java b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
index 12127aa..04e96ca 100644
--- a/src/java/com/android/internal/telephony/DeviceStateMonitor.java
+++ b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
@@ -25,12 +25,14 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.display.DisplayManager;
-import android.hardware.radio.V1_0.IndicationFilter;
+import android.hardware.radio.V1_2.IndicationFilter;
import android.net.ConnectivityManager;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.TelephonyManager;
import android.util.LocalLog;
@@ -63,6 +65,9 @@
private static final int EVENT_CHARGING_STATE_CHANGED = 4;
private static final int EVENT_TETHERING_STATE_CHANGED = 5;
+ // TODO(b/74006656) load hysteresis values from a property when DeviceStateMonitor starts
+ private static final int HYSTERESIS_KBPS = 50;
+
private final Phone mPhone;
private final LocalLog mLocalLog = new LocalLog(100);
@@ -201,18 +206,13 @@
* @return True if low data is expected
*/
private boolean isLowDataExpected() {
- return mIsPowerSaveOn || (!mIsCharging && !mIsTetheringOn && !mIsScreenOn);
+ return !mIsCharging && !mIsTetheringOn && !mIsScreenOn;
}
/**
* @return True if signal strength update should be turned off.
*/
private boolean shouldTurnOffSignalStrength() {
- // We don't want to get signal strength update when the device is in power save mode.
- if (mIsPowerSaveOn) {
- return true;
- }
-
// We should not turn off signal strength update if one of the following condition is true.
// 1. The device is charging.
// 2. When the screen is on.
@@ -234,11 +234,6 @@
* trigger the network update unsolicited response.
*/
private boolean shouldTurnOffFullNetworkUpdate() {
- // We don't want to get full network update when the device is in power save mode.
- if (mIsPowerSaveOn) {
- return true;
- }
-
// We should not turn off full network update if one of the following condition is true.
// 1. The device is charging.
// 2. When the screen is on.
@@ -258,11 +253,6 @@
* @return True if data dormancy status update should be turned off.
*/
private boolean shouldTurnOffDormancyUpdate() {
- // We don't want to get dormancy status update when the device is in power save mode.
- if (mIsPowerSaveOn) {
- return true;
- }
-
// We should not turn off data dormancy update if one of the following condition is true.
// 1. The device is charging.
// 2. When the screen is on.
@@ -279,6 +269,44 @@
}
/**
+ * @return True if link capacity estimate update should be turned off.
+ */
+ private boolean shouldTurnOffLinkCapacityEstimate() {
+ // We should not turn off link capacity update if one of the following condition is true.
+ // 1. The device is charging.
+ // 2. When the screen is on.
+ // 3. When data tethering is on.
+ // 4. When the update mode is IGNORE_SCREEN_OFF.
+ if (mIsCharging || mIsScreenOn || mIsTetheringOn
+ || mUpdateModes.get(TelephonyManager.INDICATION_FILTER_LINK_CAPACITY_ESTIMATE)
+ == TelephonyManager.INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF) {
+ return false;
+ }
+
+ // In all other cases, we turn off link capacity update.
+ return true;
+ }
+
+ /**
+ * @return True if physical channel config update should be turned off.
+ */
+ private boolean shouldTurnOffPhysicalChannelConfig() {
+ // We should not turn off physical channel update if one of the following condition is true.
+ // 1. The device is charging.
+ // 2. When the screen is on.
+ // 3. When data tethering is on.
+ // 4. When the update mode is IGNORE_SCREEN_OFF.
+ if (mIsCharging || mIsScreenOn || mIsTetheringOn
+ || mUpdateModes.get(TelephonyManager.INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG)
+ == TelephonyManager.INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF) {
+ return false;
+ }
+
+ // In all other cases, we turn off physical channel config update.
+ return true;
+ }
+
+ /**
* Set indication update mode
*
* @param filters Indication filters. Should be a bitmask of INDICATION_FILTER_XXX.
@@ -298,6 +326,12 @@
if ((filters & TelephonyManager.INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED) != 0) {
mUpdateModes.put(TelephonyManager.INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED, mode);
}
+ if ((filters & TelephonyManager.INDICATION_FILTER_LINK_CAPACITY_ESTIMATE) != 0) {
+ mUpdateModes.put(TelephonyManager.INDICATION_FILTER_LINK_CAPACITY_ESTIMATE, mode);
+ }
+ if ((filters & TelephonyManager.INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG) != 0) {
+ mUpdateModes.put(TelephonyManager.INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG, mode);
+ }
}
/**
@@ -374,6 +408,14 @@
newFilter |= IndicationFilter.DATA_CALL_DORMANCY_CHANGED;
}
+ if (!shouldTurnOffLinkCapacityEstimate()) {
+ newFilter |= IndicationFilter.LINK_CAPACITY_ESTIMATE;
+ }
+
+ if (!shouldTurnOffPhysicalChannelConfig()) {
+ newFilter |= IndicationFilter.PHYSICAL_CHANNEL_CONFIG;
+ }
+
setUnsolResponseFilter(newFilter, false);
}
@@ -390,6 +432,8 @@
sendDeviceState(LOW_DATA_EXPECTED, mIsLowDataExpected);
sendDeviceState(POWER_SAVE_MODE, mIsPowerSaveOn);
setUnsolResponseFilter(mUnsolicitedResponseFilter, true);
+ setSignalStrengthReportingCriteria();
+ setLinkCapacityReportingCriteria();
}
/**
@@ -432,6 +476,28 @@
}
}
+ private void setSignalStrengthReportingCriteria() {
+ mPhone.setSignalStrengthReportingCriteria(
+ AccessNetworkThresholds.GERAN, AccessNetworkType.GERAN);
+ mPhone.setSignalStrengthReportingCriteria(
+ AccessNetworkThresholds.UTRAN, AccessNetworkType.UTRAN);
+ mPhone.setSignalStrengthReportingCriteria(
+ AccessNetworkThresholds.EUTRAN, AccessNetworkType.EUTRAN);
+ mPhone.setSignalStrengthReportingCriteria(
+ AccessNetworkThresholds.CDMA2000, AccessNetworkType.CDMA2000);
+ }
+
+ private void setLinkCapacityReportingCriteria() {
+ mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS,
+ LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.GERAN);
+ mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS,
+ LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.UTRAN);
+ mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS,
+ LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.EUTRAN);
+ mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS,
+ LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.CDMA2000);
+ }
+
/**
* @return True if the device is currently in power save mode.
* See {@link android.os.BatteryManager#isPowerSaveMode BatteryManager.isPowerSaveMode()}.
@@ -517,4 +583,85 @@
ipw.decreaseIndent();
ipw.flush();
}
+
+ /**
+ * dBm thresholds that correspond to changes in signal strength indications.
+ */
+ private static final class AccessNetworkThresholds {
+
+ /**
+ * List of dBm thresholds for GERAN {@link AccessNetworkType}.
+ *
+ * Calculated from GSM asu level thresholds - TS 27.007 Sec 8.5
+ */
+ public static final int[] GERAN = new int[] {
+ -109,
+ -103,
+ -97,
+ -89,
+ };
+
+ /**
+ * List of default dBm thresholds for UTRAN {@link AccessNetworkType}.
+ *
+ * These thresholds are taken from the WCDMA RSCP defaults in {@link CarrierConfigManager}.
+ * See TS 27.007 Sec 8.69.
+ */
+ public static final int[] UTRAN = new int[] {
+ -114, /* SIGNAL_STRENGTH_POOR */
+ -104, /* SIGNAL_STRENGTH_MODERATE */
+ -94, /* SIGNAL_STRENGTH_GOOD */
+ -84 /* SIGNAL_STRENGTH_GREAT */
+ };
+
+ /**
+ * List of default dBm thresholds for EUTRAN {@link AccessNetworkType}.
+ *
+ * These thresholds are taken from the LTE RSRP defaults in {@link CarrierConfigManager}.
+ */
+ public static final int[] EUTRAN = new int[] {
+ -140, /* SIGNAL_STRENGTH_NONE_OR_UNKNOWN */
+ -128, /* SIGNAL_STRENGTH_POOR */
+ -118, /* SIGNAL_STRENGTH_MODERATE */
+ -108, /* SIGNAL_STRENGTH_GOOD */
+ -98, /* SIGNAL_STRENGTH_GREAT */
+ -44 /* SIGNAL_STRENGTH_NONE_OR_UNKNOWN */
+ };
+
+ /**
+ * List of dBm thresholds for CDMA2000 {@link AccessNetworkType}.
+ *
+ * These correspond to EVDO level thresholds.
+ */
+ public static final int[] CDMA2000 = new int[] {
+ -105,
+ -90,
+ -75,
+ -65
+ };
+ }
+
+ /**
+ * Downlink reporting thresholds in kbps
+ *
+ * <p>Threshold values taken from FCC Speed Guide
+ * (https://www.fcc.gov/reports-research/guides/broadband-speed-guide) and Android WiFi speed
+ * labels (https://support.google.com/pixelphone/answer/2819519#strength_speed).
+ */
+ private static final int[] LINK_CAPACITY_DOWNLINK_THRESHOLDS = new int[] {
+ 500, // Web browsing
+ 1000, // SD video streaming
+ 5000, // HD video streaming
+ 10000, // file downloading
+ 20000, // 4K video streaming
+ };
+
+ /** Uplink reporting thresholds in kbps */
+ private static final int[] LINK_CAPACITY_UPLINK_THRESHOLDS = new int[] {
+ 100, // VoIP calls
+ 500,
+ 1000,
+ 5000,
+ 10000,
+ };
}
diff --git a/src/java/com/android/internal/telephony/ExponentialBackoff.java b/src/java/com/android/internal/telephony/ExponentialBackoff.java
deleted file mode 100644
index 80958c0..0000000
--- a/src/java/com/android/internal/telephony/ExponentialBackoff.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.annotation.NonNull;
-import android.os.Handler;
-import android.os.Looper;
-
-/** The implementation of exponential backoff with jitter applied. */
-public class ExponentialBackoff {
- private int mRetryCounter;
- private long mStartDelayMs;
- private long mMaximumDelayMs;
- private long mCurrentDelayMs;
- private int mMultiplier;
- private Runnable mRunnable;
- private Handler mHandler;
-
- public ExponentialBackoff(
- long initialDelayMs,
- long maximumDelayMs,
- int multiplier,
- @NonNull Looper looper,
- @NonNull Runnable runnable) {
- this(initialDelayMs, maximumDelayMs, multiplier, new Handler(looper), runnable);
- }
-
- public ExponentialBackoff(
- long initialDelayMs,
- long maximumDelayMs,
- int multiplier,
- @NonNull Handler handler,
- @NonNull Runnable runnable) {
- mRetryCounter = 0;
- mStartDelayMs = initialDelayMs;
- mMaximumDelayMs = maximumDelayMs;
- mMultiplier = multiplier;
- mHandler = handler;
- mRunnable = runnable;
- }
-
- /** Starts the backoff, the runnable will be executed after {@link #mStartDelayMs}. */
- public void start() {
- mRetryCounter = 0;
- mCurrentDelayMs = mStartDelayMs;
- mHandler.removeCallbacks(mRunnable);
- mHandler.postDelayed(mRunnable, mCurrentDelayMs);
- }
-
- /** Stops the backoff, all pending messages will be removed from the message queue. */
- public void stop() {
- mRetryCounter = 0;
- mHandler.removeCallbacks(mRunnable);
- }
-
- /** Should call when the retry action has failed and we want to retry after a longer delay. */
- public void notifyFailed() {
- mRetryCounter++;
- long temp = Math.min(
- mMaximumDelayMs, (long) (mStartDelayMs * Math.pow(mMultiplier, mRetryCounter)));
- mCurrentDelayMs = (long) (((1 + Math.random()) / 2) * temp);
- mHandler.removeCallbacks(mRunnable);
- mHandler.postDelayed(mRunnable, mCurrentDelayMs);
- }
-
- /** Returns the delay for the most recently posted message. */
- public long getCurrentDelay() {
- return mCurrentDelayMs;
- }
-}
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index bdad00e..5dd36ad 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -89,6 +89,7 @@
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.uicc.UiccProfile;
+import com.android.internal.telephony.uicc.UiccSlot;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -108,6 +109,13 @@
private static final boolean DBG = true;
private static final boolean VDBG = false; /* STOPSHIP if true */
+ /** Required magnitude change between unsolicited SignalStrength reports. */
+ private static final int REPORTING_HYSTERESIS_DB = 2;
+ /** Required throughput change between unsolicited LinkCapacityEstimate reports. */
+ private static final int REPORTING_HYSTERESIS_KBPS = 50;
+ /** Minimum time between unsolicited SignalStrength and LinkCapacityEstimate reports. */
+ private static final int REPORTING_HYSTERESIS_MILLIS = 3000;
+
//GSM
// Key used to read/write voice mail number
private static final String VM_NUMBER = "vm_number_key";
@@ -190,6 +198,8 @@
private int mRilVersion;
private boolean mBroadcastEmergencyCallStateChanges = false;
private CarrierKeyDownloadManager mCDM;
+ private CarrierInfoManager mCIM;
+
// Constructors
public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId,
@@ -273,6 +283,7 @@
mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
mCDM = new CarrierKeyDownloadManager(this);
+ mCIM = new CarrierInfoManager();
}
private void initRatSpecific(int precisePhoneType) {
@@ -327,7 +338,7 @@
setIsoCountryProperty(operatorNumeric);
// Updates MCC MNC device configuration information
logd("update mccmnc=" + operatorNumeric);
- MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false);
+ MccTable.updateMccMncConfiguration(mContext, operatorNumeric);
}
// Sets current entry in the telephony carrier table
@@ -348,10 +359,7 @@
} else {
String iso = "";
try {
- iso = MccTable.countryCodeForMcc(Integer.parseInt(
- operatorNumeric.substring(0,3)));
- } catch (NumberFormatException ex) {
- Rlog.e(LOG_TAG, "setIsoCountryProperty: countryCodeForMcc error", ex);
+ iso = MccTable.countryCodeForMcc(operatorNumeric.substring(0, 3));
} catch (StringIndexOutOfBoundsException ex) {
Rlog.e(LOG_TAG, "setIsoCountryProperty: countryCodeForMcc error", ex);
}
@@ -522,12 +530,6 @@
ret = PhoneConstants.DataState.DISCONNECTED;
} else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */
switch (mDcTracker.getState(apnType)) {
- case RETRYING:
- case FAILED:
- case IDLE:
- ret = PhoneConstants.DataState.DISCONNECTED;
- break;
-
case CONNECTED:
case DISCONNECTING:
if ( mCT.mState != PhoneConstants.State.IDLE
@@ -536,12 +538,12 @@
} else {
ret = PhoneConstants.DataState.CONNECTED;
}
- break;
-
+ break;
case CONNECTING:
- case SCANNING:
ret = PhoneConstants.DataState.CONNECTING;
- break;
+ break;
+ default:
+ ret = PhoneConstants.DataState.DISCONNECTED;
}
}
@@ -1112,7 +1114,8 @@
// Check non-emergency voice CS call - shouldn't dial when POWER_OFF
if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_POWER_OFF /* CS POWER_OFF */
&& !VideoProfile.isVideo(dialArgs.videoState) /* voice call */
- && !isEmergency /* non-emergency call */) {
+ && !isEmergency /* non-emergency call */
+ && !(isUt && useImsForUt) /* not UT */) {
throw new CallStateException(
CallStateException.ERROR_POWER_OFF,
"cannot dial voice call in airplane mode");
@@ -1533,6 +1536,32 @@
}
@Override
+ public void resetCarrierKeysForImsiEncryption() {
+ mCIM.resetCarrierKeysForImsiEncryption(mContext, mPhoneId);
+ }
+
+ @Override
+ public int getCarrierIdListVersion() {
+ return mCarrerIdentifier.getCarrierListVersion();
+ }
+
+ @Override
+ public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
+ String gid2, String pnn, String spn) {
+ IccRecords r = null;
+ if (isPhoneTypeGsm()) {
+ r = mIccRecords.get();
+ } else if (isPhoneTypeCdmaLte()) {
+ r = mSimRecords;
+ } else {
+ loge("setCarrierTestOverride fails in CDMA only");
+ }
+ if (r != null) {
+ r.setCarrierTestOverride(mccmnc, imsi, iccid, gid1, gid2, pnn, spn);
+ }
+ }
+
+ @Override
public String getGroupIdLevel1() {
if (isPhoneTypeGsm()) {
IccRecords r = mIccRecords.get();
@@ -1641,7 +1670,13 @@
Message resp;
mVmNumber = voiceMailNumber;
resp = obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
+
IccRecords r = mIccRecords.get();
+
+ if (!isPhoneTypeGsm() && mSimRecords != null) {
+ r = mSimRecords;
+ }
+
if (r != null) {
r.setVoiceMailNumber(alphaTag, mVmNumber, resp);
}
@@ -1704,7 +1739,8 @@
} else {
resp = onComplete;
}
- mCi.queryCallForwardStatus(commandInterfaceCFReason, 0, null, resp);
+ mCi.queryCallForwardStatus(commandInterfaceCFReason,
+ CommandsInterface.SERVICE_CLASS_VOICE, null, resp);
}
} else {
loge("getCallForwardingOption: not possible in CDMA");
@@ -2672,7 +2708,7 @@
// Updates MCC MNC device configuration information
logd("update mccmnc=" + operatorNumeric);
- MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false);
+ MccTable.updateMccMncConfiguration(mContext, operatorNumeric);
return true;
} catch (SQLException e) {
@@ -3347,6 +3383,18 @@
}
@Override
+ public void setSignalStrengthReportingCriteria(int[] thresholds, int ran) {
+ mCi.setSignalStrengthReportingCriteria(REPORTING_HYSTERESIS_MILLIS, REPORTING_HYSTERESIS_DB,
+ thresholds, ran, null);
+ }
+
+ @Override
+ public void setLinkCapacityReportingCriteria(int[] dlThresholds, int[] ulThresholds, int ran) {
+ mCi.setLinkCapacityReportingCriteria(REPORTING_HYSTERESIS_MILLIS, REPORTING_HYSTERESIS_KBPS,
+ REPORTING_HYSTERESIS_KBPS, dlThresholds, ulThresholds, ran, null);
+ }
+
+ @Override
public IccSmsInterfaceManager getIccSmsInterfaceManager(){
return mIccSmsInterfaceManager;
}
@@ -3370,7 +3418,20 @@
@Override
public IccCard getIccCard() {
- return UiccController.getInstance().getUiccProfileForPhone(mPhoneId);
+ // This function doesn't return null for backwards compatability purposes.
+ // To differentiate between cases where SIM is absent vs. unknown we return a dummy
+ // IccCard with the sim state set.
+ IccCard card = getUiccProfile();
+ if (card != null) {
+ return card;
+ } else {
+ UiccSlot slot = mUiccController.getUiccSlotForPhone(mPhoneId);
+ if (slot == null || slot.isStateUnknown()) {
+ return new IccCard(IccCardConstants.State.UNKNOWN);
+ } else {
+ return new IccCard(IccCardConstants.State.ABSENT);
+ }
+ }
}
private UiccProfile getUiccProfile() {
diff --git a/src/java/com/android/internal/telephony/IccCard.java b/src/java/com/android/internal/telephony/IccCard.java
index 7e98bc9..7bab408 100644
--- a/src/java/com/android/internal/telephony/IccCard.java
+++ b/src/java/com/android/internal/telephony/IccCard.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
@@ -34,26 +35,51 @@
* Apps (those that have access to Phone object) can retrieve this object
* by calling phone.getIccCard()
*
- * This interface is implemented by IccCardProxy and the object PhoneApp
- * gets when it calls getIccCard is IccCardProxy.
+ * This interface is implemented by UiccProfile and the object PhoneApp
+ * gets when it calls getIccCard is UiccProfile.
*/
-public interface IccCard {
+public class IccCard {
+ private State mIccCardState = State.UNKNOWN;
+
+ /**
+ * Empty constructor.
+ */
+ public IccCard() {}
+
+ /**
+ * Set the state of the IccCard to be returned in {@link getState}.
+ */
+ public IccCard(State state) {
+ mIccCardState = state;
+ }
+
/**
* @return combined Card and current App state
*/
- public State getState();
+ public State getState() {
+ return mIccCardState;
+ }
// todo: delete
/**
* @return IccRecords object belonging to current UiccCardApplication
*/
- public IccRecords getIccRecords();
+ public IccRecords getIccRecords() {
+ return null;
+ }
/**
* Notifies handler of any transition into IccCardConstants.State.NETWORK_LOCKED
*/
- public void registerForNetworkLocked(Handler h, int what, Object obj);
- public void unregisterForNetworkLocked(Handler h);
+ public void registerForNetworkLocked(Handler h, int what, Object obj) {
+ return;
+ }
+ /**
+ * Unregister for networkLocked state change.
+ */
+ public void unregisterForNetworkLocked(Handler h) {
+ return;
+ }
/**
* Supply the ICC PIN to the ICC
@@ -73,27 +99,37 @@
* && ((CommandException)(((AsyncResult)onComplete.obj).exception))
* .getCommandError() == CommandException.Error.PASSWORD_INCORRECT
*/
- public void supplyPin (String pin, Message onComplete);
+ public void supplyPin(String pin, Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Supply the ICC PUK to the ICC
*/
- public void supplyPuk (String puk, String newPin, Message onComplete);
+ public void supplyPuk(String puk, String newPin, Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Supply the ICC PIN2 to the ICC
*/
- public void supplyPin2 (String pin2, Message onComplete);
+ public void supplyPin2(String pin2, Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Supply the ICC PUK2 to the ICC
*/
- public void supplyPuk2 (String puk2, String newPin2, Message onComplete);
+ public void supplyPuk2(String puk2, String newPin2, Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Supply Network depersonalization code to the RIL
*/
- public void supplyNetworkDepersonalization (String pin, Message onComplete);
+ public void supplyNetworkDepersonalization(String pin, Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Check whether ICC pin lock is enabled
@@ -102,7 +138,18 @@
* @return true for ICC locked enabled
* false for ICC locked disabled
*/
- public boolean getIccLockEnabled();
+ public boolean getIccLockEnabled() {
+ return false;
+ }
+
+ /**
+ * Check whether fdn (fixed dialing number) service is available.
+ * @return true if ICC fdn service available
+ * false if ICC fdn service not available
+ */
+ public boolean getIccFdnAvailable() {
+ return false;
+ }
/**
* Check whether ICC fdn (fixed dialing number) is enabled
@@ -111,7 +158,9 @@
* @return true for ICC fdn enabled
* false for ICC fdn disabled
*/
- public boolean getIccFdnEnabled();
+ public boolean getIccFdnEnabled() {
+ return false;
+ }
/**
* Set the ICC pin lock enabled or disabled
@@ -124,8 +173,10 @@
* ((AsyncResult)onComplete.obj).exception == null on success
* ((AsyncResult)onComplete.obj).exception != null on fail
*/
- public void setIccLockEnabled (boolean enabled,
- String password, Message onComplete);
+ public void setIccLockEnabled(boolean enabled,
+ String password, Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Set the ICC fdn enabled or disabled
@@ -138,8 +189,10 @@
* ((AsyncResult)onComplete.obj).exception == null on success
* ((AsyncResult)onComplete.obj).exception != null on fail
*/
- public void setIccFdnEnabled (boolean enabled,
- String password, Message onComplete);
+ public void setIccFdnEnabled(boolean enabled,
+ String password, Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Change the ICC password used in ICC pin lock
@@ -153,7 +206,9 @@
* ((AsyncResult)onComplete.obj).exception != null on fail
*/
public void changeIccLockPassword(String oldPassword, String newPassword,
- Message onComplete);
+ Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Change the ICC password used in ICC fdn enable
@@ -167,7 +222,9 @@
* ((AsyncResult)onComplete.obj).exception != null on fail
*/
public void changeIccFdnPassword(String oldPassword, String newPassword,
- Message onComplete);
+ Message onComplete) {
+ sendMessageWithCardAbsentException(onComplete);
+ }
/**
* Returns service provider name stored in ICC card.
@@ -185,26 +242,42 @@
* yet available
*
*/
- public String getServiceProviderName ();
+ public String getServiceProviderName() {
+ return null;
+ }
/**
* Checks if an Application of specified type present on the card
* @param type is AppType to look for
*/
- public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type);
+ public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) {
+ return false;
+ }
/**
* @return true if a ICC card is present
*/
- public boolean hasIccCard();
+ public boolean hasIccCard() {
+ return false;
+ }
/**
* @return true if ICC card is PIN2 blocked
*/
- public boolean getIccPin2Blocked();
+ public boolean getIccPin2Blocked() {
+ return false;
+ }
/**
* @return true if ICC card is PUK2 blocked
*/
- public boolean getIccPuk2Blocked();
+ public boolean getIccPuk2Blocked() {
+ return false;
+ }
+
+ private void sendMessageWithCardAbsentException(Message onComplete) {
+ AsyncResult ret = AsyncResult.forMessage(onComplete);
+ ret.exception = new RuntimeException("No valid IccCard");
+ onComplete.sendToTarget();
+ }
}
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 81050f2..d7a45d8 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -40,17 +40,19 @@
import android.telephony.Rlog;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
-import android.telephony.TelephonyManager;
+import android.util.LocalLog;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import com.android.internal.telephony.uicc.IccConstants;
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccUtils;
-import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.util.HexDump;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -87,6 +89,8 @@
final private UserManager mUserManager;
protected SmsDispatchersController mDispatchersController;
+ private final LocalLog mCellBroadcastLocalLog = new LocalLog(100);
+
protected Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -129,12 +133,22 @@
};
protected IccSmsInterfaceManager(Phone phone) {
+ this(phone, phone.getContext(),
+ (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE),
+ (UserManager) phone.getContext().getSystemService(Context.USER_SERVICE),
+ new SmsDispatchersController(
+ phone, phone.mSmsStorageMonitor, phone.mSmsUsageMonitor));
+ }
+
+ @VisibleForTesting
+ public IccSmsInterfaceManager(
+ Phone phone, Context context, AppOpsManager appOps, UserManager userManager,
+ SmsDispatchersController dispatchersController) {
mPhone = phone;
- mContext = phone.getContext();
- mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mDispatchersController = new SmsDispatchersController(phone,
- phone.mSmsStorageMonitor, phone.mSmsUsageMonitor);
+ mContext = context;
+ mAppOps = appOps;
+ mUserManager = userManager;
+ mDispatchersController = dispatchersController;
}
protected void markMessagesAsRead(ArrayList<byte[]> messages) {
@@ -323,11 +337,10 @@
*/
public void sendDataWithSelfPermissions(String callingPackage, String destAddr, String scAddr,
int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
- mPhone.getContext().enforceCallingOrSelfPermission(
- Manifest.permission.SEND_SMS,
- "Sending SMS message");
- sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
- deliveryIntent);
+ if (!checkCallingOrSelfSendSmsPermission(callingPackage, "Sending SMS message")) {
+ return;
+ }
+ sendDataInternal(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
}
/**
@@ -336,11 +349,10 @@
*/
public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
- mPhone.getContext().enforceCallingPermission(
- Manifest.permission.SEND_SMS,
- "Sending SMS message");
- sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
- deliveryIntent);
+ if (!checkCallingSendSmsPermission(callingPackage, "Sending SMS message")) {
+ return;
+ }
+ sendDataInternal(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
}
/**
@@ -369,17 +381,13 @@
* raw pdu of the status report is in the extended data ("pdu").
*/
- private void sendDataInternal(String callingPackage, String destAddr, String scAddr,
+ private void sendDataInternal(String destAddr, String scAddr,
int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" +
destPort + " data='"+ HexDump.toHexString(data) + "' sentIntent=" +
sentIntent + " deliveryIntent=" + deliveryIntent);
}
- if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
- return;
- }
destAddr = filterDestAddress(destAddr);
mDispatchersController.sendData(destAddr, scAddr, destPort, data, sentIntent,
deliveryIntent);
@@ -392,9 +400,10 @@
public void sendText(String callingPackage, String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForNonDefaultSmsApp) {
- mPhone.getContext().enforceCallingPermission(
- Manifest.permission.SEND_SMS,
- "Sending SMS message");
+ if (!checkCallingSendTextPermissions(
+ persistMessageForNonDefaultSmsApp, callingPackage, "Sending SMS message")) {
+ return;
+ }
sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
persistMessageForNonDefaultSmsApp, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
@@ -407,9 +416,9 @@
public void sendTextWithSelfPermissions(String callingPackage, String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessage) {
- mPhone.getContext().enforceCallingOrSelfPermission(
- Manifest.permission.SEND_SMS,
- "Sending SMS message");
+ if (!checkCallingOrSelfSendSmsPermission(callingPackage, "Sending SMS message")) {
+ return;
+ }
sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, false /* expectMore */,
SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
@@ -472,13 +481,6 @@
+ " priority=" + priority + " expectMore=" + expectMore
+ " validityPeriod=" + validityPeriod);
}
- if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
- return;
- }
- if (!persistMessageForNonDefaultSmsApp) {
- enforcePrivilegedAppPermissions();
- }
destAddr = filterDestAddress(destAddr);
mDispatchersController.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp,
@@ -535,9 +537,9 @@
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore,
int validityPeriod) {
- mPhone.getContext().enforceCallingOrSelfPermission(
- Manifest.permission.SEND_SMS,
- "Sending SMS message");
+ if (!checkCallingOrSelfSendSmsPermission(callingPackage, "Sending SMS message")) {
+ return;
+ }
sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
persistMessageForNonDefaultSmsApp, priority, expectMore, validityPeriod);
}
@@ -553,7 +555,11 @@
* the same time an SMS received from radio is acknowledged back.
*/
public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
- enforcePrivilegedAppPermissions();
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ enforceCallerIsImsAppOrCarrierApp("injectSmsPdu");
+ }
+
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
log("pdu: " + pdu +
"\n format=" + format +
@@ -658,12 +664,9 @@
String scAddr, List<String> parts, List<PendingIntent> sentIntents,
List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp,
int priority, boolean expectMore, int validityPeriod) {
- mPhone.getContext().enforceCallingPermission(
- Manifest.permission.SEND_SMS,
- "Sending SMS message");
- if (!persistMessageForNonDefaultSmsApp) {
- // Only allow carrier app or carrier ims to skip auto message persistence.
- enforcePrivilegedAppPermissions();
+ if (!checkCallingSendTextPermissions(
+ persistMessageForNonDefaultSmsApp, callingPackage, "Sending SMS message")) {
+ return;
}
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
int i = 0;
@@ -672,10 +675,6 @@
", part[" + (i++) + "]=" + part);
}
}
- if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
- return;
- }
destAddr = filterDestAddress(destAddr);
@@ -794,7 +793,7 @@
} else if (ranType == SmsManager.CELL_BROADCAST_RAN_TYPE_CDMA) {
return enableCdmaBroadcastRange(startMessageId, endMessageId);
} else {
- throw new IllegalArgumentException("Not a supportted RAN Type");
+ throw new IllegalArgumentException("Not a supported RAN Type");
}
}
@@ -804,30 +803,34 @@
} else if (ranType == SmsManager.CELL_BROADCAST_RAN_TYPE_CDMA) {
return disableCdmaBroadcastRange(startMessageId, endMessageId);
} else {
- throw new IllegalArgumentException("Not a supportted RAN Type");
+ throw new IllegalArgumentException("Not a supported RAN Type");
}
}
synchronized public boolean enableGsmBroadcastRange(int startMessageId, int endMessageId) {
- Context context = mPhone.getContext();
-
- context.enforceCallingPermission(
+ mContext.enforceCallingPermission(
"android.permission.RECEIVE_SMS",
"Enabling cell broadcast SMS");
- String client = context.getPackageManager().getNameForUid(
+ String client = mContext.getPackageManager().getNameForUid(
Binder.getCallingUid());
+ String msg;
if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
- log("Failed to add GSM cell broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ msg = "Failed to add GSM cell broadcast channels range " + startMessageId
+ + " to " + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
return false;
}
- if (DBG)
- log("Added GSM cell broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ if (DBG) {
+ msg = "Added GSM cell broadcast channels range " + startMessageId
+ + " to " + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
+ }
setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty());
@@ -836,24 +839,28 @@
synchronized public boolean disableGsmBroadcastRange(int startMessageId, int endMessageId) {
- Context context = mPhone.getContext();
-
- context.enforceCallingPermission(
+ mContext.enforceCallingPermission(
"android.permission.RECEIVE_SMS",
"Disabling cell broadcast SMS");
- String client = context.getPackageManager().getNameForUid(
+ String client = mContext.getPackageManager().getNameForUid(
Binder.getCallingUid());
+ String msg;
if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
- log("Failed to remove GSM cell broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ msg = "Failed to remove GSM cell broadcast channels range " + startMessageId
+ + " to " + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
return false;
}
- if (DBG)
- log("Removed GSM cell broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ if (DBG) {
+ msg = "Removed GSM cell broadcast channels range " + startMessageId
+ + " to " + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
+ }
setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty());
@@ -862,24 +869,27 @@
synchronized public boolean enableCdmaBroadcastRange(int startMessageId, int endMessageId) {
- Context context = mPhone.getContext();
-
- context.enforceCallingPermission(
+ mContext.enforceCallingPermission(
"android.permission.RECEIVE_SMS",
"Enabling cdma broadcast SMS");
- String client = context.getPackageManager().getNameForUid(
+ String client = mContext.getPackageManager().getNameForUid(
Binder.getCallingUid());
+ String msg;
if (!mCdmaBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
- log("Failed to add cdma broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ msg = "Failed to add cdma broadcast channels range " + startMessageId + " to "
+ + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
return false;
}
- if (DBG)
- log("Added cdma broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ if (DBG) {
+ msg = "Added cdma broadcast channels range " + startMessageId + " to " + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
+ }
setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty());
@@ -888,24 +898,27 @@
synchronized public boolean disableCdmaBroadcastRange(int startMessageId, int endMessageId) {
- Context context = mPhone.getContext();
-
- context.enforceCallingPermission(
+ mContext.enforceCallingPermission(
"android.permission.RECEIVE_SMS",
"Disabling cell broadcast SMS");
- String client = context.getPackageManager().getNameForUid(
+ String client = mContext.getPackageManager().getNameForUid(
Binder.getCallingUid());
+ String msg;
if (!mCdmaBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
- log("Failed to remove cdma broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ msg = "Failed to remove cdma broadcast channels range " + startMessageId + " to "
+ + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
return false;
}
- if (DBG)
- log("Removed cdma broadcast subscription for MID range " + startMessageId
- + " to " + endMessageId + " from client " + client);
+ if (DBG) {
+ msg = "Removed cdma broadcast channels range " + startMessageId + " to " + endMessageId;
+ log(msg);
+ mCellBroadcastLocalLog.log(msg);
+ }
setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty());
@@ -1086,17 +1099,14 @@
public void sendStoredText(String callingPkg, Uri messageUri, String scAddress,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
- mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS,
- "Sending SMS message");
+ if (!checkCallingSendSmsPermission(callingPkg, "Sending SMS message")) {
+ return;
+ }
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
log("sendStoredText: scAddr=" + scAddress + " messageUri=" + messageUri
+ " sentIntent=" + sentIntent + " deliveryIntent=" + deliveryIntent);
}
- if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg)
- != AppOpsManager.MODE_ALLOWED) {
- return;
- }
- final ContentResolver resolver = mPhone.getContext().getContentResolver();
+ final ContentResolver resolver = mContext.getContentResolver();
if (!isFailedOrDraft(resolver, messageUri)) {
Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredText: not FAILED or DRAFT message");
returnUnspecifiedFailure(sentIntent);
@@ -1117,13 +1127,10 @@
public void sendStoredMultipartText(String callingPkg, Uri messageUri, String scAddress,
List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
- mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS,
- "Sending SMS message");
- if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg)
- != AppOpsManager.MODE_ALLOWED) {
+ if (!checkCallingSendSmsPermission(callingPkg, "Sending SMS message")) {
return;
}
- final ContentResolver resolver = mPhone.getContext().getContentResolver();
+ final ContentResolver resolver = mContext.getContentResolver();
if (!isFailedOrDraft(resolver, messageUri)) {
Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: "
+ "not FAILED or DRAFT message");
@@ -1268,32 +1275,69 @@
}
}
- private void enforceCarrierPrivilege() {
- UiccController controller = UiccController.getInstance();
- if (controller == null || controller.getUiccCard(mPhone.getPhoneId()) == null) {
- throw new SecurityException("No Carrier Privilege: No UICC");
+ /**
+ * Check that the caller can send text messages.
+ *
+ * For persisted messages, the caller just needs the SEND_SMS permission. For unpersisted
+ * messages, the caller must either be the IMS app or a carrier-privileged app, or they must
+ * have both the MODIFY_PHONE_STATE and SEND_SMS permissions.
+ *
+ * @throws SecurityException if the caller is missing all necessary permission declaration or
+ * has had a necessary runtime permission revoked.
+ * @return true unless the caller has all necessary permissions but has a revoked AppOps bit.
+ */
+ @VisibleForTesting
+ public boolean checkCallingSendTextPermissions(
+ boolean persistMessageForNonDefaultSmsApp, String callingPackage, String message) {
+ // TODO(b/75978989): Should we allow IMS/carrier apps for persisted messages as well?
+ if (!persistMessageForNonDefaultSmsApp) {
+ try {
+ enforceCallerIsImsAppOrCarrierApp(message);
+ // No need to also check SEND_SMS.
+ return true;
+ } catch (SecurityException e) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE, message);
+ }
}
- if (controller.getUiccCard(mPhone.getPhoneId()).getCarrierPrivilegeStatusForCurrentTransaction(
- mContext.getPackageManager()) !=
- TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
- throw new SecurityException("No Carrier Privilege.");
- }
+ return checkCallingSendSmsPermission(callingPackage, message);
}
/**
- * Enforces that the caller has {@link android.Manifest.permission#MODIFY_PHONE_STATE}
- * permission or is one of the following apps:
+ * Check that the caller (or self, if this is not an IPC) has SEND_SMS permissions.
+ *
+ * @throws SecurityException if the caller is missing the permission declaration or has had the
+ * permission revoked at runtime.
+ * @return whether the caller has the OP_SEND_SMS AppOps bit.
+ */
+ private boolean checkCallingOrSelfSendSmsPermission(String callingPackage, String message) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.SEND_SMS, message);
+ return mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage)
+ == AppOpsManager.MODE_ALLOWED;
+ }
+
+ /**
+ * Check that the caller has SEND_SMS permissions. Can only be called during an IPC.
+ *
+ * @throws SecurityException if the caller is missing the permission declaration or has had the
+ * permission revoked at runtime.
+ * @return whether the caller has the OP_SEND_SMS AppOps bit.
+ */
+ private boolean checkCallingSendSmsPermission(String callingPackage, String message) {
+ mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, message);
+ return mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPackage)
+ == AppOpsManager.MODE_ALLOWED;
+ }
+
+ /**
+ * Enforces that the caller is one of the following apps:
* <ul>
* <li> IMS App
* <li> Carrier App
* </ul>
*/
- private void enforcePrivilegedAppPermissions() {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- == PackageManager.PERMISSION_GRANTED) {
- return;
- }
-
+ @VisibleForTesting
+ public void enforceCallerIsImsAppOrCarrierApp(String message) {
int callingUid = Binder.getCallingUid();
String carrierImsPackage = CarrierSmsUtils.getCarrierImsPackageForIntent(mContext, mPhone,
new Intent(CarrierMessagingService.SERVICE_INTERFACE));
@@ -1309,7 +1353,7 @@
}
}
- enforceCarrierPrivilege();
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mPhone.getSubId(), message);
}
private String filterDestAddress(String destAddr) {
@@ -1318,4 +1362,11 @@
return result != null ? result : destAddr;
}
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("CellBroadcast log:");
+ mCellBroadcastLocalLog.dump(fd, pw, args);
+ pw.println("SMS dispatcher controller log:");
+ mDispatchersController.dump(fd, pw, args);
+ pw.flush();
+ }
}
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index 65ce0fe..5bc62d5 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -16,8 +16,11 @@
package com.android.internal.telephony;
+import android.content.Context;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.provider.Telephony.Sms.Intents;
+import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.aidl.IImsSmsListener;
@@ -26,6 +29,8 @@
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsSmsImplBase;
import android.telephony.ims.stub.ImsSmsImplBase.SendStatusResult;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
import android.util.Pair;
import com.android.ims.ImsException;
@@ -162,7 +167,9 @@
public void onSmsReceived(int token, String format, byte[] pdu)
throws RemoteException {
Rlog.d(TAG, "SMS received.");
- mSmsDispatchersController.injectSmsPdu(pdu, format, result -> {
+ android.telephony.SmsMessage message =
+ android.telephony.SmsMessage.createFromPdu(pdu, format);
+ mSmsDispatchersController.injectSmsPdu(message, format, result -> {
Rlog.d(TAG, "SMS handled result: " + result);
int mappedResult;
switch (result) {
@@ -180,7 +187,13 @@
break;
}
try {
- getImsManager().acknowledgeSms(token, 0, mappedResult);
+ if (message != null && message.mWrappedSmsMessage != null) {
+ getImsManager().acknowledgeSms(token,
+ message.mWrappedSmsMessage.mMessageRef, mappedResult);
+ } else {
+ Rlog.w(TAG, "SMS Received with a PDU that could not be parsed.");
+ getImsManager().acknowledgeSms(token, 0, mappedResult);
+ }
} catch (ImsException e) {
Rlog.e(TAG, "Failed to acknowledgeSms(). Error: " + e.getMessage());
}
@@ -220,6 +233,50 @@
getImsManager().onSmsReady();
}
+ private boolean isLteService() {
+ return ((mPhone.getServiceState().getRilVoiceRadioTechnology() ==
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE) && (mPhone.getServiceState().
+ getState() == ServiceState.STATE_IN_SERVICE));
+ }
+
+ private boolean isLimitedLteService() {
+ return ((mPhone.getServiceState().getRilVoiceRadioTechnology() ==
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE) && mPhone.getServiceState().isEmergencyOnly());
+ }
+
+ private boolean isEmergencySmsPossible() {
+ return isLteService() || isLimitedLteService();
+ }
+
+ public boolean isEmergencySmsSupport(String destAddr) {
+ PersistableBundle b;
+ boolean eSmsCarrierSupport = false;
+ if (!PhoneNumberUtils.isLocalEmergencyNumber(mContext, mPhone.getSubId(), destAddr)) {
+ Rlog.e(TAG, "Emergency Sms is not supported for: " + destAddr);
+ return false;
+ }
+ CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager == null) {
+ Rlog.e(TAG, "configManager is null");
+ return false;
+ }
+ b = configManager.getConfigForSubId(getSubId());
+ if (b == null) {
+ Rlog.e(TAG, "PersistableBundle is null");
+ return false;
+ }
+ eSmsCarrierSupport = b.getBoolean(CarrierConfigManager.
+ KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL);
+ boolean lteOrLimitedLte = isEmergencySmsPossible();
+ Rlog.i(TAG, "isEmergencySmsSupport emergencySmsCarrierSupport: "
+ + eSmsCarrierSupport + " destAddr: " + destAddr + " mIsImsServiceUp: "
+ + mIsImsServiceUp + " lteOrLimitedLte: " + lteOrLimitedLte);
+
+ return eSmsCarrierSupport && mIsImsServiceUp && lteOrLimitedLte;
+ }
+
+
public boolean isAvailable() {
synchronized (mLock) {
Rlog.d(TAG, "isAvailable: up=" + mIsImsServiceUp + ", reg= " + mIsRegistered
@@ -239,8 +296,10 @@
}
@Override
- protected boolean shouldBlockSms() {
- return SMSDispatcherUtil.shouldBlockSms(isCdmaMo(), mPhone);
+ protected boolean shouldBlockSmsForEcbm() {
+ // We should not block outgoing SMS during ECM on IMS. It only applies to outgoing CDMA
+ // SMS.
+ return false;
}
@Override
@@ -270,6 +329,10 @@
+ " mMessageRef=" + tracker.mMessageRef
+ " SS=" + mPhone.getServiceState().getState());
+ // Flag that this Tracker is using the ImsService implementation of SMS over IMS for sending
+ // this message. Any fallbacks will happen over CS only.
+ tracker.mUsesImsServiceForIms = true;
+
HashMap<String, Object> map = tracker.getData();
byte[] pdu = (byte[]) map.get(MAP_KEY_PDU);
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 53072c7..b51498e 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -60,6 +60,7 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.LocalLog;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -69,6 +70,8 @@
import com.android.internal.util.StateMachine;
import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -157,6 +160,9 @@
/** New SMS received as an AsyncResult. */
public static final int EVENT_INJECT_SMS = 8;
+ /** Update the sms tracker */
+ public static final int EVENT_UPDATE_TRACKER = 9;
+
/** Wakelock release delay when returning to idle state. */
private static final int WAKELOCK_TIMEOUT = 3000;
@@ -205,6 +211,8 @@
private UserManager mUserManager;
+ private LocalLog mLocalLog = new LocalLog(64);
+
IDeviceIdleController mDeviceIdleController;
// Delete permanently from raw table
@@ -449,6 +457,7 @@
// if any broadcasts were sent, transition to waiting state
InboundSmsTracker inboundSmsTracker = (InboundSmsTracker) msg.obj;
if (processMessagePart(inboundSmsTracker)) {
+ sendMessage(obtainMessage(EVENT_UPDATE_TRACKER, msg.obj));
transitionTo(mWaitingState);
} else {
// if event is sent from SmsBroadcastUndelivered.broadcastSms(), and
@@ -474,10 +483,17 @@
}
return HANDLED;
+ case EVENT_UPDATE_TRACKER:
+ logd("process tracker message in DeliveringState " + msg.arg1);
+ return HANDLED;
+
// we shouldn't get this message type in this state, log error and halt.
case EVENT_BROADCAST_COMPLETE:
case EVENT_START_ACCEPTING_SMS:
default:
+ String errorMsg = "Unhandled msg in delivering state, msg.what = " + msg.what;
+ loge(errorMsg);
+ mLocalLog.log(errorMsg);
// let DefaultState handle these unexpected message types
return NOT_HANDLED;
}
@@ -493,6 +509,8 @@
*/
private class WaitingState extends State {
+ private InboundSmsTracker mLastDeliveredSmsTracker;
+
@Override
public void enter() {
if (DBG) log("entering Waiting state");
@@ -512,10 +530,20 @@
switch (msg.what) {
case EVENT_BROADCAST_SMS:
// defer until the current broadcast completes
+ if (mLastDeliveredSmsTracker != null) {
+ String str = "Defer sms broadcast due to undelivered sms, "
+ + " messageCount = " + mLastDeliveredSmsTracker.getMessageCount()
+ + " destPort = " + mLastDeliveredSmsTracker.getDestPort()
+ + " timestamp = " + mLastDeliveredSmsTracker.getTimestamp()
+ + " currentTimestamp = " + System.currentTimeMillis();
+ logd(str);
+ mLocalLog.log(str);
+ }
deferMessage(msg);
return HANDLED;
case EVENT_BROADCAST_COMPLETE:
+ mLastDeliveredSmsTracker = null;
// return to idle after handling all deferred messages
sendMessage(EVENT_RETURN_TO_IDLE);
transitionTo(mDeliveringState);
@@ -525,6 +553,13 @@
// not ready to return to idle; ignore
return HANDLED;
+ case EVENT_UPDATE_TRACKER:
+ for (int i = 1; i < 10; i++) {
+ deferMessage(obtainMessage(EVENT_UPDATE_TRACKER, i, i, msg.obj));
+ }
+ mLastDeliveredSmsTracker = (InboundSmsTracker) msg.obj;
+ return HANDLED;
+
default:
// parent state handles the other message types
return NOT_HANDLED;
@@ -835,8 +870,10 @@
// Do not process null pdu(s). Check for that and return false in that case.
List<byte[]> pduList = Arrays.asList(pdus);
if (pduList.size() == 0 || pduList.contains(null)) {
- loge("processMessagePart: returning false due to " +
- (pduList.size() == 0 ? "pduList.size() == 0" : "pduList.contains(null)"));
+ String errorMsg = "processMessagePart: returning false due to "
+ + (pduList.size() == 0 ? "pduList.size() == 0" : "pduList.contains(null)");
+ loge(errorMsg);
+ mLocalLog.log(errorMsg);
return false;
}
@@ -1529,6 +1566,15 @@
}
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ super.dump(fd, pw, args);
+ if (mCellBroadcastHandler != null) {
+ mCellBroadcastHandler.dump(fd, pw, args);
+ }
+ mLocalLog.dump(fd, pw, args);
+ }
+
// Some providers send formfeeds in their messages. Convert those formfeeds to newlines.
private static String replaceFormFeeds(String s) {
return s == null ? "" : s.replace('\f', '\n');
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
new file mode 100644
index 0000000..8977b03
--- /dev/null
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright 2018 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.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.WifiManager;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoWcdma;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.LocalLog;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * The locale tracker keeps tracking the current locale of the phone.
+ */
+public class LocaleTracker extends Handler {
+ private static final boolean DBG = true;
+ private static final String TAG = LocaleTracker.class.getSimpleName();
+
+ /** Event for getting cell info from the modem */
+ private static final int EVENT_GET_CELL_INFO = 1;
+
+ /** Event for operator numeric update */
+ private static final int EVENT_UPDATE_OPERATOR_NUMERIC = 2;
+
+ /** Event for service state changed */
+ private static final int EVENT_SERVICE_STATE_CHANGED = 3;
+
+ // Todo: Read this from Settings.
+ /** The minimum delay to get cell info from the modem */
+ private static final long CELL_INFO_MIN_DELAY_MS = 2 * SECOND_IN_MILLIS;
+
+ // Todo: Read this from Settings.
+ /** The maximum delay to get cell info from the modem */
+ private static final long CELL_INFO_MAX_DELAY_MS = 10 * MINUTE_IN_MILLIS;
+
+ // Todo: Read this from Settings.
+ /** The delay for periodically getting cell info from the modem */
+ private static final long CELL_INFO_PERIODIC_POLLING_DELAY_MS = 10 * MINUTE_IN_MILLIS;
+
+ private final Phone mPhone;
+
+ /** SIM card state. Must be one of TelephonyManager.SIM_STATE_XXX */
+ private int mSimState;
+
+ /** Current serving PLMN's MCC/MNC */
+ @Nullable
+ private String mOperatorNumeric;
+
+ /** Current cell tower information */
+ @Nullable
+ private List<CellInfo> mCellInfo;
+
+ /** Count of invalid cell info we've got so far. Will reset once we get a successful one */
+ private int mFailCellInfoCount;
+
+ /** The ISO-3166 code of device's current country */
+ @Nullable
+ private String mCurrentCountryIso;
+
+ /** Current service state. Must be one of ServiceState.STATE_XXX. */
+ private int mLastServiceState = -1;
+
+ private final LocalLog mLocalLog = new LocalLog(50);
+
+ /** Broadcast receiver to get SIM card state changed event */
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED.equals(intent.getAction())) {
+ int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, 0);
+ if (phoneId == mPhone.getPhoneId()) {
+ onSimCardStateChanged(intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE,
+ TelephonyManager.SIM_STATE_UNKNOWN));
+ }
+ }
+ }
+ };
+
+ /**
+ * Message handler
+ *
+ * @param msg The message
+ */
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_GET_CELL_INFO:
+ synchronized (this) {
+ getCellInfo();
+ updateLocale();
+ }
+ break;
+ case EVENT_UPDATE_OPERATOR_NUMERIC:
+ updateOperatorNumericSync((String) msg.obj);
+ break;
+ case EVENT_SERVICE_STATE_CHANGED:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ onServiceStateChanged((ServiceState) ar.result);
+ break;
+ default:
+ throw new IllegalStateException("Unexpected message arrives. msg = " + msg.what);
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param phone The phone object
+ * @param looper The looper message handler
+ */
+ public LocaleTracker(Phone phone, Looper looper) {
+ super(looper);
+ mPhone = phone;
+ mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
+ mPhone.getContext().registerReceiver(mBroadcastReceiver, filter);
+
+ mPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
+ }
+
+ /**
+ * Get the device's current country.
+ *
+ * @return The device's current country. Empty string if the information is not available.
+ */
+ @NonNull
+ public synchronized String getCurrentCountry() {
+ return (mCurrentCountryIso != null) ? mCurrentCountryIso : "";
+ }
+
+ /**
+ * Get the MCC from cell tower information.
+ *
+ * @return MCC in string format. Null if the information is not available.
+ */
+ @Nullable
+ private String getMccFromCellInfo() {
+ String selectedMcc = null;
+ if (mCellInfo != null) {
+ Map<String, Integer> countryCodeMap = new HashMap<>();
+ int maxCount = 0;
+ for (CellInfo cellInfo : mCellInfo) {
+ String mcc = null;
+ if (cellInfo instanceof CellInfoGsm) {
+ mcc = ((CellInfoGsm) cellInfo).getCellIdentity().getMccString();
+ } else if (cellInfo instanceof CellInfoLte) {
+ mcc = ((CellInfoLte) cellInfo).getCellIdentity().getMccString();
+ } else if (cellInfo instanceof CellInfoWcdma) {
+ mcc = ((CellInfoWcdma) cellInfo).getCellIdentity().getMccString();
+ }
+ if (mcc != null) {
+ int count = 1;
+ if (countryCodeMap.containsKey(mcc)) {
+ count = countryCodeMap.get(mcc) + 1;
+ }
+ countryCodeMap.put(mcc, count);
+ // This is unlikely, but if MCC from cell info looks different, we choose the
+ // MCC that occurs most.
+ if (count > maxCount) {
+ maxCount = count;
+ selectedMcc = mcc;
+ }
+ }
+ }
+ }
+ return selectedMcc;
+ }
+
+ /**
+ * Called when SIM card state changed. Only when we absolutely know the SIM is absent, we get
+ * cell info from the network. Other SIM states like NOT_READY might be just a transitioning
+ * state.
+ *
+ * @param state SIM card state. Must be one of TelephonyManager.SIM_STATE_XXX.
+ */
+ private synchronized void onSimCardStateChanged(int state) {
+ if (mSimState != state && state == TelephonyManager.SIM_STATE_ABSENT) {
+ if (DBG) log("Sim absent. Get latest cell info from the modem.");
+ getCellInfo();
+ updateLocale();
+ }
+ mSimState = state;
+ }
+
+ /**
+ * Called when service state changed.
+ *
+ * @param serviceState Service state
+ */
+ private void onServiceStateChanged(ServiceState serviceState) {
+ int state = serviceState.getState();
+ if (state != mLastServiceState) {
+ if (state != ServiceState.STATE_POWER_OFF && TextUtils.isEmpty(mOperatorNumeric)) {
+ // When the device is out of airplane mode or powered on, and network's MCC/MNC is
+ // not available, we get cell info from the modem.
+ String msg = "Service state " + ServiceState.rilServiceStateToString(state)
+ + ". Get cell info now.";
+ if (DBG) log(msg);
+ mLocalLog.log(msg);
+ getCellInfo();
+ } else if (state == ServiceState.STATE_POWER_OFF) {
+ // Clear the cell info when the device is in airplane mode.
+ if (mCellInfo != null) mCellInfo.clear();
+ stopCellInfoRetry();
+ }
+ updateLocale();
+ mLastServiceState = state;
+ }
+ }
+
+ /**
+ * Update MCC/MNC from network service state synchronously. Note if this is called from phone
+ * process's main thread and if the update operation requires getting cell info from the modem,
+ * the cached cell info will be used to determine the locale. If the cached cell info is not
+ * acceptable, use {@link #updateOperatorNumericAsync(String)} instead.
+ *
+ * @param operatorNumeric MCC/MNC of the operator
+ */
+ public synchronized void updateOperatorNumericSync(String operatorNumeric) {
+ // Check if the operator numeric changes.
+ if (DBG) log("updateOperatorNumericSync. mcc/mnc=" + operatorNumeric);
+ if (!Objects.equals(mOperatorNumeric, operatorNumeric)) {
+ String msg = "Operator numeric changes to " + operatorNumeric;
+ if (DBG) log(msg);
+ mLocalLog.log(msg);
+ mOperatorNumeric = operatorNumeric;
+
+ // If the operator numeric becomes unavailable, we need to get the latest cell info so
+ // that we can get MCC from it.
+ if (TextUtils.isEmpty(mOperatorNumeric)) {
+ if (DBG) {
+ log("Operator numeric unavailable. Get latest cell info from the modem.");
+ }
+ getCellInfo();
+ } else {
+ // If operator numeric is available, that means we camp on network. So we should
+ // clear the cell info and stop cell info retry.
+ if (mCellInfo != null) mCellInfo.clear();
+ stopCellInfoRetry();
+ }
+ updateLocale();
+ }
+ }
+
+ /**
+ * Update MCC/MNC from network service state asynchronously. The update operation will run
+ * in locale tracker's handler's thread, which can get cell info synchronously from service
+ * state tracker. Note that the country code will not be available immediately after calling
+ * this method.
+ *
+ * @param operatorNumeric MCC/MNC of the operator
+ */
+ public void updateOperatorNumericAsync(String operatorNumeric) {
+ if (DBG) log("updateOperatorNumericAsync. mcc/mnc=" + operatorNumeric);
+ sendMessage(obtainMessage(EVENT_UPDATE_OPERATOR_NUMERIC, operatorNumeric));
+ }
+
+ /**
+ * Get the delay time to get cell info from modem. The delay time grows exponentially to prevent
+ * battery draining.
+ *
+ * @param failCount Count of invalid cell info we've got so far.
+ * @return The delay time for next get cell info
+ */
+ private long getCellInfoDelayTime(int failCount) {
+ // Exponentially grow the delay time
+ long delay = CELL_INFO_MIN_DELAY_MS * (long) Math.pow(2, failCount - 1);
+ if (delay < CELL_INFO_MIN_DELAY_MS) {
+ delay = CELL_INFO_MIN_DELAY_MS;
+ } else if (delay > CELL_INFO_MAX_DELAY_MS) {
+ delay = CELL_INFO_MAX_DELAY_MS;
+ }
+ return delay;
+ }
+
+ /**
+ * Stop retrying getting cell info from the modem. It cancels any scheduled cell info retrieving
+ * request.
+ */
+ private void stopCellInfoRetry() {
+ mFailCellInfoCount = 0;
+ removeMessages(EVENT_GET_CELL_INFO);
+ }
+
+ /**
+ * Get cell info from the modem.
+ */
+ private void getCellInfo() {
+ String msg;
+ if (!mPhone.getServiceStateTracker().getDesiredPowerState()) {
+ msg = "Radio is off. Stopped cell info retry. Cleared the previous cached cell info.";
+ if (mCellInfo != null) mCellInfo.clear();
+ if (DBG) log(msg);
+ mLocalLog.log(msg);
+ stopCellInfoRetry();
+ return;
+ }
+
+ // Get all cell info. Passing null to use default worksource, which indicates the original
+ // request is from telephony internally.
+ mCellInfo = mPhone.getAllCellInfo(null);
+ msg = "getCellInfo: cell info=" + mCellInfo;
+ if (DBG) log(msg);
+ mLocalLog.log(msg);
+ if (mCellInfo == null || mCellInfo.size() == 0) {
+ // If we can't get a valid cell info. Try it again later.
+ long delay = getCellInfoDelayTime(++mFailCellInfoCount);
+ if (DBG) log("Can't get cell info. Try again in " + delay / 1000 + " secs.");
+ removeMessages(EVENT_GET_CELL_INFO);
+ sendMessageDelayed(obtainMessage(EVENT_GET_CELL_INFO), delay);
+ } else {
+ // We successfully got cell info from the modem. We should stop cell info retry.
+ stopCellInfoRetry();
+
+ // Now we need to get the cell info from the modem periodically even if we already got
+ // the cell info because the user can move.
+ sendMessageDelayed(obtainMessage(EVENT_GET_CELL_INFO),
+ CELL_INFO_PERIODIC_POLLING_DELAY_MS);
+ }
+ }
+
+ /**
+ * Update the device's current locale
+ */
+ private void updateLocale() {
+ // If MCC is available from network service state, use it first.
+ String mcc = null;
+ String countryIso = "";
+ if (!TextUtils.isEmpty(mOperatorNumeric)) {
+ try {
+ mcc = mOperatorNumeric.substring(0, 3);
+ countryIso = MccTable.countryCodeForMcc(mcc);
+ } catch (StringIndexOutOfBoundsException ex) {
+ loge("updateLocale: Can't get country from operator numeric. mcc = "
+ + mcc + ". ex=" + ex);
+ }
+ }
+
+ // If for any reason we can't get country from operator numeric, try to get it from cell
+ // info.
+ if (TextUtils.isEmpty(countryIso)) {
+ mcc = getMccFromCellInfo();
+ countryIso = MccTable.countryCodeForMcc(mcc);
+ }
+
+ String msg = "updateLocale: mcc = " + mcc + ", country = " + countryIso;
+ log(msg);
+ mLocalLog.log(msg);
+ if (!Objects.equals(countryIso, mCurrentCountryIso)) {
+ msg = "updateLocale: Change the current country to " + countryIso;
+ log(msg);
+ mLocalLog.log(msg);
+ mCurrentCountryIso = countryIso;
+
+ TelephonyManager.setTelephonyProperty(mPhone.getPhoneId(),
+ TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, mCurrentCountryIso);
+
+ // Set the country code for wifi. This sets allowed wifi channels based on the
+ // country of the carrier we see. If we can't see any, reset to 0 so we don't
+ // broadcast on forbidden channels.
+ ((WifiManager) mPhone.getContext().getSystemService(Context.WIFI_SERVICE))
+ .setCountryCode(countryIso, false);
+ }
+ }
+
+ private void log(String msg) {
+ Rlog.d(TAG, msg);
+ }
+
+ private void loge(String msg) {
+ Rlog.e(TAG, msg);
+ }
+
+ /**
+ * Print the DeviceStateMonitor into the given stream.
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param pw A PrintWriter to which the dump is to be set.
+ * @param args Additional arguments to the dump request.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ pw.println("LocaleTracker:");
+ ipw.increaseIndent();
+ ipw.println("mOperatorNumeric = " + mOperatorNumeric);
+ ipw.println("mSimState = " + mSimState);
+ ipw.println("mCellInfo = " + mCellInfo);
+ ipw.println("mCurrentCountryIso = " + mCurrentCountryIso);
+ ipw.println("mFailCellInfoCount = " + mFailCellInfoCount);
+ ipw.println("Local logs:");
+ ipw.increaseIndent();
+ mLocalLog.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+ ipw.decreaseIndent();
+ ipw.flush();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/MccTable.java b/src/java/com/android/internal/telephony/MccTable.java
index 7d4656f..fb28194 100644
--- a/src/java/com/android/internal/telephony/MccTable.java
+++ b/src/java/com/android/internal/telephony/MccTable.java
@@ -19,11 +19,9 @@
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
-import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Slog;
@@ -114,6 +112,19 @@
/**
* Given a GSM Mobile Country Code, returns
+ * an ISO two-character country code if available.
+ * Returns empty string if unavailable.
+ */
+ public static String countryCodeForMcc(String mcc) {
+ try {
+ return countryCodeForMcc(Integer.parseInt(mcc));
+ } catch (NumberFormatException ex) {
+ return "";
+ }
+ }
+
+ /**
+ * Given a GSM Mobile Country Code, returns
* an ISO 2-3 character language code if available.
* Returns null if unavailable.
*/
@@ -159,11 +170,9 @@
* correct version of resources. If MCC is 0, MCC and MNC will be ignored (not set).
* @param context Context to act on.
* @param mccmnc truncated imsi with just the MCC and MNC - MNC assumed to be from 4th to end
- * @param fromServiceState true if coming from the radio service state, false if from SIM
*/
- public static void updateMccMncConfiguration(Context context, String mccmnc,
- boolean fromServiceState) {
- Slog.d(LOG_TAG, "updateMccMncConfiguration mccmnc='" + mccmnc + "' fromServiceState=" + fromServiceState);
+ public static void updateMccMncConfiguration(Context context, String mccmnc) {
+ Slog.d(LOG_TAG, "updateMccMncConfiguration mccmnc='" + mccmnc);
if (Build.IS_DEBUGGABLE) {
String overrideMcc = SystemProperties.get("persist.sys.override_mcc");
@@ -176,19 +185,11 @@
if (!TextUtils.isEmpty(mccmnc)) {
int mcc, mnc;
- String defaultMccMnc = TelephonyManager.getDefault().getSimOperatorNumeric();
- Slog.d(LOG_TAG, "updateMccMncConfiguration defaultMccMnc=" + defaultMccMnc);
- //Update mccmnc only for default subscription in case of MultiSim.
-// if (!defaultMccMnc.equals(mccmnc)) {
-// Slog.d(LOG_TAG, "Not a Default subscription, ignoring mccmnc config update.");
-// return;
-// }
-
try {
- mcc = Integer.parseInt(mccmnc.substring(0,3));
+ mcc = Integer.parseInt(mccmnc.substring(0, 3));
mnc = Integer.parseInt(mccmnc.substring(3));
- } catch (NumberFormatException e) {
- Slog.e(LOG_TAG, "Error parsing IMSI: " + mccmnc);
+ } catch (NumberFormatException | StringIndexOutOfBoundsException ex) {
+ Slog.e(LOG_TAG, "Error parsing IMSI: " + mccmnc + ". ex=" + ex);
return;
}
@@ -196,33 +197,24 @@
if (mcc != 0) {
setTimezoneFromMccIfNeeded(context, mcc);
}
- if (fromServiceState) {
- setWifiCountryCodeFromMcc(context, mcc);
- } else {
- // from SIM
- try {
- Configuration config = new Configuration();
- boolean updateConfig = false;
- if (mcc != 0) {
- config.mcc = mcc;
- config.mnc = mnc == 0 ? Configuration.MNC_ZERO : mnc;
- updateConfig = true;
- }
- if (updateConfig) {
- Slog.d(LOG_TAG, "updateMccMncConfiguration updateConfig config=" + config);
- ActivityManager.getService().updateConfiguration(config);
- } else {
- Slog.d(LOG_TAG, "updateMccMncConfiguration nothing to update");
- }
- } catch (RemoteException e) {
- Slog.e(LOG_TAG, "Can't update configuration", e);
+ try {
+ Configuration config = new Configuration();
+ boolean updateConfig = false;
+ if (mcc != 0) {
+ config.mcc = mcc;
+ config.mnc = mnc == 0 ? Configuration.MNC_ZERO : mnc;
+ updateConfig = true;
}
- }
- } else {
- if (fromServiceState) {
- // an empty mccmnc means no signal - tell wifi we don't know
- setWifiCountryCodeFromMcc(context, 0);
+
+ if (updateConfig) {
+ Slog.d(LOG_TAG, "updateMccMncConfiguration updateConfig config=" + config);
+ ActivityManager.getService().updateConfiguration(config);
+ } else {
+ Slog.d(LOG_TAG, "updateMccMncConfiguration nothing to update");
+ }
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Can't update configuration", e);
}
}
}
@@ -358,12 +350,25 @@
* @param mcc Mobile Country Code of the SIM or SIM-like entity (build prop on CDMA)
*/
private static void setTimezoneFromMccIfNeeded(Context context, int mcc) {
- if (!TimeServiceHelper.isTimeZoneSettingInitializedStatic()) {
- String zoneId = defaultTimeZoneForMcc(mcc);
- if (zoneId != null && zoneId.length() > 0) {
- // Set time zone based on MCC
- TimeServiceHelper.setDeviceTimeZoneStatic(context, zoneId);
- Slog.d(LOG_TAG, "timezone set to " + zoneId);
+ // Switch to use the time service helper associated with the NitzStateMachine impl
+ // being used. This logic will be removed once the old implementation is removed.
+ if (TelephonyComponentFactory.USE_NEW_NITZ_STATE_MACHINE) {
+ if (!NewTimeServiceHelper.isTimeZoneSettingInitializedStatic()) {
+ String zoneId = defaultTimeZoneForMcc(mcc);
+ if (zoneId != null && zoneId.length() > 0) {
+ // Set time zone based on MCC
+ NewTimeServiceHelper.setDeviceTimeZoneStatic(context, zoneId);
+ Slog.d(LOG_TAG, "timezone set to " + zoneId);
+ }
+ }
+ } else {
+ if (!OldTimeServiceHelper.isTimeZoneSettingInitializedStatic()) {
+ String zoneId = defaultTimeZoneForMcc(mcc);
+ if (zoneId != null && zoneId.length() > 0) {
+ // Set time zone based on MCC
+ OldTimeServiceHelper.setDeviceTimeZoneStatic(context, zoneId);
+ Slog.d(LOG_TAG, "timezone set to " + zoneId);
+ }
}
}
}
@@ -396,20 +401,6 @@
return locale;
}
- /**
- * Set the country code for wifi. This sets allowed wifi channels based on the
- * country of the carrier we see. If we can't see any, reset to 0 so we don't
- * broadcast on forbidden channels.
- * @param context Context to act on.
- * @param mcc Mobile Country Code of the operator. 0 if not known
- */
- private static void setWifiCountryCodeFromMcc(Context context, int mcc) {
- String country = MccTable.countryCodeForMcc(mcc);
- Slog.d(LOG_TAG, "WIFI_COUNTRY_CODE set to " + country);
- WifiManager wM = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- wM.setCountryCode(country, false);
- }
-
static {
sTable = new ArrayList<MccEntry>(240);
diff --git a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
index 30210a8..ae7ede0 100644
--- a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
+++ b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
@@ -36,6 +36,9 @@
import android.telephony.NetworkService;
import android.telephony.Rlog;
+import java.util.Hashtable;
+import java.util.Map;
+
/**
* Class that serves as the layer between NetworkService and ServiceStateTracker. It helps binding,
* sending request and registering for state change to NetworkService.
@@ -79,22 +82,27 @@
mRegStateChangeRegistrants.addUnique(h, what, obj);
}
+ private final Map<NetworkRegStateCallback, Message> mCallbackTable = new Hashtable();
+
public void getNetworkRegistrationState(int domain, Message onCompleteMessage) {
if (onCompleteMessage == null) return;
logd("getNetworkRegistrationState domain " + domain);
if (!isServiceConnected()) {
+ logd("service not connected.");
onCompleteMessage.obj = new AsyncResult(onCompleteMessage.obj, null,
new IllegalStateException("Service not connected."));
onCompleteMessage.sendToTarget();
return;
}
+ NetworkRegStateCallback callback = new NetworkRegStateCallback();
try {
- mServiceBinder.getNetworkRegistrationState(mPhone.getPhoneId(), domain,
- new NetworkRegStateCallback(onCompleteMessage));
+ mCallbackTable.put(callback, onCompleteMessage);
+ mServiceBinder.getNetworkRegistrationState(mPhone.getPhoneId(), domain, callback);
} catch (RemoteException e) {
Rlog.e(TAG, "getNetworkRegistrationState RemoteException " + e);
+ mCallbackTable.remove(callback);
onCompleteMessage.obj = new AsyncResult(onCompleteMessage.obj, null, e);
onCompleteMessage.sendToTarget();
}
@@ -119,13 +127,14 @@
private class NetworkServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
+ logd("service connected.");
mServiceBinder = (INetworkService.Stub) service;
mDeathRecipient = new RegManagerDeathRecipient(name);
try {
mServiceBinder.linkToDeath(mDeathRecipient, 0);
mServiceBinder.createNetworkServiceProvider(mPhone.getPhoneId());
mServiceBinder.registerForNetworkRegistrationStateChanged(mPhone.getPhoneId(),
- new NetworkRegStateCallback(null));
+ new NetworkRegStateCallback());
} catch (RemoteException exception) {
// Remote exception means that the binder already died.
mDeathRecipient.binderDied();
@@ -143,23 +152,19 @@
}
private class NetworkRegStateCallback extends INetworkServiceCallback.Stub {
- // Message only used upon onGetNetworkRegistrationStateComplete.
- // If the callback is passed to listen to network state change,
- // this message is null.
- private final Message mOnCompleteMessage;
-
- NetworkRegStateCallback(Message onCompleteMessage) {
- mOnCompleteMessage = onCompleteMessage;
- }
-
@Override
public void onGetNetworkRegistrationStateComplete(
int result, NetworkRegistrationState state) {
logd("onGetNetworkRegistrationStateComplete result "
+ result + " state " + state);
- mOnCompleteMessage.arg1 = result;
- mOnCompleteMessage.obj = new AsyncResult(mOnCompleteMessage.obj, state, null);
- mOnCompleteMessage.sendToTarget();
+ Message onCompleteMessage = mCallbackTable.remove(this);
+ if (onCompleteMessage != null) {
+ onCompleteMessage.arg1 = result;
+ onCompleteMessage.obj = new AsyncResult(onCompleteMessage.obj, state, null);
+ onCompleteMessage.sendToTarget();
+ } else {
+ loge("onCompleteMessage is null");
+ }
}
@Override
diff --git a/src/java/com/android/internal/telephony/NewNitzStateMachine.java b/src/java/com/android/internal/telephony/NewNitzStateMachine.java
new file mode 100644
index 0000000..20c729f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/NewNitzStateMachine.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright 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.content.Context;
+import android.os.PowerManager;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.TimestampedValue;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
+import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * {@hide}
+ */
+public final class NewNitzStateMachine implements NitzStateMachine {
+
+ private static final String LOG_TAG = ServiceStateTracker.LOG_TAG;
+ private static final boolean DBG = ServiceStateTracker.DBG;
+
+ // Time detection state.
+
+ /**
+ * The last NITZ-sourced time considered sent to the time detector service. Used to rate-limit
+ * calls to the time detector.
+ */
+ private TimestampedValue<Long> mSavedNitzTime;
+
+ // Time Zone detection state.
+
+ /** We always keep the last NITZ signal received in mLatestNitzSignal. */
+ private TimestampedValue<NitzData> mLatestNitzSignal;
+
+ /**
+ * Records whether the device should have a country code available via
+ * {@link DeviceState#getNetworkCountryIsoForPhone()}. Before this an NITZ signal
+ * received is (almost always) not enough to determine time zone. On test networks the country
+ * code should be available but can still be an empty string but this flag indicates that the
+ * information available is unlikely to improve.
+ */
+ private boolean mGotCountryCode = false;
+
+ /**
+ * The last time zone ID that has been determined. It may not have been set as the device time
+ * zone if automatic time zone detection is disabled but may later be used to set the time zone
+ * if the user enables automatic time zone detection.
+ */
+ private String mSavedTimeZoneId;
+
+ /**
+ * Boolean is {@code true} if NITZ has been used to determine a time zone (which may not
+ * ultimately have been used due to user settings). Cleared by {@link #handleNetworkAvailable()}
+ * and {@link #handleNetworkUnavailable()}. The flag can be used when historic NITZ data may no
+ * longer be valid. {@code false} indicates it is reasonable to try to set the time zone using
+ * less reliable algorithms than NITZ-based detection such as by just using network country
+ * code.
+ */
+ private boolean mNitzTimeZoneDetectionSuccessful = false;
+
+ // Miscellaneous dependencies and helpers not related to detection state.
+ private final LocalLog mTimeLog = new LocalLog(15);
+ private final LocalLog mTimeZoneLog = new LocalLog(15);
+ private final GsmCdmaPhone mPhone;
+ private final DeviceState mDeviceState;
+ private final NewTimeServiceHelper mTimeServiceHelper;
+ private final TimeZoneLookupHelper mTimeZoneLookupHelper;
+ /** Wake lock used while setting time of day. */
+ private final PowerManager.WakeLock mWakeLock;
+ private static final String WAKELOCK_TAG = "NitzStateMachine";
+
+ public NewNitzStateMachine(GsmCdmaPhone phone) {
+ this(phone,
+ new NewTimeServiceHelper(phone.getContext()),
+ new DeviceState(phone),
+ new TimeZoneLookupHelper());
+ }
+
+ @VisibleForTesting
+ public NewNitzStateMachine(GsmCdmaPhone phone, NewTimeServiceHelper timeServiceHelper,
+ DeviceState deviceState, TimeZoneLookupHelper timeZoneLookupHelper) {
+ mPhone = phone;
+
+ Context context = phone.getContext();
+ PowerManager powerManager =
+ (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
+
+ mDeviceState = deviceState;
+ mTimeZoneLookupHelper = timeZoneLookupHelper;
+ mTimeServiceHelper = timeServiceHelper;
+ mTimeServiceHelper.setListener(new NewTimeServiceHelper.Listener() {
+ @Override
+ public void onTimeZoneDetectionChange(boolean enabled) {
+ if (enabled) {
+ handleAutoTimeZoneEnabled();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void handleNetworkCountryCodeSet(boolean countryChanged) {
+ boolean hadCountryCode = mGotCountryCode;
+ mGotCountryCode = true;
+
+ String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
+ if (!TextUtils.isEmpty(isoCountryCode) && !mNitzTimeZoneDetectionSuccessful) {
+ updateTimeZoneFromNetworkCountryCode(isoCountryCode);
+ }
+
+ if (mLatestNitzSignal != null && (countryChanged || !hadCountryCode)) {
+ updateTimeZoneFromCountryAndNitz();
+ }
+ }
+
+ private void updateTimeZoneFromCountryAndNitz() {
+ String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
+ TimestampedValue<NitzData> nitzSignal = mLatestNitzSignal;
+
+ // TimeZone.getDefault() returns a default zone (GMT) even when time zone have never
+ // been set which makes it difficult to tell if it's what the user / time zone detection
+ // has chosen. isTimeZoneSettingInitialized() tells us whether the time zone of the
+ // device has ever been explicit set by the user or code.
+ final boolean isTimeZoneSettingInitialized =
+ mTimeServiceHelper.isTimeZoneSettingInitialized();
+
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz:"
+ + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+ + " nitzSignal=" + nitzSignal
+ + " isoCountryCode=" + isoCountryCode);
+ }
+
+ try {
+ NitzData nitzData = nitzSignal.getValue();
+
+ String zoneId;
+ if (nitzData.getEmulatorHostTimeZone() != null) {
+ zoneId = nitzData.getEmulatorHostTimeZone().getID();
+ } else if (!mGotCountryCode) {
+ // We don't have a country code so we won't try to look up the time zone.
+ zoneId = null;
+ } else if (TextUtils.isEmpty(isoCountryCode)) {
+ // We have a country code but it's empty. This is most likely because we're on a
+ // test network that's using a bogus MCC (eg, "001"). Obtain a TimeZone based only
+ // on the NITZ parameters: it's only going to be correct in a few cases but it
+ // should at least have the correct offset.
+ OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitz(nitzData);
+ String logMsg = "updateTimeZoneFromCountryAndNitz: lookupByNitz returned"
+ + " lookupResult=" + lookupResult;
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg);
+ }
+ // We log this in the time zone log because it has been a source of bugs.
+ mTimeZoneLog.log(logMsg);
+
+ zoneId = lookupResult != null ? lookupResult.zoneId : null;
+ } else if (mLatestNitzSignal == null) {
+ if (DBG) {
+ Rlog.d(LOG_TAG,
+ "updateTimeZoneFromCountryAndNitz: No cached NITZ data available,"
+ + " not setting zone");
+ }
+ zoneId = null;
+ } else if (isNitzSignalOffsetInfoBogus(nitzSignal, isoCountryCode)) {
+ String logMsg = "updateTimeZoneFromCountryAndNitz: Received NITZ looks bogus, "
+ + " isoCountryCode=" + isoCountryCode
+ + " nitzSignal=" + nitzSignal;
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg);
+ }
+ // We log this in the time zone log because it has been a source of bugs.
+ mTimeZoneLog.log(logMsg);
+
+ zoneId = null;
+ } else {
+ OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitzCountry(
+ nitzData, isoCountryCode);
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: using"
+ + " lookupByNitzCountry(nitzData, isoCountryCode),"
+ + " nitzData=" + nitzData
+ + " isoCountryCode=" + isoCountryCode
+ + " lookupResult=" + lookupResult);
+ }
+ zoneId = lookupResult != null ? lookupResult.zoneId : null;
+ }
+
+ // Log the action taken to the dedicated time zone log.
+ final String tmpLog = "updateTimeZoneFromCountryAndNitz:"
+ + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+ + " isoCountryCode=" + isoCountryCode
+ + " nitzSignal=" + nitzSignal
+ + " zoneId=" + zoneId
+ + " isTimeZoneDetectionEnabled()="
+ + mTimeServiceHelper.isTimeZoneDetectionEnabled();
+ mTimeZoneLog.log(tmpLog);
+
+ // Set state as needed.
+ if (zoneId != null) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: zoneId=" + zoneId);
+ }
+ if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
+ setAndBroadcastNetworkSetTimeZone(zoneId);
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: skip changing zone"
+ + " as isTimeZoneDetectionEnabled() is false");
+ }
+ }
+ mSavedTimeZoneId = zoneId;
+ mNitzTimeZoneDetectionSuccessful = true;
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG,
+ "updateTimeZoneFromCountryAndNitz: zoneId == null, do nothing");
+ }
+ }
+ } catch (RuntimeException ex) {
+ Rlog.e(LOG_TAG, "updateTimeZoneFromCountryAndNitz: Processing NITZ data"
+ + " nitzSignal=" + nitzSignal
+ + " isoCountryCode=" + isoCountryCode
+ + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+ + " ex=" + ex);
+ }
+ }
+
+ /**
+ * Returns true if the NITZ signal is definitely bogus, assuming that the country is correct.
+ */
+ private boolean isNitzSignalOffsetInfoBogus(
+ TimestampedValue<NitzData> nitzSignal, String isoCountryCode) {
+
+ if (TextUtils.isEmpty(isoCountryCode)) {
+ // We cannot say for sure.
+ return false;
+ }
+
+ NitzData newNitzData = nitzSignal.getValue();
+ boolean zeroOffsetNitz = newNitzData.getLocalOffsetMillis() == 0 && !newNitzData.isDst();
+ return zeroOffsetNitz && !countryUsesUtc(isoCountryCode, nitzSignal);
+ }
+
+ private boolean countryUsesUtc(
+ String isoCountryCode, TimestampedValue<NitzData> nitzSignal) {
+ return mTimeZoneLookupHelper.countryUsesUtc(
+ isoCountryCode,
+ nitzSignal.getValue().getCurrentTimeInMillis());
+ }
+
+ @Override
+ public void handleNetworkAvailable() {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleNetworkAvailable: mNitzTimeZoneDetectionSuccessful="
+ + mNitzTimeZoneDetectionSuccessful
+ + ", Setting mNitzTimeZoneDetectionSuccessful=false");
+ }
+ mNitzTimeZoneDetectionSuccessful = false;
+ }
+
+ @Override
+ public void handleNetworkUnavailable() {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleNetworkUnavailable");
+ }
+
+ mGotCountryCode = false;
+ mNitzTimeZoneDetectionSuccessful = false;
+ }
+
+ @Override
+ public void handleNitzReceived(TimestampedValue<NitzData> nitzSignal) {
+ // Always store the latest NITZ signal received.
+ mLatestNitzSignal = nitzSignal;
+
+ updateTimeZoneFromCountryAndNitz();
+ updateTimeFromNitz();
+ }
+
+ private void updateTimeFromNitz() {
+ TimestampedValue<NitzData> nitzSignal = mLatestNitzSignal;
+ try {
+ boolean ignoreNitz = mDeviceState.getIgnoreNitz();
+ if (ignoreNitz) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeFromNitz: Not suggesting system clock because"
+ + " gsm.ignore-nitz is set");
+ }
+ return;
+ }
+
+ // Validate the nitzTimeSignal to reject obviously bogus elapsedRealtime values.
+ try {
+ // Acquire the wake lock as we are reading the elapsed realtime clock below.
+ mWakeLock.acquire();
+
+ long elapsedRealtime = mTimeServiceHelper.elapsedRealtime();
+ long millisSinceNitzReceived =
+ elapsedRealtime - nitzSignal.getReferenceTimeMillis();
+ if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeFromNitz: not setting time, unexpected"
+ + " elapsedRealtime=" + elapsedRealtime
+ + " nitzSignal=" + nitzSignal);
+ }
+ return;
+ }
+ } finally {
+ mWakeLock.release();
+ }
+
+ TimestampedValue<Long> newNitzTime = new TimestampedValue<>(
+ nitzSignal.getReferenceTimeMillis(),
+ nitzSignal.getValue().getCurrentTimeInMillis());
+
+ // Perform rate limiting: a NITZ signal received too close to a previous
+ // one will be disregarded unless there is a significant difference between the
+ // UTC times they represent.
+ if (mSavedNitzTime != null) {
+ int nitzUpdateSpacing = mDeviceState.getNitzUpdateSpacingMillis();
+ int nitzUpdateDiff = mDeviceState.getNitzUpdateDiffMillis();
+
+ // Calculate the elapsed time between the new signal and the last signal.
+ long elapsedRealtimeSinceLastSaved = newNitzTime.getReferenceTimeMillis()
+ - mSavedNitzTime.getReferenceTimeMillis();
+
+ // Calculate the UTC difference between the time the two signals hold.
+ long utcTimeDifferenceMillis =
+ newNitzTime.getValue() - mSavedNitzTime.getValue();
+
+ // Ideally the difference between elapsedRealtimeSinceLastSaved and
+ // utcTimeDifferenceMillis would be zero.
+ long millisGained = utcTimeDifferenceMillis - elapsedRealtimeSinceLastSaved;
+
+ if (elapsedRealtimeSinceLastSaved <= nitzUpdateSpacing
+ && Math.abs(millisGained) <= nitzUpdateDiff) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeFromNitz: not setting time. NITZ signal is"
+ + " too similar to previous value received "
+ + " mSavedNitzTime=" + mSavedNitzTime
+ + ", nitzSignal=" + nitzSignal
+ + ", nitzUpdateSpacing=" + nitzUpdateSpacing
+ + ", nitzUpdateDiff=" + nitzUpdateDiff);
+ }
+ return;
+ }
+ }
+
+ String logMsg = "updateTimeFromNitz: suggesting system clock update"
+ + " nitzSignal=" + nitzSignal
+ + ", newNitzTime=" + newNitzTime
+ + ", mSavedNitzTime= " + mSavedNitzTime;
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg);
+ }
+ mTimeLog.log(logMsg);
+ mTimeServiceHelper.suggestDeviceTime(newNitzTime);
+ TelephonyMetrics.getInstance().writeNITZEvent(
+ mPhone.getPhoneId(), newNitzTime.getValue());
+
+ // Save the last NITZ time signal that was suggested to enable rate limiting.
+ mSavedNitzTime = newNitzTime;
+ } catch (RuntimeException ex) {
+ Rlog.e(LOG_TAG, "updateTimeFromNitz: Processing NITZ data"
+ + " nitzSignal=" + nitzSignal
+ + " ex=" + ex);
+ }
+ }
+
+ private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "setAndBroadcastNetworkSetTimeZone: zoneId=" + zoneId);
+ }
+ mTimeServiceHelper.setDeviceTimeZone(zoneId);
+ if (DBG) {
+ Rlog.d(LOG_TAG,
+ "setAndBroadcastNetworkSetTimeZone: called setDeviceTimeZone()"
+ + " zoneId=" + zoneId);
+ }
+ }
+
+ private void handleAutoTimeZoneEnabled() {
+ String tmpLog = "handleAutoTimeZoneEnabled: Reverting to NITZ TimeZone:"
+ + " mSavedTimeZoneId=" + mSavedTimeZoneId;
+ if (DBG) {
+ Rlog.d(LOG_TAG, tmpLog);
+ }
+ mTimeZoneLog.log(tmpLog);
+ if (mSavedTimeZoneId != null) {
+ setAndBroadcastNetworkSetTimeZone(mSavedTimeZoneId);
+ }
+ }
+
+ @Override
+ public void dumpState(PrintWriter pw) {
+ // Time Detection State
+ pw.println(" mSavedTime=" + mSavedNitzTime);
+
+ // Time Zone Detection State
+ pw.println(" mLatestNitzSignal=" + mLatestNitzSignal);
+ pw.println(" mGotCountryCode=" + mGotCountryCode);
+ pw.println(" mSavedTimeZoneId=" + mSavedTimeZoneId);
+ pw.println(" mNitzTimeZoneDetectionSuccessful=" + mNitzTimeZoneDetectionSuccessful);
+
+ // Miscellaneous
+ pw.println(" mWakeLock=" + mWakeLock);
+ pw.flush();
+ }
+
+ @Override
+ public void dumpLogs(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+ ipw.println(" Time Logs:");
+ ipw.increaseIndent();
+ mTimeLog.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+
+ ipw.println(" Time zone Logs:");
+ ipw.increaseIndent();
+ mTimeZoneLog.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+ }
+
+ /**
+ * Update time zone by network country code, works well on countries which only have one time
+ * zone or multiple zones with the same offset.
+ *
+ * @param iso Country code from network MCC
+ */
+ private void updateTimeZoneFromNetworkCountryCode(String iso) {
+ CountryResult lookupResult = mTimeZoneLookupHelper.lookupByCountry(
+ iso, mTimeServiceHelper.currentTimeMillis());
+ if (lookupResult != null && lookupResult.allZonesHaveSameOffset) {
+ String logMsg = "updateTimeZoneFromNetworkCountryCode: tz result found"
+ + " iso=" + iso
+ + " lookupResult=" + lookupResult;
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg);
+ }
+ mTimeZoneLog.log(logMsg);
+ String zoneId = lookupResult.zoneId;
+ if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
+ setAndBroadcastNetworkSetTimeZone(zoneId);
+ }
+ mSavedTimeZoneId = zoneId;
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromNetworkCountryCode: no good zone for"
+ + " iso=" + iso
+ + " lookupResult=" + lookupResult);
+ }
+ }
+ }
+
+ public boolean getNitzTimeZoneDetectionSuccessful() {
+ return mNitzTimeZoneDetectionSuccessful;
+ }
+
+ @Override
+ public NitzData getCachedNitzData() {
+ return mLatestNitzSignal != null ? mLatestNitzSignal.getValue() : null;
+ }
+
+ @Override
+ public String getSavedTimeZoneId() {
+ return mSavedTimeZoneId;
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/TimeServiceHelper.java b/src/java/com/android/internal/telephony/NewTimeServiceHelper.java
similarity index 79%
copy from src/java/com/android/internal/telephony/TimeServiceHelper.java
copy to src/java/com/android/internal/telephony/NewTimeServiceHelper.java
index 94b094f..1346c5f 100644
--- a/src/java/com/android/internal/telephony/TimeServiceHelper.java
+++ b/src/java/com/android/internal/telephony/NewTimeServiceHelper.java
@@ -17,6 +17,8 @@
package com.android.internal.telephony;
import android.app.AlarmManager;
+import android.app.timedetector.TimeDetector;
+import android.app.timedetector.TimeSignal;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -26,24 +28,20 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.TimestampedValue;
/**
* An interface to various time / time zone detection behaviors that should be centralized into a
* new service.
*/
// Non-final to allow mocking.
-public class TimeServiceHelper {
+public class NewTimeServiceHelper {
/**
* Callback interface for automatic detection enable/disable changes.
*/
public interface Listener {
/**
- * Automatic time detection has been enabled or disabled.
- */
- void onTimeDetectionChange(boolean enabled);
-
- /**
* Automatic time zone detection has been enabled or disabled.
*/
void onTimeZoneDetectionChange(boolean enabled);
@@ -53,13 +51,15 @@
private final Context mContext;
private final ContentResolver mCr;
+ private final TimeDetector mTimeDetector;
private Listener mListener;
/** Creates a TimeServiceHelper */
- public TimeServiceHelper(Context context) {
+ public NewTimeServiceHelper(Context context) {
mContext = context;
mCr = context.getContentResolver();
+ mTimeDetector = context.getSystemService(TimeDetector.class);
}
/**
@@ -75,13 +75,6 @@
}
this.mListener = listener;
mCr.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
- new ContentObserver(new Handler()) {
- public void onChange(boolean selfChange) {
- listener.onTimeDetectionChange(isTimeDetectionEnabled());
- }
- });
- mCr.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
new ContentObserver(new Handler()) {
public void onChange(boolean selfChange) {
@@ -113,17 +106,6 @@
}
/**
- * Returns true if automatic time detection is enabled in settings.
- */
- public boolean isTimeDetectionEnabled() {
- try {
- return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME) > 0;
- } catch (Settings.SettingNotFoundException snfe) {
- return true;
- }
- }
-
- /**
* Returns true if automatic time zone detection is enabled in settings.
*/
public boolean isTimeZoneDetectionEnabled() {
@@ -145,17 +127,13 @@
}
/**
- * Set the time and Send out a sticky broadcast so the system can determine
- * if the time was set by the carrier.
+ * Suggest the time to the {@link TimeDetector}.
*
- * @param time time set by network
+ * @param signalTimeMillis the signal time as received from the network
*/
- public void setDeviceTime(long time) {
- SystemClock.setCurrentTimeMillis(time);
- Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra("time", time);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ public void suggestDeviceTime(TimestampedValue<Long> signalTimeMillis) {
+ TimeSignal timeSignal = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, signalTimeMillis);
+ mTimeDetector.suggestTime(timeSignal);
}
/**
diff --git a/src/java/com/android/internal/telephony/NitzStateMachine.java b/src/java/com/android/internal/telephony/NitzStateMachine.java
index 1a365ee..6a5e47a 100644
--- a/src/java/com/android/internal/telephony/NitzStateMachine.java
+++ b/src/java/com/android/internal/telephony/NitzStateMachine.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 2018 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.
@@ -18,38 +18,72 @@
import android.content.ContentResolver;
import android.content.Context;
-import android.os.PowerManager;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.telephony.Rlog;
import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.TimeUtils;
+import android.util.TimestampedValue;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
-import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
-import com.android.internal.telephony.metrics.TelephonyMetrics;
-import com.android.internal.telephony.util.TimeStampedValue;
import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.TimeZone;
/**
* {@hide}
*/
-// Non-final to allow mocking.
-public class NitzStateMachine {
+public interface NitzStateMachine {
+
+ /**
+ * Called when the network country is set on the Phone. Although set, the network country code
+ * may be invalid.
+ *
+ * @param countryChanged true when the country code is known to have changed, false if it
+ * probably hasn't
+ */
+ void handleNetworkCountryCodeSet(boolean countryChanged);
+
+ /**
+ * Informs the {@link NitzStateMachine} that the network has become available.
+ */
+ void handleNetworkAvailable();
+
+ /**
+ * Informs the {@link NitzStateMachine} that the network has become unavailable.
+ */
+ void handleNetworkUnavailable();
+
+ /**
+ * Handle a new NITZ signal being received.
+ */
+ void handleNitzReceived(TimestampedValue<NitzData> nitzSignal);
+
+ /**
+ * Dumps the current in-memory state to the supplied PrintWriter.
+ */
+ void dumpState(PrintWriter pw);
+
+ /**
+ * Dumps the time / time zone logs to the supplied IndentingPrintWriter.
+ */
+ void dumpLogs(FileDescriptor fd, IndentingPrintWriter ipw, String[] args);
+
+ /**
+ * Returns the last NITZ data that was cached.
+ */
+ NitzData getCachedNitzData();
+
+ /**
+ * Returns the time zone ID from the most recent time that a time zone could be determined by
+ * this state machine.
+ */
+ String getSavedTimeZoneId();
/**
* A proxy over device state that allows things like system properties, system clock
* to be faked for tests.
*/
// Non-final to allow mocking.
- public static class DeviceState {
+ class DeviceState {
private static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10;
private final int mNitzUpdateSpacing;
@@ -102,588 +136,4 @@
return mTelephonyManager.getNetworkCountryIsoForPhone(mPhone.getPhoneId());
}
}
-
- private static final String LOG_TAG = ServiceStateTracker.LOG_TAG;
- private static final boolean DBG = ServiceStateTracker.DBG;
-
- // Time detection state.
-
- /**
- * The last NITZ-sourced time considered. If auto time detection was off at the time this may
- * not have been used to set the device time, but it can be used if auto time detection is
- * re-enabled.
- */
- private TimeStampedValue<Long> mSavedNitzTime;
-
- // Time Zone detection state.
-
- /**
- * Sometimes we get the NITZ time before we know what country we are in. We keep the time zone
- * information from the NITZ string in mLatestNitzSignal so we can fix the time zone once we
- * know the country.
- */
- private boolean mNeedCountryCodeForNitz = false;
-
- private TimeStampedValue<NitzData> mLatestNitzSignal;
- private boolean mGotCountryCode = false;
- private String mSavedTimeZoneId;
-
- /**
- * Boolean is {@code true} if {@link #handleNitzReceived(TimeStampedValue)} has been called and
- * was able to determine a time zone (which may not ultimately have been used due to user
- * settings). Cleared by {@link #handleNetworkAvailable()} and
- * {@link #handleNetworkUnavailable()}. The flag can be used when historic NITZ data may no
- * longer be valid. {@code true} indicates it's not reasonable to try to set the time zone using
- * less reliable algorithms than NITZ-based detection such as by just using network country
- * code.
- */
- private boolean mNitzTimeZoneDetectionSuccessful = false;
-
- // Miscellaneous dependencies and helpers not related to detection state.
- private final LocalLog mTimeLog = new LocalLog(15);
- private final LocalLog mTimeZoneLog = new LocalLog(15);
- private final GsmCdmaPhone mPhone;
- private final DeviceState mDeviceState;
- private final TimeServiceHelper mTimeServiceHelper;
- private final TimeZoneLookupHelper mTimeZoneLookupHelper;
- /** Wake lock used while setting time of day. */
- private final PowerManager.WakeLock mWakeLock;
- private static final String WAKELOCK_TAG = "NitzStateMachine";
-
- public NitzStateMachine(GsmCdmaPhone phone) {
- this(phone,
- new TimeServiceHelper(phone.getContext()),
- new DeviceState(phone),
- new TimeZoneLookupHelper());
- }
-
- @VisibleForTesting
- public NitzStateMachine(GsmCdmaPhone phone, TimeServiceHelper timeServiceHelper,
- DeviceState deviceState, TimeZoneLookupHelper timeZoneLookupHelper) {
- mPhone = phone;
-
- Context context = phone.getContext();
- PowerManager powerManager =
- (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
-
- mDeviceState = deviceState;
- mTimeZoneLookupHelper = timeZoneLookupHelper;
- mTimeServiceHelper = timeServiceHelper;
- mTimeServiceHelper.setListener(new TimeServiceHelper.Listener() {
- @Override
- public void onTimeDetectionChange(boolean enabled) {
- if (enabled) {
- handleAutoTimeEnabled();
- }
- }
-
- @Override
- public void onTimeZoneDetectionChange(boolean enabled) {
- if (enabled) {
- handleAutoTimeZoneEnabled();
- }
- }
- });
- }
-
- /**
- * Called when the network country is set on the Phone. Although set, the network country code
- * may be invalid.
- *
- * @param countryChanged true when the country code is known to have changed, false if it
- * probably hasn't
- */
- public void handleNetworkCountryCodeSet(boolean countryChanged) {
- mGotCountryCode = true;
-
- String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
- if (!TextUtils.isEmpty(isoCountryCode)
- && !mNitzTimeZoneDetectionSuccessful
- && mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
- updateTimeZoneByNetworkCountryCode(isoCountryCode);
- }
-
- if (countryChanged || mNeedCountryCodeForNitz) {
- // TimeZone.getDefault() returns a default zone (GMT) even when time zone have never
- // been set which makes it difficult to tell if it's what the user / time zone detection
- // has chosen. isTimeZoneSettingInitialized() tells us whether the time zone of the
- // device has ever been explicit set by the user or code.
- final boolean isTimeZoneSettingInitialized =
- mTimeServiceHelper.isTimeZoneSettingInitialized();
- if (DBG) {
- Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet:"
- + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
- + " mLatestNitzSignal=" + mLatestNitzSignal
- + " isoCountryCode=" + isoCountryCode);
- }
- String zoneId;
- if (TextUtils.isEmpty(isoCountryCode) && mNeedCountryCodeForNitz) {
- // Country code not found. This is likely a test network.
- // Get a TimeZone based only on the NITZ parameters (best guess).
-
- // mNeedCountryCodeForNitz is only set to true when mLatestNitzSignal is set so
- // there's no need to check mLatestNitzSignal == null.
- OffsetResult lookupResult =
- mTimeZoneLookupHelper.lookupByNitz(mLatestNitzSignal.mValue);
- if (DBG) {
- Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: guessZoneIdByNitz() returned"
- + " lookupResult=" + lookupResult);
- }
- zoneId = lookupResult != null ? lookupResult.zoneId : null;
- } else if (mLatestNitzSignal == null) {
- zoneId = null;
- if (DBG) {
- Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: No cached NITZ data available,"
- + " not setting zone");
- }
- } else { // mLatestNitzSignal != null
- if (nitzOffsetMightBeBogus(mLatestNitzSignal.mValue)
- && isTimeZoneSettingInitialized
- && !countryUsesUtc(isoCountryCode, mLatestNitzSignal)) {
-
- // This case means that (1) the device received an NITZ signal that could be
- // bogus due to having a zero offset from UTC, (2) the device has had a time
- // zone set explicitly and (3) the iso tells us the country is NOT one that uses
- // a zero offset. This is interpreted as being NITZ incorrectly reporting a
- // local time and not a UTC time. The zone is left as the current device's zone
- // setting, and the system clock may be adjusted by taking the NITZ time and
- // assuming the current zone setting is correct.
-
- TimeZone zone = TimeZone.getDefault();
- if (DBG) {
- Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: NITZ looks bogus, maybe using"
- + " current default zone to adjust the system clock,"
- + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz
- + " mLatestNitzSignal=" + mLatestNitzSignal
- + " zone=" + zone);
- }
- zoneId = zone.getID();
-
- if (mNeedCountryCodeForNitz) {
- NitzData nitzData = mLatestNitzSignal.mValue;
- try {
- // Acquire the wakelock as we're reading the elapsed realtime clock
- // here.
- mWakeLock.acquire();
-
- // Use the time that came with the NITZ offset that we think is bogus:
- // we just interpret it as local time.
- long ctm = nitzData.getCurrentTimeInMillis();
- long delayAdjustedCtm = ctm + (mTimeServiceHelper.elapsedRealtime()
- - mLatestNitzSignal.mElapsedRealtime);
- long tzOffset = zone.getOffset(delayAdjustedCtm);
- if (DBG) {
- Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet:"
- + " tzOffset=" + tzOffset
- + " delayAdjustedCtm="
- + TimeUtils.logTimeOfDay(delayAdjustedCtm));
- }
- if (mTimeServiceHelper.isTimeDetectionEnabled()) {
- long timeZoneAdjustedCtm = delayAdjustedCtm - tzOffset;
- String msg = "handleNetworkCountryCodeSet: setting time"
- + " timeZoneAdjustedCtm="
- + TimeUtils.logTimeOfDay(timeZoneAdjustedCtm);
- setAndBroadcastNetworkSetTime(msg, timeZoneAdjustedCtm);
- } else {
- // Adjust the saved NITZ time to account for tzOffset.
- mSavedNitzTime = new TimeStampedValue<>(
- mSavedNitzTime.mValue - tzOffset,
- mSavedNitzTime.mElapsedRealtime);
- if (DBG) {
- Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet:"
- + "adjusting time mSavedNitzTime=" + mSavedNitzTime);
- }
- }
- } finally {
- mWakeLock.release();
- }
- }
- } else {
- NitzData nitzData = mLatestNitzSignal.mValue;
- OffsetResult lookupResult =
- mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, isoCountryCode);
- if (DBG) {
- Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: using"
- + " guessZoneIdByNitzCountry(nitzData, isoCountryCode),"
- + " nitzData=" + nitzData
- + " isoCountryCode=" + isoCountryCode
- + " lookupResult=" + lookupResult);
- }
- zoneId = lookupResult != null ? lookupResult.zoneId : null;
- }
- }
- final String tmpLog = "handleNetworkCountryCodeSet:"
- + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
- + " mLatestNitzSignal=" + mLatestNitzSignal
- + " isoCountryCode=" + isoCountryCode
- + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz
- + " zoneId=" + zoneId;
- mTimeZoneLog.log(tmpLog);
-
- if (zoneId != null) {
- Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: zoneId != null, zoneId=" + zoneId);
- if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
- setAndBroadcastNetworkSetTimeZone(zoneId);
- } else {
- Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: skip changing zone as"
- + " isTimeZoneDetectionEnabled() is false");
- }
- if (mNeedCountryCodeForNitz) {
- mSavedTimeZoneId = zoneId;
- }
- } else {
- Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: lookupResult == null, do nothing");
- }
- mNeedCountryCodeForNitz = false;
- }
- }
-
- private boolean countryUsesUtc(
- String isoCountryCode, TimeStampedValue<NitzData> nitzSignal) {
- return mTimeZoneLookupHelper.countryUsesUtc(
- isoCountryCode,
- nitzSignal.mValue.getCurrentTimeInMillis());
- }
-
- /**
- * Informs the {@link NitzStateMachine} that the network has become available.
- */
- public void handleNetworkAvailable() {
- if (DBG) {
- Rlog.d(LOG_TAG, "handleNetworkAvailable: mNitzTimeZoneDetectionSuccessful="
- + mNitzTimeZoneDetectionSuccessful
- + ", Setting mNitzTimeZoneDetectionSuccessful=false");
- }
- mNitzTimeZoneDetectionSuccessful = false;
- }
-
- /**
- * Informs the {@link NitzStateMachine} that the network has become unavailable.
- */
- public void handleNetworkUnavailable() {
- if (DBG) {
- Rlog.d(LOG_TAG, "handleNetworkUnavailable");
- }
-
- mGotCountryCode = false;
- mNitzTimeZoneDetectionSuccessful = false;
- }
-
- /**
- * Returns {@code true} if the NITZ data looks like it might be incomplete or bogus, i.e. it has
- * a zero offset from UTC with either no DST information available or a zero DST offset.
- */
- private static boolean nitzOffsetMightBeBogus(NitzData nitzData) {
- return nitzData.getLocalOffsetMillis() == 0 && !nitzData.isDst();
- }
-
- /**
- * Handle a new NITZ signal being received.
- */
- public void handleNitzReceived(TimeStampedValue<NitzData> nitzSignal) {
- handleTimeZoneFromNitz(nitzSignal);
- handleTimeFromNitz(nitzSignal);
- }
-
- private void handleTimeZoneFromNitz(TimeStampedValue<NitzData> nitzSignal) {
- try {
- NitzData newNitzData = nitzSignal.mValue;
- String iso = mDeviceState.getNetworkCountryIsoForPhone();
- String zoneId;
- if (newNitzData.getEmulatorHostTimeZone() != null) {
- zoneId = newNitzData.getEmulatorHostTimeZone().getID();
- } else {
- if (!mGotCountryCode) {
- zoneId = null;
- } else if (!TextUtils.isEmpty(iso)) {
- OffsetResult lookupResult =
- mTimeZoneLookupHelper.lookupByNitzCountry(newNitzData, iso);
- zoneId = lookupResult != null ? lookupResult.zoneId : null;
- } else {
- // We don't have a valid iso country code. This is
- // most likely because we're on a test network that's
- // using a bogus MCC (eg, "001"), so get a TimeZone
- // based only on the NITZ parameters.
- OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitz(newNitzData);
- if (DBG) {
- Rlog.d(LOG_TAG, "handleTimeZoneFromNitz: guessZoneIdByNitz returned"
- + " lookupResult=" + lookupResult);
- }
- zoneId = lookupResult != null ? lookupResult.zoneId : null;
- }
- }
-
- if ((zoneId == null)
- || mLatestNitzSignal == null
- || offsetInfoDiffers(newNitzData, mLatestNitzSignal.mValue)) {
- // We got the time before the country, or the zone has changed
- // so we don't know how to identify the DST rules yet. Save
- // the information and hope to fix it up later.
- mNeedCountryCodeForNitz = true;
- mLatestNitzSignal = nitzSignal;
- }
-
- String tmpLog = "handleTimeZoneFromNitz: nitzSignal=" + nitzSignal
- + " zoneId=" + zoneId
- + " iso=" + iso + " mGotCountryCode=" + mGotCountryCode
- + " mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz
- + " isTimeZoneDetectionEnabled()="
- + mTimeServiceHelper.isTimeZoneDetectionEnabled();
- if (DBG) {
- Rlog.d(LOG_TAG, tmpLog);
- }
- mTimeZoneLog.log(tmpLog);
-
- if (zoneId != null) {
- if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
- setAndBroadcastNetworkSetTimeZone(zoneId);
- }
- mNitzTimeZoneDetectionSuccessful = true;
- mSavedTimeZoneId = zoneId;
- }
- } catch (RuntimeException ex) {
- Rlog.e(LOG_TAG, "handleTimeZoneFromNitz: Processing NITZ data"
- + " nitzSignal=" + nitzSignal
- + " ex=" + ex);
- }
- }
-
- private static boolean offsetInfoDiffers(NitzData one, NitzData two) {
- return one.getLocalOffsetMillis() != two.getLocalOffsetMillis()
- || one.isDst() != two.isDst();
- }
-
- private void handleTimeFromNitz(TimeStampedValue<NitzData> nitzSignal) {
- try {
- boolean ignoreNitz = mDeviceState.getIgnoreNitz();
- if (ignoreNitz) {
- Rlog.d(LOG_TAG,
- "handleTimeFromNitz: Not setting clock because gsm.ignore-nitz is set");
- return;
- }
-
- try {
- // Acquire the wake lock as we are reading the elapsed realtime clock and system
- // clock.
- mWakeLock.acquire();
-
- // Validate the nitzTimeSignal to reject obviously bogus elapsedRealtime values.
- long elapsedRealtime = mTimeServiceHelper.elapsedRealtime();
- long millisSinceNitzReceived = elapsedRealtime - nitzSignal.mElapsedRealtime;
- if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) {
- if (DBG) {
- Rlog.d(LOG_TAG, "handleTimeFromNitz: not setting time, unexpected"
- + " elapsedRealtime=" + elapsedRealtime
- + " nitzSignal=" + nitzSignal);
- }
- return;
- }
-
- // Adjust the NITZ time by the delay since it was received to get the time now.
- long adjustedCurrentTimeMillis =
- nitzSignal.mValue.getCurrentTimeInMillis() + millisSinceNitzReceived;
- long gained = adjustedCurrentTimeMillis - mTimeServiceHelper.currentTimeMillis();
-
- if (mTimeServiceHelper.isTimeDetectionEnabled()) {
- String logMsg = "handleTimeFromNitz:"
- + " nitzSignal=" + nitzSignal
- + " adjustedCurrentTimeMillis=" + adjustedCurrentTimeMillis
- + " millisSinceNitzReceived= " + millisSinceNitzReceived
- + " gained=" + gained;
-
- if (mSavedNitzTime == null) {
- logMsg += ": First update received.";
- setAndBroadcastNetworkSetTime(logMsg, adjustedCurrentTimeMillis);
- } else {
- long elapsedRealtimeSinceLastSaved = mTimeServiceHelper.elapsedRealtime()
- - mSavedNitzTime.mElapsedRealtime;
- int nitzUpdateSpacing = mDeviceState.getNitzUpdateSpacingMillis();
- int nitzUpdateDiff = mDeviceState.getNitzUpdateDiffMillis();
- if (elapsedRealtimeSinceLastSaved > nitzUpdateSpacing
- || Math.abs(gained) > nitzUpdateDiff) {
- // Either it has been a while since we received an update, or the gain
- // is sufficiently large that we want to act on it.
- logMsg += ": New update received.";
- setAndBroadcastNetworkSetTime(logMsg, adjustedCurrentTimeMillis);
- } else {
- if (DBG) {
- Rlog.d(LOG_TAG, logMsg + ": Update throttled.");
- }
-
- // Return early. This means that we don't reset the
- // mSavedNitzTime for next time and that we may act on more
- // NITZ time signals overall but should end up with a system clock that
- // tracks NITZ more closely than if we saved throttled values (which
- // would reset mSavedNitzTime.elapsedRealtime used to calculate time
- // since the last NITZ signal was received).
- return;
- }
- }
- }
-
- // Save the last NITZ time signal used so we can return to it later
- // if auto-time detection is toggled.
- mSavedNitzTime = new TimeStampedValue<>(
- adjustedCurrentTimeMillis, nitzSignal.mElapsedRealtime);
- } finally {
- mWakeLock.release();
- }
- } catch (RuntimeException ex) {
- Rlog.e(LOG_TAG, "handleTimeFromNitz: Processing NITZ data"
- + " nitzSignal=" + nitzSignal
- + " ex=" + ex);
- }
- }
-
- private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
- if (DBG) {
- Rlog.d(LOG_TAG, "setAndBroadcastNetworkSetTimeZone: zoneId=" + zoneId);
- }
- mTimeServiceHelper.setDeviceTimeZone(zoneId);
- if (DBG) {
- Rlog.d(LOG_TAG,
- "setAndBroadcastNetworkSetTimeZone: called setDeviceTimeZone()"
- + " zoneId=" + zoneId);
- }
- }
-
- private void setAndBroadcastNetworkSetTime(String msg, long time) {
- if (!mWakeLock.isHeld()) {
- Rlog.w(LOG_TAG, "setAndBroadcastNetworkSetTime: Wake lock not held while setting device"
- + " time (msg=" + msg + ")");
- }
-
- msg = "setAndBroadcastNetworkSetTime: [Setting time to time=" + time + "]:" + msg;
- if (DBG) {
- Rlog.d(LOG_TAG, msg);
- }
- mTimeLog.log(msg);
- mTimeServiceHelper.setDeviceTime(time);
- TelephonyMetrics.getInstance().writeNITZEvent(mPhone.getPhoneId(), time);
- }
-
- private void handleAutoTimeEnabled() {
- if (DBG) {
- Rlog.d(LOG_TAG, "handleAutoTimeEnabled: Reverting to NITZ Time:"
- + " mSavedNitzTime=" + mSavedNitzTime);
- }
- if (mSavedNitzTime != null) {
- try {
- // Acquire the wakelock as we're reading the elapsed realtime clock here.
- mWakeLock.acquire();
-
- long elapsedRealtime = mTimeServiceHelper.elapsedRealtime();
- String msg = "mSavedNitzTime: Reverting to NITZ time"
- + " elapsedRealtime=" + elapsedRealtime
- + " mSavedNitzTime=" + mSavedNitzTime;
- long adjustedCurrentTimeMillis =
- mSavedNitzTime.mValue + (elapsedRealtime - mSavedNitzTime.mElapsedRealtime);
- setAndBroadcastNetworkSetTime(msg, adjustedCurrentTimeMillis);
- } finally {
- mWakeLock.release();
- }
- }
- }
-
- private void handleAutoTimeZoneEnabled() {
- String tmpLog = "handleAutoTimeZoneEnabled: Reverting to NITZ TimeZone:"
- + " mSavedTimeZoneId=" + mSavedTimeZoneId;
- if (DBG) {
- Rlog.d(LOG_TAG, tmpLog);
- }
- mTimeZoneLog.log(tmpLog);
- if (mSavedTimeZoneId != null) {
- setAndBroadcastNetworkSetTimeZone(mSavedTimeZoneId);
- } else {
- String iso = mDeviceState.getNetworkCountryIsoForPhone();
- if (!TextUtils.isEmpty(iso)) {
- updateTimeZoneByNetworkCountryCode(iso);
- }
- }
- }
-
- /**
- * Dumps the current in-memory state to the supplied PrintWriter.
- */
- public void dumpState(PrintWriter pw) {
- // Time Detection State
- pw.println(" mSavedTime=" + mSavedNitzTime);
-
- // Time Zone Detection State
- pw.println(" mNeedCountryCodeForNitz=" + mNeedCountryCodeForNitz);
- pw.println(" mLatestNitzSignal=" + mLatestNitzSignal);
- pw.println(" mGotCountryCode=" + mGotCountryCode);
- pw.println(" mSavedTimeZoneId=" + mSavedTimeZoneId);
- pw.println(" mNitzTimeZoneDetectionSuccessful=" + mNitzTimeZoneDetectionSuccessful);
-
- // Miscellaneous
- pw.println(" mWakeLock=" + mWakeLock);
- pw.flush();
- }
-
- /**
- * Dumps the time / time zone logs to the supplied IndentingPrintWriter.
- */
- public void dumpLogs(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
- ipw.println(" Time Logs:");
- ipw.increaseIndent();
- mTimeLog.dump(fd, ipw, args);
- ipw.decreaseIndent();
-
- ipw.println(" Time zone Logs:");
- ipw.increaseIndent();
- mTimeZoneLog.dump(fd, ipw, args);
- ipw.decreaseIndent();
- }
-
- /**
- * Update time zone by network country code, works well on countries which only have one time
- * zone or multiple zones with the same offset.
- *
- * @param iso Country code from network MCC
- */
- private void updateTimeZoneByNetworkCountryCode(String iso) {
- CountryResult lookupResult = mTimeZoneLookupHelper.lookupByCountry(
- iso, mTimeServiceHelper.currentTimeMillis());
- if (lookupResult != null && lookupResult.allZonesHaveSameOffset) {
- String logMsg = "updateTimeZoneByNetworkCountryCode: set time"
- + " lookupResult=" + lookupResult
- + " iso=" + iso;
- if (DBG) {
- Rlog.d(LOG_TAG, logMsg);
- }
- mTimeZoneLog.log(logMsg);
- setAndBroadcastNetworkSetTimeZone(lookupResult.zoneId);
- } else {
- if (DBG) {
- Rlog.d(LOG_TAG, "updateTimeZoneByNetworkCountryCode: no good zone for"
- + " iso=" + iso
- + " lookupResult=" + lookupResult);
- }
- }
- }
-
- /**
- * Get the mNitzTimeZoneDetectionSuccessful flag value.
- */
- public boolean getNitzTimeZoneDetectionSuccessful() {
- return mNitzTimeZoneDetectionSuccessful;
- }
-
- /**
- * Returns the last NITZ data that was cached.
- */
- public NitzData getCachedNitzData() {
- return mLatestNitzSignal != null ? mLatestNitzSignal.mValue : null;
- }
-
- /**
- * Returns the time zone ID from the most recent time that a time zone could be determined by
- * this state machine.
- */
- public String getSavedTimeZoneId() {
- return mSavedTimeZoneId;
- }
-
}
diff --git a/src/java/com/android/internal/telephony/OemHookIndication.java b/src/java/com/android/internal/telephony/OemHookIndication.java
new file mode 100644
index 0000000..122a70e
--- /dev/null
+++ b/src/java/com/android/internal/telephony/OemHookIndication.java
@@ -0,0 +1,53 @@
+/**
+ * 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.hardware.radio.deprecated.V1_0.IOemHookIndication;
+import android.os.AsyncResult;
+
+import java.util.ArrayList;
+
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_OEM_HOOK_RAW;
+
+/**
+ * Class containing oem hook indication callbacks
+ */
+public class OemHookIndication extends IOemHookIndication.Stub {
+ RIL mRil;
+
+ public OemHookIndication(RIL ril) {
+ mRil = ril;
+ }
+
+ /**
+ * @param indicationType RadioIndicationType
+ * @param data Data sent by oem
+ */
+ public void oemHookRaw(int indicationType, ArrayList<Byte> data) {
+ mRil.processIndication(indicationType);
+
+ byte[] response = RIL.arrayListToPrimitiveArray(data);
+ if (RIL.RILJ_LOGD) {
+ mRil.unsljLogvRet(RIL_UNSOL_OEM_HOOK_RAW,
+ com.android.internal.telephony.uicc.IccUtils.bytesToHexString(response));
+ }
+
+ if (mRil.mUnsolOemHookRawRegistrant != null) {
+ mRil.mUnsolOemHookRawRegistrant.notifyRegistrant(new AsyncResult(null, response, null));
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/OemHookResponse.java b/src/java/com/android/internal/telephony/OemHookResponse.java
new file mode 100644
index 0000000..0afeac8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/OemHookResponse.java
@@ -0,0 +1,59 @@
+/**
+ * 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.hardware.radio.deprecated.V1_0.IOemHookResponse;
+import android.hardware.radio.V1_0.RadioError;
+import android.hardware.radio.V1_0.RadioResponseInfo;
+
+import java.util.ArrayList;
+
+/**
+ * Class containing oem hook response callbacks
+ */
+public class OemHookResponse extends IOemHookResponse.Stub {
+ RIL mRil;
+
+ public OemHookResponse(RIL ril) {
+ mRil = ril;
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ * @param data Data returned by oem
+ */
+ public void sendRequestRawResponse(RadioResponseInfo responseInfo, ArrayList<Byte> data) {
+ RILRequest rr = mRil.processResponse(responseInfo);
+
+ if (rr != null) {
+ byte[] ret = null;
+ if (responseInfo.error == RadioError.NONE) {
+ ret = RIL.arrayListToPrimitiveArray(data);
+ RadioResponse.sendMessageResponse(rr.mResult, ret);
+ }
+ mRil.processResponseDone(rr, responseInfo, ret);
+ }
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ * @param data Data returned by oem
+ */
+ public void sendRequestStringsResponse(RadioResponseInfo responseInfo, ArrayList<String> data) {
+ RadioResponse.responseStringArrayList(mRil, responseInfo, data);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/OldNitzStateMachine.java b/src/java/com/android/internal/telephony/OldNitzStateMachine.java
new file mode 100644
index 0000000..bb43f1e
--- /dev/null
+++ b/src/java/com/android/internal/telephony/OldNitzStateMachine.java
@@ -0,0 +1,534 @@
+/*
+ * Copyright 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.content.Context;
+import android.os.PowerManager;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.TimestampedValue;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
+import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * {@hide}
+ */
+public final class OldNitzStateMachine implements NitzStateMachine {
+
+ private static final String LOG_TAG = ServiceStateTracker.LOG_TAG;
+ private static final boolean DBG = ServiceStateTracker.DBG;
+
+ // Time detection state.
+
+ /**
+ * The last NITZ-sourced time considered. If auto time detection was off at the time this may
+ * not have been used to set the device time, but it can be used if auto time detection is
+ * re-enabled.
+ */
+ private TimestampedValue<Long> mSavedNitzTime;
+
+ // Time Zone detection state.
+
+ /** We always keep the last NITZ signal received in mLatestNitzSignal. */
+ private TimestampedValue<NitzData> mLatestNitzSignal;
+
+ /**
+ * Records whether the device should have a country code available via
+ * {@link DeviceState#getNetworkCountryIsoForPhone()}. Before this an NITZ signal
+ * received is (almost always) not enough to determine time zone. On test networks the country
+ * code should be available but can still be an empty string but this flag indicates that the
+ * information available is unlikely to improve.
+ */
+ private boolean mGotCountryCode = false;
+
+ /**
+ * The last time zone ID that has been determined. It may not have been set as the device time
+ * zone if automatic time zone detection is disabled but may later be used to set the time zone
+ * if the user enables automatic time zone detection.
+ */
+ private String mSavedTimeZoneId;
+
+ /**
+ * Boolean is {@code true} if NITZ has been used to determine a time zone (which may not
+ * ultimately have been used due to user settings). Cleared by {@link #handleNetworkAvailable()}
+ * and {@link #handleNetworkUnavailable()}. The flag can be used when historic NITZ data may no
+ * longer be valid. {@code false} indicates it is reasonable to try to set the time zone using
+ * less reliable algorithms than NITZ-based detection such as by just using network country
+ * code.
+ */
+ private boolean mNitzTimeZoneDetectionSuccessful = false;
+
+ // Miscellaneous dependencies and helpers not related to detection state.
+ private final LocalLog mTimeLog = new LocalLog(15);
+ private final LocalLog mTimeZoneLog = new LocalLog(15);
+ private final GsmCdmaPhone mPhone;
+ private final DeviceState mDeviceState;
+ private final OldTimeServiceHelper mTimeServiceHelper;
+ private final TimeZoneLookupHelper mTimeZoneLookupHelper;
+ /** Wake lock used while setting time of day. */
+ private final PowerManager.WakeLock mWakeLock;
+ private static final String WAKELOCK_TAG = "NitzStateMachine";
+
+ public OldNitzStateMachine(GsmCdmaPhone phone) {
+ this(phone,
+ new OldTimeServiceHelper(phone.getContext()),
+ new DeviceState(phone),
+ new TimeZoneLookupHelper());
+ }
+
+ @VisibleForTesting
+ public OldNitzStateMachine(GsmCdmaPhone phone, OldTimeServiceHelper timeServiceHelper,
+ DeviceState deviceState, TimeZoneLookupHelper timeZoneLookupHelper) {
+ mPhone = phone;
+
+ Context context = phone.getContext();
+ PowerManager powerManager =
+ (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
+
+ mDeviceState = deviceState;
+ mTimeZoneLookupHelper = timeZoneLookupHelper;
+ mTimeServiceHelper = timeServiceHelper;
+ mTimeServiceHelper.setListener(new OldTimeServiceHelper.Listener() {
+ @Override
+ public void onTimeDetectionChange(boolean enabled) {
+ if (enabled) {
+ handleAutoTimeEnabled();
+ }
+ }
+
+ @Override
+ public void onTimeZoneDetectionChange(boolean enabled) {
+ if (enabled) {
+ handleAutoTimeZoneEnabled();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void handleNetworkCountryCodeSet(boolean countryChanged) {
+ boolean hadCountryCode = mGotCountryCode;
+ mGotCountryCode = true;
+
+ String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
+ if (!TextUtils.isEmpty(isoCountryCode) && !mNitzTimeZoneDetectionSuccessful) {
+ updateTimeZoneFromNetworkCountryCode(isoCountryCode);
+ }
+
+ if (mLatestNitzSignal != null && (countryChanged || !hadCountryCode)) {
+ updateTimeZoneFromCountryAndNitz();
+ }
+ }
+
+ private void updateTimeZoneFromCountryAndNitz() {
+ String isoCountryCode = mDeviceState.getNetworkCountryIsoForPhone();
+ TimestampedValue<NitzData> nitzSignal = mLatestNitzSignal;
+
+ // TimeZone.getDefault() returns a default zone (GMT) even when time zone have never
+ // been set which makes it difficult to tell if it's what the user / time zone detection
+ // has chosen. isTimeZoneSettingInitialized() tells us whether the time zone of the
+ // device has ever been explicit set by the user or code.
+ final boolean isTimeZoneSettingInitialized =
+ mTimeServiceHelper.isTimeZoneSettingInitialized();
+
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz:"
+ + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+ + " nitzSignal=" + nitzSignal
+ + " isoCountryCode=" + isoCountryCode);
+ }
+
+ try {
+ NitzData nitzData = nitzSignal.getValue();
+
+ String zoneId;
+ if (nitzData.getEmulatorHostTimeZone() != null) {
+ zoneId = nitzData.getEmulatorHostTimeZone().getID();
+ } else if (!mGotCountryCode) {
+ // We don't have a country code so we won't try to look up the time zone.
+ zoneId = null;
+ } else if (TextUtils.isEmpty(isoCountryCode)) {
+ // We have a country code but it's empty. This is most likely because we're on a
+ // test network that's using a bogus MCC (eg, "001"). Obtain a TimeZone based only
+ // on the NITZ parameters: it's only going to be correct in a few cases but it
+ // should at least have the correct offset.
+ OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitz(nitzData);
+ String logMsg = "updateTimeZoneFromCountryAndNitz: lookupByNitz returned"
+ + " lookupResult=" + lookupResult;
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg);
+ }
+ // We log this in the time zone log because it has been a source of bugs.
+ mTimeZoneLog.log(logMsg);
+
+ zoneId = lookupResult != null ? lookupResult.zoneId : null;
+ } else if (mLatestNitzSignal == null) {
+ if (DBG) {
+ Rlog.d(LOG_TAG,
+ "updateTimeZoneFromCountryAndNitz: No cached NITZ data available,"
+ + " not setting zone");
+ }
+ zoneId = null;
+ } else if (isNitzSignalOffsetInfoBogus(nitzSignal, isoCountryCode)) {
+ String logMsg = "updateTimeZoneFromCountryAndNitz: Received NITZ looks bogus, "
+ + " isoCountryCode=" + isoCountryCode
+ + " nitzSignal=" + nitzSignal;
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg);
+ }
+ // We log this in the time zone log because it has been a source of bugs.
+ mTimeZoneLog.log(logMsg);
+
+ zoneId = null;
+ } else {
+ OffsetResult lookupResult = mTimeZoneLookupHelper.lookupByNitzCountry(
+ nitzData, isoCountryCode);
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: using"
+ + " lookupByNitzCountry(nitzData, isoCountryCode),"
+ + " nitzData=" + nitzData
+ + " isoCountryCode=" + isoCountryCode
+ + " lookupResult=" + lookupResult);
+ }
+ zoneId = lookupResult != null ? lookupResult.zoneId : null;
+ }
+
+ // Log the action taken to the dedicated time zone log.
+ final String tmpLog = "updateTimeZoneFromCountryAndNitz:"
+ + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+ + " isoCountryCode=" + isoCountryCode
+ + " nitzSignal=" + nitzSignal
+ + " zoneId=" + zoneId
+ + " isTimeZoneDetectionEnabled()="
+ + mTimeServiceHelper.isTimeZoneDetectionEnabled();
+ mTimeZoneLog.log(tmpLog);
+
+ // Set state as needed.
+ if (zoneId != null) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: zoneId=" + zoneId);
+ }
+ if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
+ setAndBroadcastNetworkSetTimeZone(zoneId);
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromCountryAndNitz: skip changing zone"
+ + " as isTimeZoneDetectionEnabled() is false");
+ }
+ }
+ mSavedTimeZoneId = zoneId;
+ mNitzTimeZoneDetectionSuccessful = true;
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG,
+ "updateTimeZoneFromCountryAndNitz: zoneId == null, do nothing");
+ }
+ }
+ } catch (RuntimeException ex) {
+ Rlog.e(LOG_TAG, "updateTimeZoneFromCountryAndNitz: Processing NITZ data"
+ + " nitzSignal=" + nitzSignal
+ + " isoCountryCode=" + isoCountryCode
+ + " isTimeZoneSettingInitialized=" + isTimeZoneSettingInitialized
+ + " ex=" + ex);
+ }
+ }
+
+ /**
+ * Returns true if the NITZ signal is definitely bogus, assuming that the country is correct.
+ */
+ private boolean isNitzSignalOffsetInfoBogus(
+ TimestampedValue<NitzData> nitzSignal, String isoCountryCode) {
+
+ if (TextUtils.isEmpty(isoCountryCode)) {
+ // We cannot say for sure.
+ return false;
+ }
+
+ NitzData newNitzData = nitzSignal.getValue();
+ boolean zeroOffsetNitz = newNitzData.getLocalOffsetMillis() == 0 && !newNitzData.isDst();
+ return zeroOffsetNitz && !countryUsesUtc(isoCountryCode, nitzSignal);
+ }
+
+ private boolean countryUsesUtc(
+ String isoCountryCode, TimestampedValue<NitzData> nitzSignal) {
+ return mTimeZoneLookupHelper.countryUsesUtc(
+ isoCountryCode,
+ nitzSignal.getValue().getCurrentTimeInMillis());
+ }
+
+ @Override
+ public void handleNetworkAvailable() {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleNetworkAvailable: mNitzTimeZoneDetectionSuccessful="
+ + mNitzTimeZoneDetectionSuccessful
+ + ", Setting mNitzTimeZoneDetectionSuccessful=false");
+ }
+ mNitzTimeZoneDetectionSuccessful = false;
+ }
+
+ @Override
+ public void handleNetworkUnavailable() {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleNetworkUnavailable");
+ }
+
+ mGotCountryCode = false;
+ mNitzTimeZoneDetectionSuccessful = false;
+ }
+
+ @Override
+ public void handleNitzReceived(TimestampedValue<NitzData> nitzSignal) {
+ // Always store the latest NITZ signal received.
+ mLatestNitzSignal = nitzSignal;
+
+ updateTimeZoneFromCountryAndNitz();
+ updateTimeFromNitz();
+ }
+
+ private void updateTimeFromNitz() {
+ TimestampedValue<NitzData> nitzSignal = mLatestNitzSignal;
+ try {
+ boolean ignoreNitz = mDeviceState.getIgnoreNitz();
+ if (ignoreNitz) {
+ if (DBG) {
+ Rlog.d(LOG_TAG,
+ "updateTimeFromNitz: Not setting clock because gsm.ignore-nitz is set");
+ }
+ return;
+ }
+
+ try {
+ // Acquire the wake lock as we are reading the elapsed realtime clock and system
+ // clock.
+ mWakeLock.acquire();
+
+ // Validate the nitzTimeSignal to reject obviously bogus elapsedRealtime values.
+ long elapsedRealtime = mTimeServiceHelper.elapsedRealtime();
+ long millisSinceNitzReceived =
+ elapsedRealtime - nitzSignal.getReferenceTimeMillis();
+ if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeFromNitz: not setting time, unexpected"
+ + " elapsedRealtime=" + elapsedRealtime
+ + " nitzSignal=" + nitzSignal);
+ }
+ return;
+ }
+
+ // Adjust the NITZ time by the delay since it was received to get the time now.
+ long adjustedCurrentTimeMillis =
+ nitzSignal.getValue().getCurrentTimeInMillis() + millisSinceNitzReceived;
+ long gained = adjustedCurrentTimeMillis - mTimeServiceHelper.currentTimeMillis();
+
+ if (mTimeServiceHelper.isTimeDetectionEnabled()) {
+ String logMsg = "updateTimeFromNitz:"
+ + " nitzSignal=" + nitzSignal
+ + " adjustedCurrentTimeMillis=" + adjustedCurrentTimeMillis
+ + " millisSinceNitzReceived= " + millisSinceNitzReceived
+ + " gained=" + gained;
+
+ if (mSavedNitzTime == null) {
+ logMsg += ": First update received.";
+ setAndBroadcastNetworkSetTime(logMsg, adjustedCurrentTimeMillis);
+ } else {
+ long elapsedRealtimeSinceLastSaved = mTimeServiceHelper.elapsedRealtime()
+ - mSavedNitzTime.getReferenceTimeMillis();
+ int nitzUpdateSpacing = mDeviceState.getNitzUpdateSpacingMillis();
+ int nitzUpdateDiff = mDeviceState.getNitzUpdateDiffMillis();
+ if (elapsedRealtimeSinceLastSaved > nitzUpdateSpacing
+ || Math.abs(gained) > nitzUpdateDiff) {
+ // Either it has been a while since we received an update, or the gain
+ // is sufficiently large that we want to act on it.
+ logMsg += ": New update received.";
+ setAndBroadcastNetworkSetTime(logMsg, adjustedCurrentTimeMillis);
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg + ": Update throttled.");
+ }
+
+ // Return early. This means that we don't reset the
+ // mSavedNitzTime for next time and that we may act on more
+ // NITZ time signals overall but should end up with a system clock that
+ // tracks NITZ more closely than if we saved throttled values (which
+ // would reset mSavedNitzTime.elapsedRealtime used to calculate time
+ // since the last NITZ signal was received).
+ return;
+ }
+ }
+ }
+
+ // Save the last NITZ time signal used so we can return to it later
+ // if auto-time detection is toggled.
+ mSavedNitzTime = new TimestampedValue<>(
+ nitzSignal.getReferenceTimeMillis(),
+ nitzSignal.getValue().getCurrentTimeInMillis());
+ } finally {
+ mWakeLock.release();
+ }
+ } catch (RuntimeException ex) {
+ Rlog.e(LOG_TAG, "updateTimeFromNitz: Processing NITZ data"
+ + " nitzSignal=" + nitzSignal
+ + " ex=" + ex);
+ }
+ }
+
+ private void setAndBroadcastNetworkSetTimeZone(String zoneId) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "setAndBroadcastNetworkSetTimeZone: zoneId=" + zoneId);
+ }
+ mTimeServiceHelper.setDeviceTimeZone(zoneId);
+ if (DBG) {
+ Rlog.d(LOG_TAG,
+ "setAndBroadcastNetworkSetTimeZone: called setDeviceTimeZone()"
+ + " zoneId=" + zoneId);
+ }
+ }
+
+ private void setAndBroadcastNetworkSetTime(String msg, long time) {
+ if (!mWakeLock.isHeld()) {
+ Rlog.w(LOG_TAG, "setAndBroadcastNetworkSetTime: Wake lock not held while setting device"
+ + " time (msg=" + msg + ")");
+ }
+
+ msg = "setAndBroadcastNetworkSetTime: [Setting time to time=" + time + "]:" + msg;
+ if (DBG) {
+ Rlog.d(LOG_TAG, msg);
+ }
+ mTimeLog.log(msg);
+ mTimeServiceHelper.setDeviceTime(time);
+ TelephonyMetrics.getInstance().writeNITZEvent(mPhone.getPhoneId(), time);
+ }
+
+ private void handleAutoTimeEnabled() {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleAutoTimeEnabled: Reverting to NITZ Time:"
+ + " mSavedNitzTime=" + mSavedNitzTime);
+ }
+ if (mSavedNitzTime != null) {
+ try {
+ // Acquire the wakelock as we're reading the elapsed realtime clock here.
+ mWakeLock.acquire();
+
+ long elapsedRealtime = mTimeServiceHelper.elapsedRealtime();
+ String msg = "mSavedNitzTime: Reverting to NITZ time"
+ + " elapsedRealtime=" + elapsedRealtime
+ + " mSavedNitzTime=" + mSavedNitzTime;
+ long adjustedCurrentTimeMillis = mSavedNitzTime.getValue()
+ + (elapsedRealtime - mSavedNitzTime.getReferenceTimeMillis());
+ setAndBroadcastNetworkSetTime(msg, adjustedCurrentTimeMillis);
+ } finally {
+ mWakeLock.release();
+ }
+ }
+ }
+
+ private void handleAutoTimeZoneEnabled() {
+ String tmpLog = "handleAutoTimeZoneEnabled: Reverting to NITZ TimeZone:"
+ + " mSavedTimeZoneId=" + mSavedTimeZoneId;
+ if (DBG) {
+ Rlog.d(LOG_TAG, tmpLog);
+ }
+ mTimeZoneLog.log(tmpLog);
+ if (mSavedTimeZoneId != null) {
+ setAndBroadcastNetworkSetTimeZone(mSavedTimeZoneId);
+ }
+ }
+
+ @Override
+ public void dumpState(PrintWriter pw) {
+ // Time Detection State
+ pw.println(" mSavedTime=" + mSavedNitzTime);
+
+ // Time Zone Detection State
+ pw.println(" mLatestNitzSignal=" + mLatestNitzSignal);
+ pw.println(" mGotCountryCode=" + mGotCountryCode);
+ pw.println(" mSavedTimeZoneId=" + mSavedTimeZoneId);
+ pw.println(" mNitzTimeZoneDetectionSuccessful=" + mNitzTimeZoneDetectionSuccessful);
+
+ // Miscellaneous
+ pw.println(" mWakeLock=" + mWakeLock);
+ pw.flush();
+ }
+
+ @Override
+ public void dumpLogs(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+ ipw.println(" Time Logs:");
+ ipw.increaseIndent();
+ mTimeLog.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+
+ ipw.println(" Time zone Logs:");
+ ipw.increaseIndent();
+ mTimeZoneLog.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+ }
+
+ /**
+ * Update time zone by network country code, works well on countries which only have one time
+ * zone or multiple zones with the same offset.
+ *
+ * @param iso Country code from network MCC
+ */
+ private void updateTimeZoneFromNetworkCountryCode(String iso) {
+ CountryResult lookupResult = mTimeZoneLookupHelper.lookupByCountry(
+ iso, mTimeServiceHelper.currentTimeMillis());
+ if (lookupResult != null && lookupResult.allZonesHaveSameOffset) {
+ String logMsg = "updateTimeZoneFromNetworkCountryCode: tz result found"
+ + " iso=" + iso
+ + " lookupResult=" + lookupResult;
+ if (DBG) {
+ Rlog.d(LOG_TAG, logMsg);
+ }
+ mTimeZoneLog.log(logMsg);
+ String zoneId = lookupResult.zoneId;
+ if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
+ setAndBroadcastNetworkSetTimeZone(zoneId);
+ }
+ mSavedTimeZoneId = zoneId;
+ } else {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "updateTimeZoneFromNetworkCountryCode: no good zone for"
+ + " iso=" + iso
+ + " lookupResult=" + lookupResult);
+ }
+ }
+ }
+
+ public boolean getNitzTimeZoneDetectionSuccessful() {
+ return mNitzTimeZoneDetectionSuccessful;
+ }
+
+ @Override
+ public NitzData getCachedNitzData() {
+ return mLatestNitzSignal != null ? mLatestNitzSignal.getValue() : null;
+ }
+
+ @Override
+ public String getSavedTimeZoneId() {
+ return mSavedTimeZoneId;
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/TimeServiceHelper.java b/src/java/com/android/internal/telephony/OldTimeServiceHelper.java
similarity index 98%
rename from src/java/com/android/internal/telephony/TimeServiceHelper.java
rename to src/java/com/android/internal/telephony/OldTimeServiceHelper.java
index 94b094f..9c7a763 100644
--- a/src/java/com/android/internal/telephony/TimeServiceHelper.java
+++ b/src/java/com/android/internal/telephony/OldTimeServiceHelper.java
@@ -32,7 +32,7 @@
* new service.
*/
// Non-final to allow mocking.
-public class TimeServiceHelper {
+public class OldTimeServiceHelper {
/**
* Callback interface for automatic detection enable/disable changes.
@@ -57,7 +57,7 @@
private Listener mListener;
/** Creates a TimeServiceHelper */
- public TimeServiceHelper(Context context) {
+ public OldTimeServiceHelper(Context context) {
mContext = context;
mCr = context.getContentResolver();
}
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 3da6326..899f1fc 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -186,6 +186,7 @@
private static final int EVENT_SRVCC_STATE_CHANGED = 31;
private static final int EVENT_INITIATE_SILENT_REDIAL = 32;
private static final int EVENT_RADIO_NOT_AVAILABLE = 33;
+ private static final int EVENT_UNSOL_OEM_HOOK_RAW = 34;
protected static final int EVENT_GET_RADIO_CAPABILITY = 35;
protected static final int EVENT_SS = 36;
private static final int EVENT_CONFIG_LCE = 37;
@@ -549,6 +550,7 @@
if (getPhoneType() != PhoneConstants.PHONE_TYPE_SIP) {
mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
}
+ mCi.setOnUnsolOemHookRaw(this, EVENT_UNSOL_OEM_HOOK_RAW, null);
mCi.startLceService(DEFAULT_REPORT_INTERVAL_MS, LCE_PULL_MODE,
obtainMessage(EVENT_CONFIG_LCE));
}
@@ -683,6 +685,16 @@
}
break;
+ case EVENT_UNSOL_OEM_HOOK_RAW:
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception == null) {
+ byte[] data = (byte[])ar.result;
+ mNotifier.notifyOemHookRawEventForSubscriber(getSubId(), data);
+ } else {
+ Rlog.e(LOG_TAG, "OEM hook raw exception: " + ar.exception);
+ }
+ break;
+
case EVENT_CONFIG_LCE:
ar = (AsyncResult) msg.obj;
if (ar.exception != null) {
@@ -1408,8 +1420,6 @@
*/
public void registerForServiceStateChanged(
Handler h, int what, Object obj) {
- checkCorrectThread(h);
-
mServiceStateRegistrants.add(h, what, obj);
}
@@ -2046,6 +2056,45 @@
}
/**
+ * Invokes RIL_REQUEST_OEM_HOOK_RAW on RIL implementation.
+ *
+ * @param data The data for the request.
+ * @param response <strong>On success</strong>,
+ * (byte[])(((AsyncResult)response.obj).result)
+ * <strong>On failure</strong>,
+ * (((AsyncResult)response.obj).result) == null and
+ * (((AsyncResult)response.obj).exception) being an instance of
+ * com.android.internal.telephony.gsm.CommandException
+ *
+ * @see #invokeOemRilRequestRaw(byte[], android.os.Message)
+ * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
+ */
+ @Deprecated
+ public void invokeOemRilRequestRaw(byte[] data, Message response) {
+ mCi.invokeOemRilRequestRaw(data, response);
+ }
+
+ /**
+ * Invokes RIL_REQUEST_OEM_HOOK_Strings on RIL implementation.
+ *
+ * @param strings The strings to make available as the request data.
+ * @param response <strong>On success</strong>, "response" bytes is
+ * made available as:
+ * (String[])(((AsyncResult)response.obj).result).
+ * <strong>On failure</strong>,
+ * (((AsyncResult)response.obj).result) == null and
+ * (((AsyncResult)response.obj).exception) being an instance of
+ * com.android.internal.telephony.gsm.CommandException
+ *
+ * @see #invokeOemRilRequestStrings(java.lang.String[], android.os.Message)
+ * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
+ */
+ @Deprecated
+ public void invokeOemRilRequestStrings(String[] strings, Message response) {
+ mCi.invokeOemRilRequestStrings(strings, response);
+ }
+
+ /**
* Read one of the NV items defined in {@link RadioNVItems} / {@code ril_nv_items.h}.
* Used for device configuration by some CDMA operators.
*
@@ -3017,6 +3066,19 @@
return null;
}
+ public int getCarrierIdListVersion() {
+ return TelephonyManager.UNKNOWN_CARRIER_ID_LIST_VERSION;
+ }
+
+ /**
+ * Resets the Carrier Keys in the database. This involves 2 steps:
+ * 1. Delete the keys from the database.
+ * 2. Send an intent to download new Certificates.
+ */
+ public void resetCarrierKeysForImsiEncryption() {
+ return;
+ }
+
/**
* Return if UT capability of ImsPhone is enabled or not
*/
@@ -3070,6 +3132,13 @@
* Returns the subscription id.
*/
public int getSubId() {
+ if (SubscriptionController.getInstance() == null) {
+ // TODO b/78359408 getInstance sometimes returns null in Treehugger tests, which causes
+ // flakiness. Even though we haven't seen this crash in the wild we should keep this
+ // check in until we've figured out the root cause.
+ Rlog.e(LOG_TAG, "SubscriptionController.getInstance = null! Returning default subId");
+ return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+ }
return SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhoneId);
}
@@ -3422,6 +3491,16 @@
mCi.setAllowedCarriers(carriers, response);
}
+ /** Sets the SignalStrength reporting criteria. */
+ public void setSignalStrengthReportingCriteria(int[] thresholds, int ran) {
+ // no-op default implementation
+ }
+
+ /** Sets the SignalStrength reporting criteria. */
+ public void setLinkCapacityReportingCriteria(int[] dlThresholds, int[] ulThresholds, int ran) {
+ // no-op default implementation
+ }
+
/**
* Get allowed carriers
*/
@@ -3578,6 +3657,10 @@
}
}
+ public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
+ String gid2, String pnn, String spn) {
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Phone: subId=" + getSubId());
pw.println(" mPhoneId=" + mPhoneId);
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 3653ea0..ba77747 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -155,7 +155,7 @@
Rlog.i(LOG_TAG, "ImsResolver: defaultImsPackage: " + defaultImsPackage);
sImsResolver = new ImsResolver(sContext, defaultImsPackage, numPhones,
isDynamicBinding);
- sImsResolver.populateCacheAndStartBind();
+ sImsResolver.initPopulateCacheAndStartBind();
int[] networkModes = new int[numPhones];
sPhones = new Phone[numPhones];
@@ -307,6 +307,10 @@
}
}
+ public static SubscriptionInfoUpdater getSubscriptionInfoUpdater() {
+ return sSubInfoRecordUpdater;
+ }
+
public static ImsResolver getImsResolver() {
return sImsResolver;
}
@@ -454,7 +458,10 @@
pw.println("++++++++++++++++++++++++++++++++");
try {
- ((UiccProfile) phone.getIccCard()).dump(fd, pw, args);
+ UiccProfile uiccProfile = (UiccProfile) phone.getIccCard();
+ if (uiccProfile != null) {
+ uiccProfile.dump(fd, pw, args);
+ }
} catch (Exception e) {
e.printStackTrace();
}
diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
index ae8fdad..91b7b28 100644
--- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java
+++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -894,4 +894,9 @@
* decrypt the permanent identity.
*/
public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType);
+
+ /**
+ * Resets the Carrier Keys, by deleting them from the database and sending a download intent.
+ */
+ public void resetCarrierKeysForImsiEncryption();
}
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index 476394f..5c8faa6 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -68,4 +68,6 @@
public void notifyDataActivationStateChanged(Phone sender, int activationState);
public void notifyUserMobileDataStateChanged(Phone sender, boolean state);
+
+ public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData);
}
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index ea17410..23eb0b8 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -19,6 +19,7 @@
package com.android.internal.telephony;
import static android.Manifest.permission.CALL_PRIVILEGED;
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
import android.app.AppOpsManager;
@@ -59,15 +60,15 @@
}
public String getDeviceIdForPhone(int phoneId, String callingPackage) {
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getDeviceId")) {
- return null;
- }
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
phoneId = 0;
}
final Phone phone = mPhone[phoneId];
if (phone != null) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, phone.getSubId(), callingPackage, "getDeviceId")) {
+ return null;
+ }
return phone.getDeviceId();
} else {
loge("getDeviceIdForPhone phone " + phoneId + " is null");
@@ -79,7 +80,7 @@
Phone phone = getPhone(subId);
if (phone != null) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getNai")) {
+ mContext, subId, callingPackage, "getNai")) {
return null;
}
return phone.getNai();
@@ -93,7 +94,7 @@
Phone phone = getPhone(subId);
if (phone != null) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getImei")) {
+ mContext, subId, callingPackage, "getImei")) {
return null;
}
return phone.getImei();
@@ -104,11 +105,12 @@
}
public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int subId, int keyType,
- String callingPackage) {
+ String callingPackage) {
Phone phone = getPhone(subId);
if (phone != null) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getCarrierInfoForImsiEncryption")) {
+ mContext, subId, callingPackage,
+ "getCarrierInfoForImsiEncryption")) {
return null;
}
return phone.getCarrierInfoForImsiEncryption(keyType);
@@ -123,9 +125,10 @@
Phone phone = getPhone(subId);
if (phone != null) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "setCarrierInfoForImsiEncryption")) {
+ mContext, subId, callingPackage, "setCarrierInfoForImsiEncryption")) {
return;
}
+ enforceModifyPermission();
phone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo);
} else {
loge("setCarrierInfoForImsiEncryption phone is null for Subscription:" + subId);
@@ -133,6 +136,25 @@
}
}
+ /**
+ * Resets the Carrier Keys in the database. This involves 2 steps:
+ * 1. Delete the keys from the database.
+ * 2. Send an intent to download new Certificates.
+ * @param subId
+ * @param callingPackage
+ */
+ public void resetCarrierKeysForImsiEncryption(int subId, String callingPackage) {
+ Phone phone = getPhone(subId);
+ if (phone != null) {
+ enforceModifyPermission();
+ phone.resetCarrierKeysForImsiEncryption();
+ return;
+ } else {
+ loge("resetCarrierKeysForImsiEncryption phone is null for Subscription:" + subId);
+ return;
+ }
+ }
+
public String getDeviceSvn(String callingPackage) {
return getDeviceSvnUsingSubId(getDefaultSubscription(), callingPackage);
@@ -142,7 +164,7 @@
Phone phone = getPhone(subId);
if (phone != null) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getDeviceSvn")) {
+ mContext, subId, callingPackage, "getDeviceSvn")) {
return null;
}
return phone.getDeviceSvn();
@@ -160,7 +182,7 @@
Phone phone = getPhone(subId);
if (phone != null) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getSubscriberId")) {
+ mContext, subId, callingPackage, "getSubscriberId")) {
return null;
}
return phone.getSubscriberId();
@@ -181,7 +203,7 @@
Phone phone = getPhone(subId);
if (phone != null) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getIccSerialNumber")) {
+ mContext, subId, callingPackage, "getIccSerialNumber")) {
return null;
}
return phone.getIccSerialNumber();
@@ -200,7 +222,7 @@
if (phone != null) {
// This is open to apps with WRITE_SMS.
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneNumber(
- mContext, callingPackage, "getLine1Number")) {
+ mContext, subId, callingPackage, "getLine1Number")) {
return null;
}
return phone.getLine1Number();
@@ -218,7 +240,7 @@
Phone phone = getPhone(subId);
if (phone != null) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getLine1AlphaTag")) {
+ mContext, subId, callingPackage, "getLine1AlphaTag")) {
return null;
}
return phone.getLine1AlphaTag();
@@ -236,7 +258,7 @@
Phone phone = getPhone(subId);
if (phone != null) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getMsisdn")) {
+ mContext, subId, callingPackage, "getMsisdn")) {
return null;
}
return phone.getMsisdn();
@@ -254,7 +276,7 @@
Phone phone = getPhone(subId);
if (phone != null) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getVoiceMailNumber")) {
+ mContext, subId, callingPackage, "getVoiceMailNumber")) {
return null;
}
String number = PhoneNumberUtils.extractNetworkPortion(phone.getVoiceMailNumber());
@@ -292,7 +314,7 @@
Phone phone = getPhone(subId);
if (phone != null) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getVoiceMailAlphaTag")) {
+ mContext, subId, callingPackage, "getVoiceMailAlphaTag")) {
return null;
}
return phone.getVoiceMailAlphaTag();
@@ -328,6 +350,14 @@
TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(subId, message);
}
+ /**
+ * Make sure caller has modify phone state permission.
+ */
+ private void enforceModifyPermission() {
+ mContext.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE,
+ "Requires MODIFY_PHONE_STATE");
+ }
+
private int getDefaultSubscription() {
return PhoneFactory.getDefaultSubscription();
}
@@ -465,15 +495,11 @@
return uiccApp.getIccRecords().getIccSimChallengeResponse(authType, data);
}
- public String getGroupIdLevel1(String callingPackage) {
- return getGroupIdLevel1ForSubscriber(getDefaultSubscription(), callingPackage);
- }
-
public String getGroupIdLevel1ForSubscriber(int subId, String callingPackage) {
Phone phone = getPhone(subId);
if (phone != null) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getGroupIdLevel1")) {
+ mContext, subId, callingPackage, "getGroupIdLevel1")) {
return null;
}
return phone.getGroupIdLevel1();
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 4f4d011..9f7ea05 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -29,6 +29,7 @@
import android.hardware.radio.V1_0.CellInfoCdma;
import android.hardware.radio.V1_0.CellInfoGsm;
import android.hardware.radio.V1_0.CellInfoLte;
+import android.hardware.radio.V1_0.CellInfoTdscdma;
import android.hardware.radio.V1_0.CellInfoType;
import android.hardware.radio.V1_0.CellInfoWcdma;
import android.hardware.radio.V1_0.DataProfileInfo;
@@ -39,6 +40,7 @@
import android.hardware.radio.V1_0.IRadio;
import android.hardware.radio.V1_0.IccIo;
import android.hardware.radio.V1_0.ImsSmsMessage;
+import android.hardware.radio.V1_0.IndicationFilter;
import android.hardware.radio.V1_0.LceDataInfo;
import android.hardware.radio.V1_0.MvnoType;
import android.hardware.radio.V1_0.NvWriteItem;
@@ -51,6 +53,8 @@
import android.hardware.radio.V1_0.SimApdu;
import android.hardware.radio.V1_0.SmsWriteArgs;
import android.hardware.radio.V1_0.UusInfo;
+import android.hardware.radio.V1_2.AccessNetwork;
+import android.hardware.radio.deprecated.V1_0.IOemHook;
import android.net.ConnectivityManager;
import android.net.KeepalivePacketData;
import android.net.LinkProperties;
@@ -184,6 +188,9 @@
RadioResponse mRadioResponse;
RadioIndication mRadioIndication;
volatile IRadio mRadioProxy = null;
+ OemHookResponse mOemHookResponse;
+ OemHookIndication mOemHookIndication;
+ volatile IOemHook mOemHookProxy = null;
final AtomicLong mRadioProxyCookie = new AtomicLong(0);
final RadioProxyDeathRecipient mRadioProxyDeathRecipient;
final RilHandler mRilHandler;
@@ -326,6 +333,7 @@
private void resetProxyAndRequestList() {
mRadioProxy = null;
+ mOemHookProxy = null;
// increment the cookie so that death notification can be ignored
mRadioProxyCookie.incrementAndGet();
@@ -337,6 +345,7 @@
clearRequestList(RADIO_NOT_AVAILABLE, false);
getRadioProxy(null);
+ getOemHookProxy(null);
}
/** Returns a {@link IRadio} instance or null if the service is not available. */
@@ -384,6 +393,49 @@
return mRadioProxy;
}
+ /** Returns an {@link IOemHook} instance or null if the service is not available. */
+ @VisibleForTesting
+ public IOemHook getOemHookProxy(Message result) {
+ if (!mIsMobileNetworkSupported) {
+ if (RILJ_LOGV) riljLog("getOemHookProxy: Not calling getService(): wifi-only");
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
+ result.sendToTarget();
+ }
+ return null;
+ }
+
+ if (mOemHookProxy != null) {
+ return mOemHookProxy;
+ }
+
+ try {
+ mOemHookProxy = IOemHook.getService(
+ HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId], true);
+ if (mOemHookProxy != null) {
+ // not calling linkToDeath() as ril service runs in the same process and death
+ // notification for that should be sufficient
+ mOemHookProxy.setResponseFunctions(mOemHookResponse, mOemHookIndication);
+ } else {
+ riljLoge("getOemHookProxy: mOemHookProxy == null");
+ }
+ } catch (RemoteException | RuntimeException e) {
+ mOemHookProxy = null;
+ riljLoge("OemHookProxy getService/setResponseFunctions: " + e);
+ }
+
+ if (mOemHookProxy == null) {
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
+ result.sendToTarget();
+ }
+ }
+
+ return mOemHookProxy;
+ }
+
//***** Constructors
public RIL(Context context, int preferredNetworkType, int cdmaSubscription) {
@@ -410,6 +462,8 @@
mRadioResponse = new RadioResponse(this);
mRadioIndication = new RadioIndication(this);
+ mOemHookResponse = new OemHookResponse(this);
+ mOemHookIndication = new OemHookIndication(this);
mRilHandler = new RilHandler();
mRadioProxyDeathRecipient = new RadioProxyDeathRecipient();
@@ -432,6 +486,7 @@
// set radio callback; needed to set RadioIndication callback (should be done after
// wakelock stuff is initialized above as callbacks are received on separate binder threads)
getRadioProxy(null);
+ getOemHookProxy(null);
}
@Override
@@ -1134,18 +1189,27 @@
try {
if (radioProxy12 == null) {
// IRadio V1.0
+
+ // Getting data RAT here is just a workaround to support the older 1.0 vendor
+ // RIL. The new data service interface passes access network type instead of
+ // RAT for setup data request. It is impossible to convert access network
+ // type back to RAT here, so we directly get the data RAT from phone.
+ int dataRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
+ Phone phone = PhoneFactory.getPhone(mPhoneId);
+ if (phone != null) {
+ ServiceState ss = phone.getServiceState();
+ if (ss != null) {
+ dataRat = ss.getRilDataRadioTechnology();
+ }
+ }
if (RILJ_LOGD) {
riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
- + ",radioTechnology=unknown,isRoaming=" + isRoaming
+ + ",dataRat=" + dataRat + ",isRoaming=" + isRoaming
+ ",allowRoaming=" + allowRoaming + "," + dataProfile);
}
- // The RAT field in setup data call request was never used before. Starting from
- // P, the new API passes in access network type instead of RAT. Since it's
- // not possible to convert access network type back to RAT, but we still need to
- // support the 1.0 API, we passed in unknown RAT to the modem. And modem must
- // setup the data call on its current camped network.
- radioProxy.setupDataCall(rr.mSerial, ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN,
- dpi, dataProfile.isModemCognitive(), allowRoaming, isRoaming);
+
+ radioProxy.setupDataCall(rr.mSerial, dataRat, dpi,
+ dataProfile.isModemCognitive(), allowRoaming, isRoaming);
} else {
// IRadio V1.2
ArrayList<String> addresses = new ArrayList<>();
@@ -1930,6 +1994,59 @@
}
@Override
+ public void invokeOemRilRequestRaw(byte[] data, Message response) {
+ IOemHook oemHookProxy = getOemHookProxy(response);
+ if (oemHookProxy != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_OEM_HOOK_RAW, response,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + "[" + IccUtils.bytesToHexString(data) + "]");
+ }
+
+ try {
+ oemHookProxy.sendRequestRaw(rr.mSerial, primitiveArrayToArrayList(data));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "invokeOemRilRequestRaw", e);
+ }
+ } else {
+ // OEM Hook service is disabled for P and later devices.
+ // Deprecated OEM Hook APIs will perform dummy before being removed.
+ if (RILJ_LOGD) riljLog("Radio Oem Hook Service is disabled for P and later devices. ");
+ }
+ }
+
+ @Override
+ public void invokeOemRilRequestStrings(String[] strings, Message result) {
+ IOemHook oemHookProxy = getOemHookProxy(result);
+ if (oemHookProxy != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_OEM_HOOK_STRINGS, result,
+ mRILDefaultWorkSource);
+
+ String logStr = "";
+ for (int i = 0; i < strings.length; i++) {
+ logStr = logStr + strings[i] + " ";
+ }
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " strings = "
+ + logStr);
+ }
+
+ try {
+ oemHookProxy.sendRequestStrings(rr.mSerial,
+ new ArrayList<String>(Arrays.asList(strings)));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "invokeOemRilRequestStrings", e);
+ }
+ } else {
+ // OEM Hook service is disabled for P and later devices.
+ // Deprecated OEM Hook APIs will perform dummy before being removed.
+ if (RILJ_LOGD) riljLog("Radio Oem Hook Service is disabled for P and later devices. ");
+ }
+ }
+
+ @Override
public void setSuppServiceNotifications(boolean enable, Message result) {
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
@@ -2982,7 +3099,7 @@
try {
radioProxy.sendImsSms(rr.mSerial, msg);
mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
- SmsSession.Event.Format.SMS_FORMAT_3GPP);
+ SmsSession.Event.Format.SMS_FORMAT_3GPP2);
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "sendImsCdmaSms", e);
}
@@ -3602,15 +3719,105 @@
riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + filter);
}
- try {
- radioProxy.setIndicationFilter(rr.mSerial, filter);
- } catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(rr, "setIndicationFilter", e);
+ android.hardware.radio.V1_2.IRadio radioProxy12 =
+ android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
+
+ if (radioProxy12 != null) {
+ try {
+ radioProxy12.setIndicationFilter_1_2(rr.mSerial, filter);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "setIndicationFilter_1_2", e);
+ }
+ } else {
+ try {
+ int filter10 = filter & IndicationFilter.ALL;
+ radioProxy.setIndicationFilter(rr.mSerial, filter10);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "setIndicationFilter", e);
+ }
}
}
}
@Override
+ public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
+ int[] thresholdsDbm, int ran, Message result) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ android.hardware.radio.V1_2.IRadio radioProxy12 =
+ android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
+ if (radioProxy12 == null) {
+ riljLoge("setSignalStrengthReportingCriteria ignored. RadioProxy 1.2 is null!");
+ return;
+ }
+
+ RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA,
+ result, mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+
+ try {
+ radioProxy12.setSignalStrengthReportingCriteria(rr.mSerial, hysteresisMs,
+ hysteresisDb, primitiveArrayToArrayList(thresholdsDbm),
+ convertRanToHalRan(ran));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "setSignalStrengthReportingCriteria", e);
+ }
+ }
+ }
+
+ @Override
+ public void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
+ int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
+ Message result) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ android.hardware.radio.V1_2.IRadio radioProxy12 =
+ android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
+ if (radioProxy12 == null) {
+ riljLoge("setLinkCapacityReportingCriteria ignored. RadioProxy 1.2 is null!");
+ return;
+ }
+
+ RILRequest rr = obtainRequest(RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA,
+ result, mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+
+ try {
+ radioProxy12.setLinkCapacityReportingCriteria(rr.mSerial, hysteresisMs,
+ hysteresisDlKbps, hysteresisUlKbps,
+ primitiveArrayToArrayList(thresholdsDlKbps),
+ primitiveArrayToArrayList(thresholdsUlKbps), convertRanToHalRan(ran));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "setLinkCapacityReportingCriteria", e);
+ }
+ }
+ }
+
+ private static int convertRanToHalRan(int radioAccessNetwork) {
+ switch (radioAccessNetwork) {
+ case AccessNetworkType.GERAN:
+ return AccessNetwork.GERAN;
+ case AccessNetworkType.UTRAN:
+ return AccessNetwork.UTRAN;
+ case AccessNetworkType.EUTRAN:
+ return AccessNetwork.EUTRAN;
+ case AccessNetworkType.CDMA2000:
+ return AccessNetwork.CDMA2000;
+ case AccessNetworkType.IWLAN:
+ return AccessNetwork.IWLAN;
+ case AccessNetworkType.UNKNOWN:
+ default:
+ return 0;
+ }
+ }
+
+ @Override
public void setSimCardPower(int state, Message result) {
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
@@ -3745,6 +3952,7 @@
appendPrimitiveArrayToArrayList(
packetData.dstAddress.getAddress(), req.destinationAddress);
req.destinationPort = packetData.dstPort;
+ req.maxKeepaliveIntervalMillis = intervalMillis;
radioProxy11.startKeepalive(rr.mSerial, req);
} catch (RemoteException | RuntimeException e) {
@@ -4545,6 +4753,10 @@
return "DATA_CALL_LIST";
case RIL_REQUEST_RESET_RADIO:
return "RESET_RADIO";
+ case RIL_REQUEST_OEM_HOOK_RAW:
+ return "OEM_HOOK_RAW";
+ case RIL_REQUEST_OEM_HOOK_STRINGS:
+ return "OEM_HOOK_STRINGS";
case RIL_REQUEST_SCREEN_STATE:
return "SCREEN_STATE";
case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION:
@@ -4716,6 +4928,10 @@
return "RIL_REQUEST_START_KEEPALIVE";
case RIL_REQUEST_STOP_KEEPALIVE:
return "RIL_REQUEST_STOP_KEEPALIVE";
+ case RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA:
+ return "RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA";
+ case RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA:
+ return "RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA";
default: return "<unknown request>";
}
}
@@ -4778,6 +4994,8 @@
return "UNSOL_CDMA_OTA_PROVISION_STATUS";
case RIL_UNSOL_CDMA_INFO_REC:
return "UNSOL_CDMA_INFO_REC";
+ case RIL_UNSOL_OEM_HOOK_RAW:
+ return "UNSOL_OEM_HOOK_RAW";
case RIL_UNSOL_RINGBACK_TONE:
return "UNSOL_RINGBACK_TONE";
case RIL_UNSOL_RESEND_INCALL_MUTE:
@@ -4822,6 +5040,8 @@
return "RIL_UNSOL_ICC_SLOT_STATUS";
case RIL_UNSOL_KEEPALIVE_STATUS:
return "RIL_UNSOL_KEEPALIVE_STATUS";
+ case RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG:
+ return "RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG";
default:
return "<unknown response>";
}
@@ -4917,6 +5137,15 @@
return arrayList;
}
+ /** Convert a primitive int array to an ArrayList<Integer>. */
+ public static ArrayList<Integer> primitiveArrayToArrayList(int[] arr) {
+ ArrayList<Integer> arrayList = new ArrayList<>(arr.length);
+ for (int i : arr) {
+ arrayList.add(i);
+ }
+ return arrayList;
+ }
+
/** Convert an ArrayList of Bytes to an exactly-sized primitive array */
public static byte[] arrayListToPrimitiveArray(ArrayList<Byte> bytes) {
byte[] ret = new byte[bytes.size()];
@@ -5010,12 +5239,12 @@
p.writeInt(CellIdentity.TYPE_GSM);
p.writeString(mcc);
p.writeString(mnc);
+ p.writeString(al);
+ p.writeString(as);
p.writeInt(lac);
p.writeInt(cid);
p.writeInt(arfcn);
p.writeInt(bsic);
- p.writeString(al);
- p.writeString(as);
p.writeInt(ss);
p.writeInt(ber);
p.writeInt(ta);
@@ -5034,13 +5263,13 @@
p.writeInt(CellIdentity.TYPE_LTE);
p.writeString(mcc);
p.writeString(mnc);
+ p.writeString(al);
+ p.writeString(as);
p.writeInt(ci);
p.writeInt(pci);
p.writeInt(tac);
p.writeInt(earfcn);
p.writeInt(bandwidth);
- p.writeString(al);
- p.writeString(as);
p.writeInt(ss);
p.writeInt(rsrp);
p.writeInt(rsrq);
@@ -5051,18 +5280,37 @@
private static void writeToParcelForWcdma(
Parcel p, int lac, int cid, int psc, int uarfcn, String mcc, String mnc,
- String al, String as, int ss, int ber) {
+ String al, String as, int ss, int ber, int rscp, int ecno) {
p.writeInt(CellIdentity.TYPE_WCDMA);
p.writeString(mcc);
p.writeString(mnc);
+ p.writeString(al);
+ p.writeString(as);
p.writeInt(lac);
p.writeInt(cid);
p.writeInt(psc);
p.writeInt(uarfcn);
- p.writeString(al);
- p.writeString(as);
p.writeInt(ss);
p.writeInt(ber);
+ p.writeInt(rscp);
+ p.writeInt(ecno);
+ }
+
+ private static void writeToParcelForTdscdma(
+ Parcel p, int lac, int cid, int cpid, int uarfcn, String mcc, String mnc,
+ String al, String as, int ss, int ber, int rscp) {
+ p.writeInt(CellIdentity.TYPE_TDSCDMA);
+ p.writeString(mcc);
+ p.writeString(mnc);
+ p.writeString(al);
+ p.writeString(as);
+ p.writeInt(lac);
+ p.writeInt(cid);
+ p.writeInt(cpid);
+ p.writeInt(uarfcn);
+ p.writeInt(ss);
+ p.writeInt(ber);
+ p.writeInt(rscp);
}
/**
@@ -5156,10 +5404,29 @@
EMPTY_ALPHA_LONG,
EMPTY_ALPHA_SHORT,
cellInfoWcdma.signalStrengthWcdma.signalStrength,
- cellInfoWcdma.signalStrengthWcdma.bitErrorRate);
+ cellInfoWcdma.signalStrengthWcdma.bitErrorRate,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE);
break;
}
+ case CellInfoType.TD_SCDMA: {
+ CellInfoTdscdma cellInfoTdscdma = record.tdscdma.get(0);
+ writeToParcelForTdscdma(
+ p,
+ cellInfoTdscdma.cellIdentityTdscdma.lac,
+ cellInfoTdscdma.cellIdentityTdscdma.cid,
+ cellInfoTdscdma.cellIdentityTdscdma.cpid,
+ Integer.MAX_VALUE,
+ cellInfoTdscdma.cellIdentityTdscdma.mcc,
+ cellInfoTdscdma.cellIdentityTdscdma.mnc,
+ EMPTY_ALPHA_LONG,
+ EMPTY_ALPHA_SHORT,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ convertTdscdmaRscpTo1_2(cellInfoTdscdma.signalStrengthTdscdma.rscp));
+ break;
+ }
default:
throw new RuntimeException("unexpected cellinfotype: " + record.cellInfoType);
}
@@ -5264,7 +5531,28 @@
cellInfoWcdma.cellIdentityWcdma.operatorNames.alphaLong,
cellInfoWcdma.cellIdentityWcdma.operatorNames.alphaShort,
cellInfoWcdma.signalStrengthWcdma.base.signalStrength,
- cellInfoWcdma.signalStrengthWcdma.base.bitErrorRate);
+ cellInfoWcdma.signalStrengthWcdma.base.bitErrorRate,
+ cellInfoWcdma.signalStrengthWcdma.rscp,
+ cellInfoWcdma.signalStrengthWcdma.ecno);
+ break;
+ }
+
+ case CellInfoType.TD_SCDMA: {
+ android.hardware.radio.V1_2.CellInfoTdscdma cellInfoTdscdma =
+ record.tdscdma.get(0);
+ writeToParcelForTdscdma(
+ p,
+ cellInfoTdscdma.cellIdentityTdscdma.base.lac,
+ cellInfoTdscdma.cellIdentityTdscdma.base.cid,
+ cellInfoTdscdma.cellIdentityTdscdma.base.cpid,
+ cellInfoTdscdma.cellIdentityTdscdma.uarfcn,
+ cellInfoTdscdma.cellIdentityTdscdma.base.mcc,
+ cellInfoTdscdma.cellIdentityTdscdma.base.mnc,
+ cellInfoTdscdma.cellIdentityTdscdma.operatorNames.alphaLong,
+ cellInfoTdscdma.cellIdentityTdscdma.operatorNames.alphaShort,
+ cellInfoTdscdma.signalStrengthTdscdma.signalStrength,
+ cellInfoTdscdma.signalStrengthTdscdma.bitErrorRate,
+ cellInfoTdscdma.signalStrengthTdscdma.rscp);
break;
}
@@ -5281,19 +5569,22 @@
return response;
}
+ private static int convertTdscdmaRscpTo1_2(int rscp) {
+ // The HAL 1.0 range is 25..120; the ASU/ HAL 1.2 range is 0..96;
+ // yes, this means the range in 1.0 cannot express -24dBm = 96
+ if (rscp >= 25 && rscp <= 120) {
+ // First we flip the sign to convert from the HALs -rscp to the actual RSCP value.
+ int rscpDbm = -rscp;
+ // Then to convert from RSCP to ASU, we apply the offset which aligns 0 ASU to -120dBm.
+ return rscpDbm + 120;
+ }
+ return Integer.MAX_VALUE;
+ }
+
/** Convert HAL 1.0 Signal Strength to android SignalStrength */
@VisibleForTesting
public static SignalStrength convertHalSignalStrength(
android.hardware.radio.V1_0.SignalStrength signalStrength) {
- int tdscdmaRscp_1_2 = 255; // 255 is the value for unknown/unreported ASU.
- // The HAL 1.0 range is 25..120; the ASU/ HAL 1.2 range is 0..96;
- // yes, this means the range in 1.0 cannot express -24dBm = 96
- if (signalStrength.tdScdma.rscp >= 25 && signalStrength.tdScdma.rscp <= 120) {
- // First we flip the sign to convert from the HALs -rscp to the actual RSCP value.
- int rscpDbm = -signalStrength.tdScdma.rscp;
- // Then to convert from RSCP to ASU, we apply the offset which aligns 0 ASU to -120dBm.
- tdscdmaRscp_1_2 = rscpDbm + 120;
- }
return new SignalStrength(
signalStrength.gw.signalStrength,
signalStrength.gw.bitErrorRate,
@@ -5307,7 +5598,7 @@
signalStrength.lte.rsrq,
signalStrength.lte.rssnr,
signalStrength.lte.cqi,
- tdscdmaRscp_1_2);
+ convertTdscdmaRscpTo1_2(signalStrength.tdScdma.rscp));
}
/** Convert HAL 1.2 Signal Strength to android SignalStrength */
diff --git a/src/java/com/android/internal/telephony/RILRequest.java b/src/java/com/android/internal/telephony/RILRequest.java
index c0a0b70..d20ce22 100644
--- a/src/java/com/android/internal/telephony/RILRequest.java
+++ b/src/java/com/android/internal/telephony/RILRequest.java
@@ -86,7 +86,8 @@
rr = new RILRequest();
}
- rr.mSerial = sNextSerial.getAndIncrement();
+ // Increment serial number. Wrap to 0 when reaching Integer.MAX_VALUE.
+ rr.mSerial = sNextSerial.getAndUpdate(n -> ((n + 1) % Integer.MAX_VALUE));
rr.mRequest = request;
rr.mResult = result;
@@ -151,9 +152,9 @@
}
static void resetSerial() {
- // use a random so that on recovery we probably don't mix old requests
+ // Use a non-negative random number so that on recovery we probably don't mix old requests
// with new.
- sNextSerial.set(sRandom.nextInt());
+ sNextSerial.set(sRandom.nextInt(Integer.MAX_VALUE));
}
String serialString() {
@@ -161,9 +162,9 @@
StringBuilder sb = new StringBuilder(8);
String sn;
- long adjustedSerial = (((long) mSerial) - Integer.MIN_VALUE) % 10000;
-
- sn = Long.toString(adjustedSerial);
+ // Truncate mSerial to a number with maximum 4 digits.
+ int adjustedSerial = mSerial % 10000;
+ sn = Integer.toString(adjustedSerial);
//sb.append("J[");
sb.append('[');
diff --git a/src/java/com/android/internal/telephony/RadioConfig.java b/src/java/com/android/internal/telephony/RadioConfig.java
index 02c7177..764fc10 100644
--- a/src/java/com/android/internal/telephony/RadioConfig.java
+++ b/src/java/com/android/internal/telephony/RadioConfig.java
@@ -39,6 +39,7 @@
import com.android.internal.telephony.uicc.IccSlotStatus;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.concurrent.atomic.AtomicLong;
/**
@@ -267,7 +268,8 @@
mDefaultWorkSource);
if (DBG) {
- logd(rr.serialString() + "> " + requestToString(rr.mRequest));
+ logd(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " " + Arrays.toString(physicalSlots));
}
try {
@@ -287,7 +289,7 @@
return arrayList;
}
- private static String requestToString(int request) {
+ static String requestToString(int request) {
switch (request) {
case RIL_REQUEST_GET_SLOT_STATUS:
return "GET_SLOT_STATUS";
diff --git a/src/java/com/android/internal/telephony/RadioConfigIndication.java b/src/java/com/android/internal/telephony/RadioConfigIndication.java
index b817f30..686282c 100644
--- a/src/java/com/android/internal/telephony/RadioConfigIndication.java
+++ b/src/java/com/android/internal/telephony/RadioConfigIndication.java
@@ -19,6 +19,7 @@
import android.hardware.radio.config.V1_0.IRadioConfigIndication;
import android.hardware.radio.config.V1_0.SimSlotStatus;
import android.os.AsyncResult;
+import android.telephony.Rlog;
import com.android.internal.telephony.uicc.IccSlotStatus;
@@ -29,6 +30,7 @@
*/
public class RadioConfigIndication extends IRadioConfigIndication.Stub {
private final RadioConfig mRadioConfig;
+ private static final String TAG = "RadioConfigIndication";
public RadioConfigIndication(RadioConfig radioConfig) {
mRadioConfig = radioConfig;
@@ -39,7 +41,7 @@
*/
public void simSlotsStatusChanged(int indicationType, ArrayList<SimSlotStatus> slotStatus) {
ArrayList<IccSlotStatus> ret = RadioConfig.convertHalSlotStatus(slotStatus);
-
+ Rlog.d(TAG, "[UNSL]< " + " UNSOL_SIM_SLOT_STATUS_CHANGED " + ret.toString());
if (mRadioConfig.mSimSlotStatusRegistrant != null) {
mRadioConfig.mSimSlotStatusRegistrant.notifyRegistrant(
new AsyncResult(null, ret, null));
diff --git a/src/java/com/android/internal/telephony/RadioConfigResponse.java b/src/java/com/android/internal/telephony/RadioConfigResponse.java
index f3d48ad..8177b2d 100644
--- a/src/java/com/android/internal/telephony/RadioConfigResponse.java
+++ b/src/java/com/android/internal/telephony/RadioConfigResponse.java
@@ -20,6 +20,7 @@
import android.hardware.radio.V1_0.RadioResponseInfo;
import android.hardware.radio.config.V1_0.IRadioConfigResponse;
import android.hardware.radio.config.V1_0.SimSlotStatus;
+import android.telephony.Rlog;
import com.android.internal.telephony.uicc.IccSlotStatus;
@@ -30,6 +31,7 @@
*/
public class RadioConfigResponse extends IRadioConfigResponse.Stub {
private final RadioConfig mRadioConfig;
+ private static final String TAG = "RadioConfigResponse";
public RadioConfigResponse(RadioConfig radioConfig) {
mRadioConfig = radioConfig;
@@ -47,9 +49,17 @@
if (responseInfo.error == RadioError.NONE) {
// send response
RadioResponse.sendMessageResponse(rr.mResult, ret);
+ Rlog.d(TAG, rr.serialString() + "< "
+ + mRadioConfig.requestToString(rr.mRequest) + " " + ret.toString());
} else {
rr.onError(responseInfo.error, ret);
+ Rlog.e(TAG, rr.serialString() + "< "
+ + mRadioConfig.requestToString(rr.mRequest) + " error "
+ + responseInfo.error);
}
+
+ } else {
+ Rlog.e(TAG, "getSimSlotsStatusResponse: Error " + responseInfo.toString());
}
}
@@ -63,9 +73,18 @@
if (responseInfo.error == RadioError.NONE) {
// send response
RadioResponse.sendMessageResponse(rr.mResult, null);
+ Rlog.d(TAG, rr.serialString() + "< "
+ + mRadioConfig.requestToString(rr.mRequest));
} else {
rr.onError(responseInfo.error, null);
+ Rlog.e(TAG, rr.serialString() + "< "
+ + mRadioConfig.requestToString(rr.mRequest) + " error "
+ + responseInfo.error);
}
+ } else {
+ Rlog.e(TAG, "setSimSlotsMappingResponse: Error " + responseInfo.toString());
}
}
+
+
}
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index e80e5e7..f7a7943 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -36,6 +36,7 @@
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_SS;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_USSD;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PCO_DATA;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RADIO_CAPABILITY;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESEND_INCALL_MUTE;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED;
@@ -295,6 +296,8 @@
response.add(new PhysicalChannelConfig(status, config.cellBandwidthDownlink));
}
+ if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response);
+
mRil.mPhysicalChannelConfigurationRegistrants.notifyRegistrants(
new AsyncResult(null, response, null));
}
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index c1e7b1b..0fc5279 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -270,7 +270,7 @@
responseSignalStrength_1_2(responseInfo, signalStrength);
}
- /*
+ /**
* @param responseInfo Response info struct containing response type, serial no. and error
* @param voiceRegResponse Current Voice registration response as defined by VoiceRegStateResult
* in types.hal
@@ -289,6 +289,23 @@
/**
* @param responseInfo Response info struct containing response type, serial no. and error
+ * @param voiceRegResponse Current Voice registration response as defined by VoiceRegStateResult
+ * in 1.2/types.hal
+ */
+ public void getVoiceRegistrationStateResponse_1_2(RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_2.VoiceRegStateResult voiceRegResponse) {
+ RILRequest rr = mRil.processResponse(responseInfo);
+
+ if (rr != null) {
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, voiceRegResponse);
+ }
+ mRil.processResponseDone(rr, responseInfo, voiceRegResponse);
+ }
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
* @param dataRegResponse Current Data registration response as defined by DataRegStateResult in
* types.hal
*/
@@ -306,6 +323,23 @@
/**
* @param responseInfo Response info struct containing response type, serial no. and error
+ * @param dataRegResponse Current Data registration response as defined by DataRegStateResult in
+ * 1.2/types.hal
+ */
+ public void getDataRegistrationStateResponse_1_2(RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_2.DataRegStateResult dataRegResponse) {
+ RILRequest rr = mRil.processResponse(responseInfo);
+
+ if (rr != null) {
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, dataRegResponse);
+ }
+ mRil.processResponseDone(rr, responseInfo, dataRegResponse);
+ }
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
* @param longName is long alpha ONS or EONS or empty string if unregistered
* @param shortName is short alpha ONS or EONS or empty string if unregistered
* @param numeric is 5 or 6 digit numeric code (MCC + MNC) or empty string if unregistered
@@ -603,6 +637,9 @@
responseDataCallList(responseInfo, dataCallResultList);
}
+ public void sendOemRilRequestRawResponse(RadioResponseInfo responseInfo,
+ ArrayList<Byte> var2) {}
+
/**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
@@ -1291,12 +1328,12 @@
} else {
ret = new KeepaliveStatus(keepaliveStatus.sessionHandle, convertedStatus);
}
+ // If responseInfo.error is NONE, response function sends the response message
+ // even if result is actually an error.
+ sendMessageResponse(rr.mResult, ret);
break;
case RadioError.REQUEST_NOT_SUPPORTED:
ret = new KeepaliveStatus(KeepaliveStatus.ERROR_UNSUPPORTED);
- // The request is unsupported, which is ok. We'll report it to the higher
- // layer and treat it as acceptable in the RIL.
- responseInfo.error = RadioError.NONE;
break;
case RadioError.NO_RESOURCES:
ret = new KeepaliveStatus(KeepaliveStatus.ERROR_NO_RESOURCES);
@@ -1305,7 +1342,7 @@
ret = new KeepaliveStatus(KeepaliveStatus.ERROR_UNKNOWN);
break;
}
- sendMessageResponse(rr.mResult, ret);
+ // If responseInfo.error != NONE, the processResponseDone sends the response message.
mRil.processResponseDone(rr, responseInfo, ret);
}
diff --git a/src/java/com/android/internal/telephony/RatRatcheter.java b/src/java/com/android/internal/telephony/RatRatcheter.java
index e6032a5..3745182 100644
--- a/src/java/com/android/internal/telephony/RatRatcheter.java
+++ b/src/java/com/android/internal/telephony/RatRatcheter.java
@@ -22,11 +22,12 @@
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
import android.telephony.ServiceState;
+import android.telephony.Rlog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import java.util.ArrayList;
import java.util.Arrays;
/**
@@ -48,6 +49,8 @@
private final SparseArray<SparseIntArray> mRatFamilyMap = new SparseArray<>();
private final Phone mPhone;
+ private boolean mVoiceRatchetEnabled = true;
+ private boolean mDataRatchetEnabled = true;
/**
* Updates the ServiceState with a new set of cell bandwidths IFF the new bandwidth list has a
@@ -98,27 +101,46 @@
}
/** Ratchets RATs and cell bandwidths if oldSS and newSS have the same RAT family. */
- public void ratchet(ServiceState oldSS, ServiceState newSS) {
- int newVoiceRat = ratchetRat(oldSS.getRilVoiceRadioTechnology(),
- newSS.getRilVoiceRadioTechnology());
- int newDataRat = ratchetRat(oldSS.getRilDataRadioTechnology(),
- newSS.getRilDataRadioTechnology());
-
- if (isSameRatFamily(oldSS, newSS)) {
+ public void ratchet(ServiceState oldSS, ServiceState newSS, boolean locationChange) {
+ if (!locationChange && isSameRatFamily(oldSS, newSS)) {
updateBandwidths(oldSS.getCellBandwidths(), newSS);
}
+ // temporarily disable rat ratchet on location change.
+ if (locationChange) {
+ mVoiceRatchetEnabled = false;
+ mDataRatchetEnabled = false;
+ return;
+ }
+ if (mVoiceRatchetEnabled) {
+ int newVoiceRat = ratchetRat(oldSS.getRilVoiceRadioTechnology(),
+ newSS.getRilVoiceRadioTechnology());
+ newSS.setRilVoiceRadioTechnology(newVoiceRat);
+ } else if (oldSS.getRilVoiceRadioTechnology() != newSS.getRilVoiceRadioTechnology()) {
+ // resume rat ratchet on following rat change within the same location
+ mVoiceRatchetEnabled = true;
+ }
+
+ if (mDataRatchetEnabled) {
+ int newDataRat = ratchetRat(oldSS.getRilDataRadioTechnology(),
+ newSS.getRilDataRadioTechnology());
+ newSS.setRilDataRadioTechnology(newDataRat);
+ } else if (oldSS.getRilVoiceRadioTechnology() != newSS.getRilVoiceRadioTechnology()) {
+ // resume rat ratchet on following rat change within the same location
+ mVoiceRatchetEnabled = true;
+ }
boolean newUsingCA = oldSS.isUsingCarrierAggregation()
|| newSS.isUsingCarrierAggregation()
|| newSS.getCellBandwidths().length > 1;
-
- newSS.setRilVoiceRadioTechnology(newVoiceRat);
- newSS.setRilDataRadioTechnology(newDataRat);
newSS.setIsUsingCarrierAggregation(newUsingCA);
}
private boolean isSameRatFamily(ServiceState ss1, ServiceState ss2) {
synchronized (mRatFamilyMap) {
+ // Either the two technologies are the same or their families must be non-null
+ // and the same.
+ if (ss1.getRilDataRadioTechnology() == ss2.getRilDataRadioTechnology()) return true;
+ if (mRatFamilyMap.get(ss1.getRilDataRadioTechnology()) == null) return false;
return mRatFamilyMap.get(ss1.getRilDataRadioTechnology())
== mRatFamilyMap.get(ss2.getRilDataRadioTechnology());
}
diff --git a/src/java/com/android/internal/telephony/RetryManager.java b/src/java/com/android/internal/telephony/RetryManager.java
index 23c3498..0b4bc3c 100644
--- a/src/java/com/android/internal/telephony/RetryManager.java
+++ b/src/java/com/android/internal/telephony/RetryManager.java
@@ -22,11 +22,10 @@
import android.os.SystemProperties;
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
+import android.telephony.data.ApnSetting;
import android.text.TextUtils;
import android.util.Pair;
-import com.android.internal.telephony.dataconnection.ApnSetting;
-
import java.util.ArrayList;
import java.util.Random;
@@ -506,7 +505,9 @@
if (++index == mWaitingApns.size()) index = 0;
// Stop if we find the non-failed APN.
- if (mWaitingApns.get(index).permanentFailed == false) break;
+ if (!mWaitingApns.get(index).getPermanentFailed()) {
+ break;
+ }
// If we've already cycled through all the APNs, that means there is no APN we can try
if (index == mCurrentApnIndex) return null;
@@ -553,7 +554,9 @@
if (++index >= mWaitingApns.size()) index = 0;
// Stop if we find the non-failed APN.
- if (mWaitingApns.get(index).permanentFailed == false) break;
+ if (!mWaitingApns.get(index).getPermanentFailed()) {
+ break;
+ }
// If we've already cycled through all the APNs, that means all APNs have
// permanently failed
@@ -594,7 +597,7 @@
* */
public void markApnPermanentFailed(ApnSetting apn) {
if (apn != null) {
- apn.permanentFailed = true;
+ apn.setPermanentFailed(true);
}
}
@@ -627,7 +630,7 @@
configureRetry();
for (ApnSetting apn : mWaitingApns) {
- apn.permanentFailed = false;
+ apn.setPermanentFailed(false);
}
log("Setting " + mWaitingApns.size() + " waiting APNs.");
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 5952c19..1e5afc7 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -628,7 +628,7 @@
* Send an SMS PDU. Usually just calls {@link sendRawPdu}.
*/
private void sendSubmitPdu(SmsTracker tracker) {
- if (shouldBlockSms()) {
+ if (shouldBlockSmsForEcbm()) {
Rlog.d(TAG, "Block SMS in Emergency Callback mode");
tracker.onFailed(mContext, SmsManager.RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);
} else {
@@ -637,9 +637,9 @@
}
/**
- * @return true if MO SMS should be blocked.
+ * @return true if MO SMS should be blocked for Emergency Callback Mode.
*/
- protected abstract boolean shouldBlockSms();
+ protected abstract boolean shouldBlockSmsForEcbm();
/**
* Called when SMS send completes. Broadcasts a sentIntent on success.
@@ -1454,7 +1454,13 @@
// fields need to be public for derived SmsDispatchers
private final HashMap<String, Object> mData;
public int mRetryCount;
- public int mImsRetry; // nonzero indicates initial message was sent over Ims
+ // IMS retry counter. Nonzero indicates initial message was sent over IMS channel in RIL and
+ // counts how many retries have been made on the IMS channel.
+ // Used in older implementations where the message is sent over IMS using the RIL.
+ public int mImsRetry;
+ // Tag indicating that this SMS is being handled by the ImsSmsDispatcher. This tracker
+ // should not try to use SMS over IMS over the RIL interface in this case when falling back.
+ public boolean mUsesImsServiceForIms;
public int mMessageRef;
public boolean mExpectMore;
public int mValidityPeriod;
@@ -1504,6 +1510,7 @@
mFormat = format;
mExpectMore = expectMore;
mImsRetry = 0;
+ mUsesImsServiceForIms = false;
mMessageRef = 0;
mUnsentPartCount = unsentPartCount;
mAnyPartFailed = anyPartFailed;
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 053d231..b572934 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -37,6 +37,7 @@
import android.os.BaseBundle;
import android.os.Build;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.Registrant;
@@ -48,6 +49,7 @@
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.CarrierConfigManager;
import android.telephony.CellIdentity;
import android.telephony.CellIdentityCdma;
@@ -78,6 +80,7 @@
import android.util.Pair;
import android.util.SparseArray;
import android.util.TimeUtils;
+import android.util.TimestampedValue;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
@@ -92,7 +95,6 @@
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.telephony.util.TimeStampedValue;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -128,6 +130,7 @@
private static final long LAST_CELL_INFO_LIST_MAX_AGE_MS = 2000;
private long mLastCellInfoListTime;
private List<CellInfo> mLastCellInfoList = null;
+ private List<PhysicalChannelConfig> mLastPhysicalChannelConfigList = null;
private SignalStrength mSignalStrength;
@@ -139,7 +142,8 @@
* and ignore stale responses. The value is a count-down of
* expected responses in this pollingContext.
*/
- private int[] mPollingContext;
+ @VisibleForTesting
+ public int[] mPollingContext;
private boolean mDesiredPowerState;
/**
@@ -160,6 +164,7 @@
private RegistrantList mNetworkDetachedRegistrants = new RegistrantList();
private RegistrantList mPsRestrictEnabledRegistrants = new RegistrantList();
private RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList();
+ private RegistrantList mImsCapabilityChangedRegistrants = new RegistrantList();
/* Radio power off pending flag and tag counter */
private boolean mPendingRadioPowerOffAfterDataOff = false;
@@ -243,7 +248,9 @@
private String mCurPlmn = null;
private boolean mCurShowPlmn = false;
private boolean mCurShowSpn = false;
- private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ @VisibleForTesting
+ public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private int mPrevSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mImsRegistered = false;
@@ -255,6 +262,9 @@
private final RatRatcheter mRatRatcheter;
+ private final HandlerThread mHandlerThread;
+ private final LocaleTracker mLocaleTracker;
+
private final LocalLog mRoamingLog = new LocalLog(10);
private final LocalLog mAttachLog = new LocalLog(10);
private final LocalLog mPhoneTypeLog = new LocalLog(10);
@@ -274,6 +284,7 @@
if (DBG) log("SubscriptionListener.onSubscriptionInfoChanged");
// Set the network type, in case the radio does not restore it.
int subId = mPhone.getSubId();
+ ServiceStateTracker.this.mPrevSubId = mPreviousSubId.get();
if (mPreviousSubId.getAndSet(subId) != subId) {
if (SubscriptionManager.isValidSubscriptionId(subId)) {
Context context = mPhone.getContext();
@@ -410,7 +421,7 @@
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- updateLteEarfcnLists();
+ onCarrierConfigChanged();
return;
}
@@ -503,6 +514,13 @@
this, EVENT_NETWORK_STATE_CHANGED, null);
}
+ // Create a new handler thread dedicated for locale tracker because the blocking
+ // getAllCellInfo call requires clients calling from a different thread.
+ mHandlerThread = new HandlerThread(LocaleTracker.class.getSimpleName());
+ mHandlerThread.start();
+ mLocaleTracker = TelephonyComponentFactory.getInstance().makeLocaleTracker(
+ mPhone, mHandlerThread.getLooper());
+
mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
mCi.setOnNITZTime(this, EVENT_NITZ_TIME, null);
@@ -548,6 +566,8 @@
CarrierServiceStateTracker.CARRIER_EVENT_DATA_REGISTRATION, null);
registerForDataConnectionDetached(mCSST,
CarrierServiceStateTracker.CARRIER_EVENT_DATA_DEREGISTRATION, null);
+ registerForImsCapabilityChanged(mCSST,
+ CarrierServiceStateTracker.CARRIER_EVENT_IMS_CAPABILITIES_CHANGED, null);
}
@VisibleForTesting
@@ -565,11 +585,17 @@
}
// If we are previously in service, we need to notify that we are out of service now.
+ if (mSS != null && mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
+ mNetworkDetachedRegistrants.notifyRegistrants();
+ }
+
+ // If we are previously in service, we need to notify that we are out of service now.
if (mSS != null && mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE) {
mDetachedRegistrants.notifyRegistrants();
}
mSS = new ServiceState();
+ mSS.setStateOutOfService();
mNewSS = new ServiceState();
mLastCellInfoListTime = 0;
mLastCellInfoList = null;
@@ -650,9 +676,14 @@
mCi.unregisterForPhysicalChannelConfiguration(this);
mSubscriptionManager
.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
+ mHandlerThread.quit();
mCi.unregisterForImsNetworkStateChanged(this);
mPhone.getCarrierActionAgent().unregisterForCarrierAction(this,
CARRIER_ACTION_SET_RADIO_ENABLED);
+ if (mCSST != null) {
+ mCSST.dispose();
+ mCSST = null;
+ }
}
public boolean getDesiredPowerState() {
@@ -1059,7 +1090,9 @@
case EVENT_SIM_READY:
// Reset the mPreviousSubId so we treat a SIM power bounce
// as a first boot. See b/19194287
- mOnSubscriptionsChangedListener.mPreviousSubId.set(-1);
+ mOnSubscriptionsChangedListener.mPreviousSubId.set(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ mPrevSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
mIsSimReady = true;
pollState();
// Signal strength polling stops when radio is off
@@ -1273,6 +1306,7 @@
case EVENT_IMS_CAPABILITY_CHANGED:
if (DBG) log("EVENT_IMS_CAPABILITY_CHANGED");
updateSpnDisplay();
+ mImsCapabilityChangedRegistrants.notifyRegistrants();
break;
case EVENT_IMS_SERVICE_STATE_CHANGED:
@@ -1430,6 +1464,7 @@
+ list);
}
mPhone.notifyPhysicalChannelConfiguration(list);
+ mLastPhysicalChannelConfigList = list;
// only notify if bandwidths changed
if (RatRatcheter.updateBandwidths(getBandwidthsFromConfigs(list), mSS)) {
@@ -1602,6 +1637,10 @@
if (ar.exception != null) {
CommandException.Error err=null;
+ if (ar.exception instanceof IllegalStateException) {
+ log("handlePollStateResult exception " + ar.exception);
+ }
+
if (ar.exception instanceof CommandException) {
err = ((CommandException)(ar.exception)).getCommandError();
}
@@ -1625,12 +1664,12 @@
mPollingContext[0]--;
if (mPollingContext[0] == 0) {
+ mNewSS.setEmergencyOnly(mEmergencyOnly);
if (mPhone.isPhoneTypeGsm()) {
updateRoamingState();
- mNewSS.setEmergencyOnly(mEmergencyOnly);
} else {
boolean namMatch = false;
- if (!isSidsAllZeros() && isHomeSid(mNewSS.getSystemId())) {
+ if (!isSidsAllZeros() && isHomeSid(mNewSS.getCdmaSystemId())) {
namMatch = true;
}
@@ -1765,9 +1804,11 @@
mNewSS.setCssIndicator(cssIndicator);
mNewSS.setRilVoiceRadioTechnology(newVoiceRat);
mNewSS.addNetworkRegistrationState(networkRegState);
+ setPhyCellInfoFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
//Denial reason if registrationState = 3
- int reasonForDenial = networkRegState.getReasonForDenial();
+ int reasonForDenial = networkRegState.getRejectCause();
+ mEmergencyOnly = networkRegState.isEmergencyEnabled();
if (mPhone.isPhoneTypeGsm()) {
mGsmRoaming = regCodeIsRoaming(registrationState);
@@ -1775,7 +1816,6 @@
boolean isVoiceCapable = mPhone.getContext().getResources()
.getBoolean(com.android.internal.R.bool.config_voice_capable);
- mEmergencyOnly = networkRegState.isEmergencyEnabled();
} else {
int roamingIndicator = voiceSpecificStates.roamingIndicator;
@@ -1805,7 +1845,7 @@
systemId = ((CellIdentityCdma) cellIdentity).getSystemId();
networkId = ((CellIdentityCdma) cellIdentity).getNetworkId();
}
- mNewSS.setSystemAndNetworkId(systemId, networkId);
+ mNewSS.setCdmaSystemAndNetworkId(systemId, networkId);
if (reasonForDenial == 0) {
mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_GEN;
@@ -1841,9 +1881,17 @@
mNewSS.setRilDataRadioTechnology(newDataRat);
mNewSS.addNetworkRegistrationState(networkRegState);
+ // When we receive OOS reset the PhyChanConfig list so that non-return-to-idle
+ // implementers of PhyChanConfig unsol will not carry forward a CA report
+ // (2 or more cells) to a new cell if they camp for emergency service only.
+ if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
+ mLastPhysicalChannelConfigList = null;
+ }
+ setPhyCellInfoFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
+
if (mPhone.isPhoneTypeGsm()) {
- mNewReasonDataDenied = networkRegState.getReasonForDenial();
+ mNewReasonDataDenied = networkRegState.getRejectCause();
mNewMaxDataCalls = dataSpecificStates.maxDataCalls;
mDataRoaming = regCodeIsRoaming(registrationState);
// Save the data roaming state reported by modem registration before resource
@@ -1978,6 +2026,78 @@
}
}
+ private static boolean isValidLteBandwidthKhz(int bandwidth) {
+ // Valid bandwidths, see 3gpp 36.101 sec. 5.6
+ switch (bandwidth) {
+ case 1400:
+ case 3000:
+ case 5000:
+ case 10000:
+ case 15000:
+ case 20000:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private void setPhyCellInfoFromCellIdentity(ServiceState ss, CellIdentity cellIdentity) {
+ if (cellIdentity == null) {
+ if (DBG) {
+ log("Could not set ServiceState channel number. CellIdentity null");
+ }
+ return;
+ }
+
+ ss.setChannelNumber(cellIdentity.getChannelNumber());
+ if (VDBG) {
+ log("Setting channel number: " + cellIdentity.getChannelNumber());
+ }
+
+ if (cellIdentity instanceof CellIdentityLte) {
+ CellIdentityLte cl = (CellIdentityLte) cellIdentity;
+ int[] bandwidths = null;
+ // Prioritize the PhysicalChannelConfig list because we might already be in carrier
+ // aggregation by the time poll state is performed.
+ if (!ArrayUtils.isEmpty(mLastPhysicalChannelConfigList)) {
+ bandwidths = getBandwidthsFromConfigs(mLastPhysicalChannelConfigList);
+ for (int bw : bandwidths) {
+ if (!isValidLteBandwidthKhz(bw)) {
+ loge("Invalid LTE Bandwidth in RegistrationState, " + bw);
+ bandwidths = null;
+ break;
+ }
+ }
+ }
+ // If we don't have a PhysicalChannelConfig[] list, then pull from CellIdentityLte.
+ // This is normal if we're in idle mode and the PhysicalChannelConfig[] has already
+ // been updated. This is also a fallback in case the PhysicalChannelConfig info
+ // is invalid (ie, broken).
+ // Also, for vendor implementations that do not report return-to-idle, we should
+ // prioritize the bandwidth report in the CellIdentity, because the physical channel
+ // config report may be stale in the case where a single carrier was used previously
+ // and we transition to camped-for-emergency (since we never have a physical
+ // channel active). In the normal case of single-carrier non-return-to-idle, the
+ // values *must* be the same, so it doesn't matter which is chosen.
+ if (bandwidths == null || bandwidths.length == 1) {
+ final int cbw = cl.getBandwidth();
+ if (isValidLteBandwidthKhz(cbw)) {
+ bandwidths = new int[] {cbw};
+ } else if (cbw == Integer.MAX_VALUE) {
+ // Bandwidth is unreported; c'est la vie. This is not an error because
+ // pre-1.2 HAL implementations do not support bandwidth reporting.
+ } else {
+ loge("Invalid LTE Bandwidth in RegistrationState, " + cbw);
+ }
+ }
+ if (bandwidths != null) {
+ ss.setCellBandwidths(bandwidths);
+ }
+ } else {
+ if (VDBG) log("Skipping bandwidth update for Non-LTE cell.");
+ }
+ }
+
/**
* Determine whether a roaming indicator is in the carrier-specified list of ERIs for
* home system
@@ -2072,7 +2192,7 @@
if (configLoader != null) {
try {
PersistableBundle b = configLoader.getConfigForSubId(mPhone.getSubId());
- String systemId = Integer.toString(mNewSS.getSystemId());
+ String systemId = Integer.toString(mNewSS.getCdmaSystemId());
if (alwaysOnHomeNetwork(b)) {
log("updateRoamingState: carrier config override always on home network");
@@ -2115,8 +2235,32 @@
mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_OFF);
}
+ private void updateOperatorNameFromCarrierConfig() {
+ // Brand override gets a priority over carrier config. If brand override is not available,
+ // override the operator name in home network. Also do this only for CDMA. This is temporary
+ // and should be fixed in a proper way in a later release.
+ if (!mPhone.isPhoneTypeGsm() && !mSS.getRoaming()) {
+ boolean hasBrandOverride = mUiccController.getUiccCard(getPhoneId()) != null
+ && mUiccController.getUiccCard(getPhoneId()).getOperatorBrandOverride() != null;
+ if (!hasBrandOverride) {
+ PersistableBundle config = getCarrierConfig();
+ if (config.getBoolean(
+ CarrierConfigManager.KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL)) {
+ String operator = config.getString(
+ CarrierConfigManager.KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING);
+ log("updateOperatorNameFromCarrierConfig: changing from "
+ + mSS.getOperatorAlpha() + " to " + operator);
+ // override long and short operator name, keeping numeric the same
+ mSS.setOperatorName(operator, operator, mSS.getOperatorNumeric());
+ }
+ }
+ }
+ }
+
protected void updateSpnDisplay() {
updateOperatorNameFromEri();
+ // carrier config gets a priority over ERI
+ updateOperatorNameFromCarrierConfig();
String wfcVoiceSpnFormat = null;
String wfcDataSpnFormat = null;
@@ -2635,8 +2779,8 @@
// ratchet the new tech up through its rat family but don't drop back down
// until cell change or device is OOS
boolean isDataInService = mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE;
- if (!hasLocationChanged && isDataInService) {
- mRatRatcheter.ratchet(mSS, mNewSS);
+ if (isDataInService) {
+ mRatRatcheter.ratchet(mSS, mNewSS, hasLocationChanged);
}
boolean hasRilVoiceRadioTechnologyChanged =
@@ -2806,17 +2950,19 @@
if (!mPhone.isPhoneTypeGsm()) {
// try to fix the invalid Operator Numeric
if (isInvalidOperatorNumeric(operatorNumeric)) {
- int sid = mSS.getSystemId();
+ int sid = mSS.getCdmaSystemId();
operatorNumeric = fixUnknownMcc(operatorNumeric, sid);
}
}
tm.setNetworkOperatorNumericForPhone(mPhone.getPhoneId(), operatorNumeric);
- updateCarrierMccMncConfiguration(operatorNumeric,
- prevOperatorNumeric, mPhone.getContext());
+
if (isInvalidOperatorNumeric(operatorNumeric)) {
if (DBG) log("operatorNumeric " + operatorNumeric + " is invalid");
- tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), "");
+ // Passing empty string is important for the first update. The initial value of
+ // operator numeric in locale tracker is null. The async update will allow getting
+ // cell info from the modem instead of using the cached one.
+ mLocaleTracker.updateOperatorNumericAsync("");
mNitzState.handleNetworkUnavailable();
} else if (mSS.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
// If the device is on IWLAN, modems manufacture a ServiceState with the MCC/MNC of
@@ -2828,15 +2974,8 @@
setOperatorIdd(operatorNumeric);
}
- // Update ISO.
- String countryIsoCode = "";
- try {
- String mcc = operatorNumeric.substring(0, 3);
- countryIsoCode = MccTable.countryCodeForMcc(Integer.parseInt(mcc));
- } catch (NumberFormatException | StringIndexOutOfBoundsException ex) {
- loge("pollStateDone: countryCodeForMcc error: " + ex);
- }
- tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), countryIsoCode);
+ mLocaleTracker.updateOperatorNumericSync(operatorNumeric);
+ String countryIsoCode = mLocaleTracker.getCurrentCountry();
// Update Time Zone.
boolean iccCardExists = iccCardExists();
@@ -2895,6 +3034,9 @@
if (hasRilDataRadioTechnologyChanged || hasRilVoiceRadioTechnologyChanged) {
logRatChange();
+
+ updateRatTypeForSignalStrength();
+ notifySignalStrength();
}
if (hasDataRegStateChanged || hasRilDataRadioTechnologyChanged) {
@@ -2971,8 +3113,7 @@
if (!hasBrandOverride && (mCi.getRadioState().isOn()) && (mPhone.isEriFileLoaded()) &&
(!ServiceState.isLte(mSS.getRilVoiceRadioTechnology()) ||
mPhone.getContext().getResources().getBoolean(com.android.internal.R.
- bool.config_LTE_eri_for_network_name)) &&
- (!mIsSubscriptionFromRuim)) {
+ bool.config_LTE_eri_for_network_name))) {
// Only when CDMA is in service, ERI will take effect
String eriText = mSS.getOperatorAlpha();
// Now the Phone sees the new ServiceState so it can get the new ERI text
@@ -3004,9 +3145,9 @@
((RuimRecords) mIccRecords).getCsimSpnDisplayCondition();
int iconIndex = mSS.getCdmaEriIconIndex();
- if (showSpn && (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) &&
- isInHomeSidNid(mSS.getSystemId(), mSS.getNetworkId()) &&
- mIccRecords != null) {
+ if (showSpn && (iconIndex == EriInfo.ROAMING_INDICATOR_OFF)
+ && isInHomeSidNid(mSS.getCdmaSystemId(), mSS.getCdmaNetworkId())
+ && mIccRecords != null) {
mSS.setOperatorAlphaLong(mIccRecords.getServiceProviderName());
}
}
@@ -3421,8 +3562,8 @@
NitzData newNitzData = NitzData.parse(nitzString);
if (newNitzData != null) {
try {
- TimeStampedValue<NitzData> nitzSignal =
- new TimeStampedValue<>(newNitzData, nitzReceiveTime);
+ TimestampedValue<NitzData> nitzSignal =
+ new TimestampedValue<>(nitzReceiveTime, newNitzData);
mNitzState.handleNitzReceived(nitzSignal);
} finally {
if (DBG) {
@@ -3434,17 +3575,19 @@
}
/**
- * 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.
+ * Cancels all notifications posted to NotificationManager for this subId. 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");
+ if (DBG) log("cancelAllNotifications: mPrevSubId=" + mPrevSubId);
NotificationManager notificationManager = (NotificationManager)
mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(PS_NOTIFICATION);
- notificationManager.cancel(CS_NOTIFICATION);
- notificationManager.cancel(CS_REJECT_CAUSE_NOTIFICATION);
+ if (SubscriptionManager.isValidSubscriptionId(mPrevSubId)) {
+ notificationManager.cancel(Integer.toString(mPrevSubId), PS_NOTIFICATION);
+ notificationManager.cancel(Integer.toString(mPrevSubId), CS_NOTIFICATION);
+ notificationManager.cancel(Integer.toString(mPrevSubId), CS_REJECT_CAUSE_NOTIFICATION);
+ }
}
/**
@@ -3457,6 +3600,12 @@
public void setNotification(int notifyType) {
if (DBG) log("setNotification: create notification " + notifyType);
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ // notifications are posted per-sub-id, so return if current sub-id is invalid
+ loge("cannot setNotification on invalid subid mSubId=" + mSubId);
+ return;
+ }
+
// Needed because sprout RIL sends these when they shouldn't?
boolean isSetNotification = mPhone.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_user_notification_of_restrictied_mobile_access);
@@ -3488,6 +3637,10 @@
int notificationId = CS_NOTIFICATION;
int icon = com.android.internal.R.drawable.stat_sys_warning;
+ final boolean multipleSubscriptions = (((TelephonyManager) mPhone.getContext()
+ .getSystemService(Context.TELEPHONY_SERVICE)).getPhoneCount() > 1);
+ final int simNumber = mSubscriptionController.getSlotIndex(mSubId) + 1;
+
switch (notifyType) {
case PS_ENABLED:
long dataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
@@ -3496,37 +3649,52 @@
}
notificationId = PS_NOTIFICATION;
title = context.getText(com.android.internal.R.string.RestrictedOnDataTitle);
- details = context.getText(com.android.internal.R.string.RestrictedStateContent);
+ details = multipleSubscriptions
+ ? context.getString(
+ com.android.internal.R.string.RestrictedStateContentMsimTemplate,
+ simNumber) :
+ context.getText(com.android.internal.R.string.RestrictedStateContent);
break;
case PS_DISABLED:
notificationId = PS_NOTIFICATION;
break;
case CS_ENABLED:
title = context.getText(com.android.internal.R.string.RestrictedOnAllVoiceTitle);
- details = context.getText(
- com.android.internal.R.string.RestrictedStateContent);
+ details = multipleSubscriptions
+ ? context.getString(
+ com.android.internal.R.string.RestrictedStateContentMsimTemplate,
+ simNumber) :
+ context.getText(com.android.internal.R.string.RestrictedStateContent);
break;
case CS_NORMAL_ENABLED:
title = context.getText(com.android.internal.R.string.RestrictedOnNormalTitle);
- details = context.getText(com.android.internal.R.string.RestrictedStateContent);
+ details = multipleSubscriptions
+ ? context.getString(
+ com.android.internal.R.string.RestrictedStateContentMsimTemplate,
+ simNumber) :
+ context.getText(com.android.internal.R.string.RestrictedStateContent);
break;
case CS_EMERGENCY_ENABLED:
title = context.getText(com.android.internal.R.string.RestrictedOnEmergencyTitle);
- details = context.getText(
- com.android.internal.R.string.RestrictedStateContent);
+ details = multipleSubscriptions
+ ? context.getString(
+ com.android.internal.R.string.RestrictedStateContentMsimTemplate,
+ simNumber) :
+ context.getText(com.android.internal.R.string.RestrictedStateContent);
break;
case CS_DISABLED:
// do nothing and cancel the notification later
break;
case CS_REJECT_CAUSE_ENABLED:
notificationId = CS_REJECT_CAUSE_NOTIFICATION;
- int resId = selectResourceForRejectCode(mRejectCode);
+ int resId = selectResourceForRejectCode(mRejectCode, multipleSubscriptions);
if (0 == resId) {
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);
+ // if using the single SIM resource, simNumber will be ignored
+ title = context.getString(resId, simNumber);
details = null;
}
break;
@@ -3534,7 +3702,7 @@
if (DBG) {
log("setNotification, create notification, notifyType: " + notifyType
- + ", title: " + title + ", details: " + details);
+ + ", title: " + title + ", details: " + details + ", subId: " + mSubId);
}
mNotification = new Notification.Builder(context)
@@ -3555,10 +3723,25 @@
if (notifyType == PS_DISABLED || notifyType == CS_DISABLED) {
// cancel previous post notification
- notificationManager.cancel(notificationId);
+ notificationManager.cancel(Integer.toString(mSubId), notificationId);
} else {
- // update restricted state notification
- notificationManager.notify(notificationId, mNotification);
+ boolean show = false;
+ if (mSS.isEmergencyOnly() && notifyType == CS_EMERGENCY_ENABLED) {
+ // if reg state is emergency only, always show restricted emergency notification.
+ show = true;
+ } else if (notifyType == CS_REJECT_CAUSE_ENABLED) {
+ // always show notification due to CS reject irrespective of service state.
+ show = true;
+ } else if (mSS.getState() == ServiceState.STATE_IN_SERVICE) {
+ // for non in service states, we have system UI and signal bar to indicate limited
+ // service. No need to show notification again. This also helps to mitigate the
+ // issue if phone go to OOS and camp to other networks and received restricted ind.
+ show = true;
+ }
+ // update restricted state notification for this subId
+ if (show) {
+ notificationManager.notify(Integer.toString(mSubId), notificationId, mNotification);
+ }
}
}
@@ -3568,20 +3751,28 @@
*
* @param rejCode should be compatible with TS 24.008.
*/
- private int selectResourceForRejectCode(int rejCode) {
+ private int selectResourceForRejectCode(int rejCode, boolean multipleSubscriptions) {
int rejResourceId = 0;
switch (rejCode) {
case 1:// Authentication reject
- rejResourceId = com.android.internal.R.string.mmcc_authentication_reject;
+ rejResourceId = multipleSubscriptions
+ ? com.android.internal.R.string.mmcc_authentication_reject_msim_template :
+ com.android.internal.R.string.mmcc_authentication_reject;
break;
case 2:// IMSI unknown in HLR
- rejResourceId = com.android.internal.R.string.mmcc_imsi_unknown_in_hlr;
+ rejResourceId = multipleSubscriptions
+ ? com.android.internal.R.string.mmcc_imsi_unknown_in_hlr_msim_template :
+ com.android.internal.R.string.mmcc_imsi_unknown_in_hlr;
break;
case 3:// Illegal MS
- rejResourceId = com.android.internal.R.string.mmcc_illegal_ms;
+ rejResourceId = multipleSubscriptions
+ ? com.android.internal.R.string.mmcc_illegal_ms_msim_template :
+ com.android.internal.R.string.mmcc_illegal_ms;
break;
case 6:// Illegal ME
- rejResourceId = com.android.internal.R.string.mmcc_illegal_me;
+ rejResourceId = multipleSubscriptions
+ ? com.android.internal.R.string.mmcc_illegal_me_msim_template :
+ com.android.internal.R.string.mmcc_illegal_me;
break;
default:
// The other codes are not defined or not required by operators till now.
@@ -3756,6 +3947,25 @@
}
/**
+ * Registers for IMS capability changed.
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in Message.obj
+ */
+ public void registerForImsCapabilityChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant(h, what, obj);
+ mImsCapabilityChangedRegistrants.add(r);
+ }
+
+ /**
+ * Unregisters for IMS capability changed.
+ * @param h handler to notify
+ */
+ public void unregisterForImsCapabilityChanged(Handler h) {
+ mImsCapabilityChangedRegistrants.remove(h);
+ }
+
+ /**
* Clean up existing voice and data connection then turn off radio power.
*
* Hang up the existing voice calls to decrease call drop rate.
@@ -3892,19 +4102,37 @@
return earfcnPairList;
}
- private void updateLteEarfcnLists() {
+
+ private void onCarrierConfigChanged() {
CarrierConfigManager configManager = (CarrierConfigManager)
mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
- PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
+ PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId());
+
+ if (config != null) {
+ updateLteEarfcnLists(config);
+ updateReportingCriteria(config);
+ }
+ }
+
+ private void updateLteEarfcnLists(PersistableBundle config) {
synchronized (mLteRsrpBoostLock) {
- mLteRsrpBoost = b.getInt(CarrierConfigManager.KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
- String[] earfcnsStringArrayForRsrpBoost = b.getStringArray(
+ mLteRsrpBoost = config.getInt(CarrierConfigManager.KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
+ String[] earfcnsStringArrayForRsrpBoost = config.getStringArray(
CarrierConfigManager.KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY);
mEarfcnPairListForRsrpBoost = convertEarfcnStringArrayToPairList(
earfcnsStringArrayForRsrpBoost);
}
}
+ private void updateReportingCriteria(PersistableBundle config) {
+ mPhone.setSignalStrengthReportingCriteria(
+ config.getIntArray(CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY),
+ AccessNetworkType.EUTRAN);
+ mPhone.setSignalStrengthReportingCriteria(
+ config.getIntArray(CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY),
+ AccessNetworkType.UTRAN);
+ }
+
private void updateServiceStateLteEarfcnBoost(ServiceState serviceState, int lteEarfcn) {
synchronized (mLteRsrpBoostLock) {
if ((lteEarfcn != INVALID_LTE_EARFCN)
@@ -3923,18 +4151,6 @@
* @return true if the signal strength changed and a notification was sent.
*/
protected boolean onSignalStrengthResult(AsyncResult ar) {
- boolean isGsm = false;
- int dataRat = mSS.getRilDataRadioTechnology();
- int voiceRat = mSS.getRilVoiceRadioTechnology();
-
- // Override isGsm based on currently camped data and voice RATs
- // Set isGsm to true if the RAT belongs to GSM family and not IWLAN
- if ((dataRat != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
- && ServiceState.isGsm(dataRat))
- || (voiceRat != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
- && ServiceState.isGsm(voiceRat))) {
- isGsm = true;
- }
// This signal is used for both voice and data radio signal so parse
// all fields
@@ -3942,12 +4158,6 @@
if ((ar.exception == null) && (ar.result != null)) {
mSignalStrength = (SignalStrength) ar.result;
mSignalStrength.validateInput();
- if (dataRat == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
- && voiceRat == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
- mSignalStrength.fixType();
- } else {
- mSignalStrength.setGsm(isGsm);
- }
mSignalStrength.setLteRsrpBoost(mSS.getLteEarfcnRsrpBoost());
PersistableBundle config = getCarrierConfig();
@@ -3961,14 +4171,39 @@
CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY));
} else {
log("onSignalStrengthResult() Exception from RIL : " + ar.exception);
- mSignalStrength = new SignalStrength(isGsm);
+ mSignalStrength = new SignalStrength(true);
}
+ updateRatTypeForSignalStrength();
boolean ssChanged = notifySignalStrength();
return ssChanged;
}
+ private void updateRatTypeForSignalStrength() {
+ if (mSignalStrength != null) {
+ boolean isGsm = false;
+ int dataRat = mSS.getRilDataRadioTechnology();
+ int voiceRat = mSS.getRilVoiceRadioTechnology();
+
+ // Override isGsm based on currently camped data and voice RATs
+ // Set isGsm to true if the RAT belongs to GSM family and not IWLAN
+ if ((dataRat != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ && ServiceState.isGsm(dataRat))
+ || (voiceRat != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ && ServiceState.isGsm(voiceRat))) {
+ isGsm = true;
+ }
+
+ if (dataRat == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
+ && voiceRat == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
+ mSignalStrength.fixType();
+ } else {
+ mSignalStrength.setGsm(isGsm);
+ }
+ }
+ }
+
/**
* Hang up all voice call and turn off radio. Implemented by derived class.
*/
@@ -4231,6 +4466,8 @@
pw.println(" mLteRsrpBoost=" + mLteRsrpBoost);
dumpEarfcnPairList(pw);
+ mLocaleTracker.dump(fd, pw, args);
+
pw.println(" Roaming Log:");
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.increaseIndent();
@@ -4283,15 +4520,6 @@
return value;
}
- protected void updateCarrierMccMncConfiguration(String newOp, String oldOp, Context context) {
- // if we have a change in operator, notify wifi (even to/from none)
- if (((newOp == null) && (TextUtils.isEmpty(oldOp) == false)) ||
- ((newOp != null) && (newOp.equals(oldOp) == false))) {
- log("update mccmnc=" + newOp + " fromServiceState=true");
- MccTable.updateMccMncConfiguration(context, newOp, true);
- }
- }
-
/**
* Check ISO country by MCC to see if phone is roaming in same registered country
*/
@@ -4308,8 +4536,8 @@
boolean inSameCountry = true;
final String networkMCC = operatorNumeric.substring(0, 3);
final String homeMCC = homeNumeric.substring(0, 3);
- final String networkCountry = MccTable.countryCodeForMcc(Integer.parseInt(networkMCC));
- final String homeCountry = MccTable.countryCodeForMcc(Integer.parseInt(homeMCC));
+ final String networkCountry = MccTable.countryCodeForMcc(networkMCC);
+ final String homeCountry = MccTable.countryCodeForMcc(homeMCC);
if (networkCountry.isEmpty() || homeCountry.isEmpty()) {
// Not a valid country
return false;
@@ -4550,4 +4778,8 @@
// Return static default defined in CarrierConfigManager.
return CarrierConfigManager.getDefaultConfig();
}
+
+ public LocaleTracker getLocaleTracker() {
+ return mLocaleTracker;
+ }
}
diff --git a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
index f2f2aa3..4fb02f0 100644
--- a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
+++ b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
@@ -23,9 +23,11 @@
import android.content.IntentFilter;
import android.database.Cursor;
import android.database.SQLException;
-import android.os.UserHandle;
+import android.os.PersistableBundle;
import android.os.UserManager;
+import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
@@ -45,7 +47,7 @@
private static final boolean DBG = InboundSmsHandler.DBG;
/** Delete any partial message segments older than 30 days. */
- static final long PARTIAL_SEGMENT_EXPIRE_AGE = (long) (60 * 60 * 1000) * 24 * 30;
+ static final long DEFAULT_PARTIAL_SEGMENT_EXPIRE_AGE = (long) (60 * 60 * 1000) * 24 * 30;
/**
* Query projection for dispatching pending messages at boot time.
@@ -97,7 +99,7 @@
@Override
public void run() {
- scanRawTable();
+ scanRawTable(context);
InboundSmsHandler.cancelNewMessageNotification(context);
}
}
@@ -140,7 +142,7 @@
/**
* Scan the raw table for complete SMS messages to broadcast, and old PDUs to delete.
*/
- private void scanRawTable() {
+ private void scanRawTable(Context context) {
if (DBG) Rlog.d(TAG, "scanning raw table for undelivered messages");
long startTime = System.nanoTime();
HashMap<SmsReferenceKey, Integer> multiPartReceivedCount =
@@ -176,8 +178,9 @@
Integer receivedCount = multiPartReceivedCount.get(reference);
if (receivedCount == null) {
multiPartReceivedCount.put(reference, 1); // first segment seen
+ long expirationTime = getUndeliveredSmsExpirationTime(context);
if (tracker.getTimestamp() <
- (System.currentTimeMillis() - PARTIAL_SEGMENT_EXPIRE_AGE)) {
+ (System.currentTimeMillis() - expirationTime)) {
// older than 30 days; delete if we don't find all the segments
oldMultiPartMessages.add(reference);
}
@@ -236,6 +239,20 @@
}
}
+ private long getUndeliveredSmsExpirationTime(Context context) {
+ int subId = SubscriptionManager.getDefaultSmsSubscriptionId();
+ CarrierConfigManager configManager =
+ (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ PersistableBundle bundle = configManager.getConfigForSubId(subId);
+
+ if (bundle != null) {
+ return bundle.getLong(CarrierConfigManager.KEY_UNDELIVERED_SMS_MESSAGE_EXPIRATION_TIME,
+ DEFAULT_PARTIAL_SEGMENT_EXPIRE_AGE);
+ } else {
+ return DEFAULT_PARTIAL_SEGMENT_EXPIRE_AGE;
+ }
+ }
+
/**
* Used as the HashMap key for matching concatenated message segments.
*/
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index 9eae37d..d8906e7 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -32,6 +32,7 @@
import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
@@ -40,6 +41,8 @@
import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
import com.android.internal.telephony.gsm.GsmSMSDispatcher;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
@@ -186,13 +189,17 @@
*/
@VisibleForTesting
public void injectSmsPdu(byte[] pdu, String format, SmsInjectionCallback callback) {
- injectSmsPdu(pdu, format, callback, false /* ignoreClass */);
+ // TODO We need to decide whether we should allow injecting GSM(3gpp)
+ // SMS pdus when the phone is camping on CDMA(3gpp2) network and vice versa.
+ android.telephony.SmsMessage msg =
+ android.telephony.SmsMessage.createFromPdu(pdu, format);
+ injectSmsPdu(msg, format, callback, false /* ignoreClass */);
}
/**
* Inject an SMS PDU into the android platform.
*
- * @param pdu is the byte array of pdu to be injected into android telephony layer
+ * @param msg is the {@link SmsMessage} to be injected into android telephony layer
* @param format is the format of SMS pdu (3gpp or 3gpp2)
* @param callback if not NULL this callback is triggered when the message is successfully
* received by the android telephony layer. This callback is triggered at
@@ -200,15 +207,10 @@
* @param ignoreClass if set to false, this method will inject class 1 sms only.
*/
@VisibleForTesting
- public void injectSmsPdu(byte[] pdu, String format, SmsInjectionCallback callback,
+ public void injectSmsPdu(SmsMessage msg, String format, SmsInjectionCallback callback,
boolean ignoreClass) {
Rlog.d(TAG, "SmsDispatchersController:injectSmsPdu");
try {
- // TODO We need to decide whether we should allow injecting GSM(3gpp)
- // SMS pdus when the phone is camping on CDMA(3gpp2) network and vice versa.
- android.telephony.SmsMessage msg =
- android.telephony.SmsMessage.createFromPdu(pdu, format);
-
if (msg == null) {
Rlog.e(TAG, "injectSmsPdu: createFromPdu returned null");
callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
@@ -449,7 +451,7 @@
PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
String callingPkg, boolean persistMessage, int priority,
boolean expectMore, int validityPeriod) {
- if (mImsSmsDispatcher.isAvailable()) {
+ if (mImsSmsDispatcher.isAvailable() || mImsSmsDispatcher.isEmergencySmsSupport(destAddr)) {
mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
messageUri, callingPkg, persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
false /*expectMore*/, SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
@@ -627,4 +629,9 @@
public interface SmsInjectionCallback {
void onSmsInjectedResult(int result);
}
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mGsmInboundSmsHandler.dump(fd, pw, args);
+ mCdmaInboundSmsHandler.dump(fd, pw, args);
+ }
}
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 43faef5..1c5b35e 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -64,7 +64,6 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
@@ -94,7 +93,7 @@
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();
+ private final List<SubscriptionInfo> mCacheActiveSubInfoList = new ArrayList<>();
/**
* Copied from android.util.LocalLog with flush() adding flush and line number
@@ -163,6 +162,7 @@
private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
private int[] colorArr;
+ private long mLastISubServiceRegTime;
public static SubscriptionController init(Phone phone) {
synchronized (SubscriptionController.class) {
@@ -208,7 +208,8 @@
mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
if(ServiceManager.getService("isub") == null) {
- ServiceManager.addService("isub", this);
+ ServiceManager.addService("isub", this);
+ mLastISubServiceRegTime = System.currentTimeMillis();
}
if (DBG) logdl("[SubscriptionController] init by Context");
@@ -289,10 +290,10 @@
// Get the blank bitmap for this SubInfoRecord
Bitmap iconBitmap = BitmapFactory.decodeResource(mContext.getResources(),
com.android.internal.R.drawable.ic_sim_card_multi_24px_clr);
- int mcc = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.MCC));
- int mnc = cursor.getInt(cursor.getColumnIndexOrThrow(
- SubscriptionManager.MNC));
+ String mcc = cursor.getString(cursor.getColumnIndexOrThrow(
+ SubscriptionManager.MCC_STRING));
+ String mnc = cursor.getString(cursor.getColumnIndexOrThrow(
+ SubscriptionManager.MNC_STRING));
String cardId = cursor.getString(cursor.getColumnIndexOrThrow(
SubscriptionManager.CARD_ID));
// FIXME: consider stick this into database too
@@ -368,7 +369,7 @@
subList = new ArrayList<SubscriptionInfo>();
}
subList.add(subInfo);
- }
+ }
}
} else {
if (DBG) logd("Query fail");
@@ -418,7 +419,7 @@
@Override
public SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getActiveSubscriptionInfo")) {
+ mContext, subId, callingPackage, "getActiveSubscriptionInfo")) {
return null;
}
@@ -457,12 +458,29 @@
*/
@Override
public SubscriptionInfo getActiveSubscriptionInfoForIccId(String iccId, String callingPackage) {
+ // Query the subscriptions unconditionally, and then check whether the caller has access to
+ // the given subscription.
+ final SubscriptionInfo si = getActiveSubscriptionInfoForIccIdInternal(iccId);
+
+ final int subId = si != null
+ ? si.getSubscriptionId() : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getActiveSubscriptionInfoForIccId") || iccId == null) {
+ mContext, subId, callingPackage, "getActiveSubscriptionInfoForIccId")) {
return null;
}
- // Now that all security checks passes, perform the operation as ourselves.
+ return si;
+ }
+
+ /**
+ * Get the active SubscriptionInfo associated with the given iccId. The caller *must* perform
+ * permission checks when using this method.
+ */
+ private SubscriptionInfo getActiveSubscriptionInfoForIccIdInternal(String iccId) {
+ if (iccId == null) {
+ return null;
+ }
+
final long identity = Binder.clearCallingIdentity();
try {
List<SubscriptionInfo> subList = getActiveSubscriptionInfoList(
@@ -496,8 +514,16 @@
@Override
public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex,
String callingPackage) {
+ Phone phone = PhoneFactory.getPhone(slotIndex);
+ if (phone == null) {
+ if (DBG) {
+ loge("[getActiveSubscriptionInfoForSimSlotIndex] no phone, slotIndex=" + slotIndex);
+ }
+ return null;
+ }
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getActiveSubscriptionInfoForSimSlotIndex")) {
+ mContext, phone.getSubId(), callingPackage,
+ "getActiveSubscriptionInfoForSimSlotIndex")) {
return null;
}
@@ -542,8 +568,11 @@
public List<SubscriptionInfo> getAllSubInfoList(String callingPackage) {
if (DBG) logd("[getAllSubInfoList]+");
+ // This API isn't public, so no need to provide a valid subscription ID - we're not worried
+ // about carrier-privileged callers not having access.
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getAllSubInfoList")) {
+ mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
+ "getAllSubInfoList")) {
return null;
}
@@ -570,81 +599,71 @@
*/
@Override
public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage) {
-
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getActiveSubscriptionInfoList")) {
+ if (!isSubInfoReady()) {
+ if (DBG) logdl("[getActiveSubInfoList] Sub Controller not ready");
return null;
}
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
+ boolean canReadAllPhoneState;
try {
- if (!isSubInfoReady()) {
- if (DBG) logdl("[getActiveSubInfoList] Sub Controller not ready");
- return null;
+ canReadAllPhoneState = TelephonyPermissions.checkReadPhoneState(mContext,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(),
+ Binder.getCallingUid(), callingPackage, "getActiveSubscriptionInfoList");
+ } catch (SecurityException e) {
+ canReadAllPhoneState = false;
+ }
+
+ synchronized (mCacheActiveSubInfoList) {
+ // If the caller can read all phone state, just return the full list.
+ if (canReadAllPhoneState) {
+ return new ArrayList<>(mCacheActiveSubInfoList);
}
- // 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);
+ // Filter the list to only include subscriptions which the caller can manage.
+ return mCacheActiveSubInfoList.stream()
+ .filter(subscriptionInfo -> {
+ try {
+ return TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext,
+ subscriptionInfo.getSubscriptionId(), callingPackage,
+ "getActiveSubscriptionInfoList");
+ } catch (SecurityException e) {
+ return false;
+ }
+ })
+ .collect(Collectors.toList());
}
}
/**
* 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);
-
- if (subList != null) {
- // FIXME: Unnecessary when an insertion sort is used!
- subList.sort(SUBSCRIPTION_INFO_COMPARATOR);
-
- if (DBG_CACHE) {
- logdl("[refreshCachedActiveSubscriptionInfoList]- " + subList.size()
- + " infos return");
- }
- } else {
- if (DBG_CACHE) logdl("[refreshCachedActiveSubscriptionInfoList]- no info return");
- }
-
+ @VisibleForTesting // For mockito to mock this method
+ public void refreshCachedActiveSubscriptionInfoList() {
+ if (!isSubInfoReady()) {
if (DBG_CACHE) {
- for (SubscriptionInfo si : subList) {
- logd("[refreshCachedActiveSubscriptionInfoList] Setting Cached subInfo=" + si);
+ logdl("[refreshCachedActiveSubscriptionInfoList] "
+ + "Sub Controller not ready ");
+ }
+ return;
+ }
+
+ synchronized (mCacheActiveSubInfoList) {
+ mCacheActiveSubInfoList.clear();
+ List<SubscriptionInfo> activeSubscriptionInfoList = getSubInfo(
+ SubscriptionManager.SIM_SLOT_INDEX + ">=0", null);
+ if (activeSubscriptionInfoList != null) {
+ mCacheActiveSubInfoList.addAll(activeSubscriptionInfoList);
+ }
+ if (DBG_CACHE) {
+ if (!mCacheActiveSubInfoList.isEmpty()) {
+ for (SubscriptionInfo si : mCacheActiveSubInfoList) {
+ logd("[refreshCachedActiveSubscriptionInfoList] Setting Cached info="
+ + si);
+ }
+ } else {
+ logdl("[refreshCachedActiveSubscriptionInfoList]- no info return");
}
}
- mCacheActiveSubInfoList.set(subList);
-
- } finally {
- Binder.restoreCallingIdentity(identity);
}
}
@@ -655,25 +674,14 @@
*/
@Override
public int getActiveSubInfoCount(String callingPackage) {
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getActiveSubInfoCount")) {
+ // Let getActiveSubscriptionInfoList perform permission checks / filtering.
+ List<SubscriptionInfo> records = getActiveSubscriptionInfoList(callingPackage);
+ if (records == null) {
+ if (VDBG) logd("[getActiveSubInfoCount] records null");
return 0;
}
-
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
- try {
- List<SubscriptionInfo> records = getActiveSubscriptionInfoList(
- mContext.getOpPackageName());
- if (records == null) {
- if (VDBG) logd("[getActiveSubInfoCount] records null");
- return 0;
- }
- if (VDBG) logd("[getActiveSubInfoCount]- count: " + records.size());
- return records.size();
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ if (VDBG) logd("[getActiveSubInfoCount]- count: " + records.size());
+ return records.size();
}
/**
@@ -685,8 +693,11 @@
public int getAllSubInfoCount(String callingPackage) {
if (DBG) logd("[getAllSubInfoCount]+");
+ // This API isn't public, so no need to provide a valid subscription ID - we're not worried
+ // about carrier-privileged callers not having access.
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getAllSubInfoCount")) {
+ mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
+ "getAllSubInfoCount")) {
return 0;
}
@@ -725,8 +736,11 @@
@Override
public List<SubscriptionInfo> getAvailableSubscriptionInfoList(String callingPackage) {
+ // This API isn't public, so no need to provide a valid subscription ID - we're not worried
+ // about carrier-privileged callers not having access.
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getAvailableSubscriptionInfoList")) {
+ mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
+ "getAvailableSubscriptionInfoList")) {
throw new SecurityException("Need READ_PHONE_STATE to call "
+ " getAvailableSubscriptionInfoList");
}
@@ -1359,18 +1373,22 @@
* @return the number of records updated
*/
public int setMccMnc(String mccMnc, int subId) {
+ String mccString = mccMnc.substring(0, 3);
+ String mncString = mccMnc.substring(3);
int mcc = 0;
int mnc = 0;
try {
- mcc = Integer.parseInt(mccMnc.substring(0,3));
- mnc = Integer.parseInt(mccMnc.substring(3));
+ mcc = Integer.parseInt(mccString);
+ mnc = Integer.parseInt(mncString);
} catch (NumberFormatException e) {
loge("[setMccMnc] - couldn't parse mcc/mnc: " + mccMnc);
}
if (DBG) logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId);
- ContentValues value = new ContentValues(2);
+ ContentValues value = new ContentValues(4);
value.put(SubscriptionManager.MCC, mcc);
value.put(SubscriptionManager.MNC, mnc);
+ value.put(SubscriptionManager.MCC_STRING, mccString);
+ value.put(SubscriptionManager.MNC_STRING, mncString);
int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
@@ -1781,7 +1799,7 @@
mDefaultFallbackSubId = subId;
// Update MCC MNC device configuration information
String defaultMccMnc = mTelephonyManager.getSimOperatorNumericForPhone(phoneId);
- MccTable.updateMccMncConfiguration(mContext, defaultMccMnc, false);
+ MccTable.updateMccMncConfiguration(mContext, defaultMccMnc);
// Broadcast an Intent for default sub change
Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
@@ -1862,58 +1880,48 @@
return subIds[0];
}
- public List<SubscriptionInfo> getSubInfoUsingSlotIndexWithCheck(int slotIndex,
- boolean needCheck,
- String callingPackage) {
- if (DBG) logd("[getSubInfoUsingSlotIndexWithCheck]+ slotIndex:" + slotIndex);
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getSubInfoUsingSlotIndexWithCheck")) {
+ /** Must be public for access from instrumentation tests. */
+ @VisibleForTesting
+ public List<SubscriptionInfo> getSubInfoUsingSlotIndexPrivileged(int slotIndex,
+ boolean needCheck) {
+ if (DBG) logd("[getSubInfoUsingSlotIndexPrivileged]+ slotIndex:" + slotIndex);
+ if (slotIndex == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
+ slotIndex = getSlotIndex(getDefaultSubId());
+ }
+ if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
+ if (DBG) logd("[getSubInfoUsingSlotIndexPrivileged]- invalid slotIndex");
return null;
}
- // Now that all security checks passes, perform the operation as ourselves.
- final long identity = Binder.clearCallingIdentity();
+ if (needCheck && !isSubInfoReady()) {
+ if (DBG) logd("[getSubInfoUsingSlotIndexPrivileged]- not ready");
+ return null;
+ }
+
+ Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
+ null, SubscriptionManager.SIM_SLOT_INDEX + "=?",
+ new String[]{String.valueOf(slotIndex)}, null);
+ ArrayList<SubscriptionInfo> subList = null;
try {
- if (slotIndex == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
- slotIndex = getSlotIndex(getDefaultSubId());
- }
- if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
- if (DBG) logd("[getSubInfoUsingSlotIndexWithCheck]- invalid slotIndex");
- return null;
- }
-
- if (needCheck && !isSubInfoReady()) {
- if (DBG) logd("[getSubInfoUsingSlotIndexWithCheck]- not ready");
- return null;
- }
-
- Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
- null, SubscriptionManager.SIM_SLOT_INDEX + "=?",
- new String[]{String.valueOf(slotIndex)}, null);
- ArrayList<SubscriptionInfo> subList = null;
- try {
- if (cursor != null) {
- while (cursor.moveToNext()) {
- SubscriptionInfo subInfo = getSubInfoRecord(cursor);
- if (subInfo != null) {
- if (subList == null) {
- subList = new ArrayList<SubscriptionInfo>();
- }
- subList.add(subInfo);
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ SubscriptionInfo subInfo = getSubInfoRecord(cursor);
+ if (subInfo != null) {
+ if (subList == null) {
+ subList = new ArrayList<SubscriptionInfo>();
}
+ subList.add(subInfo);
}
}
- } finally {
- if (cursor != null) {
- cursor.close();
- }
}
- if (DBG) logd("[getSubInfoUsingSlotIndex]- null info return");
-
- return subList;
} finally {
- Binder.restoreCallingIdentity(identity);
+ if (cursor != null) {
+ cursor.close();
+ }
}
+ if (DBG) logd("[getSubInfoUsingSlotIndex]- null info return");
+
+ return subList;
}
private void validateSubId(int subId) {
@@ -2059,7 +2067,7 @@
@Override
public String getSubscriptionProperty(int subId, String propKey, String callingPackage) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, callingPackage, "getSubInfoUsingSlotIndexWithCheck")) {
+ mContext, subId, callingPackage, "getSubscriptionProperty")) {
return null;
}
String resultValue = null;
@@ -2133,6 +2141,7 @@
final long token = Binder.clearCallingIdentity();
try {
pw.println("SubscriptionController:");
+ pw.println(" mLastISubServiceRegTime=" + mLastISubServiceRegTime);
pw.println(" defaultSubId=" + getDefaultSubId());
pw.println(" defaultDataSubId=" + getDefaultDataSubId());
pw.println(" defaultVoiceSubId=" + getDefaultVoiceSubId());
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index 723311b..c16ba7d 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -20,12 +20,10 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.UserSwitchObserver;
-import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.IPackageManager;
import android.os.AsyncResult;
@@ -55,7 +53,6 @@
import com.android.internal.telephony.euicc.EuiccController;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IccUtils;
-import com.android.internal.telephony.uicc.UiccProfile;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -69,6 +66,7 @@
private static final String LOG_TAG = "SubscriptionInfoUpdater";
private static final int PROJECT_SIM_NUM = TelephonyManager.getDefault().getPhoneCount();
+ private static final int EVENT_INVALID = -1;
private static final int EVENT_GET_NETWORK_SELECTION_MODE_DONE = 2;
private static final int EVENT_SIM_LOADED = 3;
private static final int EVENT_SIM_ABSENT = 4;
@@ -133,9 +131,6 @@
mEuiccManager = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE);
mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
- IntentFilter intentFilter = new IntentFilter(UiccProfile.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mContext.registerReceiver(sReceiver, intentFilter);
-
mCarrierServiceBindHelper = new CarrierServiceBindHelper(mContext);
initializeCarrierApps();
}
@@ -173,56 +168,31 @@
mCurrentlyActiveUserId);
}
- private final BroadcastReceiver sReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- logd("[Receiver]+");
- String action = intent.getAction();
- logd("Action: " + action);
-
- if (!action.equals(UiccProfile.ACTION_INTERNAL_SIM_STATE_CHANGED)) {
- return;
- }
-
- int slotIndex = intent.getIntExtra(PhoneConstants.PHONE_KEY,
- SubscriptionManager.INVALID_SIM_SLOT_INDEX);
- logd("slotIndex: " + slotIndex);
- if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
- logd("ACTION_INTERNAL_SIM_STATE_CHANGED contains invalid slotIndex: " + slotIndex);
- return;
- }
-
- String simStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
- logd("simStatus: " + simStatus);
-
- if (action.equals(UiccProfile.ACTION_INTERNAL_SIM_STATE_CHANGED)) {
- if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus)) {
- sendMessage(obtainMessage(EVENT_SIM_ABSENT, slotIndex, -1));
- } else if (IccCardConstants.INTENT_VALUE_ICC_UNKNOWN.equals(simStatus)) {
- sendMessage(obtainMessage(EVENT_SIM_UNKNOWN, slotIndex, -1));
- } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(simStatus)) {
- sendMessage(obtainMessage(EVENT_SIM_IO_ERROR, slotIndex, -1));
- } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED.equals(simStatus)) {
- sendMessage(obtainMessage(EVENT_SIM_RESTRICTED, slotIndex, -1));
- } else if (IccCardConstants.INTENT_VALUE_ICC_NOT_READY.equals(simStatus)) {
- sendEmptyMessage(EVENT_SIM_NOT_READY);
- } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(simStatus)) {
- String reason = intent.getStringExtra(
- IccCardConstants.INTENT_KEY_LOCKED_REASON);
- sendMessage(obtainMessage(EVENT_SIM_LOCKED, slotIndex, -1, reason));
- } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus)) {
- sendMessage(obtainMessage(EVENT_SIM_LOADED, slotIndex, -1));
- } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(simStatus)) {
- sendMessage(obtainMessage(EVENT_SIM_READY, slotIndex, -1));
- } else if (IccCardConstants.INTENT_VALUE_ICC_IMSI.equals(simStatus)) {
- sendMessage(obtainMessage(EVENT_SIM_IMSI, slotIndex, -1));
- } else {
- logd("Ignoring simStatus: " + simStatus);
- }
- }
- logd("[Receiver]-");
+ public void updateInternalIccState(String simStatus, String reason, int slotId) {
+ logd("updateInternalIccState to simStatus " + simStatus + " reason " + reason
+ + " slotId " + slotId);
+ int message = internalIccStateToMessage(simStatus);
+ if (message != EVENT_INVALID) {
+ sendMessage(obtainMessage(message, slotId, -1, reason));
}
- };
+ }
+
+ private int internalIccStateToMessage(String simStatus) {
+ switch(simStatus) {
+ case IccCardConstants.INTENT_VALUE_ICC_ABSENT: return EVENT_SIM_ABSENT;
+ case IccCardConstants.INTENT_VALUE_ICC_UNKNOWN: return EVENT_SIM_UNKNOWN;
+ case IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR: return EVENT_SIM_IO_ERROR;
+ case IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED: return EVENT_SIM_RESTRICTED;
+ case IccCardConstants.INTENT_VALUE_ICC_NOT_READY: return EVENT_SIM_NOT_READY;
+ case IccCardConstants.INTENT_VALUE_ICC_LOCKED: return EVENT_SIM_LOCKED;
+ case IccCardConstants.INTENT_VALUE_ICC_LOADED: return EVENT_SIM_LOADED;
+ case IccCardConstants.INTENT_VALUE_ICC_READY: return EVENT_SIM_READY;
+ case IccCardConstants.INTENT_VALUE_ICC_IMSI: return EVENT_SIM_IMSI;
+ default:
+ logd("Ignoring simStatus: " + simStatus);
+ return EVENT_INVALID;
+ }
+ }
private boolean isAllIccIdQueryDone() {
for (int i = 0; i < PROJECT_SIM_NUM; i++) {
@@ -325,16 +295,6 @@
sendMessage(obtainMessage(EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS, callback));
}
- private static class QueryIccIdUserObj {
- public String reason;
- public int slotId;
-
- QueryIccIdUserObj(String reason, int slotId) {
- this.reason = reason;
- this.slotId = slotId;
- }
- };
-
private void handleSimLocked(int slotId, String reason) {
if (mIccId[slotId] != null && mIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
logd("SIM" + (slotId + 1) + " hot plug in");
@@ -345,12 +305,12 @@
if (iccId == null) {
IccCard iccCard = mPhone[slotId].getIccCard();
if (iccCard == null) {
- logd("handleSimLoaded: IccCard null");
+ logd("handleSimLocked: IccCard null");
return;
}
IccRecords records = iccCard.getIccRecords();
if (records == null) {
- logd("handleSimLoaded: IccRecords null");
+ logd("handleSimLocked: IccRecords null");
return;
}
if (IccUtils.stripTrailingFs(records.getFullIccId()) == null) {
@@ -422,7 +382,7 @@
if (!TextUtils.isEmpty(operator)) {
if (subId == SubscriptionController.getInstance().getDefaultSubId()) {
- MccTable.updateMccMncConfiguration(mContext, operator, false);
+ MccTable.updateMccMncConfiguration(mContext, operator);
}
SubscriptionController.getInstance().setMccMnc(operator, subId);
} else {
@@ -603,9 +563,8 @@
String[] decIccId = new String[PROJECT_SIM_NUM];
for (int i = 0; i < PROJECT_SIM_NUM; i++) {
oldIccId[i] = null;
- List<SubscriptionInfo> oldSubInfo =
- SubscriptionController.getInstance().getSubInfoUsingSlotIndexWithCheck(i, false,
- mContext.getOpPackageName());
+ List<SubscriptionInfo> oldSubInfo = SubscriptionController.getInstance()
+ .getSubInfoUsingSlotIndexPrivileged(i, false);
decIccId[i] = IccUtils.getDecimalSubstring(mIccId[i]);
if (oldSubInfo != null && oldSubInfo.size() > 0) {
oldIccId[i] = oldSubInfo.get(0).getIccId();
@@ -642,8 +601,6 @@
}
//check if the inserted SIM is new SIM
- int nNewCardCount = 0;
- int nNewSimStatus = 0;
for (int i = 0; i < PROJECT_SIM_NUM; i++) {
if (mInsertSimState[i] == SIM_NOT_INSERT) {
logd("updateSubscriptionInfoByIccId: No SIM inserted in slot " + i + " this time");
@@ -655,25 +612,11 @@
+ Integer.toString(mInsertSimState[i]), i);
logd("SUB" + (i + 1) + " has invalid IccId");
} else /*if (sInsertSimState[i] != SIM_NOT_INSERT)*/ {
+ logd("updateSubscriptionInfoByIccId: adding subscription info record: iccid: "
+ + mIccId[i] + "slot: " + i);
mSubscriptionManager.addSubscriptionInfoRecord(mIccId[i], i);
}
if (isNewSim(mIccId[i], decIccId[i], oldIccId)) {
- nNewCardCount++;
- switch (i) {
- case PhoneConstants.SUB1:
- nNewSimStatus |= STATUS_SIM1_INSERTED;
- break;
- case PhoneConstants.SUB2:
- nNewSimStatus |= STATUS_SIM2_INSERTED;
- break;
- case PhoneConstants.SUB3:
- nNewSimStatus |= STATUS_SIM3_INSERTED;
- break;
- //case PhoneConstants.SUB3:
- // nNewSimStatus |= STATUS_SIM4_INSERTED;
- // break;
- }
-
mInsertSimState[i] = SIM_NEW;
}
}
@@ -880,6 +823,7 @@
sSimCardState[phoneId] = state;
Intent i = new Intent(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ i.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
i.putExtra(TelephonyManager.EXTRA_SIM_STATE, state);
SubscriptionManager.putPhoneIdAndSubIdExtra(i, phoneId);
logd("Broadcasting intent ACTION_SIM_CARD_STATE_CHANGED " + simStateString(state)
@@ -897,6 +841,7 @@
&& sSimApplicationState[phoneId] == TelephonyManager.SIM_STATE_UNKNOWN))) {
sSimApplicationState[phoneId] = state;
Intent i = new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
+ i.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
i.putExtra(TelephonyManager.EXTRA_SIM_STATE, state);
SubscriptionManager.putPhoneIdAndSubIdExtra(i, phoneId);
@@ -937,11 +882,6 @@
}
}
- public void dispose() {
- logd("[dispose]");
- mContext.unregisterReceiver(sReceiver);
- }
-
private void logd(String message) {
Rlog.d(LOG_TAG, message);
}
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index e379a69..dc8d9fa 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -20,6 +20,7 @@
import android.database.Cursor;
import android.os.Handler;
import android.os.IDeviceIdleController;
+import android.os.Looper;
import android.os.ServiceManager;
import android.telephony.AccessNetworkConstants.TransportType;
@@ -29,7 +30,6 @@
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
-import com.android.internal.telephony.uicc.IccCardProxy;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccProfile;
@@ -66,10 +66,18 @@
}
/**
+ * Sets the NitzStateMachine implementation to use during implementation. This boolean
+ * should be removed once the new implementation is stable.
+ */
+ static final boolean USE_NEW_NITZ_STATE_MACHINE = true;
+
+ /**
* Returns a new {@link NitzStateMachine} instance.
*/
public NitzStateMachine makeNitzStateMachine(GsmCdmaPhone phone) {
- return new NitzStateMachine(phone);
+ return USE_NEW_NITZ_STATE_MACHINE
+ ? new NewNitzStateMachine(phone)
+ : new OldNitzStateMachine(phone);
}
public SimActivationTracker makeSimActivationTracker(Phone phone) {
@@ -100,16 +108,12 @@
return new IccSmsInterfaceManager(phone);
}
- public IccCardProxy makeIccCardProxy(Context context, CommandsInterface ci, int phoneId) {
- return new IccCardProxy(context, ci, phoneId);
- }
-
/**
* Create a new UiccProfile object.
*/
public UiccProfile makeUiccProfile(Context context, CommandsInterface ci, IccCardStatus ics,
- int phoneId, UiccCard uiccCard) {
- return new UiccProfile(context, ci, ics, phoneId, uiccCard);
+ int phoneId, UiccCard uiccCard, Object lock) {
+ return new UiccProfile(context, ci, ics, phoneId, uiccCard, lock);
}
public EriManager makeEriManager(Phone phone, Context context, int eriFileSource) {
@@ -177,4 +181,7 @@
return IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
}
+ public LocaleTracker makeLocaleTracker(Phone phone, Looper looper) {
+ return new LocaleTracker(phone, looper);
+ }
}
diff --git a/src/java/com/android/internal/telephony/TelephonyTester.java b/src/java/com/android/internal/telephony/TelephonyTester.java
index d4e0c6b..52235e4 100644
--- a/src/java/com/android/internal/telephony/TelephonyTester.java
+++ b/src/java/com/android/internal/telephony/TelephonyTester.java
@@ -23,7 +23,6 @@
import android.net.Uri;
import android.os.BadParcelableException;
import android.os.Build;
-import android.telephony.NetworkRegistrationState;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.ims.ImsCallProfile;
@@ -115,6 +114,7 @@
private static final String EXTRA_DATA_REG_STATE = "data_reg_state";
private static final String EXTRA_VOICE_ROAMING_TYPE = "voice_roaming_type";
private static final String EXTRA_DATA_ROAMING_TYPE = "data_roaming_type";
+ private static final String EXTRA_OPERATOR = "operator";
private static final String ACTION_RESET = "reset";
@@ -341,13 +341,13 @@
}
if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_REG_STATE)) {
ss.setVoiceRegState(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_REG_STATE,
- NetworkRegistrationState.REG_STATE_UNKNOWN));
- log("Override voice reg state with " + ss.getVoiceRegState());
+ ServiceState.STATE_OUT_OF_SERVICE));
+ log("Override voice service state with " + ss.getVoiceRegState());
}
if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_REG_STATE)) {
ss.setDataRegState(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE,
- NetworkRegistrationState.REG_STATE_UNKNOWN));
- log("Override data reg state with " + ss.getDataRegState());
+ ServiceState.STATE_OUT_OF_SERVICE));
+ log("Override data service state with " + ss.getDataRegState());
}
if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_RAT)) {
ss.setRilVoiceRadioTechnology(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_RAT,
@@ -369,5 +369,10 @@
ServiceState.ROAMING_TYPE_UNKNOWN));
log("Override data roaming type with " + ss.getDataRoamingType());
}
+ if (mServiceStateTestIntent.hasExtra(EXTRA_OPERATOR)) {
+ String operator = mServiceStateTestIntent.getStringExtra(EXTRA_OPERATOR);
+ ss.setOperatorName(operator, operator, "");
+ log("Override operator with " + operator);
+ }
}
}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/UiccSmsController.java b/src/java/com/android/internal/telephony/UiccSmsController.java
index 59449b8..5fb2ff3 100644
--- a/src/java/com/android/internal/telephony/UiccSmsController.java
+++ b/src/java/com/android/internal/telephony/UiccSmsController.java
@@ -24,7 +24,6 @@
import android.content.Context;
import android.net.Uri;
import android.os.Binder;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
@@ -33,13 +32,16 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
/**
- * UiccSmsController to provide an inter-process communication to
- * access Sms in Icc.
+ * Implements the ISmsImplBase interface used in the SmsManager API.
*/
-public class UiccSmsController extends ISms.Stub {
+public class UiccSmsController extends ISmsImplBase {
static final String LOG_TAG = "RIL_UiccSmsController";
protected UiccSmsController() {
@@ -57,9 +59,8 @@
}
@Override
- public boolean
- updateMessageOnIccEfForSubscriber(int subId, String callingPackage, int index, int status,
- byte[] pdu) throws android.os.RemoteException {
+ public boolean updateMessageOnIccEfForSubscriber(int subId, String callingPackage, int index,
+ int status, byte[] pdu) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
return iccSmsIntMgr.updateMessageOnIccEf(callingPackage, index, status, pdu);
@@ -72,7 +73,7 @@
@Override
public boolean copyMessageToIccEfForSubscriber(int subId, String callingPackage, int status,
- byte[] pdu, byte[] smsc) throws android.os.RemoteException {
+ byte[] pdu, byte[] smsc) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
return iccSmsIntMgr.copyMessageToIccEf(callingPackage, status, pdu, smsc);
@@ -84,8 +85,7 @@
}
@Override
- public List<SmsRawData> getAllMessagesFromIccEfForSubscriber(int subId, String callingPackage)
- throws android.os.RemoteException {
+ public List<SmsRawData> getAllMessagesFromIccEfForSubscriber(int subId, String callingPackage) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
return iccSmsIntMgr.getAllMessagesFromIccEf(callingPackage);
@@ -112,6 +112,7 @@
}
}
+ @Override
public void sendDataForSubscriberWithSelfPermissions(int subId, String callingPackage,
String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
PendingIntent deliveryIntent) {
@@ -125,12 +126,6 @@
}
}
- public void sendText(String callingPackage, String destAddr, String scAddr,
- String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
- sendTextForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr, scAddr,
- text, sentIntent, deliveryIntent, true /* persistMessageForNonDefaultSmsApp*/);
- }
-
@Override
public void sendTextForSubscriber(int subId, String callingPackage, String destAddr,
String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
@@ -146,6 +141,7 @@
}
}
+ @Override
public void sendTextForSubscriberWithSelfPermissions(int subId, String callingPackage,
String destAddr, String scAddr, String text, PendingIntent sentIntent,
PendingIntent deliveryIntent, boolean persistMessage) {
@@ -174,19 +170,10 @@
}
}
- public void sendMultipartText(String callingPackage, String destAddr, String scAddr,
- List<String> parts, List<PendingIntent> sentIntents,
- List<PendingIntent> deliveryIntents) throws android.os.RemoteException {
- sendMultipartTextForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr,
- scAddr, parts, sentIntents, deliveryIntents,
- true /* persistMessageForNonDefaultSmsApp */);
- }
-
@Override
public void sendMultipartTextForSubscriber(int subId, String callingPackage, String destAddr,
String scAddr, List<String> parts, List<PendingIntent> sentIntents,
- List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp)
- throws android.os.RemoteException {
+ List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null ) {
iccSmsIntMgr.sendMultipartText(callingPackage, destAddr, scAddr, parts, sentIntents,
@@ -215,15 +202,14 @@
}
@Override
- public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
- throws android.os.RemoteException {
+ public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType) {
return enableCellBroadcastRangeForSubscriber(subId, messageIdentifier, messageIdentifier,
ranType);
}
@Override
public boolean enableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
- int endMessageId, int ranType) throws android.os.RemoteException {
+ int endMessageId, int ranType) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null ) {
return iccSmsIntMgr.enableCellBroadcastRange(startMessageId, endMessageId, ranType);
@@ -235,15 +221,15 @@
}
@Override
- public boolean disableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
- throws android.os.RemoteException {
+ public boolean disableCellBroadcastForSubscriber(int subId,
+ int messageIdentifier, int ranType) {
return disableCellBroadcastRangeForSubscriber(subId, messageIdentifier, messageIdentifier,
ranType);
}
@Override
public boolean disableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
- int endMessageId, int ranType) throws android.os.RemoteException {
+ int endMessageId, int ranType) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null ) {
return iccSmsIntMgr.disableCellBroadcastRange(startMessageId, endMessageId, ranType);
@@ -355,14 +341,6 @@
}
/**
- * Get sms interface manager object based on subscription.
- * @return ICC SMS manager
- */
- private @Nullable IccSmsInterfaceManager getIccSmsInterfaceManager(int subId) {
- return getPhone(subId).getIccSmsInterfaceManager();
- }
-
- /**
* Get User preferred SMS subscription
* @return User preferred SMS subscription
*/
@@ -382,7 +360,7 @@
@Override
public void sendStoredText(int subId, String callingPkg, Uri messageUri, String scAddress,
- PendingIntent sentIntent, PendingIntent deliveryIntent) throws RemoteException {
+ PendingIntent sentIntent, PendingIntent deliveryIntent) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null) {
iccSmsIntMgr.sendStoredText(callingPkg, messageUri, scAddress, sentIntent,
@@ -395,8 +373,8 @@
@Override
public void sendStoredMultipartText(int subId, String callingPkg, Uri messageUri,
- String scAddress, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents)
- throws RemoteException {
+ String scAddress, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null ) {
iccSmsIntMgr.sendStoredMultipartText(callingPkg, messageUri, scAddress, sentIntents,
@@ -413,6 +391,36 @@
return getPhone(subId).getAppSmsManager().createAppSpecificSmsToken(callingPkg, intent);
}
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ IndentingPrintWriter indentingPW =
+ new IndentingPrintWriter(pw, " " /* singleIndent */);
+ for (Phone phone : PhoneFactory.getPhones()) {
+ int subId = phone.getSubId();
+ indentingPW.println(String.format("SmsManager for subId = %d:", subId));
+ indentingPW.increaseIndent();
+ if (getIccSmsInterfaceManager(subId) != null) {
+ getIccSmsInterfaceManager(subId).dump(fd, indentingPW, args);
+ }
+ indentingPW.decreaseIndent();
+ }
+ indentingPW.flush();
+ }
+
+ public void sendText(String callingPackage, String destAddr, String scAddr,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ sendTextForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr, scAddr,
+ text, sentIntent, deliveryIntent, true /* persistMessageForNonDefaultSmsApp*/);
+ }
+
+ public void sendMultipartText(String callingPackage, String destAddr, String scAddr,
+ List<String> parts, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents) {
+ sendMultipartTextForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr,
+ scAddr, parts, sentIntents, deliveryIntents,
+ true /* persistMessageForNonDefaultSmsApp */);
+ }
+
private void sendErrorInPendingIntent(@Nullable PendingIntent intent, int errorCode) {
if (intent != null) {
try {
@@ -427,4 +435,12 @@
sendErrorInPendingIntent(intent, errorCode);
}
}
+
+ /**
+ * Get sms interface manager object based on subscription.
+ * @return ICC SMS manager
+ */
+ private @Nullable IccSmsInterfaceManager getIccSmsInterfaceManager(int subId) {
+ return getPhone(subId).getIccSmsInterfaceManager();
+ }
}
diff --git a/src/java/com/android/internal/telephony/cat/AppInterface.java b/src/java/com/android/internal/telephony/cat/AppInterface.java
index 1f2d3a0..9410b98 100755
--- a/src/java/com/android/internal/telephony/cat/AppInterface.java
+++ b/src/java/com/android/internal/telephony/cat/AppInterface.java
@@ -78,6 +78,7 @@
SEND_SS(0x11),
SEND_USSD(0x12),
SEND_SMS(0x13),
+ RUN_AT(0x34),
SEND_DTMF(0x14),
SET_UP_EVENT_LIST(0x05),
SET_UP_IDLE_MODE_TEXT(0x28),
diff --git a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
index 62d8869..cbad866 100644
--- a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
+++ b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
@@ -82,6 +82,8 @@
case SET_UP_IDLE_MODE_TEXT:
case SEND_DTMF:
case SEND_SMS:
+ case REFRESH:
+ case RUN_AT:
case SEND_SS:
case SEND_USSD:
mTextMsg = ((DisplayTextParams) cmdParams).mTextMsg;
@@ -121,7 +123,6 @@
mSetupEventListSettings.eventList = ((SetEventListParams) cmdParams).mEventInfo;
break;
case PROVIDE_LOCAL_INFORMATION:
- case REFRESH:
default:
break;
}
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index 91d01e6..1084454 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -20,6 +20,8 @@
.IDLE_SCREEN_AVAILABLE_EVENT;
import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants
.LANGUAGE_SELECTION_EVENT;
+import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants
+ .USER_ACTIVITY_EVENT;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
@@ -350,6 +352,7 @@
* Language Selection. */
case IDLE_SCREEN_AVAILABLE_EVENT:
case LANGUAGE_SELECTION_EVENT:
+ case USER_ACTIVITY_EVENT:
break;
default:
flag = false;
@@ -392,11 +395,6 @@
break;
case DISPLAY_TEXT:
break;
- case REFRESH:
- // ME side only handles refresh commands which meant to remove IDLE
- // MODE TEXT.
- cmdParams.mCmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value();
- break;
case SET_UP_IDLE_MODE_TEXT:
resultCode = cmdParams.mLoadIconFailed ? ResultCode.PRFRMD_ICON_NOT_DISPLAYED
: ResultCode.OK;
@@ -437,6 +435,13 @@
case GET_INPUT:
case GET_INKEY:
break;
+ case REFRESH:
+ case RUN_AT:
+ if (STK_DEFAULT.equals(((DisplayTextParams)cmdParams).mTextMsg.text)) {
+ // Remove the default text which was temporarily added and shall not be shown
+ ((DisplayTextParams)cmdParams).mTextMsg.text = null;
+ }
+ break;
case SEND_DTMF:
case SEND_SMS:
case SEND_SS:
@@ -763,6 +768,8 @@
// Language length should be 2 byte
buf.write(0x02);
break;
+ case USER_ACTIVITY_EVENT:
+ break;
default:
break;
}
diff --git a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index 232f808..049e668 100644
--- a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -61,12 +61,6 @@
static final int LOAD_SINGLE_ICON = 1;
static final int LOAD_MULTI_ICONS = 2;
- // Command Qualifier values for refresh command
- static final int REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE = 0x00;
- static final int REFRESH_NAA_INIT_AND_FILE_CHANGE = 0x02;
- static final int REFRESH_NAA_INIT = 0x03;
- static final int REFRESH_UICC_RESET = 0x04;
-
// Command Qualifier values for PLI command
static final int DTTZ_SETTING = 0x03;
static final int LANGUAGE_SETTING = 0x04;
@@ -188,6 +182,8 @@
break;
case SEND_DTMF:
case SEND_SMS:
+ case REFRESH:
+ case RUN_AT:
case SEND_SS:
case SEND_USSD:
cmdPending = processEventNotify(cmdDet, ctlvs);
@@ -196,10 +192,6 @@
case SET_UP_CALL:
cmdPending = processSetupCall(cmdDet, ctlvs);
break;
- case REFRESH:
- processRefresh(cmdDet, ctlvs);
- cmdPending = false;
- break;
case LAUNCH_BROWSER:
cmdPending = processLaunchBrowser(cmdDet, ctlvs);
break;
@@ -585,32 +577,6 @@
}
/**
- * Processes REFRESH proactive command from the SIM card.
- *
- * @param cmdDet Command Details container object.
- * @param ctlvs List of ComprehensionTlv objects following Command Details
- * object and Device Identities object within the proactive command
- */
- private boolean processRefresh(CommandDetails cmdDet,
- List<ComprehensionTlv> ctlvs) {
-
- CatLog.d(this, "process Refresh");
-
- // REFRESH proactive command is rerouted by the baseband and handled by
- // the telephony layer. IDLE TEXT should be removed for a REFRESH command
- // with "initialization" or "reset"
- switch (cmdDet.commandQualifier) {
- case REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE:
- case REFRESH_NAA_INIT_AND_FILE_CHANGE:
- case REFRESH_NAA_INIT:
- case REFRESH_UICC_RESET:
- mCmdParams = new DisplayTextParams(cmdDet, null);
- break;
- }
- return false;
- }
-
- /**
* Processes SELECT_ITEM proactive command from the SIM card.
*
* @param cmdDet Command Details container object.
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
index 5a40c4e..e2c178a 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.res.Resources;
import android.os.Message;
-import android.os.SystemProperties;
import android.provider.Telephony.Sms.Intents;
import android.telephony.SmsCbMessage;
@@ -33,7 +32,6 @@
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.SmsStorageMonitor;
import com.android.internal.telephony.TelephonyComponentFactory;
-import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.WspTypeDecoder;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import com.android.internal.util.HexDump;
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index f000caa..bbaa3ca 100755
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -16,12 +16,7 @@
package com.android.internal.telephony.cdma;
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.content.Intent;
import android.os.Message;
-import android.provider.Telephony.Sms;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
@@ -73,8 +68,9 @@
}
@Override
- protected boolean shouldBlockSms() {
- return SMSDispatcherUtil.shouldBlockSms(isCdmaMo(), mPhone);
+ protected boolean shouldBlockSmsForEcbm() {
+ // We only block outgoing SMS during ECBM when using CDMA.
+ return mPhone.isInEcm() && isCdmaMo() && !isIms();
}
@Override
@@ -123,6 +119,7 @@
+ " mRetryCount=" + tracker.mRetryCount
+ " mImsRetry=" + tracker.mImsRetry
+ " mMessageRef=" + tracker.mMessageRef
+ + " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
+ " SS=" + mPhone.getServiceState().getState());
int ss = mPhone.getServiceState().getState();
@@ -146,8 +143,11 @@
// sms over cdma is used:
// if sms over IMS is not supported AND
// this is not a retry case after sms over IMS failed
- // indicated by mImsRetry > 0
- if (0 == tracker.mImsRetry && !isIms() || imsSmsDisabled) {
+ // indicated by mImsRetry > 0 OR
+ // SMS over IMS is disabled because of the network type OR
+ // SMS over IMS is being handled by the ImsSmsDispatcher implementation and has indicated
+ // that the message should fall back to sending over CS.
+ if (0 == tracker.mImsRetry && !isIms() || imsSmsDisabled || tracker.mUsesImsServiceForIms) {
mCi.sendCdmaSms(pdu, reply);
} else {
mCi.sendImsCdmaSms(pdu, tracker.mImsRetry, tracker.mMessageRef, reply);
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
index 1b42d4a..f068acd 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
@@ -22,6 +22,8 @@
import android.net.NetworkConfig;
import android.net.NetworkRequest;
import android.telephony.Rlog;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.ApnSetting.ApnType;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.SparseIntArray;
@@ -29,7 +31,6 @@
import com.android.internal.R;
import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RetryManager;
import com.android.internal.util.IndentingPrintWriter;
@@ -129,6 +130,14 @@
}
/**
+ * Gets the APN type bitmask.
+ * @return The APN type bitmask
+ */
+ public int getApnTypeBitmask() {
+ return ApnSetting.getApnTypesBitmaskFromString(mApnType);
+ }
+
+ /**
* Get the data call async channel.
* @return The data call async channel
*/
@@ -392,8 +401,8 @@
String provisioningApn = mPhone.getContext().getResources()
.getString(R.string.mobile_provisioning_apn);
if (!TextUtils.isEmpty(provisioningApn) &&
- (mApnSetting != null) && (mApnSetting.apn != null)) {
- return (mApnSetting.apn.equals(provisioningApn));
+ (mApnSetting != null) && (mApnSetting.getApnName() != null)) {
+ return (mApnSetting.getApnName().equals(provisioningApn));
} else {
return false;
}
@@ -418,7 +427,7 @@
} else {
mLocalLogs.add(log);
mNetworkRequests.add(networkRequest);
- mDcTracker.setEnabled(apnIdForApnName(mApnType), true);
+ mDcTracker.setEnabled(ApnSetting.getApnTypesBitmaskFromString(mApnType), true);
}
}
}
@@ -438,7 +447,7 @@
log.log("ApnContext.releaseNetwork left with " + mNetworkRequests.size() +
" requests.");
if (mNetworkRequests.size() == 0) {
- mDcTracker.setEnabled(apnIdForApnName(mApnType), false);
+ mDcTracker.setEnabled(ApnSetting.getApnTypesBitmaskFromString(mApnType), false);
}
}
}
@@ -540,88 +549,78 @@
return mRetryManager.getRetryAfterDisconnectDelay();
}
- public static int apnIdForType(int networkType) {
+ public static int getApnTypeFromNetworkType(int networkType) {
switch (networkType) {
- case ConnectivityManager.TYPE_MOBILE:
- return DctConstants.APN_DEFAULT_ID;
- case ConnectivityManager.TYPE_MOBILE_MMS:
- return DctConstants.APN_MMS_ID;
- case ConnectivityManager.TYPE_MOBILE_SUPL:
- return DctConstants.APN_SUPL_ID;
- case ConnectivityManager.TYPE_MOBILE_DUN:
- return DctConstants.APN_DUN_ID;
- case ConnectivityManager.TYPE_MOBILE_FOTA:
- return DctConstants.APN_FOTA_ID;
- case ConnectivityManager.TYPE_MOBILE_IMS:
- return DctConstants.APN_IMS_ID;
- case ConnectivityManager.TYPE_MOBILE_CBS:
- return DctConstants.APN_CBS_ID;
- case ConnectivityManager.TYPE_MOBILE_IA:
- return DctConstants.APN_IA_ID;
- case ConnectivityManager.TYPE_MOBILE_EMERGENCY:
- return DctConstants.APN_EMERGENCY_ID;
- default:
- return DctConstants.APN_INVALID_ID;
+ case ConnectivityManager.TYPE_MOBILE:
+ return ApnSetting.TYPE_DEFAULT;
+ case ConnectivityManager.TYPE_MOBILE_MMS:
+ return ApnSetting.TYPE_MMS;
+ case ConnectivityManager.TYPE_MOBILE_SUPL:
+ return ApnSetting.TYPE_SUPL;
+ case ConnectivityManager.TYPE_MOBILE_DUN:
+ return ApnSetting.TYPE_DUN;
+ case ConnectivityManager.TYPE_MOBILE_FOTA:
+ return ApnSetting.TYPE_FOTA;
+ case ConnectivityManager.TYPE_MOBILE_IMS:
+ return ApnSetting.TYPE_IMS;
+ case ConnectivityManager.TYPE_MOBILE_CBS:
+ return ApnSetting.TYPE_CBS;
+ case ConnectivityManager.TYPE_MOBILE_IA:
+ return ApnSetting.TYPE_IA;
+ case ConnectivityManager.TYPE_MOBILE_EMERGENCY:
+ return ApnSetting.TYPE_EMERGENCY;
+ default:
+ return ApnSetting.TYPE_NONE;
}
}
- public static int apnIdForNetworkRequest(NetworkRequest nr) {
+ static @ApnType int getApnTypeFromNetworkRequest(NetworkRequest nr) {
NetworkCapabilities nc = nr.networkCapabilities;
// For now, ignore the bandwidth stuff
if (nc.getTransportTypes().length > 0 &&
nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == false) {
- return DctConstants.APN_INVALID_ID;
+ return ApnSetting.TYPE_NONE;
}
// in the near term just do 1-1 matches.
// TODO - actually try to match the set of capabilities
- int apnId = DctConstants.APN_INVALID_ID;
+ int apnType = ApnSetting.TYPE_NONE;
boolean error = false;
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- apnId = DctConstants.APN_DEFAULT_ID;
+ apnType = ApnSetting.TYPE_DEFAULT;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_MMS_ID;
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_MMS;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_SUPL_ID;
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_SUPL;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_DUN_ID;
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_DUN;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_FOTA_ID;
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_FOTA;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_IMS_ID;
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_IMS;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_CBS_ID;
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_CBS;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_IA_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_RCS)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
-
- Rlog.d(SLOG_TAG, "RCS APN type not yet supported");
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
-
- Rlog.d(SLOG_TAG, "XCAP APN type not yet supported");
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_IA;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
- if (apnId != DctConstants.APN_INVALID_ID) error = true;
- apnId = DctConstants.APN_EMERGENCY_ID;
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_EMERGENCY;
}
if (error) {
// TODO: If this error condition is removed, the framework's handling of
@@ -630,66 +629,10 @@
// NetworkCapabilities.maybeMarkCapabilitiesRestricted currently works.
Rlog.d(SLOG_TAG, "Multiple apn types specified in request - result is unspecified!");
}
- if (apnId == DctConstants.APN_INVALID_ID) {
+ if (apnType == ApnSetting.TYPE_NONE) {
Rlog.d(SLOG_TAG, "Unsupported NetworkRequest in Telephony: nr=" + nr);
}
- return apnId;
- }
-
- // TODO - kill The use of these strings
- public static int apnIdForApnName(String type) {
- switch (type) {
- case PhoneConstants.APN_TYPE_DEFAULT:
- return DctConstants.APN_DEFAULT_ID;
- case PhoneConstants.APN_TYPE_MMS:
- return DctConstants.APN_MMS_ID;
- case PhoneConstants.APN_TYPE_SUPL:
- return DctConstants.APN_SUPL_ID;
- case PhoneConstants.APN_TYPE_DUN:
- return DctConstants.APN_DUN_ID;
- case PhoneConstants.APN_TYPE_HIPRI:
- return DctConstants.APN_HIPRI_ID;
- case PhoneConstants.APN_TYPE_IMS:
- return DctConstants.APN_IMS_ID;
- case PhoneConstants.APN_TYPE_FOTA:
- return DctConstants.APN_FOTA_ID;
- case PhoneConstants.APN_TYPE_CBS:
- return DctConstants.APN_CBS_ID;
- case PhoneConstants.APN_TYPE_IA:
- return DctConstants.APN_IA_ID;
- case PhoneConstants.APN_TYPE_EMERGENCY:
- return DctConstants.APN_EMERGENCY_ID;
- default:
- return DctConstants.APN_INVALID_ID;
- }
- }
-
- private static String apnNameForApnId(int id) {
- switch (id) {
- case DctConstants.APN_DEFAULT_ID:
- return PhoneConstants.APN_TYPE_DEFAULT;
- case DctConstants.APN_MMS_ID:
- return PhoneConstants.APN_TYPE_MMS;
- case DctConstants.APN_SUPL_ID:
- return PhoneConstants.APN_TYPE_SUPL;
- case DctConstants.APN_DUN_ID:
- return PhoneConstants.APN_TYPE_DUN;
- case DctConstants.APN_HIPRI_ID:
- return PhoneConstants.APN_TYPE_HIPRI;
- case DctConstants.APN_IMS_ID:
- return PhoneConstants.APN_TYPE_IMS;
- case DctConstants.APN_FOTA_ID:
- return PhoneConstants.APN_TYPE_FOTA;
- case DctConstants.APN_CBS_ID:
- return PhoneConstants.APN_TYPE_CBS;
- case DctConstants.APN_IA_ID:
- return PhoneConstants.APN_TYPE_IA;
- case DctConstants.APN_EMERGENCY_ID:
- return PhoneConstants.APN_TYPE_EMERGENCY;
- default:
- Rlog.d(SLOG_TAG, "Unknown id (" + id + ") in apnIdToType");
- return PhoneConstants.APN_TYPE_DEFAULT;
- }
+ return apnType;
}
@Override
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
deleted file mode 100644
index 9394ecb..0000000
--- a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
+++ /dev/null
@@ -1,785 +0,0 @@
-/*
- * Copyright (C) 2006 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.dataconnection;
-
-import android.content.Context;
-import android.hardware.radio.V1_0.ApnTypes;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
-import android.telephony.ServiceState;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.uicc.IccRecords;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * This class represents a apn setting for create PDP link
- */
-public class ApnSetting {
-
- static final String LOG_TAG = "ApnSetting";
-
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
-
- static final String V2_FORMAT_REGEX = "^\\[ApnSettingV2\\]\\s*";
- static final String V3_FORMAT_REGEX = "^\\[ApnSettingV3\\]\\s*";
- static final String V4_FORMAT_REGEX = "^\\[ApnSettingV4\\]\\s*";
- static final String TAG = "ApnSetting";
-
- public final String carrier;
- public final String apn;
- public final String proxy;
- public final String port;
- public final String mmsc;
- public final String mmsProxy;
- public final String mmsPort;
- public final String user;
- public final String password;
- public final int authType;
- public final String[] types;
- public final int typesBitmap;
- public final int id;
- public final String numeric;
- public final String protocol;
- public final String roamingProtocol;
- public final int mtu;
-
- /**
- * Current status of APN
- * true : enabled APN, false : disabled APN.
- */
- public final boolean carrierEnabled;
- /**
- * Radio Access Technology info
- * To check what values can hold, refer to ServiceState.java.
- * This should be spread to other technologies,
- * but currently only used for LTE(14) and EHRPD(13).
- *
- * @deprecated use {@code networkTypeBitmask} instead
- */
- @Deprecated
- private final int bearer;
- /**
- * Radio Access Technology info
- * To check what values can hold, refer to ServiceState.java. This is a bitmask of radio
- * technologies in ServiceState.
- * This should be spread to other technologies,
- * but currently only used for LTE(14) and EHRPD(13).
- *
- * @deprecated use {@code networkTypeBitmask} instead
- */
- @Deprecated
- public final int bearerBitmask;
-
- /**
- * Radio Technology (Network Type) info
- * To check what values can hold, refer to TelephonyManager.java. This is a bitmask of radio
- * technologies ({@code NETWORK_TYPE_} constants) in {@link TelephonyManager}.
- */
- public final int networkTypeBitmask;
-
- /* ID of the profile in the modem */
- public final int profileId;
- public final boolean modemCognitive;
- public final int maxConns;
- public final int waitTime;
- public final int maxConnsTime;
-
- /**
- * MVNO match type. Possible values:
- * "spn": Service provider name.
- * "imsi": IMSI.
- * "gid": Group identifier level 1.
- * "iccid": ICCID
- */
- public final String mvnoType;
- /**
- * MVNO data. Examples:
- * "spn": A MOBILE, BEN NL
- * "imsi": 302720x94, 2060188
- * "gid": 4E, 33
- * "iccid": 898603 etc.
- */
- public final String mvnoMatchData;
-
- /**
- * Indicates this APN setting is permanently failed and cannot be
- * retried by the retry manager anymore.
- * */
- public boolean permanentFailed = false;
-
- /**
- * @deprecated this constructor is no longer supported. Use the other constructor which takes
- * a network type bitmask instead of the deprecated bearer bitmask and bearer field.
- * */
- @Deprecated
- public ApnSetting(int id, String numeric, String carrier, String apn,
- String proxy, String port,
- String mmsc, String mmsProxy, String mmsPort,
- String user, String password, int authType, String[] types,
- String protocol, String roamingProtocol, boolean carrierEnabled, int bearer,
- int bearerBitmask, int profileId, boolean modemCognitive, int maxConns,
- int waitTime, int maxConnsTime, int mtu, String mvnoType,
- String mvnoMatchData) {
- this.id = id;
- this.numeric = numeric;
- this.carrier = carrier;
- this.apn = apn;
- this.proxy = proxy;
- this.port = port;
- this.mmsc = mmsc;
- this.mmsProxy = mmsProxy;
- this.mmsPort = mmsPort;
- this.user = user;
- this.password = password;
- this.authType = authType;
- this.types = new String[types.length];
- int apnBitmap = 0;
- for (int i = 0; i < types.length; i++) {
- this.types[i] = types[i].toLowerCase();
- apnBitmap |= getApnBitmask(this.types[i]);
- }
- this.typesBitmap = apnBitmap;
- this.protocol = protocol;
- this.roamingProtocol = roamingProtocol;
- this.carrierEnabled = carrierEnabled;
- this.bearer = bearer;
- this.bearerBitmask = (bearerBitmask | ServiceState.getBitmaskForTech(bearer));
- this.profileId = profileId;
- this.modemCognitive = modemCognitive;
- this.maxConns = maxConns;
- this.waitTime = waitTime;
- this.maxConnsTime = maxConnsTime;
- this.mtu = mtu;
- this.mvnoType = mvnoType;
- this.mvnoMatchData = mvnoMatchData;
- this.networkTypeBitmask = ServiceState.convertBearerBitmaskToNetworkTypeBitmask(
- this.bearerBitmask);
- }
-
- public ApnSetting(int id, String numeric, String carrier, String apn,
- String proxy, String port,
- String mmsc, String mmsProxy, String mmsPort,
- String user, String password, int authType, String[] types,
- String protocol, String roamingProtocol, boolean carrierEnabled,
- int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
- int waitTime, int maxConnsTime, int mtu, String mvnoType,
- String mvnoMatchData) {
- this.id = id;
- this.numeric = numeric;
- this.carrier = carrier;
- this.apn = apn;
- this.proxy = proxy;
- this.port = port;
- this.mmsc = mmsc;
- this.mmsProxy = mmsProxy;
- this.mmsPort = mmsPort;
- this.user = user;
- this.password = password;
- this.authType = authType;
- this.types = new String[types.length];
- int apnBitmap = 0;
- for (int i = 0; i < types.length; i++) {
- this.types[i] = types[i].toLowerCase();
- apnBitmap |= getApnBitmask(this.types[i]);
- }
- this.typesBitmap = apnBitmap;
- this.protocol = protocol;
- this.roamingProtocol = roamingProtocol;
- this.carrierEnabled = carrierEnabled;
- this.bearer = 0;
- this.bearerBitmask =
- ServiceState.convertNetworkTypeBitmaskToBearerBitmask(networkTypeBitmask);
- this.networkTypeBitmask = networkTypeBitmask;
- this.profileId = profileId;
- this.modemCognitive = modemCognitive;
- this.maxConns = maxConns;
- this.waitTime = waitTime;
- this.maxConnsTime = maxConnsTime;
- this.mtu = mtu;
- this.mvnoType = mvnoType;
- this.mvnoMatchData = mvnoMatchData;
- }
-
- public ApnSetting(ApnSetting apn) {
- this(apn.id, apn.numeric, apn.carrier, apn.apn, apn.proxy, apn.port, apn.mmsc, apn.mmsProxy,
- apn.mmsPort, apn.user, apn.password, apn.authType, apn.types, apn.protocol,
- apn.roamingProtocol, apn.carrierEnabled, apn.networkTypeBitmask, apn.profileId,
- apn.modemCognitive, apn.maxConns, apn.waitTime, apn.maxConnsTime,
- apn.mtu, apn.mvnoType, apn.mvnoMatchData);
- }
-
- /**
- * Creates an ApnSetting object from a string.
- *
- * @param data the string to read.
- *
- * The string must be in one of two formats (newlines added for clarity,
- * spaces are optional):
- *
- * v1 format:
- * <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
- * <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
- * <type>[| <type>...],
- *
- * v2 format:
- * [ApnSettingV2] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
- * <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
- * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
- *
- * v3 format:
- * [ApnSettingV3] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
- * <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
- * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
- * <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>,
- * <mvnoType>, <mvnoMatchData>
- *
- * v4 format:
- * [ApnSettingV4] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
- * <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
- * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
- * <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>,
- * <mvnoType>, <mvnoMatchData>, <networkTypeBitmask>
- *
- * Note that the strings generated by toString() do not contain the username
- * and password and thus cannot be read by this method.
- */
- public static ApnSetting fromString(String data) {
- if (data == null) return null;
-
- int version;
- // matches() operates on the whole string, so append .* to the regex.
- if (data.matches(V4_FORMAT_REGEX + ".*")) {
- version = 4;
- data = data.replaceFirst(V4_FORMAT_REGEX, "");
- } else if (data.matches(V3_FORMAT_REGEX + ".*")) {
- version = 3;
- data = data.replaceFirst(V3_FORMAT_REGEX, "");
- } else if (data.matches(V2_FORMAT_REGEX + ".*")) {
- version = 2;
- data = data.replaceFirst(V2_FORMAT_REGEX, "");
- } else {
- version = 1;
- }
-
- String[] a = data.split("\\s*,\\s*");
- if (a.length < 14) {
- return null;
- }
-
- int authType;
- try {
- authType = Integer.parseInt(a[12]);
- } catch (NumberFormatException e) {
- authType = 0;
- }
-
- String[] typeArray;
- String protocol, roamingProtocol;
- boolean carrierEnabled;
- int bearerBitmask = 0;
- int networkTypeBitmask = 0;
- int profileId = 0;
- boolean modemCognitive = false;
- int maxConns = 0;
- int waitTime = 0;
- int maxConnsTime = 0;
- int mtu = PhoneConstants.UNSET_MTU;
- String mvnoType = "";
- String mvnoMatchData = "";
- if (version == 1) {
- typeArray = new String[a.length - 13];
- System.arraycopy(a, 13, typeArray, 0, a.length - 13);
- protocol = RILConstants.SETUP_DATA_PROTOCOL_IP;
- roamingProtocol = RILConstants.SETUP_DATA_PROTOCOL_IP;
- carrierEnabled = true;
- } else {
- if (a.length < 18) {
- return null;
- }
- typeArray = a[13].split("\\s*\\|\\s*");
- protocol = a[14];
- roamingProtocol = a[15];
- carrierEnabled = Boolean.parseBoolean(a[16]);
-
- bearerBitmask = ServiceState.getBitmaskFromString(a[17]);
-
- if (a.length > 22) {
- modemCognitive = Boolean.parseBoolean(a[19]);
- try {
- profileId = Integer.parseInt(a[18]);
- maxConns = Integer.parseInt(a[20]);
- waitTime = Integer.parseInt(a[21]);
- maxConnsTime = Integer.parseInt(a[22]);
- } catch (NumberFormatException e) {
- }
- }
- if (a.length > 23) {
- try {
- mtu = Integer.parseInt(a[23]);
- } catch (NumberFormatException e) {
- }
- }
- if (a.length > 25) {
- mvnoType = a[24];
- mvnoMatchData = a[25];
- }
- if (a.length > 26) {
- networkTypeBitmask = ServiceState.getBitmaskFromString(a[26]);
- }
- }
-
- // If both bearerBitmask and networkTypeBitmask were specified, bearerBitmask would be
- // ignored.
- if (networkTypeBitmask == 0) {
- networkTypeBitmask =
- ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask);
- }
- return new ApnSetting(-1, a[10] + a[11], a[0], a[1], a[2], a[3], a[7], a[8], a[9], a[4],
- a[5], authType, typeArray, protocol, roamingProtocol, carrierEnabled,
- networkTypeBitmask, profileId, modemCognitive, maxConns, waitTime, maxConnsTime,
- mtu, mvnoType, mvnoMatchData);
- }
-
- /**
- * Creates an array of ApnSetting objects from a string.
- *
- * @param data the string to read.
- *
- * Builds on top of the same format used by fromString, but allows for multiple entries
- * separated by "; ".
- */
- public static List<ApnSetting> arrayFromString(String data) {
- List<ApnSetting> retVal = new ArrayList<ApnSetting>();
- if (TextUtils.isEmpty(data)) {
- return retVal;
- }
- String[] apnStrings = data.split("\\s*;\\s*");
- for (String apnString : apnStrings) {
- ApnSetting apn = fromString(apnString);
- if (apn != null) {
- retVal.add(apn);
- }
- }
- return retVal;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("[ApnSettingV4] ")
- .append(carrier)
- .append(", ").append(id)
- .append(", ").append(numeric)
- .append(", ").append(apn)
- .append(", ").append(proxy)
- .append(", ").append(mmsc)
- .append(", ").append(mmsProxy)
- .append(", ").append(mmsPort)
- .append(", ").append(port)
- .append(", ").append(authType).append(", ");
- for (int i = 0; i < types.length; i++) {
- sb.append(types[i]);
- if (i < types.length - 1) {
- sb.append(" | ");
- }
- }
- sb.append(", ").append(protocol);
- sb.append(", ").append(roamingProtocol);
- sb.append(", ").append(carrierEnabled);
- sb.append(", ").append(bearer);
- sb.append(", ").append(bearerBitmask);
- sb.append(", ").append(profileId);
- sb.append(", ").append(modemCognitive);
- sb.append(", ").append(maxConns);
- sb.append(", ").append(waitTime);
- sb.append(", ").append(maxConnsTime);
- sb.append(", ").append(mtu);
- sb.append(", ").append(mvnoType);
- sb.append(", ").append(mvnoMatchData);
- sb.append(", ").append(permanentFailed);
- sb.append(", ").append(networkTypeBitmask);
- return sb.toString();
- }
-
- /**
- * Returns true if there are MVNO params specified.
- */
- public boolean hasMvnoParams() {
- return !TextUtils.isEmpty(mvnoType) && !TextUtils.isEmpty(mvnoMatchData);
- }
-
- public boolean canHandleType(String type) {
- if (!carrierEnabled) return false;
- boolean wildcardable = true;
- if (PhoneConstants.APN_TYPE_IA.equalsIgnoreCase(type)) wildcardable = false;
- for (String t : types) {
- // DEFAULT handles all, and HIPRI is handled by DEFAULT
- if (t.equalsIgnoreCase(type) ||
- (wildcardable && t.equalsIgnoreCase(PhoneConstants.APN_TYPE_ALL)) ||
- (t.equalsIgnoreCase(PhoneConstants.APN_TYPE_DEFAULT) &&
- type.equalsIgnoreCase(PhoneConstants.APN_TYPE_HIPRI))) {
- return true;
- }
- }
- return false;
- }
-
- private static boolean iccidMatches(String mvnoData, String iccId) {
- String[] mvnoIccidList = mvnoData.split(",");
- for (String mvnoIccid : mvnoIccidList) {
- if (iccId.startsWith(mvnoIccid)) {
- Log.d(TAG, "mvno icc id match found");
- return true;
- }
- }
- return false;
- }
-
- private static boolean imsiMatches(String imsiDB, String imsiSIM) {
- // Note: imsiDB value has digit number or 'x' character for seperating USIM information
- // for MVNO operator. And then digit number is matched at same order and 'x' character
- // could replace by any digit number.
- // ex) if imsiDB inserted '310260x10xxxxxx' for GG Operator,
- // that means first 6 digits, 8th and 9th digit
- // should be set in USIM for GG Operator.
- int len = imsiDB.length();
- int idxCompare = 0;
-
- if (len <= 0) return false;
- if (len > imsiSIM.length()) return false;
-
- for (int idx=0; idx<len; idx++) {
- char c = imsiDB.charAt(idx);
- if ((c == 'x') || (c == 'X') || (c == imsiSIM.charAt(idx))) {
- continue;
- } else {
- return false;
- }
- }
- return true;
- }
-
- public static boolean mvnoMatches(IccRecords r, String mvnoType, String mvnoMatchData) {
- if (mvnoType.equalsIgnoreCase("spn")) {
- if ((r.getServiceProviderName() != null) &&
- r.getServiceProviderName().equalsIgnoreCase(mvnoMatchData)) {
- return true;
- }
- } else if (mvnoType.equalsIgnoreCase("imsi")) {
- String imsiSIM = r.getIMSI();
- if ((imsiSIM != null) && imsiMatches(mvnoMatchData, imsiSIM)) {
- return true;
- }
- } else if (mvnoType.equalsIgnoreCase("gid")) {
- String gid1 = r.getGid1();
- int mvno_match_data_length = mvnoMatchData.length();
- if ((gid1 != null) && (gid1.length() >= mvno_match_data_length) &&
- gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvnoMatchData)) {
- return true;
- }
- } else if (mvnoType.equalsIgnoreCase("iccid")) {
- String iccId = r.getIccId();
- if ((iccId != null) && iccidMatches(mvnoMatchData, iccId)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Check if this APN type is metered.
- *
- * @param type The APN type
- * @param phone The phone object
- * @return True if the APN type is metered, otherwise false.
- */
- public static boolean isMeteredApnType(String type, Phone phone) {
- if (phone == null) {
- return true;
- }
-
- boolean isRoaming = phone.getServiceState().getDataRoaming();
- boolean isIwlan = phone.getServiceState().getRilDataRadioTechnology()
- == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
- int subId = phone.getSubId();
-
- String carrierConfig;
- // First check if the device is in IWLAN mode. If yes, use the IWLAN metered APN list. Then
- // check if the device is roaming. If yes, use the roaming metered APN list. Otherwise, use
- // the normal metered APN list.
- if (isIwlan) {
- carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS;
- } else if (isRoaming) {
- carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS;
- } else {
- carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS;
- }
-
- if (DBG) {
- Rlog.d(LOG_TAG, "isMeteredApnType: isRoaming=" + isRoaming + ", isIwlan=" + isIwlan);
- }
-
- CarrierConfigManager configManager = (CarrierConfigManager)
- phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager == null) {
- Rlog.e(LOG_TAG, "Carrier config service is not available");
- return true;
- }
-
- PersistableBundle b = configManager.getConfigForSubId(subId);
- if (b == null) {
- Rlog.e(LOG_TAG, "Can't get the config. subId = " + subId);
- return true;
- }
-
- String[] meteredApnTypes = b.getStringArray(carrierConfig);
- if (meteredApnTypes == null) {
- Rlog.e(LOG_TAG, carrierConfig + " is not available. " + "subId = " + subId);
- return true;
- }
-
- HashSet<String> meteredApnSet = new HashSet<>(Arrays.asList(meteredApnTypes));
- if (DBG) {
- Rlog.d(LOG_TAG, "For subId = " + subId + ", metered APN types are "
- + Arrays.toString(meteredApnSet.toArray()));
- }
-
- // If all types of APN are metered, then this APN setting must be metered.
- if (meteredApnSet.contains(PhoneConstants.APN_TYPE_ALL)) {
- if (DBG) Rlog.d(LOG_TAG, "All APN types are metered.");
- return true;
- }
-
- if (meteredApnSet.contains(type)) {
- if (DBG) Rlog.d(LOG_TAG, type + " is metered.");
- return true;
- } else if (type.equals(PhoneConstants.APN_TYPE_ALL)) {
- // Assuming no configuration error, if at least one APN type is
- // metered, then this APN setting is metered.
- if (meteredApnSet.size() > 0) {
- if (DBG) Rlog.d(LOG_TAG, "APN_TYPE_ALL APN is metered.");
- return true;
- }
- }
-
- if (DBG) Rlog.d(LOG_TAG, type + " is not metered.");
- return false;
- }
-
- /**
- * Check if this APN setting is metered.
- *
- * @param phone The phone object
- * @return True if this APN setting is metered, otherwise false.
- */
- public boolean isMetered(Phone phone) {
- if (phone == null) {
- return true;
- }
-
- for (String type : types) {
- // If one of the APN type is metered, then this APN setting is metered.
- if (isMeteredApnType(type, phone)) {
- return true;
- }
- }
- return false;
- }
-
- // TODO - if we have this function we should also have hashCode.
- // Also should handle changes in type order and perhaps case-insensitivity
- @Override
- public boolean equals(Object o) {
- if (o instanceof ApnSetting == false) {
- return false;
- }
-
- ApnSetting other = (ApnSetting) o;
-
- return carrier.equals(other.carrier)
- && id == other.id
- && numeric.equals(other.numeric)
- && apn.equals(other.apn)
- && proxy.equals(other.proxy)
- && mmsc.equals(other.mmsc)
- && mmsProxy.equals(other.mmsProxy)
- && TextUtils.equals(mmsPort, other.mmsPort)
- && port.equals(other.port)
- && TextUtils.equals(user, other.user)
- && TextUtils.equals(password, other.password)
- && authType == other.authType
- && Arrays.deepEquals(types, other.types)
- && typesBitmap == other.typesBitmap
- && protocol.equals(other.protocol)
- && roamingProtocol.equals(other.roamingProtocol)
- && carrierEnabled == other.carrierEnabled
- && bearer == other.bearer
- && bearerBitmask == other.bearerBitmask
- && profileId == other.profileId
- && modemCognitive == other.modemCognitive
- && maxConns == other.maxConns
- && waitTime == other.waitTime
- && maxConnsTime == other.maxConnsTime
- && mtu == other.mtu
- && mvnoType.equals(other.mvnoType)
- && mvnoMatchData.equals(other.mvnoMatchData)
- && networkTypeBitmask == other.networkTypeBitmask;
- }
-
- /**
- * Compare two APN settings
- *
- * Note: This method does not compare 'id', 'bearer', 'bearerBitmask', 'networkTypeBitmask'.
- * We only use this for determining if tearing a data call is needed when conditions change. See
- * cleanUpConnectionsOnUpdatedApns in DcTracker.
- *
- * @param o the other object to compare
- * @param isDataRoaming True if the device is on data roaming
- * @return True if the two APN settings are same
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public boolean equals(Object o, boolean isDataRoaming) {
- if (!(o instanceof ApnSetting)) {
- return false;
- }
-
- ApnSetting other = (ApnSetting) o;
-
- return carrier.equals(other.carrier)
- && numeric.equals(other.numeric)
- && apn.equals(other.apn)
- && proxy.equals(other.proxy)
- && mmsc.equals(other.mmsc)
- && mmsProxy.equals(other.mmsProxy)
- && TextUtils.equals(mmsPort, other.mmsPort)
- && port.equals(other.port)
- && TextUtils.equals(user, other.user)
- && TextUtils.equals(password, other.password)
- && authType == other.authType
- && Arrays.deepEquals(types, other.types)
- && typesBitmap == other.typesBitmap
- && (isDataRoaming || protocol.equals(other.protocol))
- && (!isDataRoaming || roamingProtocol.equals(other.roamingProtocol))
- && carrierEnabled == other.carrierEnabled
- && profileId == other.profileId
- && modemCognitive == other.modemCognitive
- && maxConns == other.maxConns
- && waitTime == other.waitTime
- && maxConnsTime == other.maxConnsTime
- && mtu == other.mtu
- && mvnoType.equals(other.mvnoType)
- && mvnoMatchData.equals(other.mvnoMatchData);
- }
-
- /**
- * Check if neither mention DUN and are substantially similar
- *
- * @param other The other APN settings to compare
- * @return True if two APN settings are similar
- */
- public boolean similar(ApnSetting other) {
- return (!this.canHandleType(PhoneConstants.APN_TYPE_DUN)
- && !other.canHandleType(PhoneConstants.APN_TYPE_DUN)
- && Objects.equals(this.apn, other.apn)
- && !typeSameAny(this, other)
- && xorEquals(this.proxy, other.proxy)
- && xorEquals(this.port, other.port)
- && xorEquals(this.protocol, other.protocol)
- && xorEquals(this.roamingProtocol, other.roamingProtocol)
- && this.carrierEnabled == other.carrierEnabled
- && this.bearerBitmask == other.bearerBitmask
- && this.profileId == other.profileId
- && Objects.equals(this.mvnoType, other.mvnoType)
- && Objects.equals(this.mvnoMatchData, other.mvnoMatchData)
- && xorEquals(this.mmsc, other.mmsc)
- && xorEquals(this.mmsProxy, other.mmsProxy)
- && xorEquals(this.mmsPort, other.mmsPort))
- && this.networkTypeBitmask == other.networkTypeBitmask;
- }
-
- // check whether the types of two APN same (even only one type of each APN is same)
- private boolean typeSameAny(ApnSetting first, ApnSetting second) {
- if (VDBG) {
- StringBuilder apnType1 = new StringBuilder(first.apn + ": ");
- for (int index1 = 0; index1 < first.types.length; index1++) {
- apnType1.append(first.types[index1]);
- apnType1.append(",");
- }
-
- StringBuilder apnType2 = new StringBuilder(second.apn + ": ");
- for (int index1 = 0; index1 < second.types.length; index1++) {
- apnType2.append(second.types[index1]);
- apnType2.append(",");
- }
- Rlog.d(LOG_TAG, "APN1: is " + apnType1);
- Rlog.d(LOG_TAG, "APN2: is " + apnType2);
- }
-
- for (int index1 = 0; index1 < first.types.length; index1++) {
- for (int index2 = 0; index2 < second.types.length; index2++) {
- if (first.types[index1].equals(PhoneConstants.APN_TYPE_ALL)
- || second.types[index2].equals(PhoneConstants.APN_TYPE_ALL)
- || first.types[index1].equals(second.types[index2])) {
- if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true");
- return true;
- }
- }
- }
-
- if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return false");
- return false;
- }
-
- // equal or one is not specified
- private boolean xorEquals(String first, String second) {
- return (Objects.equals(first, second)
- || TextUtils.isEmpty(first)
- || TextUtils.isEmpty(second));
- }
-
- // Helper function to convert APN string into a 32-bit bitmask.
- private static int getApnBitmask(String apn) {
- switch (apn) {
- case PhoneConstants.APN_TYPE_DEFAULT: return ApnTypes.DEFAULT;
- case PhoneConstants.APN_TYPE_MMS: return ApnTypes.MMS;
- case PhoneConstants.APN_TYPE_SUPL: return ApnTypes.SUPL;
- case PhoneConstants.APN_TYPE_DUN: return ApnTypes.DUN;
- case PhoneConstants.APN_TYPE_HIPRI: return ApnTypes.HIPRI;
- case PhoneConstants.APN_TYPE_FOTA: return ApnTypes.FOTA;
- case PhoneConstants.APN_TYPE_IMS: return ApnTypes.IMS;
- case PhoneConstants.APN_TYPE_CBS: return ApnTypes.CBS;
- case PhoneConstants.APN_TYPE_IA: return ApnTypes.IA;
- case PhoneConstants.APN_TYPE_EMERGENCY: return ApnTypes.EMERGENCY;
- case PhoneConstants.APN_TYPE_ALL: return ApnTypes.ALL;
- default: return ApnTypes.NONE;
- }
- }
-}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java b/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
new file mode 100644
index 0000000..e9a6fc8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2018 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.dataconnection;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.data.ApnSetting;
+import android.util.Log;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.uicc.IccRecords;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * This class represents a apn setting for create PDP link
+ */
+public class ApnSettingUtils {
+
+ static final String LOG_TAG = "ApnSetting";
+
+ private static final boolean DBG = false;
+
+ private static boolean iccidMatches(String mvnoData, String iccId) {
+ String[] mvnoIccidList = mvnoData.split(",");
+ for (String mvnoIccid : mvnoIccidList) {
+ if (iccId.startsWith(mvnoIccid)) {
+ Log.d(LOG_TAG, "mvno icc id match found");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean imsiMatches(String imsiDB, String imsiSIM) {
+ // Note: imsiDB value has digit number or 'x' character for seperating USIM information
+ // for MVNO operator. And then digit number is matched at same order and 'x' character
+ // could replace by any digit number.
+ // ex) if imsiDB inserted '310260x10xxxxxx' for GG Operator,
+ // that means first 6 digits, 8th and 9th digit
+ // should be set in USIM for GG Operator.
+ int len = imsiDB.length();
+
+ if (len <= 0) return false;
+ if (len > imsiSIM.length()) return false;
+
+ for (int idx = 0; idx < len; idx++) {
+ char c = imsiDB.charAt(idx);
+ if ((c == 'x') || (c == 'X') || (c == imsiSIM.charAt(idx))) {
+ continue;
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Check if MVNO type and data match IccRecords.
+ *
+ * @param r the IccRecords
+ * @param mvnoType the MVNO type
+ * @param mvnoMatchData the MVNO match data
+ * @return {@code true} if MVNO type and data match IccRecords, {@code false} otherwise.
+ */
+ public static boolean mvnoMatches(IccRecords r, int mvnoType, String mvnoMatchData) {
+ if (mvnoType == ApnSetting.MVNO_TYPE_SPN) {
+ if ((r.getServiceProviderName() != null)
+ && r.getServiceProviderName().equalsIgnoreCase(mvnoMatchData)) {
+ return true;
+ }
+ } else if (mvnoType == ApnSetting.MVNO_TYPE_IMSI) {
+ String imsiSIM = r.getIMSI();
+ if ((imsiSIM != null) && imsiMatches(mvnoMatchData, imsiSIM)) {
+ return true;
+ }
+ } else if (mvnoType == ApnSetting.MVNO_TYPE_GID) {
+ String gid1 = r.getGid1();
+ int mvno_match_data_length = mvnoMatchData.length();
+ if ((gid1 != null) && (gid1.length() >= mvno_match_data_length)
+ && gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvnoMatchData)) {
+ return true;
+ }
+ } else if (mvnoType == ApnSetting.MVNO_TYPE_ICCID) {
+ String iccId = r.getIccId();
+ if ((iccId != null) && iccidMatches(mvnoMatchData, iccId)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if this APN type is metered.
+ *
+ * @param type the APN type
+ * @param phone the phone object
+ * @return {@code true} if the APN type is metered, {@code false} otherwise.
+ */
+ public static boolean isMeteredApnType(String type, Phone phone) {
+ if (phone == null) {
+ return true;
+ }
+
+ boolean isRoaming = phone.getServiceState().getDataRoaming();
+ boolean isIwlan = phone.getServiceState().getRilDataRadioTechnology()
+ == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+ int subId = phone.getSubId();
+
+ String carrierConfig;
+ // First check if the device is in IWLAN mode. If yes, use the IWLAN metered APN list. Then
+ // check if the device is roaming. If yes, use the roaming metered APN list. Otherwise, use
+ // the normal metered APN list.
+ if (isIwlan) {
+ carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS;
+ } else if (isRoaming) {
+ carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS;
+ } else {
+ carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS;
+ }
+
+ if (DBG) {
+ Rlog.d(LOG_TAG, "isMeteredApnType: isRoaming=" + isRoaming + ", isIwlan=" + isIwlan);
+ }
+
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager == null) {
+ Rlog.e(LOG_TAG, "Carrier config service is not available");
+ return true;
+ }
+
+ PersistableBundle b = configManager.getConfigForSubId(subId);
+ if (b == null) {
+ Rlog.e(LOG_TAG, "Can't get the config. subId = " + subId);
+ return true;
+ }
+
+ String[] meteredApnTypes = b.getStringArray(carrierConfig);
+ if (meteredApnTypes == null) {
+ Rlog.e(LOG_TAG, carrierConfig + " is not available. " + "subId = " + subId);
+ return true;
+ }
+
+ HashSet<String> meteredApnSet = new HashSet<>(Arrays.asList(meteredApnTypes));
+ if (DBG) {
+ Rlog.d(LOG_TAG, "For subId = " + subId + ", metered APN types are "
+ + Arrays.toString(meteredApnSet.toArray()));
+ }
+
+ // If all types of APN are metered, then this APN setting must be metered.
+ if (meteredApnSet.contains(PhoneConstants.APN_TYPE_ALL)) {
+ if (DBG) Rlog.d(LOG_TAG, "All APN types are metered.");
+ return true;
+ }
+
+ if (meteredApnSet.contains(type)) {
+ if (DBG) Rlog.d(LOG_TAG, type + " is metered.");
+ return true;
+ } else if (type.equals(PhoneConstants.APN_TYPE_ALL)) {
+ // Assuming no configuration error, if at least one APN type is
+ // metered, then this APN setting is metered.
+ if (meteredApnSet.size() > 0) {
+ if (DBG) Rlog.d(LOG_TAG, "APN_TYPE_ALL APN is metered.");
+ return true;
+ }
+ }
+
+ if (DBG) Rlog.d(LOG_TAG, type + " is not metered.");
+ return false;
+ }
+
+ /**
+ * Check if this APN setting is metered.
+ *
+ * @param phone The phone object
+ * @return True if this APN setting is metered, otherwise false.
+ */
+ public static boolean isMetered(ApnSetting apn, Phone phone) {
+ if (phone == null) {
+ return true;
+ }
+
+ String[] types = ApnSetting.getApnTypesStringFromBitmask(
+ apn.getApnTypeBitmask()).split(",");
+
+ for (String type : types) {
+ // If one of the APN type is metered, then this APN setting is metered.
+ if (isMeteredApnType(type, phone)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 8a101b7..00af9fe 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -39,9 +39,11 @@
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.telephony.AccessNetworkConstants;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
import android.telephony.data.DataService;
@@ -297,10 +299,22 @@
return new LinkProperties(mLinkProperties);
}
- boolean getIsInactive() {
+ boolean isInactive() {
return getCurrentState() == mInactiveState;
}
+ boolean isDisconnecting() {
+ return getCurrentState() == mDisconnectingState;
+ }
+
+ boolean isActive() {
+ return getCurrentState() == mActiveState;
+ }
+
+ boolean isActivating() {
+ return getCurrentState() == mActivatingState;
+ }
+
int getCid() {
return mCid;
}
@@ -430,9 +444,9 @@
return;
}
- if (apn != null && apn.mtu != PhoneConstants.UNSET_MTU) {
- lp.setMtu(apn.mtu);
- if (DBG) log("MTU set by APN to: " + apn.mtu);
+ if (apn != null && apn.getMtu() != PhoneConstants.UNSET_MTU) {
+ lp.setMtu(apn.getMtu());
+ if (DBG) log("MTU set by APN to: " + apn.getMtu());
return;
}
@@ -489,9 +503,12 @@
* @param cp is the connection parameters
*/
private void onConnect(ConnectionParams cp) {
- if (DBG) log("onConnect: carrier='" + mApnSetting.carrier
- + "' APN='" + mApnSetting.apn
- + "' proxy='" + mApnSetting.proxy + "' port='" + mApnSetting.port + "'");
+ if (DBG) {
+ log("onConnect: carrier='" + mApnSetting.getEntryName()
+ + "' APN='" + mApnSetting.getApnName()
+ + "' proxy='" + mApnSetting.getProxyAddressAsString()
+ + "' port='" + mApnSetting.getProxyPort() + "'");
+ }
if (cp.mApnContext != null) cp.mApnContext.requestLog("DataConnection.onConnect");
// Check if we should fake an error.
@@ -770,12 +787,12 @@
// Do not apply the race condition workaround for MMS APN
// if Proxy is an IP-address.
// Otherwise, the default APN will not be restored anymore.
- if (!mApnSetting.types[0].equals(PhoneConstants.APN_TYPE_MMS)
- || !isIpAddress(mApnSetting.mmsProxy)) {
+ if (!isIpAddress(mApnSetting.getMmsProxyAddressAsString())) {
log(String.format(
- "isDnsOk: return false apn.types[0]=%s APN_TYPE_MMS=%s isIpAddress(%s)=%s",
- mApnSetting.types[0], PhoneConstants.APN_TYPE_MMS, mApnSetting.mmsProxy,
- isIpAddress(mApnSetting.mmsProxy)));
+ "isDnsOk: return false apn.types=%d APN_TYPE_MMS=%s isIpAddress(%s)=%s",
+ mApnSetting.getApnTypeBitmask(), PhoneConstants.APN_TYPE_MMS,
+ mApnSetting.getMmsProxyAddressAsString(),
+ isIpAddress(mApnSetting.getMmsProxyAddressAsString())));
return false;
}
}
@@ -909,7 +926,7 @@
// Do we need a restricted network to satisfy the request?
// Is this network metered? If not, then don't add restricted
- if (!mApnSetting.isMetered(mPhone)) {
+ if (!ApnSettingUtils.isMetered(mApnSetting, mPhone)) {
return;
}
@@ -922,11 +939,12 @@
result.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
if (mApnSetting != null) {
- ApnSetting securedDunApn = mDct.fetchDunApn();
- for (String type : mApnSetting.types) {
+ final String[] types = ApnSetting.getApnTypesStringFromBitmask(
+ mApnSetting.getApnTypeBitmask()).split(",");
+ for (String type : types) {
if (!mRestrictedNetworkOverride
&& (mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
- && ApnSetting.isMeteredApnType(type, mPhone)) {
+ && ApnSettingUtils.isMeteredApnType(type, mPhone)) {
log("Dropped the metered " + type + " for the unmetered data call.");
continue;
}
@@ -939,11 +957,7 @@
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);
- }
+ result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
break;
}
case PhoneConstants.APN_TYPE_DEFAULT: {
@@ -959,9 +973,7 @@
break;
}
case PhoneConstants.APN_TYPE_DUN: {
- if (securedDunApn == null || securedDunApn.equals(mApnSetting)) {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
- }
+ result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
break;
}
case PhoneConstants.APN_TYPE_FOTA: {
@@ -993,7 +1005,7 @@
// 2. The non-restricted data and is intended for unmetered use only.
if (((mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
&& !mRestrictedNetworkOverride)
- || !mApnSetting.isMetered(mPhone)) {
+ || !ApnSettingUtils.isMetered(mApnSetting, mPhone)) {
result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
} else {
result.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
@@ -1164,7 +1176,7 @@
// only NOT be set only if we're in DcInactiveState.
mApnSetting = apnContext.getApnSetting();
}
- if (mApnSetting == null || !mApnSetting.canHandleType(apnContext.getApnType())) {
+ if (mApnSetting == null || !mApnSetting.canHandleType(apnContext.getApnTypeBitmask())) {
if (DBG) {
log("initConnection: incompatible apnSetting in ConnectionParams cp=" + cp
+ " dc=" + DataConnection.this);
@@ -1271,7 +1283,7 @@
break;
}
case DcAsyncChannel.REQ_IS_INACTIVE: {
- boolean val = getIsInactive();
+ boolean val = isInactive();
if (VDBG) log("REQ_IS_INACTIVE isInactive=" + val);
mAc.replyToMessage(msg, DcAsyncChannel.RSP_IS_INACTIVE, val ? 1 : 0);
break;
@@ -1677,7 +1689,7 @@
mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED,
mNetworkInfo.getReason(), null);
- mNetworkInfo.setExtraInfo(mApnSetting.apn);
+ mNetworkInfo.setExtraInfo(mApnSetting.getApnName());
updateTcpBufferSizes(mRilRat);
final NetworkMisc misc = new NetworkMisc();
@@ -1690,6 +1702,7 @@
misc.subscriberId = mPhone.getSubscriberId();
setNetworkRestriction();
+ if (DBG) log("mRestrictedNetworkOverride = " + mRestrictedNetworkOverride);
mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
"DcNetworkAgent", mNetworkInfo, getNetworkCapabilities(), mLinkProperties,
50, misc);
@@ -1838,10 +1851,26 @@
KeepalivePacketData pkt = (KeepalivePacketData) msg.obj;
int slotId = msg.arg1;
int intervalMillis = msg.arg2 * 1000;
- mPhone.mCi.startNattKeepalive(
- DataConnection.this.mCid, pkt, intervalMillis,
- DataConnection.this.obtainMessage(
- EVENT_KEEPALIVE_STARTED, slotId, 0, null));
+ if (mDataServiceManager.getTransportType()
+ == AccessNetworkConstants.TransportType.WWAN) {
+ mPhone.mCi.startNattKeepalive(
+ DataConnection.this.mCid, pkt, intervalMillis,
+ DataConnection.this.obtainMessage(
+ EVENT_KEEPALIVE_STARTED, slotId, 0, null));
+ } else {
+ // We currently do not support NATT Keepalive requests using the
+ // DataService API, so unless the request is WWAN (always bound via
+ // the CommandsInterface), the request cannot be honored.
+ //
+ // TODO: b/72331356 to add support for Keepalive to the DataService
+ // so that keepalive requests can be handled (if supported) by the
+ // underlying transport.
+ if (mNetworkAgent != null) {
+ mNetworkAgent.onPacketKeepaliveEvent(
+ msg.arg1,
+ ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
+ }
+ }
retVal = HANDLED;
break;
}
@@ -2100,7 +2129,6 @@
String logStr = "Changed from " + mNetworkCapabilities + " to "
+ networkCapabilities + ", Data RAT="
+ mPhone.getServiceState().getRilDataRadioTechnology()
- + ", DUN APN=\"" + mDct.fetchDunApn() + "\""
+ ", mApnSetting=" + mApnSetting;
mNetCapsLocalLog.log(logStr);
log(logStr);
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java b/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
index 8273dee..06bb7de 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
@@ -20,6 +20,7 @@
import android.net.NetworkCapabilities;
import android.net.ProxyInfo;
import android.os.Message;
+import android.telephony.data.ApnSetting;
import com.android.internal.telephony.dataconnection.DataConnection.ConnectionParams;
import com.android.internal.telephony.dataconnection.DataConnection.DisconnectParams;
@@ -153,7 +154,7 @@
value = false;
}
} else {
- value = mDc.getIsInactive();
+ value = mDc.isInactive();
}
return value;
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcRequest.java b/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
index 79cfcec..bf2a6af 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
@@ -15,22 +15,10 @@
*/
package com.android.internal.telephony.dataconnection;
-import static com.android.internal.telephony.DctConstants.APN_CBS_ID;
-import static com.android.internal.telephony.DctConstants.APN_DEFAULT_ID;
-import static com.android.internal.telephony.DctConstants.APN_DUN_ID;
-import static com.android.internal.telephony.DctConstants.APN_EMERGENCY_ID;
-import static com.android.internal.telephony.DctConstants.APN_FOTA_ID;
-import static com.android.internal.telephony.DctConstants.APN_IA_ID;
-import static com.android.internal.telephony.DctConstants.APN_IMS_ID;
-import static com.android.internal.telephony.DctConstants.APN_INVALID_ID;
-import static com.android.internal.telephony.DctConstants.APN_MMS_ID;
-import static com.android.internal.telephony.DctConstants.APN_SUPL_ID;
-
import android.content.Context;
-import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
import android.net.NetworkRequest;
-import android.telephony.Rlog;
+import android.telephony.data.ApnSetting.ApnType;
import java.util.HashMap;
@@ -39,17 +27,17 @@
public final NetworkRequest networkRequest;
public final int priority;
- public final int apnId;
+ public final @ApnType int apnType;
public DcRequest(NetworkRequest nr, Context context) {
initApnPriorities(context);
networkRequest = nr;
- apnId = apnIdForNetworkRequest(networkRequest);
- priority = priorityForApnId(apnId);
+ apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
+ priority = priorityForApnType(apnType);
}
public String toString() {
- return networkRequest.toString() + ", priority=" + priority + ", apnId=" + apnId;
+ return networkRequest.toString() + ", priority=" + priority + ", apnType=" + apnType;
}
public int hashCode() {
@@ -67,78 +55,6 @@
return o.priority - priority;
}
- private int apnIdForNetworkRequest(NetworkRequest nr) {
- NetworkCapabilities nc = nr.networkCapabilities;
- // For now, ignore the bandwidth stuff
- if (nc.getTransportTypes().length > 0 &&
- nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == false) {
- return APN_INVALID_ID;
- }
-
- // in the near term just do 1-1 matches.
- // TODO - actually try to match the set of capabilities
- int apnId = APN_INVALID_ID;
-
- boolean error = false;
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_DEFAULT_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_MMS_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_SUPL_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_DUN_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_FOTA_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_IMS_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_CBS_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_IA_ID;
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_RCS)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_INVALID_ID;
- loge("RCS APN type not yet supported");
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_INVALID_ID;
- loge("XCAP APN type not yet supported");
- }
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
- if (apnId != APN_INVALID_ID) error = true;
- apnId = APN_EMERGENCY_ID;
- }
- if (error) {
- // TODO: If this error condition is removed, the framework's handling of
- // NET_CAPABILITY_NOT_RESTRICTED will need to be updated so requests for
- // say FOTA and INTERNET are marked as restricted. This is not how
- // NetworkCapabilities.maybeMarkCapabilitiesRestricted currently works.
- loge("Multiple apn types specified in request - result is unspecified!");
- }
- if (apnId == APN_INVALID_ID) {
- loge("Unsupported NetworkRequest in Telephony: nr=" + nr);
- }
- return apnId;
- }
-
private static final HashMap<Integer, Integer> sApnPriorityMap =
new HashMap<Integer, Integer>();
@@ -149,19 +65,15 @@
com.android.internal.R.array.networkAttributes);
for (String networkConfigString : networkConfigStrings) {
NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
- final int apnId = ApnContext.apnIdForType(networkConfig.type);
- sApnPriorityMap.put(apnId, networkConfig.priority);
+ final int apnType = ApnContext.getApnTypeFromNetworkType(networkConfig.type);
+ sApnPriorityMap.put(apnType, networkConfig.priority);
}
}
}
}
- private int priorityForApnId(int apnId) {
- Integer priority = sApnPriorityMap.get(apnId);
+ private int priorityForApnType(int apnType) {
+ Integer priority = sApnPriorityMap.get(apnType);
return (priority != null ? priority.intValue() : 0);
}
-
- private void loge(String s) {
- Rlog.e(LOG_TAG, s);
- }
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 381a66e..50130c8 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.dataconnection;
+import android.annotation.NonNull;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.ProgressDialog;
@@ -35,7 +36,6 @@
import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
import android.net.NetworkRequest;
-import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
@@ -64,6 +64,7 @@
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.data.ApnSetting;
import android.telephony.data.DataProfile;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
@@ -97,7 +98,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map.Entry;
@@ -122,7 +122,7 @@
private final AlarmManager mAlarmManager;
/* Currently requested APN type (TODO: This should probably be a parameter not a member) */
- private String mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
+ private int mRequestedApnType = ApnSetting.TYPE_DEFAULT;
// All data enabling/disabling related settings
private final DataEnabledSettings mDataEnabledSettings;
@@ -216,6 +216,7 @@
private AsyncChannel mReplyAc = new AsyncChannel();
private final LocalLog mDataRoamingLeakageLog = new LocalLog(50);
+ private final LocalLog mApnSettingsInitializationLog = new LocalLog(50);
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver () {
@Override
@@ -353,10 +354,21 @@
return "{txSum=" + txPkts + " rxSum=" + rxPkts + "}";
}
- public void updateTxRxSum() {
+ /**
+ * Get Tcp Tx/Rx packet count from TrafficStats
+ */
+ public void updateTcpTxRxSum() {
this.txPkts = TrafficStats.getMobileTcpTxPackets();
this.rxPkts = TrafficStats.getMobileTcpRxPackets();
}
+
+ /**
+ * Get total Tx/Rx packet count from TrafficStats
+ */
+ public void updateTotalTxRxSum() {
+ this.txPkts = TrafficStats.getMobileTxPackets();
+ this.rxPkts = TrafficStats.getMobileRxPackets();
+ }
}
private void onActionIntentReconnectAlarm(Intent intent) {
@@ -503,7 +515,7 @@
private final ConcurrentHashMap<String, ApnContext> mApnContexts =
new ConcurrentHashMap<String, ApnContext>();
- private final SparseArray<ApnContext> mApnContextsById = new SparseArray<ApnContext>();
+ private final SparseArray<ApnContext> mApnContextsByType = new SparseArray<ApnContext>();
private int mDisconnectPendingCount = 0;
@@ -731,7 +743,7 @@
mPhone.getContext().getContentResolver().unregisterContentObserver(mApnObserver);
mApnContexts.clear();
- mApnContextsById.clear();
+ mApnContextsByType.clear();
mPrioritySortedApnContexts.clear();
unregisterForAllEvents();
@@ -824,7 +836,7 @@
// data connection.
apnContext.setReason(Phone.REASON_DATA_ENABLED);
cleanUpConnection(true, apnContext);
- } else if (apnContext.getApnSetting().isMetered(mPhone)
+ } else if (ApnSettingUtils.isMetered(apnContext.getApnSetting(), mPhone)
&& (netCaps != null && netCaps.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_METERED))) {
if (DBG) {
@@ -868,40 +880,19 @@
}
public void requestNetwork(NetworkRequest networkRequest, LocalLog log) {
- final int apnId = ApnContext.apnIdForNetworkRequest(networkRequest);
- final ApnContext apnContext = mApnContextsById.get(apnId);
+ final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
+ final ApnContext apnContext = mApnContextsByType.get(apnType);
log.log("DcTracker.requestNetwork for " + networkRequest + " found " + apnContext);
if (apnContext != null) apnContext.requestNetwork(networkRequest, log);
}
public void releaseNetwork(NetworkRequest networkRequest, LocalLog log) {
- final int apnId = ApnContext.apnIdForNetworkRequest(networkRequest);
- final ApnContext apnContext = mApnContextsById.get(apnId);
+ final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
+ final ApnContext apnContext = mApnContextsByType.get(apnType);
log.log("DcTracker.releaseNetwork for " + networkRequest + " found " + apnContext);
if (apnContext != null) apnContext.releaseNetwork(networkRequest, log);
}
- public boolean isApnSupported(String name) {
- if (name == null) {
- loge("isApnSupported: name=null");
- return false;
- }
- ApnContext apnContext = mApnContexts.get(name);
- if (apnContext == null) {
- loge("Request for unsupported mobile name: " + name);
- return false;
- }
- return true;
- }
-
- public int getApnPriority(String name) {
- ApnContext apnContext = mApnContexts.get(name);
- if (apnContext == null) {
- loge("Request for unsupported mobile name: " + name);
- }
- return apnContext.priority;
- }
-
// Turn telephony radio on or off.
private void setRadio(boolean on) {
final ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
@@ -972,7 +963,7 @@
private ApnContext addApnContext(String type, NetworkConfig networkConfig) {
ApnContext apnContext = new ApnContext(mPhone, type, LOG_TAG, networkConfig, this);
mApnContexts.put(type, apnContext);
- mApnContextsById.put(ApnContext.apnIdForApnName(type), apnContext);
+ mApnContextsByType.put(ApnSetting.getApnTypesBitmaskFromString(type), apnContext);
mPrioritySortedApnContexts.add(apnContext);
return apnContext;
}
@@ -1076,19 +1067,37 @@
if (apnContext != null) {
ApnSetting apnSetting = apnContext.getApnSetting();
if (apnSetting != null) {
- return apnSetting.apn;
+ return apnSetting.getApnName();
}
}
return null;
}
- // Return state of specific apn type
+ /**
+ * Returns {@link DctConstants.State} based on the state of the {@link DataConnection} that
+ * contains a {@link ApnSetting} that supported the given apn type {@code anpType}.
+ *
+ * <p>
+ * Assumes there is less than one {@link ApnSetting} can support the given apn type.
+ */
public DctConstants.State getState(String apnType) {
- ApnContext apnContext = mApnContexts.get(apnType);
- if (apnContext != null) {
- return apnContext.getState();
+ final int apnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(apnType);
+ for (DataConnection dc : mDataConnections.values()) {
+ ApnSetting apnSetting = dc.getApnSetting();
+ if (apnSetting != null && apnSetting.canHandleType(apnTypeBitmask)) {
+ if (dc.isActive()) {
+ return DctConstants.State.CONNECTED;
+ } else if (dc.isActivating()) {
+ return DctConstants.State.CONNECTING;
+ } else if (dc.isInactive()) {
+ return DctConstants.State.IDLE;
+ } else if (dc.isDisconnecting()) {
+ return DctConstants.State.DISCONNECTING;
+ }
+ }
}
- return DctConstants.State.FAILED;
+
+ return DctConstants.State.IDLE;
}
// Return if apn type is a provisioning apn.
@@ -1240,7 +1249,7 @@
SubscriptionManager.getDefaultDataSubscriptionId());
boolean isMeteredApnType = apnContext == null
- || ApnSetting.isMeteredApnType(apnContext.getApnType(), mPhone);
+ || ApnSettingUtils.isMeteredApnType(apnContext.getApnType(), mPhone);
PhoneConstants.State phoneState = PhoneConstants.State.IDLE;
// Note this is explicitly not using mPhone.getState. See b/19090488.
@@ -1337,11 +1346,11 @@
reasons.add(DataAllowedReasonType.UNMETERED_APN);
}
- // If the request is restricted and there are only soft disallowed reasons (e.g. data
- // disabled, data roaming disabled) existing, we should allow the data.
+ // If the request is restricted and there are only disallowed reasons due to data
+ // disabled, we should allow the data.
if (apnContext != null
&& !apnContext.hasNoRestrictedRequests(true)
- && !reasons.allowed()) {
+ && reasons.contains(DataDisallowedReasonType.DATA_DISABLED)) {
reasons.add(DataAllowedReasonType.RESTRICTED_REQUEST);
}
@@ -1544,7 +1553,7 @@
// Use ApnSetting to decide metered or non-metered.
// Tear down all metered data connections.
ApnSetting apnSetting = apnContext.getApnSetting();
- if (apnSetting != null && apnSetting.isMetered(mPhone)) {
+ if (apnSetting != null && ApnSettingUtils.isMetered(apnSetting, mPhone)) {
if (apnContext.isDisconnected() == false) didDisconnect = true;
if (DBG) log("clean up metered ApnContext Type: " + apnContext.getApnType());
apnContext.setReason(reason);
@@ -1567,7 +1576,7 @@
stopDataStallAlarm();
// TODO: Do we need mRequestedApnType?
- mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
+ mRequestedApnType = ApnSetting.TYPE_DEFAULT;
log("cleanUpConnection: mDisconnectPendingCount = " + mDisconnectPendingCount);
if (tearDown && mDisconnectPendingCount == 0) {
@@ -1689,27 +1698,27 @@
}
/**
- * Fetch dun apn
- * @return ApnSetting to be used for dun
+ * Fetch the DUN apns
+ * @return a list of DUN ApnSetting objects
*/
@VisibleForTesting
- public ApnSetting fetchDunApn() {
+ public @NonNull ArrayList<ApnSetting> fetchDunApns() {
if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)) {
- log("fetchDunApn: net.tethering.noprovisioning=true ret: null");
- return null;
+ log("fetchDunApns: net.tethering.noprovisioning=true ret: empty list");
+ return new ArrayList<ApnSetting>(0);
}
int bearer = mPhone.getServiceState().getRilDataRadioTechnology();
IccRecords r = mIccRecords.get();
String operator = (r != null) ? r.getOperatorNumeric() : "";
ArrayList<ApnSetting> dunCandidates = new ArrayList<ApnSetting>();
- ApnSetting retDunSetting = null;
+ ArrayList<ApnSetting> retDunSettings = new ArrayList<ApnSetting>();
// Places to look for tether APN in order: TETHER_DUN_APN setting (to be deprecated soon),
// APN database, and config_tether_apndata resource (to be deprecated soon).
String apnData = Settings.Global.getString(mResolver, Settings.Global.TETHER_DUN_APN);
if (!TextUtils.isEmpty(apnData)) {
dunCandidates.addAll(ApnSetting.arrayFromString(apnData));
- if (VDBG) log("fetchDunApn: dunCandidates from Setting: " + dunCandidates);
+ if (VDBG) log("fetchDunApns: dunCandidates from Setting: " + dunCandidates);
}
// todo: remove this and config_tether_apndata after APNs are moved from overlay to apns xml
@@ -1724,48 +1733,60 @@
// apn may be null if apnString isn't valid or has error parsing
if (apn != null) dunCandidates.add(apn);
}
- if (VDBG) log("fetchDunApn: dunCandidates from resource: " + dunCandidates);
+ if (VDBG) log("fetchDunApns: dunCandidates from resource: " + dunCandidates);
}
}
if (dunCandidates.isEmpty()) {
if (!ArrayUtils.isEmpty(mAllApnSettings)) {
for (ApnSetting apn : mAllApnSettings) {
- if (apn.canHandleType(PhoneConstants.APN_TYPE_DUN)) {
+ if (apn.canHandleType(ApnSetting.TYPE_DUN)) {
dunCandidates.add(apn);
}
}
- if (VDBG) log("fetchDunApn: dunCandidates from database: " + dunCandidates);
+ if (VDBG) log("fetchDunApns: dunCandidates from database: " + dunCandidates);
}
}
for (ApnSetting dunSetting : dunCandidates) {
- if (!ServiceState.bitmaskHasTech(dunSetting.networkTypeBitmask,
+ if (!ServiceState.bitmaskHasTech(dunSetting.getNetworkTypeBitmask(),
ServiceState.rilRadioTechnologyToNetworkType(bearer))) {
continue;
}
- if (dunSetting.numeric.equals(operator)) {
+ if (dunSetting.getOperatorNumeric().equals(operator)) {
if (dunSetting.hasMvnoParams()) {
- if (r != null && ApnSetting.mvnoMatches(r, dunSetting.mvnoType,
- dunSetting.mvnoMatchData)) {
- retDunSetting = dunSetting;
- break;
+ if (r != null && ApnSettingUtils.mvnoMatches(r, dunSetting.getMvnoType(),
+ dunSetting.getMvnoMatchData())) {
+ retDunSettings.add(dunSetting);
}
} else if (mMvnoMatched == false) {
- retDunSetting = dunSetting;
- break;
+ retDunSettings.add(dunSetting);
}
}
}
- if (VDBG) log("fetchDunApn: dunSetting=" + retDunSetting);
- return retDunSetting;
+ if (VDBG) log("fetchDunApns: dunSettings=" + retDunSettings);
+ return retDunSettings;
+ }
+
+ private int getPreferredApnSetId() {
+ Cursor c = mPhone.getContext().getContentResolver()
+ .query(Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI,
+ "preferapnset/subId/" + mPhone.getSubId()),
+ new String[] {Telephony.Carriers.APN_SET_ID}, null, null, null);
+ if (c.getCount() < 1) {
+ loge("getPreferredApnSetId: no APNs found");
+ return Telephony.Carriers.NO_SET_SET;
+ } else {
+ c.moveToFirst();
+ return c.getInt(0 /* index of Telephony.Carriers.APN_SET_ID */);
+ }
}
public boolean hasMatchedTetherApnSetting() {
- ApnSetting matched = fetchDunApn();
- log("hasMatchedTetherApnSetting: APN=" + matched);
- return matched != null;
+ ArrayList<ApnSetting> matches = fetchDunApns();
+ log("hasMatchedTetherApnSetting: APNs=" + matches);
+ return matches.size() > 0;
}
/**
@@ -1776,7 +1797,8 @@
final int rilRat = mPhone.getServiceState().getRilDataRadioTechnology();
if (ServiceState.isCdma(rilRat)) return true;
- return (fetchDunApn() != null);
+ ArrayList<ApnSetting> apns = fetchDunApns();
+ return apns.size() > 0;
}
/**
@@ -1797,72 +1819,11 @@
}
}
- /**
- * @param types comma delimited list of APN types
- * @return array of APN types
- */
- private String[] parseTypes(String types) {
- String[] result;
- // If unset, set to DEFAULT.
- if (types == null || types.equals("")) {
- result = new String[1];
- result[0] = PhoneConstants.APN_TYPE_ALL;
- } else {
- result = types.split(",");
- }
- return result;
- }
-
boolean isPermanentFailure(DcFailCause dcFailCause) {
return (dcFailCause.isPermanentFailure(mPhone.getContext(), mPhone.getSubId()) &&
(mAttached.get() == false || dcFailCause != DcFailCause.SIGNAL_LOST));
}
- private ApnSetting makeApnSetting(Cursor cursor) {
- String[] types = parseTypes(
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
- int networkTypeBitmask = cursor.getInt(
- cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
-
- ApnSetting apn = new ApnSetting(
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
- NetworkUtils.trimV4AddrZeros(
- cursor.getString(
- cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)),
- NetworkUtils.trimV4AddrZeros(
- cursor.getString(
- cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
- NetworkUtils.trimV4AddrZeros(
- cursor.getString(
- cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
- types,
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
- cursor.getString(cursor.getColumnIndexOrThrow(
- Telephony.Carriers.ROAMING_PROTOCOL)),
- cursor.getInt(cursor.getColumnIndexOrThrow(
- Telephony.Carriers.CARRIER_ENABLED)) == 1,
- networkTypeBitmask,
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
- cursor.getInt(cursor.getColumnIndexOrThrow(
- Telephony.Carriers.MODEM_COGNITIVE)) == 1,
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS)),
- cursor.getInt(cursor.getColumnIndexOrThrow(
- Telephony.Carriers.WAIT_TIME)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNS_TIME)),
- cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_TYPE)),
- cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_MATCH_DATA)));
- return apn;
- }
-
private ArrayList<ApnSetting> createApnList(Cursor cursor) {
ArrayList<ApnSetting> mnoApns = new ArrayList<ApnSetting>();
ArrayList<ApnSetting> mvnoApns = new ArrayList<ApnSetting>();
@@ -1870,13 +1831,14 @@
if (cursor.moveToFirst()) {
do {
- ApnSetting apn = makeApnSetting(cursor);
+ ApnSetting apn = ApnSetting.makeApnSetting(cursor);
if (apn == null) {
continue;
}
if (apn.hasMvnoParams()) {
- if (r != null && ApnSetting.mvnoMatches(r, apn.mvnoType, apn.mvnoMatchData)) {
+ if (r != null && ApnSettingUtils.mvnoMatches(r, apn.getMvnoType(),
+ apn.getMvnoMatchData())) {
mvnoApns.add(apn);
}
} else {
@@ -1950,7 +1912,7 @@
return false;
}
- int profileId = apnSetting.profileId;
+ int profileId = apnSetting.getProfileId();
if (profileId == 0) {
profileId = getApnProfileID(apnContext.getApnType());
}
@@ -1959,8 +1921,8 @@
// a dun-profiled connection so we can't share an existing one
// On GSM/LTE we can share existing apn connections provided they support
// this type.
- if (apnContext.getApnType() != PhoneConstants.APN_TYPE_DUN ||
- teardownForDun() == false) {
+ if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DUN)
+ || ServiceState.isGsm(mPhone.getServiceState().getRilDataRadioTechnology())) {
dcac = checkForCompatibleConnectedApnContext(apnContext);
if (dcac != null) {
// Get the dcacApnSetting for the connection we want to share.
@@ -2035,7 +1997,7 @@
log("setInitialApn: E mPreferredApn=" + mPreferredApn);
- if (mPreferredApn != null && mPreferredApn.canHandleType(PhoneConstants.APN_TYPE_IA)) {
+ if (mPreferredApn != null && mPreferredApn.canHandleType(ApnSetting.TYPE_IA)) {
iaApnSetting = mPreferredApn;
} else if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
firstApnSetting = mAllApnSettings.get(0);
@@ -2043,13 +2005,13 @@
// Search for Initial APN setting and the first apn that can handle default
for (ApnSetting apn : mAllApnSettings) {
- if (apn.canHandleType(PhoneConstants.APN_TYPE_IA)) {
+ if (apn.canHandleType(ApnSetting.TYPE_IA)) {
// The Initial Attach APN is highest priority so use it if there is one
log("setInitialApn: iaApnSetting=" + apn);
iaApnSetting = apn;
break;
} else if ((defaultApnSetting == null)
- && (apn.canHandleType(PhoneConstants.APN_TYPE_DEFAULT))) {
+ && (apn.canHandleType(ApnSetting.TYPE_DEFAULT))) {
// Use the first default apn if no better choice
log("setInitialApn: defaultApnSetting=" + apn);
defaultApnSetting = apn;
@@ -2325,24 +2287,6 @@
mAutoAttachOnCreation.set(false);
}
- private void onSetDependencyMet(String apnType, boolean met) {
- // don't allow users to tweak hipri to work around default dependency not met
- if (PhoneConstants.APN_TYPE_HIPRI.equals(apnType)) return;
-
- ApnContext apnContext = mApnContexts.get(apnType);
- if (apnContext == null) {
- loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" +
- apnType + ", " + met + ")");
- return;
- }
- applyNewState(apnContext, apnContext.isEnabled(), met);
- if (PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)) {
- // tie actions on default to similar actions on HIPRI regarding dependencyMet
- apnContext = mApnContexts.get(PhoneConstants.APN_TYPE_HIPRI);
- if (apnContext != null) applyNewState(apnContext, apnContext.isEnabled(), met);
- }
- }
-
public void setPolicyDataEnabled(boolean enabled) {
if (DBG) log("setPolicyDataEnabled: " + enabled);
Message msg = obtainMessage(DctConstants.CMD_SET_POLICY_DATA_ENABLE);
@@ -2447,11 +2391,11 @@
}
private DcAsyncChannel checkForCompatibleConnectedApnContext(ApnContext apnContext) {
- String apnType = apnContext.getApnType();
- ApnSetting dunSetting = null;
+ int apnType = apnContext.getApnTypeBitmask();
+ ArrayList<ApnSetting> dunSettings = null;
- if (PhoneConstants.APN_TYPE_DUN.equals(apnType)) {
- dunSetting = fetchDunApn();
+ if (ApnSetting.TYPE_DUN == apnType) {
+ dunSettings = sortApnListByPreferred(fetchDunApns());
}
if (DBG) {
log("checkForCompatibleConnectedApnContext: apnContext=" + apnContext );
@@ -2464,23 +2408,26 @@
if (curDcac != null) {
ApnSetting apnSetting = curApnCtx.getApnSetting();
log("apnSetting: " + apnSetting);
- if (dunSetting != null) {
- if (dunSetting.equals(apnSetting)) {
- switch (curApnCtx.getState()) {
- case CONNECTED:
- if (DBG) {
- log("checkForCompatibleConnectedApnContext:"
- + " found dun conn=" + curDcac
- + " curApnCtx=" + curApnCtx);
- }
- return curDcac;
- case RETRYING:
- case CONNECTING:
- potentialDcac = curDcac;
- potentialApnCtx = curApnCtx;
- default:
- // Not connected, potential unchanged
- break;
+ if (dunSettings != null && dunSettings.size() > 0) {
+ for (ApnSetting dunSetting : dunSettings) {
+ if (dunSetting.equals(apnSetting)) {
+ switch (curApnCtx.getState()) {
+ case CONNECTED:
+ if (DBG) {
+ log("checkForCompatibleConnectedApnContext:"
+ + " found dun conn=" + curDcac
+ + " curApnCtx=" + curApnCtx);
+ }
+ return curDcac;
+ case RETRYING:
+ case CONNECTING:
+ potentialDcac = curDcac;
+ potentialApnCtx = curApnCtx;
+ break;
+ default:
+ // Not connected, potential unchanged
+ break;
+ }
}
}
} else if (apnSetting != null && apnSetting.canHandleType(apnType)) {
@@ -2496,6 +2443,7 @@
case CONNECTING:
potentialDcac = curDcac;
potentialApnCtx = curApnCtx;
+ break;
default:
// Not connected, potential unchanged
break;
@@ -2519,17 +2467,17 @@
return null;
}
- public void setEnabled(int id, boolean enable) {
+ public void setEnabled(int apnType, boolean enable) {
Message msg = obtainMessage(DctConstants.EVENT_ENABLE_NEW_APN);
- msg.arg1 = id;
+ msg.arg1 = apnType;
msg.arg2 = (enable ? DctConstants.ENABLED : DctConstants.DISABLED);
sendMessage(msg);
}
- private void onEnableApn(int apnId, int enabled) {
- ApnContext apnContext = mApnContextsById.get(apnId);
+ private void onEnableApn(int apnType, int enabled) {
+ ApnContext apnContext = mApnContextsByType.get(apnType);
if (apnContext == null) {
- loge("onEnableApn(" + apnId + ", " + enabled + "): NO ApnContext");
+ loge("onEnableApn(" + apnType + ", " + enabled + "): NO ApnContext");
return;
}
// TODO change our retry manager to use the appropriate numbers for the new APN
@@ -2874,18 +2822,20 @@
} else {
ApnSetting apn = apnContext.getApnSetting();
if (DBG) {
- log("onDataSetupComplete: success apn=" + (apn == null ? "unknown" : apn.apn));
+ log("onDataSetupComplete: success apn=" + (apn == null ? "unknown"
+ : apn.getApnName()));
}
- if (apn != null && apn.proxy != null && apn.proxy.length() != 0) {
+ if (apn != null && !TextUtils.isEmpty(apn.getProxyAddressAsString())) {
try {
- String port = apn.port;
- if (TextUtils.isEmpty(port)) port = "8080";
- ProxyInfo proxy = new ProxyInfo(apn.proxy,
- Integer.parseInt(port), null);
+ int port = apn.getProxyPort();
+ if (port == -1) {
+ port = 8080;
+ }
+ ProxyInfo proxy = new ProxyInfo(apn.getProxyAddressAsString(), port, null);
dcac.setLinkPropertiesHttpProxySync(proxy);
} catch (NumberFormatException e) {
- loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" +
- apn.port + "): " + e);
+ loge("onDataSetupComplete: NumberFormatException making ProxyProperties ("
+ + apn.getProxyPort() + "): " + e);
}
}
@@ -2900,7 +2850,7 @@
if (DBG) log("onDataSetupComplete: PREFERRED APN is null");
mPreferredApn = apn;
if (mPreferredApn != null) {
- setPreferredApn(mPreferredApn.id);
+ setPreferredApn(mPreferredApn.getId());
}
}
} else {
@@ -2983,7 +2933,7 @@
if (DBG) {
ApnSetting apn = apnContext.getApnSetting();
log(String.format("onDataSetupComplete: error apn=%s cause=%s",
- (apn == null ? "unknown" : apn.apn), cause));
+ (apn == null ? "unknown" : apn.getApnName()), cause));
}
if (cause.isEventLoggable()) {
// Log this failure to the Event Logs.
@@ -2993,7 +2943,8 @@
}
ApnSetting apn = apnContext.getApnSetting();
mPhone.notifyPreciseDataConnectionFailed(apnContext.getReason(),
- apnContext.getApnType(), apn != null ? apn.apn : "unknown", cause.toString());
+ apnContext.getApnType(), apn != null ? apn.getApnName()
+ : "unknown", cause.toString());
// Compose broadcast intent send to the specific carrier signaling receivers
Intent intent = new Intent(TelephonyIntents
@@ -3221,9 +3172,9 @@
setupDataOnConnectableApns(Phone.REASON_VOICE_CALL_ENDED);
}
- private void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
+ private void onCleanUpConnection(boolean tearDown, int apnType, String reason) {
if (DBG) log("onCleanUpConnection");
- ApnContext apnContext = mApnContextsById.get(apnId);
+ ApnContext apnContext = mApnContextsByType.get(apnType);
if (apnContext != null) {
apnContext.setReason(reason);
cleanUpConnection(tearDown, apnContext);
@@ -3269,7 +3220,7 @@
if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
ArrayList<DataProfile> dps = new ArrayList<DataProfile>();
for (ApnSetting apn : mAllApnSettings) {
- if (apn.modemCognitive) {
+ if (apn.getModemCognitive()) {
DataProfile dp = createDataProfile(apn);
if (!dps.contains(dp)) {
dps.add(dp);
@@ -3307,8 +3258,14 @@
if (cursor != null) {
if (cursor.getCount() > 0) {
mAllApnSettings = createApnList(cursor);
+ } else {
+ if (DBG) log("createAllApnList: cursor count is 0");
+ mApnSettingsInitializationLog.log("no APN in db for carrier: " + operator);
}
cursor.close();
+ } else {
+ if (DBG) log("createAllApnList: cursor is null");
+ mApnSettingsInitializationLog.log("cursor is null for carrier: " + operator);
}
}
@@ -3318,12 +3275,13 @@
if (mAllApnSettings.isEmpty()) {
if (DBG) log("createAllApnList: No APN found for carrier: " + operator);
+ mApnSettingsInitializationLog.log("no APN found for carrier: " + operator);
mPreferredApn = null;
// TODO: What is the right behavior?
//notifyNoData(DataConnection.FailCause.MISSING_UNKNOWN_APN);
} else {
mPreferredApn = getPreferredApn();
- if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
+ if (mPreferredApn != null && !mPreferredApn.getOperatorNumeric().equals(operator)) {
mPreferredApn = null;
setPreferredApn(-1);
}
@@ -3360,36 +3318,33 @@
}
private ApnSetting mergeApns(ApnSetting dest, ApnSetting src) {
- int id = dest.id;
- ArrayList<String> resultTypes = new ArrayList<String>();
- resultTypes.addAll(Arrays.asList(dest.types));
- for (String srcType : src.types) {
- if (resultTypes.contains(srcType) == false) resultTypes.add(srcType);
- if (srcType.equals(PhoneConstants.APN_TYPE_DEFAULT)) id = src.id;
+ int id = dest.getId();
+ if ((src.getApnTypeBitmask() & ApnSetting.TYPE_DEFAULT) == ApnSetting.TYPE_DEFAULT) {
+ id = src.getId();
}
- String mmsc = (TextUtils.isEmpty(dest.mmsc) ? src.mmsc : dest.mmsc);
- String mmsProxy = (TextUtils.isEmpty(dest.mmsProxy) ? src.mmsProxy : dest.mmsProxy);
- String mmsPort = (TextUtils.isEmpty(dest.mmsPort) ? src.mmsPort : dest.mmsPort);
- String proxy = (TextUtils.isEmpty(dest.proxy) ? src.proxy : dest.proxy);
- String port = (TextUtils.isEmpty(dest.port) ? src.port : dest.port);
- String protocol = src.protocol.equals("IPV4V6") ? src.protocol : dest.protocol;
- String roamingProtocol = src.roamingProtocol.equals("IPV4V6") ? src.roamingProtocol :
- dest.roamingProtocol;
- int networkTypeBitmask = (dest.networkTypeBitmask == 0 || src.networkTypeBitmask == 0)
- ? 0 : (dest.networkTypeBitmask | src.networkTypeBitmask);
- if (networkTypeBitmask == 0) {
- int bearerBitmask = (dest.bearerBitmask == 0 || src.bearerBitmask == 0)
- ? 0 : (dest.bearerBitmask | src.bearerBitmask);
- networkTypeBitmask = ServiceState.convertBearerBitmaskToNetworkTypeBitmask(
- bearerBitmask);
- }
+ final int resultApnType = src.getApnTypeBitmask() | dest.getApnTypeBitmask();
+ Uri mmsc = (dest.getMmsc() == null ? src.getMmsc() : dest.getMmsc());
+ String mmsProxy = TextUtils.isEmpty(dest.getMmsProxyAddressAsString())
+ ? src.getMmsProxyAddressAsString() : dest.getMmsProxyAddressAsString();
+ int mmsPort = dest.getMmsProxyPort() == -1 ? src.getMmsProxyPort() : dest.getMmsProxyPort();
+ String proxy = TextUtils.isEmpty(dest.getProxyAddressAsString())
+ ? src.getProxyAddressAsString() : dest.getProxyAddressAsString();
+ int port = dest.getProxyPort() == -1 ? src.getProxyPort() : dest.getProxyPort();
+ int protocol = src.getProtocol() == ApnSetting.PROTOCOL_IPV4V6 ? src.getProtocol()
+ : dest.getProtocol();
+ int roamingProtocol = src.getRoamingProtocol() == ApnSetting.PROTOCOL_IPV4V6
+ ? src.getRoamingProtocol() : dest.getRoamingProtocol();
+ int networkTypeBitmask = (dest.getNetworkTypeBitmask() == 0
+ || src.getNetworkTypeBitmask() == 0)
+ ? 0 : (dest.getNetworkTypeBitmask() | src.getNetworkTypeBitmask());
- return new ApnSetting(id, dest.numeric, dest.carrier, dest.apn,
- proxy, port, mmsc, mmsProxy, mmsPort, dest.user, dest.password,
- dest.authType, resultTypes.toArray(new String[0]), protocol,
- roamingProtocol, dest.carrierEnabled, networkTypeBitmask, dest.profileId,
- (dest.modemCognitive || src.modemCognitive), dest.maxConns, dest.waitTime,
- dest.maxConnsTime, dest.mtu, dest.mvnoType, dest.mvnoMatchData);
+ return ApnSetting.makeApnSetting(id, dest.getOperatorNumeric(), dest.getEntryName(),
+ dest.getApnName(), proxy, port, mmsc, mmsProxy, mmsPort, dest.getUser(),
+ dest.getPassword(), dest.getAuthType(), resultApnType, protocol, roamingProtocol,
+ dest.isEnabled(), networkTypeBitmask, dest.getProfileId(),
+ (dest.getModemCognitive() || src.getModemCognitive()), dest.getMaxConns(),
+ dest.getWaitTime(), dest.getMaxConnsTime(), dest.getMtu(), dest.getMvnoType(),
+ dest.getMvnoMatchData(), dest.getApnSetId());
}
/** Return the DC AsyncChannel for the new data connection */
@@ -3432,12 +3387,15 @@
if (DBG) log("buildWaitingApns: E requestedApnType=" + requestedApnType);
ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
- if (requestedApnType.equals(PhoneConstants.APN_TYPE_DUN)) {
- ApnSetting dun = fetchDunApn();
- if (dun != null) {
- apnList.add(dun);
- if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
- return apnList;
+ int requestedApnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(requestedApnType);
+ if (requestedApnTypeBitmask == ApnSetting.TYPE_DUN) {
+ ArrayList<ApnSetting> dunApns = fetchDunApns();
+ if (dunApns.size() > 0) {
+ for (ApnSetting dun : dunApns) {
+ apnList.add(dun);
+ if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
+ }
+ return sortApnListByPreferred(apnList);
}
}
@@ -3469,15 +3427,16 @@
}
if (usePreferred && mCanSetPreferApn && mPreferredApn != null &&
- mPreferredApn.canHandleType(requestedApnType)) {
+ mPreferredApn.canHandleType(requestedApnTypeBitmask)) {
if (DBG) {
log("buildWaitingApns: Preferred APN:" + operator + ":"
- + mPreferredApn.numeric + ":" + mPreferredApn);
+ + mPreferredApn.getOperatorNumeric() + ":" + mPreferredApn);
}
- if (mPreferredApn.numeric.equals(operator)) {
- if (ServiceState.bitmaskHasTech(mPreferredApn.networkTypeBitmask,
+ if (mPreferredApn.getOperatorNumeric().equals(operator)) {
+ if (ServiceState.bitmaskHasTech(mPreferredApn.getNetworkTypeBitmask(),
ServiceState.rilRadioTechnologyToNetworkType(radioTech))) {
apnList.add(mPreferredApn);
+ apnList = sortApnListByPreferred(apnList);
if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
return apnList;
} else {
@@ -3494,15 +3453,15 @@
if (mAllApnSettings != null) {
if (DBG) log("buildWaitingApns: mAllApnSettings=" + mAllApnSettings);
for (ApnSetting apn : mAllApnSettings) {
- if (apn.canHandleType(requestedApnType)) {
- if (ServiceState.bitmaskHasTech(apn.networkTypeBitmask,
+ if (apn.canHandleType(requestedApnTypeBitmask)) {
+ if (ServiceState.bitmaskHasTech(apn.getNetworkTypeBitmask(),
ServiceState.rilRadioTechnologyToNetworkType(radioTech))) {
if (DBG) log("buildWaitingApns: adding apn=" + apn);
apnList.add(apn);
} else {
if (DBG) {
- log("buildWaitingApns: bearerBitmask:" + apn.bearerBitmask
- + " or " + "networkTypeBitmask:" + apn.networkTypeBitmask
+ log("buildWaitingApns: networkTypeBitmask:"
+ + apn.getNetworkTypeBitmask()
+ "do not include radioTech:" + radioTech);
}
}
@@ -3514,10 +3473,45 @@
} else {
loge("mAllApnSettings is null!");
}
+
+ apnList = sortApnListByPreferred(apnList);
if (DBG) log("buildWaitingApns: " + apnList.size() + " APNs in the list: " + apnList);
return apnList;
}
+ /**
+ * Sort a list of ApnSetting objects, with the preferred APNs at the front of the list
+ *
+ * e.g. if the preferred APN set = 2 and we have
+ * 1. APN with apn_set_id = 0 = Carriers.NO_SET_SET (no set is set)
+ * 2. APN with apn_set_id = 1 (not preferred set)
+ * 3. APN with apn_set_id = 2 (preferred set)
+ * Then the return order should be (3, 1, 2) or (3, 2, 1)
+ *
+ * e.g. if the preferred APN set = Carriers.NO_SET_SET (no preferred set) then the
+ * return order can be anything
+ */
+ @VisibleForTesting
+ public ArrayList<ApnSetting> sortApnListByPreferred(ArrayList<ApnSetting> list) {
+ if (list == null || list.size() <= 1) return list;
+ int preferredApnSetId = getPreferredApnSetId();
+ if (preferredApnSetId != Telephony.Carriers.NO_SET_SET) {
+ list.sort(new Comparator<ApnSetting>() {
+ @Override
+ public int compare(ApnSetting apn1, ApnSetting apn2) {
+ if (apn1.getApnSetId() == preferredApnSetId) {
+ return -1;
+ }
+ if (apn2.getApnSetId() == preferredApnSetId) {
+ return 1;
+ }
+ return 0;
+ }
+ });
+ }
+ return list;
+ }
+
private String apnListToString (ArrayList<ApnSetting> apns) {
StringBuilder result = new StringBuilder();
for (int i = 0, size = apns.size(); i < size; i++) {
@@ -3574,7 +3568,7 @@
pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
for(ApnSetting p : mAllApnSettings) {
log("getPreferredApn: apnSetting=" + p);
- if (p.id == pos && p.canHandleType(mRequestedApnType)) {
+ if (p.getId() == pos && p.canHandleType(mRequestedApnType)) {
log("getPreferredApn: X found apnSetting" + p);
cursor.close();
return p;
@@ -3651,7 +3645,7 @@
cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
mReregisterOnReconnectFailure = false;
}
- ApnContext apnContext = mApnContextsById.get(DctConstants.APN_DEFAULT_ID);
+ ApnContext apnContext = mApnContextsByType.get(ApnSetting.TYPE_DEFAULT);
if (apnContext != null) {
apnContext.setReason(Phone.REASON_PS_RESTRICT_ENABLED);
trySetupData(apnContext);
@@ -3789,19 +3783,6 @@
onSetUserDataEnabled(enabled);
break;
}
- // TODO - remove
- case DctConstants.CMD_SET_DEPENDENCY_MET: {
- boolean met = (msg.arg1 == DctConstants.ENABLED) ? true : false;
- if (DBG) log("CMD_SET_DEPENDENCY_MET met=" + met);
- Bundle bundle = msg.getData();
- if (bundle != null) {
- String apnType = (String)bundle.get(DctConstants.APN_TYPE_KEY);
- if (apnType != null) {
- onSetDependencyMet(apnType, met);
- }
- }
- break;
- }
case DctConstants.CMD_SET_POLICY_DATA_ENABLE: {
final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
onSetPolicyDataEnabled(enabled);
@@ -3867,7 +3848,7 @@
}
case DctConstants.EVENT_PROVISIONING_APN_ALARM: {
if (DBG) log("EVENT_PROVISIONING_APN_ALARM");
- ApnContext apnCtx = mApnContextsById.get(DctConstants.APN_DEFAULT_ID);
+ ApnContext apnCtx = mApnContextsByType.get(ApnSetting.TYPE_DEFAULT);
if (apnCtx.isProvisioningApn() && apnCtx.isConnectedOrConnecting()) {
if (mProvisioningApnAlarmTag == msg.arg1) {
if (DBG) log("EVENT_PROVISIONING_APN_ALARM: Disconnecting");
@@ -3939,6 +3920,7 @@
break;
case DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED:
onDataServiceBindingChanged((Boolean) ((AsyncResult) msg.obj).result);
+ break;
default:
Rlog.e("DcTracker", "Unhandled event=" + msg);
break;
@@ -4141,6 +4123,8 @@
pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator);
pw.println(" mDataRoamingLeakageLog= ");
mDataRoamingLeakageLog.dump(fd, pw, args);
+ pw.println(" mApnSettingsInitializationLog= ");
+ mApnSettingsInitializationLog.dump(fd, pw, args);
pw.flush();
pw.println(" ***************************************");
DcController dcc = mDcc;
@@ -4221,9 +4205,9 @@
}
if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_EMERGENCY)) {
- apnContext = mApnContextsById.get(DctConstants.APN_EMERGENCY_ID);
+ apnContext = mApnContextsByType.get(ApnSetting.TYPE_EMERGENCY);
} else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IMS)) {
- apnContext = mApnContextsById.get(DctConstants.APN_IMS_ID);
+ apnContext = mApnContextsByType.get(ApnSetting.TYPE_IMS);
} else {
log("apnType is invalid, return null");
return null;
@@ -4268,7 +4252,7 @@
if (cursor != null) {
if (cursor.getCount() > 0) {
if (cursor.moveToFirst()) {
- mEmergencyApn = makeApnSetting(cursor);
+ mEmergencyApn = ApnSetting.makeApnSetting(cursor);
}
}
cursor.close();
@@ -4285,7 +4269,7 @@
} else {
boolean hasEmergencyApn = false;
for (ApnSetting apn : mAllApnSettings) {
- if (ArrayUtils.contains(apn.types, PhoneConstants.APN_TYPE_EMERGENCY)) {
+ if ((apn.getApnTypeBitmask() & ApnSetting.TYPE_EMERGENCY) > 0) {
hasEmergencyApn = true;
break;
}
@@ -4357,7 +4341,7 @@
stopDataStallAlarm();
}
- mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
+ mRequestedApnType = ApnSetting.TYPE_DEFAULT;
if (DBG) log("mDisconnectPendingCount = " + mDisconnectPendingCount);
if (tearDown && mDisconnectPendingCount == 0) {
@@ -4436,7 +4420,7 @@
TxRxSum preTxRxSum = new TxRxSum(mTxPkts, mRxPkts);
TxRxSum curTxRxSum = new TxRxSum();
- curTxRxSum.updateTxRxSum();
+ curTxRxSum.updateTotalTxRxSum();
mTxPkts = curTxRxSum.txPkts;
mRxPkts = curTxRxSum.rxPkts;
@@ -4600,7 +4584,7 @@
long sent, received;
TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum);
- mDataStallTxRxSum.updateTxRxSum();
+ mDataStallTxRxSum.updateTcpTxRxSum();
if (VDBG_STALL) {
log("updateDataStallInfo: mDataStallTxRxSum=" + mDataStallTxRxSum +
@@ -4792,7 +4776,7 @@
}
private static DataProfile createDataProfile(ApnSetting apn) {
- return createDataProfile(apn, apn.profileId);
+ return createDataProfile(apn, apn.getProfileId());
}
@VisibleForTesting
@@ -4801,7 +4785,7 @@
int bearerBitmap = 0;
bearerBitmap = ServiceState.convertNetworkTypeBitmaskToBearerBitmask(
- apn.networkTypeBitmask);
+ apn.getNetworkTypeBitmask());
if (bearerBitmap == 0) {
profileType = DataProfile.TYPE_COMMON;
@@ -4811,11 +4795,13 @@
profileType = DataProfile.TYPE_3GPP;
}
- return new DataProfile(profileId, apn.apn, apn.protocol,
- apn.authType, apn.user, apn.password, profileType,
- apn.maxConnsTime, apn.maxConns, apn.waitTime, apn.carrierEnabled, apn.typesBitmap,
- apn.roamingProtocol, bearerBitmap, apn.mtu, apn.mvnoType, apn.mvnoMatchData,
- apn.modemCognitive);
+ return new DataProfile(profileId, apn.getApnName(),
+ ApnSetting.getProtocolStringFromInt(apn.getProtocol()), apn.getAuthType(),
+ apn.getUser(), apn.getPassword(), profileType, apn.getMaxConnsTime(),
+ apn.getMaxConns(), apn.getWaitTime(), apn.isEnabled(), apn.getApnTypeBitmask(),
+ ApnSetting.getProtocolStringFromInt(apn.getRoamingProtocol()), bearerBitmap,
+ apn.getMtu(), ApnSetting.getMvnoTypeStringFromInt(apn.getMvnoType()),
+ apn.getMvnoMatchData(), apn.getModemCognitive());
}
private void onDataServiceBindingChanged(boolean bound) {
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
index 2326091..d3c59a3 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
@@ -39,6 +39,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.uicc.UiccSlot;
import com.android.internal.telephony.uicc.euicc.EuiccCard;
@@ -179,7 +180,7 @@
return (EuiccCard) controller.getUiccCardForSlot(slotId);
}
}
- Log.e(TAG, "EuiccCard is null. CardId : " + cardId);
+ loge("EuiccCard is null. CardId : " + cardId);
return null;
}
@@ -200,7 +201,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getAllProfiles callback failure.", exception);
}
return;
}
@@ -212,7 +213,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getAllProfiles callback failure.", exception);
}
}
@@ -221,7 +222,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getAllProfiles callback failure.", exception);
}
}
};
@@ -239,7 +240,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getProfile callback failure.", exception);
}
return;
}
@@ -250,7 +251,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getProfile callback failure.", exception);
}
}
@@ -259,7 +260,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getProfile callback failure.", exception);
}
}
};
@@ -277,7 +278,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("disableProfile callback failure.", exception);
}
return;
}
@@ -288,7 +289,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("disableProfile callback failure.", exception);
}
}
@@ -297,7 +298,7 @@
try {
callback.onComplete(getResultCode(e));
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("disableProfile callback failure.", exception);
}
}
};
@@ -315,7 +316,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("switchToProfile callback failure.", exception);
}
return;
}
@@ -330,7 +331,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, profile);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("switchToProfile callback failure.", exception);
}
}
@@ -339,7 +340,7 @@
try {
callback.onComplete(getResultCode(e), profile);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("switchToProfile callback failure.", exception);
}
}
};
@@ -352,7 +353,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("switchToProfile callback failure.", exception);
}
}
};
@@ -370,7 +371,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("setNickname callback failure.", exception);
}
return;
}
@@ -381,7 +382,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("setNickname callback failure.", exception);
}
}
@@ -390,7 +391,7 @@
try {
callback.onComplete(getResultCode(e));
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("setNickname callback failure.", exception);
}
}
};
@@ -408,7 +409,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("deleteProfile callback failure.", exception);
}
return;
}
@@ -416,19 +417,22 @@
AsyncResultCallback<Void> cardCb = new AsyncResultCallback<Void>() {
@Override
public void onResult(Void result) {
- try {
- callback.onComplete(EuiccCardManager.RESULT_OK);
- } catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
- }
- }
+ SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh(
+ () -> {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK);
+ } catch (RemoteException exception) {
+ loge("deleteProfile callback failure.", exception);
+ }
+ });
+ };
@Override
public void onException(Throwable e) {
try {
callback.onComplete(getResultCode(e));
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("deleteProfile callback failure.", exception);
}
}
};
@@ -446,7 +450,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("resetMemory callback failure.", exception);
}
return;
}
@@ -454,11 +458,14 @@
AsyncResultCallback<Void> cardCb = new AsyncResultCallback<Void>() {
@Override
public void onResult(Void result) {
- try {
- callback.onComplete(EuiccCardManager.RESULT_OK);
- } catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
- }
+ SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh(
+ () -> {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK);
+ } catch (RemoteException exception) {
+ loge("resetMemory callback failure.", exception);
+ }
+ });
}
@Override
@@ -466,7 +473,7 @@
try {
callback.onComplete(getResultCode(e));
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("resetMemory callback failure.", exception);
}
}
};
@@ -484,7 +491,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getDefaultSmdpAddress callback failure.", exception);
}
return;
}
@@ -495,7 +502,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getDefaultSmdpAddress callback failure.", exception);
}
}
@@ -504,7 +511,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getDefaultSmdpAddress callback failure.", exception);
}
}
};
@@ -522,7 +529,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getSmdsAddress callback failure.", exception);
}
return;
}
@@ -533,7 +540,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getSmdsAddress callback failure.", exception);
}
}
@@ -542,7 +549,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getSmdsAddress callback failure.", exception);
}
}
};
@@ -560,7 +567,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("setDefaultSmdpAddress callback failure.", exception);
}
return;
}
@@ -571,7 +578,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("setDefaultSmdpAddress callback failure.", exception);
}
}
@@ -580,7 +587,7 @@
try {
callback.onComplete(getResultCode(e));
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("setDefaultSmdpAddress callback failure.", exception);
}
}
};
@@ -598,7 +605,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getRulesAuthTable callback failure.", exception);
}
return;
}
@@ -610,7 +617,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getRulesAuthTable callback failure.", exception);
}
}
@@ -619,7 +626,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getRulesAuthTable callback failure.", exception);
}
}
};
@@ -637,7 +644,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getEuiccChallenge callback failure.", exception);
}
return;
}
@@ -648,7 +655,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getEuiccChallenge callback failure.", exception);
}
}
@@ -657,7 +664,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getEuiccChallenge callback failure.", exception);
}
}
};
@@ -675,7 +682,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getEuiccInfo1 callback failure.", exception);
}
return;
}
@@ -686,7 +693,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getEuiccInfo1 callback failure.", exception);
}
}
@@ -695,7 +702,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getEuiccInfo1 callback failure.", exception);
}
}
};
@@ -713,7 +720,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getEuiccInfo2 callback failure.", exception);
}
return;
}
@@ -724,7 +731,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getEuiccInfo2 callback failure.", exception);
}
}
@@ -733,7 +740,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("getEuiccInfo2 callback failure.", exception);
}
}
};
@@ -752,7 +759,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("authenticateServer callback failure.", exception);
}
return;
}
@@ -763,7 +770,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("authenticateServer callback failure.", exception);
}
}
@@ -772,7 +779,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("authenticateServer callback failure.", exception);
}
}
};
@@ -792,7 +799,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("prepareDownload callback failure.", exception);
}
return;
}
@@ -803,7 +810,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("prepareDownload callback failure.", exception);
}
}
@@ -812,7 +819,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("prepareDownload callback failure.", exception);
}
}
};
@@ -831,7 +838,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("loadBoundProfilePackage callback failure.", exception);
}
return;
}
@@ -839,11 +846,14 @@
AsyncResultCallback<byte[]> cardCb = new AsyncResultCallback<byte[]>() {
@Override
public void onResult(byte[] result) {
- try {
- callback.onComplete(EuiccCardManager.RESULT_OK, result);
- } catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
- }
+ SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh(
+ () -> {
+ try {
+ callback.onComplete(EuiccCardManager.RESULT_OK, result);
+ } catch (RemoteException exception) {
+ loge("loadBoundProfilePackage callback failure.", exception);
+ }
+ });
}
@Override
@@ -851,7 +861,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("loadBoundProfilePackage callback failure.", exception);
}
}
};
@@ -869,7 +879,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("cancelSession callback failure.", exception);
}
return;
}
@@ -880,7 +890,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("cancelSession callback failure.", exception);
}
}
@@ -889,7 +899,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("cancelSession callback failure.", exception);
}
}
};
@@ -907,7 +917,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("listNotifications callback failure.", exception);
}
return;
}
@@ -919,7 +929,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("listNotifications callback failure.", exception);
}
}
@@ -928,7 +938,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("listNotifications callback failure.", exception);
}
}
};
@@ -946,7 +956,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("retrieveNotificationList callback failure.", exception);
}
return;
}
@@ -958,7 +968,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("retrieveNotificationList callback failure.", exception);
}
}
@@ -967,7 +977,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("retrieveNotificationList callback failure.", exception);
}
}
};
@@ -985,7 +995,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND, null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("retrieveNotification callback failure.", exception);
}
return;
}
@@ -997,7 +1007,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK, result);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("retrieveNotification callback failure.", exception);
}
}
@@ -1006,7 +1016,7 @@
try {
callback.onComplete(getResultCode(e), null);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("retrieveNotification callback failure.", exception);
}
}
};
@@ -1024,7 +1034,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_EUICC_NOT_FOUND);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("removeNotificationFromList callback failure.", exception);
}
return;
}
@@ -1035,7 +1045,7 @@
try {
callback.onComplete(EuiccCardManager.RESULT_OK);
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("removeNotificationFromList callback failure.", exception);
}
}
@@ -1045,7 +1055,7 @@
try {
callback.onComplete(getResultCode(e));
} catch (RemoteException exception) {
- throw exception.rethrowFromSystemServer();
+ loge("removeNotificationFromList callback failure.", exception);
}
}
};
@@ -1065,4 +1075,12 @@
Binder.restoreCallingIdentity(token);
}
+
+ private static void loge(String message) {
+ Log.e(TAG, message);
+ }
+
+ private static void loge(String message, Throwable tr) {
+ Log.e(TAG, message, tr);
+ }
}
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index 27018b5..8c1b81e 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -69,6 +69,8 @@
EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR;
private static final int ERROR =
EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR;
+ private static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION =
+ EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION;
private static EuiccController sInstance;
@@ -263,7 +265,7 @@
case EuiccService.RESULT_OK:
resultCode = OK;
extrasIntent.putExtra(
- EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION,
+ EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION,
result.getDownloadableSubscription());
break;
case EuiccService.RESULT_MUST_DEACTIVATE_SIM:
@@ -446,6 +448,9 @@
mContext.getContentResolver(),
Settings.Global.EUICC_PROVISIONED,
1);
+ extrasIntent.putExtra(
+ EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION,
+ subscription);
if (!switchAfterDownload) {
// Since we're not switching, nothing will trigger a
// subscription list refresh on its own, so request one here.
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 8c4e9aa..99082ee 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -16,13 +16,8 @@
package com.android.internal.telephony.gsm;
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.content.Intent;
import android.os.AsyncResult;
import android.os.Message;
-import android.provider.Telephony.Sms;
import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
import android.telephony.ServiceState;
@@ -106,8 +101,9 @@
}
@Override
- protected boolean shouldBlockSms() {
- return SMSDispatcherUtil.shouldBlockSms(isCdmaMo(), mPhone);
+ protected boolean shouldBlockSmsForEcbm() {
+ // There is no such thing as ECBM for GSM. This only applies to CDMA.
+ return false;
}
@Override
@@ -187,6 +183,7 @@
+ " mRetryCount=" + tracker.mRetryCount
+ " mImsRetry=" + tracker.mImsRetry
+ " mMessageRef=" + tracker.mMessageRef
+ + " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
+ " SS=" + mPhone.getServiceState().getState());
int ss = mPhone.getServiceState().getState();
@@ -202,8 +199,11 @@
// sms over gsm is used:
// if sms over IMS is not supported AND
// this is not a retry case after sms over IMS failed
- // indicated by mImsRetry > 0
- if (0 == tracker.mImsRetry && !isIms()) {
+ // indicated by mImsRetry > 0 OR
+ // this tracker uses ImsSmsDispatcher to handle SMS over IMS. This dispatcher has received
+ // this message because the ImsSmsDispatcher has indicated that the message needs to
+ // fall back to sending over CS.
+ if (0 == tracker.mImsRetry && !isIms() || tracker.mUsesImsServiceForIms) {
if (tracker.mRetryCount == 0 && tracker.mExpectMore) {
mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc),
IccUtils.bytesToHexString(pdu), reply);
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index 081d41c..f0aee01 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -28,6 +28,8 @@
import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.telephony.CarrierConfigManager;
@@ -38,21 +40,21 @@
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.Log;
-import android.util.Pair;
import android.util.SparseArray;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.os.SomeArgs;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
-import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -78,6 +80,10 @@
"android.telephony.ims.EMERGENCY_MMTEL_FEATURE";
public static final String METADATA_MMTEL_FEATURE = "android.telephony.ims.MMTEL_FEATURE";
public static final String METADATA_RCS_FEATURE = "android.telephony.ims.RCS_FEATURE";
+ // Overrides the sanity permission check of android.permission.BIND_IMS_SERVICE for any
+ // ImsService that is connecting to the platform.
+ // This should ONLY be used for testing and should not be used in production ImsServices.
+ private static final String METADATA_OVERRIDE_PERM_CHECK = "override_bind_check";
// Based on updates from PackageManager
private static final int HANDLER_ADD_PACKAGE = 0;
@@ -85,6 +91,16 @@
private static final int HANDLER_REMOVE_PACKAGE = 1;
// Based on updates from CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED
private static final int HANDLER_CONFIG_CHANGED = 2;
+ // A query has been started for an ImsService to relay the features they support.
+ private static final int HANDLER_START_DYNAMIC_FEATURE_QUERY = 3;
+ // A query to request ImsService features has completed or the ImsService has updated features.
+ private static final int HANDLER_DYNAMIC_FEATURE_CHANGE = 4;
+ // Testing: Overrides the current configuration for ImsService binding
+ private static final int HANDLER_OVERRIDE_IMS_SERVICE_CONFIG = 5;
+
+ // Delay between dynamic ImsService queries.
+ private static final int DELAY_DYNAMIC_QUERY_MS = 5000;
+
/**
* Stores information about an ImsService, including the package name, class name, and features
@@ -93,10 +109,36 @@
@VisibleForTesting
public static class ImsServiceInfo {
public ComponentName name;
- public Set<Integer> supportedFeatures;
- public boolean supportsEmergencyMmTel = false;
+ // Determines if features were created from metadata in the manifest or through dynamic
+ // query.
+ public boolean featureFromMetadata = true;
public ImsServiceControllerFactory controllerFactory;
+ // Map slotId->Feature
+ private final HashSet<ImsFeatureConfiguration.FeatureSlotPair> mSupportedFeatures;
+ private final int mNumSlots;
+
+ public ImsServiceInfo(int numSlots) {
+ mNumSlots = numSlots;
+ mSupportedFeatures = new HashSet<>();
+ }
+
+ void addFeatureForAllSlots(int feature) {
+ for (int i = 0; i < mNumSlots; i++) {
+ mSupportedFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(i, feature));
+ }
+ }
+
+ void replaceFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> newFeatures) {
+ mSupportedFeatures.clear();
+ mSupportedFeatures.addAll(newFeatures);
+ }
+
+ @VisibleForTesting
+ public HashSet<ImsFeatureConfiguration.FeatureSlotPair> getSupportedFeatures() {
+ return mSupportedFeatures;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -104,10 +146,8 @@
ImsServiceInfo that = (ImsServiceInfo) o;
- if (supportsEmergencyMmTel != that.supportsEmergencyMmTel) return false;
if (name != null ? !name.equals(that.name) : that.name != null) return false;
- if (supportedFeatures != null ? !supportedFeatures.equals(that.supportedFeatures)
- : that.supportedFeatures != null) {
+ if (!mSupportedFeatures.equals(that.mSupportedFeatures)) {
return false;
}
return controllerFactory != null ? controllerFactory.equals(that.controllerFactory)
@@ -116,12 +156,28 @@
@Override
public int hashCode() {
+ // We do not include mSupportedFeatures in hashcode because the internal structure
+ // changes after adding.
int result = name != null ? name.hashCode() : 0;
- result = 31 * result + (supportedFeatures != null ? supportedFeatures.hashCode() : 0);
- result = 31 * result + (supportsEmergencyMmTel ? 1 : 0);
result = 31 * result + (controllerFactory != null ? controllerFactory.hashCode() : 0);
return result;
}
+
+ @Override
+ public String toString() {
+ StringBuilder res = new StringBuilder();
+ res.append("[ImsServiceInfo] name=");
+ res.append(name);
+ res.append(", supportedFeatures=[ ");
+ for (ImsFeatureConfiguration.FeatureSlotPair feature : mSupportedFeatures) {
+ res.append("(");
+ res.append(feature.slotId);
+ res.append(",");
+ res.append(feature.featureType);
+ res.append(") ");
+ }
+ return res.toString();
+ }
}
// Receives broadcasts from the system involving changes to the installed applications. If
@@ -134,6 +190,8 @@
switch (action) {
case Intent.ACTION_PACKAGE_ADDED:
// intentional fall-through
+ case Intent.ACTION_PACKAGE_REPLACED:
+ // intentional fall-through
case Intent.ACTION_PACKAGE_CHANGED:
mHandler.obtainMessage(HANDLER_ADD_PACKAGE, packageName).sendToTarget();
break;
@@ -152,17 +210,17 @@
@Override
public void onReceive(Context context, Intent intent) {
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ int slotId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX,
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX);
- if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- Log.i(TAG, "Received SIM change for invalid sub id.");
+ if (slotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ Log.i(TAG, "Received SIM change for invalid slot id.");
return;
}
- Log.i(TAG, "Received Carrier Config Changed for SubId: " + subId);
+ Log.i(TAG, "Received Carrier Config Changed for SlotId: " + slotId);
- mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, subId).sendToTarget();
+ mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, slotId).sendToTarget();
}
};
@@ -229,6 +287,15 @@
}
};
+ /**
+ * Used for testing.
+ */
+ @VisibleForTesting
+ public interface ImsDynamicQueryManagerFactory {
+ ImsServiceFeatureQueryManager create(Context context,
+ ImsServiceFeatureQueryManager.Listener listener);
+ }
+
private ImsServiceControllerFactory mImsServiceControllerFactoryCompat =
new ImsServiceControllerFactory() {
@Override
@@ -258,6 +325,9 @@
}
};
+ private ImsDynamicQueryManagerFactory mDynamicQueryManagerFactory =
+ ImsServiceFeatureQueryManager::new;
+
private final CarrierConfigManager mCarrierConfigManager;
private final Context mContext;
// Locks mBoundImsServicesByFeature only. Be careful to avoid deadlocks from
@@ -265,6 +335,8 @@
private final Object mBoundServicesLock = new Object();
private final int mNumSlots;
private final boolean mIsDynamicBinding;
+ // Package name of the default device service.
+ private String mDeviceService;
// Synchronize all messages on a handler to ensure that the cache includes the most recent
// version of the installed ImsServices.
@@ -281,8 +353,52 @@
break;
}
case HANDLER_CONFIG_CHANGED: {
- int subId = (Integer) msg.obj;
- maybeRebindService(subId);
+ int slotId = (Integer) msg.obj;
+ carrierConfigChanged(slotId);
+ break;
+ }
+ case HANDLER_START_DYNAMIC_FEATURE_QUERY: {
+ ImsServiceInfo info = (ImsServiceInfo) msg.obj;
+ startDynamicQuery(info);
+ break;
+ }
+ case HANDLER_DYNAMIC_FEATURE_CHANGE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ ComponentName name = (ComponentName) args.arg1;
+ Set<ImsFeatureConfiguration.FeatureSlotPair> features =
+ (Set<ImsFeatureConfiguration.FeatureSlotPair>) args.arg2;
+ args.recycle();
+ dynamicQueryComplete(name, features);
+ break;
+ }
+ case HANDLER_OVERRIDE_IMS_SERVICE_CONFIG: {
+ int slotId = msg.arg1;
+ // arg2 will be equal to 1 if it is a carrier service.
+ boolean isCarrierImsService = (msg.arg2 == 1);
+ String packageName = (String) msg.obj;
+ if (isCarrierImsService) {
+ Log.i(TAG, "overriding carrier ImsService - slot=" + slotId + " packageName="
+ + packageName);
+ maybeRebindService(slotId, packageName);
+ } else {
+ Log.i(TAG, "overriding device ImsService - packageName=" + packageName);
+ if (packageName == null || packageName.isEmpty()) {
+ unbindImsService(getImsServiceInfoFromCache(mDeviceService));
+ }
+ mDeviceService = packageName;
+ ImsServiceInfo deviceInfo = getImsServiceInfoFromCache(mDeviceService);
+ if (deviceInfo == null) {
+ // The package name is either "" or does not exist on the device.
+ break;
+ }
+ if (deviceInfo.featureFromMetadata) {
+ bindImsService(deviceInfo);
+ } else {
+ // newly added ImsServiceInfo that has not had features queried yet. Start
+ // async bind and query features.
+ scheduleQueryForFeatures(deviceInfo);
+ }
+ }
break;
}
default:
@@ -291,19 +407,37 @@
return true;
});
- // Package name of the default device service.
- private String mDeviceService;
+ // Results from dynamic queries to ImsService regarding the features they support.
+ private ImsServiceFeatureQueryManager.Listener mDynamicQueryListener =
+ new ImsServiceFeatureQueryManager.Listener() {
+
+ @Override
+ public void onComplete(ComponentName name,
+ Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
+ Log.d(TAG, "onComplete called for name: " + name + "features:"
+ + printFeatures(features));
+ handleFeaturesChanged(name, features);
+ }
+
+ @Override
+ public void onError(ComponentName name) {
+ Log.w(TAG, "onError: " + name + "returned with an error result");
+ scheduleQueryForFeatures(name, DELAY_DYNAMIC_QUERY_MS);
+ }
+ };
+
// Array index corresponds to slot Id associated with the service package name.
private String[] mCarrierServices;
// List index corresponds to Slot Id, Maps ImsFeature.FEATURE->bound ImsServiceController
// Locked on mBoundServicesLock
private List<SparseArray<ImsServiceController>> mBoundImsServicesByFeature;
// not locked, only accessed on a handler thread.
- private Set<ImsServiceInfo> mInstalledServicesCache = new ArraySet<>();
+ private Map<ComponentName, ImsServiceInfo> mInstalledServicesCache = new HashMap<>();
// not locked, only accessed on a handler thread.
- private Set<ImsServiceController> mActiveControllers = new ArraySet<>();
- // Only used as the
+ private Map<ComponentName, ImsServiceController> mActiveControllers = new HashMap<>();
+ // Only used as the Component name for legacy ImsServices that did not use dynamic binding.
private final ComponentName mStaticComponent;
+ private ImsServiceFeatureQueryManager mFeatureQueryManager;
public ImsResolver(Context context, String defaultImsPackageName, int numSlots,
boolean isDynamicBinding) {
@@ -353,14 +487,21 @@
return mHandler;
}
+ @VisibleForTesting
+ public void setImsDynamicQueryManagerFactory(ImsDynamicQueryManagerFactory m) {
+ mDynamicQueryManagerFactory = m;
+ }
+
/**
* Needs to be called after the constructor to first populate the cache and possibly bind to
* ImsServices.
*/
- public void populateCacheAndStartBind() {
+ public void initPopulateCacheAndStartBind() {
Log.i(TAG, "Initializing cache and binding.");
+ mFeatureQueryManager = mDynamicQueryManagerFactory.create(mContext, mDynamicQueryListener);
// Populates the CarrierConfig override package names for each slot
- mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, -1).sendToTarget();
+ mHandler.obtainMessage(HANDLER_CONFIG_CHANGED,
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX).sendToTarget();
// Starts first bind to the system.
mHandler.obtainMessage(HANDLER_ADD_PACKAGE, null).sendToTarget();
}
@@ -449,19 +590,6 @@
return null;
}
- /**
- * @return true if the ImsService associated with this slot supports emergency calling over IMS,
- * false if the call should be placed over circuit switch instead.
- */
- public boolean isEmergencyMmTelAvailable(int slotId) {
- ImsServiceController controller = getImsServiceController(slotId, ImsFeature.FEATURE_MMTEL);
- if (controller != null) {
- return controller.canPlaceEmergencyCalls();
- }
- Log.w(TAG, "isEmergencyMmTelAvailable: No controller found for slot " + slotId);
- return false;
- }
-
@VisibleForTesting
public ImsServiceController getImsServiceController(int slotId, int feature) {
if (slotId < 0 || slotId >= mNumSlots) {
@@ -497,12 +625,42 @@
ImsServiceController controller = getImsServiceController(slotId, feature);
if (controller != null) {
- controller.addImsServiceFeatureListener(callback);
+ controller.addImsServiceFeatureCallback(callback);
return controller;
}
return null;
}
+ // Used for testing only.
+ public boolean overrideImsServiceConfiguration(int slotId, boolean isCarrierService,
+ String packageName) {
+ if (slotId < 0 || slotId >= mNumSlots) {
+ Log.w(TAG, "overrideImsServiceConfiguration: invalid slotId!");
+ return false;
+ }
+
+ if (packageName == null) {
+ Log.w(TAG, "overrideImsServiceConfiguration: null packageName!");
+ return false;
+ }
+
+ // encode boolean to int for Message.
+ int carrierService = isCarrierService ? 1 : 0;
+ Message.obtain(mHandler, HANDLER_OVERRIDE_IMS_SERVICE_CONFIG, slotId, carrierService,
+ packageName).sendToTarget();
+ return true;
+ }
+
+ // used for testing only.
+ public String getImsServiceConfiguration(int slotId, boolean isCarrierService) {
+ if (slotId < 0 || slotId >= mNumSlots) {
+ Log.w(TAG, "getImsServiceConfiguration: invalid slotId!");
+ return "";
+ }
+
+ return isCarrierService ? mCarrierServices[slotId] : mDeviceService;
+ }
+
private void putImsController(int slotId, int feature, ImsServiceController controller) {
if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID
|| feature >= ImsFeature.FEATURE_MAX) {
@@ -554,21 +712,32 @@
for (ImsServiceInfo info : infos) {
// Checking to see if the ComponentName is the same, so we can update the supported
// features. Will only be one (if it exists), since it is a set.
- Optional<ImsServiceInfo> match = getInfoByComponentName(mInstalledServicesCache,
- info.name);
- if (match.isPresent()) {
- // update features in the cache
- Log.i(TAG, "Updating features in cached ImsService: " + info.name);
- Log.d(TAG, "Updating features - Old features: " + match.get().supportedFeatures
- + " new features: " + info.supportedFeatures
- + ", supports emergency: " + info.supportsEmergencyMmTel);
- match.get().supportsEmergencyMmTel = info.supportsEmergencyMmTel;
- match.get().supportedFeatures = info.supportedFeatures;
- updateImsServiceFeatures(info);
+ ImsServiceInfo match = getInfoByComponentName(mInstalledServicesCache, info.name);
+ if (match != null) {
+ // for dynamic query the new "info" will have no supported features yet. Don't wipe
+ // out the cache for the existing features or update yet. Instead start a query
+ // for features dynamically.
+ if (info.featureFromMetadata) {
+ // update features in the cache
+ Log.i(TAG, "Updating features in cached ImsService: " + info.name);
+ Log.d(TAG, "Updating features - Old features: " + match + " new features: "
+ + info);
+ match.replaceFeatures(info.getSupportedFeatures());
+ updateImsServiceFeatures(info);
+ } else {
+ // start a query to get ImsService features
+ scheduleQueryForFeatures(info);
+ }
} else {
Log.i(TAG, "Adding newly added ImsService to cache: " + info.name);
- mInstalledServicesCache.add(info);
- newlyAddedInfos.add(info);
+ mInstalledServicesCache.put(info.name, info);
+ if (info.featureFromMetadata) {
+ newlyAddedInfos.add(info);
+ } else {
+ // newly added ImsServiceInfo that has not had features queried yet. Start async
+ // bind and query features.
+ scheduleQueryForFeatures(info);
+ }
}
}
// Loop through the newly created ServiceInfos in a separate loop to make sure the cache
@@ -577,12 +746,12 @@
if (isActiveCarrierService(info)) {
// New ImsService is registered to active carrier services and must be newly
// bound.
- bindNewImsService(info);
+ bindImsService(info);
// Update existing device service features
updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
} else if (isDeviceService(info)) {
// New ImsService is registered as device default and must be newly bound.
- bindNewImsService(info);
+ bindImsService(info);
}
}
}
@@ -591,11 +760,11 @@
// killed.
// Called from the handler ONLY
private boolean maybeRemovedImsService(String packageName) {
- Optional<ImsServiceInfo> match = getInfoByPackageName(mInstalledServicesCache, packageName);
- if (match.isPresent()) {
- mInstalledServicesCache.remove(match.get());
- Log.i(TAG, "Removing ImsService: " + match.get().name);
- unbindImsService(match.get());
+ ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName);
+ if (match != null) {
+ mInstalledServicesCache.remove(match.name);
+ Log.i(TAG, "Removing ImsService: " + match.name);
+ unbindImsService(match);
updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
return true;
}
@@ -624,25 +793,26 @@
return i;
}
}
- return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
}
- private Optional<ImsServiceController> getControllerByServiceInfo(
- Set<ImsServiceController> searchSet, ImsServiceInfo matchValue) {
- return searchSet.stream()
- .filter(c -> Objects.equals(c.getComponentName(), matchValue.name)).findFirst();
+ private ImsServiceController getControllerByServiceInfo(
+ Map<ComponentName, ImsServiceController> searchMap, ImsServiceInfo matchValue) {
+ return searchMap.values().stream()
+ .filter(c -> Objects.equals(c.getComponentName(), matchValue.name))
+ .findFirst().orElse(null);
}
- private Optional<ImsServiceInfo> getInfoByPackageName(Set<ImsServiceInfo> searchSet,
+ private ImsServiceInfo getInfoByPackageName(Map<ComponentName, ImsServiceInfo> searchMap,
String matchValue) {
- return searchSet.stream()
- .filter((i) -> Objects.equals(i.name.getPackageName(), matchValue)).findFirst();
+ return searchMap.values().stream()
+ .filter((i) -> Objects.equals(i.name.getPackageName(), matchValue))
+ .findFirst().orElse(null);
}
- private Optional<ImsServiceInfo> getInfoByComponentName(Set<ImsServiceInfo> searchSet,
- ComponentName matchValue) {
- return searchSet.stream()
- .filter((i) -> Objects.equals(i.name, matchValue)).findFirst();
+ private ImsServiceInfo getInfoByComponentName(
+ Map<ComponentName, ImsServiceInfo> searchMap, ComponentName matchValue) {
+ return searchMap.get(matchValue);
}
// Creates new features in active ImsServices and removes obsolete cached features. If
@@ -652,51 +822,71 @@
if (newInfo == null) {
return;
}
- Optional<ImsServiceController> o = getControllerByServiceInfo(mActiveControllers,
- newInfo);
- if (o.isPresent()) {
- Log.d(TAG, "Updating canPlaceEmergencyCalls: " + newInfo.supportsEmergencyMmTel);
- o.get().setCanPlaceEmergencyCalls(newInfo.supportsEmergencyMmTel);
- Log.i(TAG, "Updating features for ImsService: " + o.get().getComponentName());
- HashSet<Pair<Integer, Integer>> features = calculateFeaturesToCreate(newInfo);
+ ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, newInfo);
+ // Will return zero if these features are overridden or it should not currently have any
+ // features because it is not carrier/device.
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features =
+ calculateFeaturesToCreate(newInfo);
+ if (shouldFeaturesCauseBind(features)) {
try {
- if (features.size() > 0) {
+ if (controller != null) {
+ Log.i(TAG, "Updating features for ImsService: "
+ + controller.getComponentName());
Log.d(TAG, "Updating Features - New Features: " + features);
- o.get().changeImsServiceFeatures(features);
-
- // If the carrier service features have changed, the device features will also
- // need to be recalculated.
- if (isActiveCarrierService(newInfo)
- // Prevent infinite recursion from bad behavior
- && !TextUtils.equals(newInfo.name.getPackageName(), mDeviceService)) {
- Log.i(TAG, "Updating device default");
- updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
- }
+ controller.changeImsServiceFeatures(features);
} else {
- Log.i(TAG, "Unbinding: features = 0 for ImsService: "
- + o.get().getComponentName());
- o.get().unbind();
+ Log.i(TAG, "updateImsServiceFeatures: unbound with active features, rebinding");
+ bindImsServiceWithFeatures(newInfo, features);
+ }
+ // If the carrier service features have changed, the device features will also
+ // need to be recalculated.
+ if (isActiveCarrierService(newInfo)
+ // Prevent infinite recursion from bad behavior
+ && !TextUtils.equals(newInfo.name.getPackageName(), mDeviceService)) {
+ Log.i(TAG, "Updating device default");
+ updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
}
} catch (RemoteException e) {
Log.e(TAG, "updateImsServiceFeatures: Remote Exception: " + e.getMessage());
}
+ // Don't stay bound if the ImsService is providing no features.
+ } else if (controller != null) {
+ Log.i(TAG, "Unbinding: features = 0 for ImsService: " + controller.getComponentName());
+ unbindImsService(newInfo);
}
}
- // Bind to a new ImsService and wait for the service to be connected to create ImsFeatures.
- private void bindNewImsService(ImsServiceInfo info) {
+ // Bind to an ImsService and wait for the service to be connected to create ImsFeatures.
+ private void bindImsService(ImsServiceInfo info) {
if (info == null) {
return;
}
- ImsServiceController controller = info.controllerFactory.create(mContext, info.name, this);
- HashSet<Pair<Integer, Integer>> features = calculateFeaturesToCreate(info);
- controller.setCanPlaceEmergencyCalls(info.supportsEmergencyMmTel);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info);
+ bindImsServiceWithFeatures(info, features);
+ }
+
+ private void bindImsServiceWithFeatures(ImsServiceInfo info,
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features) {
// Only bind if there are features that will be created by the service.
- if (features.size() > 0) {
- Log.i(TAG, "Binding ImsService: " + controller.getComponentName() + " with features: "
- + features + ", supports emergency calling: " + info.supportsEmergencyMmTel);
- controller.bind(features);
- mActiveControllers.add(controller);
+ if (shouldFeaturesCauseBind(features)) {
+ // Check to see if an active controller already exists
+ ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info);
+ if (controller != null) {
+ Log.i(TAG, "ImsService connection exists, updating features " + features);
+ try {
+ controller.changeImsServiceFeatures(features);
+ // Features have been set, there was an error adding/removing. When the
+ // controller recovers, it will add/remove again.
+ } catch (RemoteException e) {
+ Log.w(TAG, "bindImsService: error=" + e.getMessage());
+ }
+ } else {
+ controller = info.controllerFactory.create(mContext, info.name, this);
+ Log.i(TAG, "Binding ImsService: " + controller.getComponentName()
+ + " with features: " + features);
+ controller.bind(features);
+ }
+ mActiveControllers.put(info.name, controller);
}
}
@@ -705,16 +895,16 @@
if (info == null) {
return;
}
- Optional<ImsServiceController> o = getControllerByServiceInfo(mActiveControllers, info);
- if (o.isPresent()) {
+ ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info);
+ if (controller != null) {
// Calls imsServiceFeatureRemoved on all features in the controller
try {
- Log.i(TAG, "Unbinding ImsService: " + o.get().getComponentName());
- o.get().unbind();
+ Log.i(TAG, "Unbinding ImsService: " + controller.getComponentName());
+ controller.unbind();
} catch (RemoteException e) {
Log.e(TAG, "unbindImsService: Remote Exception: " + e.getMessage());
}
- mActiveControllers.remove(o.get());
+ mActiveControllers.remove(info.name);
}
}
@@ -722,13 +912,16 @@
// ImsServiceController, it will be granted all of the features it requests on the associated
// slot. If it is the device ImsService, it will get all of the features not covered by the
// carrier implementation.
- private HashSet<Pair<Integer, Integer>> calculateFeaturesToCreate(ImsServiceInfo info) {
- HashSet<Pair<Integer, Integer>> imsFeaturesBySlot = new HashSet<>();
+ private HashSet<ImsFeatureConfiguration.FeatureSlotPair> calculateFeaturesToCreate(
+ ImsServiceInfo info) {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> imsFeaturesBySlot = new HashSet<>();
// Check if the info is a carrier service
int slotId = getSlotForActiveCarrierService(info);
- if (slotId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- imsFeaturesBySlot.addAll(info.supportedFeatures.stream().map(
- feature -> new Pair<>(slotId, feature)).collect(Collectors.toList()));
+ if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream()
+ // Match slotId with feature slotId.
+ .filter(feature -> slotId == feature.slotId)
+ .collect(Collectors.toList()));
} else if (isDeviceService(info)) {
// For all slots that are not currently using a carrier ImsService, enable all features
// for the device default.
@@ -736,17 +929,19 @@
final int currSlotId = i;
ImsServiceInfo carrierImsInfo = getImsServiceInfoFromCache(mCarrierServices[i]);
if (carrierImsInfo == null) {
- // No Carrier override, add all features
- imsFeaturesBySlot.addAll(info.supportedFeatures.stream().map(
- feature -> new Pair<>(currSlotId, feature)).collect(
- Collectors.toList()));
+ // No Carrier override, add all features for this slot
+ imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream()
+ .filter(feature -> currSlotId == feature.slotId)
+ .collect(Collectors.toList()));
} else {
// Add all features to the device service that are not currently covered by
// the carrier ImsService.
- Set<Integer> deviceFeatures = new HashSet<>(info.supportedFeatures);
- deviceFeatures.removeAll(carrierImsInfo.supportedFeatures);
- imsFeaturesBySlot.addAll(deviceFeatures.stream().map(
- feature -> new Pair<>(currSlotId, feature)).collect(
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatures =
+ new HashSet<>(info.getSupportedFeatures());
+ deviceFeatures.removeAll(carrierImsInfo.getSupportedFeatures());
+ // only add features for current slot
+ imsFeaturesBySlot.addAll(deviceFeatures.stream()
+ .filter(feature -> currSlotId == feature.slotId).collect(
Collectors.toList()));
}
}
@@ -772,28 +967,64 @@
removeImsController(slotId, feature);
}
+ /**
+ * Implementation of
+ * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeaturesChanged, which
+ * notify the ImsResolver of a change to the supported ImsFeatures of a connected ImsService.
+ */
+ public void imsServiceFeaturesChanged(ImsFeatureConfiguration config,
+ ImsServiceController controller) {
+ if (controller == null || config == null) {
+ return;
+ }
+ Log.i(TAG, "imsServiceFeaturesChanged: config=" + config.getServiceFeatures()
+ + ", ComponentName=" + controller.getComponentName());
+ handleFeaturesChanged(controller.getComponentName(), config.getServiceFeatures());
+ }
+
+ /**
+ * Determines if the features specified should cause a bind or keep a binding active to an
+ * ImsService.
+ * @return true if MMTEL or RCS features are present, false if they are not or only
+ * EMERGENCY_MMTEL is specified.
+ */
+ private boolean shouldFeaturesCauseBind(
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features) {
+ long bindableFeatures = features.stream()
+ // remove all emergency features
+ .filter(f -> f.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL).count();
+ return bindableFeatures > 0;
+ }
+
// Possibly rebind to another ImsService if currently installed ImsServices were changed or if
// the SIM card has changed.
// Called from the handler ONLY
- private void maybeRebindService(int subId) {
- if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ private void maybeRebindService(int slotId, String newPackageName) {
+ if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
// not specified, replace package on all slots.
for (int i = 0; i < mNumSlots; i++) {
- // get Sub id from Slot Id
- subId = mSubscriptionManagerProxy.getSubId(i);
- updateBoundCarrierServices(subId);
+ updateBoundCarrierServices(i, newPackageName);
}
} else {
- updateBoundCarrierServices(subId);
+ updateBoundCarrierServices(slotId, newPackageName);
}
}
- private void updateBoundCarrierServices(int subId) {
- int slotId = mSubscriptionManagerProxy.getSlotIndex(subId);
- String newPackageName = mCarrierConfigManager.getConfigForSubId(subId).getString(
- CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
- if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX && slotId < mNumSlots) {
+ private void carrierConfigChanged(int slotId) {
+ int subId = mSubscriptionManagerProxy.getSubId(slotId);
+ PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
+ if (config != null) {
+ String newPackageName = config.getString(
+ CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
+ maybeRebindService(slotId, newPackageName);
+ } else {
+ Log.w(TAG, "carrierConfigChanged: CarrierConfig is null!");
+ }
+ }
+
+ private void updateBoundCarrierServices(int slotId, String newPackageName) {
+ if (slotId > SubscriptionManager.INVALID_SIM_SLOT_INDEX && slotId < mNumSlots) {
String oldPackageName = mCarrierServices[slotId];
mCarrierServices[slotId] = newPackageName;
if (!TextUtils.equals(newPackageName, oldPackageName)) {
@@ -802,14 +1033,132 @@
// ImsService is retrieved from the cache. If the cache hasn't been populated yet,
// the calls to unbind/bind will fail (intended during initial start up).
unbindImsService(getImsServiceInfoFromCache(oldPackageName));
- bindNewImsService(getImsServiceInfoFromCache(newPackageName));
- // Recalculate the device ImsService features to reflect changes.
- updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
+ ImsServiceInfo newInfo = getImsServiceInfoFromCache(newPackageName);
+ // if there is no carrier ImsService, newInfo is null. This we still want to update
+ // bindings for device ImsService to pick up the missing features.
+ if (newInfo == null || newInfo.featureFromMetadata) {
+ bindImsService(newInfo);
+ // Recalculate the device ImsService features to reflect changes.
+ updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
+ } else {
+ // ImsServiceInfo that has not had features queried yet. Start async
+ // bind and query features.
+ scheduleQueryForFeatures(newInfo);
+ }
}
}
}
/**
+ * Schedules a query for dynamic ImsService features.
+ */
+ private void scheduleQueryForFeatures(ImsServiceInfo service, int delayMs) {
+ // if not current device/carrier service, don't perform query. If this changes, this method
+ // will be called again.
+ if (!isDeviceService(service) && getSlotForActiveCarrierService(service)
+ == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ Log.i(TAG, "scheduleQueryForFeatures: skipping query for ImsService that is not"
+ + " set as carrier/device ImsService.");
+ return;
+ }
+ Message msg = Message.obtain(mHandler, HANDLER_START_DYNAMIC_FEATURE_QUERY, service);
+ if (mHandler.hasMessages(HANDLER_START_DYNAMIC_FEATURE_QUERY, service)) {
+ Log.d(TAG, "scheduleQueryForFeatures: dynamic query for " + service.name
+ + " already scheduled");
+ return;
+ }
+ Log.d(TAG, "scheduleQueryForFeatures: starting dynamic query for " + service.name
+ + " in " + delayMs + "ms.");
+ mHandler.sendMessageDelayed(msg, delayMs);
+ }
+
+ private void scheduleQueryForFeatures(ComponentName name, int delayMs) {
+ ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName());
+ if (service == null) {
+ Log.w(TAG, "scheduleQueryForFeatures: Couldn't find cached info for name: " + name);
+ return;
+ }
+ scheduleQueryForFeatures(service, delayMs);
+ }
+
+ private void scheduleQueryForFeatures(ImsServiceInfo service) {
+ scheduleQueryForFeatures(service, 0);
+ }
+
+ /**
+ * Schedules the processing of a completed query.
+ */
+ private void handleFeaturesChanged(ComponentName name,
+ Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = name;
+ args.arg2 = features;
+ mHandler.obtainMessage(HANDLER_DYNAMIC_FEATURE_CHANGE, args).sendToTarget();
+ }
+
+ // Starts a dynamic query. Called from handler ONLY.
+ private void startDynamicQuery(ImsServiceInfo service) {
+ boolean queryStarted = mFeatureQueryManager.startQuery(service.name,
+ service.controllerFactory.getServiceInterface());
+ if (!queryStarted) {
+ Log.w(TAG, "startDynamicQuery: service could not connect. Retrying after delay.");
+ scheduleQueryForFeatures(service, DELAY_DYNAMIC_QUERY_MS);
+ } else {
+ Log.d(TAG, "startDynamicQuery: Service queried, waiting for response.");
+ }
+ }
+
+ // process complete dynamic query. Called from handler ONLY.
+ private void dynamicQueryComplete(ComponentName name,
+ Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
+ ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName());
+ if (service == null) {
+ Log.w(TAG, "handleFeaturesChanged: Couldn't find cached info for name: "
+ + name);
+ return;
+ }
+ // Add features to service
+ service.replaceFeatures(features);
+ if (isActiveCarrierService(service)) {
+ // New ImsService is registered to active carrier services and must be newly
+ // bound.
+ bindImsService(service);
+ // Update existing device service features
+ updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
+ } else if (isDeviceService(service)) {
+ // New ImsService is registered as device default and must be newly bound.
+ bindImsService(service);
+ }
+ }
+
+ /**
+ * @return true if the ImsResolver is in the process of resolving a dynamic query and should not
+ * be considered available, false if the ImsResolver is idle.
+ */
+ public boolean isResolvingBinding() {
+ return mHandler.hasMessages(HANDLER_START_DYNAMIC_FEATURE_QUERY)
+ // We haven't processed this message yet, so it is still resolving.
+ || mHandler.hasMessages(HANDLER_DYNAMIC_FEATURE_CHANGE)
+ || mFeatureQueryManager.isQueryInProgress();
+ }
+
+ private String printFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
+ StringBuilder featureString = new StringBuilder();
+ featureString.append("features: [");
+ if (features != null) {
+ for (ImsFeatureConfiguration.FeatureSlotPair feature : features) {
+ featureString.append("{");
+ featureString.append(feature.slotId);
+ featureString.append(",");
+ featureString.append(feature.featureType);
+ featureString.append("} ");
+ }
+ featureString.append("]");
+ }
+ return featureString.toString();
+ }
+
+ /**
* Returns the ImsServiceInfo that matches the provided packageName. Visible for testing
* the ImsService caching functionality.
*/
@@ -818,10 +1167,9 @@
if (TextUtils.isEmpty(packageName)) {
return null;
}
- Optional<ImsServiceInfo> infoFilter = getInfoByPackageName(mInstalledServicesCache,
- packageName);
- if (infoFilter.isPresent()) {
- return infoFilter.get();
+ ImsServiceInfo infoFilter = getInfoByPackageName(mInstalledServicesCache, packageName);
+ if (infoFilter != null) {
+ return infoFilter;
} else {
return null;
}
@@ -846,12 +1194,11 @@
private List<ImsServiceInfo> getStaticImsService() {
List<ImsServiceInfo> infos = new ArrayList<>();
- ImsServiceInfo info = new ImsServiceInfo();
+ ImsServiceInfo info = new ImsServiceInfo(mNumSlots);
info.name = mStaticComponent;
- info.supportedFeatures = new HashSet<>(ImsFeature.FEATURE_MAX);
info.controllerFactory = mImsServiceControllerFactoryStaticBindingCompat;
- info.supportsEmergencyMmTel = true;
- info.supportedFeatures.add(ImsFeature.FEATURE_MMTEL);
+ info.addFeatureForAllSlots(ImsFeature.FEATURE_EMERGENCY_MMTEL);
+ info.addFeatureForAllSlots(ImsFeature.FEATURE_MMTEL);
infos.add(info);
return infos;
}
@@ -871,31 +1218,51 @@
ServiceInfo serviceInfo = entry.serviceInfo;
if (serviceInfo != null) {
- ImsServiceInfo info = new ImsServiceInfo();
+ ImsServiceInfo info = new ImsServiceInfo(mNumSlots);
info.name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
- info.supportedFeatures = new HashSet<>(ImsFeature.FEATURE_MAX);
info.controllerFactory = controllerFactory;
- // Add all supported features
- if (serviceInfo.metaData != null) {
- if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE, false)) {
- info.supportsEmergencyMmTel = true;
+
+ // we will allow the manifest method of declaring manifest features in two cases:
+ // 1) it is the device overlay "default" ImsService, where the features do not
+ // change (the new method can still be used if the default does not define manifest
+ // entries).
+ // 2) using the "compat" ImsService, which only supports manifest query.
+ if (isDeviceService(info)
+ || mImsServiceControllerFactoryCompat == controllerFactory) {
+ if (serviceInfo.metaData != null) {
+ if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE,
+ false)) {
+ info.addFeatureForAllSlots(ImsFeature.FEATURE_EMERGENCY_MMTEL);
+ }
+ if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
+ info.addFeatureForAllSlots(ImsFeature.FEATURE_MMTEL);
+ }
+ if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) {
+ info.addFeatureForAllSlots(ImsFeature.FEATURE_RCS);
+ }
}
- if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
- info.supportedFeatures.add(ImsFeature.FEATURE_MMTEL);
+ // Only dynamic query if we are not a compat version of ImsService and the
+ // default service.
+ if (mImsServiceControllerFactoryCompat != controllerFactory
+ && info.getSupportedFeatures().isEmpty()) {
+ // metadata empty, try dynamic query instead
+ info.featureFromMetadata = false;
}
- if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) {
- info.supportedFeatures.add(ImsFeature.FEATURE_RCS);
- }
+ } else {
+ // We are a carrier service and not using the compat version of ImsService.
+ info.featureFromMetadata = false;
}
+ Log.i(TAG, "service name: " + info.name + ", manifest query: "
+ + info.featureFromMetadata);
// Check manifest permission to be sure that the service declares the correct
- // permissions.
- if (TextUtils.equals(serviceInfo.permission,
- Manifest.permission.BIND_IMS_SERVICE)) {
- Log.d(TAG, "ImsService (" + serviceIntent + ") added to cache: "
- + info.name + " with features: " + info.supportedFeatures);
+ // permissions. Overridden if the METADATA_OVERRIDE_PERM_CHECK metadata is set to
+ // true.
+ // NOTE: METADATA_OVERRIDE_PERM_CHECK should only be set for testing.
+ if (TextUtils.equals(serviceInfo.permission, Manifest.permission.BIND_IMS_SERVICE)
+ || serviceInfo.metaData.getBoolean(METADATA_OVERRIDE_PERM_CHECK, false)) {
infos.add(info);
} else {
- Log.w(TAG, "ImsService does not have BIND_IMS_SERVICE permission: "
+ Log.w(TAG, "ImsService is not protected with BIND_IMS_SERVICE permission: "
+ info.name);
}
}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
index 30de179..60f11c6 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -34,8 +34,8 @@
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsServiceController;
import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.util.Log;
-import android.util.Pair;
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.ims.internal.IImsServiceFeatureCallback;
@@ -45,21 +45,21 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Manages the Binding lifecycle of one ImsService as well as the relevant ImsFeatures that the
* ImsService will support.
*
- * When the ImsService is first bound, {@link IImsServiceController#createImsFeature} will be
- * called
+ * When the ImsService is first bound, {@link ImsService#createMmTelFeature(int)} and
+ * {@link ImsService#createRcsFeature(int)} will be called
* on each feature that the service supports. For each ImsFeature that is created,
* {@link ImsServiceControllerCallbacks#imsServiceFeatureCreated} will be called to notify the
* listener that the ImsService now supports that feature.
*
* When {@link #changeImsServiceFeatures} is called with a set of features that is different from
- * the original set, {@link IImsServiceController#createImsFeature} and
- * {@link IImsServiceController#removeImsFeature} will be called for each feature that is
- * created/removed.
+ * the original set, create and {@link IImsServiceController#removeImsFeature} will be called for
+ * each feature that is created/removed.
*/
public class ImsServiceController {
@@ -74,7 +74,11 @@
@Override
public void binderDied() {
Log.e(LOG_TAG, "ImsService(" + mComponentName + ") died. Restarting...");
- notifyAllFeaturesRemoved();
+ synchronized (mLock) {
+ mIsBinding = false;
+ mIsBound = false;
+ }
+ cleanupAllFeatures();
cleanUpService();
startDelayedRebindToService();
}
@@ -88,7 +92,6 @@
synchronized (mLock) {
mIsBound = true;
mIsBinding = false;
- grantPermissionsToService();
Log.d(LOG_TAG, "ImsService(" + name + "): onServiceConnected with binder: "
+ service);
if (service != null) {
@@ -97,8 +100,9 @@
service.linkToDeath(mImsDeathRecipient, 0);
mImsServiceControllerBinder = service;
setServiceController(service);
+ notifyImsServiceReady();
// create all associated features in the ImsService
- for (Pair<Integer, Integer> i : mImsFeatures) {
+ for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) {
addImsServiceFeature(i);
}
} catch (RemoteException e) {
@@ -120,29 +124,61 @@
synchronized (mLock) {
mIsBinding = false;
}
+ cleanupConnection();
+ Log.w(LOG_TAG, "ImsService(" + name + "): onServiceDisconnected. Waiting...");
+ // Service disconnected, but we are still technically bound. Waiting for reconnect.
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ synchronized (mLock) {
+ mIsBinding = false;
+ mIsBound = false;
+ }
+ cleanupConnection();
+ Log.w(LOG_TAG, "ImsService(" + name + "): onBindingDied. Starting rebind...");
+ startDelayedRebindToService();
+ }
+
+ private void cleanupConnection() {
if (isServiceControllerAvailable()) {
mImsServiceControllerBinder.unlinkToDeath(mImsDeathRecipient, 0);
}
- notifyAllFeaturesRemoved();
+ cleanupAllFeatures();
cleanUpService();
- Log.w(LOG_TAG, "ImsService(" + name + "): onServiceDisconnected. Rebinding...");
- startDelayedRebindToService();
}
}
+ private ImsService.Listener mFeatureChangedListener = new ImsService.Listener() {
+ @Override
+ public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
+ if (mCallbacks == null) {
+ return;
+ }
+ mCallbacks.imsServiceFeaturesChanged(c, ImsServiceController.this);
+ }
+ };
+
/**
* Defines callbacks that are used by the ImsServiceController to notify when an ImsService
* has created or removed a new feature as well as the associated ImsServiceController.
*/
public interface ImsServiceControllerCallbacks {
/**
- * Called by ImsServiceController when a new feature has been created.
+ * Called by ImsServiceController when a new MMTEL or RCS feature has been created.
*/
void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller);
/**
- * Called by ImsServiceController when a new feature has been removed.
+ * Called by ImsServiceController when a new MMTEL or RCS feature has been removed.
*/
void imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller);
+
+ /**
+ * Called by the ImsServiceController when the ImsService has notified the framework that
+ * its features have changed.
+ */
+ void imsServiceFeaturesChanged(ImsFeatureConfiguration config,
+ ImsServiceController controller);
}
/**
@@ -175,18 +211,16 @@
private boolean mIsBound = false;
private boolean mIsBinding = false;
// Set of a pair of slotId->feature
- private HashSet<Pair<Integer, Integer>> mImsFeatures;
+ private HashSet<ImsFeatureConfiguration.FeatureSlotPair> mImsFeatures;
// Binder interfaces to the features set in mImsFeatures;
private HashSet<ImsFeatureContainer> mImsFeatureBinders = new HashSet<>();
private IImsServiceController mIImsServiceController;
private IBinder mImsServiceControllerBinder;
private ImsServiceConnection mImsServiceConnection;
private ImsDeathRecipient mImsDeathRecipient;
- private Set<IImsServiceFeatureCallback> mImsStatusCallbacks = new HashSet<>();
+ private Set<IImsServiceFeatureCallback> mImsStatusCallbacks = ConcurrentHashMap.newKeySet();
// Only added or removed, never accessed on purpose.
private Set<ImsFeatureStatusCallback> mFeatureStatusCallbacks = new HashSet<>();
- // Determines whether or not emergency calls can be placed through this ImsService.
- private boolean mCanPlaceEmergencyCalls = false;
protected final Object mLock = new Object();
protected final Context mContext;
@@ -315,19 +349,20 @@
}
/**
- * Sends request to bind to ImsService designated by the {@ComponentName} with the feature set
- * imsFeatureSet
+ * Sends request to bind to ImsService designated by the {@link ComponentName} with the feature
+ * set imsFeatureSet.
*
* @param imsFeatureSet a Set of Pairs that designate the slotId->featureId that need to be
* created once the service is bound.
* @return {@link true} if the service is in the process of being bound, {@link false} if it
* has failed.
*/
- public boolean bind(HashSet<Pair<Integer, Integer>> imsFeatureSet) {
+ public boolean bind(HashSet<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet) {
synchronized (mLock) {
if (!mIsBound && !mIsBinding) {
mIsBinding = true;
mImsFeatures = imsFeatureSet;
+ grantPermissionsToService();
Intent imsServiceIntent = new Intent(getServiceInterface()).setComponent(
mComponentName);
mImsServiceConnection = new ImsServiceConnection();
@@ -338,6 +373,7 @@
boolean bindSucceeded = startBindToService(imsServiceIntent,
mImsServiceConnection, serviceFlags);
if (!bindSucceeded) {
+ mIsBinding = false;
mBackoff.notifyFailed();
}
return bindSucceeded;
@@ -375,7 +411,7 @@
}
// Clean up all features
changeImsServiceFeatures(new HashSet<>());
- removeImsServiceFeatureListener();
+ removeImsServiceFeatureCallbacks();
mImsServiceControllerBinder.unlinkToDeath(mImsDeathRecipient, 0);
Log.i(LOG_TAG, "Unbinding ImsService: " + mComponentName);
mContext.unbindService(mImsServiceConnection);
@@ -387,50 +423,32 @@
* For every feature that is added, the service calls the associated create. For every
* ImsFeature that is removed, {@link IImsServiceController#removeImsFeature} is called.
*/
- public void changeImsServiceFeatures(HashSet<Pair<Integer, Integer>> newImsFeatures)
+ public void changeImsServiceFeatures(
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> newImsFeatures)
throws RemoteException {
synchronized (mLock) {
+ Log.i(LOG_TAG, "Features changed (" + mImsFeatures + "->" + newImsFeatures + ") for "
+ + "ImsService: " + mComponentName);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> oldImsFeatures =
+ new HashSet<>(mImsFeatures);
+ // Set features first in case we lose binding and need to rebind later.
+ mImsFeatures = newImsFeatures;
if (mIsBound) {
// add features to service.
- HashSet<Pair<Integer, Integer>> newFeatures = new HashSet<>(newImsFeatures);
- newFeatures.removeAll(mImsFeatures);
- for (Pair<Integer, Integer> i : newFeatures) {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> newFeatures =
+ new HashSet<>(mImsFeatures);
+ newFeatures.removeAll(oldImsFeatures);
+ for (ImsFeatureConfiguration.FeatureSlotPair i : newFeatures) {
addImsServiceFeature(i);
}
// remove old features
- HashSet<Pair<Integer, Integer>> oldFeatures = new HashSet<>(mImsFeatures);
- oldFeatures.removeAll(newImsFeatures);
- for (Pair<Integer, Integer> i : oldFeatures) {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> oldFeatures =
+ new HashSet<>(oldImsFeatures);
+ oldFeatures.removeAll(mImsFeatures);
+ for (ImsFeatureConfiguration.FeatureSlotPair i : oldFeatures) {
removeImsServiceFeature(i);
}
}
- Log.i(LOG_TAG, "Features changed (" + mImsFeatures + "->" + newImsFeatures + ") for "
- + "ImsService: " + mComponentName);
- mImsFeatures = newImsFeatures;
- }
- }
-
- /**
- * Sets the ability for the ImsService to place emergency calls. This is controlled by the
- * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL} feature attribute, which is set either as metadata
- * in the AndroidManifest service definition or via dynamic query in
- * {@link ImsService#querySupportedImsFeatures()}.
- */
- public void setCanPlaceEmergencyCalls(boolean canPlaceEmergencyCalls) {
- synchronized (mLock) {
- mCanPlaceEmergencyCalls = canPlaceEmergencyCalls;
- }
- }
-
- /**
- * Whether or not the ImsService connected to this controller is able to place emergency calls
- * over IMS.
- * @return true if this ImsService can place emergency calls over IMS, false if the framework
- * should instead place the emergency call over circuit switch.
- */
- public boolean canPlaceEmergencyCalls() {
- synchronized (mLock) {
- return mCanPlaceEmergencyCalls;
}
}
@@ -456,15 +474,30 @@
/**
* Add a callback to ImsManager that signals a new feature that the ImsServiceProxy can handle.
*/
- public void addImsServiceFeatureListener(IImsServiceFeatureCallback callback) {
+ public void addImsServiceFeatureCallback(IImsServiceFeatureCallback callback) {
+ mImsStatusCallbacks.add(callback);
synchronized (mLock) {
- mImsStatusCallbacks.add(callback);
+ if (mImsFeatures == null || mImsFeatures.isEmpty()) {
+ return;
+ }
+ // notify the new status callback of the features that are available.
+ try {
+ for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) {
+ callback.imsFeatureCreated(i.slotId, i.featureType);
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "addImsServiceFeatureCallback: exception notifying callback");
+ }
}
}
public void enableIms(int slotId) {
try {
- mIImsServiceController.enableIms(slotId);
+ synchronized (mLock) {
+ if (isServiceControllerAvailable()) {
+ mIImsServiceController.enableIms(slotId);
+ }
+ }
} catch (RemoteException e) {
Log.w(LOG_TAG, "Couldn't enable IMS: " + e.getMessage());
}
@@ -472,7 +505,11 @@
public void disableIms(int slotId) {
try {
- mIImsServiceController.disableIms(slotId);
+ synchronized (mLock) {
+ if (isServiceControllerAvailable()) {
+ mIImsServiceController.disableIms(slotId);
+ }
+ }
} catch (RemoteException e) {
Log.w(LOG_TAG, "Couldn't disable IMS: " + e.getMessage());
}
@@ -512,7 +549,8 @@
*/
public IImsRegistration getRegistration(int slotId) throws RemoteException {
synchronized (mLock) {
- return mIImsServiceController.getRegistration(slotId);
+ return isServiceControllerAvailable()
+ ? mIImsServiceController.getRegistration(slotId) : null;
}
}
@@ -521,7 +559,20 @@
*/
public IImsConfig getConfig(int slotId) throws RemoteException {
synchronized (mLock) {
- return mIImsServiceController.getConfig(slotId);
+ return isServiceControllerAvailable() ? mIImsServiceController.getConfig(slotId) : null;
+ }
+ }
+
+ /**
+ * notify the ImsService that the ImsService is ready for feature creation.
+ */
+ protected void notifyImsServiceReady() throws RemoteException {
+ synchronized (mLock) {
+ if (isServiceControllerAvailable()) {
+ Log.d(LOG_TAG, "notifyImsServiceReady");
+ mIImsServiceController.setListener(mFeatureChangedListener);
+ mIImsServiceController.notifyImsServiceReadyForFeatureCreation();
+ }
}
}
@@ -538,6 +589,15 @@
}
/**
+ * @return true if the controller is currently bound.
+ */
+ public boolean isBound() {
+ synchronized (mLock) {
+ return mIsBound;
+ }
+ }
+
+ /**
* Check to see if the service controller is available, overridden for compat versions,
* @return true if available, false otherwise;
*/
@@ -545,10 +605,9 @@
return mIImsServiceController != null;
}
- private void removeImsServiceFeatureListener() {
- synchronized (mLock) {
+ @VisibleForTesting
+ public void removeImsServiceFeatureCallbacks() {
mImsStatusCallbacks.clear();
- }
}
// Only add a new rebind if there are no pending rebinds waiting.
@@ -572,97 +631,110 @@
}
private void sendImsFeatureCreatedCallback(int slot, int feature) {
- synchronized (mLock) {
- for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator();
- i.hasNext(); ) {
- IImsServiceFeatureCallback callbacks = i.next();
- try {
- callbacks.imsFeatureCreated(slot, feature);
- } catch (RemoteException e) {
- // binder died, remove callback.
- Log.w(LOG_TAG, "sendImsFeatureCreatedCallback: Binder died, removing "
- + "callback. Exception:" + e.getMessage());
- i.remove();
- }
+ for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator();
+ i.hasNext(); ) {
+ IImsServiceFeatureCallback callbacks = i.next();
+ try {
+ callbacks.imsFeatureCreated(slot, feature);
+ } catch (RemoteException e) {
+ // binder died, remove callback.
+ Log.w(LOG_TAG, "sendImsFeatureCreatedCallback: Binder died, removing "
+ + "callback. Exception:" + e.getMessage());
+ i.remove();
}
}
}
private void sendImsFeatureRemovedCallback(int slot, int feature) {
- synchronized (mLock) {
- for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator();
- i.hasNext(); ) {
- IImsServiceFeatureCallback callbacks = i.next();
- try {
- callbacks.imsFeatureRemoved(slot, feature);
- } catch (RemoteException e) {
- // binder died, remove callback.
- Log.w(LOG_TAG, "sendImsFeatureRemovedCallback: Binder died, removing "
- + "callback. Exception:" + e.getMessage());
- i.remove();
- }
+ for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator();
+ i.hasNext(); ) {
+ IImsServiceFeatureCallback callbacks = i.next();
+ try {
+ callbacks.imsFeatureRemoved(slot, feature);
+ } catch (RemoteException e) {
+ // binder died, remove callback.
+ Log.w(LOG_TAG, "sendImsFeatureRemovedCallback: Binder died, removing "
+ + "callback. Exception:" + e.getMessage());
+ i.remove();
}
}
}
private void sendImsFeatureStatusChanged(int slot, int feature, int status) {
- synchronized (mLock) {
- for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator();
- i.hasNext(); ) {
- IImsServiceFeatureCallback callbacks = i.next();
- try {
- callbacks.imsStatusChanged(slot, feature, status);
- } catch (RemoteException e) {
- // binder died, remove callback.
- Log.w(LOG_TAG, "sendImsFeatureStatusChanged: Binder died, removing "
- + "callback. Exception:" + e.getMessage());
- i.remove();
- }
+ for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator();
+ i.hasNext(); ) {
+ IImsServiceFeatureCallback callbacks = i.next();
+ try {
+ callbacks.imsStatusChanged(slot, feature, status);
+ } catch (RemoteException e) {
+ // binder died, remove callback.
+ Log.w(LOG_TAG, "sendImsFeatureStatusChanged: Binder died, removing "
+ + "callback. Exception:" + e.getMessage());
+ i.remove();
}
}
}
// This method should only be called when synchronized on mLock
- private void addImsServiceFeature(Pair<Integer, Integer> featurePair) throws RemoteException {
+ private void addImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair)
+ throws RemoteException {
if (!isServiceControllerAvailable() || mCallbacks == null) {
Log.w(LOG_TAG, "addImsServiceFeature called with null values.");
return;
}
- ImsFeatureStatusCallback c = new ImsFeatureStatusCallback(featurePair.first,
- featurePair.second);
- mFeatureStatusCallbacks.add(c);
- IInterface f = createImsFeature(featurePair.first, featurePair.second, c.getCallback());
- addImsFeatureBinder(featurePair.first, featurePair.second, f);
- // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
- mCallbacks.imsServiceFeatureCreated(featurePair.first, featurePair.second, this);
- // Send callback to ImsServiceProxy to change supported ImsFeatures
- sendImsFeatureCreatedCallback(featurePair.first, featurePair.second);
+ if (featurePair.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL) {
+ ImsFeatureStatusCallback c = new ImsFeatureStatusCallback(featurePair.slotId,
+ featurePair.featureType);
+ mFeatureStatusCallbacks.add(c);
+ IInterface f = createImsFeature(featurePair.slotId, featurePair.featureType,
+ c.getCallback());
+ addImsFeatureBinder(featurePair.slotId, featurePair.featureType, f);
+ // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
+ mCallbacks.imsServiceFeatureCreated(featurePair.slotId, featurePair.featureType, this);
+ } else {
+ // Don't update ImsService for emergency MMTEL feature.
+ Log.i(LOG_TAG, "supports emergency calling on slot " + featurePair.slotId);
+ }
+ // Send callback to ImsServiceProxy to change supported ImsFeatures including emergency
+ // MMTEL state.
+ sendImsFeatureCreatedCallback(featurePair.slotId, featurePair.featureType);
}
// This method should only be called when synchronized on mLock
- private void removeImsServiceFeature(Pair<Integer, Integer> featurePair)
- throws RemoteException {
+ private void removeImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair) {
if (!isServiceControllerAvailable() || mCallbacks == null) {
Log.w(LOG_TAG, "removeImsServiceFeature called with null values.");
return;
}
- ImsFeatureStatusCallback callbackToRemove = mFeatureStatusCallbacks.stream().filter(c ->
- c.mSlotId == featurePair.first && c.mFeatureType == featurePair.second)
- .findFirst().orElse(null);
- // Remove status callbacks from list.
- if (callbackToRemove != null) {
- mFeatureStatusCallbacks.remove(callbackToRemove);
+ if (featurePair.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL) {
+ ImsFeatureStatusCallback callbackToRemove = mFeatureStatusCallbacks.stream().filter(c ->
+ c.mSlotId == featurePair.slotId && c.mFeatureType == featurePair.featureType)
+ .findFirst().orElse(null);
+ // Remove status callbacks from list.
+ if (callbackToRemove != null) {
+ mFeatureStatusCallbacks.remove(callbackToRemove);
+ }
+ removeImsFeatureBinder(featurePair.slotId, featurePair.featureType);
+ // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
+ mCallbacks.imsServiceFeatureRemoved(featurePair.slotId, featurePair.featureType, this);
+ try {
+ removeImsFeature(featurePair.slotId, featurePair.featureType,
+ (callbackToRemove != null ? callbackToRemove.getCallback() : null));
+ } catch (RemoteException e) {
+ // The connection to this ImsService doesn't exist. This may happen if the service
+ // has died and we are removing features.
+ Log.i(LOG_TAG, "Couldn't remove feature {" + featurePair.featureType
+ + "}, connection is down: " + e.getMessage());
+ }
+ } else {
+ // Don't update ImsService for emergency MMTEL feature.
+ Log.i(LOG_TAG, "doesn't support emergency calling on slot " + featurePair.slotId);
}
- removeImsFeature(featurePair.first, featurePair.second,
- (callbackToRemove != null ? callbackToRemove.getCallback() : null));
- removeImsFeatureBinder(featurePair.first, featurePair.second);
- // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
- mCallbacks.imsServiceFeatureRemoved(featurePair.first, featurePair.second, this);
// Send callback to ImsServiceProxy to change supported ImsFeatures
// Ensure that ImsServiceProxy callback occurs after ImsResolver callback. If an
// ImsManager requests the ImsService while it is being removed in ImsResolver, this
// callback will clean it up after.
- sendImsFeatureRemovedCallback(featurePair.first, featurePair.second);
+ sendImsFeatureRemovedCallback(featurePair.slotId, featurePair.featureType);
}
// This method should only be called when already synchronized on mLock.
@@ -708,16 +780,15 @@
.findFirst().orElse(null);
}
- private void notifyAllFeaturesRemoved() {
- if (mCallbacks == null) {
- Log.w(LOG_TAG, "notifyAllFeaturesRemoved called with invalid callbacks.");
- return;
- }
+ private void cleanupAllFeatures() {
synchronized (mLock) {
- for (Pair<Integer, Integer> feature : mImsFeatures) {
- mCallbacks.imsServiceFeatureRemoved(feature.first, feature.second, this);
- sendImsFeatureRemovedCallback(feature.first, feature.second);
+ // Remove all features and clean up all associated Binders.
+ for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) {
+ removeImsServiceFeature(i);
}
+ // remove all MmTelFeatureConnection callbacks, since we have already sent removed
+ // callback.
+ removeImsServiceFeatureCallbacks();
}
}
@@ -727,7 +798,6 @@
mImsServiceConnection = null;
mImsServiceControllerBinder = null;
setServiceController(null);
- mIsBound = false;
}
}
}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java b/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
index 094be71..4e0aea0 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
@@ -61,7 +61,7 @@
}
@Override
- protected String getServiceInterface() {
+ protected final String getServiceInterface() {
// Return compatibility version of String.
return ImsService.SERVICE_INTERFACE;
}
@@ -70,7 +70,7 @@
* Converts the new command to {@link MMTelFeature#turnOnIms()}.
*/
@Override
- public void enableIms(int slotId) {
+ public final void enableIms(int slotId) {
MmTelFeatureCompatAdapter adapter = mMmTelCompatAdapters.get(slotId);
if (adapter == null) {
Log.w(TAG, "enableIms: adapter null for slot :" + slotId);
@@ -87,7 +87,7 @@
* Converts the new command to {@link MMTelFeature#turnOffIms()}.
*/
@Override
- public void disableIms(int slotId) {
+ public final void disableIms(int slotId) {
MmTelFeatureCompatAdapter adapter = mMmTelCompatAdapters.get(slotId);
if (adapter == null) {
Log.w(TAG, "enableIms: adapter null for slot :" + slotId);
@@ -103,7 +103,8 @@
/**
* @return the IImsRegistration that corresponds to the slot id specified.
*/
- public IImsRegistration getRegistration(int slotId) throws RemoteException {
+ @Override
+ public final IImsRegistration getRegistration(int slotId) {
ImsRegistrationCompatAdapter adapter = mRegCompatAdapters.get(slotId);
if (adapter == null) {
Log.w(TAG, "getRegistration: Registration does not exist for slot " + slotId);
@@ -115,7 +116,8 @@
/**
* @return the IImsConfig that corresponds to the slot id specified.
*/
- public IImsConfig getConfig(int slotId) throws RemoteException {
+ @Override
+ public final IImsConfig getConfig(int slotId) {
ImsConfigCompatAdapter adapter = mConfigCompatAdapters.get(slotId);
if (adapter == null) {
Log.w(TAG, "getConfig: Config does not exist for slot " + slotId);
@@ -125,7 +127,14 @@
}
@Override
- protected IInterface createImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+ protected final void notifyImsServiceReady() {
+ Log.d(TAG, "notifyImsServiceReady");
+ // don't do anything for compat impl.
+ }
+
+ @Override
+ protected final IInterface createImsFeature(int slotId, int featureType,
+ IImsFeatureStatusCallback c)
throws RemoteException {
switch (featureType) {
case ImsFeature.MMTEL: {
@@ -140,14 +149,16 @@
}
@Override
- protected void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+ protected final void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
throws RemoteException {
if (featureType == ImsFeature.MMTEL) {
mMmTelCompatAdapters.remove(slotId);
mRegCompatAdapters.remove(slotId);
mConfigCompatAdapters.remove(slotId);
}
- mServiceController.removeImsFeature(slotId, featureType, c);
+ if (mServiceController != null) {
+ mServiceController.removeImsFeature(slotId, featureType, c);
+ }
}
@Override
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceControllerStaticCompat.java b/src/java/com/android/internal/telephony/ims/ImsServiceControllerStaticCompat.java
index 5b080d1..e39aa52 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceControllerStaticCompat.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceControllerStaticCompat.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
@@ -66,8 +65,13 @@
}
@Override
- protected MmTelInterfaceAdapter getInterface(int slotId, IImsFeatureStatusCallback c)
- throws RemoteException {
+ // used for add/remove features and cleanup in ImsServiceController.
+ protected boolean isServiceControllerAvailable() {
+ return mImsServiceCompat != null;
+ }
+
+ @Override
+ protected MmTelInterfaceAdapter getInterface(int slotId, IImsFeatureStatusCallback c) {
if (mImsServiceCompat == null) {
Log.w(TAG, "getInterface: IImsService returned null.");
return null;
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java b/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java
new file mode 100644
index 0000000..24ec0be
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsServiceController;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Manages the querying of multiple ImsServices asynchronously in order to retrieve the ImsFeatures
+ * they support.
+ */
+
+public class ImsServiceFeatureQueryManager {
+
+ private final class ImsServiceFeatureQuery implements ServiceConnection {
+
+ private static final String LOG_TAG = "ImsServiceFeatureQuery";
+
+ private final ComponentName mName;
+ private final String mIntentFilter;
+
+ ImsServiceFeatureQuery(ComponentName name, String intentFilter) {
+ mName = name;
+ mIntentFilter = intentFilter;
+ }
+
+ /**
+ * Starts the bind to the ImsService specified ComponentName.
+ * @return true if binding started, false if it failed and will not recover.
+ */
+ public boolean start() {
+ Log.d(LOG_TAG, "start: intent filter=" + mIntentFilter + ", name=" + mName);
+ Intent imsServiceIntent = new Intent(mIntentFilter).setComponent(mName);
+ int serviceFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
+ | Context.BIND_IMPORTANT;
+ boolean bindStarted = mContext.bindService(imsServiceIntent, this, serviceFlags);
+ if (!bindStarted) {
+ // Docs say to unbind if this fails.
+ cleanup();
+ }
+ return bindStarted;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.i(LOG_TAG, "onServiceConnected for component: " + name);
+ if (service != null) {
+ queryImsFeatures(IImsServiceController.Stub.asInterface(service));
+ } else {
+ Log.w(LOG_TAG, "onServiceConnected: " + name + " binder null, cleaning up.");
+ cleanup();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.w(LOG_TAG, "onServiceDisconnected for component: " + name);
+ }
+
+ private void queryImsFeatures(IImsServiceController controller) {
+ ImsFeatureConfiguration config;
+ try {
+ config = controller.querySupportedImsFeatures();
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "queryImsFeatures - error: " + e);
+ cleanup();
+ mListener.onError(mName);
+ return;
+ }
+ Set<ImsFeatureConfiguration.FeatureSlotPair> servicePairs = config.getServiceFeatures();
+ // Complete, remove from active queries and notify.
+ cleanup();
+ mListener.onComplete(mName, servicePairs);
+ }
+
+ private void cleanup() {
+ mContext.unbindService(this);
+ synchronized (mLock) {
+ mActiveQueries.remove(mName);
+ }
+ }
+ }
+
+ public interface Listener {
+ /**
+ * Called when a query has completed.
+ * @param name The Package Name of the query
+ * @param features A Set of slotid->feature pairs that the ImsService supports.
+ */
+ void onComplete(ComponentName name, Set<ImsFeatureConfiguration.FeatureSlotPair> features);
+
+ /**
+ * Called when a query has failed and should be retried.
+ */
+ void onError(ComponentName name);
+ }
+
+ // Maps an active ImsService query (by Package Name String) its query.
+ private final Map<ComponentName, ImsServiceFeatureQuery> mActiveQueries = new HashMap<>();
+ private final Context mContext;
+ private final Listener mListener;
+ private final Object mLock = new Object();
+
+ public ImsServiceFeatureQueryManager(Context context, Listener listener) {
+ mContext = context;
+ mListener = listener;
+ }
+
+ /**
+ * Starts an ImsService feature query for the ComponentName and Intent specified.
+ * @param name The ComponentName of the ImsService being queried.
+ * @param intentFilter The Intent filter that the ImsService specified.
+ * @return true if the query started, false if it was unable to start.
+ */
+ public boolean startQuery(ComponentName name, String intentFilter) {
+ synchronized (mLock) {
+ if (mActiveQueries.containsKey(name)) {
+ // We already have an active query, wait for it to return.
+ return true;
+ }
+ ImsServiceFeatureQuery query = new ImsServiceFeatureQuery(name, intentFilter);
+ mActiveQueries.put(name, query);
+ return query.start();
+ }
+ }
+
+ /**
+ * @return true if there are any active queries, false if the manager is idle.
+ */
+ public boolean isQueryInProgress() {
+ synchronized (mLock) {
+ return !mActiveQueries.isEmpty();
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java b/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
index a9653d5..7b0619b 100644
--- a/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
+++ b/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
@@ -155,6 +155,10 @@
@Override
public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException {
+ // At de-registration, notify the framework that no IMS capabilities are currently
+ // available.
+ Log.i(TAG, "registrationDisconnected: resetting MMTEL capabilities.");
+ notifyCapabilitiesStatusChanged(new MmTelCapabilities());
// Implemented in the Registration Adapter
}
@@ -510,7 +514,7 @@
private PendingIntent createIncomingCallPendingIntent() {
Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.setPackage(TelephonyManager.PHONE_PROCESS_NAME);
return PendingIntent.getBroadcast(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 69c03f6..0a86867 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -689,7 +689,7 @@
return mCT.dial(dialString, imsDialArgsBuilder.build());
} else if (mmi.isTemporaryModeCLIR()) {
imsDialArgsBuilder.setClirMode(mmi.getCLIRMode());
- return mCT.dial(dialString, imsDialArgsBuilder.build());
+ return mCT.dial(mmi.getDialingNumber(), imsDialArgsBuilder.build());
} else if (!mmi.isSupportedOverImsPhone()) {
// If the mmi is not supported by IMS service,
// try to initiate dialing with default phone
@@ -1279,12 +1279,12 @@
private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) {
CallForwardInfo cfInfo = new CallForwardInfo();
- cfInfo.status = info.mStatus;
- cfInfo.reason = getCFReasonFromCondition(info.mCondition);
+ cfInfo.status = info.getStatus();
+ cfInfo.reason = getCFReasonFromCondition(info.getCondition());
cfInfo.serviceClass = SERVICE_CLASS_VOICE;
- cfInfo.toa = info.mToA;
- cfInfo.number = info.mNumber;
- cfInfo.timeSeconds = info.mTimeSeconds;
+ cfInfo.toa = info.getToA();
+ cfInfo.number = info.getNumber();
+ cfInfo.timeSeconds = info.getTimeSeconds();
return cfInfo;
}
@@ -1308,10 +1308,10 @@
}
} else {
for (int i = 0, s = infos.length; i < s; i++) {
- if (infos[i].mCondition == ImsUtInterface.CDIV_CF_UNCONDITIONAL) {
+ if (infos[i].getCondition() == ImsUtInterface.CDIV_CF_UNCONDITIONAL) {
if (r != null) {
- setVoiceCallForwardingFlag(r, 1, (infos[i].mStatus == 1),
- infos[i].mNumber);
+ setVoiceCallForwardingFlag(r, 1, (infos[i].getStatus() == 1),
+ infos[i].getNumber());
}
}
cfInfos[i] = getCallForwardInfo(infos[i]);
@@ -1325,7 +1325,7 @@
int[] cbInfos = new int[1];
cbInfos[0] = SERVICE_CLASS_NONE;
- if (infos[0].mStatus == 1) {
+ if (infos[0].getStatus() == 1) {
cbInfos[0] = SERVICE_CLASS_VOICE;
}
@@ -1336,7 +1336,7 @@
int[] cwInfos = new int[2];
cwInfos[0] = 0;
- if (infos[0].mStatus == 1) {
+ if (infos[0].getStatus() == 1) {
cwInfos[0] = 1;
cwInfos[1] = SERVICE_CLASS_VOICE;
}
@@ -1432,8 +1432,14 @@
ServiceState newServiceState = (ServiceState) ar.result;
// only update if roaming status changed
if (mRoaming != newServiceState.getRoaming()) {
- if (DBG) logd("Roaming state changed");
- updateRoamingState(newServiceState.getRoaming());
+ if (DBG) logd("Roaming state changed - " + mRoaming);
+ // Update WFC mode only if voice or data is in service.
+ // The STATE_IN_SERVICE is checked to prevent wifi calling mode change
+ // when phone moves from roaming to no service.
+ boolean isInService =
+ (newServiceState.getVoiceRegState() == ServiceState.STATE_IN_SERVICE ||
+ newServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE);
+ updateRoamingState(newServiceState.getRoaming(), isInService);
}
break;
case EVENT_VOICE_CALL_ENDED:
@@ -1442,7 +1448,7 @@
// only update if roaming status changed
boolean newRoaming = getCurrentRoaming();
if (mRoaming != newRoaming) {
- updateRoamingState(newRoaming);
+ updateRoamingState(newRoaming, true);
}
break;
@@ -1804,12 +1810,16 @@
return mCT.getVtDataUsage(perUidStats);
}
- private void updateRoamingState(boolean newRoaming) {
+ private void updateRoamingState(boolean newRoaming, boolean isInService) {
if (mCT.getState() == PhoneConstants.State.IDLE) {
if (DBG) logd("updateRoamingState now: " + newRoaming);
mRoaming = newRoaming;
- ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
- imsManager.setWfcMode(imsManager.getWfcMode(newRoaming), newRoaming);
+ if (isInService) {
+ ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
+ imsManager.setWfcMode(imsManager.getWfcMode(newRoaming), newRoaming);
+ } else {
+ if (DBG) Rlog.d(LOG_TAG, "updateRoamingState service state is OUT_OF_SERVICE");
+ }
} else {
if (DBG) logd("updateRoamingState postponed: " + newRoaming);
mCT.registerForVoiceCallEnded(this,
@@ -1820,7 +1830,7 @@
private boolean getCurrentRoaming() {
TelephonyManager tm = (TelephonyManager) mContext
.getSystemService(Context.TELEPHONY_SERVICE);
- return tm.isNetworkRoaming();
+ return tm.isNetworkRoaming(getSubId());
}
@Override
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index ce6822e..460ad95 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -1537,6 +1537,16 @@
return mDesiredMute;
}
+ /**
+ * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c that represents the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ * @param result the result message to send when done. If non-null, the {@link Message} must
+ * contain a valid {@link android.os.Messenger} in the {@link Message#replyTo} field,
+ * since this can be used across IPC boundaries.
+ */
public void sendDtmf(char c, Message result) {
if (DBG) log("sendDtmf");
@@ -2258,7 +2268,8 @@
if (mRingingCall.getState().isRinging()) {
// Drop pending MO. We should address incoming call first
mPendingMO = null;
- } else if (mPendingMO != null) {
+ } else if (mPendingMO != null
+ && mPendingMO.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
sendEmptyMessage(EVENT_DIAL_PENDINGMO);
}
}
@@ -2373,6 +2384,9 @@
mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
}
+ if (imsCall != mCallExpectedToResume) {
+ mCallExpectedToResume = null;
+ }
}
mPhone.notifySuppServiceFailed(Phone.SuppService.HOLD);
}
@@ -2545,6 +2559,8 @@
// based on the user facing UI.
mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE);
+ call.resetIsMergeRequestedByConf(false);
+
// Start plumbing this even through Telecom so other components can take
// appropriate action.
ImsPhoneConnection conn = findConnection(call);
@@ -2670,10 +2686,6 @@
ImsPhoneConnection conn = findConnection(imsCall);
if (conn != null) {
conn.onRttModifyResponseReceived(status);
- if (status ==
- android.telecom.Connection.RttModifyStatus.SESSION_MODIFY_REQUEST_SUCCESS) {
- conn.startRttTextProcessing();
- }
}
}
@@ -2793,7 +2805,6 @@
@Override
public void onDeregistered(ImsReasonInfo imsReasonInfo) {
if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo);
- resetImsCapabilities();
mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
mPhone.setImsRegistered(false);
mPhone.processDisconnectReason(imsReasonInfo);
@@ -3580,7 +3591,8 @@
if (imsCall != null) {
if (conn.hasCapabilities(
Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL |
- Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE)) {
+ Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE)
+ && !mSupportPauseVideo) {
// If the carrier supports downgrading to voice, then we can simply issue a
// downgrade to voice instead of terminating the call.
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
index 76cc26d..93a6d3e 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
@@ -404,6 +404,14 @@
}
@Override
+ public void invokeOemRilRequestRaw(byte[] data, Message response) {
+ }
+
+ @Override
+ public void invokeOemRilRequestStrings(String[] strings, Message response) {
+ }
+
+ @Override
public void setBandMode (int bandMode, Message response) {
}
@@ -645,6 +653,17 @@
}
@Override
+ public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
+ int[] thresholdsDbm, int ran, Message result) {
+ }
+
+ @Override
+ public void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
+ int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
+ Message result) {
+ }
+
+ @Override
public void setSimCardPower(int state, Message result) {
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 2448ea6..d63b9c2 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -23,6 +23,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Messenger;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.Registrant;
@@ -33,12 +34,12 @@
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
import android.telephony.ServiceState;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsStreamMediaProfile;
import android.text.TextUtils;
import com.android.ims.ImsCall;
-import android.telephony.ims.ImsCallProfile;
import com.android.ims.ImsException;
-import android.telephony.ims.ImsStreamMediaProfile;
import com.android.ims.internal.ImsVideoCallProviderWrapper;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
@@ -79,6 +80,7 @@
private UUSInfo mUusInfo;
private Handler mHandler;
+ private Messenger mHandlerMessenger;
private PowerManager.WakeLock mPartialWakeLock;
@@ -171,6 +173,7 @@
mOwner = ct;
mHandler = new MyHandler(mOwner.getLooper());
+ mHandlerMessenger = new Messenger(mHandler);
mImsCall = imsCall;
if ((imsCall != null) && (imsCall.getCallProfile() != null)) {
@@ -495,7 +498,9 @@
private boolean
processPostDialChar(char c) {
if (PhoneNumberUtils.is12Key(c)) {
- mOwner.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
+ Message dtmfComplete = mHandler.obtainMessage(EVENT_DTMF_DONE);
+ dtmfComplete.replyTo = mHandlerMessenger;
+ mOwner.sendDtmf(c, dtmfComplete);
} else if (c == PhoneNumberUtils.PAUSE) {
// From TS 22.101:
// It continues...
@@ -985,18 +990,35 @@
imsCall.sendRttModifyResponse(accept);
if (accept) {
setCurrentRttTextStream(textStream);
- startRttTextProcessing();
} else {
Rlog.e(LOG_TAG, "sendRttModifyResponse: foreground call has no connections");
}
}
public void onRttMessageReceived(String message) {
- getOrCreateRttTextHandler().sendToInCall(message);
+ synchronized (this) {
+ if (mRttTextHandler == null) {
+ Rlog.w(LOG_TAG, "onRttMessageReceived: RTT text handler not available."
+ + " Attempting to create one.");
+ if (mRttTextStream == null) {
+ Rlog.e(LOG_TAG, "onRttMessageReceived:"
+ + " Unable to process incoming message. No textstream available");
+ return;
+ }
+ createRttTextHandler();
+ }
+ }
+ mRttTextHandler.sendToInCall(message);
}
public void setCurrentRttTextStream(android.telecom.Connection.RttTextStream rttTextStream) {
- mRttTextStream = rttTextStream;
+ synchronized (this) {
+ mRttTextStream = rttTextStream;
+ if (mRttTextHandler == null && mIsRttEnabledForCall) {
+ Rlog.i(LOG_TAG, "setCurrentRttTextStream: Creating a text handler");
+ createRttTextHandler();
+ }
+ }
}
public boolean hasRttTextStream() {
@@ -1008,20 +1030,24 @@
}
public void startRttTextProcessing() {
- if (mRttTextStream == null) {
- Rlog.w(LOG_TAG, "startRttTextProcessing: no RTT text stream. Ignoring.");
- return;
+ synchronized (this) {
+ if (mRttTextStream == null) {
+ Rlog.w(LOG_TAG, "startRttTextProcessing: no RTT text stream. Ignoring.");
+ return;
+ }
+ if (mRttTextHandler != null) {
+ Rlog.w(LOG_TAG, "startRttTextProcessing: RTT text handler already exists");
+ return;
+ }
+ createRttTextHandler();
}
- getOrCreateRttTextHandler().initialize(mRttTextStream);
}
- private ImsRttTextHandler getOrCreateRttTextHandler() {
- if (mRttTextHandler != null) {
- return mRttTextHandler;
- }
+ // Make sure to synchronize on ImsPhoneConnection.this before calling.
+ private void createRttTextHandler() {
mRttTextHandler = new ImsRttTextHandler(Looper.getMainLooper(),
(message) -> getImsCall().sendRttMessage(message));
- return mRttTextHandler;
+ mRttTextHandler.initialize(mRttTextStream);
}
/**
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index a72b904..87e51ec 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -36,13 +36,13 @@
import android.os.ResultReceiver;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsData;
+import android.telephony.ims.ImsSsInfo;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import com.android.ims.ImsException;
-import android.telephony.ims.ImsReasonInfo;
-import android.telephony.ims.ImsSsData;
-import android.telephony.ims.ImsSsInfo;
import com.android.ims.ImsUtInterface;
import com.android.internal.telephony.CallForwardInfo;
import com.android.internal.telephony.CallStateException;
@@ -50,7 +50,6 @@
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.MmiCode;
import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.uicc.IccRecords;
import java.util.regex.Matcher;
@@ -1278,10 +1277,18 @@
sb.append(mContext.getText(
com.android.internal.R.string.serviceEnabled));
}
+ // Record CLIR setting
+ if (mSc.equals(SC_CLIR)) {
+ mPhone.saveClirSetting(CommandsInterface.CLIR_INVOCATION);
+ }
} else if (isDeactivate()) {
mState = State.COMPLETE;
sb.append(mContext.getText(
com.android.internal.R.string.serviceDisabled));
+ // Record CLIR setting
+ if (mSc.equals(SC_CLIR)) {
+ mPhone.saveClirSetting(CommandsInterface.CLIR_SUPPRESSION);
+ }
} else if (isRegister()) {
mState = State.COMPLETE;
sb.append(mContext.getText(
@@ -1472,11 +1479,11 @@
ssInfo = (ImsSsInfo) ssInfoResp.getParcelable(UT_BUNDLE_KEY_SSINFO);
if (ssInfo != null) {
Rlog.d(LOG_TAG,
- "onSuppSvcQueryComplete: ImsSsInfo mStatus = " + ssInfo.mStatus);
- if (ssInfo.mStatus == ImsSsInfo.DISABLED) {
+ "onSuppSvcQueryComplete: ImsSsInfo mStatus = " + ssInfo.getStatus());
+ if (ssInfo.getStatus() == ImsSsInfo.DISABLED) {
sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
mState = State.COMPLETE;
- } else if (ssInfo.mStatus == ImsSsInfo.ENABLED) {
+ } else if (ssInfo.getStatus() == ImsSsInfo.ENABLED) {
sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
mState = State.COMPLETE;
} else {
@@ -1524,10 +1531,10 @@
sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
} else {
for (int i = 0, s = infos.length; i < s ; i++) {
- if (infos[i].mIcbNum !=null) {
- sb.append("Num: " + infos[i].mIcbNum + " status: "
- + infos[i].mStatus + "\n");
- } else if (infos[i].mStatus == 1) {
+ if (infos[i].getIcbNum() != null) {
+ sb.append("Num: " + infos[i].getIcbNum() + " status: "
+ + infos[i].getStatus() + "\n");
+ } else if (infos[i].getStatus() == 1) {
sb.append(mContext.getText(com.android.internal
.R.string.serviceEnabled));
} else {
@@ -1718,7 +1725,7 @@
}
void parseSsData(ImsSsData ssData) {
- ImsException ex = (ssData.result != RILConstants.SUCCESS)
+ ImsException ex = (ssData.result != ImsSsData.RESULT_SUCCESS)
? new ImsException(null, ssData.result) : null;
mSc = getScStringFromScType(ssData.serviceType);
mAction = getActionStringFromReqType(ssData.requestType);
@@ -1729,7 +1736,7 @@
case ImsSsData.SS_DEACTIVATION:
case ImsSsData.SS_REGISTRATION:
case ImsSsData.SS_ERASURE:
- if ((ssData.result == RILConstants.SUCCESS)
+ if ((ssData.result == ImsSsData.RESULT_SUCCESS)
&& ssData.isTypeUnConditional()) {
/*
* When ssData.serviceType is unconditional (SS_CFU or SS_CF_ALL) and
@@ -1750,30 +1757,31 @@
Rlog.e(LOG_TAG, "setCallForwardingFlag aborted. sim records is null.");
}
}
- onSetComplete(null, new AsyncResult(null, ssData.cfInfo, ex));
+ onSetComplete(null, new AsyncResult(null, ssData.getCallForwardInfo(), ex));
break;
case ImsSsData.SS_INTERROGATION:
if (ssData.isTypeClir()) {
Rlog.d(LOG_TAG, "CLIR INTERROGATION");
Bundle clirInfo = new Bundle();
- clirInfo.putIntArray(UT_BUNDLE_KEY_CLIR, ssData.ssInfo);
+ clirInfo.putIntArray(UT_BUNDLE_KEY_CLIR, ssData.getSuppServiceInfo());
onQueryClirComplete(new AsyncResult(null, clirInfo, ex));
} else if (ssData.isTypeCF()) {
Rlog.d(LOG_TAG, "CALL FORWARD INTERROGATION");
onQueryCfComplete(new AsyncResult(null, mPhone
- .handleCfQueryResult(ssData.cfInfo), ex));
+ .handleCfQueryResult(ssData.getCallForwardInfo()), ex));
} else if (ssData.isTypeBarring()) {
- onSuppSvcQueryComplete(new AsyncResult(null, ssData.ssInfo, ex));
+ onSuppSvcQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo(), ex));
} else if (ssData.isTypeColr() || ssData.isTypeClip() || ssData.isTypeColp()) {
- ImsSsInfo ssInfo = new ImsSsInfo();
- ssInfo.mStatus = ssData.ssInfo[0];
+ int[] suppServiceInfo = ssData.getSuppServiceInfo();
+ ImsSsInfo ssInfo = new ImsSsInfo(suppServiceInfo[0], null);
Bundle clInfo = new Bundle();
clInfo.putParcelable(UT_BUNDLE_KEY_SSINFO, ssInfo);
onSuppSvcQueryComplete(new AsyncResult(null, clInfo, ex));
} else if (ssData.isTypeIcb()) {
- onIcbQueryComplete(new AsyncResult(null, ssData.imsSsInfo, ex));
+ onIcbQueryComplete(new AsyncResult(null, ssData.getImsSpecificSuppServiceInfo(),
+ ex));
} else {
- onQueryComplete(new AsyncResult(null, ssData.ssInfo, ex));
+ onQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo(), ex));
}
break;
default:
diff --git a/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java b/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
index 530be1d..5004ce7 100644
--- a/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
@@ -88,4 +88,9 @@
mEvent.format = format;
return this;
}
+
+ public SmsSessionEventBuilder setCellBroadcastMessage(SmsSession.Event.CBMessage msg) {
+ mEvent.cellBroadcastMessage = msg;
+ return this;
+ }
}
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index 11ca080..8b9d580 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -1705,7 +1705,7 @@
* @param tech SMS RAT
* @param format SMS format. Either 3GPP or 3GPP2.
*/
- public void writeRilSendSms(int phoneId, int rilSerial, int tech, int format) {
+ public synchronized void writeRilSendSms(int phoneId, int rilSerial, int tech, int format) {
InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_SEND)
@@ -1724,7 +1724,7 @@
* @param tech SMS RAT
* @param format SMS format. Either 3GPP or 3GPP2.
*/
- public void writeRilNewSms(int phoneId, int tech, int format) {
+ public synchronized void writeRilNewSms(int phoneId, int tech, int format) {
InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED)
@@ -1736,6 +1736,42 @@
}
/**
+ * Write incoming Broadcast SMS event
+ *
+ * @param phoneId Phone id
+ * @param format CB msg format
+ * @param priority CB msg priority
+ * @param isCMAS true if msg is CMAS
+ * @param isETWS true if msg is ETWS
+ * @param serviceCategory Service category of CB msg
+ */
+ public synchronized void writeNewCBSms(int phoneId, int format, int priority, boolean isCMAS,
+ boolean isETWS, int serviceCategory) {
+ InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
+
+ int type;
+ if (isCMAS) {
+ type = SmsSession.Event.CBMessageType.CMAS;
+ } else if (isETWS) {
+ type = SmsSession.Event.CBMessageType.ETWS;
+ } else {
+ type = SmsSession.Event.CBMessageType.OTHER;
+ }
+
+ SmsSession.Event.CBMessage cbm = new SmsSession.Event.CBMessage();
+ cbm.msgFormat = format;
+ cbm.msgPriority = priority + 1;
+ cbm.msgType = type;
+ cbm.serviceCategory = serviceCategory;
+
+ smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.CB_SMS_RECEIVED)
+ .setCellBroadcastMessage(cbm)
+ );
+
+ finishSmsSessionIfNeeded(smsSession);
+ }
+
+ /**
* Write NITZ event
*
* @param phoneId Phone id
@@ -1782,12 +1818,18 @@
final CarrierIdMatchingResult carrierIdMatchingResult = new CarrierIdMatchingResult();
if (cid != TelephonyManager.UNKNOWN_CARRIER_ID) {
+ // Successful matching event if result only has carrierId
carrierIdMatchingResult.carrierId = cid;
+ // Unknown Gid1 event if result only has carrierId, gid1 and mccmnc
if (gid1 != null) {
+ carrierIdMatchingResult.mccmnc = mccmnc;
carrierIdMatchingResult.gid1 = gid1;
}
} else {
- carrierIdMatchingResult.mccmnc = mccmnc;
+ // Unknown mccmnc event if result only has mccmnc
+ if (mccmnc != null) {
+ carrierIdMatchingResult.mccmnc = mccmnc;
+ }
}
carrierIdMatching.cidTableVersion = version;
diff --git a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
index 0cd8fab..396fd4b 100644
--- a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -405,6 +405,14 @@
}
@Override
+ public void invokeOemRilRequestRaw(byte[] data, Message response) {
+ }
+
+ @Override
+ public void invokeOemRilRequestStrings(String[] strings, Message response) {
+ }
+
+ @Override
public void setBandMode (int bandMode, Message response) {
}
@@ -647,6 +655,17 @@
}
@Override
+ public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
+ int[] thresholdsDbm, int ran, Message result) {
+ }
+
+ @Override
+ public void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
+ int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
+ Message result) {
+ }
+
+ @Override
public void setSimCardPower(int state, Message result) {
}
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommands.java b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
index 8598403..1fc9a26 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -1509,6 +1509,15 @@
}
@Override
+ public void invokeOemRilRequestRaw(byte[] data, Message response) {
+ // Just echo back data
+ if (response != null) {
+ AsyncResult.forMessage(response).result = data;
+ response.sendToTarget();
+ }
+ }
+
+ @Override
public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
Message response) {
// Just echo back data
@@ -1518,6 +1527,15 @@
}
}
+ @Override
+ public void invokeOemRilRequestStrings(String[] strings, Message response) {
+ // Just echo back data
+ if (response != null) {
+ AsyncResult.forMessage(response).result = strings;
+ response.sendToTarget();
+ }
+ }
+
//***** SimulatedRadioControl
@@ -2201,6 +2219,17 @@
}
@Override
+ public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
+ int[] thresholdsDbm, int ran, Message result) {
+ }
+
+ @Override
+ public void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
+ int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
+ Message result) {
+ }
+
+ @Override
public void setSimCardPower(int state, Message result) {
}
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java b/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
index 0a62268..f43cee0 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
@@ -1058,6 +1058,26 @@
}
@Override
+ public void invokeOemRilRequestRaw(byte[] data, Message response) {
+
+ }
+
+ @Override
+ public void invokeOemRilRequestStrings(String[] strings, Message response) {
+
+ }
+
+ @Override
+ public void setOnUnsolOemHookRaw(Handler h, int what, Object obj) {
+
+ }
+
+ @Override
+ public void unSetOnUnsolOemHookRaw(Handler h) {
+
+ }
+
+ @Override
public void sendTerminalResponse(String contents, Message response) {
}
@@ -1394,6 +1414,17 @@
}
@Override
+ public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
+ int[] thresholdsDbm, int ran, Message result) {
+ }
+
+ @Override
+ public void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
+ int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
+ Message result) {
+ }
+
+ @Override
public void setSimCardPower(int state, Message result) {
}
diff --git a/src/java/com/android/internal/telephony/uicc/CarrierTestOverride.java b/src/java/com/android/internal/telephony/uicc/CarrierTestOverride.java
index 2e72500..61379d2 100644
--- a/src/java/com/android/internal/telephony/uicc/CarrierTestOverride.java
+++ b/src/java/com/android/internal/telephony/uicc/CarrierTestOverride.java
@@ -43,6 +43,7 @@
* Sample xml:
* <carrierTestOverrides>
<carrierTestOverride key="isInTestMode" value="true"/>
+ <carrierTestOverride key="mccmnc" value="310010" />
<carrierTestOverride key="gid1" value="bae0000000000000"/>
<carrierTestOverride key="gid2" value="ffffffffffffffff"/>
<carrierTestOverride key="imsi" value="310010123456789"/>
@@ -58,6 +59,7 @@
static final String CARRIER_TEST_XML_ITEM_KEY = "key";
static final String CARRIER_TEST_XML_ITEM_VALUE = "value";
static final String CARRIER_TEST_XML_ITEM_KEY_STRING_ISINTESTMODE = "isInTestMode";
+ static final String CARRIER_TEST_XML_ITEM_KEY_STRING_MCCMNC = "mccmnc";
static final String CARRIER_TEST_XML_ITEM_KEY_STRING_GID1 = "gid1";
static final String CARRIER_TEST_XML_ITEM_KEY_STRING_GID2 = "gid2";
static final String CARRIER_TEST_XML_ITEM_KEY_STRING_IMSI = "imsi";
@@ -144,6 +146,29 @@
}
}
+ String getFakeMccMnc() {
+ try {
+ String mccmnc = mCarrierTestParamMap.get(CARRIER_TEST_XML_ITEM_KEY_STRING_MCCMNC);
+ Rlog.d(LOG_TAG, "reading mccmnc from CarrierTestConfig file: " + mccmnc);
+ return mccmnc;
+ } catch (NullPointerException e) {
+ Rlog.w(LOG_TAG, "No mccmnc in CarrierTestConfig file ");
+ return null;
+ }
+ }
+
+ void override(String mccmnc, String imsi, String iccid, String gid1, String gid2, String pnn,
+ String spn) {
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_ISINTESTMODE, "true");
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_MCCMNC, mccmnc);
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_IMSI, imsi);
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_ICCID, iccid);
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_GID1, gid1);
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_GID2, gid2);
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_PNN, pnn);
+ mCarrierTestParamMap.put(CARRIER_TEST_XML_ITEM_KEY_STRING_SPN, spn);
+ }
+
private void loadCarrierTestOverrides() {
FileReader carrierTestConfigReader;
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
deleted file mode 100644
index f9629e7..0000000
--- a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
+++ /dev/null
@@ -1,778 +0,0 @@
-/*
- * Copyright (C) 2012 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 android.app.ActivityManager;
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Registrant;
-import android.os.RegistrantList;
-import android.os.UserHandle;
-import android.telephony.Rlog;
-import android.telephony.ServiceState;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.CommandsInterface.RadioState;
-import com.android.internal.telephony.IccCard;
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.IccCardConstants.State;
-import com.android.internal.telephony.MccTable;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
-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 java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * @Deprecated use {@link UiccController}.getUiccCard instead.
- *
- * The Phone App assumes that there is only one icc card, and one icc application
- * available at a time. Moreover, it assumes such object (represented with IccCard)
- * is available all the time (whether {@link RILConstants#RIL_REQUEST_GET_SIM_STATUS} returned
- * or not, whether card has desired application or not, whether there really is a card in the
- * slot or not).
- *
- * UiccController, however, can handle multiple instances of icc objects (multiple
- * {@link UiccCardApplication}, multiple {@link IccFileHandler}, multiple {@link IccRecords})
- * created and destroyed dynamically during phone operation.
- *
- * This class implements the IccCard interface that is always available (right after default
- * phone object is constructed) to expose the current (based on voice radio technology)
- * application on the uicc card, so that external apps won't break.
- */
-
-public class IccCardProxy extends Handler implements IccCard {
- private static final boolean DBG = true;
- private static final String LOG_TAG = "IccCardProxy";
-
- private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 1;
- private static final int EVENT_RADIO_ON = 2;
- private static final int EVENT_ICC_CHANGED = 3;
- private static final int EVENT_ICC_ABSENT = 4;
- private static final int EVENT_ICC_LOCKED = 5;
- private static final int EVENT_APP_READY = 6;
- private static final int EVENT_RECORDS_LOADED = 7;
- private static final int EVENT_IMSI_READY = 8;
- private static final int EVENT_NETWORK_LOCKED = 9;
-
- private static final int EVENT_ICC_RECORD_EVENTS = 500;
- private static final int EVENT_CARRIER_PRIVILEGES_LOADED = 503;
-
- private Integer mPhoneId = null;
-
- private final Object mLock = new Object();
- private Context mContext;
- private CommandsInterface mCi;
- private TelephonyManager mTelephonyManager;
-
- private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
-
- private int mCurrentAppType = UiccController.APP_FAM_3GPP; //default to 3gpp?
- private UiccController mUiccController = null;
- private UiccSlot mUiccSlot = null;
- private UiccCard mUiccCard = null;
- private UiccCardApplication mUiccApplication = null;
- private IccRecords mIccRecords = null;
- private RadioState mRadioState = RadioState.RADIO_UNAVAILABLE;
- private boolean mInitialized = false;
- private State mExternalState = State.UNKNOWN;
-
- public static final String ACTION_INTERNAL_SIM_STATE_CHANGED = "android.intent.action.internal_sim_state_changed";
-
- public IccCardProxy(Context context, CommandsInterface ci, int phoneId) {
- if (DBG) log("ctor: ci=" + ci + " phoneId=" + phoneId);
- mContext = context;
- mCi = ci;
- mPhoneId = phoneId;
- mTelephonyManager = (TelephonyManager) mContext.getSystemService(
- Context.TELEPHONY_SERVICE);
- mUiccController = UiccController.getInstance();
- mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
- ci.registerForOn(this,EVENT_RADIO_ON, null);
- ci.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
-
- resetProperties();
- }
-
- public void dispose() {
- synchronized (mLock) {
- log("Disposing");
- //Cleanup icc references
- mUiccController.unregisterForIccChanged(this);
- mUiccController = null;
- mCi.unregisterForOn(this);
- mCi.unregisterForOffOrNotAvailable(this);
- }
- }
-
- /*
- * The card application that the external world sees will be based on the
- * voice radio technology only!
- */
- public void setVoiceRadioTech(int radioTech) {
- synchronized (mLock) {
- if (DBG) {
- log("Setting radio tech " + ServiceState.rilRadioTechnologyToString(radioTech));
- }
- if (ServiceState.isGsm(radioTech)) {
- mCurrentAppType = UiccController.APP_FAM_3GPP;
- } else {
- mCurrentAppType = UiccController.APP_FAM_3GPP2;
- }
- updateCurrentAppType();
- }
- }
-
- /**
- * Update current app type and post EVENT_ICC_CHANGED.
- */
- private void updateCurrentAppType() {
- synchronized (mLock) {
- boolean isLteOnCdmaMode = TelephonyManager.getLteOnCdmaModeStatic()
- == PhoneConstants.LTE_ON_CDMA_TRUE;
- if (mCurrentAppType == UiccController.APP_FAM_3GPP2) {
- if (isLteOnCdmaMode) {
- log("updateCurrentAppType: is cdma/lte device, force IccCardProxy into 3gpp"
- + " mode");
- mCurrentAppType = UiccController.APP_FAM_3GPP;
- }
-
- if (DBG) {
- log("updateCurrentAppType: "
- + " mCurrentAppType=" + mCurrentAppType
- + " isLteOnCdmaMode=" + isLteOnCdmaMode);
- }
- }
-
- mInitialized = true;
- sendMessage(obtainMessage(EVENT_ICC_CHANGED));
- }
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_RADIO_OFF_OR_UNAVAILABLE:
- mRadioState = mCi.getRadioState();
- updateExternalState();
- break;
- case EVENT_RADIO_ON:
- mRadioState = RadioState.RADIO_ON;
- if (!mInitialized) {
- updateCurrentAppType();
- } else {
- // updateCurrentAppType() triggers ICC_CHANGED, which eventually
- // calls updateExternalState; thus, we don't need this in the
- // above case
- updateExternalState();
- }
- break;
- case EVENT_ICC_CHANGED:
- if (mInitialized) {
- updateIccAvailability();
- }
- break;
- case EVENT_ICC_ABSENT:
- setExternalState(State.ABSENT);
- break;
- case EVENT_ICC_LOCKED:
- processLockedState();
- break;
- case EVENT_APP_READY:
- setExternalState(State.READY);
- break;
- case EVENT_RECORDS_LOADED:
- // Update the MCC/MNC.
- if (mIccRecords != null) {
- String operator = mIccRecords.getOperatorNumeric();
- log("operator=" + operator + " mPhoneId=" + mPhoneId);
-
- if (!TextUtils.isEmpty(operator)) {
- mTelephonyManager.setSimOperatorNumericForPhone(mPhoneId, operator);
- String countryCode = operator.substring(0,3);
- if (countryCode != null) {
- mTelephonyManager.setSimCountryIsoForPhone(mPhoneId,
- MccTable.countryCodeForMcc(Integer.parseInt(countryCode)));
- } else {
- loge("EVENT_RECORDS_LOADED Country code is null");
- }
- } else {
- loge("EVENT_RECORDS_LOADED Operator name is null");
- }
- }
- if (mUiccCard != null && !mUiccCard.areCarrierPriviligeRulesLoaded()) {
- mUiccCard.registerForCarrierPrivilegeRulesLoaded(
- this, EVENT_CARRIER_PRIVILEGES_LOADED, null);
- } else {
- setExternalState(State.LOADED);
- }
- break;
- case EVENT_IMSI_READY:
- broadcastInternalIccStateChangedIntent(IccCardConstants.INTENT_VALUE_ICC_IMSI,
- null);
- break;
- case EVENT_NETWORK_LOCKED:
- mNetworkLockedRegistrants.notifyRegistrants();
- setExternalState(State.NETWORK_LOCKED);
- break;
- case EVENT_ICC_RECORD_EVENTS:
- if ((mCurrentAppType == UiccController.APP_FAM_3GPP) && (mIccRecords != null)) {
- AsyncResult ar = (AsyncResult)msg.obj;
- int eventCode = (Integer) ar.result;
- if (eventCode == SIMRecords.EVENT_SPN) {
- mTelephonyManager.setSimOperatorNameForPhone(
- mPhoneId, mIccRecords.getServiceProviderName());
- }
- }
- break;
-
- case EVENT_CARRIER_PRIVILEGES_LOADED:
- log("EVENT_CARRIER_PRIVILEGES_LOADED");
- if (mUiccCard != null) {
- mUiccCard.unregisterForCarrierPrivilegeRulesLoaded(this);
- }
- setExternalState(State.LOADED);
- break;
-
- default:
- loge("Unhandled message with number: " + msg.what);
- break;
- }
- }
-
- private void updateIccAvailability() {
- synchronized (mLock) {
- UiccSlot newSlot = mUiccController.getUiccSlotForPhone(mPhoneId);
- UiccCard newCard = mUiccController.getUiccCard(mPhoneId);
- UiccCardApplication newApp = null;
- IccRecords newRecords = null;
- if (newCard != null) {
- newApp = newCard.getApplication(mCurrentAppType);
- if (newApp != null) {
- newRecords = newApp.getIccRecords();
- }
- }
-
- if (mIccRecords != newRecords || mUiccApplication != newApp || mUiccCard != newCard
- || mUiccSlot != newSlot) {
- if (DBG) log("Icc changed. Reregistering.");
- unregisterUiccCardEvents();
- mUiccSlot = newSlot;
- mUiccCard = newCard;
- mUiccApplication = newApp;
- mIccRecords = newRecords;
- registerUiccCardEvents();
- }
- updateExternalState();
- }
- }
-
- void resetProperties() {
- if (mCurrentAppType == UiccController.APP_FAM_3GPP) {
- log("update icc_operator_numeric=" + "");
- mTelephonyManager.setSimOperatorNumericForPhone(mPhoneId, "");
- mTelephonyManager.setSimCountryIsoForPhone(mPhoneId, "");
- mTelephonyManager.setSimOperatorNameForPhone(mPhoneId, "");
- }
- }
-
- private void HandleDetectedState() {
- // CAF_MSIM SAND
-// setExternalState(State.DETECTED, false);
- }
-
- private void updateExternalState() {
-
- // mUiccCard could be null at bootup, before valid card states have
- // been received from UiccController.
- if (mUiccCard == null) {
- setExternalState(State.UNKNOWN);
- return;
- }
-
- if (mUiccCard.getCardState() == CardState.CARDSTATE_ABSENT) {
- /*
- * Both IccCardProxy and UiccController are registered for
- * RadioState changes. When the UiccController receives a radio
- * state changed to Unknown it will dispose of all of the IccCard
- * objects, which will then notify the IccCardProxy and the null
- * object will force the state to unknown. However, because the
- * IccCardProxy is also registered for RadioState changes, it will
- * recieve that signal first. By triggering on radio state changes
- * directly, we reduce the time window during which the modem is
- * UNAVAILABLE but the IccStatus is reported as something valid.
- * This is not ideal.
- */
- if (mRadioState == RadioState.RADIO_UNAVAILABLE) {
- setExternalState(State.UNKNOWN);
- } else {
- setExternalState(State.ABSENT);
- }
- return;
- }
-
- if (mUiccCard.getCardState() == CardState.CARDSTATE_ERROR) {
- setExternalState(State.CARD_IO_ERROR);
- return;
- }
-
- if (mUiccCard.getCardState() == CardState.CARDSTATE_RESTRICTED) {
- setExternalState(State.CARD_RESTRICTED);
- return;
- }
-
- if (mUiccApplication == null) {
- setExternalState(State.NOT_READY);
- return;
- }
-
- // By process of elimination, the UICC Card State = PRESENT
- switch (mUiccApplication.getState()) {
- case APPSTATE_UNKNOWN:
- /*
- * APPSTATE_UNKNOWN is a catch-all state reported whenever the app
- * is not explicitly in one of the other states. To differentiate the
- * case where we know that there is a card present, but the APP is not
- * ready, we choose NOT_READY here instead of unknown. This is possible
- * in at least two cases:
- * 1) A transient during the process of the SIM bringup
- * 2) There is no valid App on the SIM to load, which can be the case with an
- * eSIM/soft SIM.
- */
- setExternalState(State.NOT_READY);
- break;
- case APPSTATE_DETECTED:
- HandleDetectedState();
- break;
- case APPSTATE_SUBSCRIPTION_PERSO:
- if (mUiccApplication.getPersoSubState() ==
- PersoSubState.PERSOSUBSTATE_SIM_NETWORK) {
- setExternalState(State.NETWORK_LOCKED);
- }
- // Otherwise don't change external SIM state.
- break;
- case APPSTATE_READY:
- setExternalState(State.READY);
- break;
- }
- }
-
- private void registerUiccCardEvents() {
- if (mUiccApplication != null) {
- mUiccApplication.registerForReady(this, EVENT_APP_READY, null);
- }
- if (mIccRecords != null) {
- mIccRecords.registerForImsiReady(this, EVENT_IMSI_READY, null);
- mIccRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
- mIccRecords.registerForLockedRecordsLoaded(this, EVENT_ICC_LOCKED, null);
- mIccRecords.registerForNetworkLockedRecordsLoaded(this, EVENT_NETWORK_LOCKED, null);
- mIccRecords.registerForRecordsEvents(this, EVENT_ICC_RECORD_EVENTS, null);
- }
- }
-
- private void unregisterUiccCardEvents() {
- if (mUiccCard != null) mUiccCard.unregisterForCarrierPrivilegeRulesLoaded(this);
- if (mUiccApplication != null) {
- mUiccApplication.unregisterForReady(this);
- }
- if (mIccRecords != null) {
- mIccRecords.unregisterForImsiReady(this);
- mIccRecords.unregisterForRecordsLoaded(this);
- mIccRecords.unregisterForLockedRecordsLoaded(this);
- mIccRecords.unregisterForNetworkLockedRecordsLoaded(this);
- mIccRecords.unregisterForRecordsEvents(this);
- }
- }
-
- private void broadcastInternalIccStateChangedIntent(String value, String reason) {
- synchronized (mLock) {
- if (mPhoneId == null) {
- loge("broadcastInternalIccStateChangedIntent: Card Index is not set; Return!!");
- return;
- }
-
- Intent intent = new Intent(ACTION_INTERNAL_SIM_STATE_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
- 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("broadcastInternalIccStateChangedIntent: Sending intent "
- + "ACTION_INTERNAL_SIM_STATE_CHANGED value = " + value
- + " for mPhoneId : " + mPhoneId);
- ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
- }
- }
-
- private void setExternalState(State newState, boolean override) {
- synchronized (mLock) {
- if (mPhoneId == null || !SubscriptionManager.isValidSlotIndex(mPhoneId)) {
- loge("setExternalState: mPhoneId=" + mPhoneId + " is invalid; Return!!");
- return;
- }
-
- if (!override && newState == mExternalState) {
- log("setExternalState: !override and newstate unchanged from " + newState);
- return;
- }
- mExternalState = newState;
- log("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState);
- mTelephonyManager.setSimStateForPhone(mPhoneId, getState().toString());
-
- broadcastInternalIccStateChangedIntent(getIccStateIntentString(mExternalState),
- getIccStateReason(mExternalState));
- }
- }
-
- private void processLockedState() {
- synchronized (mLock) {
- if (mUiccApplication == null) {
- //Don't need to do anything if non-existent application is locked
- return;
- }
- PinState pin1State = mUiccApplication.getPin1State();
- if (pin1State == PinState.PINSTATE_ENABLED_PERM_BLOCKED) {
- setExternalState(State.PERM_DISABLED);
- return;
- }
-
- AppState appState = mUiccApplication.getState();
- switch (appState) {
- case APPSTATE_PIN:
- setExternalState(State.PIN_REQUIRED);
- break;
- case APPSTATE_PUK:
- setExternalState(State.PUK_REQUIRED);
- break;
- case APPSTATE_DETECTED:
- case APPSTATE_READY:
- case APPSTATE_SUBSCRIPTION_PERSO:
- case APPSTATE_UNKNOWN:
- // Neither required
- break;
- }
- }
- }
-
- private void setExternalState(State newState) {
- setExternalState(newState, false);
- }
-
- public boolean getIccRecordsLoaded() {
- synchronized (mLock) {
- if (mIccRecords != null) {
- return mIccRecords.getRecordsLoaded();
- }
- return false;
- }
- }
-
- private String getIccStateIntentString(State state) {
- switch (state) {
- case ABSENT: return IccCardConstants.INTENT_VALUE_ICC_ABSENT;
- case PIN_REQUIRED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
- case PUK_REQUIRED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
- case NETWORK_LOCKED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
- case READY: return IccCardConstants.INTENT_VALUE_ICC_READY;
- case NOT_READY: return IccCardConstants.INTENT_VALUE_ICC_NOT_READY;
- case PERM_DISABLED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
- case CARD_IO_ERROR: return IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR;
- case CARD_RESTRICTED: return IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED;
- case LOADED: return IccCardConstants.INTENT_VALUE_ICC_LOADED;
- default: return IccCardConstants.INTENT_VALUE_ICC_UNKNOWN;
- }
- }
-
- /**
- * Locked state have a reason (PIN, PUK, NETWORK, PERM_DISABLED, CARD_IO_ERROR)
- * @return reason
- */
- private String getIccStateReason(State state) {
- switch (state) {
- case PIN_REQUIRED: return IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN;
- case PUK_REQUIRED: return IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK;
- case NETWORK_LOCKED: return IccCardConstants.INTENT_VALUE_LOCKED_NETWORK;
- case PERM_DISABLED: return IccCardConstants.INTENT_VALUE_ABSENT_ON_PERM_DISABLED;
- case CARD_IO_ERROR: return IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR;
- case CARD_RESTRICTED: return IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED;
- default: return null;
- }
- }
-
- /* IccCard interface implementation */
- @Override
- public State getState() {
- synchronized (mLock) {
- return mExternalState;
- }
- }
-
- @Override
- public IccRecords getIccRecords() {
- synchronized (mLock) {
- return mIccRecords;
- }
- }
-
- /**
- * Notifies handler of any transition into State.NETWORK_LOCKED
- */
- @Override
- public void registerForNetworkLocked(Handler h, int what, Object obj) {
- synchronized (mLock) {
- Registrant r = new Registrant (h, what, obj);
-
- mNetworkLockedRegistrants.add(r);
-
- if (getState() == State.NETWORK_LOCKED) {
- r.notifyRegistrant();
- }
- }
- }
-
- @Override
- public void unregisterForNetworkLocked(Handler h) {
- synchronized (mLock) {
- mNetworkLockedRegistrants.remove(h);
- }
- }
-
- @Override
- public void supplyPin(String pin, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.supplyPin(pin, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public void supplyPuk(String puk, String newPin, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.supplyPuk(puk, newPin, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public void supplyPin2(String pin2, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.supplyPin2(pin2, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public void supplyPuk2(String puk2, String newPin2, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.supplyPuk2(puk2, newPin2, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public void supplyNetworkDepersonalization(String pin, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.supplyNetworkDepersonalization(pin, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("CommandsInterface is not set.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public boolean getIccLockEnabled() {
- synchronized (mLock) {
- /* defaults to false, if ICC is absent/deactivated */
- Boolean retValue = mUiccApplication != null ?
- mUiccApplication.getIccLockEnabled() : false;
- return retValue;
- }
- }
-
- @Override
- public boolean getIccFdnEnabled() {
- synchronized (mLock) {
- Boolean retValue = mUiccApplication != null ?
- mUiccApplication.getIccFdnEnabled() : false;
- return retValue;
- }
- }
-
- public boolean getIccPin2Blocked() {
- /* defaults to disabled */
- Boolean retValue = mUiccApplication != null ? mUiccApplication.getIccPin2Blocked() : false;
- return retValue;
- }
-
- public boolean getIccPuk2Blocked() {
- /* defaults to disabled */
- Boolean retValue = mUiccApplication != null ? mUiccApplication.getIccPuk2Blocked() : false;
- return retValue;
- }
-
- @Override
- public void setIccLockEnabled(boolean enabled, String password, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.setIccLockEnabled(enabled, password, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public void setIccFdnEnabled(boolean enabled, String password, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.setIccFdnEnabled(enabled, password, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public void changeIccLockPassword(String oldPassword, String newPassword, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.changeIccLockPassword(oldPassword, newPassword, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public void changeIccFdnPassword(String oldPassword, String newPassword, Message onComplete) {
- synchronized (mLock) {
- if (mUiccApplication != null) {
- mUiccApplication.changeIccFdnPassword(oldPassword, newPassword, onComplete);
- } else if (onComplete != null) {
- Exception e = new RuntimeException("ICC card is absent.");
- AsyncResult.forMessage(onComplete).exception = e;
- onComplete.sendToTarget();
- return;
- }
- }
- }
-
- @Override
- public String getServiceProviderName() {
- synchronized (mLock) {
- if (mIccRecords != null) {
- return mIccRecords.getServiceProviderName();
- }
- return null;
- }
- }
-
- @Override
- public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) {
- synchronized (mLock) {
- Boolean retValue = mUiccCard != null ? mUiccCard.isApplicationOnIcc(type) : false;
- return retValue;
- }
- }
-
- @Override
- public boolean hasIccCard() {
- synchronized (mLock) {
- if (mUiccCard != null && mUiccCard.getCardState() != CardState.CARDSTATE_ABSENT) {
- return true;
- }
- return false;
- }
- }
-
- private void log(String s) {
- Rlog.d(LOG_TAG, s);
- }
-
- private void loge(String msg) {
- Rlog.e(LOG_TAG, msg);
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("IccCardProxy: " + this);
- pw.println(" mContext=" + mContext);
- pw.println(" mCi=" + mCi);
- pw.println(" mNetworkLockedRegistrants: size=" + mNetworkLockedRegistrants.size());
- for (int i = 0; i < mNetworkLockedRegistrants.size(); i++) {
- pw.println(" mNetworkLockedRegistrants[" + i + "]="
- + ((Registrant)mNetworkLockedRegistrants.get(i)).getHandler());
- }
- pw.println(" mCurrentAppType=" + mCurrentAppType);
- pw.println(" mUiccController=" + mUiccController);
- pw.println(" mUiccCard=" + mUiccCard);
- pw.println(" mUiccApplication=" + mUiccApplication);
- pw.println(" mIccRecords=" + mIccRecords);
- pw.println(" mRadioState=" + mRadioState);
- pw.println(" mInitialized=" + mInitialized);
- pw.println(" mExternalState=" + mExternalState);
-
- pw.flush();
- }
-}
diff --git a/src/java/com/android/internal/telephony/uicc/IccIoResult.java b/src/java/com/android/internal/telephony/uicc/IccIoResult.java
index 4a35e14..b1b6e75 100644
--- a/src/java/com/android/internal/telephony/uicc/IccIoResult.java
+++ b/src/java/com/android/internal/telephony/uicc/IccIoResult.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.uicc;
+import android.os.Build;
/**
* {@hide}
@@ -154,6 +155,12 @@
+ "CHV blocked"
+ "UNBLOCK CHV blocked";
case 0x50: return "increase cannot be performed, Max value reached";
+ // The definition for these status codes can be found in TS 31.102 7.3.1
+ case 0x62: return "authentication error, application specific";
+ case 0x64: return "authentication error, security context not supported";
+ case 0x65: return "key freshness failure";
+ case 0x66: return "authentication error, no memory space available";
+ case 0x67: return "authentication error, no memory space available in EF_MUK";
}
break;
case 0x9E: return null; // success
@@ -181,7 +188,9 @@
@Override
public String toString() {
return "IccIoResult sw1:0x" + Integer.toHexString(sw1) + " sw2:0x"
- + Integer.toHexString(sw2) + ((!success()) ? " Error: " + getErrorString() : "");
+ + Integer.toHexString(sw2) + " Payload: "
+ + ((Build.IS_DEBUGGABLE && Build.IS_ENG) ? payload : "*******")
+ + ((!success()) ? " Error: " + getErrorString() : "");
}
/**
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index a298548..6a630d6 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -80,7 +80,6 @@
protected int mLockedRecordsReqReason = LOCKED_RECORDS_REQ_REASON_NONE;
protected String mIccId; // Includes only decimals (no hex)
- protected String mFakeIccId;
protected String mFullIccId; // Includes hex characters in ICCID
protected String mMsisdn = null; // My mobile number
@@ -93,22 +92,17 @@
protected String mNewVoiceMailTag = null;
protected boolean mIsVoiceMailFixed = false;
protected String mImsi;
- protected String mFakeImsi;
private IccIoResult auth_rsp;
protected int mMncLength = UNINITIALIZED;
protected int mMailboxIndex = 0; // 0 is no mailbox dailing number associated
private String mSpn;
- private String mFakeSpn;
protected String mGid1;
- protected String mFakeGid1;
protected String mGid2;
- protected String mFakeGid2;
protected String mPnnHomeName;
- protected String mFakePnnHomeName;
protected String mPrefLang;
@@ -170,7 +164,8 @@
+ " recordsRequested=" + mRecordsRequested
+ " lockedRecordsReqReason=" + mLockedRecordsReqReason
+ " iccid=" + iccIdToPrint
- + (mCarrierTestOverride.isInTestMode() ? "mFakeIccid=" + mFakeIccId : "")
+ + (mCarrierTestOverride.isInTestMode() ? "mFakeIccid="
+ + mCarrierTestOverride.getFakeIccid() : "")
+ " msisdnTag=" + mMsisdnTag
+ " voiceMailNum=" + Rlog.pii(VDBG, mVoiceMailNum)
+ " voiceMailTag=" + mVoiceMailTag
@@ -179,11 +174,13 @@
+ " isVoiceMailFixed=" + mIsVoiceMailFixed
+ " mImsi=" + ((mImsi != null) ?
mImsi.substring(0, 6) + Rlog.pii(VDBG, mImsi.substring(6)) : "null")
- + (mCarrierTestOverride.isInTestMode() ? " mFakeImsi=" + mFakeImsi : "")
+ + (mCarrierTestOverride.isInTestMode() ? " mFakeImsi="
+ + mCarrierTestOverride.getFakeIMSI() : "")
+ " mncLength=" + mMncLength
+ " mailboxIndex=" + mMailboxIndex
+ " spn=" + mSpn
- + (mCarrierTestOverride.isInTestMode() ? " mFakeSpn=" + mFakeSpn : "");
+ + (mCarrierTestOverride.isInTestMode() ? " mFakeSpn="
+ + mCarrierTestOverride.getFakeSpn() : "");
}
/**
@@ -212,29 +209,18 @@
Context.TELEPHONY_SERVICE);
mCarrierTestOverride = new CarrierTestOverride();
-
- if (mCarrierTestOverride.isInTestMode()) {
- mFakeImsi = mCarrierTestOverride.getFakeIMSI();
- log("load mFakeImsi: " + mFakeImsi);
-
- mFakeGid1 = mCarrierTestOverride.getFakeGid1();
- log("load mFakeGid1: " + mFakeGid1);
-
- mFakeGid2 = mCarrierTestOverride.getFakeGid2();
- log("load mFakeGid2: " + mFakeGid2);
-
- mFakeSpn = mCarrierTestOverride.getFakeSpn();
- log("load mFakeSpn: " + mFakeSpn);
-
- mFakePnnHomeName = mCarrierTestOverride.getFakePnnHomeName();
- log("load mFakePnnHomeName: " + mFakePnnHomeName);
-
- mFakeIccId = mCarrierTestOverride.getFakeIccid();
- log("load mFakeIccId: " + mFakeIccId);
- }
mCi.registerForIccRefresh(this, EVENT_REFRESH, null);
}
+ // Override IccRecords for testing
+ public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
+ String gid2, String pnn, String spn) {
+ mCarrierTestOverride.override(mccmnc, imsi, iccid, gid1, gid2, pnn, spn);
+ mTelephonyManager.setSimOperatorNameForPhone(mParentApp.getPhoneId(), spn);
+ mTelephonyManager.setSimOperatorNumericForPhone(mParentApp.getPhoneId(), mccmnc);
+ mRecordsLoadedRegistrants.notifyRegistrants();
+ }
+
/**
* Call when the IccRecords object is no longer going to be used.
*/
@@ -295,8 +281,8 @@
* @return ICC ID without hex digits
*/
public String getIccId() {
- if (mCarrierTestOverride.isInTestMode() && mFakeIccId != null) {
- return mFakeIccId;
+ if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeIccid() != null) {
+ return mCarrierTestOverride.getFakeIccid();
} else {
return mIccId;
}
@@ -442,8 +428,8 @@
* @return null if SIM is not yet ready or unavailable
*/
public String getIMSI() {
- if (mCarrierTestOverride.isInTestMode() && mFakeImsi != null) {
- return mFakeImsi;
+ if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeIMSI() != null) {
+ return mCarrierTestOverride.getFakeIMSI();
} else {
return mImsi;
}
@@ -477,8 +463,8 @@
* @return null if SIM is not yet ready
*/
public String getGid1() {
- if (mCarrierTestOverride.isInTestMode() && mFakeGid1 != null) {
- return mFakeGid1;
+ if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeGid1() != null) {
+ return mCarrierTestOverride.getFakeGid1();
} else {
return mGid1;
}
@@ -489,8 +475,8 @@
* @return null if SIM is not yet ready
*/
public String getGid2() {
- if (mCarrierTestOverride.isInTestMode() && mFakeGid2 != null) {
- return mFakeGid2;
+ if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeGid2() != null) {
+ return mCarrierTestOverride.getFakeGid2();
} else {
return mGid2;
}
@@ -501,8 +487,9 @@
* @return null if SIM is not yet ready
*/
public String getPnnHomeName() {
- if (mCarrierTestOverride.isInTestMode() && mFakePnnHomeName != null) {
- return mFakePnnHomeName;
+ if (mCarrierTestOverride.isInTestMode()
+ && mCarrierTestOverride.getFakePnnHomeName() != null) {
+ return mCarrierTestOverride.getFakePnnHomeName();
} else {
return mPnnHomeName;
}
@@ -531,8 +518,8 @@
* @return null if SIM is not yet ready or no RUIM entry
*/
public String getServiceProviderName() {
- if (mCarrierTestOverride.isInTestMode() && mFakeSpn != null) {
- return mFakeSpn;
+ if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeSpn() != null) {
+ return mCarrierTestOverride.getFakeSpn();
}
String providerName = mSpn;
@@ -981,13 +968,13 @@
pw.println(" mImsi=" + ((mImsi != null) ?
mImsi.substring(0, 6) + Rlog.pii(VDBG, mImsi.substring(6)) : "null"));
if (mCarrierTestOverride.isInTestMode()) {
- pw.println(" mFakeImsi=" + mFakeImsi);
+ pw.println(" mFakeImsi=" + mCarrierTestOverride.getFakeIMSI());
}
pw.println(" mMncLength=" + mMncLength);
pw.println(" mMailboxIndex=" + mMailboxIndex);
pw.println(" mSpn=" + mSpn);
if (mCarrierTestOverride.isInTestMode()) {
- pw.println(" mFakeSpn=" + mFakeSpn);
+ pw.println(" mFakeSpn=" + mCarrierTestOverride.getFakeSpn());
}
pw.flush();
}
diff --git a/src/java/com/android/internal/telephony/uicc/RuimRecords.java b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
index 88ac071..fd4debf 100644
--- a/src/java/com/android/internal/telephony/uicc/RuimRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
@@ -645,7 +645,7 @@
if (operatorNumeric != null) {
if (operatorNumeric.length() <= 6) {
log("update mccmnc=" + operatorNumeric);
- MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false);
+ MccTable.updateMccMncConfiguration(mContext, operatorNumeric);
}
}
} else {
@@ -794,10 +794,8 @@
if (!TextUtils.isEmpty(imsi)) {
log("onAllRecordsLoaded set mcc imsi=" + (VDBG ? ("=" + imsi) : ""));
- mTelephonyManager.setSimCountryIsoForPhone(
- mParentApp.getPhoneId(),
- MccTable.countryCodeForMcc(
- Integer.parseInt(imsi.substring(0, 3))));
+ mTelephonyManager.setSimCountryIsoForPhone(mParentApp.getPhoneId(),
+ MccTable.countryCodeForMcc(imsi.substring(0, 3)));
} else {
log("onAllRecordsLoaded empty imsi skipping setting mcc");
}
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index 67c0900..ca5e562 100755
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -16,10 +16,7 @@
package com.android.internal.telephony.uicc;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.AsyncResult;
import android.os.Message;
@@ -30,7 +27,6 @@
import android.telephony.ServiceState;
import android.telephony.SmsMessage;
import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import com.android.internal.telephony.CommandsInterface;
@@ -172,7 +168,6 @@
// TODO: Possibly move these to IccRecords.java
private static final int SYSTEM_EVENT_BASE = 0x100;
- private static final int EVENT_CARRIER_CONFIG_CHANGED = 1 + SYSTEM_EVENT_BASE;
private static final int EVENT_APP_LOCKED = 2 + SYSTEM_EVENT_BASE;
private static final int EVENT_APP_NETWORK_LOCKED = 3 + SYSTEM_EVENT_BASE;
@@ -225,21 +220,8 @@
mParentApp.registerForLocked(this, EVENT_APP_LOCKED, null);
mParentApp.registerForNetworkLocked(this, EVENT_APP_NETWORK_LOCKED, null);
if (DBG) log("SIMRecords X ctor this=" + this);
-
- IntentFilter intentfilter = new IntentFilter();
- intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- c.registerReceiver(mReceiver, intentfilter);
}
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- sendMessage(obtainMessage(EVENT_CARRIER_CONFIG_CHANGED));
- }
- }
- };
-
@Override
public void dispose() {
if (DBG) log("Disposing SIMRecords this=" + this);
@@ -248,7 +230,6 @@
mParentApp.unregisterForReady(this);
mParentApp.unregisterForLocked(this);
mParentApp.unregisterForNetworkLocked(this);
- mContext.unregisterReceiver(mReceiver);
resetRecords();
super.dispose();
}
@@ -732,7 +713,7 @@
// finally have both the imsi and the mncLength and
// can parse the imsi properly
MccTable.updateMccMncConfiguration(mContext,
- imsi.substring(0, 3 + mMncLength), false);
+ imsi.substring(0, 3 + mMncLength));
}
mImsiReadyRegistrants.notifyRegistrants();
break;
@@ -1009,7 +990,7 @@
// the imsi properly
log("update mccmnc=" + imsi.substring(0, 3 + mMncLength));
MccTable.updateMccMncConfiguration(mContext,
- imsi.substring(0, 3 + mMncLength), false);
+ imsi.substring(0, 3 + mMncLength));
}
}
break;
@@ -1365,10 +1346,6 @@
}
break;
- case EVENT_CARRIER_CONFIG_CHANGED:
- handleCarrierNameOverride();
- break;
-
default:
super.handleMessage(msg); // IccRecords handles generic record load responses
}
@@ -1594,8 +1571,7 @@
if (!TextUtils.isEmpty(imsi) && imsi.length() >= 3) {
log("onAllRecordsLoaded set mcc imsi" + (VDBG ? ("=" + imsi) : ""));
mTelephonyManager.setSimCountryIsoForPhone(
- mParentApp.getPhoneId(), MccTable.countryCodeForMcc(
- Integer.parseInt(imsi.substring(0, 3))));
+ mParentApp.getPhoneId(), MccTable.countryCodeForMcc(imsi.substring(0, 3)));
} else {
log("onAllRecordsLoaded empty imsi skipping setting mcc");
}
@@ -1607,63 +1583,6 @@
//***** Private methods
- /**
- * Override the carrier name with either carrier config or SPN
- * if an override is provided.
- */
- private void handleCarrierNameOverride() {
- final int phoneId = mParentApp.getPhoneId();
- SubscriptionController subCon = SubscriptionController.getInstance();
- final int subId = subCon.getSubIdUsingPhoneId(phoneId);
- if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- loge("subId not valid for Phone " + phoneId);
- return;
- }
-
- CarrierConfigManager configLoader = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configLoader == null) {
- loge("Failed to load a Carrier Config");
- return;
- }
-
- PersistableBundle config = configLoader.getConfigForSubId(subId);
- boolean preferCcName = config.getBoolean(
- CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
- String ccName = config.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
- // If carrier config is priority, use it regardless - the preference
- // and the name were both set by the carrier, so this is safe;
- // otherwise, if the SPN is priority but we don't have one *and* we have
- // a name in carrier config, use the carrier config name as a backup.
- if (preferCcName || (TextUtils.isEmpty(getServiceProviderName())
- && !TextUtils.isEmpty(ccName))) {
- setServiceProviderName(ccName);
- mTelephonyManager.setSimOperatorNameForPhone(phoneId, ccName);
- }
-
- updateCarrierNameForSubscription(subCon, subId);
- }
-
- private void updateCarrierNameForSubscription(SubscriptionController subCon, int subId) {
- /* update display name with carrier override */
- SubscriptionInfo subInfo = subCon.getActiveSubscriptionInfo(
- subId, mContext.getOpPackageName());
-
- if (subInfo == null || subInfo.getNameSource()
- == SubscriptionManager.NAME_SOURCE_USER_INPUT) {
- // either way, there is no subinfo to update
- return;
- }
-
- CharSequence oldSubName = subInfo.getDisplayName();
- String newCarrierName = mTelephonyManager.getSimOperatorName(subId);
-
- if (!TextUtils.isEmpty(newCarrierName) && !newCarrierName.equals(oldSubName)) {
- log("sim name[" + mParentApp.getPhoneId() + "] = " + newCarrierName);
- subCon.setDisplayName(newCarrierName, subId);
- }
- }
-
private void setVoiceMailByCountry (String spn) {
if (mVmConfig.containsCarrier(spn)) {
mIsVoiceMailFixed = true;
@@ -2214,15 +2133,15 @@
pw.println(" mUsimServiceTable=" + mUsimServiceTable);
pw.println(" mGid1=" + mGid1);
if (mCarrierTestOverride.isInTestMode()) {
- pw.println(" mFakeGid1=" + mFakeGid1);
+ pw.println(" mFakeGid1=" + mCarrierTestOverride.getFakeGid1());
}
pw.println(" mGid2=" + mGid2);
if (mCarrierTestOverride.isInTestMode()) {
- pw.println(" mFakeGid2=" + mFakeGid2);
+ pw.println(" mFakeGid2=" + mCarrierTestOverride.getFakeGid2());
}
pw.println(" mPnnHomeName=" + mPnnHomeName);
if (mCarrierTestOverride.isInTestMode()) {
- pw.println(" mFakePnnHomeName=" + mFakePnnHomeName);
+ pw.println(" mFakePnnHomeName=" + mCarrierTestOverride.getFakePnnHomeName());
}
pw.println(" mPlmnActRecords[]=" + Arrays.toString(mPlmnActRecords));
pw.println(" mOplmnActRecords[]=" + Arrays.toString(mOplmnActRecords));
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCard.java b/src/java/com/android/internal/telephony/uicc/UiccCard.java
index a664a08..361f70b 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCard.java
@@ -25,7 +25,6 @@
import android.os.Message;
import android.telephony.Rlog;
import android.telephony.TelephonyManager;
-import android.util.LocalLog;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.TelephonyComponentFactory;
@@ -47,22 +46,22 @@
public static final String EXTRA_ICC_CARD_ADDED =
"com.android.internal.telephony.uicc.ICC_CARD_ADDED";
- private static final String OPERATOR_BRAND_OVERRIDE_PREFIX = "operator_branding_";
-
- private final Object mLock = new Object();
+ // The lock object is created by UiccSlot that owns this UiccCard - this is to share the lock
+ // between UiccSlot, UiccCard and UiccProfile for now.
+ private final Object mLock;
private CardState mCardState;
private String mIccid;
protected String mCardId;
private UiccProfile mUiccProfile;
private Context mContext;
private CommandsInterface mCi;
- private static final LocalLog mLocalLog = new LocalLog(100);
private final int mPhoneId;
- public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId) {
+ public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock) {
if (DBG) log("Creating");
mCardState = ics.mCardState;
mPhoneId = phoneId;
+ mLock = lock;
update(c, ci, ics);
}
@@ -87,7 +86,7 @@
if (mCardState != CardState.CARDSTATE_ABSENT) {
if (mUiccProfile == null) {
mUiccProfile = TelephonyComponentFactory.getInstance().makeUiccProfile(
- mContext, mCi, ics, mPhoneId, this);
+ mContext, mCi, ics, mPhoneId, this, mLock);
} else {
mUiccProfile.update(mContext, mCi, ics);
}
@@ -231,13 +230,13 @@
*
* A null aid implies a card level reset - all applications must be reset.
*
- * @deprecated Please use {@link UiccProfile#resetAppWithAid(String)} instead.
+ * @deprecated Please use {@link UiccProfile#resetAppWithAid(String, boolean)} instead.
*/
@Deprecated
- public boolean resetAppWithAid(String aid) {
+ public boolean resetAppWithAid(String aid, boolean reset) {
synchronized (mLock) {
if (mUiccProfile != null) {
- return mUiccProfile.resetAppWithAid(aid);
+ return mUiccProfile.resetAppWithAid(aid, reset);
} else {
return false;
}
@@ -506,10 +505,6 @@
Rlog.e(LOG_TAG, msg);
}
- private void loglocal(String msg) {
- if (DBG) mLocalLog.log(msg);
- }
-
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("UiccCard:");
pw.println(" mCi=" + mCi);
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
index 918e635..7a361e3 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
@@ -73,6 +73,9 @@
private boolean mDesiredFdnEnabled;
private boolean mIccLockEnabled;
private boolean mDesiredPinLocked;
+
+ // App state will be ignored while deciding whether the card is ready or not.
+ private boolean mIgnoreApp;
private boolean mIccFdnAvailable = true; // Default is enabled.
private CommandsInterface mCi;
@@ -101,6 +104,7 @@
mPin1Replaced = (as.pin1_replaced != 0);
mPin1State = as.pin1;
mPin2State = as.pin2;
+ mIgnoreApp = false;
mContext = c;
mCi = ci;
@@ -874,6 +878,14 @@
return mUiccProfile.getPhoneId();
}
+ public boolean isAppIgnored() {
+ return mIgnoreApp;
+ }
+
+ public void setAppIgnoreState(boolean ignore) {
+ mIgnoreApp = ignore;
+ }
+
protected UiccProfile getUiccProfile() {
return mUiccProfile;
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 27fe2f7..783d82f 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
@@ -27,20 +28,22 @@
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.TelephonyManager;
-import android.text.format.Time;
+import android.util.LocalLog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.RadioConfig;
+import com.android.internal.telephony.SubscriptionInfoUpdater;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.Set;
/**
@@ -113,6 +116,7 @@
private UiccSlot[] mUiccSlots;
private int[] mPhoneIdToSlotId;
private boolean mIsSlotStatusSupported = true;
+ private boolean mIsCdmaSupported = true;
private static final Object mLock = new Object();
private static UiccController mInstance;
@@ -125,14 +129,13 @@
private UiccStateChangedLauncher mLauncher;
private RadioConfig mRadioConfig;
- // Logging for dumpsys. Useful in cases when the cards run into errors.
- private static final int MAX_PROACTIVE_COMMANDS_TO_LOG = 20;
- private LinkedList<String> mCardLogs = new LinkedList<String>();
+ // LocalLog buffer to hold important SIM related events for debugging
+ static LocalLog sLocalLog = new LocalLog(100);
public static UiccController make(Context c, CommandsInterface[] ci) {
synchronized (mLock) {
if (mInstance != null) {
- throw new RuntimeException("MSimUiccController.make() should only be called once");
+ throw new RuntimeException("UiccController.make() should only be called once");
}
mInstance = new UiccController(c, ci);
return mInstance;
@@ -143,9 +146,11 @@
if (DBG) log("Creating UiccController");
mContext = c;
mCis = ci;
- if (VDBG) {
- log("config_num_physical_slots = " + c.getResources().getInteger(
- com.android.internal.R.integer.config_num_physical_slots));
+ if (DBG) {
+ String logStr = "config_num_physical_slots = " + c.getResources().getInteger(
+ com.android.internal.R.integer.config_num_physical_slots);
+ log(logStr);
+ sLocalLog.log(logStr);
}
int numPhysicalSlots = c.getResources().getInteger(
com.android.internal.R.integer.config_num_physical_slots);
@@ -179,6 +184,11 @@
}
mLauncher = new UiccStateChangedLauncher(c, this);
+
+ // set mIsCdmaSupported based on PackageManager.FEATURE_TELEPHONY_CDMA
+ PackageManager packageManager = c.getPackageManager();
+ mIsCdmaSupported =
+ packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA);
}
private int getSlotIdFromPhoneId(int phoneId) {
@@ -371,6 +381,8 @@
return;
}
+ sLocalLog.log("handleMessage: Received " + msg.what + " for phoneId " + phoneId);
+
AsyncResult ar = (AsyncResult)msg.obj;
switch (msg.what) {
case EVENT_ICC_STATUS_CHANGED:
@@ -459,6 +471,15 @@
}
}
+ static void updateInternalIccState(String value, String reason, int phoneId) {
+ SubscriptionInfoUpdater subInfoUpdator = PhoneFactory.getSubscriptionInfoUpdater();
+ if (subInfoUpdator != null) {
+ subInfoUpdator.updateInternalIccState(value, reason, phoneId);
+ } else {
+ Rlog.e(LOG_TAG, "subInfoUpdate is null.");
+ }
+ }
+
private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {
if (ar.exception != null) {
Rlog.e(LOG_TAG,"Error getting ICC status. "
@@ -473,12 +494,14 @@
IccCardStatus status = (IccCardStatus)ar.result;
+ sLocalLog.log("onGetIccCardStatusDone: phoneId " + index + " IccCardStatus: " + status);
+
int slotId = status.physicalSlotIndex;
if (VDBG) log("onGetIccCardStatusDone: phoneId " + index + " physicalSlotIndex " + slotId);
if (slotId == INVALID_SLOT_ID) {
slotId = index;
- mPhoneIdToSlotId[index] = slotId;
}
+ mPhoneIdToSlotId[index] = slotId;
if (VDBG) logPhoneIdToSlotIdMapping();
@@ -503,15 +526,20 @@
}
Throwable e = ar.exception;
if (e != null) {
+ String logStr;
if (!(e instanceof CommandException) || ((CommandException) e).getCommandError()
!= CommandException.Error.REQUEST_NOT_SUPPORTED) {
// this is not expected; there should be no exception other than
// REQUEST_NOT_SUPPORTED
- Rlog.e(LOG_TAG, "Unexpected error getting slot status: " + ar.exception);
+ logStr = "Unexpected error getting slot status: " + ar.exception;
+ Rlog.e(LOG_TAG, logStr);
+ sLocalLog.log(logStr);
} else {
// REQUEST_NOT_SUPPORTED
- log("onGetSlotStatusDone: request not supported; marking mIsSlotStatusSupported "
- + "to false");
+ logStr = "onGetSlotStatusDone: request not supported; marking "
+ + "mIsSlotStatusSupported to false";
+ log(logStr);
+ sLocalLog.log(logStr);
mIsSlotStatusSupported = false;
}
return;
@@ -524,6 +552,8 @@
return;
}
+ sLastSlotStatus = status;
+
int numActiveSlots = 0;
for (int i = 0; i < status.size(); i++) {
IccSlotStatus iss = status.get(i);
@@ -568,6 +598,8 @@
// broadcast slot status changed
Intent intent = new Intent(TelephonyManager.ACTION_SIM_SLOT_STATUS_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mContext.sendBroadcast(intent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
}
@@ -589,22 +621,10 @@
log(" phoneId " + i + " slotId " + mPhoneIdToSlotId[i]);
}
}
- /**
- * Slots are initialized when none of them are null.
- * todo: is this even needed?
- */
- private synchronized boolean areSlotsInitialized() {
- for (UiccSlot slot : mUiccSlots) {
- if (slot == null) {
- return false;
- }
- }
- return true;
- }
private void onSimRefresh(AsyncResult ar, Integer index) {
if (ar.exception != null) {
- Rlog.e(LOG_TAG, "Sim REFRESH with exception: " + ar.exception);
+ Rlog.e(LOG_TAG, "onSimRefresh: Sim REFRESH with exception: " + ar.exception);
return;
}
@@ -615,6 +635,7 @@
IccRefreshResponse resp = (IccRefreshResponse) ar.result;
log("onSimRefresh: " + resp);
+ sLocalLog.log("onSimRefresh: " + resp);
if (resp == null) {
Rlog.e(LOG_TAG, "onSimRefresh: received without input");
@@ -627,17 +648,19 @@
return;
}
- log("Handling refresh: " + resp);
boolean changed = false;
switch(resp.refreshResult) {
+ // Reset the required apps when we know about the refresh so that
+ // anyone interested does not get stale state.
case IccRefreshResponse.REFRESH_RESULT_RESET:
+ changed = uiccCard.resetAppWithAid(resp.aid, true /* reset */);
+ break;
case IccRefreshResponse.REFRESH_RESULT_INIT:
- // Reset the required apps when we know about the refresh so that
- // anyone interested does not get stale state.
- changed = uiccCard.resetAppWithAid(resp.aid);
- break;
+ // don't dispose CatService on SIM REFRESH of type INIT
+ changed = uiccCard.resetAppWithAid(resp.aid, false /* initialize */);
+ break;
default:
- return;
+ return;
}
if (changed && resp.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET) {
@@ -658,6 +681,10 @@
mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
}
+ public boolean isCdmaSupported() {
+ return mIsCdmaSupported;
+ }
+
private boolean isValidPhoneIndex(int index) {
return (index >= 0 && index < TelephonyManager.getDefault().getPhoneCount());
}
@@ -670,14 +697,8 @@
Rlog.d(LOG_TAG, string);
}
- // TODO: This is hacky. We need a better way of saving the logs.
public void addCardLog(String data) {
- Time t = new Time();
- t.setToNow();
- mCardLogs.addLast(t.format("%m-%d %H:%M:%S") + " " + data);
- if (mCardLogs.size() > MAX_PROACTIVE_COMMANDS_TO_LOG) {
- mCardLogs.removeFirst();
- }
+ sLocalLog.log(data);
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -691,6 +712,7 @@
}
pw.println();
pw.flush();
+ pw.println(" mIsCdmaSupported=" + mIsCdmaSupported);
pw.println(" mUiccSlots: size=" + mUiccSlots.length);
for (int i = 0; i < mUiccSlots.length; i++) {
if (mUiccSlots[i] == null) {
@@ -700,9 +722,7 @@
mUiccSlots[i].dump(fd, pw, args);
}
}
- pw.println("mCardLogs: ");
- for (int i = 0; i < mCardLogs.size(); ++i) {
- pw.println(" " + mCardLogs.get(i));
- }
+ pw.println(" sLocalLog= ");
+ sLocalLog.dump(fd, pw, args);
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index 3c23d66..f7aeaa1 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -16,12 +16,13 @@
package com.android.internal.telephony.uicc;
-import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.usage.UsageStatsManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -35,7 +36,6 @@
import android.os.PersistableBundle;
import android.os.Registrant;
import android.os.RegistrantList;
-import android.os.UserHandle;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
@@ -45,10 +45,10 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.util.LocalLog;
import android.view.WindowManager;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.IccCardConstants;
@@ -81,15 +81,16 @@
*
* {@hide}
*/
-public class UiccProfile extends Handler implements IccCard {
+public class UiccProfile extends IccCard {
protected static final String LOG_TAG = "UiccProfile";
protected static final boolean DBG = true;
private static final boolean VDBG = false; //STOPSHIP if true
- private static final boolean ICC_CARD_PROXY_REMOVED = true;
private static final String OPERATOR_BRAND_OVERRIDE_PREFIX = "operator_branding_";
- private final Object mLock = new Object();
+ // The lock object is created by UiccSlot that owns the UiccCard that owns this UiccProfile.
+ // This is to share the lock between UiccSlot, UiccCard and UiccProfile for now.
+ private final Object mLock;
private PinState mUniversalPinState;
private int mGsmUmtsSubscriptionAppIndex;
private int mCdmaSubscriptionAppIndex;
@@ -98,34 +99,31 @@
new UiccCardApplication[IccCardStatus.CARD_MAX_APPS];
private Context mContext;
private CommandsInterface mCi;
- private UiccCard mUiccCard; //parent
+ private final UiccCard mUiccCard; //parent
private CatService mCatService;
private UiccCarrierPrivilegeRules mCarrierPrivilegeRules;
private boolean mDisposed = false;
private RegistrantList mCarrierPrivilegeRegistrants = new RegistrantList();
-
- private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 15;
- private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 16;
- 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_PRIVILEGES_LOADED = 20;
-
- private static final LocalLog sLocalLog = new LocalLog(100);
+ private RegistrantList mOperatorBrandOverrideRegistrants = new RegistrantList();
private final int mPhoneId;
- /*----------------------------------------------------*/
- // logic moved over from IccCardProxy
private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 1;
- private static final int EVENT_ICC_LOCKED = 5;
- private static final int EVENT_APP_READY = 6;
- private static final int EVENT_RECORDS_LOADED = 7;
- private static final int EVENT_NETWORK_LOCKED = 9;
- private static final int EVENT_EID_READY = 10;
-
- private static final int EVENT_ICC_RECORD_EVENTS = 500;
+ private static final int EVENT_ICC_LOCKED = 2;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public static final int EVENT_APP_READY = 3;
+ private static final int EVENT_RECORDS_LOADED = 4;
+ private static final int EVENT_NETWORK_LOCKED = 5;
+ private static final int EVENT_EID_READY = 6;
+ private static final int EVENT_ICC_RECORD_EVENTS = 7;
+ private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 8;
+ private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 9;
+ private static final int EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE = 10;
+ private static final int EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE = 11;
+ private static final int EVENT_SIM_IO_DONE = 12;
+ private static final int EVENT_CARRIER_PRIVILEGES_LOADED = 13;
+ private static final int EVENT_CARRIER_CONFIG_CHANGED = 14;
private TelephonyManager mTelephonyManager;
@@ -136,55 +134,130 @@
private IccRecords mIccRecords = null;
private IccCardConstants.State mExternalState = IccCardConstants.State.UNKNOWN;
- public static final String ACTION_INTERNAL_SIM_STATE_CHANGED =
- "android.intent.action.internal_sim_state_changed";
- /*----------------------------------------------------*/
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARRIER_CONFIG_CHANGED));
+ }
+ }
+ };
+
+ @VisibleForTesting
+ public final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ // We still need to handle the following response messages even the UiccProfile has been
+ // disposed because whoever sent the request may be still waiting for the response.
+ if (mDisposed && msg.what != EVENT_OPEN_LOGICAL_CHANNEL_DONE
+ && msg.what != EVENT_CLOSE_LOGICAL_CHANNEL_DONE
+ && msg.what != EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE
+ && msg.what != EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE
+ && msg.what != EVENT_SIM_IO_DONE) {
+ loge("handleMessage: Received " + msg.what
+ + " after dispose(); ignoring the message");
+ return;
+ }
+ loglocal("handleMessage: Received " + msg.what + " for phoneId " + mPhoneId);
+ switch (msg.what) {
+ case EVENT_NETWORK_LOCKED:
+ mNetworkLockedRegistrants.notifyRegistrants();
+ // intentional fall through
+ case EVENT_RADIO_OFF_OR_UNAVAILABLE:
+ case EVENT_ICC_LOCKED:
+ case EVENT_APP_READY:
+ case EVENT_RECORDS_LOADED:
+ case EVENT_EID_READY:
+ if (VDBG) log("handleMessage: Received " + msg.what);
+ updateExternalState();
+ break;
+
+ case EVENT_ICC_RECORD_EVENTS:
+ if ((mCurrentAppType == UiccController.APP_FAM_3GPP) && (mIccRecords != null)) {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ int eventCode = (Integer) ar.result;
+ if (eventCode == SIMRecords.EVENT_SPN) {
+ mTelephonyManager.setSimOperatorNameForPhone(
+ mPhoneId, mIccRecords.getServiceProviderName());
+ }
+ }
+ break;
+
+ case EVENT_CARRIER_PRIVILEGES_LOADED:
+ if (VDBG) log("handleMessage: EVENT_CARRIER_PRIVILEGES_LOADED");
+ onCarrierPriviligesLoadedMessage();
+ updateExternalState();
+ break;
+
+ case EVENT_CARRIER_CONFIG_CHANGED:
+ handleCarrierNameOverride();
+ break;
+
+ case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
+ case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
+ case EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE:
+ case EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE:
+ case EVENT_SIM_IO_DONE:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ loglocal("handleMessage: Exception " + ar.exception);
+ log("handleMessage: Error in SIM access with exception" + ar.exception);
+ }
+ AsyncResult.forMessage((Message) ar.userObj, ar.result, ar.exception);
+ ((Message) ar.userObj).sendToTarget();
+ break;
+
+ default:
+ loge("handleMessage: Unhandled message with number: " + msg.what);
+ break;
+ }
+ }
+ };
public UiccProfile(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId,
- UiccCard uiccCard) {
+ UiccCard uiccCard, Object lock) {
if (DBG) log("Creating profile");
+ mLock = lock;
mUiccCard = uiccCard;
mPhoneId = phoneId;
- if (ICC_CARD_PROXY_REMOVED) {
- // set current app type based on phone type - do this before calling update() as that
- // calls updateIccAvailability() which uses mCurrentAppType
- Phone phone = PhoneFactory.getPhone(phoneId);
- if (phone != null) {
- setCurrentAppType(phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM);
- }
+ // set current app type based on phone type - do this before calling update() as that
+ // calls updateIccAvailability() which uses mCurrentAppType
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone != null) {
+ setCurrentAppType(phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM);
}
if (mUiccCard instanceof EuiccCard) {
- ((EuiccCard) mUiccCard).registerForEidReady(this, EVENT_EID_READY, null);
+ ((EuiccCard) mUiccCard).registerForEidReady(mHandler, EVENT_EID_READY, null);
}
update(c, ci, ics);
+ ci.registerForOffOrNotAvailable(mHandler, EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
+ resetProperties();
- if (ICC_CARD_PROXY_REMOVED) {
- ci.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
-
- resetProperties();
- }
+ IntentFilter intentfilter = new IntentFilter();
+ intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ c.registerReceiver(mReceiver, intentfilter);
}
/**
* Dispose the UiccProfile.
*/
public void dispose() {
- synchronized (mLock) {
- if (DBG) log("Disposing profile");
+ if (DBG) log("Disposing profile");
+ // mUiccCard is outside of mLock in order to prevent deadlocking. This is safe because
+ // EuiccCard#unregisterForEidReady handles its own lock
+ if (mUiccCard instanceof EuiccCard) {
+ ((EuiccCard) mUiccCard).unregisterForEidReady(mHandler);
+ }
+ synchronized (mLock) {
unregisterAllAppEvents();
unregisterCurrAppEvents();
- if (mUiccCard instanceof EuiccCard) {
- ((EuiccCard) mUiccCard).unregisterForEidReady(this);
- }
-
- if (ICC_CARD_PROXY_REMOVED) {
- mCi.unregisterForOffOrNotAvailable(this);
- }
+ mCi.unregisterForOffOrNotAvailable(mHandler);
+ mContext.unregisterReceiver(mReceiver);
if (mCatService != null) mCatService.dispose();
for (UiccCardApplication app : mUiccApplications) {
@@ -216,9 +289,7 @@
private void setCurrentAppType(boolean isGsm) {
if (VDBG) log("setCurrentAppType");
synchronized (mLock) {
- boolean isLteOnCdmaMode = TelephonyManager.getLteOnCdmaModeStatic()
- == PhoneConstants.LTE_ON_CDMA_TRUE;
- if (isGsm || isLteOnCdmaMode) {
+ if (isGsm) {
mCurrentAppType = UiccController.APP_FAM_3GPP;
} else {
mCurrentAppType = UiccController.APP_FAM_3GPP2;
@@ -226,59 +297,62 @@
}
}
- @Override
- public void handleMessage(Message msg) {
- if (mDisposed) {
- loge("handleMessage: Received " + msg.what + " after dispose(); ignoring the message");
+ /**
+ * Override the carrier name with either carrier config or SPN
+ * if an override is provided.
+ */
+ private void handleCarrierNameOverride() {
+ SubscriptionController subCon = SubscriptionController.getInstance();
+ final int subId = subCon.getSubIdUsingPhoneId(mPhoneId);
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ loge("subId not valid for Phone " + mPhoneId);
return;
}
- switch (msg.what) {
- case EVENT_NETWORK_LOCKED:
- mNetworkLockedRegistrants.notifyRegistrants();
- // intentional fall through
- case EVENT_RADIO_OFF_OR_UNAVAILABLE:
- case EVENT_ICC_LOCKED:
- case EVENT_APP_READY:
- case EVENT_RECORDS_LOADED:
- case EVENT_EID_READY:
- if (VDBG) log("handleMessage: Received " + msg.what);
- updateExternalState();
- break;
- case EVENT_ICC_RECORD_EVENTS:
- if ((mCurrentAppType == UiccController.APP_FAM_3GPP) && (mIccRecords != null)) {
- AsyncResult ar = (AsyncResult) msg.obj;
- int eventCode = (Integer) ar.result;
- if (eventCode == SIMRecords.EVENT_SPN) {
- mTelephonyManager.setSimOperatorNameForPhone(
- mPhoneId, mIccRecords.getServiceProviderName());
- }
- }
- break;
+ CarrierConfigManager configLoader = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configLoader == null) {
+ loge("Failed to load a Carrier Config");
+ return;
+ }
- case EVENT_CARRIER_PRIVILEGES_LOADED:
- if (VDBG) log("EVENT_CARRIER_PRIVILEGES_LOADED");
- onCarrierPriviligesLoadedMessage();
- updateExternalState();
- break;
+ PersistableBundle config = configLoader.getConfigForSubId(subId);
+ boolean preferCcName = config.getBoolean(
+ CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
+ String ccName = config.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
+ // If carrier config is priority, use it regardless - the preference
+ // and the name were both set by the carrier, so this is safe;
+ // otherwise, if the SPN is priority but we don't have one *and* we have
+ // a name in carrier config, use the carrier config name as a backup.
+ if (preferCcName || (TextUtils.isEmpty(getServiceProviderName())
+ && !TextUtils.isEmpty(ccName))) {
+ if (mIccRecords != null) {
+ mIccRecords.setServiceProviderName(ccName);
+ }
+ mTelephonyManager.setSimOperatorNameForPhone(mPhoneId, ccName);
+ mOperatorBrandOverrideRegistrants.notifyRegistrants();
+ }
- case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
- case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
- case EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE:
- case EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE:
- case EVENT_SIM_IO_DONE:
- AsyncResult ar = (AsyncResult) msg.obj;
- if (ar.exception != null) {
- loglocal("Exception: " + ar.exception);
- log("Error in SIM access with exception" + ar.exception);
- }
- AsyncResult.forMessage((Message) ar.userObj, ar.result, ar.exception);
- ((Message) ar.userObj).sendToTarget();
- break;
+ updateCarrierNameForSubscription(subCon, subId);
+ }
- default:
- loge("Unhandled message with number: " + msg.what);
- break;
+ private void updateCarrierNameForSubscription(SubscriptionController subCon, int subId) {
+ /* update display name with carrier override */
+ SubscriptionInfo subInfo = subCon.getActiveSubscriptionInfo(
+ subId, mContext.getOpPackageName());
+
+ if (subInfo == null || subInfo.getNameSource()
+ == SubscriptionManager.NAME_SOURCE_USER_INPUT) {
+ // either way, there is no subinfo to update
+ return;
+ }
+
+ CharSequence oldSubName = subInfo.getDisplayName();
+ String newCarrierName = mTelephonyManager.getSimOperatorName(subId);
+
+ if (!TextUtils.isEmpty(newCarrierName) && !newCarrierName.equals(oldSubName)) {
+ log("sim name[" + mPhoneId + "] = " + newCarrierName);
+ subCon.setDisplayName(newCarrierName, subId);
}
}
@@ -316,7 +390,11 @@
}
}
- private void updateExternalState() {
+ /**
+ * Update the external SIM state
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public void updateExternalState() {
// First check if card state is IO_ERROR or RESTRICTED
if (mUiccCard.getCardState() == IccCardStatus.CardState.CARDSTATE_ERROR) {
setExternalState(IccCardConstants.State.CARD_IO_ERROR);
@@ -409,6 +487,7 @@
setExternalState(IccCardConstants.State.NOT_READY);
break;
case APPSTATE_READY:
+ checkAndUpdateIfAnyAppToBeIgnored();
if (areAllApplicationsReady()) {
if (areAllRecordsLoaded() && areCarrierPriviligeRulesLoaded()) {
if (VDBG) log("updateExternalState: setting state to LOADED");
@@ -437,12 +516,12 @@
for (UiccCardApplication app : mUiccApplications) {
if (app != null) {
if (VDBG) log("registerUiccCardEvents: registering for EVENT_APP_READY");
- app.registerForReady(this, EVENT_APP_READY, null);
+ app.registerForReady(mHandler, EVENT_APP_READY, null);
IccRecords ir = app.getIccRecords();
if (ir != null) {
if (VDBG) log("registerUiccCardEvents: registering for EVENT_RECORDS_LOADED");
- ir.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
- ir.registerForRecordsEvents(this, EVENT_ICC_RECORD_EVENTS, null);
+ ir.registerForRecordsLoaded(mHandler, EVENT_RECORDS_LOADED, null);
+ ir.registerForRecordsEvents(mHandler, EVENT_ICC_RECORD_EVENTS, null);
}
}
}
@@ -451,11 +530,11 @@
private void unregisterAllAppEvents() {
for (UiccCardApplication app : mUiccApplications) {
if (app != null) {
- app.unregisterForReady(this);
+ app.unregisterForReady(mHandler);
IccRecords ir = app.getIccRecords();
if (ir != null) {
- ir.unregisterForRecordsLoaded(this);
- ir.unregisterForRecordsEvents(this);
+ ir.unregisterForRecordsLoaded(mHandler);
+ ir.unregisterForRecordsEvents(mHandler);
}
}
}
@@ -464,31 +543,18 @@
private void registerCurrAppEvents() {
// In case of locked, only listen to the current application.
if (mIccRecords != null) {
- mIccRecords.registerForLockedRecordsLoaded(this, EVENT_ICC_LOCKED, null);
- mIccRecords.registerForNetworkLockedRecordsLoaded(this, EVENT_NETWORK_LOCKED, null);
+ mIccRecords.registerForLockedRecordsLoaded(mHandler, EVENT_ICC_LOCKED, null);
+ mIccRecords.registerForNetworkLockedRecordsLoaded(mHandler, EVENT_NETWORK_LOCKED, null);
}
}
private void unregisterCurrAppEvents() {
if (mIccRecords != null) {
- mIccRecords.unregisterForLockedRecordsLoaded(this);
- mIccRecords.unregisterForNetworkLockedRecordsLoaded(this);
+ mIccRecords.unregisterForLockedRecordsLoaded(mHandler);
+ mIccRecords.unregisterForNetworkLockedRecordsLoaded(mHandler);
}
}
- static void broadcastInternalIccStateChangedIntent(String value, String reason, int phoneId) {
- Intent intent = new Intent(ACTION_INTERNAL_SIM_STATE_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
- intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, value);
- intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
- intent.putExtra(PhoneConstants.PHONE_KEY, phoneId); // SubId may not be valid.
- log("Sending intent ACTION_INTERNAL_SIM_STATE_CHANGED value=" + value
- + " for mPhoneId : " + phoneId);
- ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
- }
-
private void setExternalState(IccCardConstants.State newState, boolean override) {
synchronized (mLock) {
if (!SubscriptionManager.isValidSlotIndex(mPhoneId)) {
@@ -505,26 +571,26 @@
// Update the MCC/MNC.
if (mIccRecords != null) {
String operator = mIccRecords.getOperatorNumeric();
- log("operator=" + operator + " mPhoneId=" + mPhoneId);
+ log("setExternalState: operator=" + operator + " mPhoneId=" + mPhoneId);
if (!TextUtils.isEmpty(operator)) {
mTelephonyManager.setSimOperatorNumericForPhone(mPhoneId, operator);
String countryCode = operator.substring(0, 3);
if (countryCode != null) {
mTelephonyManager.setSimCountryIsoForPhone(mPhoneId,
- MccTable.countryCodeForMcc(Integer.parseInt(countryCode)));
+ MccTable.countryCodeForMcc(countryCode));
} else {
- loge("EVENT_RECORDS_LOADED Country code is null");
+ loge("setExternalState: state LOADED; Country code is null");
}
} else {
- loge("EVENT_RECORDS_LOADED Operator name is null");
+ loge("setExternalState: state LOADED; Operator name is null");
}
}
}
log("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState);
mTelephonyManager.setSimStateForPhone(mPhoneId, getState().toString());
- broadcastInternalIccStateChangedIntent(getIccStateIntentString(mExternalState),
+ UiccController.updateInternalIccState(getIccStateIntentString(mExternalState),
getIccStateReason(mExternalState), mPhoneId);
}
}
@@ -702,6 +768,13 @@
}
@Override
+ public boolean getIccFdnAvailable() {
+ synchronized (mLock) {
+ return mUiccApplication != null && mUiccApplication.getIccFdnAvailable();
+ }
+ }
+
+ @Override
public boolean getIccPin2Blocked() {
/* defaults to disabled */
return mUiccApplication != null && mUiccApplication.getIccPin2Blocked();
@@ -781,15 +854,14 @@
@Override
public boolean hasIccCard() {
- synchronized (mLock) {
- if (mUiccCard != null && mUiccCard.getCardState()
- != IccCardStatus.CardState.CARDSTATE_ABSENT) {
- return true;
- }
- loge("hasIccCard: UiccProfile is not null but UiccCard is null or card state is "
- + "ABSENT");
- return false;
+ // mUiccCard is initialized in constructor, so won't be null
+ if (mUiccCard.getCardState()
+ != IccCardStatus.CardState.CARDSTATE_ABSENT) {
+ return true;
}
+ loge("hasIccCard: UiccProfile is not null but UiccCard is null or card state is "
+ + "ABSENT");
+ return false;
}
/**
@@ -831,7 +903,7 @@
log("Before privilege rules: " + mCarrierPrivilegeRules + " : " + ics.mCardState);
if (mCarrierPrivilegeRules == null && ics.mCardState == CardState.CARDSTATE_PRESENT) {
mCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(this,
- obtainMessage(EVENT_CARRIER_PRIVILEGES_LOADED));
+ mHandler.obtainMessage(EVENT_CARRIER_PRIVILEGES_LOADED));
} else if (mCarrierPrivilegeRules != null
&& ics.mCardState != CardState.CARDSTATE_PRESENT) {
mCarrierPrivilegeRules = null;
@@ -884,24 +956,48 @@
* this only checks for SIM/USIM and CSIM/RUIM apps. ISIM is considered not supported for this
* purpose as there are cards that have ISIM app that is never read (there are SIMs for which
* the state of ISIM goes to DETECTED but never to READY).
+ * CSIM/RUIM apps are considered not supported if CDMA is not supported.
*/
private boolean isSupportedApplication(UiccCardApplication app) {
// TODO: 2/15/18 Add check to see if ISIM app will go to READY state, and if yes, check for
// ISIM also (currently ISIM is considered as not supported in this function)
- if (app.getType() != AppType.APPTYPE_USIM && app.getType() != AppType.APPTYPE_CSIM
- && app.getType() != AppType.APPTYPE_SIM && app.getType() != AppType.APPTYPE_RUIM) {
- return false;
+ if (app.getType() == AppType.APPTYPE_USIM || app.getType() == AppType.APPTYPE_SIM
+ || (UiccController.getInstance().isCdmaSupported()
+ && (app.getType() == AppType.APPTYPE_CSIM
+ || app.getType() == AppType.APPTYPE_RUIM))) {
+ return true;
}
- return true;
+ return false;
+ }
+
+ private void checkAndUpdateIfAnyAppToBeIgnored() {
+ boolean[] appReadyStateTracker = new boolean[AppType.APPTYPE_ISIM.ordinal() + 1];
+ for (UiccCardApplication app : mUiccApplications) {
+ if (app != null && isSupportedApplication(app) && app.isReady()) {
+ appReadyStateTracker[app.getType().ordinal()] = true;
+ }
+ }
+
+ for (UiccCardApplication app : mUiccApplications) {
+ if (app != null && isSupportedApplication(app) && !app.isReady()) {
+ /* Checks if the appReadyStateTracker has already an entry in ready state
+ with same type as app */
+ if (appReadyStateTracker[app.getType().ordinal()]) {
+ app.setAppIgnoreState(true);
+ }
+ }
+ }
}
private boolean areAllApplicationsReady() {
for (UiccCardApplication app : mUiccApplications) {
- if (app != null && isSupportedApplication(app) && !app.isReady()) {
+ if (app != null && isSupportedApplication(app) && !app.isReady()
+ && !app.isAppIgnored()) {
if (VDBG) log("areAllApplicationsReady: return false");
return false;
}
}
+
if (VDBG) {
log("areAllApplicationsReady: outside loop, return " + (mUiccApplication != null));
}
@@ -910,7 +1006,7 @@
private boolean areAllRecordsLoaded() {
for (UiccCardApplication app : mUiccApplications) {
- if (app != null && isSupportedApplication(app)) {
+ if (app != null && isSupportedApplication(app) && !app.isAppIgnored()) {
IccRecords ir = app.getIccRecords();
if (ir == null || !ir.isLoaded()) {
if (VDBG) log("areAllRecordsLoaded: return false");
@@ -947,6 +1043,20 @@
}
/**
+ * Registers the handler when operator brand name is overridden.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForOpertorBrandOverride(Handler h, int what, Object obj) {
+ synchronized (mLock) {
+ Registrant r = new Registrant(h, what, obj);
+ mOperatorBrandOverrideRegistrants.add(r);
+ }
+ }
+
+ /**
* Registers the handler when carrier privilege rules are loaded.
*
* @param h Handler for notification message.
@@ -975,6 +1085,17 @@
mCarrierPrivilegeRegistrants.remove(h);
}
}
+
+ /**
+ * Unregister for notifications when operator brand name is overriden.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForOperatorBrandOverride(Handler h) {
+ synchronized (mLock) {
+ mOperatorBrandOverrideRegistrants.remove(h);
+ }
+ }
private boolean isPackageInstalled(String pkgName) {
PackageManager pm = mContext.getPackageManager();
@@ -1144,8 +1265,12 @@
* Resets the application with the input AID. Returns true if any changes were made.
*
* A null aid implies a card level reset - all applications must be reset.
+ *
+ * @param aid aid of the application which should be reset; null imples all applications
+ * @param reset true if reset is required. false for initialization.
+ * @return boolean indicating if there was any change made as part of the reset
*/
- public boolean resetAppWithAid(String aid) {
+ public boolean resetAppWithAid(String aid, boolean reset) {
synchronized (mLock) {
boolean changed = false;
for (int i = 0; i < mUiccApplications.length; i++) {
@@ -1157,11 +1282,12 @@
changed = true;
}
}
- if (TextUtils.isEmpty(aid)) {
+ if (reset && TextUtils.isEmpty(aid)) {
if (mCarrierPrivilegeRules != null) {
mCarrierPrivilegeRules = null;
changed = true;
}
+ // CatService shall be disposed only when a card level reset happens.
if (mCatService != null) {
mCatService.dispose();
mCatService = null;
@@ -1176,19 +1302,19 @@
* Exposes {@link CommandsInterface#iccOpenLogicalChannel}
*/
public void iccOpenLogicalChannel(String aid, int p2, Message response) {
- loglocal("Open Logical Channel: " + aid + " , " + p2 + " by pid:" + Binder.getCallingPid()
+ loglocal("iccOpenLogicalChannel: " + aid + " , " + p2 + " by pid:" + Binder.getCallingPid()
+ " uid:" + Binder.getCallingUid());
mCi.iccOpenLogicalChannel(aid, p2,
- obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, response));
+ mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, response));
}
/**
* Exposes {@link CommandsInterface#iccCloseLogicalChannel}
*/
public void iccCloseLogicalChannel(int channel, Message response) {
- loglocal("Close Logical Channel: " + channel);
+ loglocal("iccCloseLogicalChannel: " + channel);
mCi.iccCloseLogicalChannel(channel,
- obtainMessage(EVENT_CLOSE_LOGICAL_CHANNEL_DONE, response));
+ mHandler.obtainMessage(EVENT_CLOSE_LOGICAL_CHANNEL_DONE, response));
}
/**
@@ -1197,7 +1323,7 @@
public void iccTransmitApduLogicalChannel(int channel, int cla, int command,
int p1, int p2, int p3, String data, Message response) {
mCi.iccTransmitApduLogicalChannel(channel, cla, command, p1, p2, p3,
- data, obtainMessage(EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE, response));
+ data, mHandler.obtainMessage(EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE, response));
}
/**
@@ -1206,7 +1332,7 @@
public void iccTransmitApduBasicChannel(int cla, int command,
int p1, int p2, int p3, String data, Message response) {
mCi.iccTransmitApduBasicChannel(cla, command, p1, p2, p3,
- data, obtainMessage(EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE, response));
+ data, mHandler.obtainMessage(EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE, response));
}
/**
@@ -1215,7 +1341,7 @@
public void iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
String pathID, Message response) {
mCi.iccIO(command, fileID, pathID, p1, p2, p3, null, null,
- obtainMessage(EVENT_SIM_IO_DONE, response));
+ mHandler.obtainMessage(EVENT_SIM_IO_DONE, response));
}
/**
@@ -1351,6 +1477,7 @@
} else {
spEditor.putString(key, brand).commit();
}
+ mOperatorBrandOverrideRegistrants.notifyRegistrants();
return true;
}
@@ -1363,21 +1490,7 @@
return null;
}
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- String brandName = sp.getString(OPERATOR_BRAND_OVERRIDE_PREFIX + iccId, null);
- if (brandName == null) {
- // Check if CarrierConfig sets carrier name
- CarrierConfigManager manager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- int subId = SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhoneId);
- if (manager != null) {
- PersistableBundle bundle = manager.getConfigForSubId(subId);
- if (bundle != null && bundle.getBoolean(
- CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL)) {
- brandName = bundle.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
- }
- }
- }
- return brandName;
+ return sp.getString(OPERATOR_BRAND_OVERRIDE_PREFIX + iccId, null);
}
/**
@@ -1405,7 +1518,7 @@
}
private void loglocal(String msg) {
- if (DBG) sLocalLog.log(msg);
+ if (DBG) UiccController.sLocalLog.log("UiccProfile[" + mPhoneId + "]: " + msg);
}
/**
@@ -1419,6 +1532,10 @@
pw.println(" mCarrierPrivilegeRegistrants[" + i + "]="
+ ((Registrant) mCarrierPrivilegeRegistrants.get(i)).getHandler());
}
+ for (int i = 0; i < mOperatorBrandOverrideRegistrants.size(); i++) {
+ pw.println(" mOperatorBrandOverrideRegistrants[" + i + "]="
+ + ((Registrant) mOperatorBrandOverrideRegistrants.get(i)).getHandler());
+ }
pw.println(" mUniversalPinState=" + mUniversalPinState);
pw.println(" mGsmUmtsSubscriptionAppIndex=" + mGsmUmtsSubscriptionAppIndex);
pw.println(" mCdmaSubscriptionAppIndex=" + mCdmaSubscriptionAppIndex);
@@ -1464,22 +1581,16 @@
}
pw.flush();
- if (ICC_CARD_PROXY_REMOVED) {
- pw.println(" mNetworkLockedRegistrants: size=" + mNetworkLockedRegistrants.size());
- for (int i = 0; i < mNetworkLockedRegistrants.size(); i++) {
- pw.println(" mNetworkLockedRegistrants[" + i + "]="
- + ((Registrant) mNetworkLockedRegistrants.get(i)).getHandler());
- }
- pw.println(" mCurrentAppType=" + mCurrentAppType);
- pw.println(" mUiccCard=" + mUiccCard);
- pw.println(" mUiccApplication=" + mUiccApplication);
- pw.println(" mIccRecords=" + mIccRecords);
- pw.println(" mExternalState=" + mExternalState);
- pw.flush();
+ pw.println(" mNetworkLockedRegistrants: size=" + mNetworkLockedRegistrants.size());
+ for (int i = 0; i < mNetworkLockedRegistrants.size(); i++) {
+ pw.println(" mNetworkLockedRegistrants[" + i + "]="
+ + ((Registrant) mNetworkLockedRegistrants.get(i)).getHandler());
}
-
- pw.println("sLocalLog:");
- sLocalLog.dump(fd, pw, args);
+ pw.println(" mCurrentAppType=" + mCurrentAppType);
+ pw.println(" mUiccCard=" + mUiccCard);
+ pw.println(" mUiccApplication=" + mUiccApplication);
+ pw.println(" mIccRecords=" + mIccRecords);
+ pw.println(" mExternalState=" + mExternalState);
pw.flush();
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccSlot.java b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
index aa141ab..25ef637 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccSlot.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
@@ -27,7 +27,6 @@
import android.os.Message;
import android.os.PowerManager;
import android.telephony.Rlog;
-import android.util.LocalLog;
import android.view.WindowManager;
import com.android.internal.R;
@@ -53,6 +52,7 @@
private final Object mLock = new Object();
private boolean mActive;
+ private boolean mStateIsUnknown = true;
private CardState mCardState;
private Context mContext;
private CommandsInterface mCi;
@@ -66,22 +66,22 @@
private static final int EVENT_CARD_REMOVED = 13;
private static final int EVENT_CARD_ADDED = 14;
- private static final LocalLog sLocalLog = new LocalLog(100);
-
public UiccSlot(Context c, boolean isActive) {
if (DBG) log("Creating");
mContext = c;
mActive = isActive;
- mCardState = CardState.CARDSTATE_ABSENT;
+ mCardState = null;
}
/**
* Update slot. The main trigger for this is a change in the ICC Card status.
*/
public void update(CommandsInterface ci, IccCardStatus ics, int phoneId) {
+ if (DBG) log("cardStatus update: " + ics.toString());
synchronized (mLock) {
CardState oldState = mCardState;
mCardState = ics.mCardState;
+ mIccId = ics.iccid;
mPhoneId = phoneId;
parseAtr(ics.atr);
mCi = ci;
@@ -91,22 +91,14 @@
log("update: radioState=" + radioState + " mLastRadioState=" + mLastRadioState);
}
- if (oldState != CardState.CARDSTATE_ABSENT
- && mCardState == CardState.CARDSTATE_ABSENT) {
- // No notifications while radio is off or we just powering up
- if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
- if (DBG) log("update: notify card removed");
- sendMessage(obtainMessage(EVENT_CARD_REMOVED, null));
- }
-
- UiccProfile.broadcastInternalIccStateChangedIntent(
- IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, mPhoneId);
-
- // no card present in the slot now; dispose card and make mUiccCard null
- mUiccCard.dispose();
- mUiccCard = null;
- } else if (oldState == CardState.CARDSTATE_ABSENT
- && mCardState != CardState.CARDSTATE_ABSENT) {
+ if (absentStateUpdateNeeded(oldState)) {
+ updateCardStateAbsent();
+ // Because mUiccCard may be updated in both IccCardStatus and IccSlotStatus, we need to
+ // create a new UiccCard instance in two scenarios:
+ // 1. mCardState is changing from ABSENT to non ABSENT.
+ // 2. The latest mCardState is not ABSENT, but there is no UiccCard instance.
+ } else if ((oldState == null || oldState == CardState.CARDSTATE_ABSENT
+ || mUiccCard == null) && mCardState != CardState.CARDSTATE_ABSENT) {
// No notifications while radio is off or we just powering up
if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
if (DBG) log("update: notify card added");
@@ -120,9 +112,9 @@
}
if (!mIsEuicc) {
- mUiccCard = new UiccCard(mContext, mCi, ics, mPhoneId);
+ mUiccCard = new UiccCard(mContext, mCi, ics, mPhoneId, mLock);
} else {
- mUiccCard = new EuiccCard(mContext, mCi, ics, phoneId);
+ mUiccCard = new EuiccCard(mContext, mCi, ics, phoneId, mLock);
}
} else {
if (mUiccCard != null) {
@@ -137,32 +129,73 @@
* Update slot based on IccSlotStatus.
*/
public void update(CommandsInterface ci, IccSlotStatus iss) {
- log("slotStatus update");
+ if (DBG) log("slotStatus update: " + iss.toString());
synchronized (mLock) {
+ CardState oldState = mCardState;
mCi = ci;
+ parseAtr(iss.atr);
+ mCardState = iss.cardState;
+ mIccId = iss.iccid;
if (iss.slotState == IccSlotStatus.SlotState.SLOTSTATE_INACTIVE) {
+ // TODO: (b/79432584) evaluate whether should broadcast card state change
+ // even if it's inactive.
if (mActive) {
mActive = false;
mLastRadioState = RadioState.RADIO_UNAVAILABLE;
mPhoneId = INVALID_PHONE_ID;
if (mUiccCard != null) mUiccCard.dispose();
- mUiccCard = null;
+ nullifyUiccCard(true /* sim state is unknown */);
}
- parseAtr(iss.atr);
- mCardState = iss.cardState;
- mIccId = iss.iccid;
- } else if (!mActive && iss.slotState == IccSlotStatus.SlotState.SLOTSTATE_ACTIVE) {
+ } else {
mActive = true;
- parseAtr(iss.atr);
- // todo - ignoring these fields for now; relying on sim state changed to update
- // these
- // iss.cardState;
- // iss.iccid;
- // iss.logicalSlotIndex;
+ mPhoneId = iss.logicalSlotIndex;
+ if (absentStateUpdateNeeded(oldState)) {
+ updateCardStateAbsent();
+ }
+ // TODO: (b/79432584) Create UiccCard or EuiccCard object here.
+ // Right now It's OK not creating it because Card status update will do it.
+ // But we should really make them symmetric.
}
}
}
+ private boolean absentStateUpdateNeeded(CardState oldState) {
+ return (oldState != CardState.CARDSTATE_ABSENT || mUiccCard != null)
+ && mCardState == CardState.CARDSTATE_ABSENT;
+ }
+
+ private void updateCardStateAbsent() {
+ RadioState radioState =
+ (mCi == null) ? RadioState.RADIO_UNAVAILABLE : mCi.getRadioState();
+ // No notifications while radio is off or we just powering up
+ if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
+ if (DBG) log("update: notify card removed");
+ sendMessage(obtainMessage(EVENT_CARD_REMOVED, null));
+ }
+
+ UiccController.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, mPhoneId);
+
+ // no card present in the slot now; dispose card and make mUiccCard null
+ if (mUiccCard != null) {
+ mUiccCard.dispose();
+ }
+ nullifyUiccCard(false /* sim state is not unknown */);
+ mLastRadioState = radioState;
+ }
+
+ // whenever we set mUiccCard to null, we lose the ability to differentiate between absent and
+ // unknown states. To mitigate this, we will us mStateIsUnknown to keep track. The sim is only
+ // unknown if we haven't heard from the radio or if the radio has become unavailable.
+ private void nullifyUiccCard(boolean stateUnknown) {
+ mStateIsUnknown = stateUnknown;
+ mUiccCard = null;
+ }
+
+ public boolean isStateUnknown() {
+ return (mCardState == null || mCardState == CardState.CARDSTATE_ABSENT) && mStateIsUnknown;
+ }
+
private void checkIsEuiccSupported() {
if (mAtr != null && mAtr.isEuiccSupported()) {
mIsEuicc = true;
@@ -201,6 +234,10 @@
}
}
+ public boolean isExtendedApduSupported() {
+ return (mAtr != null && mAtr.isExtendedApduSupported());
+ }
+
@Override
protected void finalize() {
if (DBG) log("UiccSlot finalized");
@@ -300,7 +337,11 @@
*/
public CardState getCardState() {
synchronized (mLock) {
- return mCardState;
+ if (mCardState == null) {
+ return CardState.CARDSTATE_ABSENT;
+ } else {
+ return mCardState;
+ }
}
}
@@ -320,10 +361,10 @@
if (mUiccCard != null) {
mUiccCard.dispose();
}
- mUiccCard = null;
+ nullifyUiccCard(true /* sim state is unknown */);
if (mPhoneId != INVALID_PHONE_ID) {
- UiccProfile.broadcastInternalIccStateChangedIntent(
+ UiccController.updateInternalIccState(
IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null, mPhoneId);
}
@@ -339,10 +380,6 @@
Rlog.e(TAG, msg);
}
- private void loglocal(String msg) {
- if (DBG) sLocalLog.log(msg);
- }
-
/**
* Dump
*/
@@ -360,8 +397,6 @@
}
pw.println();
pw.flush();
- pw.println("sLocalLog:");
- sLocalLog.dump(fd, pw, args);
pw.flush();
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
index 6acfb80..103c75b 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
@@ -116,8 +116,8 @@
private EuiccSpecVersion mSpecVersion;
private volatile String mEid;
- public EuiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId) {
- super(c, ci, ics, phoneId);
+ public EuiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock) {
+ super(c, ci, ics, phoneId, lock);
// TODO: Set supportExtendedApdu based on ATR.
mApduSender = new ApduSender(ci, ISD_R_AID, false /* supportExtendedApdu */);
diff --git a/src/java/com/android/internal/telephony/util/NotificationChannelController.java b/src/java/com/android/internal/telephony/util/NotificationChannelController.java
index 54d7d1a..3f6da54 100644
--- a/src/java/com/android/internal/telephony/util/NotificationChannelController.java
+++ b/src/java/com/android/internal/telephony/util/NotificationChannelController.java
@@ -57,6 +57,8 @@
NotificationManager.IMPORTANCE_DEFAULT);
alertChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
+ // allow users to block notifications from system
+ alertChannel.setBlockableSystem(true);
final NotificationChannel mobileDataStatusChannel = new NotificationChannel(
CHANNEL_ID_MOBILE_DATA_STATUS,
diff --git a/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java b/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
index a3c7926..17f2611 100644
--- a/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
+++ b/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
@@ -36,17 +36,6 @@
private SMSDispatcherUtil() {}
/**
- * Whether to block SMS or not.
- *
- * @param isCdma true if cdma format should be used.
- * @param phone the Phone to use
- * @return true to block sms; false otherwise.
- */
- public static boolean shouldBlockSms(boolean isCdma, Phone phone) {
- return isCdma && phone.isInEcm();
- }
-
- /**
* Trigger the proper implementation for getting submit pdu for text sms based on format.
*
* @param isCdma true if cdma format should be used.
diff --git a/src/java/com/android/internal/telephony/util/TimeStampedValue.java b/src/java/com/android/internal/telephony/util/TimeStampedValue.java
deleted file mode 100644
index e2628f6..0000000
--- a/src/java/com/android/internal/telephony/util/TimeStampedValue.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 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.util;
-
-import android.os.SystemClock;
-
-/**
- * A pair containing a value and an associated time stamp.
- *
- * @param <T> The type of the value.
- */
-public final class TimeStampedValue<T> {
-
- /** The value. */
- public final T mValue;
-
- /**
- * The value of {@link SystemClock#elapsedRealtime} or equivalent when value was
- * determined.
- */
- public final long mElapsedRealtime;
-
- public TimeStampedValue(T value, long elapsedRealtime) {
- this.mValue = value;
- this.mElapsedRealtime = elapsedRealtime;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- TimeStampedValue<?> that = (TimeStampedValue<?>) o;
-
- if (mElapsedRealtime != that.mElapsedRealtime) {
- return false;
- }
- return mValue != null ? mValue.equals(that.mValue) : that.mValue == null;
- }
-
- @Override
- public int hashCode() {
- int result = mValue != null ? mValue.hashCode() : 0;
- result = 31 * result + (int) (mElapsedRealtime ^ (mElapsedRealtime >>> 32));
- return result;
- }
-
- @Override
- public String toString() {
- return "TimeStampedValue{"
- + "mValue=" + mValue
- + ", elapsedRealtime=" + mElapsedRealtime
- + '}';
- }
-}
diff --git a/src/java/com/google/android/mms/pdu/PduComposer.java b/src/java/com/google/android/mms/pdu/PduComposer.java
index bb44bab..582caec 100644
--- a/src/java/com/google/android/mms/pdu/PduComposer.java
+++ b/src/java/com/google/android/mms/pdu/PduComposer.java
@@ -155,7 +155,8 @@
/* make the message */
switch (type) {
case PduHeaders.MESSAGE_TYPE_SEND_REQ:
- if (makeSendReqPdu() != PDU_COMPOSE_SUCCESS) {
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ if (makeSendRetrievePdu(type) != PDU_COMPOSE_SUCCESS) {
return null;
}
break;
@@ -564,6 +565,7 @@
case PduHeaders.PRIORITY:
case PduHeaders.DELIVERY_REPORT:
case PduHeaders.READ_REPORT:
+ case PduHeaders.RETRIEVE_STATUS:
int octet = mPduHeader.getOctet(field);
if (0 == octet) {
return PDU_COMPOSE_FIELD_NOT_SET;
@@ -584,6 +586,7 @@
break;
case PduHeaders.SUBJECT:
+ case PduHeaders.RETRIEVE_TEXT:
EncodedStringValue enString =
mPduHeader.getEncodedStringValue(field);
if (null == enString) {
@@ -757,7 +760,7 @@
/**
* Make Send.req.
*/
- private int makeSendReqPdu() {
+ private int makeSendRetrievePdu(int type) {
if (mMessage == null) {
mMessage = new ByteArrayOutputStream();
mPosition = 0;
@@ -765,7 +768,7 @@
// X-Mms-Message-Type
appendOctet(PduHeaders.MESSAGE_TYPE);
- appendOctet(PduHeaders.MESSAGE_TYPE_SEND_REQ);
+ appendOctet(type);
// X-Mms-Transaction-ID
appendOctet(PduHeaders.TRANSACTION_ID);
@@ -831,17 +834,25 @@
// X-Mms-Read-Report Optional
appendHeader(PduHeaders.READ_REPORT);
+ if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
+ // X-Mms-Retrieve-Status Optional
+ appendHeader(PduHeaders.RETRIEVE_STATUS);
+ // X-Mms-Retrieve-Text Optional
+ appendHeader(PduHeaders.RETRIEVE_TEXT);
+ }
+
+
// Content-Type
appendOctet(PduHeaders.CONTENT_TYPE);
// Message body
- return makeMessageBody();
+ return makeMessageBody(type);
}
/**
* Make message body.
*/
- private int makeMessageBody() {
+ private int makeMessageBody(int type) {
// 1. add body informations
mStack.newbuf(); // Switching buffer because we need to
@@ -858,7 +869,12 @@
appendShortInteger(contentTypeIdentifier.intValue());
// content-type parameter: start
- PduBody body = ((SendReq) mPdu).getBody();
+ PduBody body;
+ if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
+ body = ((RetrieveConf) mPdu).getBody();
+ } else {
+ body = ((SendReq) mPdu).getBody();
+ }
if (null == body || body.getPartsNum() == 0) {
// empty message
appendUintvarInteger(0);
diff --git a/tests/telephonytests/Android.mk b/tests/telephonytests/Android.mk
index 3356e68..6709d46 100644
--- a/tests/telephonytests/Android.mk
+++ b/tests/telephonytests/Android.mk
@@ -20,7 +20,4 @@
LOCAL_COMPATIBILITY_SUITE := device-tests
-# b/72575505
-LOCAL_ERROR_PRONE_FLAGS := -Xep:JUnit4TestNotRun:WARN
-
include $(BUILD_PACKAGE)
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java b/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java
index 65cb5f7..3810f76 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java
@@ -68,8 +68,8 @@
@Test
public void testRegistrationConfigParcel() {
ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration.Builder()
- .addFeature(ImsFeature.FEATURE_MMTEL)
- .addFeature(ImsFeature.FEATURE_RCS)
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL)
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS)
.build();
Parcel p = Parcel.obtain();
testConfig.writeToParcel(p, 0);
@@ -85,14 +85,14 @@
@Test
public void testRegistrationConfigPermutationEqual() {
ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration.Builder()
- .addFeature(ImsFeature.FEATURE_MMTEL)
- .addFeature(ImsFeature.FEATURE_RCS)
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL)
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS)
.build();
// Permute field insertion ordering to ensure order doesn't matter.
ImsFeatureConfiguration testConfig2 = new ImsFeatureConfiguration.Builder()
- .addFeature(ImsFeature.FEATURE_RCS)
- .addFeature(ImsFeature.FEATURE_MMTEL)
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS)
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL)
.build();
assertEquals(testConfig, testConfig2);
@@ -101,13 +101,16 @@
@SmallTest
@Test
public void testRegistrationConfigConstructorsEqual() {
- ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration(
- new int[] {ImsFeature.FEATURE_MMTEL, ImsFeature.FEATURE_RCS});
+ // Permute field insertion ordering to ensure order doesn't matter.
+ ImsFeatureConfiguration testConfig = new ImsFeatureConfiguration.Builder()
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL)
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS)
+ .build();
// Permute field insertion ordering to ensure order doesn't matter.
ImsFeatureConfiguration testConfig2 = new ImsFeatureConfiguration.Builder()
- .addFeature(ImsFeature.FEATURE_RCS)
- .addFeature(ImsFeature.FEATURE_MMTEL)
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_RCS)
+ .addFeature(/*slotId*/ 0, ImsFeature.FEATURE_MMTEL)
.build();
assertEquals(testConfig, testConfig2);
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java b/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
index 37fe7b7..34febcb 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
@@ -175,8 +175,8 @@
@SmallTest
public void testQuerySupportedImsFeatures() throws RemoteException {
ImsFeatureConfiguration config = new ImsFeatureConfiguration.Builder()
- .addFeature(ImsFeature.FEATURE_MMTEL)
- .addFeature(ImsFeature.FEATURE_RCS)
+ .addFeature(0, ImsFeature.FEATURE_MMTEL)
+ .addFeature(0, ImsFeature.FEATURE_RCS)
.build();
mTestImsService.testFeatureConfig = config;
diff --git a/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java b/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
index ccbc251..6be2fb0 100644
--- a/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
+++ b/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
@@ -17,14 +17,19 @@
package android.telephony.ims;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import android.os.RemoteException;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
import android.support.test.runner.AndroidJUnit4;
+import android.telecom.TelecomManager;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
@@ -32,6 +37,7 @@
import android.test.suitebuilder.annotation.SmallTest;
import com.android.ims.internal.IImsCallSession;
+import com.android.internal.telephony.ims.ImsTestBase;
import org.junit.After;
import org.junit.Before;
@@ -41,23 +47,55 @@
import org.mockito.Mockito;
@RunWith(AndroidJUnit4.class)
-public class MmTelFeatureTests {
+public class MmTelFeatureTests extends ImsTestBase {
private static final int TEST_CAPABILITY = 1;
private static final int TEST_RADIO_TECH = 0;
+ private static final int TEST_TTY_RESULT = 0;
+ private static final int TEST_SEND_DTMF_RESULT = 1;
+ private static final int TEST_RESULT_MAX = 2;
+
+ private static final int TEST_RESULT_DELAY_MS = 5000;
+
private android.telephony.ims.TestMmTelFeature mFeature;
private IImsMmTelFeature mFeatureBinder;
private ImsFeature.CapabilityCallback mCapabilityCallback;
private MmTelFeature.Listener mListener;
+ // set to true when the handler receives a message back from the Feature.
+ private boolean[] mHandlerResults;
+
+ private class TestHandler extends Handler {
+
+ TestHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case TEST_TTY_RESULT:
+ mHandlerResults[TEST_TTY_RESULT] = true;
+ break;
+ case TEST_SEND_DTMF_RESULT:
+ mHandlerResults[TEST_SEND_DTMF_RESULT] = true;
+ break;
+ }
+ }
+ }
+ private final Handler mHandler = new TestHandler(Looper.getMainLooper());
+ private final Messenger mHandlerMessenger = new Messenger(mHandler);
+
@Before
- public void setup() throws RemoteException {
+ public void setup() throws Exception {
+ super.setUp();
mFeature = new TestMmTelFeature();
mFeatureBinder = mFeature.getBinder();
mCapabilityCallback = spy(new ImsFeature.CapabilityCallback());
mListener = spy(new MmTelFeature.Listener());
mFeatureBinder.setListener(mListener);
+ mHandlerResults = new boolean[TEST_RESULT_MAX];
}
@After
@@ -91,4 +129,25 @@
assertEquals(sessionBinder, captor.getValue());
}
+
+ @SmallTest
+ @Test
+ public void testSetTtyMessageMessenger() throws Exception {
+ Message resultMessage = Message.obtain(mHandler, TEST_TTY_RESULT);
+ resultMessage.replyTo = mHandlerMessenger;
+ mFeatureBinder.setUiTtyMode(TelecomManager.TTY_MODE_FULL, resultMessage);
+ waitForHandlerAction(mHandler, TEST_RESULT_DELAY_MS);
+ assertTrue(mHandlerResults[TEST_TTY_RESULT]);
+ }
+
+ @SmallTest
+ @Test
+ public void testSendDtmfMessageMessenger() throws Exception {
+ Message resultMessage = Message.obtain(mHandler, TEST_SEND_DTMF_RESULT);
+ resultMessage.replyTo = mHandlerMessenger;
+ IImsCallSession callSession = mFeatureBinder.createCallSession(null);
+ callSession.sendDtmf('0', resultMessage);
+ waitForHandlerAction(mHandler, TEST_RESULT_DELAY_MS);
+ assertTrue(mHandlerResults[TEST_SEND_DTMF_RESULT]);
+ }
}
diff --git a/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java b/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
index 795d1c0..6414403 100644
--- a/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
+++ b/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
@@ -16,6 +16,7 @@
package android.telephony.ims;
+import android.os.Message;
import android.os.RemoteException;
import android.telephony.ims.feature.CapabilityChangeRequest;
import android.telephony.ims.feature.ImsFeature;
@@ -33,6 +34,22 @@
public CapabilityChangeRequest lastRequest;
public boolean isUtInterfaceCalled = false;
+ private final TestImsCallSession mCallSession = new TestImsCallSession();
+ private class TestImsCallSession extends ImsCallSessionImplBase {
+
+ @Override
+ public void sendDtmf(char c, Message result) {
+ // Just call result to signify complete for test.
+ if (result.replyTo != null) {
+ try {
+ result.replyTo.send(result);
+ } catch (RemoteException e) {
+ // eat error, test will fail.
+ }
+ }
+ }
+ }
+
public void incomingCall(ImsCallSessionImplBase c) throws RemoteException {
notifyIncomingCall(c, null);
}
@@ -44,7 +61,7 @@
@Override
public ImsCallSessionImplBase createCallSession(ImsCallProfile profile) {
- return super.createCallSession(profile);
+ return mCallSession;
}
@Override
@@ -64,6 +81,16 @@
}
@Override
+ public void setUiTtyMode(int mode, Message onCompleteMessage) {
+ try {
+ // just send complete message.
+ onCompleteMessage.replyTo.send(onCompleteMessage);
+ } catch (RemoteException e) {
+ // do nothing, test will fail.
+ }
+ }
+
+ @Override
public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
// Base implementation - Override to provide functionality
diff --git a/tests/telephonytests/src/android/telephony/mbms/MbmsReceiverTest.java b/tests/telephonytests/src/android/telephony/mbms/MbmsReceiverTest.java
new file mode 100644
index 0000000..46cf4f7
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/mbms/MbmsReceiverTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 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 android.telephony.mbms;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(AndroidJUnit4.class)
+public class MbmsReceiverTest {
+ @Test
+ @SmallTest
+ public void testFileHierarchyRecreation() throws Exception {
+ String rootPath = "http://www.example.com/files/";
+ assertEquals("subdir1/file.txt",
+ MbmsDownloadReceiver.getFileRelativePath(rootPath, rootPath + "/subdir1/file.txt"));
+ assertEquals("subdir1/subdir2/file.txt",
+ MbmsDownloadReceiver.getFileRelativePath(
+ rootPath,
+ rootPath + "/subdir1/subdir2/file.txt"));
+ assertEquals("file.txt",
+ MbmsDownloadReceiver.getFileRelativePath(
+ rootPath + "/subdir1/file.*",
+ rootPath + "/subdir1/file.txt"));
+ assertEquals("file.txt",
+ MbmsDownloadReceiver.getFileRelativePath(
+ rootPath + "/subdir1/*",
+ rootPath + "/subdir1/file.txt"));
+ assertEquals("subdir1/file.txt",
+ MbmsDownloadReceiver.getFileRelativePath(
+ rootPath + "/subdir*",
+ rootPath + "/subdir1/file.txt"));
+ assertEquals("file.txt",
+ MbmsDownloadReceiver.getFileRelativePath(
+ rootPath,
+ rootPath + "/file.txt"));
+ assertEquals("file.txt",
+ MbmsDownloadReceiver.getFileRelativePath(
+ rootPath + "/*",
+ rootPath + "/file.txt"));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
index 4b99cbe..623cf13 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
@@ -45,6 +45,7 @@
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;
@@ -124,7 +125,7 @@
/**
* Checks if the expiration date is calculated correctly
- * In this case the expiration date should be the expiration date of the key.
+ * In this case the expiration date should be within the window (7 to 21 days).
**/
@Test
@SmallTest
@@ -133,16 +134,20 @@
mCarrierKeyDM.mKeyAvailability = 3;
SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd");
Calendar cal = new GregorianCalendar();
- cal.add(Calendar.DATE, 10);
+ cal.add(Calendar.DATE, 30);
Date date = cal.getTime();
- Calendar expectedCal = new GregorianCalendar();
- expectedCal.add(Calendar.DATE, 3);
- String dateExpected = dt.format(expectedCal.getTime());
+ Calendar minExpirationCal = new GregorianCalendar();
+ Calendar maxExpirationCal = new GregorianCalendar();
+ minExpirationCal.add(Calendar.DATE, 23);
+ maxExpirationCal.add(Calendar.DATE, 9);
+ Date minExpirationDate = minExpirationCal.getTime();
+ Date maxExpirationDate = maxExpirationCal.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));
+ assertTrue(expirationDate.before(minExpirationDate));
+ assertTrue(expirationDate.after(maxExpirationDate));
}
/**
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
index 8be2f60..59d8872 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
@@ -16,22 +16,10 @@
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 com.android.internal.telephony.TelephonyTestUtils.waitForMs;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.isA;
@@ -39,6 +27,28 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
+import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Map;
+
/**
* Unit tests for {@link com.android.internal.telephony.CarrierServiceStateTracker}.
*/
@@ -47,25 +57,34 @@
public static final String LOG_TAG = "CSST";
public static final int TEST_TIMEOUT = 5000;
+ private CarrierServiceStateTracker mSpyCarrierSST;
private CarrierServiceStateTracker mCarrierSST;
private CarrierServiceStateTrackerTestHandler mCarrierServiceStateTrackerTestHandler;
- private CarrierServiceStateTracker.PrefNetworkNotification mPrefNetworkNotification;
- private CarrierServiceStateTracker.EmergencyNetworkNotification mEmergencyNetworkNotification;
+ private FakeContentResolver mFakeContentResolver;
- @Mock Context mContext;
- @Mock ServiceStateTracker mServiceStateTracker;
- @Mock NotificationManager mNotificationManager;
- @Mock Resources mResources;
+ NotificationManager mNotificationManager;
+ PersistableBundle mBundle;
+
+ private class FakeContentResolver extends MockContentResolver {
+ @Override
+ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+ super.notifyChange(uri, observer, syncToNetwork);
+ logd("onChanged(uri=" + uri + ")" + observer);
+ if (observer != null) {
+ observer.dispatchChange(false, uri);
+ }
+ }
+ }
private class CarrierServiceStateTrackerTestHandler extends HandlerThread {
-
private CarrierServiceStateTrackerTestHandler(String name) {
super(name);
}
@Override
public void onLooperPrepared() {
- mCarrierSST = spy(new CarrierServiceStateTracker(mPhone, mServiceStateTracker));
+ mCarrierSST = new CarrierServiceStateTracker(mPhone, mSST);
+ mSpyCarrierSST = spy(mCarrierSST);
setReady(true);
}
}
@@ -75,15 +94,29 @@
MockitoAnnotations.initMocks(this);
logd(LOG_TAG + "Setup!");
super.setUp(getClass().getSimpleName());
+ mBundle = mContextFixture.getCarrierConfigBundle();
+ when(mPhone.getSubId()).thenReturn(1);
mCarrierServiceStateTrackerTestHandler =
new CarrierServiceStateTrackerTestHandler(getClass().getSimpleName());
mCarrierServiceStateTrackerTestHandler.start();
- when(mContext.getResources()).thenReturn(mResources);
- when(mContext.getPackageManager()).thenReturn(mPackageManager);
- when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+ mFakeContentResolver = new CarrierServiceStateTrackerTest.FakeContentResolver();
+
+ when(mPhone.getContext().getContentResolver()).thenReturn(mFakeContentResolver);
+
+ mNotificationManager = (NotificationManager) mContext.getSystemService(
+ Context.NOTIFICATION_SERVICE);
+
+ setDefaultValues();
waitUntilReady();
}
+ private void setDefaultValues() {
+ mBundle.putInt(CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT,
+ 0);
+ mBundle.putInt(CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT,
+ 0);
+ }
+
@After
public void tearDown() throws Exception {
mCarrierServiceStateTrackerTestHandler.quit();
@@ -94,12 +127,12 @@
@SmallTest
public void testCancelBothNotifications() {
logd(LOG_TAG + ":testCancelBothNotifications()");
- Message notificationMsg = mCarrierSST.obtainMessage(
+ Message notificationMsg = mSpyCarrierSST.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);
+ doReturn(false).when(mSpyCarrierSST).evaluateSendingMessage(any());
+ doReturn(mNotificationManager).when(mSpyCarrierSST).getNotificationManager(any());
+ mSpyCarrierSST.handleMessage(notificationMsg);
+ waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
verify(mNotificationManager).cancel(
CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK);
verify(mNotificationManager).cancel(
@@ -111,18 +144,95 @@
public void testSendBothNotifications() {
logd(LOG_TAG + ":testSendBothNotifications()");
Notification.Builder mNotificationBuilder = new Notification.Builder(mContext);
- Message notificationMsg = mCarrierSST.obtainMessage(
+ Message notificationMsg = mSpyCarrierSST.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);
+ doReturn(true).when(mSpyCarrierSST).evaluateSendingMessage(any());
+ doReturn(false).when(mSpyCarrierSST).isRadioOffOrAirplaneMode();
+ doReturn(0).when(mSpyCarrierSST).getDelay(any());
+ doReturn(mNotificationBuilder).when(mSpyCarrierSST).getNotificationBuilder(any());
+ doReturn(mNotificationManager).when(mSpyCarrierSST).getNotificationManager(any());
+ mSpyCarrierSST.handleMessage(notificationMsg);
+ waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
verify(mNotificationManager).notify(
eq(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK), isA(Notification.class));
verify(mNotificationManager).notify(
eq(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK), any());
}
+
+ @Test
+ @SmallTest
+ public void testSendPrefNetworkNotification() {
+ logd(LOG_TAG + ":testSendPrefNetworkNotification()");
+ Intent intent = new Intent().setAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ mContext.sendBroadcast(intent);
+ waitForMs(300);
+
+ Map<Integer, CarrierServiceStateTracker.NotificationType> notificationTypeMap =
+ mCarrierSST.getNotificationTypeMap();
+ CarrierServiceStateTracker.NotificationType prefNetworkNotification =
+ notificationTypeMap.get(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK);
+ CarrierServiceStateTracker.NotificationType spyPrefNetworkNotification = spy(
+ prefNetworkNotification);
+ notificationTypeMap.put(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK,
+ spyPrefNetworkNotification);
+ Notification.Builder mNotificationBuilder = new Notification.Builder(mContext);
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST.mSS).getVoiceRegState();
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST.mSS).getDataRegState();
+ doReturn(true).when(mSST).isRadioOn();
+ doReturn(mNotificationBuilder).when(spyPrefNetworkNotification).getNotificationBuilder();
+
+ String prefNetworkMode = Settings.Global.PREFERRED_NETWORK_MODE + mPhone.getSubId();
+ Settings.Global.putInt(mFakeContentResolver, prefNetworkMode,
+ RILConstants.NETWORK_MODE_LTE_CDMA_EVDO);
+ mFakeContentResolver.notifyChange(
+ Settings.Global.getUriFor(prefNetworkMode), mSpyCarrierSST.getContentObserver());
+ waitForMs(500);
+ verify(mNotificationManager).notify(
+ eq(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK), isA(Notification.class));
+
+ Settings.Global.putInt(mFakeContentResolver, prefNetworkMode,
+ RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
+ mFakeContentResolver.notifyChange(
+ Settings.Global.getUriFor(prefNetworkMode), mSpyCarrierSST.getContentObserver());
+ waitForMs(500);
+ verify(mNotificationManager, atLeast(1)).cancel(
+ CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendEmergencyNetworkNotification() {
+ logd(LOG_TAG + ":testSendEmergencyNetworkNotification()");
+ Intent intent = new Intent().setAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ mContext.sendBroadcast(intent);
+ waitForMs(300);
+
+ Map<Integer, CarrierServiceStateTracker.NotificationType> notificationTypeMap =
+ mCarrierSST.getNotificationTypeMap();
+ CarrierServiceStateTracker.NotificationType emergencyNetworkNotification =
+ notificationTypeMap.get(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK);
+ CarrierServiceStateTracker.NotificationType spyEmergencyNetworkNotification = spy(
+ emergencyNetworkNotification);
+ notificationTypeMap.put(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK,
+ spyEmergencyNetworkNotification);
+ Notification.Builder mNotificationBuilder = new Notification.Builder(mContext);
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST.mSS).getVoiceRegState();
+ doReturn(mNotificationBuilder).when(spyEmergencyNetworkNotification)
+ .getNotificationBuilder();
+
+ doReturn(true).when(mPhone).isWifiCallingEnabled();
+ Message notificationMsg = mSpyCarrierSST.obtainMessage(
+ CarrierServiceStateTracker.CARRIER_EVENT_IMS_CAPABILITIES_CHANGED, null);
+ mSpyCarrierSST.handleMessage(notificationMsg);
+ waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
+ verify(mNotificationManager).notify(
+ eq(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK),
+ isA(Notification.class));
+
+ doReturn(false).when(mPhone).isWifiCallingEnabled();
+ mSpyCarrierSST.handleMessage(notificationMsg);
+ waitForHandlerAction(mSpyCarrierSST, TEST_TIMEOUT);
+ verify(mNotificationManager, atLeast(2)).cancel(
+ CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityGsmTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityGsmTest.java
index e826709..cb003b7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityGsmTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityGsmTest.java
@@ -51,6 +51,7 @@
assertEquals(LAC, ci.getLac());
assertEquals(CID, ci.getCid());
assertEquals(ARFCN, ci.getArfcn());
+ assertEquals(ARFCN, ci.getChannelNumber());
assertEquals(BSIC, ci.getBsic());
assertEquals(MCC, ci.getMcc());
assertEquals(MNC, ci.getMnc());
@@ -180,12 +181,15 @@
@SmallTest
public void testParcelWithUnknowMccMnc() {
- CellIdentityGsm ci = new CellIdentityGsm(LAC, CID, ARFCN, BSIC, null, null, null, null);
+ CellIdentityGsm ci =
+ new CellIdentityGsm(LAC, CID, ARFCN, BSIC, null, null, ALPHA_LONG, ALPHA_SHORT);
Parcel p = Parcel.obtain();
p.writeInt(CellIdentityGsm.TYPE_GSM);
p.writeString(String.valueOf(Integer.MAX_VALUE));
p.writeString(String.valueOf(Integer.MAX_VALUE));
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
p.writeInt(LAC);
p.writeInt(CID);
p.writeInt(ARFCN);
@@ -200,12 +204,15 @@
public void testParcelWithInvalidMccMnc() {
final String invalidMcc = "randomStuff";
final String invalidMnc = "randomStuff";
- CellIdentityGsm ci = new CellIdentityGsm(LAC, CID, ARFCN, BSIC, null, null, null, null);
+ CellIdentityGsm ci =
+ new CellIdentityGsm(LAC, CID, ARFCN, BSIC, null, null, ALPHA_LONG, ALPHA_SHORT);
Parcel p = Parcel.obtain();
p.writeInt(CellIdentity.TYPE_GSM);
p.writeString(invalidMcc);
p.writeString(invalidMnc);
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
p.writeInt(LAC);
p.writeInt(CID);
p.writeInt(ARFCN);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java
index 56575ca..bbd9078 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java
@@ -52,6 +52,7 @@
assertEquals(PCI, ci.getPci());
assertEquals(TAC, ci.getTac());
assertEquals(EARFCN, ci.getEarfcn());
+ assertEquals(EARFCN, ci.getChannelNumber());
assertEquals(BANDWIDTH, ci.getBandwidth());
assertEquals(MCC, ci.getMcc());
assertEquals(MNC, ci.getMnc());
@@ -189,12 +190,14 @@
@SmallTest
public void testParcelWithUnknownMccMnc() {
CellIdentityLte ci = new CellIdentityLte(
- CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, null, null);
+ CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, ALPHA_LONG, ALPHA_SHORT);
Parcel p = Parcel.obtain();
p.writeInt(CellIdentity.TYPE_LTE);
p.writeString(String.valueOf(Integer.MAX_VALUE));
p.writeString(String.valueOf(Integer.MAX_VALUE));
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
p.writeInt(CI);
p.writeInt(PCI);
p.writeInt(TAC);
@@ -211,12 +214,14 @@
final String invalidMcc = "randomStuff";
final String invalidMnc = "randomStuff";
CellIdentityLte ci = new CellIdentityLte(
- CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, null, null);
+ CI, PCI, TAC, EARFCN, BANDWIDTH, null, null, ALPHA_LONG, ALPHA_SHORT);
Parcel p = Parcel.obtain();
p.writeInt(CellIdentity.TYPE_LTE);
p.writeString(invalidMcc);
p.writeString(invalidMnc);
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
p.writeInt(CI);
p.writeInt(PCI);
p.writeInt(TAC);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTdscdmaTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTdscdmaTest.java
index 949d99b..5915062 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTdscdmaTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityTdscdmaTest.java
@@ -33,7 +33,7 @@
// Tracking area code ranges from 0 to 65535.
private static final int TAC = 65535;
// Absolute RF Channel Number ranges from 0 to 262140.
- private static final int EARFCN = 262140;
+ private static final int UARFCN = 262140;
private static final int MCC = 120;
private static final int MNC = 260;
private static final String MCC_STR = "120";
@@ -52,33 +52,38 @@
@SmallTest
public void testDefaultConstructor() {
CellIdentityTdscdma ci =
- new CellIdentityTdscdma(MCC_STR, MNC_STR, LAC, CID, CPID);
+ new CellIdentityTdscdma(
+ MCC_STR, MNC_STR, LAC, CID, CPID, UARFCN, ALPHA_LONG, ALPHA_SHORT);
assertEquals(MCC_STR, ci.getMccString());
assertEquals(MNC_STR, ci.getMncString());
assertEquals(LAC, ci.getLac());
assertEquals(CID, ci.getCid());
assertEquals(CPID, ci.getCpid());
+ assertEquals(UARFCN, ci.getChannelNumber());
+ assertEquals(ALPHA_LONG, ci.getOperatorAlphaLong());
+ assertEquals(ALPHA_SHORT, ci.getOperatorAlphaShort());
}
@SmallTest
public void testConstructorWithEmptyMccMnc() {
- CellIdentityTdscdma ci = new CellIdentityTdscdma(null, null, LAC, CID, CPID);
+ CellIdentityTdscdma ci = new CellIdentityTdscdma(
+ null, null, LAC, CID, CPID, UARFCN, "", "");
assertNull(ci.getMccString());
assertNull(ci.getMncString());
- ci = new CellIdentityTdscdma(MCC_STR, null, LAC, CID, CPID);
+ ci = new CellIdentityTdscdma(MCC_STR, null, LAC, CID, CPID, UARFCN, "", "");
assertEquals(MCC_STR, ci.getMccString());
assertNull(ci.getMncString());
- ci = new CellIdentityTdscdma(null, MNC_STR, LAC, CID, CPID);
+ ci = new CellIdentityTdscdma(null, MNC_STR, LAC, CID, CPID, UARFCN, "", "");
assertEquals(MNC_STR, ci.getMncString());
assertNull(ci.getMccString());
- ci = new CellIdentityTdscdma("", "", LAC, CID, CPID);
+ ci = new CellIdentityTdscdma("", "", LAC, CID, CPID, UARFCN, "", "");
assertNull(ci.getMccString());
assertNull(ci.getMncString());
@@ -86,7 +91,8 @@
@SmallTest
public void testParcel() {
- CellIdentityTdscdma ci = new CellIdentityTdscdma(MCC_STR, MNC_STR, LAC, CID, CPID);
+ CellIdentityTdscdma ci = new CellIdentityTdscdma(
+ MCC_STR, MNC_STR, LAC, CID, UARFCN, CPID, ALPHA_LONG, ALPHA_SHORT);
Parcel p = Parcel.obtain();
ci.writeToParcel(p, 0);
@@ -98,15 +104,20 @@
@SmallTest
public void testParcelWithUnknowMccMnc() {
- CellIdentityTdscdma ci = new CellIdentityTdscdma(null, null, LAC, CID, CPID);
+ CellIdentityTdscdma ci =
+ new CellIdentityTdscdma(
+ null, null, LAC, CID, CPID, UARFCN, ALPHA_LONG, ALPHA_SHORT);
Parcel p = Parcel.obtain();
p.writeInt(CellIdentity.TYPE_TDSCDMA);
p.writeString(String.valueOf(Integer.MAX_VALUE));
p.writeString(String.valueOf(Integer.MAX_VALUE));
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
p.writeInt(LAC);
p.writeInt(CID);
p.writeInt(CPID);
+ p.writeInt(UARFCN);
p.setDataPosition(0);
CellIdentityTdscdma newCi = CellIdentityTdscdma.CREATOR.createFromParcel(p);
@@ -117,15 +128,20 @@
public void testParcelWithInvalidMccMnc() {
final String invalidMcc = "randomStuff";
final String invalidMnc = "randomStuff";
- CellIdentityTdscdma ci = new CellIdentityTdscdma(null, null, LAC, CID, CPID);
+ CellIdentityTdscdma ci =
+ new CellIdentityTdscdma(
+ null, null, LAC, CID, CPID, UARFCN, ALPHA_LONG, ALPHA_SHORT);
Parcel p = Parcel.obtain();
p.writeInt(CellIdentity.TYPE_TDSCDMA);
p.writeString(invalidMcc);
p.writeString(invalidMnc);
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
p.writeInt(LAC);
p.writeInt(CID);
p.writeInt(CPID);
+ p.writeInt(UARFCN);
p.setDataPosition(0);
CellIdentityTdscdma newCi = CellIdentityTdscdma.CREATOR.createFromParcel(p);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityWcdmaTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityWcdmaTest.java
index f5065d9..d0e30b4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityWcdmaTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityWcdmaTest.java
@@ -50,6 +50,8 @@
assertEquals(LAC, ci.getLac());
assertEquals(CID, ci.getCid());
assertEquals(PSC, ci.getPsc());
+ assertEquals(UARFCN, ci.getUarfcn());
+ assertEquals(UARFCN, ci.getChannelNumber());
assertEquals(MCC, ci.getMcc());
assertEquals(MNC, ci.getMnc());
assertEquals(MCC_STR, ci.getMccString());
@@ -177,12 +179,15 @@
@SmallTest
public void testParcelWithUnknowMccMnc() {
- CellIdentityWcdma ci = new CellIdentityWcdma(LAC, CID, PSC, UARFCN, null, null, null, null);
+ CellIdentityWcdma ci =
+ new CellIdentityWcdma(LAC, CID, PSC, UARFCN, null, null, ALPHA_LONG, ALPHA_SHORT);
Parcel p = Parcel.obtain();
p.writeInt(CellIdentity.TYPE_WCDMA);
p.writeString(String.valueOf(Integer.MAX_VALUE));
p.writeString(String.valueOf(Integer.MAX_VALUE));
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
p.writeInt(LAC);
p.writeInt(CID);
p.writeInt(PSC);
@@ -197,12 +202,15 @@
public void testParcelWithInvalidMccMnc() {
final String invalidMcc = "randomStuff";
final String invalidMnc = "randomStuff";
- CellIdentityWcdma ci = new CellIdentityWcdma(LAC, CID, PSC, UARFCN, null, null, null, null);
+ CellIdentityWcdma ci =
+ new CellIdentityWcdma(LAC, CID, PSC, UARFCN, null, null, ALPHA_LONG, ALPHA_SHORT);
Parcel p = Parcel.obtain();
p.writeInt(CellIdentity.TYPE_WCDMA);
p.writeString(invalidMcc);
p.writeString(invalidMnc);
+ p.writeString(ALPHA_LONG);
+ p.writeString(ALPHA_SHORT);
p.writeInt(LAC);
p.writeInt(CID);
p.writeInt(PSC);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
index 16bc535..a595c36 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
@@ -54,7 +54,7 @@
mCellularNetworkService = new CellularNetworkService();
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.packageName = "com.android.phone";
- serviceInfo.permission = "android.permission.BIND_NETWORK_SERVICE";
+ serviceInfo.permission = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
IntentFilter filter = new IntentFilter();
mContextFixture.addService(
NetworkService.NETWORK_SERVICE_INTERFACE,
@@ -132,7 +132,7 @@
waitForMs(1000);
NetworkRegistrationState expectedState = new NetworkRegistrationState(
- AccessNetworkConstants.TransportType.WWAN, domain, voiceRegState,
+ domain, AccessNetworkConstants.TransportType.WWAN, voiceRegState,
ServiceState.rilRadioTechnologyToNetworkType(voiceRadioTech), reasonForDenial,
false, availableServices, null, cssSupported,
roamingIndicator, systemIsInPrl, defaultRoamingIndicator);
@@ -155,7 +155,7 @@
waitForMs(1000);
expectedState = new NetworkRegistrationState(
- AccessNetworkConstants.TransportType.WWAN, domain, voiceRegState,
+ domain, AccessNetworkConstants.TransportType.WWAN, voiceRegState,
ServiceState.rilRadioTechnologyToNetworkType(voiceRadioTech), reasonForDenial,
false, availableServices, null, maxDataCalls);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index c274dea..d2e28ca 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -40,9 +40,11 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
@@ -267,11 +269,21 @@
}
@Override
+ public AssetManager getAssets() {
+ return mAssetManager;
+ }
+
+ @Override
public Resources getResources() {
return mResources;
}
@Override
+ public ApplicationInfo getApplicationInfo() {
+ return mApplicationInfo;
+ }
+
+ @Override
public String getOpPackageName() {
return "com.android.internal.telephony";
}
@@ -282,6 +294,11 @@
}
@Override
+ public Resources.Theme getTheme() {
+ return null;
+ }
+
+ @Override
public void unregisterReceiver(BroadcastReceiver receiver) {
}
@@ -515,6 +532,7 @@
// when(...) logic to be used to add specific little responses where needed.
private final Resources mResources = mock(Resources.class);
+ private final ApplicationInfo mApplicationInfo = mock(ApplicationInfo.class);
private final PackageManager mPackageManager = mock(PackageManager.class);
private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
private final DownloadManager mDownloadManager = mock(DownloadManager.class);
@@ -524,6 +542,7 @@
private final CarrierConfigManager mCarrierConfigManager = mock(CarrierConfigManager.class);
private final SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
private final AlarmManager mAlarmManager = mock(AlarmManager.class);
+ private final AssetManager mAssetManager = new AssetManager();
private final ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
private final UsageStatsManager mUsageStatManager = null;
private final WifiManager mWifiManager = mock(WifiManager.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index fadfaf2..77eb7dc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -31,6 +31,7 @@
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -57,8 +58,11 @@
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.test.SimulatedCommands;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus;
import com.android.internal.telephony.uicc.IccException;
import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.UiccProfile;
+import com.android.internal.telephony.uicc.UiccSlot;
import org.junit.After;
import org.junit.Before;
@@ -79,6 +83,7 @@
private static final int EVENT_EMERGENCY_CALLBACK_MODE_EXIT = 1;
private static final int EVENT_EMERGENCY_CALL_TOGGLE = 2;
+ private static final int EVENT_SET_ICC_LOCK_ENABLED = 3;
private class GsmCdmaPhoneTestHandler extends HandlerThread {
@@ -398,7 +403,7 @@
// voicemail number from sharedPreference
mPhoneUT.setVoiceMailNumber("alphaTag", voiceMailNumber, null);
ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
- verify(mRuimRecords).setVoiceMailNumber(eq("alphaTag"), eq(voiceMailNumber),
+ verify(mSimRecords).setVoiceMailNumber(eq("alphaTag"), eq(voiceMailNumber),
messageArgumentCaptor.capture());
Message msg = messageArgumentCaptor.getValue();
@@ -475,8 +480,8 @@
doReturn(imsi).when(mSimRecords).getIMSI();
mPhoneUT.getCallForwardingOption(CF_REASON_UNCONDITIONAL, null);
verify(mSimulatedCommandsVerifier).queryCallForwardStatus(
- eq(CF_REASON_UNCONDITIONAL), anyInt(), nullable(String.class),
- nullable(Message.class));
+ eq(CF_REASON_UNCONDITIONAL), eq(CommandsInterface.SERVICE_CLASS_VOICE),
+ nullable(String.class), nullable(Message.class));
waitForMs(50);
verify(mSimRecords).setVoiceCallForwardingFlag(anyInt(), anyBoolean(),
nullable(String.class));
@@ -812,4 +817,60 @@
waitForMs(100);
verify(mEriManager, times(1)).loadEriFile();
}
+
+ @Test
+ @SmallTest
+ public void testGetIccCardUnknownAndAbsent() {
+ // If UiccSlot.isStateUnknown is true, we should return a dummy IccCard with the state
+ // set to UNKNOWN
+ doReturn(null).when(mUiccController).getUiccProfileForPhone(anyInt());
+ UiccSlot mockSlot = mock(UiccSlot.class);
+ doReturn(mockSlot).when(mUiccController).getUiccSlotForPhone(anyInt());
+ doReturn(true).when(mockSlot).isStateUnknown();
+
+ IccCard iccCard = mPhoneUT.getIccCard();
+ assertEquals(IccCardConstants.State.UNKNOWN, iccCard.getState());
+
+ // if isStateUnknown is false, we should return a dummy IccCard with the state set to
+ // ABSENT
+ doReturn(false).when(mockSlot).isStateUnknown();
+ iccCard = mPhoneUT.getIccCard();
+ assertEquals(IccCardConstants.State.ABSENT, iccCard.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetEmptyIccCard() {
+ doReturn(null).when(mUiccController).getUiccProfileForPhone(anyInt());
+
+ IccCard iccCard = mPhoneUT.getIccCard();
+
+ // The iccCard should be a dummy object, not null.
+ assertTrue(!(iccCard instanceof UiccProfile));
+
+ assertTrue(iccCard != null);
+ assertEquals(IccCardConstants.State.UNKNOWN, iccCard.getState());
+ assertEquals(null, iccCard.getIccRecords());
+ assertEquals(false, iccCard.getIccLockEnabled());
+ assertEquals(false, iccCard.getIccFdnEnabled());
+ assertEquals(false, iccCard.isApplicationOnIcc(
+ IccCardApplicationStatus.AppType.APPTYPE_SIM));
+ assertEquals(false, iccCard.hasIccCard());
+ assertEquals(false, iccCard.getIccPin2Blocked());
+ assertEquals(false, iccCard.getIccPuk2Blocked());
+
+ Message onComplete = mTestHandler.obtainMessage(EVENT_SET_ICC_LOCK_ENABLED);
+ iccCard.setIccLockEnabled(true, "password", onComplete);
+
+ waitForMs(100);
+
+ ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+ // Verify that message is sent back with exception.
+ verify(mTestHandler, times(1)).sendMessageAtTime(messageArgumentCaptor.capture(),
+ anyLong());
+ Message message = messageArgumentCaptor.getAllValues().get(0);
+ AsyncResult ret = (AsyncResult) message.obj;
+ assertEquals(EVENT_SET_ICC_LOCK_ENABLED, message.what);
+ assertTrue(ret.exception != null);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java
new file mode 100644
index 0000000..3bc2c1b
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2018 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 org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.UserManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class IccSmsInterfaceManagerTest {
+ private static final String PACKAGE = "com.example.package";
+ private static final String MESSAGE = "msg";
+
+ private HandlerThread mHandlerThread;
+
+ @Mock
+ private Phone mMockPhone;
+ @Mock
+ private Context mMockContext;
+ @Mock
+ private AppOpsManager mMockAppOps;
+ @Mock
+ private UserManager mMockUserManager;
+ @Mock
+ private SmsDispatchersController mMockDispatchersController;
+
+ private IccSmsInterfaceManager mIccSmsInterfaceManager;
+
+ private boolean mCallerHasCarrierPrivileges;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mHandlerThread = new HandlerThread("IccSmsInterfaceManagerTest");
+ mHandlerThread.start();
+ final CountDownLatch initialized = new CountDownLatch(1);
+ new Handler(mHandlerThread.getLooper()).post(() -> {
+ mIccSmsInterfaceManager = new IccSmsInterfaceManager(
+ mMockPhone, mMockContext, mMockAppOps, mMockUserManager,
+ mMockDispatchersController) {
+ @Override
+ public void enforceCallerIsImsAppOrCarrierApp(String message) {
+ if (!mCallerHasCarrierPrivileges) {
+ throw new SecurityException(message);
+ }
+ }
+ };
+ initialized.countDown();
+ });
+ // Wait for object to initialize.
+ if (!initialized.await(30, TimeUnit.SECONDS)) {
+ fail("Could not initialize IccSmsInterfaceManager");
+ }
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mHandlerThread.quit();
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_persist_grant() {
+ assertTrue(mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ true /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE));
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_persist_noGrant() {
+ Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext)
+ .enforceCallingPermission(Manifest.permission.SEND_SMS, MESSAGE);
+ try {
+ mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ true /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE);
+ fail();
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_persist_noAppOps() {
+ Mockito.when(mMockAppOps.noteOp(
+ AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), PACKAGE))
+ .thenReturn(AppOpsManager.MODE_ERRORED);
+ assertFalse(mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ true /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE));
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_noPersist_grantViaCarrierApp() {
+ mCallerHasCarrierPrivileges = true;
+ // Other permissions shouldn't matter.
+ Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext)
+ .enforceCallingPermission(Manifest.permission.MODIFY_PHONE_STATE, MESSAGE);
+ Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext)
+ .enforceCallingPermission(Manifest.permission.SEND_SMS, MESSAGE);
+ Mockito.when(mMockAppOps.noteOp(
+ AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), PACKAGE))
+ .thenReturn(AppOpsManager.MODE_ERRORED);
+
+ assertTrue(mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE));
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_noPersist_grantViaModifyAndSend() {
+ assertTrue(mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE));
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_noPersist_noModify() {
+ Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext)
+ .enforceCallingPermission(Manifest.permission.MODIFY_PHONE_STATE, MESSAGE);
+ try {
+ mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE);
+ fail();
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_noPersist_noSendSmsPermission() {
+ Mockito.doThrow(new SecurityException(MESSAGE)).when(mMockContext)
+ .enforceCallingPermission(Manifest.permission.SEND_SMS, MESSAGE);
+ try {
+ mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE);
+ fail();
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCheckCallingSendTextPermissions_noPersist_noAppOps() {
+ Mockito.when(mMockAppOps.noteOp(
+ AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), PACKAGE))
+ .thenReturn(AppOpsManager.MODE_ERRORED);
+ assertFalse(mIccSmsInterfaceManager.checkCallingSendTextPermissions(
+ false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
new file mode 100644
index 0000000..bcc1730
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2018 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 com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.os.AsyncResult;
+import android.os.HandlerThread;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellInfoGsm;
+import android.telephony.ServiceState;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class LocaleTrackerTest extends TelephonyTest {
+
+ private static final String US_MCC = "310";
+ private static final String FAKE_MNC = "123";
+ private static final String US_COUNTRY_CODE = "us";
+ private static final String COUNTRY_CODE_UNAVAILABLE = "";
+
+ private LocaleTracker mLocaleTracker;
+ private LocaleTrackerTestHandler mLocaleTrackerTestHandler;
+
+ private CellInfoGsm mCellInfo;
+ private WifiManager mWifiManager;
+
+ private class LocaleTrackerTestHandler extends HandlerThread {
+
+ private LocaleTrackerTestHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mLocaleTracker = new LocaleTracker(mPhone, this.getLooper());
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ logd("LocaleTrackerTest +Setup!");
+ super.setUp(getClass().getSimpleName());
+
+ // This is a workaround to bypass setting system properties, which causes access violation.
+ doReturn(-1).when(mPhone).getPhoneId();
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+
+ mCellInfo = new CellInfoGsm();
+ mCellInfo.setCellIdentity(new CellIdentityGsm(Integer.parseInt(US_MCC),
+ Integer.parseInt(FAKE_MNC), 0, 0));
+ doReturn(Arrays.asList(mCellInfo)).when(mPhone).getAllCellInfo(isNull());
+ doReturn(true).when(mSST).getDesiredPowerState();
+
+ mLocaleTrackerTestHandler = new LocaleTrackerTestHandler(getClass().getSimpleName());
+ mLocaleTrackerTestHandler.start();
+ waitUntilReady();
+ logd("LocaleTrackerTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mLocaleTracker.removeCallbacksAndMessages(null);
+ mLocaleTrackerTestHandler.quit();
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateOperatorNumericSync() throws Exception {
+ mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateOperatorNumericAsync() throws Exception {
+ mLocaleTracker.updateOperatorNumericAsync(US_MCC + FAKE_MNC);
+ waitForMs(100);
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testNoSim() throws Exception {
+ mLocaleTracker.updateOperatorNumericAsync("");
+ waitForHandlerAction(mLocaleTracker, 100);
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testBootupInAirplaneModeOn() throws Exception {
+ doReturn(false).when(mSST).getDesiredPowerState();
+ mLocaleTracker.updateOperatorNumericAsync("");
+ waitForHandlerAction(mLocaleTracker, 100);
+ assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testTogglingAirplaneMode() throws Exception {
+ mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+
+ doReturn(false).when(mSST).getDesiredPowerState();
+ mLocaleTracker.updateOperatorNumericAsync("");
+ waitForHandlerAction(mLocaleTracker, 100);
+ assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+
+ doReturn(true).when(mSST).getDesiredPowerState();
+ mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager, times(2)).setCountryCode(US_COUNTRY_CODE, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testCellInfoUnavailableRetry() throws Exception {
+ doReturn(null).when(mPhone).getAllCellInfo(isNull());
+ mLocaleTracker.updateOperatorNumericAsync("");
+ waitForHandlerAction(mLocaleTracker, 100);
+ assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+
+ doReturn(Arrays.asList(mCellInfo)).when(mPhone).getAllCellInfo(isNull());
+ waitForHandlerActionDelayed(mLocaleTracker, 100, 2500);
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testOutOfAirplaneMode() throws Exception {
+ doReturn(null).when(mPhone).getAllCellInfo(isNull());
+ mLocaleTracker.updateOperatorNumericAsync("");
+ waitForHandlerAction(mLocaleTracker, 100);
+ assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+
+ doReturn(Arrays.asList(mCellInfo)).when(mPhone).getAllCellInfo(isNull());
+ ServiceState ss = new ServiceState();
+ ss.setState(ServiceState.STATE_IN_SERVICE);
+ AsyncResult ar = new AsyncResult(null, ss, null);
+ mLocaleTracker.sendMessage(mLocaleTracker.obtainMessage(3, ar));
+ waitForHandlerAction(mLocaleTracker, 100);
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+ verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationStateTest.java
new file mode 100644
index 0000000..eb52e7c
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationStateTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 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 junit.framework.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.CellIdentityLte;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.TelephonyManager;
+
+import org.junit.Test;
+
+/** Unit tests for {@link NetworkRegistrationState}. */
+
+public class NetworkRegistrationStateTest {
+
+ @Test
+ @SmallTest
+ public void testParcel() {
+ NetworkRegistrationState nrs = new NetworkRegistrationState(
+ NetworkRegistrationState.DOMAIN_CS,
+ TransportType.WWAN,
+ NetworkRegistrationState.REG_STATE_HOME,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ 0,
+ false,
+ new int[]{NetworkRegistrationState.SERVICE_TYPE_DATA},
+ new CellIdentityLte());
+
+ Parcel p = Parcel.obtain();
+ nrs.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ NetworkRegistrationState newNrs = NetworkRegistrationState.CREATOR.createFromParcel(p);
+ assertEquals(nrs, newNrs);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NewNitzStateMachineTest.java b/tests/telephonytests/src/com/android/internal/telephony/NewNitzStateMachineTest.java
new file mode 100644
index 0000000..028cbcc
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/NewNitzStateMachineTest.java
@@ -0,0 +1,970 @@
+/*
+ * Copyright 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.icu.util.Calendar;
+import android.icu.util.GregorianCalendar;
+import android.icu.util.TimeZone;
+import android.util.TimestampedValue;
+
+import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
+import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class NewNitzStateMachineTest extends TelephonyTest {
+
+ // A country with a single zone : the zone can be guessed from the country.
+ // The UK uses UTC for part of the year so it is not good for detecting bogus NITZ signals.
+ private static final Scenario UNITED_KINGDOM_SCENARIO = new Scenario.Builder()
+ .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+ .setInitialDeviceRealtimeMillis(123456789L)
+ .setTimeZone("Europe/London")
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .setCountryIso("gb")
+ .build();
+
+ // A country that has multiple zones, but there is only one matching time zone at the time :
+ // the zone cannot be guessed from the country alone, but can be guessed from the country +
+ // NITZ. The US never uses UTC so it can be used for testing bogus NITZ signal handling.
+ private static final Scenario UNIQUE_US_ZONE_SCENARIO = new Scenario.Builder()
+ .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+ .setInitialDeviceRealtimeMillis(123456789L)
+ .setTimeZone("America/Los_Angeles")
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .setCountryIso("us")
+ .build();
+
+ // A country with a single zone: the zone can be guessed from the country alone. CZ never uses
+ // UTC so it can be used for testing bogus NITZ signal handling.
+ private static final Scenario CZECHIA_SCENARIO = new Scenario.Builder()
+ .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+ .setInitialDeviceRealtimeMillis(123456789L)
+ .setTimeZone("Europe/Prague")
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .setCountryIso("cz")
+ .build();
+
+ @Mock
+ private NewNitzStateMachine.DeviceState mDeviceState;
+
+ @Mock
+ private NewTimeServiceHelper mTimeServiceHelper;
+
+ private TimeZoneLookupHelper mRealTimeZoneLookupHelper;
+
+ private NewNitzStateMachine mNitzStateMachine;
+
+ @Before
+ public void setUp() throws Exception {
+ logd("NitzStateMachineTest +Setup!");
+ super.setUp("NitzStateMachineTest");
+
+ // In tests we use the real TimeZoneLookupHelper.
+ mRealTimeZoneLookupHelper = new TimeZoneLookupHelper();
+ mNitzStateMachine = new NewNitzStateMachine(
+ mPhone, mTimeServiceHelper, mDeviceState, mRealTimeZoneLookupHelper);
+
+ logd("ServiceStateTrackerTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ checkNoUnverifiedSetOperations(mTimeServiceHelper);
+
+ super.tearDown();
+ }
+
+ @Test
+ public void test_uniqueUsZone_Assumptions() {
+ // Check we'll get the expected behavior from TimeZoneLookupHelper.
+
+ // allZonesHaveSameOffset == false, so we shouldn't pick an arbitrary zone.
+ CountryResult expectedCountryLookupResult = new CountryResult(
+ "America/New_York", false /* allZonesHaveSameOffset */,
+ UNIQUE_US_ZONE_SCENARIO.getInitialSystemClockMillis());
+ CountryResult actualCountryLookupResult =
+ mRealTimeZoneLookupHelper.lookupByCountry(
+ UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode(),
+ UNIQUE_US_ZONE_SCENARIO.getInitialSystemClockMillis());
+ assertEquals(expectedCountryLookupResult, actualCountryLookupResult);
+
+ // isOnlyMatch == true, so the combination of country + NITZ should be enough.
+ OffsetResult expectedLookupResult =
+ new OffsetResult("America/Los_Angeles", true /* isOnlyMatch */);
+ OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry(
+ UNIQUE_US_ZONE_SCENARIO.getNitzSignal().getValue(),
+ UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode());
+ assertEquals(expectedLookupResult, actualLookupResult);
+ }
+
+ @Test
+ public void test_unitedKingdom_Assumptions() {
+ // Check we'll get the expected behavior from TimeZoneLookupHelper.
+
+ // allZonesHaveSameOffset == true (not only that, there is only one zone), so we can pick
+ // the zone knowing only the country.
+ CountryResult expectedCountryLookupResult = new CountryResult(
+ "Europe/London", true /* allZonesHaveSameOffset */,
+ UNITED_KINGDOM_SCENARIO.getInitialSystemClockMillis());
+ CountryResult actualCountryLookupResult =
+ mRealTimeZoneLookupHelper.lookupByCountry(
+ UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode(),
+ UNITED_KINGDOM_SCENARIO.getInitialSystemClockMillis());
+ assertEquals(expectedCountryLookupResult, actualCountryLookupResult);
+
+ OffsetResult expectedLookupResult =
+ new OffsetResult("Europe/London", true /* isOnlyMatch */);
+ OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry(
+ UNITED_KINGDOM_SCENARIO.getNitzSignal().getValue(),
+ UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode());
+ assertEquals(expectedLookupResult, actualLookupResult);
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeZoneEnabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country won't be enough for time zone detection.
+ .verifyNothingWasSetAndReset()
+ .nitzReceived(scenario.getNitzSignal())
+ // Country + NITZ is enough for both time + time zone detection.
+ .verifyTimeSuggestedAndZoneSetAndReset(
+ scenario.getNitzSignal(), scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeZoneEnabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country alone is enough to guess the time zone.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId())
+ .nitzReceived(scenario.getNitzSignal())
+ // Country + NITZ is enough for both time + time zone detection.
+ .verifyTimeSuggestedAndZoneSetAndReset(
+ scenario.getNitzSignal(), scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeZoneDisabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(false)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country is not enough to guess the time zone and time zone detection is disabled.
+ .verifyNothingWasSetAndReset()
+ .nitzReceived(scenario.getNitzSignal())
+ // Time zone detection is disabled, but time should be suggested from NITZ.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeZoneDisabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(false)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country alone would be enough for time zone detection, but it's disabled.
+ .verifyNothingWasSetAndReset()
+ .nitzReceived(scenario.getNitzSignal())
+ // Time zone detection is disabled, but time should be suggested from NITZ.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeZoneEnabled_nitzThenCountry() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ // The NITZ alone isn't enough to detect a time zone.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The NITZ + country is enough to detect the time zone.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeZoneEnabled_nitzThenCountry() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ // The NITZ alone isn't enough to detect a time zone.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode());
+
+ // The NITZ + country is enough to detect the time zone.
+ // NOTE: setting the time zone happens twice because of a quirk in NitzStateMachine: it
+ // handles the country lookup / set, then combines the country with the NITZ state and does
+ // another lookup / set. We shouldn't require it is set twice but we do for simplicity.
+ script.verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 2 /* times */);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_validCzNitzSignal_nitzReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(goodNitzSignal)
+ // The NITZ alone isn't enough to detect a time zone.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The NITZ country is enough to detect the time zone, but the NITZ + country is
+ // also sufficient so we expect the time zone to be set twice.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 2);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_validCzNitzSignal_countryReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The NITZ country is enough to detect the time zone.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 1);
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(goodNitzSignal)
+ // The time will be suggested from the NITZ signal.
+ // The combination of NITZ + country will cause the time zone to be set.
+ .verifyTimeSuggestedAndZoneSetAndReset(
+ scenario.getNitzSignal(), scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusCzNitzSignal_nitzReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ alone isn't enough to detect a time zone, but there isn't enough
+ // information to work out it is bogus.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country is enough to detect the time zone for CZ. If the NITZ signal
+ // wasn't obviously bogus we'd try to set it twice.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 1);
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusCzNitzSignal_countryReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country is enough to detect the time zone for CZ.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ should be detected as bogus so only the time will be suggested.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusUniqueUsNitzSignal_nitzReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ alone isn't enough to detect a time zone, but there isn't enough
+ // information to work out its bogus.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country isn't enough to detect the time zone for US so we will leave the time
+ // zone unset.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusUsUniqueNitzSignal_countryReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country isn't enough to detect the time zone for US so we will leave the time
+ // zone unset.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ should be detected as bogus so only the time will be suggested.
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_emulatorNitzExtensionUsedForTimeZone() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> originalNitzSignal = scenario.getNitzSignal();
+
+ // Create an NITZ signal with an explicit time zone (as can happen on emulators)
+ NitzData originalNitzData = originalNitzSignal.getValue();
+ // A time zone that is obviously not in the US, but it should not be questioned.
+ String emulatorTimeZoneId = "Europe/London";
+ NitzData emulatorNitzData = NitzData.createForTests(
+ originalNitzData.getLocalOffsetMillis(),
+ originalNitzData.getDstAdjustmentMillis(),
+ originalNitzData.getCurrentTimeInMillis(),
+ java.util.TimeZone.getTimeZone(emulatorTimeZoneId) /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> emulatorNitzSignal = new TimestampedValue<>(
+ originalNitzSignal.getReferenceTimeMillis(), emulatorNitzData);
+
+ // Simulate receiving the emulator NITZ signal.
+ script.nitzReceived(emulatorNitzSignal)
+ .verifyTimeSuggestedAndZoneSetAndReset(
+ scenario.getNitzSignal(), emulatorTimeZoneId);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(emulatorNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(emulatorTimeZoneId, mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_emptyCountryStringUsTime_countryReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ String expectedZoneId = checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(scenario);
+
+ // Nothing should be set. The country is not valid.
+ script.countryReceived("").verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving the NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ .verifyTimeSuggestedAndZoneSetAndReset(scenario.getNitzSignal(), expectedZoneId);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(expectedZoneId, mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_emptyCountryStringUsTime_nitzReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ String expectedZoneId = checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(scenario);
+
+ // Simulate receiving the NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ .verifyOnlyTimeWasSuggestedAndReset(scenario.getNitzSignal());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // The time zone should be set (but the country is not valid so it's unlikely to be
+ // correct).
+ script.countryReceived("").verifyOnlyTimeZoneWasSetAndReset(expectedZoneId);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(expectedZoneId, mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ /**
+ * Asserts a test scenario has the properties we expect for NITZ-only lookup. There are
+ * usually multiple zones that will share the same UTC offset so we get a low quality / low
+ * confidence answer, but the zone we find should at least have the correct offset.
+ */
+ private String checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(Scenario scenario) {
+ OffsetResult result =
+ mRealTimeZoneLookupHelper.lookupByNitz(scenario.getNitzSignal().getValue());
+ String expectedZoneId = result.zoneId;
+ // All our scenarios should return multiple matches. The only cases where this wouldn't be
+ // true are places that use offsets like XX:15, XX:30 and XX:45.
+ assertFalse(result.isOnlyMatch);
+ assertSameOffset(scenario.getActualTimeMillis(), expectedZoneId, scenario.getTimeZoneId());
+ return expectedZoneId;
+ }
+
+ private static void assertSameOffset(long timeMillis, String zoneId1, String zoneId2) {
+ assertEquals(TimeZone.getTimeZone(zoneId1).getOffset(timeMillis),
+ TimeZone.getTimeZone(zoneId2).getOffset(timeMillis));
+ }
+
+ private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute,
+ int second) {
+ Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC"));
+ cal.clear();
+ cal.set(year, monthInYear - 1, day, hourOfDay, minute, second);
+ return cal.getTimeInMillis();
+ }
+
+ /**
+ * A helper class for common test operations involving a device.
+ */
+ class Script {
+ private final Device mDevice;
+
+ Script(Device device) {
+ this.mDevice = device;
+ }
+
+ Script countryReceived(String countryIsoCode) {
+ mDevice.networkCountryKnown(countryIsoCode);
+ return this;
+ }
+
+ Script nitzReceived(TimestampedValue<NitzData> nitzSignal) {
+ mDevice.nitzSignalReceived(nitzSignal);
+ return this;
+ }
+
+ Script verifyNothingWasSetAndReset() {
+ mDevice.verifyTimeZoneWasNotSet();
+ mDevice.verifyTimeWasNotSuggested();
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script verifyOnlyTimeZoneWasSetAndReset(String timeZoneId, int times) {
+ mDevice.verifyTimeZoneWasSet(timeZoneId, times);
+ mDevice.verifyTimeWasNotSuggested();
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script verifyOnlyTimeZoneWasSetAndReset(String timeZoneId) {
+ return verifyOnlyTimeZoneWasSetAndReset(timeZoneId, 1);
+ }
+
+ Script verifyOnlyTimeWasSuggestedAndReset(TimestampedValue<NitzData> nitzSignal) {
+ mDevice.verifyTimeZoneWasNotSet();
+
+ TimestampedValue<Long> time = new TimestampedValue<>(
+ nitzSignal.getReferenceTimeMillis(),
+ nitzSignal.getValue().getCurrentTimeInMillis());
+ mDevice.verifyTimeWasSuggested(time);
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script verifyTimeSuggestedAndZoneSetAndReset(
+ TimestampedValue<NitzData> nitzSignal, String timeZoneId) {
+ mDevice.verifyTimeZoneWasSet(timeZoneId);
+
+ TimestampedValue<Long> time = new TimestampedValue<>(
+ nitzSignal.getReferenceTimeMillis(),
+ nitzSignal.getValue().getCurrentTimeInMillis());
+ mDevice.verifyTimeWasSuggested(time);
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script reset() {
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+ }
+
+ /**
+ * An abstraction of a device for use in telephony time zone detection tests. It can be used to
+ * retrieve device state, modify device state and verify changes.
+ */
+ class Device {
+
+ private final long mInitialSystemClockMillis;
+ private final long mInitialRealtimeMillis;
+ private final boolean mTimeZoneDetectionEnabled;
+ private final boolean mTimeZoneSettingInitialized;
+
+ Device(long initialSystemClockMillis, long initialRealtimeMillis,
+ boolean timeZoneDetectionEnabled, boolean timeZoneSettingInitialized) {
+ mInitialSystemClockMillis = initialSystemClockMillis;
+ mInitialRealtimeMillis = initialRealtimeMillis;
+ mTimeZoneDetectionEnabled = timeZoneDetectionEnabled;
+ mTimeZoneSettingInitialized = timeZoneSettingInitialized;
+ }
+
+ void initialize() {
+ // Set initial configuration.
+ when(mDeviceState.getIgnoreNitz()).thenReturn(false);
+ when(mDeviceState.getNitzUpdateDiffMillis()).thenReturn(2000);
+ when(mDeviceState.getNitzUpdateSpacingMillis()).thenReturn(1000 * 60 * 10);
+
+ // Simulate the country not being known.
+ when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn("");
+
+ when(mTimeServiceHelper.elapsedRealtime()).thenReturn(mInitialRealtimeMillis);
+ when(mTimeServiceHelper.currentTimeMillis()).thenReturn(mInitialSystemClockMillis);
+ when(mTimeServiceHelper.isTimeZoneDetectionEnabled())
+ .thenReturn(mTimeZoneDetectionEnabled);
+ when(mTimeServiceHelper.isTimeZoneSettingInitialized())
+ .thenReturn(mTimeZoneSettingInitialized);
+ }
+
+ void networkCountryKnown(String countryIsoCode) {
+ when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn(countryIsoCode);
+ mNitzStateMachine.handleNetworkCountryCodeSet(true);
+ }
+
+ void nitzSignalReceived(TimestampedValue<NitzData> nitzSignal) {
+ mNitzStateMachine.handleNitzReceived(nitzSignal);
+ }
+
+ void verifyTimeZoneWasNotSet() {
+ verify(mTimeServiceHelper, times(0)).setDeviceTimeZone(any(String.class));
+ }
+
+ void verifyTimeZoneWasSet(String timeZoneId) {
+ verifyTimeZoneWasSet(timeZoneId, 1 /* times */);
+ }
+
+ void verifyTimeZoneWasSet(String timeZoneId, int times) {
+ verify(mTimeServiceHelper, times(times)).setDeviceTimeZone(timeZoneId);
+ }
+
+ void verifyTimeWasNotSuggested() {
+ verify(mTimeServiceHelper, times(0)).suggestDeviceTime(any());
+ }
+
+ void verifyTimeWasSuggested(TimestampedValue<Long> expectedTime) {
+ verify(mTimeServiceHelper, times(1)).suggestDeviceTime(eq(expectedTime));
+ }
+
+ /**
+ * Used after calling verify... methods to reset expectations.
+ */
+ void resetInvocations() {
+ clearInvocations(mTimeServiceHelper);
+ }
+
+ void checkNoUnverifiedSetOperations() {
+ NewNitzStateMachineTest.checkNoUnverifiedSetOperations(mTimeServiceHelper);
+ }
+ }
+
+ /** A class used to construct a Device. */
+ class DeviceBuilder {
+
+ private long mInitialSystemClock;
+ private long mInitialRealtimeMillis;
+ private boolean mTimeZoneDetectionEnabled;
+ private boolean mTimeZoneSettingInitialized;
+
+ Device initialize() {
+ Device device = new Device(mInitialSystemClock, mInitialRealtimeMillis,
+ mTimeZoneDetectionEnabled, mTimeZoneSettingInitialized);
+ device.initialize();
+ return device;
+ }
+
+ DeviceBuilder setTimeZoneDetectionEnabled(boolean enabled) {
+ mTimeZoneDetectionEnabled = enabled;
+ return this;
+ }
+
+ DeviceBuilder setTimeZoneSettingInitialized(boolean initialized) {
+ mTimeZoneSettingInitialized = initialized;
+ return this;
+ }
+
+ DeviceBuilder setClocksFromScenario(Scenario scenario) {
+ mInitialRealtimeMillis = scenario.getInitialRealTimeMillis();
+ mInitialSystemClock = scenario.getInitialSystemClockMillis();
+ return this;
+ }
+ }
+
+ /**
+ * A scenario used during tests. Describes a fictional reality.
+ */
+ static class Scenario {
+
+ private final long mInitialDeviceSystemClockMillis;
+ private final long mInitialDeviceRealtimeMillis;
+ private final long mActualTimeMillis;
+ private final TimeZone mZone;
+ private final String mNetworkCountryIsoCode;
+
+ private TimestampedValue<NitzData> mNitzSignal;
+
+ Scenario(long initialDeviceSystemClock, long elapsedRealtime, long timeMillis,
+ String zoneId, String countryIsoCode) {
+ mInitialDeviceSystemClockMillis = initialDeviceSystemClock;
+ mActualTimeMillis = timeMillis;
+ mInitialDeviceRealtimeMillis = elapsedRealtime;
+ mZone = TimeZone.getTimeZone(zoneId);
+ mNetworkCountryIsoCode = countryIsoCode;
+ }
+
+ TimestampedValue<NitzData> getNitzSignal() {
+ if (mNitzSignal == null) {
+ int[] offsets = new int[2];
+ mZone.getOffset(mActualTimeMillis, false /* local */, offsets);
+ int zoneOffsetMillis = offsets[0] + offsets[1];
+ NitzData nitzData = NitzData.createForTests(
+ zoneOffsetMillis, offsets[1], mActualTimeMillis,
+ null /* emulatorHostTimeZone */);
+ mNitzSignal = new TimestampedValue<>(mInitialDeviceRealtimeMillis, nitzData);
+ }
+ return mNitzSignal;
+ }
+
+ long getInitialRealTimeMillis() {
+ return mInitialDeviceRealtimeMillis;
+ }
+
+ long getInitialSystemClockMillis() {
+ return mInitialDeviceSystemClockMillis;
+ }
+
+ String getNetworkCountryIsoCode() {
+ return mNetworkCountryIsoCode;
+ }
+
+ String getTimeZoneId() {
+ return mZone.getID();
+ }
+
+ long getActualTimeMillis() {
+ return mActualTimeMillis;
+ }
+
+ static class Builder {
+
+ private long mInitialDeviceSystemClockMillis;
+ private long mInitialDeviceRealtimeMillis;
+ private long mActualTimeMillis;
+ private String mZoneId;
+ private String mCountryIsoCode;
+
+ Builder setInitialDeviceSystemClockUtc(int year, int monthInYear, int day,
+ int hourOfDay, int minute, int second) {
+ mInitialDeviceSystemClockMillis = createUtcTime(year, monthInYear, day, hourOfDay,
+ minute, second);
+ return this;
+ }
+
+ Builder setInitialDeviceRealtimeMillis(long realtimeMillis) {
+ mInitialDeviceRealtimeMillis = realtimeMillis;
+ return this;
+ }
+
+ Builder setActualTimeUtc(int year, int monthInYear, int day, int hourOfDay,
+ int minute, int second) {
+ mActualTimeMillis = createUtcTime(year, monthInYear, day, hourOfDay, minute,
+ second);
+ return this;
+ }
+
+ Builder setTimeZone(String zoneId) {
+ mZoneId = zoneId;
+ return this;
+ }
+
+ Builder setCountryIso(String isoCode) {
+ mCountryIsoCode = isoCode;
+ return this;
+ }
+
+ Scenario build() {
+ return new Scenario(mInitialDeviceSystemClockMillis, mInitialDeviceRealtimeMillis,
+ mActualTimeMillis, mZoneId, mCountryIsoCode);
+ }
+ }
+ }
+
+ /**
+ * Confirms all mTimeServiceHelper side effects were verified.
+ */
+ private static void checkNoUnverifiedSetOperations(NewTimeServiceHelper mTimeServiceHelper) {
+ // We don't care about current auto time / time zone state retrievals / listening so we can
+ // use "at least 0" times to indicate they don't matter.
+ verify(mTimeServiceHelper, atLeast(0)).setListener(any());
+ verify(mTimeServiceHelper, atLeast(0)).isTimeZoneDetectionEnabled();
+ verify(mTimeServiceHelper, atLeast(0)).isTimeZoneSettingInitialized();
+ verify(mTimeServiceHelper, atLeast(0)).elapsedRealtime();
+ verify(mTimeServiceHelper, atLeast(0)).currentTimeMillis();
+ verifyNoMoreInteractions(mTimeServiceHelper);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTest.java b/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTest.java
deleted file mode 100644
index c327581..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTest.java
+++ /dev/null
@@ -1,739 +0,0 @@
-/*
- * Copyright 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 org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.icu.util.Calendar;
-import android.icu.util.GregorianCalendar;
-import android.icu.util.TimeZone;
-
-import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
-import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
-import com.android.internal.telephony.util.TimeStampedValue;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-
-public class NitzStateMachineTest extends TelephonyTest {
-
- @Mock
- private NitzStateMachine.DeviceState mDeviceState;
-
- @Mock
- private TimeServiceHelper mTimeServiceHelper;
-
- private TimeZoneLookupHelper mRealTimeZoneLookupHelper;
-
- private NitzStateMachine mNitzStateMachine;
-
- @Before
- public void setUp() throws Exception {
- logd("NitzStateMachineTest +Setup!");
- super.setUp("NitzStateMachineTest");
-
- // In tests we use the real TimeZoneLookupHelper.
- mRealTimeZoneLookupHelper = new TimeZoneLookupHelper();
- mNitzStateMachine = new NitzStateMachine(
- mPhone, mTimeServiceHelper, mDeviceState, mRealTimeZoneLookupHelper);
-
- logd("ServiceStateTrackerTest -Setup!");
- }
-
- @After
- public void tearDown() throws Exception {
- checkNoUnverifiedSetOperations(mTimeServiceHelper);
-
- super.tearDown();
- }
-
- // A country that has multiple zones, but there is only one matching time zone at the time :
- // the zone cannot be guessed from the country alone, but can be guessed from the country +
- // NITZ.
- private static final Scenario UNIQUE_US_ZONE_SCENARIO = new Scenario.Builder()
- .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
- .setInitialDeviceRealtimeMillis(123456789L)
- .setTimeZone("America/Los_Angeles")
- .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
- .setCountryIso("us")
- .build();
-
- @Test
- public void test_uniqueUsZone_Assumptions() {
- // Check we'll get the expected behavior from TimeZoneLookupHelper.
-
- // allZonesHaveSameOffset == false, so we shouldn't pick an arbitrary zone.
- CountryResult expectedCountryLookupResult = new CountryResult(
- "America/New_York", false /* allZonesHaveSameOffset */,
- UNIQUE_US_ZONE_SCENARIO.getInitialSystemClockMillis());
- CountryResult actualCountryLookupResult =
- mRealTimeZoneLookupHelper.lookupByCountry(
- UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode(),
- UNIQUE_US_ZONE_SCENARIO.getInitialSystemClockMillis());
- assertEquals(expectedCountryLookupResult, actualCountryLookupResult);
-
- // isOnlyMatch == true, so the combination of country + NITZ should be enough.
- OffsetResult expectedLookupResult =
- new OffsetResult("America/Los_Angeles", true /* isOnlyMatch */);
- OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry(
- UNIQUE_US_ZONE_SCENARIO.getNitzSignal().mValue,
- UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode());
- assertEquals(expectedLookupResult, actualLookupResult);
- }
-
- // A country with a single zone : the zone can be guessed from the country.
- private static final Scenario UNITED_KINGDOM_SCENARIO = new Scenario.Builder()
- .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
- .setInitialDeviceRealtimeMillis(123456789L)
- .setTimeZone("Europe/London")
- .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
- .setCountryIso("gb")
- .build();
-
- @Test
- public void test_unitedKingdom_Assumptions() {
- // Check we'll get the expected behavior from TimeZoneLookupHelper.
-
- // allZonesHaveSameOffset == true (not only that, there is only one zone), so we can pick
- // the zone knowing only the country.
- CountryResult expectedCountryLookupResult = new CountryResult(
- "Europe/London", true /* allZonesHaveSameOffset */,
- UNITED_KINGDOM_SCENARIO.getInitialSystemClockMillis());
- CountryResult actualCountryLookupResult =
- mRealTimeZoneLookupHelper.lookupByCountry(
- UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode(),
- UNITED_KINGDOM_SCENARIO.getInitialSystemClockMillis());
- assertEquals(expectedCountryLookupResult, actualCountryLookupResult);
-
- OffsetResult expectedLookupResult =
- new OffsetResult("Europe/London", true /* isOnlyMatch */);
- OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry(
- UNITED_KINGDOM_SCENARIO.getNitzSignal().mValue,
- UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode());
- assertEquals(expectedLookupResult, actualLookupResult);
- }
-
- @Test
- public void test_uniqueUsZone_timeEnabledTimeZoneEnabled_countryThenNitz() throws Exception {
- Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
- Device device = new DeviceBuilder()
- .setClocksFromScenario(scenario)
- .setTimeDetectionEnabled(true)
- .setTimeZoneDetectionEnabled(true)
- .setTimeZoneSettingInitialized(false)
- .initialize();
- Script script = new Script(device);
-
- int clockIncrement = 1250;
- long expectedTimeMillis = scenario.getActualTimeMillis() + clockIncrement;
- script.countryReceived(scenario.getNetworkCountryIsoCode())
- // Country won't be enough for time zone detection.
- .verifyNothingWasSetAndReset()
- // Increment the clock so we can tell the time was adjusted correctly when set.
- .incrementClocks(clockIncrement)
- .nitzReceived(scenario.getNitzSignal())
- // Country + NITZ is enough for both time + time zone detection.
- .verifyTimeAndZoneSetAndReset(expectedTimeMillis, scenario.getTimeZoneId());
-
- // Check NitzStateMachine state.
- assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
- assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
- assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
- }
-
- @Test
- public void test_unitedKingdom_timeEnabledTimeZoneEnabled_countryThenNitz() throws Exception {
- Scenario scenario = UNITED_KINGDOM_SCENARIO;
- Device device = new DeviceBuilder()
- .setClocksFromScenario(scenario)
- .setTimeDetectionEnabled(true)
- .setTimeZoneDetectionEnabled(true)
- .setTimeZoneSettingInitialized(false)
- .initialize();
- Script script = new Script(device);
-
- int clockIncrement = 1250;
- long expectedTimeMillis = scenario.getActualTimeMillis() + clockIncrement;
- script.countryReceived(scenario.getNetworkCountryIsoCode())
- // Country alone is enough to guess the time zone.
- .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId())
- // Increment the clock so we can tell the time was adjusted correctly when set.
- .incrementClocks(clockIncrement)
- .nitzReceived(scenario.getNitzSignal())
- // Country + NITZ is enough for both time + time zone detection.
- .verifyTimeAndZoneSetAndReset(expectedTimeMillis, scenario.getTimeZoneId());
-
- // Check NitzStateMachine state.
- assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
- assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
- assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
- }
-
- @Test
- public void test_uniqueUsZone_timeEnabledTimeZoneDisabled_countryThenNitz() throws Exception {
- Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
- Device device = new DeviceBuilder()
- .setClocksFromScenario(scenario)
- .setTimeDetectionEnabled(true)
- .setTimeZoneDetectionEnabled(false)
- .setTimeZoneSettingInitialized(false)
- .initialize();
- Script script = new Script(device);
-
- int clockIncrement = 1250;
- script.countryReceived(scenario.getNetworkCountryIsoCode())
- // Country is not enough to guess the time zone and time zone detection is disabled.
- .verifyNothingWasSetAndReset()
- // Increment the clock so we can tell the time was adjusted correctly when set.
- .incrementClocks(clockIncrement)
- .nitzReceived(scenario.getNitzSignal())
- // Time zone detection is disabled, but time should be set from NITZ.
- .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis() + clockIncrement);
-
- // Check NitzStateMachine state.
- assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
- assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
- assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
- }
-
- @Test
- public void test_unitedKingdom_timeEnabledTimeZoneDisabled_countryThenNitz()
- throws Exception {
- Scenario scenario = UNITED_KINGDOM_SCENARIO;
- Device device = new DeviceBuilder()
- .setClocksFromScenario(scenario)
- .setTimeDetectionEnabled(true)
- .setTimeZoneDetectionEnabled(false)
- .setTimeZoneSettingInitialized(false)
- .initialize();
- Script script = new Script(device);
-
- int clockIncrement = 1250;
- script.countryReceived(scenario.getNetworkCountryIsoCode())
- // Country alone would be enough for time zone detection, but it's disabled.
- .verifyNothingWasSetAndReset()
- // Increment the clock so we can tell the time was adjusted correctly when set.
- .incrementClocks(clockIncrement)
- .nitzReceived(scenario.getNitzSignal())
- // Time zone detection is disabled, but time should be set from NITZ.
- .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis() + clockIncrement);
-
- // Check NitzStateMachine state.
- assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
- assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
- assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
- }
-
- @Test
- public void test_uniqueUsZone_timeDisabledTimeZoneEnabled_countryThenNitz() throws Exception {
- Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
- Device device = new DeviceBuilder()
- .setClocksFromScenario(scenario)
- .setTimeDetectionEnabled(false)
- .setTimeZoneDetectionEnabled(true)
- .setTimeZoneSettingInitialized(false)
- .initialize();
- Script script = new Script(device);
-
- script.countryReceived(scenario.getNetworkCountryIsoCode())
- // Country won't be enough for time zone detection.
- .verifyNothingWasSetAndReset()
- .nitzReceived(scenario.getNitzSignal())
- // Time detection is disabled, but time zone should be detected from country + NITZ.
- .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
-
- // Check NitzStateMachine state.
- assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
- assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
- assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
- }
-
- @Test
- public void test_unitedKingdom_timeDisabledTimeZoneEnabled_countryThenNitz() throws Exception {
- Scenario scenario = UNITED_KINGDOM_SCENARIO;
- Device device = new DeviceBuilder()
- .setClocksFromScenario(scenario)
- .setTimeDetectionEnabled(false)
- .setTimeZoneDetectionEnabled(true)
- .setTimeZoneSettingInitialized(false)
- .initialize();
- Script script = new Script(device);
-
- script.countryReceived(scenario.getNetworkCountryIsoCode())
- // Country alone is enough to detect time zone.
- .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId())
- .nitzReceived(scenario.getNitzSignal())
- // Time detection is disabled, so we don't set the clock from NITZ.
- .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
-
- // Check NitzStateMachine state.
- assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
- assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
- assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
- }
-
- @Test
- public void test_uniqueUsZone_timeDisabledTimeZoneDisabled_countryThenNitz() throws Exception {
- Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
- Device device = new DeviceBuilder()
- .setClocksFromScenario(scenario)
- .setTimeDetectionEnabled(false)
- .setTimeZoneDetectionEnabled(false)
- .setTimeZoneSettingInitialized(false)
- .initialize();
- Script script = new Script(device);
-
- script.countryReceived(scenario.getNetworkCountryIsoCode())
- // Time and time zone detection is disabled.
- .verifyNothingWasSetAndReset()
- .nitzReceived(scenario.getNitzSignal())
- // Time and time zone detection is disabled.
- .verifyNothingWasSetAndReset();
-
- // Check NitzStateMachine state.
- assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
- assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
- assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
- }
-
- @Test
- public void test_unitedKingdom_timeDisabledTimeZoneDisabled_countryThenNitz() throws Exception {
- Scenario scenario = UNITED_KINGDOM_SCENARIO;
- Device device = new DeviceBuilder()
- .setClocksFromScenario(scenario)
- .setTimeDetectionEnabled(false)
- .setTimeZoneDetectionEnabled(false)
- .setTimeZoneSettingInitialized(false)
- .initialize();
- Script script = new Script(device);
-
- script.countryReceived(scenario.getNetworkCountryIsoCode())
- // Time and time zone detection is disabled.
- .verifyNothingWasSetAndReset()
- .nitzReceived(scenario.getNitzSignal())
- // Time and time zone detection is disabled.
- .verifyNothingWasSetAndReset();
-
- // Check NitzStateMachine state.
- assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
- assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
- assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
- }
-
- @Test
- public void test_uniqueUsZone_timeDisabledTimeZoneEnabled_nitzThenCountry() throws Exception {
- Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
- Device device = new DeviceBuilder()
- .setClocksFromScenario(scenario)
- .setTimeDetectionEnabled(false)
- .setTimeZoneDetectionEnabled(true)
- .setTimeZoneSettingInitialized(false)
- .initialize();
- Script script = new Script(device);
-
- // Simulate receiving an NITZ signal.
- script.nitzReceived(scenario.getNitzSignal())
- // The NITZ alone isn't enough to detect a time zone.
- .verifyNothingWasSetAndReset();
-
- // Check NitzStateMachine state.
- assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
- assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
- assertNull(mNitzStateMachine.getSavedTimeZoneId());
-
- // Simulate the country code becoming known.
- script.countryReceived(scenario.getNetworkCountryIsoCode())
- // The NITZ + country is enough to detect the time zone.
- .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
-
- // Check NitzStateMachine state.
- // TODO(nfuller): The following line should probably be assertTrue but the logic under test
- // may be buggy. Look at whether it needs to change.
- assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
- assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
- assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
- }
-
- @Test
- public void test_unitedKingdom_timeDisabledTimeZoneEnabled_nitzThenCountry() throws Exception {
- Scenario scenario = UNITED_KINGDOM_SCENARIO;
- Device device = new DeviceBuilder()
- .setClocksFromScenario(scenario)
- .setTimeDetectionEnabled(false)
- .setTimeZoneDetectionEnabled(true)
- .setTimeZoneSettingInitialized(false)
- .initialize();
- Script script = new Script(device);
-
- // Simulate receiving an NITZ signal.
- script.nitzReceived(scenario.getNitzSignal())
- // The NITZ alone isn't enough to detect a time zone.
- .verifyNothingWasSetAndReset();
-
- // Check NitzStateMachine state.
- assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
- assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
- assertNull(mNitzStateMachine.getSavedTimeZoneId());
-
- // Simulate the country code becoming known.
- script.countryReceived(scenario.getNetworkCountryIsoCode());
-
- // The NITZ + country is enough to detect the time zone.
- // NOTE: setting the timezone happens twice because of a quirk in NitzStateMachine: it
- // handles the country lookup / set, then combines the country with the NITZ state and does
- // another lookup / set. We shouldn't require it is set twice but we do for simplicity.
- script.verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 2 /* times */);
-
- // Check NitzStateMachine state.
- // TODO(nfuller): The following line should probably be assertTrue but the logic under test
- // may be buggy. Look at whether it needs to change.
- assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
- assertEquals(scenario.getNitzSignal().mValue, mNitzStateMachine.getCachedNitzData());
- assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
- }
-
- private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute,
- int second) {
- Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC"));
- cal.clear();
- cal.set(year, monthInYear - 1, day, hourOfDay, minute, second);
- return cal.getTimeInMillis();
- }
-
- /**
- * A helper class for common test operations involving a device.
- */
- class Script {
- private final Device mDevice;
-
- Script(Device device) {
- this.mDevice = device;
- }
-
- Script countryReceived(String countryIsoCode) {
- mDevice.networkCountryKnown(countryIsoCode);
- return this;
- }
-
- Script nitzReceived(TimeStampedValue<NitzData> nitzSignal) {
- mDevice.nitzSignalReceived(nitzSignal);
- return this;
- }
-
- Script incrementClocks(int clockIncrement) {
- mDevice.incrementClocks(clockIncrement);
- return this;
- }
-
- Script verifyNothingWasSetAndReset() {
- mDevice.verifyTimeZoneWasNotSet();
- mDevice.verifyTimeWasNotSet();
- mDevice.checkNoUnverifiedSetOperations();
- mDevice.resetInvocations();
- return this;
- }
-
- Script verifyOnlyTimeZoneWasSetAndReset(String timeZoneId, int times) {
- mDevice.verifyTimeZoneWasSet(timeZoneId, times);
- mDevice.verifyTimeWasNotSet();
- mDevice.checkNoUnverifiedSetOperations();
- mDevice.resetInvocations();
- return this;
- }
-
- Script verifyOnlyTimeZoneWasSetAndReset(String timeZoneId) {
- return verifyOnlyTimeZoneWasSetAndReset(timeZoneId, 1);
- }
-
- Script verifyOnlyTimeWasSetAndReset(long expectedTimeMillis) {
- mDevice.verifyTimeZoneWasNotSet();
- mDevice.verifyTimeWasSet(expectedTimeMillis);
- mDevice.checkNoUnverifiedSetOperations();
- mDevice.resetInvocations();
- return this;
- }
-
- Script verifyTimeAndZoneSetAndReset(long expectedTimeMillis, String timeZoneId) {
- mDevice.verifyTimeZoneWasSet(timeZoneId);
- mDevice.verifyTimeWasSet(expectedTimeMillis);
- mDevice.checkNoUnverifiedSetOperations();
- mDevice.resetInvocations();
- return this;
- }
-
- Script reset() {
- mDevice.checkNoUnverifiedSetOperations();
- mDevice.resetInvocations();
- return this;
- }
- }
-
- /**
- * An abstraction of a device for use in telephony time zone detection tests. It can be used to
- * retrieve device state, modify device state and verify changes.
- */
- class Device {
-
- private final long mInitialSystemClockMillis;
- private final long mInitialRealtimeMillis;
- private final boolean mTimeDetectionEnabled;
- private final boolean mTimeZoneDetectionEnabled;
- private final boolean mTimeZoneSettingInitialized;
-
- Device(long initialSystemClockMillis, long initialRealtimeMillis,
- boolean timeDetectionEnabled, boolean timeZoneDetectionEnabled,
- boolean timeZoneSettingInitialized) {
- mInitialSystemClockMillis = initialSystemClockMillis;
- mInitialRealtimeMillis = initialRealtimeMillis;
- mTimeDetectionEnabled = timeDetectionEnabled;
- mTimeZoneDetectionEnabled = timeZoneDetectionEnabled;
- mTimeZoneSettingInitialized = timeZoneSettingInitialized;
- }
-
- void initialize() {
- // Set initial configuration.
- when(mDeviceState.getIgnoreNitz()).thenReturn(false);
- when(mDeviceState.getNitzUpdateDiffMillis()).thenReturn(2000);
- when(mDeviceState.getNitzUpdateSpacingMillis()).thenReturn(1000 * 60 * 10);
-
- // Simulate the country not being known.
- when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn("");
-
- when(mTimeServiceHelper.elapsedRealtime()).thenReturn(mInitialRealtimeMillis);
- when(mTimeServiceHelper.currentTimeMillis()).thenReturn(mInitialSystemClockMillis);
- when(mTimeServiceHelper.isTimeDetectionEnabled()).thenReturn(mTimeDetectionEnabled);
- when(mTimeServiceHelper.isTimeZoneDetectionEnabled())
- .thenReturn(mTimeZoneDetectionEnabled);
- when(mTimeServiceHelper.isTimeZoneSettingInitialized())
- .thenReturn(mTimeZoneSettingInitialized);
- }
-
- void networkCountryKnown(String countryIsoCode) {
- when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn(countryIsoCode);
- mNitzStateMachine.handleNetworkCountryCodeSet(true);
- }
-
- void incrementClocks(int millis) {
- long currentElapsedRealtime = mTimeServiceHelper.elapsedRealtime();
- when(mTimeServiceHelper.elapsedRealtime()).thenReturn(currentElapsedRealtime + millis);
- long currentTimeMillis = mTimeServiceHelper.currentTimeMillis();
- when(mTimeServiceHelper.currentTimeMillis()).thenReturn(currentTimeMillis + millis);
- }
-
- void nitzSignalReceived(TimeStampedValue<NitzData> nitzSignal) {
- mNitzStateMachine.handleNitzReceived(nitzSignal);
- }
-
- void verifyTimeZoneWasNotSet() {
- verify(mTimeServiceHelper, times(0)).setDeviceTimeZone(any(String.class));
- }
-
- void verifyTimeZoneWasSet(String timeZoneId) {
- verifyTimeZoneWasSet(timeZoneId, 1 /* times */);
- }
-
- void verifyTimeZoneWasSet(String timeZoneId, int times) {
- verify(mTimeServiceHelper, times(times)).setDeviceTimeZone(timeZoneId);
- }
-
- void verifyTimeWasNotSet() {
- verify(mTimeServiceHelper, times(0)).setDeviceTime(anyLong());
- }
-
- void verifyTimeWasSet(long expectedTimeMillis) {
- ArgumentCaptor<Long> timeServiceTimeCaptor = ArgumentCaptor.forClass(Long.TYPE);
- verify(mTimeServiceHelper, times(1)).setDeviceTime(timeServiceTimeCaptor.capture());
- assertEquals(expectedTimeMillis, (long) timeServiceTimeCaptor.getValue());
- }
-
- /**
- * Used after calling verify... methods to reset expectations.
- */
- void resetInvocations() {
- clearInvocations(mTimeServiceHelper);
- }
-
- void checkNoUnverifiedSetOperations() {
- NitzStateMachineTest.checkNoUnverifiedSetOperations(mTimeServiceHelper);
- }
- }
-
- /** A class used to construct a Device. */
- class DeviceBuilder {
-
- private long mInitialSystemClock;
- private long mInitialRealtimeMillis;
- private boolean mTimeDetectionEnabled;
- private boolean mTimeZoneDetectionEnabled;
- private boolean mTimeZoneSettingInitialized;
-
- Device initialize() {
- Device device = new Device(mInitialSystemClock, mInitialRealtimeMillis,
- mTimeDetectionEnabled, mTimeZoneDetectionEnabled, mTimeZoneSettingInitialized);
- device.initialize();
- return device;
- }
-
- DeviceBuilder setTimeDetectionEnabled(boolean enabled) {
- mTimeDetectionEnabled = enabled;
- return this;
- }
-
- DeviceBuilder setTimeZoneDetectionEnabled(boolean enabled) {
- mTimeZoneDetectionEnabled = enabled;
- return this;
- }
-
- DeviceBuilder setTimeZoneSettingInitialized(boolean initialized) {
- mTimeZoneSettingInitialized = initialized;
- return this;
- }
-
- DeviceBuilder setClocksFromScenario(Scenario scenario) {
- mInitialRealtimeMillis = scenario.getInitialRealTimeMillis();
- mInitialSystemClock = scenario.getInitialSystemClockMillis();
- return this;
- }
- }
-
- /**
- * A scenario used during tests. Describes a fictional reality.
- */
- static class Scenario {
-
- private final long mInitialDeviceSystemClockMillis;
- private final long mInitialDeviceRealtimeMillis;
- private final long mActualTimeMillis;
- private final TimeZone mZone;
- private final String mNetworkCountryIsoCode;
-
- private TimeStampedValue<NitzData> mNitzSignal;
-
- Scenario(long initialDeviceSystemClock, long elapsedRealtime, long timeMillis,
- String zoneId, String countryIsoCode) {
- mInitialDeviceSystemClockMillis = initialDeviceSystemClock;
- mActualTimeMillis = timeMillis;
- mInitialDeviceRealtimeMillis = elapsedRealtime;
- mZone = TimeZone.getTimeZone(zoneId);
- mNetworkCountryIsoCode = countryIsoCode;
- }
-
- TimeStampedValue<NitzData> getNitzSignal() {
- if (mNitzSignal == null) {
- int[] offsets = new int[2];
- mZone.getOffset(mActualTimeMillis, false /* local */, offsets);
- int zoneOffsetMillis = offsets[0] + offsets[1];
- NitzData nitzData = NitzData
- .createForTests(zoneOffsetMillis, offsets[1], mActualTimeMillis, null);
- mNitzSignal = new TimeStampedValue<>(nitzData, mInitialDeviceRealtimeMillis);
- }
- return mNitzSignal;
- }
-
- long getInitialRealTimeMillis() {
- return mInitialDeviceRealtimeMillis;
- }
-
- long getInitialSystemClockMillis() {
- return mInitialDeviceSystemClockMillis;
- }
-
- String getNetworkCountryIsoCode() {
- return mNetworkCountryIsoCode;
- }
-
- String getTimeZoneId() {
- return mZone.getID();
- }
-
- long getActualTimeMillis() {
- return mActualTimeMillis;
- }
-
- static class Builder {
-
- private long mInitialDeviceSystemClockMillis;
- private long mInitialDeviceRealtimeMillis;
- private long mActualTimeMillis;
- private String mZoneId;
- private String mCountryIsoCode;
-
- Builder setInitialDeviceSystemClockUtc(int year, int monthInYear, int day,
- int hourOfDay, int minute, int second) {
- mInitialDeviceSystemClockMillis = createUtcTime(year, monthInYear, day, hourOfDay,
- minute, second);
- return this;
- }
-
- Builder setInitialDeviceRealtimeMillis(long realtimeMillis) {
- mInitialDeviceRealtimeMillis = realtimeMillis;
- return this;
- }
-
- Builder setActualTimeUtc(int year, int monthInYear, int day, int hourOfDay,
- int minute, int second) {
- mActualTimeMillis = createUtcTime(year, monthInYear, day, hourOfDay, minute,
- second);
- return this;
- }
-
- Builder setTimeZone(String zoneId) {
- mZoneId = zoneId;
- return this;
- }
-
- Builder setCountryIso(String isoCode) {
- mCountryIsoCode = isoCode;
- return this;
- }
-
- Scenario build() {
- return new Scenario(mInitialDeviceSystemClockMillis, mInitialDeviceRealtimeMillis,
- mActualTimeMillis, mZoneId, mCountryIsoCode);
- }
- }
- }
-
- /**
- * Confirms all mTimeServiceHelper side effects were verified.
- */
- private static void checkNoUnverifiedSetOperations(TimeServiceHelper mTimeServiceHelper) {
- // We don't care about current auto time / time zone state retrievals / listening so we can
- // use "at least 0" times to indicate they don't matter.
- verify(mTimeServiceHelper, atLeast(0)).setListener(any());
- verify(mTimeServiceHelper, atLeast(0)).isTimeDetectionEnabled();
- verify(mTimeServiceHelper, atLeast(0)).isTimeZoneDetectionEnabled();
- verify(mTimeServiceHelper, atLeast(0)).isTimeZoneSettingInitialized();
- verify(mTimeServiceHelper, atLeast(0)).elapsedRealtime();
- verify(mTimeServiceHelper, atLeast(0)).currentTimeMillis();
- verifyNoMoreInteractions(mTimeServiceHelper);
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/OldNitzStateMachineTest.java b/tests/telephonytests/src/com/android/internal/telephony/OldNitzStateMachineTest.java
new file mode 100644
index 0000000..2fc864a
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/OldNitzStateMachineTest.java
@@ -0,0 +1,1110 @@
+/*
+ * Copyright 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.icu.util.Calendar;
+import android.icu.util.GregorianCalendar;
+import android.icu.util.TimeZone;
+import android.util.TimestampedValue;
+
+import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
+import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+public class OldNitzStateMachineTest extends TelephonyTest {
+
+ // A country with a single zone : the zone can be guessed from the country.
+ // The UK uses UTC for part of the year so it is not good for detecting bogus NITZ signals.
+ private static final Scenario UNITED_KINGDOM_SCENARIO = new Scenario.Builder()
+ .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+ .setInitialDeviceRealtimeMillis(123456789L)
+ .setTimeZone("Europe/London")
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .setCountryIso("gb")
+ .build();
+
+ // A country that has multiple zones, but there is only one matching time zone at the time :
+ // the zone cannot be guessed from the country alone, but can be guessed from the country +
+ // NITZ. The US never uses UTC so it can be used for testing bogus NITZ signal handling.
+ private static final Scenario UNIQUE_US_ZONE_SCENARIO = new Scenario.Builder()
+ .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+ .setInitialDeviceRealtimeMillis(123456789L)
+ .setTimeZone("America/Los_Angeles")
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .setCountryIso("us")
+ .build();
+
+ // A country with a single zone: the zone can be guessed from the country alone. CZ never uses
+ // UTC so it can be used for testing bogus NITZ signal handling.
+ private static final Scenario CZECHIA_SCENARIO = new Scenario.Builder()
+ .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+ .setInitialDeviceRealtimeMillis(123456789L)
+ .setTimeZone("Europe/Prague")
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .setCountryIso("cz")
+ .build();
+
+ @Mock
+ private OldNitzStateMachine.DeviceState mDeviceState;
+
+ @Mock
+ private OldTimeServiceHelper mTimeServiceHelper;
+
+ private TimeZoneLookupHelper mRealTimeZoneLookupHelper;
+
+ private OldNitzStateMachine mNitzStateMachine;
+
+ @Before
+ public void setUp() throws Exception {
+ logd("NitzStateMachineTest +Setup!");
+ super.setUp("NitzStateMachineTest");
+
+ // In tests we use the real TimeZoneLookupHelper.
+ mRealTimeZoneLookupHelper = new TimeZoneLookupHelper();
+ mNitzStateMachine = new OldNitzStateMachine(
+ mPhone, mTimeServiceHelper, mDeviceState, mRealTimeZoneLookupHelper);
+
+ logd("ServiceStateTrackerTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ checkNoUnverifiedSetOperations(mTimeServiceHelper);
+
+ super.tearDown();
+ }
+
+ @Test
+ public void test_uniqueUsZone_Assumptions() {
+ // Check we'll get the expected behavior from TimeZoneLookupHelper.
+
+ // allZonesHaveSameOffset == false, so we shouldn't pick an arbitrary zone.
+ CountryResult expectedCountryLookupResult = new CountryResult(
+ "America/New_York", false /* allZonesHaveSameOffset */,
+ UNIQUE_US_ZONE_SCENARIO.getInitialSystemClockMillis());
+ CountryResult actualCountryLookupResult =
+ mRealTimeZoneLookupHelper.lookupByCountry(
+ UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode(),
+ UNIQUE_US_ZONE_SCENARIO.getInitialSystemClockMillis());
+ assertEquals(expectedCountryLookupResult, actualCountryLookupResult);
+
+ // isOnlyMatch == true, so the combination of country + NITZ should be enough.
+ OffsetResult expectedLookupResult =
+ new OffsetResult("America/Los_Angeles", true /* isOnlyMatch */);
+ OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry(
+ UNIQUE_US_ZONE_SCENARIO.getNitzSignal().getValue(),
+ UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode());
+ assertEquals(expectedLookupResult, actualLookupResult);
+ }
+
+ @Test
+ public void test_unitedKingdom_Assumptions() {
+ // Check we'll get the expected behavior from TimeZoneLookupHelper.
+
+ // allZonesHaveSameOffset == true (not only that, there is only one zone), so we can pick
+ // the zone knowing only the country.
+ CountryResult expectedCountryLookupResult = new CountryResult(
+ "Europe/London", true /* allZonesHaveSameOffset */,
+ UNITED_KINGDOM_SCENARIO.getInitialSystemClockMillis());
+ CountryResult actualCountryLookupResult =
+ mRealTimeZoneLookupHelper.lookupByCountry(
+ UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode(),
+ UNITED_KINGDOM_SCENARIO.getInitialSystemClockMillis());
+ assertEquals(expectedCountryLookupResult, actualCountryLookupResult);
+
+ OffsetResult expectedLookupResult =
+ new OffsetResult("Europe/London", true /* isOnlyMatch */);
+ OffsetResult actualLookupResult = mRealTimeZoneLookupHelper.lookupByNitzCountry(
+ UNITED_KINGDOM_SCENARIO.getNitzSignal().getValue(),
+ UNITED_KINGDOM_SCENARIO.getNetworkCountryIsoCode());
+ assertEquals(expectedLookupResult, actualLookupResult);
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeEnabledTimeZoneEnabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ int clockIncrement = 1250;
+ long expectedTimeMillis = scenario.getActualTimeMillis() + clockIncrement;
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country won't be enough for time zone detection.
+ .verifyNothingWasSetAndReset()
+ // Increment the clock so we can tell the time was adjusted correctly when set.
+ .incrementClocks(clockIncrement)
+ .nitzReceived(scenario.getNitzSignal())
+ // Country + NITZ is enough for both time + time zone detection.
+ .verifyTimeAndZoneSetAndReset(expectedTimeMillis, scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeEnabledTimeZoneEnabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ int clockIncrement = 1250;
+ long expectedTimeMillis = scenario.getActualTimeMillis() + clockIncrement;
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country alone is enough to guess the time zone.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId())
+ // Increment the clock so we can tell the time was adjusted correctly when set.
+ .incrementClocks(clockIncrement)
+ .nitzReceived(scenario.getNitzSignal())
+ // Country + NITZ is enough for both time + time zone detection.
+ .verifyTimeAndZoneSetAndReset(expectedTimeMillis, scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeEnabledTimeZoneDisabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(false)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ int clockIncrement = 1250;
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country is not enough to guess the time zone and time zone detection is disabled.
+ .verifyNothingWasSetAndReset()
+ // Increment the clock so we can tell the time was adjusted correctly when set.
+ .incrementClocks(clockIncrement)
+ .nitzReceived(scenario.getNitzSignal())
+ // Time zone detection is disabled, but time should be set from NITZ.
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis() + clockIncrement);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeEnabledTimeZoneDisabled_countryThenNitz()
+ throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(false)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ int clockIncrement = 1250;
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country alone would be enough for time zone detection, but it's disabled.
+ .verifyNothingWasSetAndReset()
+ // Increment the clock so we can tell the time was adjusted correctly when set.
+ .incrementClocks(clockIncrement)
+ .nitzReceived(scenario.getNitzSignal())
+ // Time zone detection is disabled, but time should be set from NITZ.
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis() + clockIncrement);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeDisabledTimeZoneEnabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(false)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country won't be enough for time zone detection.
+ .verifyNothingWasSetAndReset()
+ .nitzReceived(scenario.getNitzSignal())
+ // Time detection is disabled, but time zone should be detected from country + NITZ.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeDisabledTimeZoneEnabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(false)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Country alone is enough to detect time zone.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId())
+ .nitzReceived(scenario.getNitzSignal())
+ // Time detection is disabled, so we don't set the clock from NITZ.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeDisabledTimeZoneDisabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(false)
+ .setTimeZoneDetectionEnabled(false)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Time and time zone detection is disabled.
+ .verifyNothingWasSetAndReset()
+ .nitzReceived(scenario.getNitzSignal())
+ // Time and time zone detection is disabled.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeDisabledTimeZoneDisabled_countryThenNitz() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(false)
+ .setTimeZoneDetectionEnabled(false)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // Time and time zone detection is disabled.
+ .verifyNothingWasSetAndReset()
+ .nitzReceived(scenario.getNitzSignal())
+ // Time and time zone detection is disabled.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_uniqueUsZone_timeDisabledTimeZoneEnabled_nitzThenCountry() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(false)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ // The NITZ alone isn't enough to detect a time zone.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The NITZ + country is enough to detect the time zone.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_unitedKingdom_timeDisabledTimeZoneEnabled_nitzThenCountry() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(false)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(false)
+ .initialize();
+ Script script = new Script(device);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ // The NITZ alone isn't enough to detect a time zone.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode());
+
+ // The NITZ + country is enough to detect the time zone.
+ // NOTE: setting the timezone happens twice because of a quirk in NitzStateMachine: it
+ // handles the country lookup / set, then combines the country with the NITZ state and does
+ // another lookup / set. We shouldn't require it is set twice but we do for simplicity.
+ script.verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 2 /* times */);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_validCzNitzSignal_nitzReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(goodNitzSignal)
+ // The NITZ alone isn't enough to detect a time zone.
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The NITZ country is enough to detect the time zone, but the NITZ + country is
+ // also sufficient so we expect the time zone to be set twice.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 2);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_validCzNitzSignal_countryReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The NITZ country is enough to detect the time zone.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 1);
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(goodNitzSignal)
+ // The time will be set from the NITZ signal.
+ // The combination of NITZ + country will cause the time zone to be set.
+ .verifyTimeAndZoneSetAndReset(
+ scenario.getActualTimeMillis(), scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(goodNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusCzNitzSignal_nitzReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ alone isn't enough to detect a time zone, but there isn't enough
+ // information to work out its bogus.
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country is enough to detect the time zone for CZ. If the NITZ signal
+ // wasn't obviously bogus we'd try to set it twice.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId(), 1);
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusCzNitzSignal_countryReceivedFirst() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country is enough to detect the time zone for CZ.
+ .verifyOnlyTimeZoneWasSetAndReset(scenario.getTimeZoneId());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ should be detected as bogus so only the time will be set.
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(scenario.getTimeZoneId(), mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusUniqueUsNitzSignal_nitzReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ alone isn't enough to detect a time zone, but there isn't enough
+ // information to work out its bogus.
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country isn't enough to detect the time zone for US so we will leave the time
+ // zone unset.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_bogusUsUniqueNitzSignal_countryReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> goodNitzSignal = scenario.getNitzSignal();
+
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ // Simulate the country code becoming known.
+ script.countryReceived(scenario.getNetworkCountryIsoCode())
+ // The country isn't enough to detect the time zone for US so we will leave the time
+ // zone unset.
+ .verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving an NITZ signal.
+ script.nitzReceived(badNitzSignal)
+ // The NITZ should be detected as bogus so only the time will be set.
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(badNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_emulatorNitzExtensionUsedForTimeZone() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(false)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ TimestampedValue<NitzData> originalNitzSignal = scenario.getNitzSignal();
+
+ // Create an NITZ signal with an explicit time zone (as can happen on emulators)
+ NitzData originalNitzData = originalNitzSignal.getValue();
+ // A time zone that is obviously not in the US, but it should not be questioned.
+ String emulatorTimeZoneId = "Europe/London";
+ NitzData emulatorNitzData = NitzData.createForTests(
+ originalNitzData.getLocalOffsetMillis(),
+ originalNitzData.getDstAdjustmentMillis(),
+ originalNitzData.getCurrentTimeInMillis(),
+ java.util.TimeZone.getTimeZone(emulatorTimeZoneId) /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> emulatorNitzSignal = new TimestampedValue<>(
+ originalNitzSignal.getReferenceTimeMillis(), emulatorNitzData);
+
+ // Simulate receiving the emulator NITZ signal.
+ script.nitzReceived(emulatorNitzSignal)
+ .verifyOnlyTimeZoneWasSetAndReset(emulatorTimeZoneId);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(emulatorNitzSignal.getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(emulatorTimeZoneId, mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_emptyCountryStringUsTime_countryReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ String expectedZoneId = checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(scenario);
+
+ // Nothing should be set. The country is not valid.
+ script.countryReceived("").verifyNothingWasSetAndReset();
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertNull(mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // Simulate receiving the NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ .verifyTimeAndZoneSetAndReset(scenario.getActualTimeMillis(), expectedZoneId);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(expectedZoneId, mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ @Test
+ public void test_emptyCountryStringUsTime_nitzReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO;
+ Device device = new DeviceBuilder()
+ .setClocksFromScenario(scenario)
+ .setTimeDetectionEnabled(true)
+ .setTimeZoneDetectionEnabled(true)
+ .setTimeZoneSettingInitialized(true)
+ .initialize();
+ Script script = new Script(device);
+
+ String expectedZoneId = checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(scenario);
+
+ // Simulate receiving the NITZ signal.
+ script.nitzReceived(scenario.getNitzSignal())
+ .verifyOnlyTimeWasSetAndReset(scenario.getActualTimeMillis());
+
+ // Check NitzStateMachine state.
+ assertFalse(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertNull(mNitzStateMachine.getSavedTimeZoneId());
+
+ // The time zone should be set (but the country is not valid so it's unlikely to be
+ // correct).
+ script.countryReceived("").verifyOnlyTimeZoneWasSetAndReset(expectedZoneId);
+
+ // Check NitzStateMachine state.
+ assertTrue(mNitzStateMachine.getNitzTimeZoneDetectionSuccessful());
+ assertEquals(scenario.getNitzSignal().getValue(), mNitzStateMachine.getCachedNitzData());
+ assertEquals(expectedZoneId, mNitzStateMachine.getSavedTimeZoneId());
+ }
+
+ /**
+ * Asserts a test scenario has the properties we expect for NITZ-only lookup. There are
+ * usually multiple zones that will share the same UTC offset so we get a low quality / low
+ * confidence answer, but the zone we find should at least have the correct offset.
+ */
+ private String checkNitzOnlyLookupIsAmbiguousAndReturnZoneId(Scenario scenario) {
+ OffsetResult result =
+ mRealTimeZoneLookupHelper.lookupByNitz(scenario.getNitzSignal().getValue());
+ String expectedZoneId = result.zoneId;
+ // All our scenarios should return multiple matches. The only cases where this wouldn't be
+ // true are places that use offsets like XX:15, XX:30 and XX:45.
+ assertFalse(result.isOnlyMatch);
+ assertSameOffset(scenario.getActualTimeMillis(), expectedZoneId, scenario.getTimeZoneId());
+ return expectedZoneId;
+ }
+
+ private static void assertSameOffset(long timeMillis, String zoneId1, String zoneId2) {
+ assertEquals(TimeZone.getTimeZone(zoneId1).getOffset(timeMillis),
+ TimeZone.getTimeZone(zoneId2).getOffset(timeMillis));
+ }
+
+ private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute,
+ int second) {
+ Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC"));
+ cal.clear();
+ cal.set(year, monthInYear - 1, day, hourOfDay, minute, second);
+ return cal.getTimeInMillis();
+ }
+
+ /**
+ * A helper class for common test operations involving a device.
+ */
+ class Script {
+ private final Device mDevice;
+
+ Script(Device device) {
+ this.mDevice = device;
+ }
+
+ Script countryReceived(String countryIsoCode) {
+ mDevice.networkCountryKnown(countryIsoCode);
+ return this;
+ }
+
+ Script nitzReceived(TimestampedValue<NitzData> nitzSignal) {
+ mDevice.nitzSignalReceived(nitzSignal);
+ return this;
+ }
+
+ Script incrementClocks(int clockIncrement) {
+ mDevice.incrementClocks(clockIncrement);
+ return this;
+ }
+
+ Script verifyNothingWasSetAndReset() {
+ mDevice.verifyTimeZoneWasNotSet();
+ mDevice.verifyTimeWasNotSet();
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script verifyOnlyTimeZoneWasSetAndReset(String timeZoneId, int times) {
+ mDevice.verifyTimeZoneWasSet(timeZoneId, times);
+ mDevice.verifyTimeWasNotSet();
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script verifyOnlyTimeZoneWasSetAndReset(String timeZoneId) {
+ return verifyOnlyTimeZoneWasSetAndReset(timeZoneId, 1);
+ }
+
+ Script verifyOnlyTimeWasSetAndReset(long expectedTimeMillis) {
+ mDevice.verifyTimeZoneWasNotSet();
+ mDevice.verifyTimeWasSet(expectedTimeMillis);
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script verifyTimeAndZoneSetAndReset(long expectedTimeMillis, String timeZoneId) {
+ mDevice.verifyTimeZoneWasSet(timeZoneId);
+ mDevice.verifyTimeWasSet(expectedTimeMillis);
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+
+ Script reset() {
+ mDevice.checkNoUnverifiedSetOperations();
+ mDevice.resetInvocations();
+ return this;
+ }
+ }
+
+ /**
+ * An abstraction of a device for use in telephony time zone detection tests. It can be used to
+ * retrieve device state, modify device state and verify changes.
+ */
+ class Device {
+
+ private final long mInitialSystemClockMillis;
+ private final long mInitialRealtimeMillis;
+ private final boolean mTimeDetectionEnabled;
+ private final boolean mTimeZoneDetectionEnabled;
+ private final boolean mTimeZoneSettingInitialized;
+
+ Device(long initialSystemClockMillis, long initialRealtimeMillis,
+ boolean timeDetectionEnabled, boolean timeZoneDetectionEnabled,
+ boolean timeZoneSettingInitialized) {
+ mInitialSystemClockMillis = initialSystemClockMillis;
+ mInitialRealtimeMillis = initialRealtimeMillis;
+ mTimeDetectionEnabled = timeDetectionEnabled;
+ mTimeZoneDetectionEnabled = timeZoneDetectionEnabled;
+ mTimeZoneSettingInitialized = timeZoneSettingInitialized;
+ }
+
+ void initialize() {
+ // Set initial configuration.
+ when(mDeviceState.getIgnoreNitz()).thenReturn(false);
+ when(mDeviceState.getNitzUpdateDiffMillis()).thenReturn(2000);
+ when(mDeviceState.getNitzUpdateSpacingMillis()).thenReturn(1000 * 60 * 10);
+
+ // Simulate the country not being known.
+ when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn("");
+
+ when(mTimeServiceHelper.elapsedRealtime()).thenReturn(mInitialRealtimeMillis);
+ when(mTimeServiceHelper.currentTimeMillis()).thenReturn(mInitialSystemClockMillis);
+ when(mTimeServiceHelper.isTimeDetectionEnabled()).thenReturn(mTimeDetectionEnabled);
+ when(mTimeServiceHelper.isTimeZoneDetectionEnabled())
+ .thenReturn(mTimeZoneDetectionEnabled);
+ when(mTimeServiceHelper.isTimeZoneSettingInitialized())
+ .thenReturn(mTimeZoneSettingInitialized);
+ }
+
+ void networkCountryKnown(String countryIsoCode) {
+ when(mDeviceState.getNetworkCountryIsoForPhone()).thenReturn(countryIsoCode);
+ mNitzStateMachine.handleNetworkCountryCodeSet(true);
+ }
+
+ void incrementClocks(int millis) {
+ long currentElapsedRealtime = mTimeServiceHelper.elapsedRealtime();
+ when(mTimeServiceHelper.elapsedRealtime()).thenReturn(currentElapsedRealtime + millis);
+ long currentTimeMillis = mTimeServiceHelper.currentTimeMillis();
+ when(mTimeServiceHelper.currentTimeMillis()).thenReturn(currentTimeMillis + millis);
+ }
+
+ void nitzSignalReceived(TimestampedValue<NitzData> nitzSignal) {
+ mNitzStateMachine.handleNitzReceived(nitzSignal);
+ }
+
+ void verifyTimeZoneWasNotSet() {
+ verify(mTimeServiceHelper, times(0)).setDeviceTimeZone(any(String.class));
+ }
+
+ void verifyTimeZoneWasSet(String timeZoneId) {
+ verifyTimeZoneWasSet(timeZoneId, 1 /* times */);
+ }
+
+ void verifyTimeZoneWasSet(String timeZoneId, int times) {
+ verify(mTimeServiceHelper, times(times)).setDeviceTimeZone(timeZoneId);
+ }
+
+ void verifyTimeWasNotSet() {
+ verify(mTimeServiceHelper, times(0)).setDeviceTime(anyLong());
+ }
+
+ void verifyTimeWasSet(long expectedTimeMillis) {
+ ArgumentCaptor<Long> timeServiceTimeCaptor = ArgumentCaptor.forClass(Long.TYPE);
+ verify(mTimeServiceHelper, times(1)).setDeviceTime(timeServiceTimeCaptor.capture());
+ assertEquals(expectedTimeMillis, (long) timeServiceTimeCaptor.getValue());
+ }
+
+ /**
+ * Used after calling verify... methods to reset expectations.
+ */
+ void resetInvocations() {
+ clearInvocations(mTimeServiceHelper);
+ }
+
+ void checkNoUnverifiedSetOperations() {
+ OldNitzStateMachineTest.checkNoUnverifiedSetOperations(mTimeServiceHelper);
+ }
+ }
+
+ /** A class used to construct a Device. */
+ class DeviceBuilder {
+
+ private long mInitialSystemClock;
+ private long mInitialRealtimeMillis;
+ private boolean mTimeDetectionEnabled;
+ private boolean mTimeZoneDetectionEnabled;
+ private boolean mTimeZoneSettingInitialized;
+
+ Device initialize() {
+ Device device = new Device(mInitialSystemClock, mInitialRealtimeMillis,
+ mTimeDetectionEnabled, mTimeZoneDetectionEnabled, mTimeZoneSettingInitialized);
+ device.initialize();
+ return device;
+ }
+
+ DeviceBuilder setTimeDetectionEnabled(boolean enabled) {
+ mTimeDetectionEnabled = enabled;
+ return this;
+ }
+
+ DeviceBuilder setTimeZoneDetectionEnabled(boolean enabled) {
+ mTimeZoneDetectionEnabled = enabled;
+ return this;
+ }
+
+ DeviceBuilder setTimeZoneSettingInitialized(boolean initialized) {
+ mTimeZoneSettingInitialized = initialized;
+ return this;
+ }
+
+ DeviceBuilder setClocksFromScenario(Scenario scenario) {
+ mInitialRealtimeMillis = scenario.getInitialRealTimeMillis();
+ mInitialSystemClock = scenario.getInitialSystemClockMillis();
+ return this;
+ }
+ }
+
+ /**
+ * A scenario used during tests. Describes a fictional reality.
+ */
+ static class Scenario {
+
+ private final long mInitialDeviceSystemClockMillis;
+ private final long mInitialDeviceRealtimeMillis;
+ private final long mActualTimeMillis;
+ private final TimeZone mZone;
+ private final String mNetworkCountryIsoCode;
+
+ private TimestampedValue<NitzData> mNitzSignal;
+
+ Scenario(long initialDeviceSystemClock, long elapsedRealtime, long timeMillis,
+ String zoneId, String countryIsoCode) {
+ mInitialDeviceSystemClockMillis = initialDeviceSystemClock;
+ mActualTimeMillis = timeMillis;
+ mInitialDeviceRealtimeMillis = elapsedRealtime;
+ mZone = TimeZone.getTimeZone(zoneId);
+ mNetworkCountryIsoCode = countryIsoCode;
+ }
+
+ TimestampedValue<NitzData> getNitzSignal() {
+ if (mNitzSignal == null) {
+ int[] offsets = new int[2];
+ mZone.getOffset(mActualTimeMillis, false /* local */, offsets);
+ int zoneOffsetMillis = offsets[0] + offsets[1];
+ NitzData nitzData = NitzData.createForTests(
+ zoneOffsetMillis, offsets[1], mActualTimeMillis,
+ null /* emulatorHostTimeZone */);
+ mNitzSignal = new TimestampedValue<>(mInitialDeviceRealtimeMillis, nitzData);
+ }
+ return mNitzSignal;
+ }
+
+ long getInitialRealTimeMillis() {
+ return mInitialDeviceRealtimeMillis;
+ }
+
+ long getInitialSystemClockMillis() {
+ return mInitialDeviceSystemClockMillis;
+ }
+
+ String getNetworkCountryIsoCode() {
+ return mNetworkCountryIsoCode;
+ }
+
+ String getTimeZoneId() {
+ return mZone.getID();
+ }
+
+ long getActualTimeMillis() {
+ return mActualTimeMillis;
+ }
+
+ static class Builder {
+
+ private long mInitialDeviceSystemClockMillis;
+ private long mInitialDeviceRealtimeMillis;
+ private long mActualTimeMillis;
+ private String mZoneId;
+ private String mCountryIsoCode;
+
+ Builder setInitialDeviceSystemClockUtc(int year, int monthInYear, int day,
+ int hourOfDay, int minute, int second) {
+ mInitialDeviceSystemClockMillis = createUtcTime(year, monthInYear, day, hourOfDay,
+ minute, second);
+ return this;
+ }
+
+ Builder setInitialDeviceRealtimeMillis(long realtimeMillis) {
+ mInitialDeviceRealtimeMillis = realtimeMillis;
+ return this;
+ }
+
+ Builder setActualTimeUtc(int year, int monthInYear, int day, int hourOfDay,
+ int minute, int second) {
+ mActualTimeMillis = createUtcTime(year, monthInYear, day, hourOfDay, minute,
+ second);
+ return this;
+ }
+
+ Builder setTimeZone(String zoneId) {
+ mZoneId = zoneId;
+ return this;
+ }
+
+ Builder setCountryIso(String isoCode) {
+ mCountryIsoCode = isoCode;
+ return this;
+ }
+
+ Scenario build() {
+ return new Scenario(mInitialDeviceSystemClockMillis, mInitialDeviceRealtimeMillis,
+ mActualTimeMillis, mZoneId, mCountryIsoCode);
+ }
+ }
+ }
+
+ /**
+ * Confirms all mTimeServiceHelper side effects were verified.
+ */
+ private static void checkNoUnverifiedSetOperations(OldTimeServiceHelper mTimeServiceHelper) {
+ // We don't care about current auto time / time zone state retrievals / listening so we can
+ // use "at least 0" times to indicate they don't matter.
+ verify(mTimeServiceHelper, atLeast(0)).setListener(any());
+ verify(mTimeServiceHelper, atLeast(0)).isTimeDetectionEnabled();
+ verify(mTimeServiceHelper, atLeast(0)).isTimeZoneDetectionEnabled();
+ verify(mTimeServiceHelper, atLeast(0)).isTimeZoneSettingInitialized();
+ verify(mTimeServiceHelper, atLeast(0)).elapsedRealtime();
+ verify(mTimeServiceHelper, atLeast(0)).currentTimeMillis();
+ verifyNoMoreInteractions(mTimeServiceHelper);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
index ec52c79..49ab00b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
@@ -15,15 +15,10 @@
*/
package com.android.internal.telephony;
-import android.app.AppOpsManager;
-import android.content.Context;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-
import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
import static android.Manifest.permission.READ_SMS;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -31,12 +26,17 @@
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
-
-import android.test.suitebuilder.annotation.SmallTest;
-
public class PhoneSubInfoControllerTest extends TelephonyTest {
private PhoneSubInfoController mPhoneSubInfoControllerUT;
private AppOpsManager mAppOsMgr;
@@ -92,7 +92,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getDeviceId", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getDeviceId"));
}
try {
@@ -100,7 +100,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getDeviceId", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getDeviceId"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -142,7 +142,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getNai", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getNai"));
}
try {
@@ -150,7 +150,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getNai", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getNai"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -192,7 +192,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getImei", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getImei"));
}
try {
@@ -200,7 +200,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getImei", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getImei"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -242,7 +242,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getDeviceSvn", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getDeviceSvn"));
}
try {
@@ -250,7 +250,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getDeviceSvn", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getDeviceSvn"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -295,7 +295,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getSubscriberId", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getSubscriberId"));
}
try {
@@ -303,7 +303,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getSubscriberId", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getSubscriberId"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -350,7 +350,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getIccSerialNumber", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getIccSerialNumber"));
}
try {
@@ -358,7 +358,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getIccSerialNumber", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getIccSerialNumber"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -483,7 +483,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getLine1AlphaTag", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getLine1AlphaTag"));
}
try {
@@ -491,7 +491,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getLine1AlphaTag", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getLine1AlphaTag"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -535,7 +535,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getMsisdn", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getMsisdn"));
}
try {
@@ -543,7 +543,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getMsisdn", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getMsisdn"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -587,7 +587,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getVoiceMailNumber", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getVoiceMailNumber"));
}
try {
@@ -595,7 +595,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getVoiceMailNumber", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getVoiceMailNumber"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
@@ -641,7 +641,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getVoiceMailAlphaTag", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getVoiceMailAlphaTag"));
}
try {
@@ -649,7 +649,7 @@
Assert.fail("expected Security Exception Thrown");
} catch (Exception ex) {
assertTrue(ex instanceof SecurityException);
- assertEquals(READ_PHONE_STATE + " denied: getVoiceMailAlphaTag", ex.getMessage());
+ assertTrue(ex.getMessage().contains("getVoiceMailAlphaTag"));
}
//case 2: no READ_PRIVILEGED_PHONE_STATE & appOsMgr READ_PHONE_PERMISSION
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index 0bedc75..10dd6f2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -57,6 +57,7 @@
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_DEVICE_STATE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_INITIAL_ATTACH_APN;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIM_CARD_POWER;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SMSC_ADDRESS;
@@ -101,6 +102,7 @@
import android.hardware.radio.V1_0.RadioResponseInfo;
import android.hardware.radio.V1_0.RadioResponseType;
import android.hardware.radio.V1_0.SmsWriteArgs;
+import android.hardware.radio.deprecated.V1_0.IOemHook;
import android.net.ConnectivityManager;
import android.os.Handler;
import android.os.HandlerThread;
@@ -109,25 +111,29 @@
import android.os.PowerManager;
import android.os.WorkSource;
import android.support.test.filters.FlakyTest;
+import android.telephony.AccessNetworkConstants;
import android.telephony.CellIdentityCdma;
import android.telephony.CellIdentityGsm;
import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityTdscdma;
import android.telephony.CellIdentityWcdma;
import android.telephony.CellInfo;
import android.telephony.CellInfoCdma;
import android.telephony.CellInfoGsm;
import android.telephony.CellInfoLte;
+import android.telephony.CellInfoTdscdma;
import android.telephony.CellInfoWcdma;
import android.telephony.CellSignalStrengthCdma;
import android.telephony.CellSignalStrengthGsm;
import android.telephony.CellSignalStrengthLte;
+import android.telephony.CellSignalStrengthTdscdma;
import android.telephony.CellSignalStrengthWcdma;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.telephony.data.DataProfile;
import com.android.internal.telephony.RIL.RilHandler;
-import com.android.internal.telephony.dataconnection.ApnSetting;
import com.android.internal.telephony.dataconnection.DcTracker;
import org.junit.After;
@@ -138,6 +144,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Arrays;
public class RILTest extends TelephonyTest {
@@ -151,6 +158,8 @@
private ConnectivityManager mConnectionManager;
@Mock
private IRadio mRadioProxy;
+ @Mock
+ private IOemHook mOemHookProxy;
private RilHandler mRilHandler;
private RIL mRILInstance;
@@ -188,6 +197,8 @@
private static final int RSSNR = 2147483647;
private static final int RSRP = 96;
private static final int RSRQ = 10;
+ private static final int RSCP = 94;
+ private static final int ECNO = 5;
private static final int SIGNAL_NOISE_RATIO = 6;
private static final int SIGNAL_STRENGTH = 24;
private static final int SYSTEM_ID = 65533;
@@ -199,6 +210,26 @@
private static final int TYPE_GSM = 1;
private static final int TYPE_LTE = 3;
private static final int TYPE_WCDMA = 4;
+ private static final int TYPE_TD_SCDMA = 5;
+
+ private static final int PROFILE_ID = 0;
+ private static final String APN = "apn";
+ private static final String PROTOCOL = "IPV6";
+ private static final int AUTH_TYPE = 0;
+ private static final String USER_NAME = "username";
+ private static final String PASSWORD = "password";
+ private static final int TYPE = 0;
+ private static final int MAX_CONNS_TIME = 1;
+ private static final int MAX_CONNS = 3;
+ private static final int WAIT_TIME = 10;
+ private static final boolean APN_ENABLED = true;
+ private static final int SUPPORTED_APNT_YPES_BITMAP = 123456;
+ private static final String ROAMING_PROTOCOL = "IPV6";
+ private static final int BEARER_BITMAP = 123123;
+ private static final int MTU = 1234;
+ private static final String MVNO_TYPE = "";
+ private static final String MVNO_MATCH_DATA = "";
+ private static final boolean MODEM_COGNITIVE = true;
private class RILTestHandler extends HandlerThread {
@@ -223,6 +254,7 @@
Phone.PREFERRED_CDMA_SUBSCRIPTION, 0);
mRILUnderTest = spy(mRILInstance);
doReturn(mRadioProxy).when(mRILUnderTest).getRadioProxy(any());
+ doReturn(mOemHookProxy).when(mRILUnderTest).getOemHookProxy(any());
mRilHandler = mRILUnderTest.getRilHandler();
@@ -242,7 +274,8 @@
}
@Before
- public void setUp() {
+ public void setUp() throws Exception {
+ super.setUp(RILTest.class.getSimpleName());
MockitoAnnotations.initMocks(this);
mTestHandler = new RILTestHandler(getClass().getSimpleName());
mTestHandler.start();
@@ -252,6 +285,7 @@
@After
public void tearDown() throws Exception {
mTestHandler.quit();
+ super.tearDown();
}
@FlakyTest
@@ -664,11 +698,12 @@
@FlakyTest
@Test
public void testSetInitialAttachApn() throws Exception {
- ApnSetting apnSetting = new ApnSetting(
- -1, "22210", "Vodafone IT", "web.omnitel.it", "", "",
- "", "", "", "", "", 0, new String[]{"DUN"}, "IP", "IP", true, 0, 0,
- 0, false, 0, 0, 0, 0, "", "");
- DataProfile dataProfile = DcTracker.createDataProfile(apnSetting, apnSetting.profileId);
+ ApnSetting apnSetting = ApnSetting.makeApnSetting(
+ -1, "22210", "Vodafone IT", "web.omnitel.it", null, -1,
+ null, null, -1, "", "", 0, ApnSetting.TYPE_DUN, ApnSetting.PROTOCOL_IP,
+ ApnSetting.PROTOCOL_IP, true, 0, 0, false, 0, 0, 0, 0, -1, "");
+ DataProfile dataProfile = DcTracker.createDataProfile(
+ apnSetting, apnSetting.getProfileId());
boolean isRoaming = false;
mRILUnderTest.setInitialAttachApn(dataProfile, isRoaming, obtainMessage());
@@ -995,6 +1030,22 @@
assertFalse(mRILInstance.getWakeLock(RIL.FOR_WAKELOCK).isHeld());
}
+ @Test
+ public void testInvokeOemRilRequestStrings() throws Exception {
+ String[] strings = new String[]{"a", "b", "c"};
+ mRILUnderTest.invokeOemRilRequestStrings(strings, obtainMessage());
+ verify(mOemHookProxy).sendRequestStrings(
+ mSerialNumberCaptor.capture(), eq(new ArrayList<>(Arrays.asList(strings))));
+ }
+
+ @Test
+ public void testInvokeOemRilRequestRaw() throws Exception {
+ byte[] data = new byte[]{1, 2, 3};
+ mRILUnderTest.invokeOemRilRequestRaw(data, obtainMessage());
+ verify(mOemHookProxy).sendRequestRaw(
+ mSerialNumberCaptor.capture(), eq(mRILUnderTest.primitiveArrayToArrayList(data)));
+ }
+
private Message obtainMessage() {
return mTestHandler.getThreadHandler().obtainMessage();
}
@@ -1139,7 +1190,8 @@
expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
CellIdentityWcdma ci = new CellIdentityWcdma(
LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
- CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(SIGNAL_STRENGTH, BIT_ERROR_RATE);
+ CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
+ SIGNAL_STRENGTH, BIT_ERROR_RATE, Integer.MAX_VALUE, Integer.MAX_VALUE);
expected.setCellIdentity(ci);
expected.setCellSignalStrength(cs);
expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
@@ -1147,6 +1199,47 @@
}
@Test
+ public void testConvertHalCellInfoListForTdscdma() throws Exception {
+ android.hardware.radio.V1_2.CellInfoTdscdma cellinfo =
+ new android.hardware.radio.V1_2.CellInfoTdscdma();
+ cellinfo.cellIdentityTdscdma.base.lac = LAC;
+ cellinfo.cellIdentityTdscdma.base.cid = CID;
+ cellinfo.cellIdentityTdscdma.base.cpid = PSC;
+ cellinfo.cellIdentityTdscdma.uarfcn = UARFCN;
+ cellinfo.cellIdentityTdscdma.base.mcc = MCC_STR;
+ cellinfo.cellIdentityTdscdma.base.mnc = MNC_STR;
+ cellinfo.signalStrengthTdscdma.signalStrength = SIGNAL_STRENGTH;
+ cellinfo.signalStrengthTdscdma.bitErrorRate = BIT_ERROR_RATE;
+ cellinfo.signalStrengthTdscdma.rscp = RSCP;
+ android.hardware.radio.V1_2.CellInfo record = new android.hardware.radio.V1_2.CellInfo();
+ record.cellInfoType = TYPE_TD_SCDMA;
+ record.registered = false;
+ record.timeStampType = RIL_TIMESTAMP_TYPE_OEM_RIL;
+ record.timeStamp = TIMESTAMP;
+ record.tdscdma.add(cellinfo);
+ ArrayList<android.hardware.radio.V1_2.CellInfo> records =
+ new ArrayList<android.hardware.radio.V1_2.CellInfo>();
+ records.add(record);
+
+ ArrayList<CellInfo> ret = RIL.convertHalCellInfoList_1_2(records);
+
+ assertEquals(1, ret.size());
+ CellInfoTdscdma cellInfoTdscdma = (CellInfoTdscdma) ret.get(0);
+ CellInfoTdscdma expected = new CellInfoTdscdma();
+ expected.setRegistered(false);
+ expected.setTimeStamp(TIMESTAMP);
+ expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
+ expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
+ CellIdentityTdscdma ci = new CellIdentityTdscdma(
+ MCC_STR, MNC_STR, LAC, CID, PSC, UARFCN, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
+ CellSignalStrengthTdscdma cs = new CellSignalStrengthTdscdma(
+ SIGNAL_STRENGTH, BIT_ERROR_RATE, RSCP);
+ expected.setCellIdentity(ci);
+ expected.setCellSignalStrength(cs);
+ assertEquals(expected, cellInfoTdscdma);
+ }
+
+ @Test
public void testConvertHalCellInfoListForCdma() throws Exception {
android.hardware.radio.V1_0.CellInfoCdma cellinfo =
new android.hardware.radio.V1_0.CellInfoCdma();
@@ -1330,7 +1423,8 @@
expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
CellIdentityWcdma ci = new CellIdentityWcdma(
LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
- CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(SIGNAL_STRENGTH, BIT_ERROR_RATE);
+ CellSignalStrengthWcdma cs =
+ new CellSignalStrengthWcdma(SIGNAL_STRENGTH, BIT_ERROR_RATE, RSCP, ECNO);
expected.setCellIdentity(ci);
expected.setCellSignalStrength(cs);
expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
@@ -1350,7 +1444,8 @@
expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
CellIdentityWcdma ci = new CellIdentityWcdma(
LAC, CID, PSC, UARFCN, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
- CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(SIGNAL_STRENGTH, BIT_ERROR_RATE);
+ CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
+ SIGNAL_STRENGTH, BIT_ERROR_RATE, RSCP, ECNO);
expected.setCellIdentity(ci);
expected.setCellSignalStrength(cs);
expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
@@ -1372,7 +1467,8 @@
expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
CellIdentityWcdma ci = new CellIdentityWcdma(
LAC, CID, PSC, UARFCN, null, null, ALPHA_LONG, ALPHA_SHORT);
- CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(SIGNAL_STRENGTH, BIT_ERROR_RATE);
+ CellSignalStrengthWcdma cs = new CellSignalStrengthWcdma(
+ SIGNAL_STRENGTH, BIT_ERROR_RATE, RSCP, ECNO);
expected.setCellIdentity(ci);
expected.setCellSignalStrength(cs);
expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
@@ -1495,8 +1591,8 @@
cellinfo.cellIdentityWcdma.operatorNames.alphaShort = alphaShort;
cellinfo.signalStrengthWcdma.base.signalStrength = SIGNAL_STRENGTH;
cellinfo.signalStrengthWcdma.base.bitErrorRate = BIT_ERROR_RATE;
- cellinfo.signalStrengthWcdma.rscp = 10;
- cellinfo.signalStrengthWcdma.ecno = 5;
+ cellinfo.signalStrengthWcdma.rscp = RSCP;
+ cellinfo.signalStrengthWcdma.ecno = ECNO;
android.hardware.radio.V1_2.CellInfo record = new android.hardware.radio.V1_2.CellInfo();
record.cellInfoType = TYPE_WCDMA;
record.registered = false;
@@ -1597,4 +1693,37 @@
assertEquals(getTdScdmaSignalStrength_1_0(-1), getTdScdmaSignalStrength_1_2(255));
}
+ @Test
+ public void testSetupDataCall() throws Exception {
+
+ DataProfile dp = new DataProfile(PROFILE_ID, APN, PROTOCOL, AUTH_TYPE, USER_NAME, PASSWORD,
+ TYPE, MAX_CONNS_TIME, MAX_CONNS, WAIT_TIME, APN_ENABLED, SUPPORTED_APNT_YPES_BITMAP,
+ ROAMING_PROTOCOL, BEARER_BITMAP, MTU, MVNO_TYPE, MVNO_MATCH_DATA, MODEM_COGNITIVE);
+ mRILUnderTest.setupDataCall(AccessNetworkConstants.AccessNetworkType.EUTRAN, dp, false,
+ false, 0, null, obtainMessage());
+ ArgumentCaptor<DataProfileInfo> dpiCaptor = ArgumentCaptor.forClass(DataProfileInfo.class);
+ verify(mRadioProxy).setupDataCall(
+ mSerialNumberCaptor.capture(), eq(AccessNetworkConstants.AccessNetworkType.EUTRAN),
+ dpiCaptor.capture(), eq(true), eq(false), eq(false));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SETUP_DATA_CALL);
+ DataProfileInfo dpi = dpiCaptor.getValue();
+ assertEquals(PROFILE_ID, dpi.profileId);
+ assertEquals(APN, dpi.apn);
+ assertEquals(PROTOCOL, dpi.protocol);
+ assertEquals(AUTH_TYPE, dpi.authType);
+ assertEquals(USER_NAME, dpi.user);
+ assertEquals(PASSWORD, dpi.password);
+ assertEquals(TYPE, dpi.type);
+ assertEquals(MAX_CONNS_TIME, dpi.maxConnsTime);
+ assertEquals(MAX_CONNS, dpi.maxConns);
+ assertEquals(WAIT_TIME, dpi.waitTime);
+ assertEquals(APN_ENABLED, dpi.enabled);
+ assertEquals(SUPPORTED_APNT_YPES_BITMAP, dpi.supportedApnTypesBitmap);
+ assertEquals(ROAMING_PROTOCOL, dpi.protocol);
+ assertEquals(BEARER_BITMAP, dpi.bearerBitmap);
+ assertEquals(MTU, dpi.mtu);
+ assertEquals(0, dpi.mvnoType);
+ assertEquals(MVNO_MATCH_DATA, dpi.mvnoMatchData);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
index a6bbfe2..55fde49 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
@@ -28,6 +28,7 @@
import junit.framework.TestCase;
import java.util.ArrayList;
+import java.util.Arrays;
public class ServiceStateTest extends TestCase {
@@ -165,6 +166,17 @@
}
}
}
+ @SmallTest
+ public void testGetCellBandwidths() {
+ ServiceState ss = new ServiceState();
+
+ ss.setCellBandwidths(null);
+ assertTrue(Arrays.equals(ss.getCellBandwidths(), new int[0]));
+
+ int[] cellBandwidths = new int[]{5000, 10000};
+ ss.setCellBandwidths(cellBandwidths);
+ assertTrue(Arrays.equals(ss.getCellBandwidths(), cellBandwidths));
+ }
@SmallTest
public void testOperatorName() {
@@ -199,9 +211,9 @@
ss.setIsManualSelection(true);
assertTrue(ss.getIsManualSelection());
- ss.setSystemAndNetworkId(123, 456);
- assertEquals(123, ss.getSystemId());
- assertEquals(456, ss.getNetworkId());
+ ss.setCdmaSystemAndNetworkId(123, 456);
+ assertEquals(123, ss.getCdmaSystemId());
+ assertEquals(456, ss.getCdmaNetworkId());
ss.setEmergencyOnly(true);
assertTrue(ss.isEmergencyOnly());
@@ -220,7 +232,7 @@
ss.setRilVoiceRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
ss.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0);
ss.setCssIndicator(1);
- ss.setSystemAndNetworkId(2, 3);
+ ss.setCdmaSystemAndNetworkId(2, 3);
ss.setCdmaRoamingIndicator(4);
ss.setCdmaDefaultRoamingIndicator(5);
ss.setCdmaEriIconIndex(6);
@@ -250,7 +262,7 @@
ss.setRilVoiceRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
ss.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0);
ss.setCssIndicator(1);
- ss.setSystemAndNetworkId(2, 3);
+ ss.setCdmaSystemAndNetworkId(2, 3);
ss.setCdmaRoamingIndicator(4);
ss.setCdmaDefaultRoamingIndicator(5);
ss.setCdmaEriIconIndex(6);
@@ -270,18 +282,18 @@
@SmallTest
public void testNetworkRegistrationState() {
NetworkRegistrationState wwanVoiceRegState = new NetworkRegistrationState(
- AccessNetworkConstants.TransportType.WWAN, NetworkRegistrationState.DOMAIN_CS,
+ NetworkRegistrationState.DOMAIN_CS, AccessNetworkConstants.TransportType.WWAN,
0, 0, 0, false,
null, null, true, 0, 0, 0);
NetworkRegistrationState wwanDataRegState = new NetworkRegistrationState(
- AccessNetworkConstants.TransportType.WWAN, NetworkRegistrationState.DOMAIN_PS,
+ NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN,
0, 0, 0, false,
null, null, 0);
NetworkRegistrationState wlanRegState = new NetworkRegistrationState(
- AccessNetworkConstants.TransportType.WLAN, NetworkRegistrationState.DOMAIN_PS,
+ NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WLAN,
0, 0, 0, false,
null, null);
@@ -291,19 +303,59 @@
ss.addNetworkRegistrationState(wwanDataRegState);
ss.addNetworkRegistrationState(wlanRegState);
- assertEquals(ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WWAN,
- NetworkRegistrationState.DOMAIN_CS), wwanVoiceRegState);
- assertEquals(ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WWAN,
- NetworkRegistrationState.DOMAIN_PS), wwanDataRegState);
- assertEquals(ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WLAN,
- NetworkRegistrationState.DOMAIN_PS), wlanRegState);
+ assertEquals(ss.getNetworkRegistrationStates(NetworkRegistrationState.DOMAIN_CS,
+ AccessNetworkConstants.TransportType.WWAN), wwanVoiceRegState);
+ assertEquals(ss.getNetworkRegistrationStates(NetworkRegistrationState.DOMAIN_PS,
+ AccessNetworkConstants.TransportType.WWAN), wwanDataRegState);
+ assertEquals(ss.getNetworkRegistrationStates(NetworkRegistrationState.DOMAIN_PS,
+ AccessNetworkConstants.TransportType.WLAN), wlanRegState);
wwanDataRegState = new NetworkRegistrationState(
- AccessNetworkConstants.TransportType.WWAN, NetworkRegistrationState.DOMAIN_PS,
+ NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN,
0, 0, 0, true,
null, null, 0);
ss.addNetworkRegistrationState(wwanDataRegState);
- assertEquals(ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WWAN,
- NetworkRegistrationState.DOMAIN_PS), wwanDataRegState);
+ assertEquals(ss.getNetworkRegistrationStates(NetworkRegistrationState.DOMAIN_PS,
+ AccessNetworkConstants.TransportType.WWAN), wwanDataRegState);
+ }
+
+ @SmallTest
+ public void testDuplexMode_notLte() {
+ ServiceState ss = new ServiceState();
+ ss.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_GSM);
+ ss.setChannelNumber(2400);
+
+ assertEquals(ss.getDuplexMode(), ServiceState.DUPLEX_MODE_UNKNOWN);
+ }
+
+ @SmallTest
+ public void testDuplexMode_invalidEarfcn() {
+ ServiceState ss = new ServiceState();
+ ss.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ ss.setChannelNumber(-1);
+
+ assertEquals(ss.getDuplexMode(), ServiceState.DUPLEX_MODE_UNKNOWN);
+
+ ss.setChannelNumber(Integer.MAX_VALUE);
+
+ assertEquals(ss.getDuplexMode(), ServiceState.DUPLEX_MODE_UNKNOWN);
+ }
+
+ @SmallTest
+ public void testDuplexMode_FddChannel() {
+ ServiceState ss = new ServiceState();
+ ss.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ ss.setChannelNumber(2400); // band 5
+
+ assertEquals(ss.getDuplexMode(), ServiceState.DUPLEX_MODE_FDD);
+ }
+
+ @SmallTest
+ public void testDuplexMode_TddChannel() {
+ ServiceState ss = new ServiceState();
+ ss.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ ss.setChannelNumber(36000); // band 33
+
+ assertEquals(ss.getDuplexMode(), ServiceState.DUPLEX_MODE_TDD);
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 74aec16..603f800 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -25,17 +25,26 @@
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.nullable;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.IAlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.Handler;
@@ -48,26 +57,30 @@
import android.os.UserHandle;
import android.os.WorkSource;
import android.support.test.filters.FlakyTest;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.CarrierConfigManager;
import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
import android.telephony.CellInfo;
import android.telephony.CellInfoGsm;
import android.telephony.NetworkRegistrationState;
import android.telephony.NetworkService;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Pair;
+import android.util.TimestampedValue;
import com.android.internal.R;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.dataconnection.DcTracker;
import com.android.internal.telephony.test.SimulatedCommands;
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
-import com.android.internal.telephony.util.TimeStampedValue;
import org.junit.After;
import org.junit.Before;
@@ -75,8 +88,10 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -126,7 +141,7 @@
mCellularNetworkService = new CellularNetworkService();
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.packageName = "com.android.phone";
- serviceInfo.permission = "android.permission.BIND_NETWORK_SERVICE";
+ serviceInfo.permission = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
IntentFilter filter = new IntentFilter();
mContextFixture.addService(
NetworkService.NETWORK_SERVICE_INTERFACE,
@@ -158,6 +173,10 @@
mBundle.putStringArray(
CarrierConfigManager.KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, new String[]{"123456"});
+ mBundle.putStringArray(CarrierConfigManager.KEY_RATCHET_RAT_FAMILIES,
+ // UMTS < GPRS < EDGE
+ new String[]{"3,1,2"});
+
mSimulatedCommands.setVoiceRegState(NetworkRegistrationState.REG_STATE_HOME);
mSimulatedCommands.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_HSPA);
mSimulatedCommands.setDataRegState(NetworkRegistrationState.REG_STATE_HOME);
@@ -428,6 +447,41 @@
}
@Test
+ public void testSetsNewSignalStrengthReportingCriteria() {
+ int[] wcdmaThresholds = {
+ -110, /* SIGNAL_STRENGTH_POOR */
+ -100, /* SIGNAL_STRENGTH_MODERATE */
+ -90, /* SIGNAL_STRENGTH_GOOD */
+ -80 /* SIGNAL_STRENGTH_GREAT */
+ };
+ mBundle.putIntArray(CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY,
+ wcdmaThresholds);
+
+ int[] lteThresholds = {
+ -130, /* SIGNAL_STRENGTH_POOR */
+ -120, /* SIGNAL_STRENGTH_MODERATE */
+ -110, /* SIGNAL_STRENGTH_GOOD */
+ -100, /* SIGNAL_STRENGTH_GREAT */
+ };
+ mBundle.putIntArray(CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+ lteThresholds);
+
+ CarrierConfigManager mockConfigManager = Mockito.mock(CarrierConfigManager.class);
+ when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+ .thenReturn(mockConfigManager);
+ when(mockConfigManager.getConfigForSubId(anyInt())).thenReturn(mBundle);
+
+ Intent intent = new Intent().setAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ mContext.sendBroadcast(intent);
+ waitForMs(300);
+
+ verify(mPhone).setSignalStrengthReportingCriteria(eq(wcdmaThresholds),
+ eq(AccessNetworkType.UTRAN));
+ verify(mPhone).setSignalStrengthReportingCriteria(eq(lteThresholds),
+ eq(AccessNetworkType.EUTRAN));
+ }
+
+ @Test
@MediumTest
public void testSignalLevelWithWcdmaRscpThresholds() {
mBundle.putIntArray(CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY,
@@ -1160,6 +1214,127 @@
assertEquals(intArgumentCaptor.getValue().intValue(), restrictedState[1]);
}
+ private boolean notificationHasTitleSet(Notification n) {
+ // Notification has no methods to check the actual title, but #toString() includes the
+ // word "tick" if the title is set so we check this as a workaround
+ return n.toString().contains("tick");
+ }
+
+ private String getNotificationTitle(Notification n) {
+ return n.extras.getString(Notification.EXTRA_TITLE);
+ }
+
+ @Test
+ @SmallTest
+ public void testSetPsNotifications() {
+ sst.mSubId = 1;
+ final NotificationManager nm = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mContextFixture.putBooleanResource(
+ R.bool.config_user_notification_of_restrictied_mobile_access, true);
+ doReturn(new ApplicationInfo()).when(mContext).getApplicationInfo();
+ Drawable mockDrawable = mock(Drawable.class);
+ Resources mockResources = mContext.getResources();
+ when(mockResources.getDrawable(anyInt(), any())).thenReturn(mockDrawable);
+
+ mContextFixture.putResource(com.android.internal.R.string.RestrictedOnDataTitle, "test1");
+ sst.setNotification(ServiceStateTracker.PS_ENABLED);
+ ArgumentCaptor<Notification> notificationArgumentCaptor =
+ ArgumentCaptor.forClass(Notification.class);
+ verify(nm).notify(anyString(), anyInt(), notificationArgumentCaptor.capture());
+ // if the postedNotification has title set then it must have been the correct notification
+ Notification postedNotification = notificationArgumentCaptor.getValue();
+ assertTrue(notificationHasTitleSet(postedNotification));
+ assertEquals("test1", getNotificationTitle(postedNotification));
+
+ sst.setNotification(ServiceStateTracker.PS_DISABLED);
+ verify(nm).cancel(anyString(), anyInt());
+ }
+
+ @Test
+ @SmallTest
+ public void testSetCsNotifications() {
+ sst.mSubId = 1;
+ final NotificationManager nm = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mContextFixture.putBooleanResource(
+ R.bool.config_user_notification_of_restrictied_mobile_access, true);
+ doReturn(new ApplicationInfo()).when(mContext).getApplicationInfo();
+ Drawable mockDrawable = mock(Drawable.class);
+ Resources mockResources = mContext.getResources();
+ when(mockResources.getDrawable(anyInt(), any())).thenReturn(mockDrawable);
+
+ mContextFixture.putResource(com.android.internal.R.string.RestrictedOnAllVoiceTitle,
+ "test2");
+ sst.setNotification(ServiceStateTracker.CS_ENABLED);
+ ArgumentCaptor<Notification> notificationArgumentCaptor =
+ ArgumentCaptor.forClass(Notification.class);
+ verify(nm).notify(anyString(), anyInt(), notificationArgumentCaptor.capture());
+ // if the postedNotification has title set then it must have been the correct notification
+ Notification postedNotification = notificationArgumentCaptor.getValue();
+ assertTrue(notificationHasTitleSet(postedNotification));
+ assertEquals("test2", getNotificationTitle(postedNotification));
+
+ sst.setNotification(ServiceStateTracker.CS_DISABLED);
+ verify(nm).cancel(anyString(), anyInt());
+ }
+
+ @Test
+ @SmallTest
+ public void testSetCsNormalNotifications() {
+ sst.mSubId = 1;
+ final NotificationManager nm = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mContextFixture.putBooleanResource(
+ R.bool.config_user_notification_of_restrictied_mobile_access, true);
+ doReturn(new ApplicationInfo()).when(mContext).getApplicationInfo();
+ Drawable mockDrawable = mock(Drawable.class);
+ Resources mockResources = mContext.getResources();
+ when(mockResources.getDrawable(anyInt(), any())).thenReturn(mockDrawable);
+
+ mContextFixture.putResource(com.android.internal.R.string.RestrictedOnNormalTitle, "test3");
+ sst.setNotification(ServiceStateTracker.CS_NORMAL_ENABLED);
+ ArgumentCaptor<Notification> notificationArgumentCaptor =
+ ArgumentCaptor.forClass(Notification.class);
+ verify(nm).notify(anyString(), anyInt(), notificationArgumentCaptor.capture());
+ // if the postedNotification has title set then it must have been the correct notification
+ Notification postedNotification = notificationArgumentCaptor.getValue();
+ assertTrue(notificationHasTitleSet(postedNotification));
+ assertEquals("test3", getNotificationTitle(postedNotification));
+
+ sst.setNotification(ServiceStateTracker.CS_DISABLED);
+ verify(nm).cancel(anyString(), anyInt());
+ }
+
+ @Test
+ @SmallTest
+ public void testSetCsEmergencyNotifications() {
+ sst.mSubId = 1;
+ final NotificationManager nm = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mContextFixture.putBooleanResource(
+ R.bool.config_user_notification_of_restrictied_mobile_access, true);
+ doReturn(new ApplicationInfo()).when(mContext).getApplicationInfo();
+ Drawable mockDrawable = mock(Drawable.class);
+ Resources mockResources = mContext.getResources();
+ when(mockResources.getDrawable(anyInt(), any())).thenReturn(mockDrawable);
+
+ mContextFixture.putResource(com.android.internal.R.string.RestrictedOnEmergencyTitle,
+ "test4");
+ sst.setNotification(ServiceStateTracker.CS_EMERGENCY_ENABLED);
+ ArgumentCaptor<Notification> notificationArgumentCaptor =
+ ArgumentCaptor.forClass(Notification.class);
+ verify(nm).notify(anyString(), anyInt(), notificationArgumentCaptor.capture());
+ // if the postedNotification has title set then it must have been the correct notification
+ Notification postedNotification = notificationArgumentCaptor.getValue();
+ assertTrue(notificationHasTitleSet(postedNotification));
+ assertEquals("test4", getNotificationTitle(postedNotification));
+
+ sst.setNotification(ServiceStateTracker.CS_DISABLED);
+ verify(nm).cancel(anyString(), anyInt());
+ sst.setNotification(ServiceStateTracker.CS_REJECT_CAUSE_ENABLED);
+ }
+
@Test
@MediumTest
public void testRegisterForSubscriptionInfoReady() {
@@ -1319,15 +1494,234 @@
mSimulatedCommands.triggerNITZupdate(nitzStr);
waitForMs(200);
- ArgumentCaptor<TimeStampedValue<NitzData>> argumentsCaptor =
- ArgumentCaptor.forClass(TimeStampedValue.class);
+ ArgumentCaptor<TimestampedValue<NitzData>> argumentsCaptor =
+ ArgumentCaptor.forClass(TimestampedValue.class);
verify(mNitzStateMachine, times(1))
.handleNitzReceived(argumentsCaptor.capture());
// Confirm the argument was what we expected.
- TimeStampedValue<NitzData> actualNitzSignal = argumentsCaptor.getValue();
- assertEquals(expectedNitzData, actualNitzSignal.mValue);
- assertTrue(actualNitzSignal.mElapsedRealtime <= SystemClock.elapsedRealtime());
+ TimestampedValue<NitzData> actualNitzSignal = argumentsCaptor.getValue();
+ assertEquals(expectedNitzData, actualNitzSignal.getValue());
+ assertTrue(actualNitzSignal.getReferenceTimeMillis() <= SystemClock.elapsedRealtime());
}
}
+
+ // Edge and GPRS are grouped under the same family and Edge has higher rate than GPRS.
+ // Expect no rat update when move from E to G.
+ @Test
+ public void testRatRatchet() throws Exception {
+ CellIdentityGsm cellIdentity = new CellIdentityGsm(-1, -1, -1, -1, -1, -1);
+ NetworkRegistrationState dataResult = new NetworkRegistrationState(
+ 0, 0, 1, 2, 0, false, null, cellIdentity, 1);
+ sst.mPollingContext[0] = 2;
+ // update data reg state to be in service
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.STATE_IN_SERVICE, mSST.getCurrentDataConnectionState());
+
+ NetworkRegistrationState voiceResult = new NetworkRegistrationState(
+ 0, 0, 1, 2, 0, false, null, cellIdentity, false, 0, 0, 0);
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_EDGE, sst.mSS.getRilVoiceRadioTechnology());
+
+ // EDGE -> GPRS
+ voiceResult = new NetworkRegistrationState(
+ 0, 0, 1, 1, 0, false, null,
+ cellIdentity, false, 0, 0, 0);
+ sst.mPollingContext[0] = 2;
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.STATE_IN_SERVICE, mSST.getCurrentDataConnectionState());
+
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_EDGE, sst.mSS.getRilVoiceRadioTechnology());
+ }
+
+ // Edge and GPRS are grouped under the same family and Edge has higher rate than GPRS.
+ // Bypass rat rachet when cell id changed. Expect rat update from E to G
+ @Test
+ public void testRatRatchetWithCellChange() throws Exception {
+ CellIdentityGsm cellIdentity = new CellIdentityGsm(-1, -1, -1, -1, -1, -1);
+ NetworkRegistrationState dataResult = new NetworkRegistrationState(
+ 0, 0, 1, 2, 0, false, null, cellIdentity, 1);
+ sst.mPollingContext[0] = 2;
+ // update data reg state to be in service
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.STATE_IN_SERVICE, mSST.getCurrentDataConnectionState());
+
+ NetworkRegistrationState voiceResult = new NetworkRegistrationState(
+ 0, 0, 1, 2, 0, false, null, cellIdentity, false, 0, 0, 0);
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_EDGE, sst.mSS.getRilVoiceRadioTechnology());
+
+ // RAT: EDGE -> GPRS cell ID: -1 -> 5
+ cellIdentity = new CellIdentityGsm(-1, -1, -1, 5, -1, -1);
+ voiceResult = new NetworkRegistrationState(
+ 0, 0, 1, 1, 0, false, null, cellIdentity, false, 0, 0, 0);
+ sst.mPollingContext[0] = 2;
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.STATE_IN_SERVICE, mSST.getCurrentDataConnectionState());
+
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_GPRS, sst.mSS.getRilVoiceRadioTechnology());
+ }
+
+ // Edge, GPRS and UMTS are grouped under the same family where Edge > GPRS > UMTS .
+ // Expect no rat update from E to G immediately following cell id change.
+ // Expect ratratchet (from G to UMTS) for the following rat update within the cell location.
+ @Test
+ public void testRatRatchetWithCellChangeBeforeRatChange() throws Exception {
+ // cell ID update
+ CellIdentityGsm cellIdentity = new CellIdentityGsm(-1, -1, -1, 5, -1, -1);
+ NetworkRegistrationState dataResult = new NetworkRegistrationState(
+ 0, 0, 1, 2, 0, false, null, cellIdentity, 1);
+ sst.mPollingContext[0] = 2;
+ // update data reg state to be in service
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.STATE_IN_SERVICE, mSST.getCurrentDataConnectionState());
+
+ NetworkRegistrationState voiceResult = new NetworkRegistrationState(
+ 0, 0, 1, 2, 0, false, null, cellIdentity, false, 0, 0, 0);
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_EDGE, sst.mSS.getRilVoiceRadioTechnology());
+
+ // RAT: EDGE -> GPRS, cell ID unchanged. Expect no rat ratchet following cell Id change.
+ voiceResult = new NetworkRegistrationState(
+ 0, 0, 1, 1, 0, false, null, cellIdentity, false, 0, 0, 0);
+ sst.mPollingContext[0] = 2;
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.STATE_IN_SERVICE, mSST.getCurrentDataConnectionState());
+
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_GPRS, sst.mSS.getRilVoiceRadioTechnology());
+
+ // RAT: GPRS -> UMTS
+ voiceResult = new NetworkRegistrationState(
+ 0, 0, 1, 3, 0, false, null, cellIdentity, false, 0, 0, 0);
+ sst.mPollingContext[0] = 2;
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.STATE_IN_SERVICE, mSST.getCurrentDataConnectionState());
+
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_GPRS, sst.mSS.getRilVoiceRadioTechnology());
+ }
+
+ private void sendPhyChanConfigChange(int[] bandwidths) {
+ ArrayList<PhysicalChannelConfig> pc = new ArrayList<>();
+ int ssType = PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING;
+ for (int bw : bandwidths) {
+ pc.add(new PhysicalChannelConfig(ssType, bw));
+
+ // All cells after the first are secondary serving cells.
+ ssType = PhysicalChannelConfig.CONNECTION_SECONDARY_SERVING;
+ }
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_PHYSICAL_CHANNEL_CONFIG,
+ new AsyncResult(null, pc, null)));
+ waitForMs(100);
+ }
+
+ private void sendRegStateUpdateForLteCellId(CellIdentityLte cellId) {
+ NetworkRegistrationState dataResult = new NetworkRegistrationState(
+ 2, 1, 1, TelephonyManager.NETWORK_TYPE_LTE, 0, false, null, cellId, 1);
+ NetworkRegistrationState voiceResult = new NetworkRegistrationState(
+ 1, 1, 1, TelephonyManager.NETWORK_TYPE_LTE, 0, false, null, cellId,
+ false, 0, 0, 0);
+ sst.mPollingContext[0] = 2;
+ // update data reg state to be in service
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ }
+
+ @Test
+ public void testPhyChanBandwidthUpdatedOnDataRegState() throws Exception {
+ // Cell ID change should trigger hasLocationChanged.
+ CellIdentityLte cellIdentity5 =
+ new CellIdentityLte(1, 1, 5, 1, 5000, "001", "01", "test", "tst");
+
+ sendPhyChanConfigChange(new int[] {10000});
+ sendRegStateUpdateForLteCellId(cellIdentity5);
+ assertTrue(Arrays.equals(new int[] {5000}, sst.mSS.getCellBandwidths()));
+ }
+
+ @Test
+ public void testPhyChanBandwidthNotUpdatedWhenInvalidInCellIdentity() throws Exception {
+ // Cell ID change should trigger hasLocationChanged.
+ CellIdentityLte cellIdentityInv =
+ new CellIdentityLte(1, 1, 5, 1, 12345, "001", "01", "test", "tst");
+
+ sendPhyChanConfigChange(new int[] {10000});
+ sendRegStateUpdateForLteCellId(cellIdentityInv);
+ assertTrue(Arrays.equals(new int[] {10000}, sst.mSS.getCellBandwidths()));
+ }
+
+ @Test
+ public void testPhyChanBandwidthPrefersCarrierAggregationReport() throws Exception {
+ // Cell ID change should trigger hasLocationChanged.
+ CellIdentityLte cellIdentity10 =
+ new CellIdentityLte(1, 1, 5, 1, 10000, "001", "01", "test", "tst");
+
+ sendPhyChanConfigChange(new int[] {10000, 5000});
+ sendRegStateUpdateForLteCellId(cellIdentity10);
+ assertTrue(Arrays.equals(new int[] {10000, 5000}, sst.mSS.getCellBandwidths()));
+ }
+
+ @Test
+ public void testPhyChanBandwidthRatchetedOnPhyChanBandwidth() throws Exception {
+ // LTE Cell with bandwidth = 10000
+ CellIdentityLte cellIdentity10 =
+ new CellIdentityLte(1, 1, 1, 1, 10000, "1", "1", "test", "tst");
+
+ sendRegStateUpdateForLteCellId(cellIdentity10);
+ assertTrue(Arrays.equals(new int[] {10000}, sst.mSS.getCellBandwidths()));
+ sendPhyChanConfigChange(new int[] {10000, 5000});
+ assertTrue(Arrays.equals(new int[] {10000, 5000}, sst.mSS.getCellBandwidths()));
+ }
+
+ @Test
+ public void testPhyChanBandwidthResetsOnOos() throws Exception {
+ testPhyChanBandwidthRatchetedOnPhyChanBandwidth();
+ NetworkRegistrationState dataResult = new NetworkRegistrationState(
+ 2, 1, 0, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null, 1);
+ NetworkRegistrationState voiceResult = new NetworkRegistrationState(
+ 1, 1, 0, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null,
+ false, 0, 0, 0);
+ sst.mPollingContext[0] = 2;
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_GPRS,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForMs(200);
+ sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_POLL_STATE_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForMs(200);
+ assertTrue(Arrays.equals(new int[0], sst.mSS.getCellBandwidths()));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
index ef9f7d4..a237010 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
@@ -65,7 +65,9 @@
SubscriptionManager.NAME_SOURCE, SubscriptionManager.COLOR,
SubscriptionManager.NUMBER, SubscriptionManager.DISPLAY_NUMBER_FORMAT,
SubscriptionManager.DATA_ROAMING, SubscriptionManager.MCC,
- SubscriptionManager.MNC, SubscriptionManager.CB_EXTREME_THREAT_ALERT,
+ SubscriptionManager.MNC, SubscriptionManager.MCC_STRING,
+ SubscriptionManager.MNC_STRING,
+ SubscriptionManager.CB_EXTREME_THREAT_ALERT,
SubscriptionManager.CB_SEVERE_THREAT_ALERT, SubscriptionManager.CB_AMBER_ALERT,
SubscriptionManager.CB_ALERT_SOUND_DURATION,
SubscriptionManager.CB_ALERT_REMINDER_INTERVAL,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
index ac97937..6770e9a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
@@ -15,15 +15,15 @@
*/
package com.android.internal.telephony;
-import android.telephony.SubscriptionManager;
+import static org.junit.Assert.assertEquals;
+
+import android.telephony.SubscriptionInfo;
import android.test.suitebuilder.annotation.SmallTest;
-import static org.junit.Assert.*;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import android.telephony.SubscriptionInfo;
-
public class SubscriptionInfoTest {
private SubscriptionInfo mSubscriptionInfoUT;
@@ -35,7 +35,7 @@
@Before
public void setUp() throws Exception {
mSubscriptionInfoUT = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0, "T-mobile",
- "T-mobile", 0, 255, "12345", 0, null, 310, 260, "156");
+ "T-mobile", 0, 255, "12345", 0, null, "310", "260", "156");
}
@Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
index 9efed51..33782b2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
@@ -52,7 +52,6 @@
import com.android.internal.telephony.euicc.EuiccController;
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccRecords;
-import com.android.internal.telephony.uicc.UiccProfile;
import org.junit.After;
import org.junit.Before;
@@ -167,14 +166,10 @@
@SmallTest
public void testSimAbsent() throws Exception {
doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
+ .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1), anyBoolean());
doReturn(new int[]{FAKE_SUB_ID_1}).when(mSubscriptionController).getActiveSubIdList();
- Intent mIntent = new Intent(UiccProfile.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_ABSENT);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
-
- mContext.sendBroadcast(mIntent);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, FAKE_SUB_ID_1);
waitForMs(100);
verify(mSubscriptionContent).put(eq(SubscriptionManager.SIM_SLOT_INDEX),
@@ -191,12 +186,8 @@
@Test
@SmallTest
public void testSimUnknown() throws Exception {
- Intent mIntent = new Intent(UiccProfile.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
-
- mContext.sendBroadcast(mIntent);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null, FAKE_SUB_ID_1);
waitForMs(100);
verify(mSubscriptionContent, times(0)).put(anyString(), any());
@@ -211,17 +202,14 @@
@Test
@SmallTest
public void testSimError() throws Exception {
- Intent mIntent = new Intent(UiccProfile.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR, null, FAKE_SUB_ID_1);
- mContext.sendBroadcast(mIntent);
waitForMs(100);
verify(mSubscriptionContent, times(0)).put(anyString(), any());
CarrierConfigManager mConfigManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager).updateConfigForPhoneId(eq(0),
+ verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
eq(IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR));
verify(mSubscriptionController, times(0)).clearSubInfo();
verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
@@ -230,12 +218,9 @@
@Test
@SmallTest
public void testWrongSimState() throws Exception {
- Intent mIntent = new Intent(UiccProfile.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_IMSI);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, 2);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_IMSI, null, 2);
- mContext.sendBroadcast(mIntent);
waitForMs(100);
verify(mSubscriptionContent, times(0)).put(anyString(), any());
CarrierConfigManager mConfigManager = (CarrierConfigManager)
@@ -251,16 +236,13 @@
public void testSimLoaded() throws Exception {
/* mock new sim got loaded and there is no sim loaded before */
doReturn(null).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
+ .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1), anyBoolean());
doReturn("89012604200000000000").when(mIccRecord).getFullIccId();
doReturn(FAKE_MCC_MNC_1).when(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
- Intent intentInternalSimStateChanged =
- new Intent(UiccProfile.ACTION_INTERNAL_SIM_STATE_CHANGED);
- intentInternalSimStateChanged.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_LOADED);
- intentInternalSimStateChanged.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
- mContext.sendBroadcast(intentInternalSimStateChanged);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1);
+
waitForMs(100);
// verify SIM_STATE_CHANGED broadcast. It should be broadcast twice, once for
@@ -319,16 +301,13 @@
public void testSimLoadedEmptyOperatorNumeric() throws Exception {
/* mock new sim got loaded and there is no sim loaded before */
doReturn(null).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
+ .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1), anyBoolean());
doReturn("89012604200000000000").when(mIccRecord).getFullIccId();
// operator numeric is empty
doReturn("").when(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
- Intent mIntent = new Intent(UiccProfile.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_LOADED);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1);
- mContext.sendBroadcast(mIntent);
waitForMs(100);
SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
verify(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
@@ -351,15 +330,11 @@
doReturn("98106240020000000000").when(mIccRecord).getFullIccId();
doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
+ .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1), anyBoolean());
- Intent mIntent = new Intent(UiccProfile.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_LOCKED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, "TESTING");
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_LOCKED, "TESTING", FAKE_SUB_ID_1);
- mContext.sendBroadcast(mIntent);
waitForMs(100);
/* old IccId != new queried IccId */
@@ -404,16 +379,14 @@
doReturn(FAKE_MCC_MNC_2).when(mTelephonyManager).getSimOperatorNumeric(eq(FAKE_SUB_ID_2));
// Mock there is no sim inserted before
doReturn(null).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexWithCheck(anyInt(), anyBoolean(), anyString());
+ .getSubInfoUsingSlotIndexPrivileged(anyInt(), anyBoolean());
verify(mSubscriptionController, times(0)).clearSubInfo();
doReturn("89012604200000000000").when(mIccRecord).getFullIccId();
// Mock sending a sim loaded for SIM 1
- Intent mIntent = new Intent(UiccProfile.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_LOADED);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
- mContext.sendBroadcast(mIntent);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_1);
+
waitForMs(100);
SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
@@ -423,11 +396,10 @@
// Mock sending a sim loaded for SIM 2
doReturn("89012604200000000001").when(mIccRecord).getFullIccId();
- mIntent = new Intent(UiccProfile.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_LOADED);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_2);
- mContext.sendBroadcast(mIntent);
+
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_2);
+
waitForMs(100);
verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq("89012604200000000000"),
@@ -448,13 +420,9 @@
replaceInstance(SubscriptionInfoUpdater.class, "mIccId", null,
new String[]{"89012604200000000000"});
- Intent mIntent = new Intent(UiccProfile.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_LOCKED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, "TESTING");
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_LOCKED, "TESTING", FAKE_SUB_ID_1);
- mContext.sendBroadcast(mIntent);
waitForMs(100);
SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
@@ -485,11 +453,11 @@
List<SubscriptionInfo> subInfoList = new ArrayList<>();
// 1: not embedded, but has matching iccid with an embedded subscription.
subInfoList.add(new SubscriptionInfo(
- 0, "1", 0, "", "", 0, 0, "", 0, null, 0, 0, "", false /* isEmbedded */,
+ 0, "1", 0, "", "", 0, 0, "", 0, null, "0", "0", "", false /* isEmbedded */,
null /* accessRules */));
// 2: embedded but no longer present.
subInfoList.add(new SubscriptionInfo(
- 0, "2", 0, "", "", 0, 0, "", 0, null, 0, 0, "", true /* isEmbedded */,
+ 0, "2", 0, "", "", 0, 0, "", 0, null, "0", "0", "", true /* isEmbedded */,
null /* accessRules */));
when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
@@ -536,11 +504,11 @@
List<SubscriptionInfo> subInfoList = new ArrayList<>();
// 1: not embedded, but has matching iccid with an embedded subscription.
subInfoList.add(new SubscriptionInfo(
- 0, "1", 0, "", "", 0, 0, "", 0, null, 0, 0, "", false /* isEmbedded */,
+ 0, "1", 0, "", "", 0, 0, "", 0, null, "0", "0", "", false /* isEmbedded */,
null /* accessRules */));
// 2: embedded.
subInfoList.add(new SubscriptionInfo(
- 0, "2", 0, "", "", 0, 0, "", 0, null, 0, 0, "", true /* isEmbedded */,
+ 0, "2", 0, "", "", 0, 0, "", 0, null, "0", "0", "", true /* isEmbedded */,
null /* accessRules */));
when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
@@ -578,7 +546,7 @@
List<SubscriptionInfo> subInfoList = new ArrayList<>();
// 1: not embedded.
subInfoList.add(new SubscriptionInfo(
- 0, "1", 0, "", "", 0, 0, "", 0, null, 0, 0, "", false /* isEmbedded */,
+ 0, "1", 0, "", "", 0, 0, "", 0, null, "0", "0", "", false /* isEmbedded */,
null /* accessRules */));
when(mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
@@ -598,16 +566,14 @@
@SmallTest
public void testHexIccIdSuffix() throws Exception {
doReturn(null).when(mSubscriptionController)
- .getSubInfoUsingSlotIndexWithCheck(anyInt(), anyBoolean(), anyString());
+ .getSubInfoUsingSlotIndexPrivileged(anyInt(), anyBoolean());
verify(mSubscriptionController, times(0)).clearSubInfo();
doReturn("890126042000000000Ff").when(mIccRecord).getFullIccId();
// Mock sending a sim loaded for SIM 1
- Intent mIntent = new Intent(UiccProfile.ACTION_INTERNAL_SIM_STATE_CHANGED);
- mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
- IccCardConstants.INTENT_VALUE_ICC_LOADED);
- mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
- mContext.sendBroadcast(mIntent);
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_LOADED, "TESTING", FAKE_SUB_ID_1);
+
waitForMs(100);
SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
index 7c2057f..3a0d375 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
@@ -27,6 +27,7 @@
import android.app.AppOpsManager;
import android.content.Context;
+import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Before;
@@ -37,6 +38,7 @@
@SmallTest
public class TelephonyPermissionsTest {
+ private static final int SUB_ID = 55555;
private static final int PID = 12345;
private static final int UID = 54321;
private static final String PACKAGE = "com.example";
@@ -46,9 +48,11 @@
private Context mMockContext;
@Mock
private AppOpsManager mMockAppOps;
+ @Mock
+ private ITelephony mMockTelephony;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mMockAppOps);
@@ -59,12 +63,15 @@
.enforcePermission(anyString(), eq(PID), eq(UID), eq(MSG));
when(mMockAppOps.noteOp(anyInt(), eq(UID), eq(PACKAGE)))
.thenReturn(AppOpsManager.MODE_ERRORED);
+ when(mMockTelephony.getCarrierPrivilegeStatusForUid(eq(SUB_ID), eq(UID)))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
}
@Test
public void testCheckReadPhoneState_noPermissions() {
try {
- TelephonyPermissions.checkReadPhoneState(mMockContext, PID, UID, PACKAGE, MSG);
+ TelephonyPermissions.checkReadPhoneState(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG);
fail("Should have thrown SecurityException");
} catch (SecurityException e) {
// expected
@@ -75,7 +82,8 @@
public void testCheckReadPhoneState_hasPrivilegedPermission() {
doNothing().when(mMockContext).enforcePermission(
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, PID, UID, MSG);
- assertTrue(TelephonyPermissions.checkReadPhoneState(mMockContext, PID, UID, PACKAGE, MSG));
+ assertTrue(TelephonyPermissions.checkReadPhoneState(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
}
@Test
@@ -84,20 +92,31 @@
android.Manifest.permission.READ_PHONE_STATE, PID, UID, MSG);
when(mMockAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, UID, PACKAGE))
.thenReturn(AppOpsManager.MODE_ALLOWED);
- assertTrue(TelephonyPermissions.checkReadPhoneState(mMockContext, PID, UID, PACKAGE, MSG));
+ assertTrue(TelephonyPermissions.checkReadPhoneState(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
}
@Test
public void testCheckReadPhoneState_hasPermissionWithoutAppOp() {
doNothing().when(mMockContext).enforcePermission(
android.Manifest.permission.READ_PHONE_STATE, PID, UID, MSG);
- assertFalse(TelephonyPermissions.checkReadPhoneState(mMockContext, PID, UID, PACKAGE, MSG));
+ assertFalse(TelephonyPermissions.checkReadPhoneState(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
+ }
+
+ @Test
+ public void testCheckReadPhoneState_hasCarrierPrivileges() throws Exception {
+ when(mMockTelephony.getCarrierPrivilegeStatusForUid(eq(SUB_ID), eq(UID)))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ assertTrue(TelephonyPermissions.checkReadPhoneState(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
}
@Test
public void testCheckReadPhoneNumber_noPermissions() {
try {
- TelephonyPermissions.checkReadPhoneNumber(mMockContext, PID, UID, PACKAGE, MSG);
+ TelephonyPermissions.checkReadPhoneNumber(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG);
fail("Should have thrown SecurityException");
} catch (SecurityException e) {
// expected
@@ -108,14 +127,16 @@
public void testCheckReadPhoneNumber_defaultSmsApp() {
when(mMockAppOps.noteOp(AppOpsManager.OP_WRITE_SMS, UID, PACKAGE))
.thenReturn(AppOpsManager.MODE_ALLOWED);
- assertTrue(TelephonyPermissions.checkReadPhoneNumber(mMockContext, PID, UID, PACKAGE, MSG));
+ assertTrue(TelephonyPermissions.checkReadPhoneNumber(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
}
@Test
public void testCheckReadPhoneNumber_hasPrivilegedPhoneStatePermission() {
doNothing().when(mMockContext).enforcePermission(
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, PID, UID, MSG);
- assertTrue(TelephonyPermissions.checkReadPhoneNumber(mMockContext, PID, UID, PACKAGE, MSG));
+ assertTrue(TelephonyPermissions.checkReadPhoneNumber(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
}
@Test
@@ -124,7 +145,8 @@
android.Manifest.permission.READ_SMS, PID, UID, MSG);
when(mMockAppOps.noteOp(AppOpsManager.OP_READ_SMS, UID, PACKAGE))
.thenReturn(AppOpsManager.MODE_ALLOWED);
- assertTrue(TelephonyPermissions.checkReadPhoneNumber(mMockContext, PID, UID, PACKAGE, MSG));
+ assertTrue(TelephonyPermissions.checkReadPhoneNumber(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
}
@Test
@@ -133,6 +155,7 @@
android.Manifest.permission.READ_PHONE_NUMBERS, PID, UID, MSG);
when(mMockAppOps.noteOp(AppOpsManager.OP_READ_PHONE_NUMBERS, UID, PACKAGE))
.thenReturn(AppOpsManager.MODE_ALLOWED);
- assertTrue(TelephonyPermissions.checkReadPhoneNumber(mMockContext, PID, UID, PACKAGE, MSG));
+ assertTrue(TelephonyPermissions.checkReadPhoneNumber(
+ mMockContext, () -> mMockTelephony, SUB_ID, PID, UID, PACKAGE, MSG));
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 0f75a94..89d8143 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -39,6 +39,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.IDeviceIdleController;
+import android.os.Looper;
import android.os.RegistrantList;
import android.os.ServiceManager;
import android.provider.BlockedNumberContract;
@@ -64,7 +65,6 @@
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
import com.android.internal.telephony.test.SimulatedCommands;
import com.android.internal.telephony.test.SimulatedCommandsVerifier;
-import com.android.internal.telephony.uicc.IccCardProxy;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IsimUiccRecords;
@@ -109,8 +109,6 @@
@Mock
protected UiccController mUiccController;
@Mock
- protected IccCardProxy mIccCardProxy;
- @Mock
protected UiccProfile mUiccProfile;
@Mock
protected CallManager mCallManager;
@@ -196,6 +194,10 @@
protected NitzStateMachine mNitzStateMachine;
@Mock
protected RadioConfig mMockRadioConfig;
+ @Mock
+ protected SubscriptionInfoUpdater mSubInfoRecordUpdater;
+ @Mock
+ protected LocaleTracker mLocaleTracker;
protected ImsCallProfile mImsCallProfile;
protected TelephonyManager mTelephonyManager;
@@ -328,12 +330,10 @@
doReturn(mSST).when(mTelephonyComponentFactory)
.makeServiceStateTracker(nullable(GsmCdmaPhone.class),
nullable(CommandsInterface.class));
- doReturn(mIccCardProxy).when(mTelephonyComponentFactory)
- .makeIccCardProxy(nullable(Context.class), nullable(CommandsInterface.class),
- anyInt());
doReturn(mUiccProfile).when(mTelephonyComponentFactory)
.makeUiccProfile(nullable(Context.class), nullable(CommandsInterface.class),
- nullable(IccCardStatus.class), anyInt(), nullable(UiccCard.class));
+ nullable(IccCardStatus.class), anyInt(), nullable(UiccCard.class),
+ nullable(Object.class));
doReturn(mCT).when(mTelephonyComponentFactory)
.makeGsmCdmaCallTracker(nullable(GsmCdmaPhone.class));
doReturn(mIccPhoneBookIntManager).when(mTelephonyComponentFactory)
@@ -372,6 +372,8 @@
.makeDeviceStateMonitor(nullable(Phone.class));
doReturn(mNitzStateMachine).when(mTelephonyComponentFactory)
.makeNitzStateMachine(nullable(GsmCdmaPhone.class));
+ doReturn(mLocaleTracker).when(mTelephonyComponentFactory)
+ .makeLocaleTracker(nullable(Phone.class), nullable(Looper.class));
//mPhone
doReturn(mContext).when(mPhone).getContext();
@@ -419,7 +421,7 @@
doReturn(mRuimRecords).when(mUiccCardApplication3gpp2).getIccRecords();
doReturn(mIsimUiccRecords).when(mUiccCardApplicationIms).getIccRecords();
- //mIccCardProxy
+ //mUiccProfile
doReturn(mSimRecords).when(mUiccProfile).getIccRecords();
doAnswer(new Answer<IccRecords>() {
public IccRecords answer(InvocationOnMock invocation) {
@@ -494,6 +496,7 @@
replaceInstance(PhoneFactory.class, "sMadeDefaults", null, true);
replaceInstance(PhoneFactory.class, "sPhone", null, mPhone);
replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+ replaceInstance(PhoneFactory.class, "sSubInfoRecordUpdater", null, mSubInfoRecordUpdater);
replaceInstance(RadioConfig.class, "sRadioConfig", null, mMockRadioConfig);
setReady(false);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
index 8c49c21..6722691 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
@@ -16,9 +16,19 @@
package com.android.internal.telephony.dataconnection;
-import android.net.NetworkCapabilities;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
import android.net.NetworkConfig;
import android.net.NetworkRequest;
+import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.LocalLog;
@@ -32,16 +42,6 @@
import org.junit.Test;
import org.mockito.Mock;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
public class ApnContextTest extends TelephonyTest {
@Mock
@@ -143,14 +143,14 @@
NetworkRequest nr = new NetworkRequest.Builder().build();
mApnContext.requestNetwork(nr, log);
- verify(mDcTracker, times(1)).setEnabled(eq(DctConstants.APN_DEFAULT_ID), eq(true));
+ verify(mDcTracker, times(1)).setEnabled(eq(ApnSetting.TYPE_DEFAULT), eq(true));
mApnContext.requestNetwork(nr, log);
- verify(mDcTracker, times(1)).setEnabled(eq(DctConstants.APN_DEFAULT_ID), eq(true));
+ verify(mDcTracker, times(1)).setEnabled(eq(ApnSetting.TYPE_DEFAULT), eq(true));
mApnContext.releaseNetwork(nr, log);
- verify(mDcTracker, times(1)).setEnabled(eq(DctConstants.APN_DEFAULT_ID), eq(false));
+ verify(mDcTracker, times(1)).setEnabled(eq(ApnSetting.TYPE_DEFAULT), eq(false));
mApnContext.releaseNetwork(nr, log);
- verify(mDcTracker, times(1)).setEnabled(eq(DctConstants.APN_DEFAULT_ID), eq(false));
+ verify(mDcTracker, times(1)).setEnabled(eq(ApnSetting.TYPE_DEFAULT), eq(false));
}
@Test
@@ -176,32 +176,31 @@
public void testProvisionApn() throws Exception {
mContextFixture.putResource(R.string.mobile_provisioning_apn, "fake_apn");
- ApnSetting myApn = new ApnSetting(
+ ApnSetting myApn = ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"fake_apn", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"", // user
"", // password
-1, // authtype
- new String[]{"default", "supl"}, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bismask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
""); // mnvo_match_data
mApnContext.setApnSetting(myApn);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
index 748ddf9..b9738e5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
@@ -16,13 +16,6 @@
package com.android.internal.telephony.dataconnection;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_ALL;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_DEFAULT;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_HIPRI;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_IA;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_MMS;
-import static com.android.internal.telephony.PhoneConstants.APN_TYPE_SUPL;
-
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -30,9 +23,11 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.doReturn;
+import android.net.Uri;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
+import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.PhoneConstants;
@@ -44,6 +39,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
@@ -62,41 +58,40 @@
super.tearDown();
}
- static ApnSetting createApnSetting(String[] apnTypes) {
- return createApnSettingInternal(apnTypes, true);
+ static ApnSetting createApnSetting(int apnTypesBitmask) {
+ return createApnSettingInternal(apnTypesBitmask, true);
}
- private static ApnSetting createDisabledApnSetting(String[] apnTypes) {
- return createApnSettingInternal(apnTypes, false);
+ private static ApnSetting createDisabledApnSetting(int apnTypesBitmask) {
+ return createApnSettingInternal(apnTypesBitmask, false);
}
- private static ApnSetting createApnSettingInternal(String[] apnTypes, boolean carrierEnabled) {
- return new ApnSetting(
+ private static ApnSetting createApnSettingInternal(int apnTypeBitmask, boolean carrierEnabled) {
+ return ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"", // user
"", // password
-1, // authtype
- apnTypes, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ apnTypeBitmask, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
carrierEnabled, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
""); // mnvo_match_data
}
@@ -108,90 +103,84 @@
}
private static void assertApnSettingEqual(ApnSetting a1, ApnSetting a2) {
- assertEquals(a1.carrier, a2.carrier);
- assertEquals(a1.apn, a2.apn);
- assertEquals(a1.proxy, a2.proxy);
- assertEquals(a1.port, a2.port);
- assertEquals(a1.mmsc, a2.mmsc);
- assertEquals(a1.mmsProxy, a2.mmsProxy);
- assertEquals(a1.mmsPort, a2.mmsPort);
- assertEquals(a1.user, a2.user);
- assertEquals(a1.password, a2.password);
- assertEquals(a1.authType, a2.authType);
- assertEquals(a1.id, a2.id);
- assertEquals(a1.numeric, a2.numeric);
- assertEquals(a1.protocol, a2.protocol);
- assertEquals(a1.roamingProtocol, a2.roamingProtocol);
- assertEquals(a1.types.length, a2.types.length);
- int i;
- for (i = 0; i < a1.types.length; i++) {
- assertEquals(a1.types[i], a2.types[i]);
- }
- assertEquals(a1.carrierEnabled, a2.carrierEnabled);
- assertEquals(a1.bearerBitmask, a2.bearerBitmask);
- assertEquals(a1.profileId, a2.profileId);
- assertEquals(a1.modemCognitive, a2.modemCognitive);
- assertEquals(a1.maxConns, a2.maxConns);
- assertEquals(a1.waitTime, a2.waitTime);
- assertEquals(a1.maxConnsTime, a2.maxConnsTime);
- assertEquals(a1.mtu, a2.mtu);
- assertEquals(a1.mvnoType, a2.mvnoType);
- assertEquals(a1.mvnoMatchData, a2.mvnoMatchData);
- assertEquals(a1.networkTypeBitmask, a2.networkTypeBitmask);
+ assertEquals(a1.getEntryName(), a2.getEntryName());
+ assertEquals(a1.getApnName(), a2.getApnName());
+ assertEquals(a1.getProxyAddressAsString(), a2.getProxyAddressAsString());
+ assertEquals(a1.getProxyPort(), a2.getProxyPort());
+ assertEquals(a1.getMmsc(), a2.getMmsc());
+ assertEquals(a1.getMmsProxyAddressAsString(), a2.getMmsProxyAddressAsString());
+ assertEquals(a1.getMmsProxyPort(), a2.getMmsProxyPort());
+ assertEquals(a1.getUser(), a2.getUser());
+ assertEquals(a1.getPassword(), a2.getPassword());
+ assertEquals(a1.getAuthType(), a2.getAuthType());
+ assertEquals(a1.getId(), a2.getId());
+ assertEquals(a1.getOperatorNumeric(), a2.getOperatorNumeric());
+ assertEquals(a1.getProtocol(), a2.getProtocol());
+ assertEquals(a1.getRoamingProtocol(), a2.getRoamingProtocol());
+ assertEquals(a1.getApnTypeBitmask(), a2.getApnTypeBitmask());
+ assertEquals(a1.isEnabled(), a2.isEnabled());
+ assertEquals(a1.getProfileId(), a2.getProfileId());
+ assertEquals(a1.getModemCognitive(), a2.getModemCognitive());
+ assertEquals(a1.getMaxConns(), a2.getMaxConns());
+ assertEquals(a1.getWaitTime(), a2.getWaitTime());
+ assertEquals(a1.getMaxConnsTime(), a2.getMaxConnsTime());
+ assertEquals(a1.getMtu(), a2.getMtu());
+ assertEquals(a1.getMvnoType(), a2.getMvnoType());
+ assertEquals(a1.getMvnoMatchData(), a2.getMvnoMatchData());
+ assertEquals(a1.getNetworkTypeBitmask(), a2.getNetworkTypeBitmask());
+ assertEquals(a1.getApnSetId(), a2.getApnSetId());
}
@Test
@SmallTest
public void testFromString() throws Exception {
- String[] dunTypes = {"DUN"};
- String[] mmsTypes = {"mms", "*"};
+ final int dunTypesBitmask = ApnSetting.TYPE_DUN;
+ final int mmsTypesBitmask = ApnSetting.TYPE_MMS | ApnSetting.TYPE_ALL;
ApnSetting expectedApn;
String testString;
// A real-world v1 example string.
testString = "Vodafone IT,web.omnitel.it,,,,,,,,,222,10,,DUN";
- expectedApn = new ApnSetting(
- -1, "22210", "Vodafone IT", "web.omnitel.it", "", "",
- "", "", "", "", "", 0, dunTypes, "IP", "IP", true, 0, 0,
- 0, false, 0, 0, 0, 0, "", "");
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "22210", "Vodafone IT", "web.omnitel.it", "", -1, null, "", -1, "", "", 0,
+ dunTypesBitmask, ApnSetting.PROTOCOL_IP, ApnSetting.PROTOCOL_IP, true,
+ 0, 0, false, 0, 0, 0, 0, -1, "");
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
// A v2 string.
testString = "[ApnSettingV2] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14";
- expectedApn = new ApnSetting(
- -1, "12345", "Name", "apn", "", "",
- "", "", "", "", "", 0, mmsTypes, "IPV6", "IP", true, 14, 0,
- 0, false, 0, 0, 0, 0, "", "");
+ int networkTypeBitmask = 1 << (13 - 1);
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0, 0, 0, 0, -1, "");
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
// A v2 string with spaces.
testString = "[ApnSettingV2] Name,apn, ,,,,,,,,123,45,,mms|*,IPV6, IP,true,14";
- expectedApn = new ApnSetting(
- -1, "12345", "Name", "apn", "", "",
- "", "", "", "", "", 0, mmsTypes, "IPV6", "IP", true, 14, 0,
- 0, false, 0, 0, 0, 0, "", "");
- assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
- int networkTypeBitmask = 1 << (13 - 1);
- expectedApn = new ApnSetting(
- -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
- "IP", true, networkTypeBitmask, 0, false, 0, 0, 0, 0, "", "");
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0, 0, 0, 0, -1, "");
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
// A v3 string.
testString = "[ApnSettingV3] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14,,,,,,,spn,testspn";
- expectedApn = new ApnSetting(
- -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
- "IP", true, 14, 0, 0, false, 0, 0, 0, 0, "spn", "testspn");
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
// A v4 string with network type bitmask.
testString =
"[ApnSettingV4] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,0,,,,,,,spn,testspn,6";
networkTypeBitmask = 1 << (6 - 1);
- expectedApn = new ApnSetting(
- -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
- "IP", true, networkTypeBitmask, 0, false, 0, 0, 0, 0, "spn", "testspn");
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
testString =
@@ -199,35 +188,48 @@
+ "4|5|6|7|8|12|13|14|19";
// The value was calculated by adding "4|5|6|7|8|12|13|14|19".
networkTypeBitmask = 276728;
- expectedApn = new ApnSetting(
- -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
- "IP", true, networkTypeBitmask, 0, false, 0, 0, 0, 0, "spn", "testspn");
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
// A v4 string with network type bitmask and compatible bearer bitmask.
testString =
"[ApnSettingV4] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,8,,,,,,,spn,testspn, 6";
networkTypeBitmask = 1 << (6 - 1);
- int bearerBitmask = 1 << (8 - 1);
- expectedApn = new ApnSetting(
- -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
- "IP", true, 0, bearerBitmask, 0, false, 0, 0, 0, 0, "spn",
- "testspn");
- assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
- expectedApn = new ApnSetting(
- -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
- "IP", true, networkTypeBitmask, 0, false, 0, 0, 0, 0, "spn",
- "testspn");
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
// A v4 string with network type bitmask and incompatible bearer bitmask.
testString =
"[ApnSettingV4] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,9,,,,,,,spn,testspn, 6";
- bearerBitmask = 1 << (8 - 1);
- expectedApn = new ApnSetting(
- -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV6",
- "IP", true, 0, bearerBitmask, 0, false, 0, 0, 0, 0, "spn",
- "testspn");
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0,
+ 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
+ assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
+
+ // A v5 string with apnSetId=0
+ testString =
+ "[ApnSettingV5] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,0,,,,,,,spn,testspn,0,0";
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ 0, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn");
+ assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
+
+ // A v5 string with apnSetId=3
+ testString =
+ "[ApnSettingV5] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,0,,,,,,,spn,testspn,0,3";
+ expectedApn = ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ 0, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn", 3);
assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
// Return no apn if insufficient fields given.
@@ -241,6 +243,7 @@
@Test
@SmallTest
public void testArrayFromString() throws Exception {
+ final int mmsTypesBitmask = ApnSetting.TYPE_MMS;
// Test a multiple v3 string.
String testString =
"[ApnSettingV3] Name,apn,,,,,,,,,123,45,,mms,IPV6,IP,true,14,,,,,,,spn,testspn";
@@ -248,30 +251,51 @@
" ;[ApnSettingV3] Name1,apn1,,,,,,,,,123,46,,mms,IPV6,IP,true,12,,,,,,,gid,testGid";
testString +=
" ;[ApnSettingV3] Name1,apn2,,,,,,,,,123,46,,mms,IPV6,IP,true,12,,,,,,,,";
+ testString +=
+ " ;[ApnSettingV5] Name1,apn2,,,,,,,,,123,46,,mms,IPV6,IP,true,0,,,,,,,,,,3";
List<ApnSetting> expectedApns = new ArrayList<ApnSetting>();
- expectedApns.add(new ApnSetting(
- -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, new String[]{"mms"}, "IPV6",
- "IP", true, 14, 0, 0, false, 0, 0, 0, 0, "spn", "testspn"));
- expectedApns.add(new ApnSetting(
- -1, "12346", "Name1", "apn1", "", "", "", "", "", "", "", 0, new String[]{"mms"}, "IPV6",
- "IP", true, 12, 0, 0, false, 0, 0, 0, 0, "gid", "testGid"));
- expectedApns.add(new ApnSetting(
- -1, "12346", "Name1", "apn2", "", "", "", "", "", "", "", 0, new String[]{"mms"}, "IPV6",
- "IP", true, 12, 0, 0, false, 0, 0, 0, 0, "", ""));
+ expectedApns.add(ApnSetting.makeApnSetting(
+ -1, "12345", "Name", "apn", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ 1 << (13 - 1), 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "testspn"));
+ expectedApns.add(ApnSetting.makeApnSetting(
+ -1, "12346", "Name1", "apn1", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ 1 << (12 - 1), 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_GID, "testGid"));
+ expectedApns.add(ApnSetting.makeApnSetting(
+ -1, "12346", "Name1", "apn2", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ 1 << (12 - 1), 0, false, 0, 0, 0, 0, -1, ""));
+ expectedApns.add(ApnSetting.makeApnSetting(
+ -1, "12346", "Name1", "apn2", "", -1, null, "", -1, "", "", 0,
+ mmsTypesBitmask, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ 0, 0, false, 0, 0, 0, 0, -1, "", 3));
assertApnSettingsEqual(expectedApns, ApnSetting.arrayFromString(testString));
}
@Test
@SmallTest
public void testToString() throws Exception {
- String[] types = {"default", "*"};
- ApnSetting apn = new ApnSetting(
- 99, "12345", "Name", "apn", "proxy", "port",
- "mmsc", "mmsproxy", "mmsport", "user", "password", 0,
- types, "IPV6", "IP", true, 14, 0, 0, false, 0, 0, 0, 0, "", "");
- String expected = "[ApnSettingV4] Name, 99, 12345, apn, proxy, "
- + "mmsc, mmsproxy, mmsport, port, 0, default | *, "
- + "IPV6, IP, true, 14, 8192, 0, false, 0, 0, 0, 0, , , false, 4096";
+ // Use default apn_set_id constructor.
+ ApnSetting apn = ApnSetting.makeApnSetting(
+ 99, "12345", "Name", "apn", null, 10,
+ null, null, -1, "user", "password", 0,
+ ApnSetting.TYPE_DEFAULT, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ 4096, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "");
+ String expected = "[ApnSettingV5] Name, 99, 12345, apn, null, "
+ + "null, null, null, 10, 0, default, "
+ + "IPV6, IP, true, 0, false, 0, 0, 0, 0, spn, , false, 4096, 0";
+ assertEquals(expected, apn.toString());
+
+ final int networkTypeBitmask = 1 << (14 - 1);
+ apn = ApnSetting.makeApnSetting(
+ 99, "12345", "Name", "apn", null, 10,
+ null, null, -1, "user", "password", 0,
+ ApnSetting.TYPE_DEFAULT, ApnSetting.PROTOCOL_IPV6, ApnSetting.PROTOCOL_IP, true,
+ networkTypeBitmask, 0, false, 0, 0, 0, 0, ApnSetting.MVNO_TYPE_SPN, "", 3);
+ expected = "[ApnSettingV5] Name, 99, 12345, apn, null, "
+ + "null, null, null, 10, 0, default, "
+ + "IPV6, IP, true, 0, false, 0, 0, 0, 0, spn, , false, 8192, 3";
assertEquals(expected, apn.toString());
}
@@ -283,50 +307,43 @@
doReturn(false).when(mServiceState).getDataRoaming();
doReturn(1).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT}).isMetered(mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_DEFAULT), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA), mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_CBS), mPhone));
+
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
// Carrier config settings changes.
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT});
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
}
@Test
@@ -337,42 +354,34 @@
doReturn(true).when(mServiceState).getDataRoaming();
doReturn(1).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_DEFAULT), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL}).
- isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_CBS), mPhone));
// Carrier config settings changes.
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_FOTA});
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
}
@Test
@@ -385,42 +394,34 @@
.getRilDataRadioTechnology();
doReturn(1).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_DEFAULT), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL})
- .isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS})
- .isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_CBS), mPhone));
// Carrier config settings changes.
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_FOTA});
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
}
@Test
@@ -431,33 +432,26 @@
doReturn(false).when(mServiceState).getDataRoaming();
doReturn(1).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_SUPL), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_IA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_IMS), mPhone));
+
+ assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
}
@Test
@@ -467,42 +461,35 @@
new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS});
doReturn(true).when(mServiceState).getDataRoaming();
doReturn(2).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_SUPL), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_IA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_IMS), mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
+ assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
+
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
}
@Test
@@ -514,42 +501,35 @@
doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN).when(mServiceState)
.getRilDataRadioTechnology();
doReturn(2).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS})
- .isMetered(mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_SUPL), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_SUPL | ApnSetting.TYPE_IA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_IMS), mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
- assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
- assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
+ assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
+
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+ assertTrue(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
}
@Test
@@ -561,19 +541,16 @@
doReturn(false).when(mServiceState).getDataRoaming();
doReturn(3).when(mPhone).getSubId();
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS})
- .isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA})
- .isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_FOTA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_ALL), mPhone));
}
@Test
@@ -584,20 +561,16 @@
doReturn(true).when(mServiceState).getDataRoaming();
doReturn(3).when(mPhone).getSubId();
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS}).
- isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA}).
- isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_FOTA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).
- isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_ALL), mPhone));
}
@Test
@@ -610,19 +583,16 @@
.getRilDataRadioTechnology();
doReturn(3).when(mPhone).getSubId();
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS})
- .isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IMS | ApnSetting.TYPE_MMS), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA})
- .isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_FOTA), mPhone));
- assertFalse(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_ALL), mPhone));
}
@Test
@@ -634,22 +604,17 @@
doReturn(false).when(mServiceState).getDataRoaming();
doReturn(4).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN}).
- isMetered(mPhone));
-
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_DUN), mPhone));
}
@Test
@@ -661,20 +626,17 @@
doReturn(true).when(mServiceState).getDataRoaming();
doReturn(4).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN})
- .isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_DUN), mPhone));
}
@Test
@@ -688,20 +650,17 @@
.getRilDataRadioTechnology();
doReturn(4).when(mPhone).getSubId();
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_ALL), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_CBS), mPhone));
- assertTrue(createApnSetting(
- new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN}).
- isMetered(mPhone));
+ assertTrue(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_IA | ApnSetting.TYPE_DUN), mPhone));
}
@Test
@@ -709,59 +668,52 @@
public void testCanHandleType() throws Exception {
String types[] = {"mms"};
- // empty string replaced with ALL ('*') when loaded to db
- assertFalse(createApnSetting(new String[]{}).
- canHandleType(APN_TYPE_MMS));
+ assertTrue(createApnSetting(ApnSetting.TYPE_ALL)
+ .canHandleType(ApnSetting.TYPE_MMS));
- assertTrue(createApnSetting(new String[]{APN_TYPE_ALL}).
- canHandleType(APN_TYPE_MMS));
+ assertFalse(createApnSetting(ApnSetting.TYPE_DEFAULT)
+ .canHandleType(ApnSetting.TYPE_MMS));
- assertFalse(createApnSetting(new String[]{APN_TYPE_DEFAULT}).
- canHandleType(APN_TYPE_MMS));
-
- assertTrue(createApnSetting(new String[]{"DEfAULT"}).
- canHandleType("defAult"));
+ assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT)
+ .canHandleType(ApnSetting.TYPE_DEFAULT));
// Hipri is asymmetric
- assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT}).
- canHandleType(APN_TYPE_HIPRI));
- assertFalse(createApnSetting(new String[]{APN_TYPE_HIPRI}).
- canHandleType(APN_TYPE_DEFAULT));
+ assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT)
+ .canHandleType(ApnSetting.TYPE_HIPRI));
+ assertFalse(createApnSetting(ApnSetting.TYPE_HIPRI)
+ .canHandleType(ApnSetting.TYPE_DEFAULT));
- assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
- canHandleType(APN_TYPE_DEFAULT));
+ assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_DEFAULT));
- assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
- canHandleType(APN_TYPE_MMS));
+ assertTrue(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_MMS));
- assertFalse(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
- canHandleType(APN_TYPE_SUPL));
+ assertFalse(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_SUPL));
// special IA case - doesn't match wildcards
- assertFalse(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
- canHandleType(APN_TYPE_IA));
- assertFalse(createApnSetting(new String[]{APN_TYPE_ALL}).
- canHandleType(APN_TYPE_IA));
- assertFalse(createApnSetting(new String[]{APN_TYPE_ALL}).
- canHandleType("iA"));
- assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS, APN_TYPE_IA}).
- canHandleType(APN_TYPE_IA));
+ assertFalse(createApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_IA));
+ assertTrue(createApnSetting(
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_IA)
+ .canHandleType(ApnSetting.TYPE_IA));
// check carrier disabled
- assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_ALL}).
- canHandleType(APN_TYPE_MMS));
- assertFalse(createDisabledApnSetting(new String[]{"DEfAULT"}).
- canHandleType("defAult"));
- assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_DEFAULT}).
- canHandleType(APN_TYPE_HIPRI));
- assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
- canHandleType(APN_TYPE_DEFAULT));
- assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
- canHandleType(APN_TYPE_MMS));
- assertFalse(createDisabledApnSetting(new String[]
- {APN_TYPE_DEFAULT, APN_TYPE_MMS, APN_TYPE_IA}).
- canHandleType(APN_TYPE_IA));
+ assertFalse(createDisabledApnSetting(ApnSetting.TYPE_ALL)
+ .canHandleType(ApnSetting.TYPE_MMS));
+ assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT)
+ .canHandleType(ApnSetting.TYPE_DEFAULT));
+ assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT)
+ .canHandleType(ApnSetting.TYPE_HIPRI));
+ assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_DEFAULT));
+ assertFalse(createDisabledApnSetting(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+ .canHandleType(ApnSetting.TYPE_MMS));
+ assertFalse(createDisabledApnSetting(
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_IA)
+ .canHandleType(ApnSetting.TYPE_IA));
}
@Test
@@ -770,8 +722,10 @@
final int dummyInt = 1;
final String dummyString = "dummy";
final String[] dummyStringArr = new String[] {"dummy"};
+ final InetAddress dummyProxyAddress = InetAddress.getByAddress(new byte[]{0, 0, 0, 0});
+ final Uri dummyUri = Uri.parse("www.google.com");
// base apn
- ApnSetting baseApn = createApnSetting(new String[] {"mms", "default"});
+ ApnSetting baseApn = createApnSetting(ApnSetting.TYPE_MMS | ApnSetting.TYPE_DEFAULT);
Field[] fields = ApnSetting.class.getDeclaredFields();
for (Field f : fields) {
int modifiers = f.getModifiers();
@@ -781,17 +735,23 @@
f.setAccessible(true);
ApnSetting testApn = null;
if (int.class.equals(f.getType())) {
- testApn = new ApnSetting(baseApn);
+ testApn = ApnSetting.makeApnSetting(baseApn);
f.setInt(testApn, dummyInt + f.getInt(testApn));
} else if (boolean.class.equals(f.getType())) {
- testApn = new ApnSetting(baseApn);
+ testApn = ApnSetting.makeApnSetting(baseApn);
f.setBoolean(testApn, !f.getBoolean(testApn));
} else if (String.class.equals(f.getType())) {
- testApn = new ApnSetting(baseApn);
+ testApn = ApnSetting.makeApnSetting(baseApn);
f.set(testApn, dummyString);
} else if (String[].class.equals(f.getType())) {
- testApn = new ApnSetting(baseApn);
+ testApn = ApnSetting.makeApnSetting(baseApn);
f.set(testApn, dummyStringArr);
+ } else if (InetAddress.class.equals(f.getType())) {
+ testApn = ApnSetting.makeApnSetting(baseApn);
+ f.set(testApn, dummyProxyAddress);
+ } else if (Uri.class.equals(f.getType())) {
+ testApn = ApnSetting.makeApnSetting(baseApn);
+ f.set(testApn, dummyUri);
} else {
fail("Unsupported field:" + f.getName());
}
@@ -804,63 +764,61 @@
@Test
@SmallTest
public void testEqualsRoamingProtocol() throws Exception {
- ApnSetting apn1 = new ApnSetting(
+ ApnSetting apn1 = ApnSetting.makeApnSetting(
1234,
"310260",
"",
"ims",
- "",
- "",
- "",
- "",
- "",
+ null,
+ -1,
+ null,
+ null,
+ -1,
"",
"",
-1,
- new String[]{"ims"},
- "IPV6",
- "",
+ ApnSetting.TYPE_IMS,
+ ApnSetting.PROTOCOL_IPV6,
+ -1,
true,
- 0,
- 131071,
+ ServiceState.convertBearerBitmaskToNetworkTypeBitmask(131071),
0,
false,
0,
0,
0,
1440,
- "",
+ -1,
"");
- ApnSetting apn2 = new ApnSetting(
+ ApnSetting apn2 = ApnSetting.makeApnSetting(
1235,
"310260",
"",
"ims",
- "",
- "",
- "",
- "",
- "",
+ null,
+ -1,
+ null,
+ null,
+ -1,
"",
"",
-1,
- new String[]{"ims"},
- "IPV6",
- "IPV6",
+ ApnSetting.TYPE_IMS,
+ ApnSetting.PROTOCOL_IPV6,
+ ApnSetting.PROTOCOL_IPV6,
true,
- 0,
- 131072,
+ ServiceState.convertBearerBitmaskToNetworkTypeBitmask(131072),
0,
false,
0,
0,
0,
1440,
- "",
+ -1,
"");
assertTrue(apn1.equals(apn2, false));
assertFalse(apn1.equals(apn2, true));
}
-}
\ No newline at end of file
+}
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 f13d2c2..ce5eaeb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -35,6 +35,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -54,6 +55,7 @@
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
+import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
import android.telephony.data.DataService;
@@ -97,32 +99,58 @@
private DataConnectionTestHandler mDataConnectionTestHandler;
private DcController mDcc;
- private ApnSetting mApn1 = new ApnSetting(
+ private ApnSetting mApn1 = ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"", // user
"", // password
-1, // authtype
- new String[]{"default", "supl"}, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
+ ""); // mnvo_match_data
+
+ private ApnSetting mApn2 = ApnSetting.makeApnSetting(
+ 2164, // id
+ "44010", // numeric
+ "sp-mode", // name
+ "spmode.ne.jp", // apn
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
+ true, // carrier_enabled
+ 0, // networktype_bitmask
+ 0, // profile_id
+ false, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ -1, // mvno_type
""); // mnvo_match_data
private class DataConnectionTestHandler extends HandlerThread {
@@ -147,7 +175,7 @@
CellularDataService cellularDataService = new CellularDataService();
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.packageName = "com.android.phone";
- serviceInfo.permission = "android.permission.BIND_DATA_SERVICE";
+ serviceInfo.permission = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
IntentFilter filter = new IntentFilter();
mContextFixture.addService(
DataService.DATA_SERVICE_INTERFACE,
@@ -170,6 +198,7 @@
ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
doReturn(mApn1).when(mApnContext).getApnSetting();
doReturn(PhoneConstants.APN_TYPE_DEFAULT).when(mApnContext).getApnType();
+ doReturn(true).when(mDcTracker).isDataEnabled();
mDcFailBringUp.saveParameters(0, 0, -2);
doReturn(mDcFailBringUp).when(mDcTesterFailBringUpAll).getDcFailBringUp();
@@ -372,6 +401,36 @@
@Test
@SmallTest
+ public void testNetworkCapability() throws Exception {
+ mContextFixture.getCarrierConfigBundle().putStringArray(
+ CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+ new String[] { "default" });
+ doReturn(mApn2).when(mApnContext).getApnSetting();
+ testConnectEvent();
+
+ assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
+ assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
+
+ mDc.sendMessage(DataConnection.EVENT_DISCONNECT, mDcp);
+ waitForMs(100);
+ doReturn(mApn1).when(mApnContext).getApnSetting();
+ mDc.sendMessage(DataConnection.EVENT_CONNECT, mCp);
+ waitForMs(200);
+
+ assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
+ assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL));
+ }
+
+ @Test
+ @SmallTest
public void testMeteredCapability() throws Exception {
mContextFixture.getCarrierConfigBundle().
@@ -441,6 +500,7 @@
assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
}
+ @Test
@SmallTest
public void testIsIpAddress() throws Exception {
// IPv4
@@ -524,6 +584,36 @@
assertEquals(SetupResult.SUCCESS, setLinkProperties(response, linkProperties));
}
+ @Test
+ @SmallTest
+ public void testStartKeepaliveWLAN() throws Exception {
+ testConnectEvent();
+ waitForMs(200);
+
+ DataServiceManager mockDsm = mock(DataServiceManager.class);
+ doReturn(TransportType.WLAN).when(mockDsm).getTransportType();
+ replaceInstance(DataConnection.class, "mDataServiceManager", mDc, mockDsm);
+
+ final int sessionHandle = 0xF00;
+ final int slotId = 3;
+ final int interval = 10; // seconds
+ // Construct a new KeepalivePacketData request as we would receive from a Network Agent,
+ // and check that the packet is sent to the RIL.
+ KeepalivePacketData kd = KeepalivePacketData.nattKeepalivePacket(
+ NetworkUtils.numericToInetAddress("1.2.3.4"),
+ 1234,
+ NetworkUtils.numericToInetAddress("8.8.8.8"),
+ 4500);
+ mDc.obtainMessage(
+ DataConnection.EVENT_KEEPALIVE_START_REQUEST, slotId, interval, kd).sendToTarget();
+ waitForMs(100);
+ // testStartStopNattKeepalive() verifies that this request is passed with WWAN.
+ // Thus, even though we can't see the response in NetworkAgent, we can verify that the
+ // CommandsInterface never receives a request and infer that it was dropped due to WLAN.
+ verify(mSimulatedCommandsVerifier, times(0))
+ .startNattKeepalive(anyInt(), eq(kd), eq(interval * 1000), any(Message.class));
+ }
+
public void checkStartStopNattKeepalive(boolean useCondensedFlow) throws Exception {
testConnectEvent();
waitForMs(200);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
index 5dbe995..97fcc75 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.dataconnection;
import android.telephony.ServiceState;
+import android.telephony.data.ApnSetting;
import android.telephony.data.DataProfile;
import android.test.suitebuilder.annotation.SmallTest;
@@ -26,132 +27,130 @@
public class DataProfileTest extends TestCase {
- private ApnSetting mApn1 = new ApnSetting(
+ private ApnSetting mApn1 = ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"fake_apn", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"user", // user
"passwd", // password
-1, // authtype
- new String[]{"default", "supl"}, // types
- "IPV6", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IPV6, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bitmask
1234, // profile_id
false, // modem_cognitive
321, // max_conns
456, // wait_time
789, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
""); // mnvo_match_data
- private ApnSetting mApn2 = new ApnSetting(
+ private ApnSetting mApn2 = ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"fake_apn", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"user", // user
"passwd", // password
-1, // authtype
- new String[]{"default", "supl"}, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bitmask
1234, // profile_id
false, // modem_cognitive
111, // max_conns
456, // wait_time
789, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
""); // mnvo_match_data
- private ApnSetting mApn3 = new ApnSetting(
+ private ApnSetting mApn3 = ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"fake_apn", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"user", // user
"passwd", // password
-1, // authtype
- new String[]{"default", "supl"}, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 276600, // network_type_bitmask
+ 276600, // networktype_bitmask
1234, // profile_id
false, // modem_cognitive
111, // max_conns
456, // wait_time
789, // max_conns_time
0, // mtu
- "", // mvno_type
- "" // mnvo_match_data
- );
+ -1, // mvno_type
+ ""); // mnvo_match_data
@SmallTest
public void testCreateFromApnSetting() throws Exception {
- DataProfile dp = DcTracker.createDataProfile(mApn1, mApn1.profileId);
- assertEquals(mApn1.profileId, dp.getProfileId());
- assertEquals(mApn1.apn, dp.getApn());
- assertEquals(mApn1.protocol, dp.getProtocol());
+ DataProfile dp = DcTracker.createDataProfile(mApn1, mApn1.getProfileId());
+ assertEquals(mApn1.getProfileId(), dp.getProfileId());
+ assertEquals(mApn1.getApnName(), dp.getApn());
+ assertEquals(ApnSetting.getProtocolStringFromInt(mApn1.getProtocol()), dp.getProtocol());
assertEquals(RILConstants.SETUP_DATA_AUTH_PAP_CHAP, dp.getAuthType());
- assertEquals(mApn1.user, dp.getUserName());
- assertEquals(mApn1.password, dp.getPassword());
+ assertEquals(mApn1.getUser(), dp.getUserName());
+ assertEquals(mApn1.getPassword(), dp.getPassword());
assertEquals(0, dp.getType()); // TYPE_COMMON
- assertEquals(mApn1.maxConnsTime, dp.getMaxConnsTime());
- assertEquals(mApn1.maxConns, dp.getMaxConns());
- assertEquals(mApn1.waitTime, dp.getWaitTime());
- assertEquals(mApn1.carrierEnabled, dp.isEnabled());
+ assertEquals(mApn1.getMaxConnsTime(), dp.getMaxConnsTime());
+ assertEquals(mApn1.getMaxConns(), dp.getMaxConns());
+ assertEquals(mApn1.getWaitTime(), dp.getWaitTime());
+ assertEquals(mApn1.isEnabled(), dp.isEnabled());
}
@SmallTest
public void testCreateFromApnSettingWithNetworkTypeBitmask() throws Exception {
- DataProfile dp = DcTracker.createDataProfile(mApn3, mApn3.profileId);
- assertEquals(mApn3.profileId, dp.getProfileId());
- assertEquals(mApn3.apn, dp.getApn());
- assertEquals(mApn3.protocol, dp.getProtocol());
+ DataProfile dp = DcTracker.createDataProfile(mApn3, mApn3.getProfileId());
+ assertEquals(mApn3.getProfileId(), dp.getProfileId());
+ assertEquals(mApn3.getApnName(), dp.getApn());
+ assertEquals(ApnSetting.getProtocolStringFromInt(mApn3.getProtocol()), dp.getProtocol());
assertEquals(RILConstants.SETUP_DATA_AUTH_PAP_CHAP, dp.getAuthType());
- assertEquals(mApn3.user, dp.getUserName());
- assertEquals(mApn3.password, dp.getPassword());
+ assertEquals(mApn3.getUser(), dp.getUserName());
+ assertEquals(mApn3.getPassword(), dp.getPassword());
assertEquals(2, dp.getType()); // TYPE_3GPP2
- assertEquals(mApn3.maxConnsTime, dp.getMaxConnsTime());
- assertEquals(mApn3.maxConns, dp.getMaxConns());
- assertEquals(mApn3.waitTime, dp.getWaitTime());
- assertEquals(mApn3.carrierEnabled, dp.isEnabled());
+ assertEquals(mApn3.getMaxConnsTime(), dp.getMaxConnsTime());
+ assertEquals(mApn3.getMaxConns(), dp.getMaxConns());
+ assertEquals(mApn3.getWaitTime(), dp.getWaitTime());
+ assertEquals(mApn3.isEnabled(), dp.isEnabled());
int expectedBearerBitmap =
- ServiceState.convertNetworkTypeBitmaskToBearerBitmask(mApn3.networkTypeBitmask);
+ ServiceState.convertNetworkTypeBitmaskToBearerBitmask(
+ mApn3.getNetworkTypeBitmask());
assertEquals(expectedBearerBitmap, dp.getBearerBitmap());
}
@SmallTest
public void testEquals() throws Exception {
- DataProfile dp1 = DcTracker.createDataProfile(mApn1, mApn1.profileId);
- DataProfile dp2 = DcTracker.createDataProfile(mApn1, mApn1.profileId);
+ DataProfile dp1 = DcTracker.createDataProfile(mApn1, mApn1.getProfileId());
+ DataProfile dp2 = DcTracker.createDataProfile(mApn1, mApn1.getProfileId());
assertEquals(dp1, dp2);
- dp2 = DcTracker.createDataProfile(mApn2, mApn2.profileId);
+ dp2 = DcTracker.createDataProfile(mApn2, mApn2.getProfileId());
assertFalse(dp1.equals(dp2));
}
}
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 f7eed07..451571e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -38,6 +38,7 @@
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
+import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -63,12 +64,14 @@
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.telephony.data.DataProfile;
import android.telephony.data.DataService;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
import android.util.LocalLog;
import com.android.internal.R;
@@ -88,6 +91,7 @@
import org.mockito.stubbing.Answer;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
@@ -121,6 +125,8 @@
1 << (TelephonyManager.NETWORK_TYPE_LTE - 1);
private static final int NETWORK_TYPE_EHRPD_BITMASK =
1 << (TelephonyManager.NETWORK_TYPE_EHRPD - 1);
+ private static final Uri PREFERAPN_URI = Uri.parse(
+ Telephony.Carriers.CONTENT_URI + "/preferapn");
@Mock
ISub mIsub;
@@ -153,7 +159,7 @@
CellularDataService cellularDataService = new CellularDataService();
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.packageName = "com.android.phone";
- serviceInfo.permission = "android.permission.BIND_DATA_SERVICE";
+ serviceInfo.permission = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
IntentFilter filter = new IntentFilter();
mContextFixture.addService(
DataService.DATA_SERVICE_INTERFACE,
@@ -178,6 +184,7 @@
}
private class ApnSettingContentProvider extends MockContentProvider {
+ private int mPreferredApnSet = 0;
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
@@ -222,7 +229,8 @@
Telephony.Carriers.MAX_CONNS_TIME, Telephony.Carriers.MTU,
Telephony.Carriers.MVNO_TYPE,
Telephony.Carriers.MVNO_MATCH_DATA,
- Telephony.Carriers.NETWORK_TYPE_BITMASK});
+ Telephony.Carriers.NETWORK_TYPE_BITMASK,
+ Telephony.Carriers.APN_SET_ID});
mc.addRow(new Object[]{
2163, // id
@@ -251,7 +259,8 @@
0, // mtu
"", // mvno_type
"", // mnvo_match_data
- NETWORK_TYPE_LTE_BITMASK // network_type_bitmask
+ NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
+ 0 // apn_set_id
});
mc.addRow(new Object[]{
@@ -281,7 +290,8 @@
0, // mtu
"", // mvno_type
"", // mnvo_match_data
- NETWORK_TYPE_LTE_BITMASK // network_type_bitmask
+ NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
+ 0 // apn_set_id
});
mc.addRow(new Object[]{
@@ -311,7 +321,8 @@
0, // mtu
"", // mvno_type
"", // mnvo_match_data
- 0 // network_type_bitmask
+ 0, // network_type_bitmask
+ 0 // apn_set_id
});
mc.addRow(new Object[]{
@@ -341,7 +352,8 @@
0, // mtu
"", // mvno_type
"", // mnvo_match_data
- NETWORK_TYPE_EHRPD_BITMASK // network_type_bitmask
+ NETWORK_TYPE_EHRPD_BITMASK, // network_type_bitmask
+ 0 // apn_set_id
});
mc.addRow(new Object[]{
@@ -371,14 +383,30 @@
0, // mtu
"", // mvno_type
"", // mnvo_match_data
- 0 // network_type_bitmask
+ 0, // network_type_bitmask
+ 0 // apn_set_id
});
+
return mc;
}
+ } else if (uri.isPathPrefixMatch(
+ Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapnset"))) {
+ MatrixCursor mc = new MatrixCursor(
+ new String[]{Telephony.Carriers.APN_SET_ID});
+ // apn_set_id is the only field used with this URL
+ mc.addRow(new Object[]{ mPreferredApnSet });
+ mc.addRow(new Object[]{ 0 });
+ return mc;
}
return null;
}
+
+ @Override
+ public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+ mPreferredApnSet = values.getAsInteger(Telephony.Carriers.APN_SET_ID);
+ return 1;
+ }
}
@Before
@@ -577,7 +605,7 @@
logd("Sending EVENT_ENABLE_NEW_APN");
// APN id 0 is APN_TYPE_DEFAULT
- mDct.setEnabled(0, true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
waitForMs(200);
dataConnectionReasons = new DataConnectionReasons();
@@ -651,7 +679,7 @@
logd("Sending EVENT_ENABLE_NEW_APN");
// APN id 0 is APN_TYPE_DEFAULT
- mDct.setEnabled(0, true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
waitForMs(200);
@@ -710,8 +738,8 @@
boolean dataEnabled = mDct.isUserDataEnabled();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
- mDct.setEnabled(5, true);
- mDct.setEnabled(0, true);
+ mDct.setEnabled(ApnSetting.TYPE_IMS, true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
logd("Sending EVENT_RECORDS_LOADED");
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -764,8 +792,9 @@
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
- mDct.setEnabled(5, true);
- mDct.setEnabled(0, true);
+
+ mDct.setEnabled(ApnSetting.TYPE_IMS, true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
logd("Sending EVENT_RECORDS_LOADED");
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -822,8 +851,8 @@
//set Default and MMS to be metered in the CarrierConfigManager
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
- mDct.setEnabled(5, true);
- mDct.setEnabled(0, true);
+ mDct.setEnabled(ApnSetting.TYPE_IMS, true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
logd("Sending DATA_ENABLED_CMD");
mDct.setUserDataEnabled(true);
@@ -937,8 +966,8 @@
boolean dataEnabled = mDct.isUserDataEnabled();
mDct.setUserDataEnabled(true);
- mDct.setEnabled(5, true);
- mDct.setEnabled(0, true);
+ mDct.setEnabled(ApnSetting.TYPE_IMS, true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
logd("Sending EVENT_RECORDS_LOADED");
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -978,7 +1007,8 @@
private void initApns(String targetApn, String[] canHandleTypes) {
doReturn(targetApn).when(mApnContext).getApnType();
doReturn(true).when(mApnContext).isConnectable();
- ApnSetting apnSetting = createApnSetting(canHandleTypes);
+ ApnSetting apnSetting = createApnSetting(ApnSetting.getApnTypesBitmaskFromString(
+ TextUtils.join(",", canHandleTypes)));
doReturn(apnSetting).when(mApnContext).getNextApnSetting();
doReturn(apnSetting).when(mApnContext).getApnSetting();
doReturn(mDcac).when(mApnContext).getDcAc();
@@ -1003,6 +1033,33 @@
any(Message.class));
}
+ @Test
+ @SmallTest
+ public void testGetDataConnectionState() throws Exception {
+ initApns(PhoneConstants.APN_TYPE_SUPL,
+ new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_DEFAULT});
+ mDct.setUserDataEnabled(false);
+
+ mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+ new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+
+ logd("Sending EVENT_RECORDS_LOADED");
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+ waitForMs(200);
+
+ logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+ waitForMs(200);
+
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+ waitForMs(200);
+
+ // Assert that both APN_TYPE_SUPL & APN_TYPE_DEFAULT are connected even we only setup data
+ // for APN_TYPE_SUPL
+ assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_SUPL));
+ assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
+ }
+
// Test the unmetered APN setup when data is disabled.
@Test
@SmallTest
@@ -1085,6 +1142,37 @@
any(Message.class));
}
+ // Test the restricted data request when roaming is disabled.
+ @Test
+ @SmallTest
+ public void testTrySetupRestrictedRoamingDisabled() throws Exception {
+ initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+ doReturn(false).when(mApnContext).hasNoRestrictedRequests(eq(true));
+
+ mDct.setUserDataEnabled(true);
+ mDct.setDataRoamingEnabledByUser(false);
+ mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+ new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ //user is in roaming
+ doReturn(true).when(mServiceState).getDataRoaming();
+
+ logd("Sending EVENT_RECORDS_LOADED");
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+ waitForMs(200);
+
+ logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+ waitForMs(200);
+
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+ waitForMs(200);
+
+ // expect no restricted data connection
+ verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ any(Message.class));
+ }
+
// Test the default data when data is not connectable.
@Test
@SmallTest
@@ -1175,7 +1263,7 @@
.getRilDataRadioTechnology();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT});
- mDct.setEnabled(0, true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
mDct.setUserDataEnabled(true);
initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
@@ -1230,7 +1318,7 @@
assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
}
-// Test for fetchDunApn()
+ // Test for fetchDunApns()
@Test
@SmallTest
public void testFetchDunApn() {
@@ -1245,15 +1333,48 @@
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.TETHER_DUN_APN, dunApnString);
// should return APN from Setting
- ApnSetting dunApn = mDct.fetchDunApn();
+ ApnSetting dunApn = mDct.fetchDunApns().get(0);
assertTrue(dunApnExpected.equals(dunApn));
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.TETHER_DUN_APN, null);
// should return APN from db
- dunApn = mDct.fetchDunApn();
- assertEquals(FAKE_APN5, dunApn.apn);
+ dunApn = mDct.fetchDunApns().get(0);
+ assertEquals(FAKE_APN5, dunApn.getApnName());
}
+
+ // Test for fetchDunApns() with apn set id
+ @Test
+ @SmallTest
+ public void testFetchDunApnWithPreferredApnSet() {
+ logd("Sending EVENT_RECORDS_LOADED");
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+ waitForMs(200);
+
+ // apnSetId=1
+ String dunApnString1 = "[ApnSettingV5]HOT mobile PC,pc.hotm,,,,,,,,,440,10,,DUN,,,true,"
+ + "0,,,,,,,,,,1";
+ // apnSetId=0
+ String dunApnString2 = "[ApnSettingV5]HOT mobile PC,pc.coldm,,,,,,,,,440,10,,DUN,,,true,"
+ + "0,,,,,,,,,,0";
+
+ ApnSetting dunApnExpected = ApnSetting.fromString(dunApnString1);
+
+ ContentResolver cr = mContext.getContentResolver();
+ Settings.Global.putString(cr, Settings.Global.TETHER_DUN_APN,
+ dunApnString1 + ";" + dunApnString2);
+
+ // set that we prefer apn set 1
+ ContentValues values = new ContentValues();
+ values.put(Telephony.Carriers.APN_SET_ID, 1);
+ cr.update(PREFERAPN_URI, values, null, null);
+
+ // return APN from Setting with apnSetId=1
+ ArrayList<ApnSetting> dunApns = mDct.sortApnListByPreferred(mDct.fetchDunApns());
+ assertEquals(2, dunApns.size());
+ assertTrue(dunApnExpected.equals(dunApns.get(0)));
+ }
+
// Test oos
@Test
@SmallTest
@@ -1262,7 +1383,7 @@
.getRilDataRadioTechnology();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{PhoneConstants.APN_TYPE_DEFAULT});
- mDct.setEnabled(0, true);
+ mDct.setEnabled(ApnSetting.TYPE_DEFAULT, true);
mDct.setUserDataEnabled(true);
initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
index 6b00289..2f452f6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
@@ -16,8 +16,12 @@
package com.android.internal.telephony.dataconnection;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
+import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.RetryManager;
@@ -31,97 +35,91 @@
import java.util.ArrayList;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
/**
* APN retry manager tests
*/
public class RetryManagerTest extends TelephonyTest {
// This is the real APN data for the Japanese carrier NTT Docomo.
- private ApnSetting mApn1 = new ApnSetting(
+ private ApnSetting mApn1 = ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"", // user
"", // password
-1, // authtype
- new String[]{"default", "supl"}, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
""); // mnvo_match_data
- private ApnSetting mApn2 = new ApnSetting(
+ private ApnSetting mApn2 = ApnSetting.makeApnSetting(
2164, // id
"44010", // numeric
"mopera U", // name
"mopera.net", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"", // user
"", // password
-1, // authtype
- new String[]{"default", "supl"}, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
""); // mnvo_match_data
- private ApnSetting mApn3 = new ApnSetting(
+ private ApnSetting mApn3 = ApnSetting.makeApnSetting(
2165, // id
"44010", // numeric
"b-mobile for Nexus", // name
"bmobile.ne.jp", // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
"", // user
"", // password
3, // authtype
- new String[]{"default", "supl"}, // types
- "IP", // protocol
- "IP", // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
+ 0, // networktype_bitmask
0, // profile_id
false, // modem_cognitive
0, // max_conns
0, // wait_time
0, // max_conns_time
0, // mtu
- "", // mvno_type
+ -1, // mvno_type
""); // mnvo_match_data
private PersistableBundle mBundle;
@@ -173,7 +171,7 @@
new String[]{"default:"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
RetryManager rm = new RetryManager(mPhone, "default");
rm.setWaitingApns(waitingApns);
@@ -195,7 +193,7 @@
new String[]{"supl:2000,3000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
RetryManager rm = new RetryManager(mPhone, "supl");
rm.setWaitingApns(waitingApns);
@@ -249,8 +247,8 @@
new String[]{"others:2000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
- waitingApns.add(new ApnSetting(mApn2));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn2));
RetryManager rm = new RetryManager(mPhone, "default");
rm.setWaitingApns(waitingApns);
@@ -287,8 +285,8 @@
new String[]{"dun:2000,5000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
- waitingApns.add(new ApnSetting(mApn2));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn2));
RetryManager rm = new RetryManager(mPhone, "dun");
rm.setWaitingApns(waitingApns);
@@ -335,8 +333,8 @@
new String[]{"mms: 3000,6000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
- waitingApns.add(new ApnSetting(mApn2));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn2));
RetryManager rm = new RetryManager(mPhone, "mms");
rm.setWaitingApns(waitingApns);
@@ -383,7 +381,7 @@
new String[]{"fota:1000,4000,7000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting apn = new ApnSetting(mApn1);
+ ApnSetting apn = ApnSetting.makeApnSetting(mApn1);
waitingApns.add(apn);
RetryManager rm = new RetryManager(mPhone, "fota");
@@ -416,8 +414,8 @@
new String[]{"xyz : 1000,4000,7000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = new ApnSetting(mApn1);
- ApnSetting myApn2 = new ApnSetting(mApn2);
+ ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+ ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
@@ -473,9 +471,9 @@
new String[]{"default:2000:2000,3000:3000", "ims:1000,4000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = new ApnSetting(mApn1);
- ApnSetting myApn2 = new ApnSetting(mApn2);
- ApnSetting myApn3 = new ApnSetting(mApn3);
+ ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+ ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
+ ApnSetting myApn3 = ApnSetting.makeApnSetting(mApn3);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
waitingApns.add(myApn3);
@@ -532,8 +530,8 @@
new String[]{"default:1000,4000,7000,9000", "mms:1234,4123"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = new ApnSetting(mApn1);
- ApnSetting myApn2 = new ApnSetting(mApn2);
+ ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+ ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
@@ -587,7 +585,7 @@
new String[]{"default:default_randomization=1000,3000:2000,6000:3000,10000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
RetryManager rm = new RetryManager(mPhone, "default");
rm.setWaitingApns(waitingApns);
@@ -624,8 +622,8 @@
new String[]{"default:max_retries=infinite,1000,2000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
- waitingApns.add(new ApnSetting(mApn2));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn2));
RetryManager rm = new RetryManager(mPhone, "default");
rm.setWaitingApns(waitingApns);
@@ -682,8 +680,8 @@
new String[]{"hipri: max_retries=4,1000,2000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
- waitingApns.add(new ApnSetting(mApn2));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn2));
RetryManager rm = new RetryManager(mPhone, "hipri");
rm.setWaitingApns(waitingApns);
@@ -752,8 +750,8 @@
mBundle.putLong(CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 2000);
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- waitingApns.add(new ApnSetting(mApn1));
- waitingApns.add(new ApnSetting(mApn2));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn1));
+ waitingApns.add(ApnSetting.makeApnSetting(mApn2));
RetryManager rm = new RetryManager(mPhone, "default");
rm.setWaitingApns(waitingApns);
@@ -800,8 +798,8 @@
new String[]{"dun:1000,4000,7000,9000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = new ApnSetting(mApn1);
- ApnSetting myApn2 = new ApnSetting(mApn2);
+ ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+ ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
@@ -845,7 +843,7 @@
// reset the retry manager
- ApnSetting myApn3 = new ApnSetting(mApn3);
+ ApnSetting myApn3 = ApnSetting.makeApnSetting(mApn3);
waitingApns.clear();
waitingApns.add(myApn3);
@@ -881,8 +879,8 @@
new String[]{"others:1000,4000,7000,9000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = new ApnSetting(mApn1);
- ApnSetting myApn2 = new ApnSetting(mApn2);
+ ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+ ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
@@ -937,8 +935,8 @@
new String[]{"default:1000,4000,7000,9000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = new ApnSetting(mApn1);
- ApnSetting myApn2 = new ApnSetting(mApn2);
+ ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+ ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
@@ -980,8 +978,8 @@
new String[]{"mms:2000,3000", "default:1000,4000,7000,9000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = new ApnSetting(mApn1);
- ApnSetting myApn2 = new ApnSetting(mApn2);
+ ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
+ ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
index 88a7d9d..f7d8671 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
@@ -837,7 +837,7 @@
private void setHasCarrierPrivilegesOnActiveSubscription(boolean hasPrivileges)
throws Exception {
SubscriptionInfo subInfo = new SubscriptionInfo(
- 0, "", 0, "", "", 0, 0, "", 0, null, 0, 0, "", true /* isEmbedded */,
+ 0, "", 0, "", "", 0, 0, "", 0, null, "0", "0", "", true /* isEmbedded */,
hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null);
when(mSubscriptionManager.canManageSubscription(subInfo, PACKAGE_NAME)).thenReturn(
hasPrivileges);
@@ -847,7 +847,7 @@
private void prepareOperationSubscription(boolean hasPrivileges) throws Exception {
SubscriptionInfo subInfo = new SubscriptionInfo(
- SUBSCRIPTION_ID, ICC_ID, 0, "", "", 0, 0, "", 0, null, 0, 0, "",
+ SUBSCRIPTION_ID, ICC_ID, 0, "", "", 0, 0, "", 0, null, "0", "0", "",
true /* isEmbedded */, hasPrivileges ? new UiccAccessRule[] { ACCESS_RULE } : null);
when(mSubscriptionManager.canManageSubscription(subInfo, PACKAGE_NAME)).thenReturn(
hasPrivileges);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java.broken b/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java.broken
index 33836ac..2e9b88c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java.broken
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java.broken
@@ -59,6 +59,7 @@
private static final int EVENT_IN_SERVICE = 10;
private static final int SUPP_SERVICE_FAILED = 11;
private static final int SERVICE_STATE_CHANGED = 12;
+ private static final int EVENT_OEM_RIL_MESSAGE = 13;
public static final int ANY_MESSAGE = -1;
@Override
@@ -1781,6 +1782,95 @@
assertEquals(MmiCode.State.CANCELLED, mmi.getState());
}
+ public void testRilHooks() throws Exception {
+ //
+ // These test cases all assume the RIL OEM hooks
+ // just echo back their input
+ //
+
+ Message msg;
+ AsyncResult ar;
+
+ // null byte array
+
+ mGSMPhone.invokeOemRilRequestRaw(null, mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
+
+ msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
+ assertNotNull("Message Time Out", msg);
+
+ ar = ((AsyncResult) msg.obj);
+
+ assertNull(ar.result);
+ assertNull(ar.exception);
+
+ // empty byte array
+
+ mGSMPhone.invokeOemRilRequestRaw(new byte[0], mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
+
+ msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
+ assertNotNull("Message Time Out", msg);
+
+ ar = ((AsyncResult) msg.obj);
+
+ assertEquals(0, ((byte[]) (ar.result)).length);
+ assertNull(ar.exception);
+
+ // byte array with data
+
+ mGSMPhone.invokeOemRilRequestRaw("Hello".getBytes("utf-8"),
+ mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
+
+ msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
+ assertNotNull("Message Time Out", msg);
+
+ ar = ((AsyncResult) msg.obj);
+
+ assertEquals("Hello", new String(((byte[]) (ar.result)), "utf-8"));
+ assertNull(ar.exception);
+
+ // null strings
+
+ mGSMPhone.invokeOemRilRequestStrings(null, mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
+
+ msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
+ assertNotNull("Message Time Out", msg);
+
+ ar = ((AsyncResult) msg.obj);
+
+ assertNull(ar.result);
+ assertNull(ar.exception);
+
+ // empty byte array
+
+ mGSMPhone.invokeOemRilRequestStrings(new String[0],
+ mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
+
+ msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
+ assertNotNull("Message Time Out", msg);
+
+ ar = ((AsyncResult) msg.obj);
+
+ assertEquals(0, ((String[]) (ar.result)).length);
+ assertNull(ar.exception);
+
+ // Strings with data
+
+ String s[] = new String[1];
+
+ s[0] = "Hello";
+
+ mGSMPhone.invokeOemRilRequestStrings(s, mHandler.obtainMessage(EVENT_OEM_RIL_MESSAGE));
+
+ msg = mGSMTestHandler.waitForMessage(EVENT_OEM_RIL_MESSAGE);
+ assertNotNull("Message Time Out", msg);
+
+ ar = ((AsyncResult) msg.obj);
+
+ assertEquals("Hello", ((String[]) (ar.result))[0]);
+ assertEquals(1, ((String[]) (ar.result)).length);
+ assertNull(ar.exception);
+ }
+
public void testMmi() throws Exception {
mRadioControl.setAutoProgressConnectingCall(false);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java.broken b/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java.broken
index 325aa67..3db8adc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java.broken
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java.broken
@@ -520,6 +520,14 @@
}
@Override
+ public void invokeOemRilRequestRaw(byte[] data, Message response) {
+ }
+
+ @Override
+ public void invokeOemRilRequestStrings(String[] strings, Message response) {
+ }
+
+ @Override
public void sendTerminalResponse(String contents, Message response) {
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
index f217c7d..fd19f80 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
@@ -22,6 +22,7 @@
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -35,6 +36,7 @@
import com.android.ims.ImsConfig;
import com.android.ims.ImsManager;
+import com.android.ims.MmTelFeatureConnection;
import com.android.internal.telephony.TelephonyTest;
import org.junit.After;
@@ -47,13 +49,16 @@
public class ImsManagerTest extends TelephonyTest {
private static final String UNSET_PROVISIONED_STRING = "unset";
private static final boolean ENHANCED_4G_MODE_DEFAULT_VAL = true;
- private static final boolean ENHANCED_4G_ENABLE_DEFAULT_VAL = true;
private static final boolean ENHANCED_4G_MODE_EDITABLE = true;
private static final boolean WFC_IMS_ENABLE_DEFAULT_VAL = false;
private static final boolean WFC_IMS_ROAMING_ENABLE_DEFAULT_VAL = true;
private static final boolean VT_IMS_ENABLE_DEFAULT_VAL = true;
- private static final int WFC_IMS_MODE_DEFAULT_VAL = 2;
- private static final int WFC_IMS_ROAMING_MODE_DEFAULT_VAL = 3;
+ private static final boolean WFC_IMS_EDITABLE_VAL = true;
+ private static final boolean WFC_IMS_NOT_EDITABLE_VAL = false;
+ private static final int WFC_IMS_MODE_DEFAULT_VAL =
+ ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
+ private static final int WFC_IMS_ROAMING_MODE_DEFAULT_VAL =
+ ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED;
PersistableBundle mBundle;
@@ -64,7 +69,7 @@
Hashtable<Integer, Integer> mProvisionedIntVals = new Hashtable<>();
Hashtable<Integer, String> mProvisionedStringVals = new Hashtable<>();
ImsConfigImplBase.ImsConfigStub mImsConfigStub;
- ImsConfig mImsConfig;
+ @Mock MmTelFeatureConnection mMmTelFeatureConnection;
private final int[] mSubId = {0};
private int mPhoneId;
@@ -80,6 +85,8 @@
doReturn(mSubscriptionController).when(mBinder).queryLocalInterface(anyString());
mServiceManagerMockedServices.put("isub", mBinder);
+ doReturn(true).when(mMmTelFeatureConnection).isBinderAlive();
+
mImsManagerInstances.remove(mPhoneId);
setDefaultValues();
@@ -92,7 +99,9 @@
private void setDefaultValues() {
mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL,
- ENHANCED_4G_ENABLE_DEFAULT_VAL);
+ ENHANCED_4G_MODE_EDITABLE);
+ mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL,
+ WFC_IMS_EDITABLE_VAL);
mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL,
WFC_IMS_ENABLE_DEFAULT_VAL);
mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL,
@@ -119,7 +128,7 @@
eq(SubscriptionManager.WFC_IMS_ENABLED),
anyString());
- assertEquals(ENHANCED_4G_ENABLE_DEFAULT_VAL,
+ assertEquals(ENHANCED_4G_MODE_DEFAULT_VAL,
imsManager.isEnhanced4gLteModeSettingEnabledByUser());
verify(mSubscriptionController, times(1)).getSubscriptionProperty(
anyInt(),
@@ -239,7 +248,126 @@
}
- private ImsManager initializeProvisionedValues() {
+ /**
+ * Tests that when a WFC mode is set for home/roaming, that setting is sent to the ImsService
+ * correctly.
+ *
+ * Preconditions:
+ * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
+ */
+ @Test @SmallTest
+ public void testSetWfcSetting_true_shouldSetWfcModeWrtRoamingState() throws Exception {
+ // First, Set WFC home/roaming mode that is not the Carrier Config default.
+ doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED))
+ .when(mSubscriptionController).getSubscriptionProperty(
+ anyInt(),
+ eq(SubscriptionManager.WFC_IMS_MODE),
+ anyString());
+ doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED))
+ .when(mSubscriptionController).getSubscriptionProperty(
+ anyInt(),
+ eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
+ anyString());
+ ImsManager imsManager = initializeProvisionedValues();
+
+ // Roaming
+ doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+ // Turn on WFC
+ imsManager.setWfcSetting(true);
+ // Roaming mode (CELLULAR_PREFERRED) should be set. With 1000 ms timeout.
+ verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+ eq(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED));
+
+ // Not roaming
+ doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+ // Turn on WFC
+ imsManager.setWfcSetting(true);
+ // Home mode (WIFI_PREFERRED) should be set. With 1000 ms timeout.
+ verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+ eq(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED));
+ }
+
+ /**
+ * Tests that the settings for WFC mode are ignored if the Carrier sets the settings to not
+ * editable.
+ *
+ * Preconditions:
+ * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = false
+ */
+ @Test @SmallTest
+ public void testSetWfcSetting_wfcNotEditable() throws Exception {
+ mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL,
+ WFC_IMS_NOT_EDITABLE_VAL);
+ // Set some values that are different than the defaults for WFC mode.
+ doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY))
+ .when(mSubscriptionController).getSubscriptionProperty(
+ anyInt(),
+ eq(SubscriptionManager.WFC_IMS_MODE),
+ anyString());
+ doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY))
+ .when(mSubscriptionController).getSubscriptionProperty(
+ anyInt(),
+ eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
+ anyString());
+ ImsManager imsManager = initializeProvisionedValues();
+
+ // Roaming
+ doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+ // Turn on WFC
+ imsManager.setWfcSetting(true);
+ // User defined setting for Roaming mode (WIFI_ONLY) should be set independent of whether or
+ // not WFC mode is editable. With 1000 ms timeout.
+ verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+ eq(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY));
+
+ // Not roaming
+ doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+ // Turn on WFC
+ imsManager.setWfcSetting(true);
+ // Default Home mode (CELLULAR_PREFERRED) should be set. With 1000 ms timeout.
+ verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+ eq(WFC_IMS_MODE_DEFAULT_VAL));
+ }
+
+ /**
+ * Tests that the CarrierConfig defaults will be used if no setting is set in the Subscription
+ * Manager.
+ *
+ * Preconditions:
+ * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
+ * - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT = Carrier preferred
+ * - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT = WiFi preferred
+ */
+ @Test @SmallTest
+ public void testSetWfcSetting_noUserSettingSet() throws Exception {
+ ImsManager imsManager = initializeProvisionedValues();
+
+ // Roaming
+ doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+ // Turn on WFC
+ imsManager.setWfcSetting(true);
+
+ // Default Roaming mode (WIFI_PREFERRED) for carrier should be set. With 1000 ms timeout.
+ verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+ eq(WFC_IMS_ROAMING_MODE_DEFAULT_VAL));
+
+ // Not roaming
+ doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
+ // Turn on WFC
+ imsManager.setWfcSetting(true);
+
+ // Default Home mode (CELLULAR_PREFERRED) for carrier should be set. With 1000 ms timeout.
+ verify(mImsConfigImplBaseMock, timeout(1000)).setConfig(
+ eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
+ eq(WFC_IMS_MODE_DEFAULT_VAL));
+ }
+
+ private ImsManager initializeProvisionedValues() throws Exception {
when(mImsConfigImplBaseMock.getConfigInt(anyInt()))
.thenAnswer(invocation -> {
return getProvisionedInt((Integer) (invocation.getArguments()[0]));
@@ -255,15 +383,13 @@
// Configure ImsConfigStub
mImsConfigStub = new ImsConfigImplBase.ImsConfigStub(mImsConfigImplBaseMock);
- doReturn(mImsConfigStub).when(mImsConfigImplBaseMock).getIImsConfig();
-
- // Configure ImsConfig
- mImsConfig = new ImsConfig(mImsConfigStub, mContext);
+ doReturn(mImsConfigStub).when(mMmTelFeatureConnection).getConfigInterface();
// Configure ImsManager
ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
try {
- replaceInstance(ImsManager.class, "mConfig", imsManager, mImsConfig);
+ replaceInstance(ImsManager.class, "mMmTelFeatureConnection", imsManager,
+ mMmTelFeatureConnection);
} catch (Exception ex) {
fail("failed with " + ex);
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
index f6aafe3..3587b8b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -20,6 +20,7 @@
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static junit.framework.TestCase.assertFalse;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Matchers.any;
@@ -47,10 +48,8 @@
import android.telephony.CarrierConfigManager;
import android.telephony.ims.ImsService;
import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Pair;
-
-import com.android.internal.telephony.PhoneConstants;
import org.junit.After;
import org.junit.Before;
@@ -58,6 +57,7 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.HashSet;
@@ -80,13 +80,22 @@
private static final ComponentName TEST_CARRIER_2_DEFAULT_NAME = new ComponentName(
"TestCarrier2Pkg", "Carrier2ImsService");
- @Mock Context mMockContext;
- @Mock PackageManager mMockPM;
- @Mock ImsResolver.SubscriptionManagerProxy mTestSubscriptionManagerProxy;
- @Mock CarrierConfigManager mMockCarrierConfigManager;
+ @Mock
+ Context mMockContext;
+ @Mock
+ PackageManager mMockPM;
+ @Mock
+ ImsResolver.SubscriptionManagerProxy mTestSubscriptionManagerProxy;
+ @Mock
+ CarrierConfigManager mMockCarrierConfigManager;
+ @Mock
+ ImsResolver.ImsDynamicQueryManagerFactory mMockQueryManagerFactory;
+ @Mock
+ ImsServiceFeatureQueryManager mMockQueryManager;
private ImsResolver mTestImsResolver;
private BroadcastReceiver mTestPackageBroadcastReceiver;
private BroadcastReceiver mTestCarrierConfigReceiver;
+ private ImsServiceFeatureQueryManager.Listener mDynamicQueryListener;
private PersistableBundle[] mCarrierConfigs;
@Before
@@ -104,26 +113,22 @@
/**
* Add a package to the package manager and make sure it is added to the cache of available
- * ImsServices in the ImsResolver
+ * ImsServices in the ImsResolver.
*/
@Test
@SmallTest
- public void testAddPackageToCache() {
+ public void testAddDevicePackageToCache() {
setupResolver(1/*numSlots*/);
- List<ResolveInfo> info = new ArrayList<>();
- Set<String> features = new HashSet<>();
+ HashSet<String> features = new HashSet<>();
+ features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
- // Only return info if not using the compat argument
- when(mMockPM.queryIntentServicesAsUser(
- argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
- anyInt(), anyInt())).thenReturn(info);
- setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ setupPackageQuery(TEST_DEVICE_DEFAULT_NAME, features, true);
+ setupController();
- mTestImsResolver.populateCacheAndStartBind();
+ // Complete package manager lookup and cache.
+ startBind();
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
ImsResolver.ImsServiceInfo testCachedService =
mTestImsResolver.getImsServiceInfoFromCache(
TEST_DEVICE_DEFAULT_NAME.getPackageName());
@@ -132,6 +137,35 @@
}
/**
+ * Add a carrier ImsService to the package manager and make sure the features declared here are
+ * ignored. We should only allow dynamic query for these services.
+ */
+ @Test
+ @SmallTest
+ public void testAddCarrierPackageToCache() {
+ setupResolver(1/*numSlots*/);
+ HashSet<String> features = new HashSet<>();
+ features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_RCS_FEATURE);
+ setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ setupPackageQuery(TEST_CARRIER_DEFAULT_NAME, features, true);
+ setupController();
+
+ // Complete package manager lookup and cache.
+ startBind();
+
+ ImsResolver.ImsServiceInfo testCachedService =
+ mTestImsResolver.getImsServiceInfoFromCache(
+ TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ assertNotNull(testCachedService);
+ // none of the manifest features we added above should be reported for carrier package.
+ assertTrue(testCachedService.getSupportedFeatures().isEmpty());
+ // we should report that the service does not use metadata to report features.
+ assertFalse(testCachedService.featureFromMetadata);
+ }
+
+ /**
* Set the carrier config override value and ensure that ImsResolver calls .bind on that
* package name with the correct ImsFeatures.
*/
@@ -139,45 +173,28 @@
@SmallTest
public void testCarrierPackageBind() throws RemoteException {
setupResolver(1/*numSlots*/);
- // Set CarrierConfig default package name and make it available to the package manager
+ // Setup the carrier features
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = new HashSet<>();
+ features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_MMTEL));
+ features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ // Set CarrierConfig default package name and make it available as the CarrierConfig.
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- List<ResolveInfo> info = new ArrayList<>();
- Set<String> features = new HashSet<>();
- features.add(ImsResolver.METADATA_MMTEL_FEATURE);
- features.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
- // Only return info if not using the compat argument
- when(mMockPM.queryIntentServicesAsUser(
- argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
- anyInt(), anyInt())).thenReturn(info);
- ImsServiceController controller = mock(ImsServiceController.class);
- mTestImsResolver.setImsServiceControllerFactory(
- new ImsResolver.ImsServiceControllerFactory() {
- @Override
- public String getServiceInterface() {
- return ImsService.SERVICE_INTERFACE;
- }
+ setupPackageQuery(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true);
+ ImsServiceController controller = setupController();
- @Override
- public ImsServiceController create(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks) {
- when(controller.getComponentName()).thenReturn(componentName);
- return controller;
- }
- });
+ // Start bind to carrier service
+ startBind();
+ // setup features response
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, features, 1);
-
- mTestImsResolver.populateCacheAndStartBind();
-
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
- verify(controller).bind(convertToHashSet(features, 0));
+ verify(controller).bind(features);
verify(controller, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
}
/**
- * Creates a carrier ImsService with a manifest that defines METADATA_EMERGENCY_MMTEL_FEATURE,
- * ensure that the controller sets this capability.
+ * Creates a carrier ImsService that defines FEATURE_EMERGENCY_MMTEL and ensure that the
+ * controller sets this capability.
*/
@Test
@SmallTest
@@ -185,46 +202,23 @@
setupResolver(1/*numSlots*/);
// Set CarrierConfig default package name and make it available to the package manager
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- List<ResolveInfo> info = new ArrayList<>();
- Set<String> features = new HashSet<>();
- features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
- features.add(ImsResolver.METADATA_MMTEL_FEATURE);
- features.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
- // Only return info if not using the compat argument
- when(mMockPM.queryIntentServicesAsUser(
- argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
- anyInt(), anyInt())).thenReturn(info);
- ImsServiceController controller = mock(ImsServiceController.class);
- mTestImsResolver.setImsServiceControllerFactory(
- new ImsResolver.ImsServiceControllerFactory() {
- @Override
- public String getServiceInterface() {
- return ImsService.SERVICE_INTERFACE;
- }
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = new HashSet<>();
+ features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_MMTEL));
+ features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ setupPackageQuery(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true);
+ ImsServiceController controller = setupController();
- @Override
- public ImsServiceController create(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks) {
- when(controller.getComponentName()).thenReturn(componentName);
- return controller;
- }
- });
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, features, 1);
-
- mTestImsResolver.populateCacheAndStartBind();
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
- verify(controller).bind(convertToHashSet(features, 0));
+ verify(controller).bind(features);
verify(controller, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
-
- verify(controller).setCanPlaceEmergencyCalls(eq(true));
}
/**
- * Creates a carrier ImsService with a manifest that doesn't define
- * METADATA_EMERGENCY_MMTEL_FEATURE and then update the ImsService to define it. Ensure that the
- * controller sets this capability once enabled.
+ * Creates a carrier ImsService that does not report FEATURE_EMERGENCY_MMTEL and then update the
+ * ImsService to define it. Ensure that the controller sets this capability once enabled.
*/
@Test
@SmallTest
@@ -232,96 +226,48 @@
setupResolver(1/*numSlots*/);
// Set CarrierConfig default package name and make it available to the package manager
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- List<ResolveInfo> info = new ArrayList<>();
- Set<String> features = new HashSet<>();
- features.add(ImsResolver.METADATA_MMTEL_FEATURE);
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
- // Only return info if not using the compat argument
- when(mMockPM.queryIntentServicesAsUser(
- argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
- anyInt(), anyInt())).thenReturn(info);
- ImsServiceController controller = mock(ImsServiceController.class);
- mTestImsResolver.setImsServiceControllerFactory(
- new ImsResolver.ImsServiceControllerFactory() {
- @Override
- public String getServiceInterface() {
- return ImsService.SERVICE_INTERFACE;
- }
-
- @Override
- public ImsServiceController create(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks) {
- when(controller.getComponentName()).thenReturn(componentName);
- return controller;
- }
- });
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = new HashSet<>();
+ features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_MMTEL));
+ setupPackageQuery(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true);
+ ImsServiceController controller = setupController();
// Bind without emergency calling
- mTestImsResolver.populateCacheAndStartBind();
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
- verify(controller).bind(convertToHashSet(features, 0));
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, features, 1);
+ verify(controller).bind(features);
verify(controller, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
- // ensure emergency calling is disabled
- verify(controller, never()).setCanPlaceEmergencyCalls(eq(true));
- // Tell package manager that app has changed and service now supports emergency calling
- Set<String> newFeatures = new HashSet<>();
- newFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
- newFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
- info.clear();
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, newFeatures, true));
-
- Intent addPackageIntent = new Intent();
- addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
- addPackageIntent.setData(new Uri.Builder().scheme("package")
- .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
- mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ packageChanged(TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> newFeatures = new HashSet<>();
+ newFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_MMTEL));
+ newFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_EMERGENCY_MMTEL));
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, newFeatures, 2);
//Verify new feature is added to the carrier override.
// add all features for slot 0
- HashSet<Pair<Integer, Integer>> newCarrierFeatureSet =
- convertToHashSet(newFeatures, 0);
- verify(controller, atLeastOnce()).changeImsServiceFeatures(newCarrierFeatureSet);
- verify(controller).setCanPlaceEmergencyCalls(eq(true));
+ verify(controller, atLeastOnce()).changeImsServiceFeatures(newFeatures);
}
/**
- * Ensure that no ImsService is bound if there is no carrier or device package explictly set.
+ * Ensure that no ImsService is bound if there is no carrier or device package explicitly set.
*/
@Test
@SmallTest
public void testDontBindWhenNullCarrierPackage() throws RemoteException {
setupResolver(1/*numSlots*/);
- List<ResolveInfo> info = new ArrayList<>();
- Set<String> features = new HashSet<>();
- features.add(ImsResolver.METADATA_MMTEL_FEATURE);
- features.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
- ImsServiceController controller = mock(ImsServiceController.class);
- mTestImsResolver.setImsServiceControllerFactory(
- new ImsResolver.ImsServiceControllerFactory() {
- @Override
- public String getServiceInterface() {
- return ImsService.SERVICE_INTERFACE;
- }
-
- @Override
- public ImsServiceController create(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks) {
- when(controller.getComponentName()).thenReturn(componentName);
- return controller;
- }
- });
+ setupPackageQuery(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true);
+ ImsServiceController controller = setupController();
// Set the CarrierConfig string to null so that ImsResolver will not bind to the available
// Services
setConfigCarrierString(0, null);
- mTestImsResolver.populateCacheAndStartBind();
+ startBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ verify(mMockQueryManager, never()).startQuery(any(), any());
verify(controller, never()).bind(any());
verify(controller, never()).unbind();
}
@@ -340,36 +286,21 @@
features.add(ImsResolver.METADATA_RCS_FEATURE);
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
- // Only return info if not using the compat argument
- when(mMockPM.queryIntentServicesAsUser(
- argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
- anyInt(), anyInt())).thenReturn(info);
- ImsServiceController controller = mock(ImsServiceController.class);
- mTestImsResolver.setImsServiceControllerFactory(
- new ImsResolver.ImsServiceControllerFactory() {
- @Override
- public String getServiceInterface() {
- return ImsService.SERVICE_INTERFACE;
- }
-
- @Override
- public ImsServiceController create(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks) {
- when(controller.getComponentName()).thenReturn(componentName);
- return controller;
- }
- });
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+ setupPackageQuery(info);
+ ImsServiceController controller = setupController();
- mTestImsResolver.populateCacheAndStartBind();
-
+ startBind();
+ // Wait to make sure that there are no dynamic queries that are being completed.
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+
// There is no carrier override set, so make sure that the ImsServiceController binds
// to all SIMs.
- HashSet<Pair<Integer, Integer>> featureSet = convertToHashSet(features, 0);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet = convertToHashSet(features, 0);
verify(controller).bind(featureSet);
verify(controller, never()).unbind();
+ verify(mMockQueryManager, never()).startQuery(any(), any());
assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName());
}
@@ -388,34 +319,29 @@
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- Set<String> carrierFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the voice feature.
- carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
// Only return info if not using the compat argument
- when(mMockPM.queryIntentServicesAsUser(
- argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
- anyInt(), anyInt())).thenReturn(info);
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
- mTestImsResolver.populateCacheAndStartBind();
-
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
// Verify that all features that have been defined for the carrier override are bound
- HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
- carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
- verify(carrierController).bind(carrierFeatureSet);
+ verify(carrierController).bind(carrierFeatures);
verify(carrierController, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
// Verify that all features that are not defined in the carrier override are bound in the
// device controller (including emergency voice for slot 0)
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 0);
- deviceFeatureSet.removeAll(carrierFeatureSet);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 0);
verify(deviceController).bind(deviceFeatureSet);
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
@@ -431,7 +357,6 @@
setupResolver(2/*numSlots*/);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
- mTestImsResolver.populateCacheAndStartBind();
// Callback from mock ImsServiceControllers
// All features on slot 1 should be the device default
@@ -464,30 +389,12 @@
features.add(ImsResolver.METADATA_MMTEL_FEATURE);
// Doesn't include RCS feature by default
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
- when(mMockPM.queryIntentServicesAsUser(
- argThat(argument -> ImsService.SERVICE_INTERFACE.equals(
- argument.getAction())), anyInt(), anyInt()))
- .thenReturn(info);
- ImsServiceController controller = mock(ImsServiceController.class);
- mTestImsResolver.setImsServiceControllerFactory(
- new ImsResolver.ImsServiceControllerFactory() {
- @Override
- public String getServiceInterface() {
- return ImsService.SERVICE_INTERFACE;
- }
-
- @Override
- public ImsServiceController create(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks) {
- when(controller.getComponentName()).thenReturn(componentName);
- return controller;
- }
- });
-
+ setupPackageQuery(info);
+ ImsServiceController controller = setupController();
// Bind using default features
- mTestImsResolver.populateCacheAndStartBind();
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
- HashSet<Pair<Integer, Integer>> featureSet = convertToHashSet(features, 0);
+ startBind();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet =
+ convertToHashSet(features, 0);
featureSet.addAll(convertToHashSet(features, 1));
verify(controller).bind(featureSet);
@@ -497,16 +404,11 @@
info.clear();
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, newFeatures, true));
- // Tell the package manager that a new device feature is installed
- Intent addPackageIntent = new Intent();
- addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
- addPackageIntent.setData(new Uri.Builder().scheme("package")
- .opaquePart(TEST_DEVICE_DEFAULT_NAME.getPackageName()).build());
- mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ packageChanged(TEST_DEVICE_DEFAULT_NAME.getPackageName());
//Verify new feature is added to the device default.
- HashSet<Pair<Integer, Integer>> newFeatureSet = convertToHashSet(newFeatures, 0);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> newFeatureSet =
+ convertToHashSet(newFeatures, 0);
newFeatureSet.addAll(convertToHashSet(newFeatures, 1));
verify(controller).changeImsServiceFeatures(newFeatureSet);
}
@@ -524,36 +426,36 @@
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- Set<String> carrierFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the emergency voice feature.
- carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
- carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_MMTEL));
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_RCS));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(
- argThat(argument -> ImsService.SERVICE_INTERFACE.equals(
- argument.getAction())), anyInt(), anyInt()))
- .thenReturn(info);
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
- mTestImsResolver.populateCacheAndStartBind();
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
// Verify that all features that have been defined for the carrier override are bound
- HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
- carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
- verify(carrierController).bind(carrierFeatureSet);
+ verify(carrierController).bind(carrierFeatures);
verify(carrierController, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
// Verify that all features that are not defined in the carrier override are bound in the
// device controller (including emergency voice for slot 0)
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
- deviceFeatures.removeAll(carrierFeatures);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
- verify(deviceController).bind(deviceFeatureSet);
+ deviceFeatureSet.removeAll(carrierFeatures);
+ // we will first have bound to device and then the features will change once the dynamic
+ // returns. So, instead of checking the bind parameters, we will check the change parameters
+ verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
@@ -565,20 +467,15 @@
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, newDeviceFeatures, true));
// Tell the package manager that a new device feature is installed
- Intent addPackageIntent = new Intent();
- addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
- addPackageIntent.setData(new Uri.Builder().scheme("package")
- .opaquePart(TEST_DEVICE_DEFAULT_NAME.getPackageName()).build());
- mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ packageChanged(TEST_DEVICE_DEFAULT_NAME.getPackageName());
//Verify new feature is added to the device default.
// add all features for slot 1
- HashSet<Pair<Integer, Integer>> newDeviceFeatureSet =
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> newDeviceFeatureSet =
convertToHashSet(newDeviceFeatures, 1);
- // remove carrier overrides for slot 0
- newDeviceFeatures.removeAll(carrierFeatures);
newDeviceFeatureSet.addAll(convertToHashSet(newDeviceFeatures, 0));
+ // remove carrier overrides for slot 0
+ newDeviceFeatureSet.removeAll(carrierFeatures);
verify(deviceController).changeImsServiceFeatures(newDeviceFeatureSet);
verify(carrierController, never()).changeImsServiceFeatures(any());
}
@@ -597,59 +494,47 @@
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- Set<String> carrierFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the emergency voice feature.
- carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_MMTEL));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(
- argThat(argument -> ImsService.SERVICE_INTERFACE.equals(
- argument.getAction())), anyInt(), anyInt()))
- .thenReturn(info);
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
- mTestImsResolver.populateCacheAndStartBind();
-
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
- HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
- carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
- verify(carrierController).bind(carrierFeatureSet);
+ verify(carrierController).bind(carrierFeatures);
verify(carrierController, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
// Verify that all features that are not defined in the carrier override are bound in the
// device controller (including emergency voice for slot 0)
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
- deviceFeatures.removeAll(carrierFeatures);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
- verify(deviceController).bind(deviceFeatureSet);
+ deviceFeatureSet.removeAll(carrierFeatures);
+ // device ImsService will bind with all of its defined features first and then when the
+ // carrier query comes back, it will change. So, checking change instead of bind here.
+ verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
// add RCS to carrier features list
- Set<String> newCarrierFeatures = new HashSet<>();
- newCarrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
- newCarrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
- info.clear();
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, newCarrierFeatures, true));
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
// Tell the package manager that a new device feature is installed
- Intent addPackageIntent = new Intent();
- addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
- addPackageIntent.setData(new Uri.Builder().scheme("package")
- .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
- mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ packageChanged(TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 2);
//Verify new feature is added to the carrier override.
// add all features for slot 0
- HashSet<Pair<Integer, Integer>> newCarrierFeatureSet =
- convertToHashSet(newCarrierFeatures, 0);
- verify(carrierController).changeImsServiceFeatures(newCarrierFeatureSet);
- deviceFeatureSet.removeAll(newCarrierFeatureSet);
+ verify(carrierController).changeImsServiceFeatures(carrierFeatures);
+ deviceFeatureSet.removeAll(carrierFeatures);
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
@@ -668,64 +553,52 @@
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- Set<String> carrierFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the voice feature.
- carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(
- argThat(argument -> ImsService.SERVICE_INTERFACE.equals(
- argument.getAction())), anyInt(), anyInt()))
- .thenReturn(info);
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
- mTestImsResolver.populateCacheAndStartBind();
-
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
- HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
- carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
- verify(carrierController).bind(carrierFeatureSet);
+ verify(carrierController).bind(carrierFeatures);
verify(carrierController, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
// Verify that all features that are not defined in the carrier override are bound in the
// device controller (including emergency voice for slot 0)
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
- deviceFeatures.removeAll(carrierFeatures);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
- verify(deviceController).bind(deviceFeatureSet);
+ deviceFeatureSet.removeAll(carrierFeatures);
+ // we will first have bound to device and then the features will change once the dynamic
+ // returns. So, instead of checking the bind parameters, we will check the change parameters
+ verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
- // remove RCS from carrier features list
- Set<String> newCarrierFeatures = new HashSet<>();
- newCarrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
- info.clear();
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, newCarrierFeatures, true));
-
+ // change supported feature to MMTEL only
+ carrierFeatures.clear();
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_MMTEL));
// Tell the package manager that a new device feature is installed
- Intent addPackageIntent = new Intent();
- addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
- addPackageIntent.setData(new Uri.Builder().scheme("package")
- .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
- mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ packageChanged(TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 2);
//Verify new feature is added to the carrier override.
- // add all features for slot 0
- HashSet<Pair<Integer, Integer>> newCarrierFeatureSet =
- convertToHashSet(newCarrierFeatures, 0);
- verify(carrierController).changeImsServiceFeatures(newCarrierFeatureSet);
+ verify(carrierController).changeImsServiceFeatures(carrierFeatures);
Set<String> newDeviceFeatures = new HashSet<>();
newDeviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
newDeviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
- HashSet<Pair<Integer, Integer>> newDeviceFeatureSet = convertToHashSet(newDeviceFeatures,
- 1);
- newDeviceFeatures.removeAll(newCarrierFeatures);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> newDeviceFeatureSet =
+ convertToHashSet(newDeviceFeatures, 1);
newDeviceFeatureSet.addAll(convertToHashSet(newDeviceFeatures, 0));
+ newDeviceFeatureSet.removeAll(carrierFeatures);
verify(deviceController).changeImsServiceFeatures(newDeviceFeatureSet);
}
@@ -744,42 +617,29 @@
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(
- argThat(argument -> (argument == null || ImsService.SERVICE_INTERFACE
- .equals(argument.getAction()))), anyInt(), anyInt()))
- .thenReturn(info);
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
- mTestImsResolver.populateCacheAndStartBind();
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ startBind();
- Set<String> carrierFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the voice feature.
- carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(
- argThat(argument -> (argument == null || ImsService.SERVICE_INTERFACE
- .equals(argument.getAction()))), anyInt(), anyInt()))
- .thenReturn(info);
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
// Tell the package manager that a new carrier app is installed
- Intent addPackageIntent = new Intent();
- addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
- addPackageIntent.setData(new Uri.Builder().scheme("package")
- .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
- mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ packageChanged(TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
- HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
- carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
- verify(carrierController).bind(carrierFeatureSet);
+ verify(carrierController).bind(carrierFeatures);
// device features change
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
- deviceFeatureSet.removeAll(carrierFeatureSet);
+ deviceFeatureSet.removeAll(carrierFeatures);
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
@@ -797,37 +657,32 @@
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- Set<String> carrierFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the voice feature.
- carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
- mTestImsResolver.populateCacheAndStartBind();
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Tell the package manager that carrier app is uninstalled
- Intent removePackageIntent = new Intent();
- removePackageIntent.setAction(Intent.ACTION_PACKAGE_REMOVED);
- removePackageIntent.setData(new Uri.Builder().scheme("package")
- .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
info.clear();
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
- mTestPackageBroadcastReceiver.onReceive(null, removePackageIntent);
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ packageRemoved(TEST_CARRIER_DEFAULT_NAME.getPackageName());
// Verify that the carrier controller is unbound
verify(carrierController).unbind();
assertNull(mTestImsResolver.getImsServiceInfoFromCache(
TEST_CARRIER_DEFAULT_NAME.getPackageName()));
// device features change to include all supported functionality
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
@@ -846,23 +701,23 @@
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- Set<String> carrierFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the voice feature.
- carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
- mTestImsResolver.populateCacheAndStartBind();
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
setConfigCarrierString(0, null);
Intent carrierConfigIntent = new Intent();
- carrierConfigIntent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, 0);
+ carrierConfigIntent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, 0);
mTestCarrierConfigReceiver.onReceive(null, carrierConfigIntent);
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
@@ -871,7 +726,8 @@
assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
TEST_CARRIER_DEFAULT_NAME.getPackageName()));
// device features change
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
@@ -890,45 +746,49 @@
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
- Set<String> carrierFeatures1 = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures1 = new HashSet<>();
// Carrier service 1
- carrierFeatures1.add(ImsResolver.METADATA_MMTEL_FEATURE);
- carrierFeatures1.add(ImsResolver.METADATA_RCS_FEATURE);
- Set<String> carrierFeatures2 = new HashSet<>();
+ carrierFeatures1.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_MMTEL));
+ carrierFeatures1.add(
+ new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures2 = new HashSet<>();
// Carrier service 2 doesn't support the voice feature.
- carrierFeatures2.add(ImsResolver.METADATA_RCS_FEATURE);
- info.add(getResolveInfo(TEST_CARRIER_2_DEFAULT_NAME, carrierFeatures2, true));
- info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures1, true));
+ carrierFeatures2.add(
+ new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ info.add(getResolveInfo(TEST_CARRIER_2_DEFAULT_NAME, new HashSet<>(), true));
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
- when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+ setupPackageQuery(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController1 = mock(ImsServiceController.class);
ImsServiceController carrierController2 = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController1, carrierController2);
- mTestImsResolver.populateCacheAndStartBind();
- waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ startBind();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures1, 1);
setConfigCarrierString(0, TEST_CARRIER_2_DEFAULT_NAME.getPackageName());
Intent carrierConfigIntent = new Intent();
- carrierConfigIntent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, 0);
+ carrierConfigIntent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, 0);
mTestCarrierConfigReceiver.onReceive(null, carrierConfigIntent);
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ setupDynamicQueryFeatures(TEST_CARRIER_2_DEFAULT_NAME, carrierFeatures2, 1);
// Verify that carrier 1 is unbound
verify(carrierController1).unbind();
assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
TEST_CARRIER_DEFAULT_NAME.getPackageName()));
// Verify that carrier 2 is bound
- HashSet<Pair<Integer, Integer>> carrier2FeatureSet = convertToHashSet(carrierFeatures2, 0);
- verify(carrierController2).bind(carrier2FeatureSet);
+ verify(carrierController2).bind(carrierFeatures2);
assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
- TEST_CARRIER_DEFAULT_NAME.getPackageName()));
+ TEST_CARRIER_2_DEFAULT_NAME.getPackageName()));
// device features change to accommodate for the features carrier 2 lacks
- HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
- deviceFeatures.removeAll(carrierFeatures2);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
+ deviceFeatureSet.removeAll(carrierFeatures2);
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
@@ -958,6 +818,86 @@
mTestCarrierConfigReceiver = carrierConfigCaptor.getValue();
mTestPackageBroadcastReceiver = packageBroadcastCaptor.getValue();
mTestImsResolver.setSubscriptionManagerProxy(mTestSubscriptionManagerProxy);
+ when(mMockQueryManagerFactory.create(any(Context.class),
+ any(ImsServiceFeatureQueryManager.Listener.class))).thenReturn(mMockQueryManager);
+ mTestImsResolver.setImsDynamicQueryManagerFactory(mMockQueryManagerFactory);
+ }
+
+ private void setupPackageQuery(List<ResolveInfo> infos) {
+ // Only return info if not using the compat argument
+ when(mMockPM.queryIntentServicesAsUser(
+ argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
+ anyInt(), anyInt())).thenReturn(infos);
+ }
+
+ private void setupPackageQuery(ComponentName name, Set<String> features,
+ boolean isPermissionGranted) {
+ List<ResolveInfo> info = new ArrayList<>();
+ info.add(getResolveInfo(name, features, isPermissionGranted));
+ // Only return info if not using the compat argument
+ when(mMockPM.queryIntentServicesAsUser(
+ argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
+ anyInt(), anyInt())).thenReturn(info);
+ }
+
+ private ImsServiceController setupController() {
+ ImsServiceController controller = mock(ImsServiceController.class);
+ mTestImsResolver.setImsServiceControllerFactory(
+ new ImsResolver.ImsServiceControllerFactory() {
+ @Override
+ public String getServiceInterface() {
+ return ImsService.SERVICE_INTERFACE;
+ }
+
+ @Override
+ public ImsServiceController create(Context context, ComponentName componentName,
+ ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+ when(controller.getComponentName()).thenReturn(componentName);
+ return controller;
+ }
+ });
+ return controller;
+ }
+
+ private void startBind() {
+ mTestImsResolver.initPopulateCacheAndStartBind();
+ ArgumentCaptor<ImsServiceFeatureQueryManager.Listener> queryManagerCaptor =
+ ArgumentCaptor.forClass(ImsServiceFeatureQueryManager.Listener.class);
+ verify(mMockQueryManagerFactory).create(any(Context.class), queryManagerCaptor.capture());
+ mDynamicQueryListener = queryManagerCaptor.getValue();
+ waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ }
+
+ private void setupDynamicQueryFeatures(ComponentName name,
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features, int times) {
+ // wait for schedule to happen
+ waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ // ensure that startQuery was called
+ when(mMockQueryManager.startQuery(any(ComponentName.class), any(String.class)))
+ .thenReturn(true);
+ verify(mMockQueryManager, Mockito.times(times)).startQuery(eq(name), any(String.class));
+ mDynamicQueryListener.onComplete(name, features);
+ // wait for handling of onComplete
+ waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ }
+
+ public void packageChanged(String packageName) {
+ // Tell the package manager that a new device feature is installed
+ Intent addPackageIntent = new Intent();
+ addPackageIntent.setAction(Intent.ACTION_PACKAGE_CHANGED);
+ addPackageIntent.setData(new Uri.Builder().scheme("package").opaquePart(packageName)
+ .build());
+ mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
+ waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+ }
+
+ public void packageRemoved(String packageName) {
+ Intent removePackageIntent = new Intent();
+ removePackageIntent.setAction(Intent.ACTION_PACKAGE_REMOVED);
+ removePackageIntent.setData(new Uri.Builder().scheme("package")
+ .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
+ mTestPackageBroadcastReceiver.onReceive(null, removePackageIntent);
+ waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
}
private void setImsServiceControllerFactory(ImsServiceController deviceController,
@@ -1022,13 +962,14 @@
CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, packageName);
}
- private HashSet<Pair<Integer, Integer>> convertToHashSet(Set<String> features, int subId) {
- HashSet<Pair<Integer, Integer>> featureSet = features.stream()
+ private HashSet<ImsFeatureConfiguration.FeatureSlotPair> convertToHashSet(
+ Set<String> features, int slotId) {
+ return features.stream()
// We do not count this as a valid feature set member.
.filter(f -> !ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE.equals(f))
- .map(f -> new Pair<>(subId, metadataStringToFeature(f)))
+ .map(f -> new ImsFeatureConfiguration.FeatureSlotPair(slotId,
+ metadataStringToFeature(f)))
.collect(Collectors.toCollection(HashSet::new));
- return featureSet;
}
private int metadataStringToFeature(String f) {
@@ -1041,6 +982,8 @@
return -1;
}
+ // Make sure the metadata provided in the service definition creates the associated features in
+ // the ImsServiceInfo. Note: this only tests for one slot.
private boolean isImsServiceInfoEqual(ComponentName name, Set<String> features,
ImsResolver.ImsServiceInfo sInfo) {
if (!Objects.equals(sInfo.name, name)) {
@@ -1049,17 +992,23 @@
for (String f : features) {
switch (f) {
case ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE:
- if (!sInfo.supportedFeatures.contains(ImsFeature.FEATURE_EMERGENCY_MMTEL)) {
+ if (!sInfo.getSupportedFeatures().contains(
+ new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_EMERGENCY_MMTEL))) {
return false;
}
break;
case ImsResolver.METADATA_MMTEL_FEATURE:
- if (!sInfo.supportedFeatures.contains(ImsFeature.FEATURE_MMTEL)) {
+ if (!sInfo.getSupportedFeatures().contains(
+ new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_MMTEL))) {
return false;
}
break;
case ImsResolver.METADATA_RCS_FEATURE:
- if (!sInfo.supportedFeatures.contains(ImsFeature.FEATURE_RCS)) {
+ if (!sInfo.getSupportedFeatures().contains(
+ new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_RCS))) {
return false;
}
break;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
index 9a7f111..af2f81b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
@@ -39,7 +39,7 @@
import android.support.test.filters.FlakyTest;
import android.support.test.runner.AndroidJUnit4;
import android.telephony.ims.ImsService;
-import android.util.Pair;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
import com.android.ims.internal.IImsServiceFeatureCallback;
@@ -75,7 +75,6 @@
};
@Spy TestImsServiceControllerAdapter mMockServiceControllerBinder;
- @Mock IBinder mMockBinder;
@Mock ImsServiceController.ImsServiceControllerCallbacks mMockCallbacks;
@Mock IImsServiceFeatureCallback mMockProxyCallbacks;
@Mock Context mMockContext;
@@ -90,7 +89,7 @@
super.setUp();
mTestImsServiceController = new ImsServiceController(mMockContext, mTestComponentName,
mMockCallbacks, mHandler, REBIND_RETRY);
- mTestImsServiceController.addImsServiceFeatureListener(mMockProxyCallbacks);
+ mTestImsServiceController.addImsServiceFeatureCallback(mMockProxyCallbacks);
when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
}
@@ -108,11 +107,11 @@
@FlakyTest
@Test
public void testBindService() {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
// Slot 1, MMTel
- testFeatures.add(new Pair<>(1, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
// Slot 1, RCS
- testFeatures.add(new Pair<>(1, 2));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
ArgumentCaptor<Intent> intentCaptor =
ArgumentCaptor.forClass(Intent.class);
@@ -132,9 +131,9 @@
@FlakyTest
@Test
public void testBindFailureWhenBound() {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
// Slot 1, MMTel
- testFeatures.add(new Pair<>(1, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
bindAndConnectService(testFeatures);
// already bound, should return false
@@ -150,11 +149,11 @@
@FlakyTest
@Test
public void testBindServiceAndConnected() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
// Slot 1, MMTel
- testFeatures.add(new Pair<>(1, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
// Slot 1, RCS
- testFeatures.add(new Pair<>(1, 2));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
bindAndConnectService(testFeatures);
@@ -173,17 +172,73 @@
}
/**
+ * Tests Emergency MMTEL ImsServiceController callbacks are properly called when an ImsService
+ * is bound and connected.
+ */
+ @FlakyTest
+ @Test
+ public void testBindEmergencyMmTel() throws RemoteException {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, Emergency MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 0));
+ // Slot 1, MmTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+
+ bindAndConnectService(testFeatures);
+
+ IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
+ verify(binder).linkToDeath(any(), anyInt());
+ verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
+ // We do not want this callback to happen for emergency MMTEL
+ verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(1), eq(0),
+ eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
+ eq(mTestImsServiceController));
+ // Make sure this callback happens, which will notify the framework of emergency calling
+ // availability.
+ verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(0));
+ verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
+ assertEquals(mMockServiceControllerBinder.getBinder(),
+ mTestImsServiceController.getImsServiceControllerBinder());
+ }
+
+ /**
+ * Tests that if a callback is added after the ImsServiceController is already bound, we get a
+ * imsFeatureCreated callback.
+ */
+ @FlakyTest
+ @Test
+ public void testCallbacksHappenWhenAddedAfterBind() throws RemoteException {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ // Slot 1, Emergency MMTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 0));
+ // Slot 1, MmTel
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+ mTestImsServiceController.removeImsServiceFeatureCallbacks();
+
+ bindAndConnectService(testFeatures);
+ // add the callback after bind
+ mTestImsServiceController.addImsServiceFeatureCallback(mMockProxyCallbacks);
+
+ // Make sure this callback happens for Emergency MMTEL and MMTEL
+ verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(0));
+ verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
+ assertEquals(mMockServiceControllerBinder.getBinder(),
+ mTestImsServiceController.getImsServiceControllerBinder());
+ }
+
+ /**
* Tests ImsServiceController callbacks are properly called when an ImsService is bound and
* connected.
*/
@FlakyTest
@Test
public void testBindServiceAndConnectedDisconnected() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
// Slot 1, MMTel
- testFeatures.add(new Pair<>(1, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
// Slot 1, RCS
- testFeatures.add(new Pair<>(1, 2));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
ServiceConnection conn = bindAndConnectService(testFeatures);
conn.onServiceDisconnected(mTestComponentName);
@@ -207,11 +262,11 @@
@FlakyTest
@Test
public void testBindServiceBindUnbind() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
// Slot 1, MMTel
- testFeatures.add(new Pair<>(1, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
// Slot 1, RCS
- testFeatures.add(new Pair<>(1, 2));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
ServiceConnection conn = bindAndConnectService(testFeatures);
mTestImsServiceController.unbind();
@@ -235,11 +290,11 @@
@FlakyTest
@Test
public void testBindServiceAndBinderDied() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
// Slot 1, MMTel
- testFeatures.add(new Pair<>(1, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
// Slot 1, RCS
- testFeatures.add(new Pair<>(1, 2));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
bindAndConnectService(testFeatures);
ArgumentCaptor<IBinder.DeathRecipient> deathCaptor =
ArgumentCaptor.forClass(IBinder.DeathRecipient.class);
@@ -262,17 +317,18 @@
@FlakyTest
@Test
public void testBindServiceAndAddFeature() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
// Slot 1, MMTel
- testFeatures.add(new Pair<>(1, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
bindAndConnectService(testFeatures);
verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
eq(mTestImsServiceController));
verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
// Create a new list with an additional item
- HashSet<Pair<Integer, Integer>> testFeaturesWithAddition = new HashSet<>(testFeatures);
- testFeaturesWithAddition.add(new Pair<>(2, 1));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
+ testFeatures);
+ testFeaturesWithAddition.add(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
@@ -288,11 +344,11 @@
@FlakyTest
@Test
public void testBindServiceAndRemoveFeature() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
// Slot 1, MMTel
- testFeatures.add(new Pair<>(1, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
// Slot 2, MMTel
- testFeatures.add(new Pair<>(2, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
bindAndConnectService(testFeatures);
verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
@@ -303,8 +359,9 @@
eq(mTestImsServiceController));
verify(mMockProxyCallbacks).imsFeatureCreated(eq(2), eq(1));
// Create a new list with one less item
- HashSet<Pair<Integer, Integer>> testFeaturesWithSubtraction = new HashSet<>(testFeatures);
- testFeaturesWithSubtraction.remove(new Pair<>(2, 1));
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithSubtraction =
+ new HashSet<>(testFeatures);
+ testFeaturesWithSubtraction.remove(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithSubtraction);
@@ -320,11 +377,11 @@
@FlakyTest
@Test
public void testBindServiceAndRemoveAllFeatures() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
// slot 1, MMTel
- testFeatures.add(new Pair<>(1, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
// slot 2, MMTel
- testFeatures.add(new Pair<>(2, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
bindAndConnectService(testFeatures);
verify(mMockServiceControllerBinder).createMMTelFeature(eq(1));
verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
@@ -354,15 +411,16 @@
@FlakyTest
@Test
public void testBindUnbindServiceAndAddFeature() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
// Slot 1, MMTel
- testFeatures.add(new Pair<>(1, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
bindAndConnectService(testFeatures);
mTestImsServiceController.unbind();
// Create a new list with an additional item
- HashSet<Pair<Integer, Integer>> testFeaturesWithAddition = new HashSet<>(testFeatures);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
+ testFeatures);
// Try to create an RCS feature
- testFeaturesWithAddition.add(new Pair<>(1, 2));
+ testFeaturesWithAddition.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
@@ -379,11 +437,11 @@
@FlakyTest
@Test
public void testAutoBindAfterBinderDied() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
// Slot 1, MMTel
- testFeatures.add(new Pair<>(1, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
// Slot 1, RCS
- testFeatures.add(new Pair<>(1, 2));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
bindAndConnectService(testFeatures);
getDeathRecipient().binderDied();
@@ -400,11 +458,11 @@
@FlakyTest
@Test
public void testNoAutoBindBeforeTimeout() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
// Slot 1, MMTel
- testFeatures.add(new Pair<>(1, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
// Slot 1, RCS
- testFeatures.add(new Pair<>(1, 2));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
bindAndConnectService(testFeatures);
getDeathRecipient().binderDied();
@@ -419,11 +477,11 @@
@FlakyTest
@Test
public void testUnbindCauseAutoBindCancelAfterBinderDied() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
// Slot 1, MMTel
- testFeatures.add(new Pair<>(1, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
// Slot 1, RCS
- testFeatures.add(new Pair<>(1, 2));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
bindAndConnectService(testFeatures);
getDeathRecipient().binderDied();
@@ -443,11 +501,11 @@
@FlakyTest
@Test
public void testBindCauseAutoBindCancelAfterBinderDied() throws RemoteException {
- HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
// Slot 1, MMTel
- testFeatures.add(new Pair<>(1, 1));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
// Slot 1, RCS
- testFeatures.add(new Pair<>(1, 2));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
bindAndConnectService(testFeatures);
getDeathRecipient().binderDied();
mTestImsServiceController.bind(testFeatures);
@@ -458,7 +516,8 @@
verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
}
- private ServiceConnection bindAndConnectService(HashSet<Pair<Integer, Integer>> testFeatures) {
+ private ServiceConnection bindAndConnectService(
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures) {
ArgumentCaptor<ServiceConnection> serviceCaptor =
ArgumentCaptor.forClass(ServiceConnection.class);
assertTrue(mTestImsServiceController.bind(testFeatures));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java b/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java
index 294d6f2..38b3203 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java
@@ -71,11 +71,6 @@
}
@Override
- public void notifyImsFeatureReady(int slotId, int featureType)
- throws RemoteException {
- }
-
- @Override
public IImsConfig getConfig(int slotId) throws RemoteException {
return new ImsConfigImplBase().getIImsConfig();
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
index 41698ce..71d0794 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
@@ -218,7 +218,7 @@
assertEquals(TelephonyEvent.Type.CARRIER_ID_MATCHING, log.events[0].type);
assertEquals(1, log.events[0].carrierIdMatching.cidTableVersion);
assertEquals(1, log.events[0].carrierIdMatching.result.carrierId);
- assertTrue(log.events[0].carrierIdMatching.result.mccmnc.isEmpty());
+ assertEquals("mccmncTest", log.events[0].carrierIdMatching.result.mccmnc);
assertEquals("gid1Test", log.events[0].carrierIdMatching.result.gid1);
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
index a09c4eb..c02f68b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
@@ -53,14 +53,6 @@
throw new RuntimeException("Not Implemented");
}
@Override
- public boolean isApnSupported(String name) {
- throw new RuntimeException("Not Implemented");
- }
- @Override
- public int getApnPriority(String name) {
- throw new RuntimeException("Not Implemented");
- }
- @Override
public LinkProperties getLinkProperties(String apnType) {
throw new RuntimeException("Not Implemented");
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
index a768914..1b20833 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
@@ -525,6 +525,14 @@
throw new RuntimeException("not implemented");
}
+ public void invokeOemRilRequestRaw(byte[] data, Message response) {
+ throw new RuntimeException("not implemented");
+ }
+
+ public void invokeOemRilRequestStrings(String[] strings, Message response) {
+ throw new RuntimeException("not implemented");
+ }
+
public void nvReadItem(int itemID, Message response) {
throw new RuntimeException("not implemented");
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
index 0200963..b14fc10 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
@@ -250,11 +250,6 @@
throw new RuntimeException("not implemented");
}
@Override
- public List<SubscriptionInfo> getSubInfoUsingSlotIndexWithCheck(int slotId, boolean needCheck,
- String callingPackage) {
- throw new RuntimeException("not implemented");
- }
- @Override
public void updatePhonesAvailability(Phone[] phones) {
throw new RuntimeException("not implemented");
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
index 7ee0053..2c73efa 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
@@ -316,6 +316,11 @@
}
@Override
+ public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ @Override
public void notifyCarrierNetworkChange(boolean active) {
throw new RuntimeException("Not implemented");
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccCardProxyTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccCardProxyTest.java
deleted file mode 100644
index 479acb7..0000000
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccCardProxyTest.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * 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.uicc;
-
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doReturn;
-
-import android.os.HandlerThread;
-import android.support.test.filters.FlakyTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.IccCardConstants.State;
-import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
-import com.android.internal.telephony.uicc.IccCardStatus.CardState;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.mockito.Mock;
-
-public class IccCardProxyTest extends TelephonyTest {
- private IccCardProxy mIccCardProxyUT;
- // private UiccCard mUiccCard;
- private IccCardProxyHandlerThread mIccCardProxyHandlerThread;
- private static final int PHONE_ID = 0;
- private static final int PHONE_COUNT = 1;
-
- private static final int SCARY_SLEEP_MS = 200;
- // Must match IccCardProxy.EVENT_ICC_CHANGED
- private static final int EVENT_ICC_CHANGED = 3;
-
- @Mock private IccCardStatus mIccCardStatus;
- @Mock private UiccCard mUiccCard;
- @Mock private UiccCardApplication mUiccCardApplication;
-
- private class IccCardProxyHandlerThread extends HandlerThread {
-
- private IccCardProxyHandlerThread(String name) {
- super(name);
- }
-
- @Override
- public void onLooperPrepared() {
- /* create a new UICC Controller associated with the simulated Commands */
- mIccCardProxyUT = new IccCardProxy(mContext, mSimulatedCommands, PHONE_ID);
- setReady(true);
- }
- }
-
- @Before
- public void setUp() throws Exception {
- super.setUp(this.getClass().getSimpleName());
- doReturn(PHONE_COUNT).when(mTelephonyManager).getPhoneCount();
- doReturn(PHONE_COUNT).when(mTelephonyManager).getSimCount();
- mSimulatedCommands.setIccCardStatus(mIccCardStatus);
- mIccCardProxyHandlerThread = new IccCardProxyHandlerThread(TAG);
- mIccCardProxyHandlerThread.start();
- waitUntilReady();
- }
-
- @After
- public void tearDown() throws Exception {
- mIccCardProxyHandlerThread.quitSafely();
- super.tearDown();
- }
-
- @Test
- @SmallTest
- public void testInitialCardState() {
- assertEquals(mIccCardProxyUT.getState(), State.UNKNOWN);
- }
-
- @Test
- @Ignore
- @FlakyTest
- @SmallTest
- public void testPowerOn() {
- mSimulatedCommands.setRadioPower(true, null);
- mSimulatedCommands.notifyRadioOn();
- doReturn(mUiccCard).when(mUiccController).getUiccCard(anyInt());
- mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
-
- waitForMs(SCARY_SLEEP_MS);
- assertEquals(CommandsInterface.RadioState.RADIO_ON, mSimulatedCommands.getRadioState());
- assertEquals(mIccCardProxyUT.getState(), State.NOT_READY);
- logd("IccCardProxy state = " + mIccCardProxyUT.getState());
- }
-
- @Test
- @Ignore
- @FlakyTest
- @SmallTest
- public void testCardLoaded() {
- testPowerOn();
- doReturn(CardState.CARDSTATE_PRESENT).when(mUiccCard).getCardState();
- mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
-
- waitForMs(SCARY_SLEEP_MS);
- assertEquals(mIccCardProxyUT.getState(), State.NOT_READY);
- }
-
- @Test
- @Ignore
- @FlakyTest
- @SmallTest
- public void testAppNotLoaded() {
- testPowerOn();
- doReturn(CardState.CARDSTATE_PRESENT).when(mUiccCard).getCardState();
- mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
- doReturn(AppState.APPSTATE_UNKNOWN).when(mUiccCardApplication).getState();
- doReturn(mUiccCardApplication).when(mUiccCard).getApplication(anyInt());
-
- waitForMs(SCARY_SLEEP_MS);
- assertEquals(mIccCardProxyUT.getState(), State.NOT_READY);
- }
-
- @Test
- @Ignore
- @FlakyTest
- @SmallTest
- public void testAppReady() {
- testPowerOn();
- doReturn(CardState.CARDSTATE_PRESENT).when(mUiccCard).getCardState();
- mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
- doReturn(AppState.APPSTATE_READY).when(mUiccCardApplication).getState();
- doReturn(mUiccCardApplication).when(mUiccCard).getApplication(anyInt());
-
- waitForMs(SCARY_SLEEP_MS);
- assertEquals(mIccCardProxyUT.getState(), State.READY);
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
index dc21b70..27b8531 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
@@ -63,7 +63,8 @@
@Override
public void onLooperPrepared() {
mUicccard = new UiccCard(mContextFixture.getTestDouble(),
- mSimulatedCommands, mIccCardStatus, 0 /* phoneId */);
+ mSimulatedCommands, mIccCardStatus, 0 /* phoneId */,
+ new Object());
/* create a custom handler for the Handler Thread */
mHandler = new Handler(mTestHandlerThread.getLooper()) {
@Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
index 9dd1af0..404754c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.uicc;
import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -23,18 +24,23 @@
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.isA;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.os.PersistableBundle;
+import android.support.test.filters.SmallTest;
+import android.telephony.CarrierConfigManager;
import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.cat.CatService;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
import org.junit.After;
import org.junit.Before;
@@ -50,8 +56,6 @@
}
private IccIoResult mIccIoResult;
- // Must match UiccProfile.EVENT_APP_READY
- private static final int EVENT_APP_READY = 6;
private static final int SCARY_SLEEP_MS = 200;
private UiccProfileHandlerThread mTestHandlerThread;
@@ -79,7 +83,7 @@
public void onLooperPrepared() {
mUiccProfile = new UiccProfile(mContextFixture.getTestDouble(),
mSimulatedCommands, mIccCardStatus, 0 /* phoneId */,
- mUiccCard);
+ mUiccCard, new Object());
/* create a custom handler for the Handler Thread */
mHandler = new Handler(mTestHandlerThread.getLooper()) {
@Override
@@ -283,7 +287,8 @@
waitForMs(50);
assertEquals(3, mUiccProfile.getNumApplications());
- mUiccProfile.sendMessage(mUiccProfile.obtainMessage(EVENT_APP_READY));
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
waitForMs(SCARY_SLEEP_MS);
assertEquals(mUiccProfile.getState(), State.NOT_READY);
}
@@ -315,7 +320,8 @@
waitForMs(50);
assertEquals(3, mUiccProfile.getNumApplications());
- mUiccProfile.sendMessage(mUiccProfile.obtainMessage(EVENT_APP_READY));
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
waitForMs(SCARY_SLEEP_MS);
// state is loaded as all records are loaded right away as SimulatedCommands returns
// response for them right away. Ideally applications and records should be mocked.
@@ -349,7 +355,86 @@
waitForMs(50);
assertEquals(3, mUiccProfile.getNumApplications());
- mUiccProfile.sendMessage(mUiccProfile.obtainMessage(EVENT_APP_READY));
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
+ waitForMs(SCARY_SLEEP_MS);
+ // state is loaded as all records are loaded right away as SimulatedCommands returns
+ // response for them right away. Ideally applications and records should be mocked.
+ assertEquals(State.LOADED, mUiccProfile.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfileApplicationWithDuplicateApps() {
+ /* update app status and index */
+ IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA2");
+ IccCardApplicationStatus imsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_ISIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA1");
+ IccCardApplicationStatus unknownApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_UNKNOWN,
+ IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA2");
+ IccCardApplicationStatus umtsAppDup = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ AppState.APPSTATE_DETECTED, "0xA2");
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{imsApp, umtsApp, unknownApp,
+ umtsAppDup};
+ mIccCardStatus.mCdmaSubscriptionAppIndex = -1;
+ mIccCardStatus.mImsSubscriptionAppIndex = 0;
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = 1;
+ Message mProfileUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_APPLICATION_EVENT);
+ setReady(false);
+ mProfileUpdate.sendToTarget();
+
+ waitUntilReady();
+
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+ assertEquals(4, mUiccProfile.getNumApplications());
+
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
+ waitForMs(SCARY_SLEEP_MS);
+ // state is loaded as all records are loaded right away as SimulatedCommands returns
+ // response for them right away. Ideally applications and records should be mocked.
+ assertEquals(State.LOADED, mUiccProfile.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfileApplicationWithDuplicateAppsInDifferentOrder() {
+ /* update app status and index */
+ IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA2");
+ IccCardApplicationStatus imsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_ISIM,
+ IccCardApplicationStatus.AppState.APPSTATE_READY, "0xA1");
+ IccCardApplicationStatus unknownApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_UNKNOWN,
+ IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA2");
+ IccCardApplicationStatus umtsAppDup = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ AppState.APPSTATE_DETECTED, "0xA2");
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{umtsAppDup, imsApp, umtsApp,
+ unknownApp};
+ mIccCardStatus.mCdmaSubscriptionAppIndex = -1;
+ mIccCardStatus.mImsSubscriptionAppIndex = 0;
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = 2;
+ Message mProfileUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_APPLICATION_EVENT);
+ setReady(false);
+ mProfileUpdate.sendToTarget();
+
+ waitUntilReady();
+
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+ assertEquals(4, mUiccProfile.getNumApplications());
+
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
waitForMs(SCARY_SLEEP_MS);
// state is loaded as all records are loaded right away as SimulatedCommands returns
// response for them right away. Ideally applications and records should be mocked.
@@ -373,7 +458,8 @@
waitForMs(50);
assertEquals(0, mUiccProfile.getNumApplications());
- mUiccProfile.sendMessage(mUiccProfile.obtainMessage(EVENT_APP_READY));
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
waitForMs(SCARY_SLEEP_MS);
// state is loaded since there is no applications.
assertEquals(State.NOT_READY, mUiccProfile.getState());
@@ -399,9 +485,121 @@
waitForMs(50);
assertEquals(1, mUiccProfile.getNumApplications());
- mUiccProfile.sendMessage(mUiccProfile.obtainMessage(EVENT_APP_READY));
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
waitForMs(SCARY_SLEEP_MS);
// state is loaded since there is no applications.
assertEquals(State.NOT_READY, mUiccProfile.getState());
}
+
+ private void testWithCsimApp() {
+ /* update app status and index */
+ IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ AppState.APPSTATE_READY, "0xA2");
+ IccCardApplicationStatus imsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_ISIM,
+ AppState.APPSTATE_READY, "0xA1");
+ IccCardApplicationStatus cdmaApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_CSIM,
+ AppState.APPSTATE_DETECTED, "0xA2");
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{imsApp, umtsApp, cdmaApp};
+ mIccCardStatus.mCdmaSubscriptionAppIndex = 2;
+ mIccCardStatus.mImsSubscriptionAppIndex = 0;
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = 1;
+
+ Message mProfileUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_APPLICATION_EVENT);
+ setReady(false);
+ mProfileUpdate.sendToTarget();
+
+ waitUntilReady();
+
+ /* wait for the carrier privilege rules to be loaded */
+ waitForMs(50);
+ assertEquals(3, mUiccProfile.getNumApplications());
+
+ mUiccProfile.mHandler.sendMessage(
+ mUiccProfile.mHandler.obtainMessage(UiccProfile.EVENT_APP_READY));
+ waitForMs(SCARY_SLEEP_MS);
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfileApplicationCdmaSupported() {
+ // CDMA supported
+ doReturn(true).when(mUiccController).isCdmaSupported();
+
+ testWithCsimApp();
+
+ // CDMA is supported and CSIM app is not ready, so state should be NOT_READY
+ assertEquals(State.NOT_READY, mUiccProfile.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateUiccProfileApplicationCdmaNotSupported() {
+ // CDMA supported
+ doReturn(false).when(mUiccController).isCdmaSupported();
+
+ testWithCsimApp();
+
+ // state is loaded as all records are loaded right away as SimulatedCommands returns
+ // response for them right away. Ideally applications and records should be mocked.
+ // CSIM is not ready but that should not matter since CDMA is not supported.
+ assertEquals(State.LOADED, mUiccProfile.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateExternalState() {
+ // IO_ERROR
+ doReturn(IccCardStatus.CardState.CARDSTATE_ERROR).when(mUiccCard).getCardState();
+ mUiccProfile.updateExternalState();
+ assertEquals(State.CARD_IO_ERROR, mUiccProfile.getState());
+
+ // RESTRICTED
+ doReturn(IccCardStatus.CardState.CARDSTATE_RESTRICTED).when(mUiccCard).getCardState();
+ mUiccProfile.updateExternalState();
+ assertEquals(State.CARD_RESTRICTED, mUiccProfile.getState());
+
+ // CARD PRESENT; no mUiccApplication - state should be NOT_READY
+ doReturn(IccCardStatus.CardState.CARDSTATE_PRESENT).when(mUiccCard).getCardState();
+ mUiccProfile.updateExternalState();
+ assertEquals(State.NOT_READY, mUiccProfile.getState());
+
+ // set mUiccApplication
+ testUpdateUiccProfileApplicationAllReady();
+ mUiccProfile.updateExternalState();
+ assertEquals(State.LOADED, mUiccProfile.getState());
+ }
+
+ @Test
+ @SmallTest
+ public void testCarrierConfigHandling() {
+ testUpdateUiccProfileApplication();
+
+ // Fake carrier name
+ String fakeCarrierName = "fakeCarrierName";
+ PersistableBundle carrierConfigBundle = mContextFixture.getCarrierConfigBundle();
+ carrierConfigBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, true);
+ carrierConfigBundle.putString(CarrierConfigManager.KEY_CARRIER_NAME_STRING,
+ fakeCarrierName);
+
+ // broadcast CARRIER_CONFIG_CHANGED
+ mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ waitForMs(200);
+
+ // verify that setSimOperatorNameForPhone() is called with fakeCarrierName
+ ArgumentCaptor<String> stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
+ verify(mTelephonyManager, atLeast(1)).setSimOperatorNameForPhone(anyInt(),
+ stringArgumentCaptor.capture());
+ boolean carrierFound = false;
+ for (String carrierName : stringArgumentCaptor.getAllValues()) {
+ if (fakeCarrierName.equals(carrierName)) {
+ carrierFound = true;
+ break;
+ }
+ }
+ assertTrue(carrierFound);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
index a001cac..457f021 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
@@ -17,14 +17,20 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.test.filters.SmallTest;
+import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.TelephonyTest;
import org.junit.After;
@@ -74,6 +80,7 @@
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
+ mContextFixture.putBooleanResource(com.android.internal.R.bool.config_hotswapCapable, true);
/* initially there are no application available */
mIccCardStatus.mApplications = new IccCardApplicationStatus[]{};
mIccCardStatus.mCdmaSubscriptionAppIndex =
@@ -95,8 +102,9 @@
@Test
@SmallTest
- public void testUpdateSlotStatus() {
+ public void testUpdateInactiveSlotStatus() {
IccSlotStatus iss = new IccSlotStatus();
+ iss.logicalSlotIndex = 0;
iss.slotState = IccSlotStatus.SlotState.SLOTSTATE_INACTIVE;
iss.cardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
iss.iccid = "fake-iccid";
@@ -115,8 +123,35 @@
assertNull(mUiccSlot.getUiccCard());
assertEquals(IccCardStatus.CardState.CARDSTATE_PRESENT, mUiccSlot.getCardState());
assertEquals(iss.iccid, mUiccSlot.getIccId());
+ }
+ @Test
+ @SmallTest
+ public void testUpdateActiveSlotStatus() {
+ // initial state
+ assertTrue(mUiccSlot.isActive());
+ assertNull(mUiccSlot.getUiccCard());
+ assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
+ assertNull(mUiccSlot.getIccId());
+
+ mSimulatedCommands.setRadioPower(true, null);
+ int phoneId = 0;
+ IccSlotStatus iss = new IccSlotStatus();
+ iss.logicalSlotIndex = phoneId;
iss.slotState = IccSlotStatus.SlotState.SLOTSTATE_ACTIVE;
+ iss.cardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
+ iss.iccid = "fake-iccid";
+
+ // update slot to inactive
+ mUiccSlot.update(mSimulatedCommands, iss);
+
+ // assert on updated values
+ assertTrue(mUiccSlot.isActive());
+ assertNull(mUiccSlot.getUiccCard());
+ assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
+ assertEquals(iss.iccid, mUiccSlot.getIccId());
+ verify(mSubInfoRecordUpdater).updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
// update slot to active
mUiccSlot.update(mSimulatedCommands, iss);
@@ -129,6 +164,7 @@
@SmallTest
public void testUpdateSlotStatusEuiccIsSupported() {
IccSlotStatus iss = new IccSlotStatus();
+ iss.logicalSlotIndex = 0;
iss.slotState = IccSlotStatus.SlotState.SLOTSTATE_INACTIVE;
iss.cardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
iss.iccid = "fake-iccid";
@@ -163,6 +199,7 @@
@SmallTest
public void testUpdateSlotStatusEuiccIsNotSupported() {
IccSlotStatus iss = new IccSlotStatus();
+ iss.logicalSlotIndex = 0;
iss.slotState = IccSlotStatus.SlotState.SLOTSTATE_INACTIVE;
iss.cardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
iss.iccid = "fake-iccid";
@@ -192,4 +229,41 @@
assertTrue(mUiccSlot.isActive());
assertFalse(mUiccSlot.isEuicc());
}
+
+ @Test
+ @SmallTest
+ public void testUpdateAbsentState() {
+ int phoneId = 0;
+ // Make sure when received CARDSTATE_ABSENT state in the first time,
+ mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
+ mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId);
+ verify(mSubInfoRecordUpdater).updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
+ assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
+ assertNull(mUiccSlot.getUiccCard());
+ }
+
+ @Test
+ @SmallTest
+ public void testUiccSlotCreateAndDispose() {
+ int phoneId = 0;
+ // Simulate when SIM is added, UiccCard and UiccProfile should be created.
+ mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
+ mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId);
+ verify(mTelephonyComponentFactory).makeUiccProfile(
+ anyObject(), eq(mSimulatedCommands), eq(mIccCardStatus), anyInt(), anyObject(),
+ anyObject());
+ assertEquals(IccCardStatus.CardState.CARDSTATE_PRESENT, mUiccSlot.getCardState());
+ assertNotNull(mUiccSlot.getUiccCard());
+
+ // Simulate when SIM is removed, UiccCard and UiccProfile should be disposed and ABSENT
+ // state is sent to SubscriptionInfoUpdater.
+ mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
+ mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId);
+ verify(mSubInfoRecordUpdater).updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, phoneId);
+ verify(mUiccProfile).dispose();
+ assertEquals(IccCardStatus.CardState.CARDSTATE_ABSENT, mUiccSlot.getCardState());
+ assertNull(mUiccSlot.getUiccCard());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
index a90e947..f933596 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
@@ -101,7 +101,7 @@
// The first broadcast should be sent after initialization.
UiccCard card = new UiccCard(mContext, mSimulatedCommands,
- makeCardStatus(CardState.CARDSTATE_PRESENT), 0 /* phoneId */);
+ makeCardStatus(CardState.CARDSTATE_PRESENT), 0 /* phoneId */, new Object());
when(UiccController.getInstance().getUiccCardForPhone(0)).thenReturn(card);
uiccLauncher.handleMessage(msg);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
index 831d0b8..dc621a5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
@@ -103,7 +103,7 @@
public void onLooperPrepared() {
mEuiccCard =
new EuiccCard(mContextFixture.getTestDouble(), mMockCi, mMockIccCardStatus,
- 0 /* phoneId */) {
+ 0 /* phoneId */, new Object()) {
@Override
protected byte[] getDeviceId() {
return IccUtils.bcdToBytes("987654321012345");
@@ -173,7 +173,7 @@
final CountDownLatch latch = new CountDownLatch(1);
mHandler.post(() -> {
mEuiccCard = new EuiccCard(mContextFixture.getTestDouble(), mMockCi,
- mMockIccCardStatus, 0 /* phoneId */);
+ mMockIccCardStatus, 0 /* phoneId */, new Object());
latch.countDown();
});
assertTrue(latch.await(WAIT_TIMEOUT_MLLIS, TimeUnit.MILLISECONDS));