Merge "refactor servicestate data/voice roaming states"
diff --git a/Android.bp b/Android.bp
index 790cb53..0fba2d1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -44,13 +44,13 @@
"voip-common",
"ims-common",
"services",
- "bouncycastle",
],
static_libs: [
"telephony-protos",
"android.hardware.radio-V1.0-java",
"android.hardware.radio-V1.1-java",
"android.hardware.radio-V1.2-java",
+ "android.hardware.radio-V1.3-java",
"android.hardware.radio.config-V1.0-java",
"android.hardware.radio.deprecated-V1.0-java",
"android.hidl.base-V1.0-java",
diff --git a/src/java/com/android/internal/telephony/CallStateException.java b/src/java/com/android/internal/telephony/CallStateException.java
index 8429146..064ecf4 100644
--- a/src/java/com/android/internal/telephony/CallStateException.java
+++ b/src/java/com/android/internal/telephony/CallStateException.java
@@ -28,6 +28,10 @@
public static final int ERROR_OUT_OF_SERVICE = 1;
public static final int ERROR_POWER_OFF = 2;
+ public static final int ERROR_ALREADY_DIALING = 3;
+ public static final int ERROR_CALL_RINGING = 4;
+ public static final int ERROR_CALLING_DISABLED = 5;
+ public static final int ERROR_TOO_MANY_CALLS = 6;
public
CallStateException()
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index 90527a1..025e31c 100755
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -274,9 +274,8 @@
// note that this triggers call state changed notif
clearDisconnected();
- if (!canDial()) {
- throw new CallStateException("cannot dial in current state");
- }
+ // Check for issues which would preclude dialing and throw a CallStateException.
+ checkForDialIssues();
String origNumber = dialString;
dialString = convertNumberIfNecessary(mPhone, dialString);
@@ -385,9 +384,8 @@
// note that this triggers call state changed notif
clearDisconnected();
- if (!canDial()) {
- throw new CallStateException("cannot dial in current state");
- }
+ // Check for issues which would preclude dialing and throw a CallStateException.
+ checkForDialIssues();
TelephonyManager tm =
(TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
@@ -613,41 +611,46 @@
&& !mForegroundCall.isFull();
}
- private boolean canDial() {
- boolean ret;
- int serviceState = mPhone.getServiceState().getState();
+ /**
+ * Determines if there are issues which would preclude dialing an outgoing call. Throws a
+ * {@link CallStateException} if there is an issue.
+ * @throws CallStateException
+ */
+ public void checkForDialIssues() throws CallStateException {
String disableCall = SystemProperties.get(
TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
- ret = (serviceState != ServiceState.STATE_POWER_OFF)
- && mPendingMO == null
- && !mRingingCall.isRinging()
- && !disableCall.equals("true")
- && (!mForegroundCall.getState().isAlive()
- || !mBackgroundCall.getState().isAlive()
- || (!isPhoneTypeGsm()
- && mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE));
-
- if (!ret) {
- log(String.format("canDial is false\n" +
- "((serviceState=%d) != ServiceState.STATE_POWER_OFF)::=%s\n" +
- "&& pendingMO == null::=%s\n" +
- "&& !ringingCall.isRinging()::=%s\n" +
- "&& !disableCall.equals(\"true\")::=%s\n" +
- "&& (!foregroundCall.getState().isAlive()::=%s\n" +
- " || foregroundCall.getState() == GsmCdmaCall.State.ACTIVE::=%s\n" +
- " ||!backgroundCall.getState().isAlive())::=%s)",
- serviceState,
- serviceState != ServiceState.STATE_POWER_OFF,
- mPendingMO == null,
- !mRingingCall.isRinging(),
- !disableCall.equals("true"),
- !mForegroundCall.getState().isAlive(),
- mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE,
- !mBackgroundCall.getState().isAlive()));
+ if (!mCi.getRadioState().isOn()) {
+ throw new CallStateException(CallStateException.ERROR_POWER_OFF,
+ "Modem not powered");
}
+ if (disableCall.equals("true")) {
+ throw new CallStateException(CallStateException.ERROR_CALLING_DISABLED,
+ "Calling disabled via ro.telephony.disable-call property");
+ }
+ if (mPendingMO != null) {
+ throw new CallStateException(CallStateException.ERROR_ALREADY_DIALING,
+ "A call is already dialing.");
+ }
+ if (mRingingCall.isRinging()) {
+ throw new CallStateException(CallStateException.ERROR_CALL_RINGING,
+ "Can't call while a call is ringing.");
+ }
+ if (isPhoneTypeGsm()
+ && mForegroundCall.getState().isAlive() && mBackgroundCall.getState().isAlive()) {
+ throw new CallStateException(CallStateException.ERROR_TOO_MANY_CALLS,
+ "There is already a foreground and background call.");
+ }
+ if (!isPhoneTypeGsm()
+ // Essentially foreground call state is one of:
+ // HOLDING, DIALING, ALERTING, INCOMING, WAITING
+ && mForegroundCall.getState().isAlive()
+ && mForegroundCall.getState() != GsmCdmaCall.State.ACTIVE
- return ret;
+ && mBackgroundCall.getState().isAlive()) {
+ throw new CallStateException(CallStateException.ERROR_TOO_MANY_CALLS,
+ "There is already a foreground and background call.");
+ }
}
public boolean canTransfer() {
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 66c05cf..58eb4e7 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -1118,7 +1118,7 @@
logi("IMS call failed with Exception: " + e.getMessage() + ". Falling back "
+ "to CS.");
} else {
- CallStateException ce = new CallStateException(e.getMessage());
+ CallStateException ce = new CallStateException(e.getError(), e.getMessage());
ce.setStackTrace(e.getStackTrace());
throw ce;
}
@@ -1747,9 +1747,15 @@
return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION);
}
+ private boolean isImsUtEnabledOverCdma() {
+ return isPhoneTypeCdmaLte()
+ && mImsPhone != null
+ && mImsPhone.isUtEnabled();
+ }
+
@Override
public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) {
- if (isPhoneTypeGsm()) {
+ if (isPhoneTypeGsm() || isImsUtEnabledOverCdma()) {
Phone imsPhone = mImsPhone;
if ((imsPhone != null)
&& ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
@@ -1770,7 +1776,7 @@
CommandsInterface.SERVICE_CLASS_VOICE, null, resp);
}
} else {
- loge("getCallForwardingOption: not possible in CDMA");
+ loge("getCallForwardingOption: not possible in CDMA without IMS");
}
}
@@ -1780,7 +1786,7 @@
String dialingNumber,
int timerSeconds,
Message onComplete) {
- if (isPhoneTypeGsm()) {
+ if (isPhoneTypeGsm() || isImsUtEnabledOverCdma()) {
Phone imsPhone = mImsPhone;
if ((imsPhone != null)
&& ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
@@ -1809,7 +1815,7 @@
resp);
}
} else {
- loge("setCallForwardingOption: not possible in CDMA");
+ loge("setCallForwardingOption: not possible in CDMA without IMS");
}
}
@@ -1902,7 +1908,7 @@
@Override
public void getCallWaiting(Message onComplete) {
- if (isPhoneTypeGsm()) {
+ if (isPhoneTypeGsm() || isImsUtEnabledOverCdma()) {
Phone imsPhone = mImsPhone;
if ((imsPhone != null)
&& ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
@@ -1921,7 +1927,7 @@
@Override
public void setCallWaiting(boolean enable, Message onComplete) {
- if (isPhoneTypeGsm()) {
+ if (isPhoneTypeGsm() || isImsUtEnabledOverCdma()) {
Phone imsPhone = mImsPhone;
if ((imsPhone != null)
&& ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
@@ -1932,7 +1938,7 @@
mCi.setCallWaiting(enable, CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
} else {
- loge("method setCallWaiting is NOT supported in CDMA!");
+ loge("method setCallWaiting is NOT supported in CDMA without IMS!");
}
}
@@ -2669,6 +2675,7 @@
simOperatorNumeric);
}
}
+ updateDataConnectionTracker();
}
}
}
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 9b01a0b..280ab9a 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -86,8 +86,8 @@
protected Phone mPhone;
final protected Context mContext;
final protected AppOpsManager mAppOps;
- final private UserManager mUserManager;
- protected SmsDispatchersController mDispatchersController;
+ @VisibleForTesting
+ public SmsDispatchersController mDispatchersController;
private final LocalLog mCellBroadcastLocalLog = new LocalLog(100);
@@ -147,7 +147,6 @@
mPhone = phone;
mContext = context;
mAppOps = appOps;
- mUserManager = userManager;
mDispatchersController = dispatchersController;
}
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index b779299..a115dba 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -61,6 +61,7 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.LocalLog;
+import android.util.Pair;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -99,13 +100,21 @@
*/
public abstract class InboundSmsHandler extends StateMachine {
protected static final boolean DBG = true;
- private static final boolean VDBG = false; // STOPSHIP if true, logs user data
+ protected static final boolean VDBG = false; // STOPSHIP if true, logs user data
/** Query projection for checking for duplicate message segments. */
- private static final String[] PDU_PROJECTION = {
- "pdu"
+ private static final String[] PDU_DELETED_FLAG_PROJECTION = {
+ "pdu",
+ "deleted"
};
+ /** Mapping from DB COLUMN to PDU_SEQUENCE_PORT PROJECTION index */
+ private static final Map<Integer, Integer> PDU_DELETED_FLAG_PROJECTION_INDEX_MAPPING =
+ new HashMap<Integer, Integer>() {{
+ put(PDU_COLUMN, 0);
+ put(DELETED_FLAG_COLUMN, 1);
+ }};
+
/** Query projection for combining concatenated message segments. */
private static final String[] PDU_SEQUENCE_PORT_PROJECTION = {
"pdu",
@@ -133,6 +142,7 @@
public static final int ID_COLUMN = 7;
public static final int MESSAGE_BODY_COLUMN = 8;
public static final int DISPLAY_ADDRESS_COLUMN = 9;
+ public static final int DELETED_FLAG_COLUMN = 10;
public static final String SELECT_BY_ID = "_id=?";
@@ -508,6 +518,8 @@
// Before moving to idle state, set wakelock timeout to WAKE_LOCK_TIMEOUT milliseconds
// to give any receivers time to take their own wake locks
setWakeLockTimeout(WAKELOCK_TIMEOUT);
+ mPhone.getIccSmsInterfaceManager().mDispatchersController.sendEmptyMessage(
+ SmsDispatchersController.EVENT_SMS_HANDLER_EXITING_WAITING_STATE);
}
@Override
@@ -1183,55 +1195,47 @@
}
/**
- * Function to check if message should be dropped because same message has already been
- * received. In certain cases it checks for similar messages instead of exact same (cases where
- * keeping both messages in db can cause ambiguity)
- * @return true if duplicate exists, false otherwise
+ * Function to detect and handle duplicate messages. If the received message should replace an
+ * existing message in the raw db, this function deletes the existing message. If an existing
+ * message takes priority (for eg, existing message has already been broadcast), then this new
+ * message should be dropped.
+ * @return true if the message represented by the passed in tracker should be dropped,
+ * false otherwise
*/
- private boolean duplicateExists(InboundSmsTracker tracker) throws SQLException {
- String address = tracker.getAddress();
- // convert to strings for query
- String refNumber = Integer.toString(tracker.getReferenceNumber());
- String count = Integer.toString(tracker.getMessageCount());
- // sequence numbers are 1-based except for CDMA WAP, which is 0-based
- int sequence = tracker.getSequenceNumber();
- String seqNumber = Integer.toString(sequence);
- String date = Long.toString(tracker.getTimestamp());
- String messageBody = tracker.getMessageBody();
- String where;
- if (tracker.getMessageCount() == 1) {
- where = "address=? AND reference_number=? AND count=? AND sequence=? AND " +
- "date=? AND message_body=?";
- } else {
- // for multi-part messages, deduping should also be done against undeleted
- // segments that can cause ambiguity when contacenating the segments, that is,
- // segments with same address, reference_number, count, sequence and message type.
- where = tracker.getQueryForMultiPartDuplicates();
- }
+ private boolean checkAndHandleDuplicate(InboundSmsTracker tracker) throws SQLException {
+ Pair<String, String[]> exactMatchQuery = tracker.getExactMatchDupDetectQuery();
Cursor cursor = null;
try {
// Check for duplicate message segments
- cursor = mResolver.query(sRawUri, PDU_PROJECTION, where,
- new String[]{address, refNumber, count, seqNumber, date, messageBody},
- null);
+ cursor = mResolver.query(sRawUri, PDU_DELETED_FLAG_PROJECTION, exactMatchQuery.first,
+ exactMatchQuery.second, null);
// moveToNext() returns false if no duplicates were found
if (cursor != null && cursor.moveToNext()) {
- loge("Discarding duplicate message segment, refNumber=" + refNumber
- + " seqNumber=" + seqNumber + " count=" + count);
- if (VDBG) {
- loge("address=" + address + " date=" + date + " messageBody=" +
- messageBody);
+ if (cursor.getCount() != 1) {
+ loge("Exact match query returned " + cursor.getCount() + " rows");
}
- String oldPduString = cursor.getString(PDU_COLUMN);
- byte[] pdu = tracker.getPdu();
- byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString);
- if (!Arrays.equals(oldPdu, tracker.getPdu())) {
- loge("Warning: dup message segment PDU of length " + pdu.length
- + " is different from existing PDU of length " + oldPdu.length);
+
+ // if the exact matching row is marked deleted, that means this message has already
+ // been received and processed, and can be discarded as dup
+ if (cursor.getInt(
+ PDU_DELETED_FLAG_PROJECTION_INDEX_MAPPING.get(DELETED_FLAG_COLUMN)) == 1) {
+ loge("Discarding duplicate message segment: " + tracker);
+ logDupPduMismatch(cursor, tracker);
+ return true; // reject message
+ } else {
+ // exact match duplicate is not marked deleted. If it is a multi-part segment,
+ // the code below for inexact match will take care of it. If it is a single
+ // part message, handle it here.
+ if (tracker.getMessageCount() == 1) {
+ // delete the old message segment permanently
+ deleteFromRawTable(exactMatchQuery.first, exactMatchQuery.second,
+ DELETE_PERMANENTLY);
+ loge("Replacing duplicate message: " + tracker);
+ logDupPduMismatch(cursor, tracker);
+ }
}
- return true; // reject message
}
} finally {
if (cursor != null) {
@@ -1239,9 +1243,49 @@
}
}
+ // The code above does an exact match. Multi-part message segments need an additional check
+ // on top of that: if there is a message segment that conflicts this new one (may not be an
+ // exact match), replace the old message segment with this one.
+ if (tracker.getMessageCount() > 1) {
+ Pair<String, String[]> inexactMatchQuery = tracker.getInexactMatchDupDetectQuery();
+ cursor = null;
+ try {
+ // Check for duplicate message segments
+ cursor = mResolver.query(sRawUri, PDU_DELETED_FLAG_PROJECTION,
+ inexactMatchQuery.first, inexactMatchQuery.second, null);
+
+ // moveToNext() returns false if no duplicates were found
+ if (cursor != null && cursor.moveToNext()) {
+ if (cursor.getCount() != 1) {
+ loge("Inexact match query returned " + cursor.getCount() + " rows");
+ }
+ // delete the old message segment permanently
+ deleteFromRawTable(inexactMatchQuery.first, inexactMatchQuery.second,
+ DELETE_PERMANENTLY);
+ loge("Replacing duplicate message segment: " + tracker);
+ logDupPduMismatch(cursor, tracker);
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
return false;
}
+ private void logDupPduMismatch(Cursor cursor, InboundSmsTracker tracker) {
+ String oldPduString = cursor.getString(
+ PDU_DELETED_FLAG_PROJECTION_INDEX_MAPPING.get(PDU_COLUMN));
+ byte[] pdu = tracker.getPdu();
+ byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString);
+ if (!Arrays.equals(oldPdu, tracker.getPdu())) {
+ loge("Warning: dup message PDU of length " + pdu.length
+ + " is different from existing PDU of length " + oldPdu.length);
+ }
+ }
+
/**
* Insert a message PDU into the raw table so we can acknowledge it immediately.
* If the device crashes before the broadcast to listeners completes, it will be delivered
@@ -1255,7 +1299,7 @@
private int addTrackerToRawTable(InboundSmsTracker tracker, boolean deDup) {
if (deDup) {
try {
- if (duplicateExists(tracker)) {
+ if (checkAndHandleDuplicate(tracker)) {
return Intents.RESULT_SMS_DUPLICATED; // reject message
}
} catch (SQLException e) {
diff --git a/src/java/com/android/internal/telephony/InboundSmsTracker.java b/src/java/com/android/internal/telephony/InboundSmsTracker.java
index 36c6996..50c84c8 100644
--- a/src/java/com/android/internal/telephony/InboundSmsTracker.java
+++ b/src/java/com/android/internal/telephony/InboundSmsTracker.java
@@ -18,6 +18,7 @@
import android.content.ContentValues;
import android.database.Cursor;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
@@ -88,19 +89,6 @@
+ "AND count=? AND (destination_port & "
+ DEST_PORT_FLAG_3GPP2_WAP_PDU + "=" + DEST_PORT_FLAG_3GPP2_WAP_PDU + ") AND deleted=0";
- @VisibleForTesting
- public static final String SELECT_BY_DUPLICATE_REFERENCE = "address=? AND "
- + "reference_number=? AND count=? AND sequence=? AND "
- + "((date=? AND message_body=?) OR deleted=0) AND (destination_port & "
- + DEST_PORT_FLAG_3GPP2_WAP_PDU + "=0)";
-
- @VisibleForTesting
- public static final String SELECT_BY_DUPLICATE_REFERENCE_3GPP2WAP = "address=? AND "
- + "reference_number=? " + "AND count=? AND sequence=? AND "
- + "((date=? AND message_body=?) OR deleted=0) AND "
- + "(destination_port & " + DEST_PORT_FLAG_3GPP2_WAP_PDU + "="
- + DEST_PORT_FLAG_3GPP2_WAP_PDU + ")";
-
/**
* Create a tracker for a single-part SMS.
*
@@ -284,13 +272,15 @@
builder.append(new Date(mTimestamp));
builder.append(" destPort=").append(mDestPort);
builder.append(" is3gpp2=").append(mIs3gpp2);
- if (mAddress != null) {
+ if (InboundSmsHandler.VDBG) {
builder.append(" address=").append(mAddress);
- builder.append(" display_originating_addr=").append(mDisplayAddress);
- builder.append(" refNumber=").append(mReferenceNumber);
- builder.append(" seqNumber=").append(mSequenceNumber);
- builder.append(" msgCount=").append(mMessageCount);
+ builder.append(" timestamp=").append(mTimestamp);
+ builder.append(" messageBody=").append(mMessageBody);
}
+ builder.append(" display_originating_addr=").append(mDisplayAddress);
+ builder.append(" refNumber=").append(mReferenceNumber);
+ builder.append(" seqNumber=").append(mSequenceNumber);
+ builder.append(" msgCount=").append(mMessageCount);
if (mDeleteWhere != null) {
builder.append(" deleteWhere(").append(mDeleteWhere);
builder.append(") deleteArgs=(").append(Arrays.toString(mDeleteWhereArgs));
@@ -324,9 +314,62 @@
return mIs3gpp2WapPdu ? SELECT_BY_REFERENCE_3GPP2WAP : SELECT_BY_REFERENCE;
}
- public String getQueryForMultiPartDuplicates() {
- return mIs3gpp2WapPdu ? SELECT_BY_DUPLICATE_REFERENCE_3GPP2WAP :
- SELECT_BY_DUPLICATE_REFERENCE;
+ /**
+ * Get the query to find the exact same message/message segment in the db.
+ * @return Pair with where as Pair.first and whereArgs as Pair.second
+ */
+ public Pair<String, String[]> getExactMatchDupDetectQuery() {
+ // convert to strings for query
+ String address = getAddress();
+ String refNumber = Integer.toString(getReferenceNumber());
+ String count = Integer.toString(getMessageCount());
+ String seqNumber = Integer.toString(getSequenceNumber());
+ String date = Long.toString(getTimestamp());
+ String messageBody = getMessageBody();
+
+ String where = "address=? AND reference_number=? AND count=? AND sequence=? AND "
+ + "date=? AND message_body=?";
+ where = addDestPortQuery(where);
+ String[] whereArgs = new String[]{address, refNumber, count, seqNumber, date, messageBody};
+
+ return new Pair<>(where, whereArgs);
+ }
+
+ /**
+ * The key differences here compared to exact match are:
+ * - this is applicable only for multi-part message segments
+ * - this does not match date or message_body
+ * - this matches deleted=0 (undeleted segments)
+ * The only difference as compared to getQueryForSegments() is that this checks for sequence as
+ * well.
+ * @return Pair with where as Pair.first and whereArgs as Pair.second
+ */
+ public Pair<String, String[]> getInexactMatchDupDetectQuery() {
+ if (getMessageCount() == 1) return null;
+
+ // convert to strings for query
+ String address = getAddress();
+ String refNumber = Integer.toString(getReferenceNumber());
+ String count = Integer.toString(getMessageCount());
+ String seqNumber = Integer.toString(getSequenceNumber());
+
+ String where = "address=? AND reference_number=? AND count=? AND sequence=? AND "
+ + "deleted=0";
+ where = addDestPortQuery(where);
+ String[] whereArgs = new String[]{address, refNumber, count, seqNumber};
+
+ return new Pair<>(where, whereArgs);
+ }
+
+ private String addDestPortQuery(String where) {
+ String whereDestPort;
+ if (mIs3gpp2WapPdu) {
+ whereDestPort = "destination_port & " + DEST_PORT_FLAG_3GPP2_WAP_PDU + "="
+ + DEST_PORT_FLAG_3GPP2_WAP_PDU;
+ } else {
+ whereDestPort = "destination_port & " + DEST_PORT_FLAG_3GPP2_WAP_PDU + "=0";
+ }
+ return where + " AND (" + whereDestPort + ")";
}
/**
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 8004f02..b5eb9a7 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -43,7 +43,6 @@
import com.android.internal.telephony.sip.SipPhone;
import com.android.internal.telephony.sip.SipPhoneFactory;
import com.android.internal.telephony.uicc.UiccController;
-import com.android.internal.telephony.uicc.UiccProfile;
import com.android.internal.telephony.util.NotificationChannelController;
import com.android.internal.util.IndentingPrintWriter;
@@ -246,7 +245,7 @@
int maxActivePhones = sPhoneConfigurationManager
.getNumberOfModemsWithSimultaneousDataConnections();
- sPhoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones,
+ sPhoneSwitcher = PhoneSwitcher.make(maxActivePhones, numPhones,
sContext, sc, Looper.myLooper(), tr, sCommandsInterfaces,
sPhones);
@@ -458,17 +457,6 @@
sTelephonyNetworkFactories[i].dump(fd, pw, args);
pw.flush();
- pw.println("++++++++++++++++++++++++++++++++");
-
- try {
- UiccProfile uiccProfile = (UiccProfile) phone.getIccCard();
- if (uiccProfile != null) {
- uiccProfile.dump(fd, pw, args);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- pw.flush();
pw.decreaseIndent();
pw.println("++++++++++++++++++++++++++++++++");
}
diff --git a/src/java/com/android/internal/telephony/PhoneSwitcher.java b/src/java/com/android/internal/telephony/PhoneSwitcher.java
index 6d0e536..bc8fdb5 100644
--- a/src/java/com/android/internal/telephony/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/PhoneSwitcher.java
@@ -39,6 +39,7 @@
import android.telephony.PhoneCapability;
import android.telephony.PhoneStateListener;
import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.LocalLog;
@@ -77,17 +78,53 @@
private final PhoneStateListener mPhoneStateListener;
private int mMaxActivePhones;
- private int mDefaultDataSubscription;
+ private static PhoneSwitcher sPhoneSwitcher = null;
- private final static int EVENT_DEFAULT_SUBSCRIPTION_CHANGED = 101;
- private final static int EVENT_SUBSCRIPTION_CHANGED = 102;
- private final static int EVENT_REQUEST_NETWORK = 103;
- private final static int EVENT_RELEASE_NETWORK = 104;
- private final static int EVENT_EMERGENCY_TOGGLE = 105;
- private final static int EVENT_RESEND_DATA_ALLOWED = 106;
+ // Default subscription ID from user setting.
+ private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ // If mPreferredDataSubId is an active subscription, it overrides
+ // mDefaultDataSubId and decides:
+ // 1. In modem layer, which subscription is preferred to have data traffic on.
+ // 2. In TelephonyNetworkFactory, which subscription will apply default network requets, which
+ // are requests without specifying a subId.
+ private int mPreferredDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ @VisibleForTesting
+ // Corresponding phoneId after considerting mPreferredDataSubId and mDefaultDataSubId above.
+ protected int mPreferredDataPhoneId = SubscriptionManager.INVALID_PHONE_INDEX;
+
+ private static final int EVENT_DEFAULT_SUBSCRIPTION_CHANGED = 101;
+ private static final int EVENT_SUBSCRIPTION_CHANGED = 102;
+ private static final int EVENT_REQUEST_NETWORK = 103;
+ private static final int EVENT_RELEASE_NETWORK = 104;
+ private static final int EVENT_EMERGENCY_TOGGLE = 105;
+ private static final int EVENT_RESEND_DATA_ALLOWED = 106;
+ private static final int EVENT_PREFERRED_SUBSCRIPTION_CHANGED = 107;
private final static int MAX_LOCAL_LOG_LINES = 30;
+ /**
+ * Method to get singleton instance.
+ */
+ public static PhoneSwitcher getInstance() {
+ return sPhoneSwitcher;
+ }
+
+ /**
+ * Method to create singleton instance.
+ */
+ public static PhoneSwitcher make(int maxActivePhones, int numPhones, Context context,
+ SubscriptionController subscriptionController, Looper looper, ITelephonyRegistry tr,
+ CommandsInterface[] cis, Phone[] phones) {
+ if (sPhoneSwitcher == null) {
+ sPhoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones, context,
+ subscriptionController, looper, tr, cis, phones);
+ }
+
+ return sPhoneSwitcher;
+ }
+
@VisibleForTesting
public PhoneSwitcher(Looper looper) {
super(looper);
@@ -108,6 +145,7 @@
};
}
+ @VisibleForTesting
public PhoneSwitcher(int maxActivePhones, int numPhones, Context context,
SubscriptionController subscriptionController, Looper looper, ITelephonyRegistry tr,
CommandsInterface[] cis, Phone[] phones) {
@@ -221,6 +259,10 @@
onResendDataAllowed(msg);
break;
}
+ case EVENT_PREFERRED_SUBSCRIPTION_CHANGED: {
+ onEvaluate(REQUESTS_UNCHANGED, "preferredDataSubIdChanged");
+ break;
+ }
}
}
@@ -292,14 +334,17 @@
return;
}
+ // Check if preferred slotId is changed.
boolean diffDetected = requestsChanged;
+
+ // Check if user setting of default data sub is changed.
final int dataSub = mSubscriptionController.getDefaultDataSubId();
- if (dataSub != mDefaultDataSubscription) {
- sb.append(" default ").append(mDefaultDataSubscription).append("->").append(dataSub);
- mDefaultDataSubscription = dataSub;
- diffDetected = true;
+ if (dataSub != mDefaultDataSubId) {
+ sb.append(" default ").append(mDefaultDataSubId).append("->").append(dataSub);
+ mDefaultDataSubId = dataSub;
}
+ // Check if phoneId to subId mapping is changed.
for (int i = 0; i < mNumPhones; i++) {
int sub = mSubscriptionController.getSubIdUsingPhoneId(i);
if (sub != mPhoneSubscriptions[i]) {
@@ -310,6 +355,15 @@
}
}
+ // Check if phoneId for preferred data is changed.
+ int oldPreferredDataPhoneId = mPreferredDataPhoneId;
+ updatePhoneIdForDefaultNetworkRequests();
+ if (oldPreferredDataPhoneId != mPreferredDataPhoneId) {
+ sb.append(" preferred phoneId ").append(oldPreferredDataPhoneId)
+ .append("->").append(mPreferredDataPhoneId);
+ diffDetected = true;
+ }
+
if (diffDetected) {
log("evaluating due to " + sb.toString());
@@ -324,7 +378,8 @@
}
if (VDBG) {
- log("default subId = " + mDefaultDataSubscription);
+ log("default subId = " + mDefaultDataSubId);
+ log("preferred subId = " + mPreferredDataSubId);
for (int i = 0; i < mNumPhones; i++) {
log(" phone[" + i + "] using sub[" + mPhoneSubscriptions[i] + "]");
}
@@ -399,17 +454,19 @@
if (mMaxActivePhones != newMaxActivePhones) {
mMaxActivePhones = newMaxActivePhones;
log("Max active phones changed to " + mMaxActivePhones);
- onEvaluate(true, "phoneCfgChanged");
+ onEvaluate(REQUESTS_UNCHANGED, "phoneCfgChanged");
}
}
private int phoneIdForRequest(NetworkRequest netRequest) {
NetworkSpecifier specifier = netRequest.networkCapabilities.getNetworkSpecifier();
+ if (specifier == null) {
+ return mPreferredDataPhoneId;
+ }
+
int subId;
- if (specifier == null) {
- subId = mDefaultDataSubscription;
- } else if (specifier instanceof StringNetworkSpecifier) {
+ if (specifier instanceof StringNetworkSpecifier) {
try {
subId = Integer.parseInt(((StringNetworkSpecifier) specifier).specifier);
} catch (NumberFormatException e) {
@@ -433,6 +490,39 @@
return phoneId;
}
+ private int getSubIdForDefaultNetworkRequests() {
+ if (mSubscriptionController.isActiveSubId(mPreferredDataSubId)) {
+ return mPreferredDataSubId;
+ } else {
+ return mDefaultDataSubId;
+ }
+ }
+
+ // This updates mPreferredDataPhoneId which decides which phone should
+ // handle default network requests.
+ private void updatePhoneIdForDefaultNetworkRequests() {
+ int subId = getSubIdForDefaultNetworkRequests();
+ int phoneId = SubscriptionManager.INVALID_PHONE_INDEX;
+
+ if (SubscriptionManager.isUsableSubIdValue(subId)) {
+ for (int i = 0; i < mNumPhones; i++) {
+ if (mPhoneSubscriptions[i] == subId) {
+ phoneId = i;
+ break;
+ }
+ }
+ }
+
+ mPreferredDataPhoneId = phoneId;
+ }
+
+ /**
+ * Returns whether phone should handle default network requests.
+ */
+ public boolean isActiveForDefaultRequests(int phoneId) {
+ return isPhoneActive(phoneId) && phoneId == mPreferredDataPhoneId;
+ }
+
public boolean isPhoneActive(int phoneId) {
validatePhoneId(phoneId);
return mPhoneStates[phoneId].active;
@@ -456,6 +546,19 @@
}
}
+ /**
+ * Set a subscription as preferred data subscription.
+ * See {@link SubscriptionManager#setPreferredData(int)} for more details.
+ */
+ public void setPreferredData(int subId) {
+ if (mPreferredDataSubId != subId) {
+ log("setPreferredData subId changed to " + subId);
+ mPreferredDataSubId = subId;
+ Message msg = PhoneSwitcher.this.obtainMessage(EVENT_PREFERRED_SUBSCRIPTION_CHANGED);
+ msg.sendToTarget();
+ }
+ }
+
private void log(String l) {
Rlog.d(LOG_TAG, l);
mLocalLog.log(l);
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index 0fc5279..dda8b60 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -1313,37 +1313,36 @@
android.hardware.radio.V1_1.KeepaliveStatus keepaliveStatus) {
RILRequest rr = mRil.processResponse(responseInfo);
-
- if (rr == null) {
- return;
- }
+ if (rr == null) return;
KeepaliveStatus ret = null;
-
- switch(responseInfo.error) {
- case RadioError.NONE:
- int convertedStatus = convertHalKeepaliveStatusCode(keepaliveStatus.code);
- if (convertedStatus < 0) {
+ try {
+ switch(responseInfo.error) {
+ case RadioError.NONE:
+ int convertedStatus = convertHalKeepaliveStatusCode(keepaliveStatus.code);
+ if (convertedStatus < 0) {
+ ret = new KeepaliveStatus(KeepaliveStatus.ERROR_UNSUPPORTED);
+ } 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);
- } 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);
- break;
- case RadioError.NO_RESOURCES:
- ret = new KeepaliveStatus(KeepaliveStatus.ERROR_NO_RESOURCES);
- break;
- default:
- ret = new KeepaliveStatus(KeepaliveStatus.ERROR_UNKNOWN);
- break;
+ break;
+ case RadioError.NO_RESOURCES:
+ ret = new KeepaliveStatus(KeepaliveStatus.ERROR_NO_RESOURCES);
+ break;
+ default:
+ ret = new KeepaliveStatus(KeepaliveStatus.ERROR_UNKNOWN);
+ break;
+ }
+ } finally {
+ // If responseInfo.error != NONE, the processResponseDone sends the response message.
+ mRil.processResponseDone(rr, responseInfo, ret);
}
- // If responseInfo.error != NONE, the processResponseDone sends the response message.
- mRil.processResponseDone(rr, responseInfo, ret);
}
/**
@@ -1351,16 +1350,16 @@
*/
public void stopKeepaliveResponse(RadioResponseInfo responseInfo) {
RILRequest rr = mRil.processResponse(responseInfo);
+ if (rr == null) return;
- if (rr == null) {
- return;
- }
-
- if (responseInfo.error == RadioError.NONE) {
- sendMessageResponse(rr.mResult, null);
+ try {
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, null);
+ } else {
+ //TODO: Error code translation
+ }
+ } finally {
mRil.processResponseDone(rr, responseInfo, null);
- } else {
- //TODO: Error code translation
}
}
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 433766c..b2f5fc4 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -4039,16 +4039,16 @@
mPhone.mCT.mBackgroundCall.hangupIfAlive();
mPhone.mCT.mForegroundCall.hangupIfAlive();
}
- dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
- if (dds != mPhone.getSubId()
- && !ProxyController.getInstance().isDataDisconnected(dds)) {
- if (DBG) log("Data is active on DDS. Wait for all data disconnect");
- // Data is not disconnected on DDS. Wait for the data disconnect complete
+ if (!ProxyController.getInstance().isDataDisconnected(mPhone.getSubId())) {
+ if (DBG) log("Wait for all data disconnect");
+ // Data is not disconnected. Wait for the data disconnect complete
// before sending the RADIO_POWER off.
- ProxyController.getInstance().registerForAllDataDisconnected(dds, this,
- EVENT_ALL_DATA_DISCONNECTED, null);
+ ProxyController.getInstance().registerForAllDataDisconnected(
+ mPhone.getSubId(), this, EVENT_ALL_DATA_DISCONNECTED, null);
mPendingRadioPowerOffAfterDataOff = true;
}
+ dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
+
Message msg = Message.obtain(this);
msg.what = EVENT_SET_RADIO_POWER_OFF;
msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
diff --git a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
index 4fb02f0..32edb9c 100644
--- a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
+++ b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
@@ -37,7 +37,7 @@
/**
* Called when the credential-encrypted storage is unlocked, collecting all acknowledged messages
- * and deleting any partial message segments older than 30 days. Called from a worker thread to
+ * and deleting any partial message segments older than 7 days. Called from a worker thread to
* avoid delaying phone app startup. The last step is to broadcast the first pending message from
* the main thread, then the remaining pending messages will be broadcast after the previous
* ordered broadcast completes.
@@ -46,8 +46,8 @@
private static final String TAG = "SmsBroadcastUndelivered";
private static final boolean DBG = InboundSmsHandler.DBG;
- /** Delete any partial message segments older than 30 days. */
- static final long DEFAULT_PARTIAL_SEGMENT_EXPIRE_AGE = (long) (60 * 60 * 1000) * 24 * 30;
+ /** Delete any partial message segments older than 7 days. */
+ static final long DEFAULT_PARTIAL_SEGMENT_EXPIRE_AGE = (long) (60 * 60 * 1000) * 24 * 7;
/**
* Query projection for dispatching pending messages at boot time.
@@ -99,7 +99,8 @@
@Override
public void run() {
- scanRawTable(context);
+ scanRawTable(context, mCdmaInboundSmsHandler, mGsmInboundSmsHandler,
+ System.currentTimeMillis() - getUndeliveredSmsExpirationTime(context));
InboundSmsHandler.cancelNewMessageNotification(context);
}
}
@@ -142,18 +143,19 @@
/**
* Scan the raw table for complete SMS messages to broadcast, and old PDUs to delete.
*/
- private void scanRawTable(Context context) {
+ static void scanRawTable(Context context, CdmaInboundSmsHandler cdmaInboundSmsHandler,
+ GsmInboundSmsHandler gsmInboundSmsHandler, long oldMessageTimestamp) {
if (DBG) Rlog.d(TAG, "scanning raw table for undelivered messages");
long startTime = System.nanoTime();
+ ContentResolver contentResolver = context.getContentResolver();
HashMap<SmsReferenceKey, Integer> multiPartReceivedCount =
new HashMap<SmsReferenceKey, Integer>(4);
HashSet<SmsReferenceKey> oldMultiPartMessages = new HashSet<SmsReferenceKey>(4);
Cursor cursor = null;
try {
// query only non-deleted ones
- cursor = mResolver.query(InboundSmsHandler.sRawUri, PDU_PENDING_MESSAGE_PROJECTION,
- "deleted = 0", null,
- null);
+ cursor = contentResolver.query(InboundSmsHandler.sRawUri,
+ PDU_PENDING_MESSAGE_PROJECTION, "deleted = 0", null, null);
if (cursor == null) {
Rlog.e(TAG, "error getting pending message cursor");
return;
@@ -172,16 +174,15 @@
if (tracker.getMessageCount() == 1) {
// deliver single-part message
- broadcastSms(tracker);
+ broadcastSms(tracker, cdmaInboundSmsHandler, gsmInboundSmsHandler);
} else {
SmsReferenceKey reference = new SmsReferenceKey(tracker);
Integer receivedCount = multiPartReceivedCount.get(reference);
if (receivedCount == null) {
multiPartReceivedCount.put(reference, 1); // first segment seen
- long expirationTime = getUndeliveredSmsExpirationTime(context);
- if (tracker.getTimestamp() <
- (System.currentTimeMillis() - expirationTime)) {
- // older than 30 days; delete if we don't find all the segments
+ if (tracker.getTimestamp() < oldMessageTimestamp) {
+ // older than oldMessageTimestamp; delete if we don't find all the
+ // segments
oldMultiPartMessages.add(reference);
}
} else {
@@ -190,7 +191,7 @@
// looks like we've got all the pieces; send a single tracker
// to state machine which will find the other pieces to broadcast
if (DBG) Rlog.d(TAG, "found complete multi-part message");
- broadcastSms(tracker);
+ broadcastSms(tracker, cdmaInboundSmsHandler, gsmInboundSmsHandler);
// don't delete this old message until after we broadcast it
oldMultiPartMessages.remove(reference);
} else {
@@ -202,7 +203,7 @@
// Delete old incomplete message segments
for (SmsReferenceKey message : oldMultiPartMessages) {
// delete permanently
- int rows = mResolver.delete(InboundSmsHandler.sRawUriPermanentDelete,
+ int rows = contentResolver.delete(InboundSmsHandler.sRawUriPermanentDelete,
message.getDeleteWhere(), message.getDeleteWhereArgs());
if (rows == 0) {
Rlog.e(TAG, "No rows were deleted from raw table!");
@@ -225,12 +226,14 @@
/**
* Send tracker to appropriate (3GPP or 3GPP2) inbound SMS handler for broadcast.
*/
- private void broadcastSms(InboundSmsTracker tracker) {
+ private static void broadcastSms(InboundSmsTracker tracker,
+ CdmaInboundSmsHandler cdmaInboundSmsHandler,
+ GsmInboundSmsHandler gsmInboundSmsHandler) {
InboundSmsHandler handler;
if (tracker.is3gpp2()) {
- handler = mCdmaInboundSmsHandler;
+ handler = cdmaInboundSmsHandler;
} else {
- handler = mGsmInboundSmsHandler;
+ handler = gsmInboundSmsHandler;
}
if (handler != null) {
handler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS, tracker);
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index ff01d5b..d858cbe 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -22,15 +22,19 @@
import android.app.Activity;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
+import android.os.UserManager;
import android.provider.Telephony.Sms;
import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
+import android.telephony.ServiceState;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.util.Pair;
@@ -51,6 +55,7 @@
*/
public class SmsDispatchersController extends Handler {
private static final String TAG = "SmsDispatchersController";
+ private static final boolean VDBG = false; // STOPSHIP if true
/** Radio is ON */
private static final int EVENT_RADIO_ON = 11;
@@ -61,6 +66,29 @@
/** Callback from RIL_REQUEST_IMS_REGISTRATION_STATE */
private static final int EVENT_IMS_STATE_DONE = 13;
+ /** Service state changed */
+ private static final int EVENT_SERVICE_STATE_CHANGED = 14;
+
+ /** Purge old message segments */
+ private static final int EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY = 15;
+
+ /** User unlocked the device */
+ private static final int EVENT_USER_UNLOCKED = 16;
+
+ /** InboundSmsHandler exited WaitingState */
+ protected static final int EVENT_SMS_HANDLER_EXITING_WAITING_STATE = 17;
+
+ /** Delete any partial message segments after being IN_SERVICE for 1 day. */
+ private static final long PARTIAL_SEGMENT_WAIT_DURATION = (long) (60 * 60 * 1000) * 24;
+ /** Constant for invalid time */
+ private static final long INVALID_TIME = -1;
+ /** Time at which last IN_SERVICE event was received */
+ private long mLastInServiceTime = INVALID_TIME;
+ /** Current IN_SERVICE duration */
+ private long mCurrentWaitElapsedDuration = 0;
+ /** Time at which the current PARTIAL_SEGMENT_WAIT_DURATION timer was started */
+ private long mCurrentWaitStartTime = INVALID_TIME;
+
private SMSDispatcher mCdmaDispatcher;
private SMSDispatcher mGsmDispatcher;
private ImsSmsDispatcher mImsSmsDispatcher;
@@ -102,11 +130,39 @@
mCi.registerForOn(this, EVENT_RADIO_ON, null);
mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
+
+ UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ if (userManager.isUserUnlocked()) {
+ if (VDBG) {
+ logd("SmsDispatchersController: user unlocked; registering for service"
+ + "state changed");
+ }
+ mPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
+ resetPartialSegmentWaitTimer();
+ } else {
+ if (VDBG) {
+ logd("SmsDispatchersController: user locked; waiting for USER_UNLOCKED");
+ }
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
+ mContext.registerReceiver(mBroadcastReceiver, userFilter);
+ }
}
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ Rlog.d(TAG, "Received broadcast " + intent.getAction());
+ if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+ sendMessage(obtainMessage(EVENT_USER_UNLOCKED));
+ }
+ }
+ };
+
public void dispose() {
mCi.unregisterForOn(this);
mCi.unregisterForImsNetworkStateChanged(this);
+ mPhone.unregisterForServiceStateChanged(this);
mGsmDispatcher.dispose();
mCdmaDispatcher.dispose();
mGsmInboundSmsHandler.dispose();
@@ -139,6 +195,23 @@
}
break;
+ case EVENT_SERVICE_STATE_CHANGED:
+ case EVENT_SMS_HANDLER_EXITING_WAITING_STATE:
+ reevaluateTimerStatus();
+ break;
+
+ case EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY:
+ handlePartialSegmentTimerExpiry((Long) msg.obj);
+ break;
+
+ case EVENT_USER_UNLOCKED:
+ if (VDBG) {
+ logd("handleMessage: EVENT_USER_UNLOCKED");
+ }
+ mPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
+ resetPartialSegmentWaitTimer();
+ break;
+
default:
if (isCdmaMo()) {
mCdmaDispatcher.handleMessage(msg);
@@ -148,6 +221,118 @@
}
}
+ private void reevaluateTimerStatus() {
+ long currentTime = System.currentTimeMillis();
+
+ // Remove unhandled timer expiry message. A new message will be posted if needed.
+ removeMessages(EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY);
+ // Update timer duration elapsed time (add time since last IN_SERVICE to now).
+ // This is needed for IN_SERVICE as well as OUT_OF_SERVICE because same events can be
+ // received back to back
+ if (mLastInServiceTime != INVALID_TIME) {
+ mCurrentWaitElapsedDuration += (currentTime - mLastInServiceTime);
+ }
+
+ if (VDBG) {
+ logd("reevaluateTimerStatus: currentTime: " + currentTime
+ + " mCurrentWaitElapsedDuration: " + mCurrentWaitElapsedDuration);
+ }
+
+ if (mCurrentWaitElapsedDuration > PARTIAL_SEGMENT_WAIT_DURATION) {
+ // handle this event as timer expiry
+ handlePartialSegmentTimerExpiry(mCurrentWaitStartTime);
+ } else {
+ if (isInService()) {
+ handleInService(currentTime);
+ } else {
+ handleOutOfService(currentTime);
+ }
+ }
+ }
+
+ private void handleInService(long currentTime) {
+ if (VDBG) {
+ logd("handleInService: timer expiry in "
+ + (PARTIAL_SEGMENT_WAIT_DURATION - mCurrentWaitElapsedDuration) + "ms");
+ }
+
+ // initialize mCurrentWaitStartTime if needed
+ if (mCurrentWaitStartTime == INVALID_TIME) mCurrentWaitStartTime = currentTime;
+
+ // Post a message for timer expiry time. mCurrentWaitElapsedDuration is the duration already
+ // elapsed from the timer.
+ sendMessageDelayed(
+ obtainMessage(EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY, mCurrentWaitStartTime),
+ PARTIAL_SEGMENT_WAIT_DURATION - mCurrentWaitElapsedDuration);
+
+ // update mLastInServiceTime as the current time
+ mLastInServiceTime = currentTime;
+ }
+
+ private void handleOutOfService(long currentTime) {
+ if (VDBG) {
+ logd("handleOutOfService: currentTime: " + currentTime
+ + " mCurrentWaitElapsedDuration: " + mCurrentWaitElapsedDuration);
+ }
+
+ // mLastInServiceTime is not relevant now since state is OUT_OF_SERVICE; set it to INVALID
+ mLastInServiceTime = INVALID_TIME;
+ }
+
+ private void handlePartialSegmentTimerExpiry(long waitTimerStart) {
+ if (mGsmInboundSmsHandler.getCurrentState().getName().equals("WaitingState")
+ || mCdmaInboundSmsHandler.getCurrentState().getName().equals("WaitingState")) {
+ logd("handlePartialSegmentTimerExpiry: ignoring timer expiry as InboundSmsHandler is"
+ + " in WaitingState");
+ return;
+ }
+
+ if (VDBG) {
+ logd("handlePartialSegmentTimerExpiry: calling scanRawTable()");
+ }
+ // Timer expired. This indicates that device has been in service for
+ // PARTIAL_SEGMENT_WAIT_DURATION since waitTimerStart. Delete orphaned message segments
+ // older than waitTimerStart.
+ SmsBroadcastUndelivered.scanRawTable(mContext, mCdmaInboundSmsHandler,
+ mGsmInboundSmsHandler, waitTimerStart);
+ if (VDBG) {
+ logd("handlePartialSegmentTimerExpiry: scanRawTable() done");
+ }
+
+ resetPartialSegmentWaitTimer();
+ }
+
+ private void resetPartialSegmentWaitTimer() {
+ long currentTime = System.currentTimeMillis();
+
+ removeMessages(EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY);
+ if (isInService()) {
+ if (VDBG) {
+ logd("resetPartialSegmentWaitTimer: currentTime: " + currentTime
+ + " IN_SERVICE");
+ }
+ mCurrentWaitStartTime = currentTime;
+ mLastInServiceTime = currentTime;
+ sendMessageDelayed(
+ obtainMessage(EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY, mCurrentWaitStartTime),
+ PARTIAL_SEGMENT_WAIT_DURATION);
+ } else {
+ if (VDBG) {
+ logd("resetPartialSegmentWaitTimer: currentTime: " + currentTime
+ + " not IN_SERVICE");
+ }
+ mCurrentWaitStartTime = INVALID_TIME;
+ mLastInServiceTime = INVALID_TIME;
+ }
+
+ mCurrentWaitElapsedDuration = 0;
+ }
+
+ private boolean isInService() {
+ ServiceState serviceState = mPhone.getServiceState();
+ return serviceState != null && serviceState.getState() == ServiceState.STATE_IN_SERVICE;
+ }
+
private void setImsSmsFormat(int format) {
switch (format) {
case PhoneConstants.PHONE_TYPE_GSM:
@@ -625,4 +810,8 @@
mGsmInboundSmsHandler.dump(fd, pw, args);
mCdmaInboundSmsHandler.dump(fd, pw, args);
}
+
+ private void logd(String msg) {
+ Rlog.d(TAG, msg);
+ }
}
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 9766cf0..8c08420 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -168,6 +168,7 @@
private int[] colorArr;
private long mLastISubServiceRegTime;
+ private int mPreferredDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
public static SubscriptionController init(Phone phone) {
synchronized (SubscriptionController.class) {
@@ -940,9 +941,8 @@
}
if (value.size() > 0) {
- resolver.update(SubscriptionManager.CONTENT_URI, value,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
- "=" + Long.toString(subId), null);
+ resolver.update(SubscriptionManager.getUriForSubscriptionId(subId),
+ value, null, null);
// Refresh the Cache of Active Subscription Info List
refreshCachedActiveSubscriptionInfoList();
@@ -1035,9 +1035,8 @@
ContentValues value = new ContentValues();
value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
- resolver.update(SubscriptionManager.CONTENT_URI, value,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
- "=" + Long.toString(subId), null);
+ resolver.update(SubscriptionManager.getUriForSubscriptionId(subId), value,
+ null, null);
// Refresh the Cache of Active Subscription Info List
refreshCachedActiveSubscriptionInfoList();
@@ -1154,9 +1153,8 @@
ContentValues value = new ContentValues(1);
value.put(SubscriptionManager.CARRIER_NAME, text);
- int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
- value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
- Long.toString(subId), null);
+ int result = mContext.getContentResolver().update(
+ SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
// Refresh the Cache of Active Subscription Info List
refreshCachedActiveSubscriptionInfoList();
@@ -1189,9 +1187,8 @@
value.put(SubscriptionManager.COLOR, tint);
if (DBG) logd("[setIconTint]- tint:" + tint + " set");
- int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
- value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
- Long.toString(subId), null);
+ int result = mContext.getContentResolver().update(
+ SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
// Refresh the Cache of Active Subscription Info List
refreshCachedActiveSubscriptionInfoList();
@@ -1253,9 +1250,8 @@
// to the eSIM itself. Currently it will be blown away the next time the subscription
// list is updated.
- int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
- value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
- Long.toString(subId), null);
+ int result = mContext.getContentResolver().update(
+ SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
// Refresh the Cache of Active Subscription Info List
refreshCachedActiveSubscriptionInfoList();
@@ -1299,9 +1295,8 @@
// that was removed as there doesn't seem to be a reason for that. If it is added
// back, watch out for deadlocks.
- result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
- + "=" + Long.toString(subId), null);
+ result = mContext.getContentResolver().update(
+ SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
// Refresh the Cache of Active Subscription Info List
refreshCachedActiveSubscriptionInfoList();
@@ -1339,9 +1334,8 @@
value.put(SubscriptionManager.DATA_ROAMING, roaming);
if (DBG) logd("[setDataRoaming]- roaming:" + roaming + " set");
- int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
- value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
- Long.toString(subId), null);
+ int result = mContext.getContentResolver().update(
+ SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
// Refresh the Cache of Active Subscription Info List
refreshCachedActiveSubscriptionInfoList();
@@ -1378,8 +1372,8 @@
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);
+ int result = mContext.getContentResolver().update(
+ SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
// Refresh the Cache of Active Subscription Info List
refreshCachedActiveSubscriptionInfoList();
@@ -2065,9 +2059,8 @@
break;
}
- return resolver.update(SubscriptionManager.CONTENT_URI, value,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
- "=" + Integer.toString(subId), null);
+ return resolver.update(SubscriptionManager.getUriForSubscriptionId(subId),
+ value, null, null);
}
/**
@@ -2301,8 +2294,23 @@
@Override
public int setPreferredData(int slotId) {
- // TODO: send to phone switcher.
- return 0;
+ enforceModifyPhoneState("setPreferredData");
+ final long token = Binder.clearCallingIdentity();
+
+ try {
+ // TODO: make this API takes in subId directly.
+ int subId = getSubIdUsingPhoneId(slotId);
+
+ if (mPreferredDataSubId != subId) {
+ mPreferredDataSubId = subId;
+ PhoneSwitcher.getInstance().setPreferredData(subId);
+ //TODO: notifyPreferredDataSubIdChanged();
+ }
+
+ return 0;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index 4a08445..1ee96a8 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -564,9 +564,8 @@
ContentValues value = new ContentValues(1);
value.put(SubscriptionManager.SIM_SLOT_INDEX,
SubscriptionManager.INVALID_SIM_SLOT_INDEX);
- contentResolver.update(SubscriptionManager.CONTENT_URI, value,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
- + Integer.toString(oldSubInfo.get(0).getSubscriptionId()), null);
+ contentResolver.update(SubscriptionManager.getUriForSubscriptionId(
+ oldSubInfo.get(0).getSubscriptionId()), value, null, null);
// refresh Cached Active Subscription Info List
SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
@@ -628,9 +627,8 @@
if (msisdn != null) {
ContentValues value = new ContentValues(1);
value.put(SubscriptionManager.NUMBER, msisdn);
- contentResolver.update(SubscriptionManager.CONTENT_URI, value,
- SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
- + Integer.toString(temp.getSubscriptionId()), null);
+ contentResolver.update(SubscriptionManager.getUriForSubscriptionId(
+ temp.getSubscriptionId()), value, null, null);
// refresh Cached Active Subscription Info List
SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
diff --git a/src/java/com/android/internal/telephony/TelephonyTester.java b/src/java/com/android/internal/telephony/TelephonyTester.java
index 9b4aa2f..bb7ac00 100644
--- a/src/java/com/android/internal/telephony/TelephonyTester.java
+++ b/src/java/com/android/internal/telephony/TelephonyTester.java
@@ -409,7 +409,7 @@
if (extras == null) {
extras = new Bundle();
}
- extras.putBoolean(ImsCallProfile.EXTRA_E_CALL, true);
+ extras.putBoolean(ImsCallProfile.EXTRA_EMERGENCY_CALL, true);
callProfile.mCallExtras = extras;
imsCall.getImsCallSessionListenerProxy().callSessionUpdated(imsCall.getSession(),
callProfile);
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index a36a8c5..b304e5b 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -39,7 +39,7 @@
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.telephony.AccessNetworkConstants;
+import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
@@ -461,8 +461,7 @@
//***** Constructor (NOTE: uses dcc.getHandler() as its Handler)
private DataConnection(Phone phone, String name, int id,
DcTracker dct, DataServiceManager dataServiceManager,
- DcTesterFailBringUpAll failBringUpAll,
- DcController dcc) {
+ DcTesterFailBringUpAll failBringUpAll, DcController dcc) {
super(name, dcc.getHandler());
setLogRecSize(300);
setLogOnlyTransitions(true);
@@ -1664,10 +1663,12 @@
mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
"DcNetworkAgent", mNetworkInfo, getNetworkCapabilities(), mLinkProperties,
50, misc);
- mPhone.mCi.registerForNattKeepaliveStatus(
- getHandler(), DataConnection.EVENT_KEEPALIVE_STATUS, null);
- mPhone.mCi.registerForLceInfo(
- getHandler(), DataConnection.EVENT_LINK_CAPACITY_CHANGED, null);
+ if (mDataServiceManager.getTransportType() == TransportType.WWAN) {
+ mPhone.mCi.registerForNattKeepaliveStatus(
+ getHandler(), DataConnection.EVENT_KEEPALIVE_STATUS, null);
+ mPhone.mCi.registerForLceInfo(
+ getHandler(), DataConnection.EVENT_LINK_CAPACITY_CHANGED, null);
+ }
}
@Override
@@ -1686,8 +1687,11 @@
mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
reason, mNetworkInfo.getExtraInfo());
- mPhone.mCi.unregisterForNattKeepaliveStatus(getHandler());
- mPhone.mCi.unregisterForLceInfo(getHandler());
+
+ if (mDataServiceManager.getTransportType() == TransportType.WWAN) {
+ mPhone.mCi.unregisterForNattKeepaliveStatus(getHandler());
+ mPhone.mCi.unregisterForLceInfo(getHandler());
+ }
if (mNetworkAgent != null) {
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
mNetworkAgent = null;
@@ -1809,8 +1813,7 @@
KeepalivePacketData pkt = (KeepalivePacketData) msg.obj;
int slotId = msg.arg1;
int intervalMillis = msg.arg2 * 1000;
- if (mDataServiceManager.getTransportType()
- == AccessNetworkConstants.TransportType.WWAN) {
+ if (mDataServiceManager.getTransportType() == TransportType.WWAN) {
mPhone.mCi.startNattKeepalive(
DataConnection.this.mCid, pkt, intervalMillis,
DataConnection.this.obtainMessage(
@@ -2081,8 +2084,10 @@
@Override
protected void pollLceData() {
- if(mPhone.getLceStatus() == RILConstants.LCE_ACTIVE) { // active LCE service
- mPhone.mCi.pullLceData(DataConnection.this.obtainMessage(EVENT_BW_REFRESH_RESPONSE));
+ if (mPhone.getLceStatus() == RILConstants.LCE_ACTIVE // active LCE service
+ && mDataServiceManager.getTransportType() == TransportType.WWAN) {
+ mPhone.mCi.pullLceData(
+ DataConnection.this.obtainMessage(EVENT_BW_REFRESH_RESPONSE));
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 394d20f..5faf169 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -536,7 +536,19 @@
private int mDisconnectPendingCount = 0;
- private ArrayList<DataProfile> mLastDataProfileList = null;
+ /** Indicate if metered APNs are disabled.
+ * set to block all the metered APNs from continuously sending requests, which causes
+ * undesired network load */
+ private boolean mMeteredApnDisabled = false;
+
+ /**
+ * int to remember whether has setDataProfiles and with roaming or not.
+ * 0: default, has never set data profile
+ * 1: has set data profile with home protocol
+ * 2: has set data profile with roaming protocol
+ * This is not needed once RIL command is updated to support both home and roaming protocol.
+ */
+ private int mSetDataProfileStatus = 0;
/**
* Handles changes to the APN db.
@@ -716,7 +728,6 @@
mPhone.getCallTracker().registerForVoiceCallStarted(this,
DctConstants.EVENT_VOICE_CALL_STARTED, null);
registerServiceStateTrackerEvents();
- mPhone.mCi.registerForPcoData(this, DctConstants.EVENT_PCO_DATA_RECEIVED, null);
mPhone.getCarrierActionAgent().registerForCarrierAction(
CarrierActionAgent.CARRIER_ACTION_SET_METERED_APNS_ENABLED, this,
DctConstants.EVENT_SET_CARRIER_DATA_ENABLED, null, false);
@@ -773,7 +784,6 @@
mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
unregisterServiceStateTrackerEvents();
- mPhone.mCi.unregisterForPcoData(this);
mPhone.getCarrierActionAgent().unregisterForCarrierAction(this,
CarrierActionAgent.CARRIER_ACTION_SET_METERED_APNS_ENABLED);
mDataServiceManager.unregisterForServiceBindingChanged(this);
@@ -3208,25 +3218,18 @@
private void setDataProfilesAsNeeded() {
if (DBG) log("setDataProfilesAsNeeded");
- ArrayList<DataProfile> dataProfileList = new ArrayList<>();
-
+ ArrayList<DataProfile> dps = new ArrayList<DataProfile>();
for (ApnSetting apn : mAllApnSettings) {
- DataProfile dp = createDataProfile(apn);
- if (!dataProfileList.contains(dp)) {
- dataProfileList.add(dp);
+ if (apn.getModemCognitive()) {
+ DataProfile dp = createDataProfile(apn);
+ if (!dps.contains(dp)) {
+ dps.add(dp);
+ }
}
}
-
- // Check if the data profiles we are sending are same as we did last time. We don't want to
- // send the redundant profiles to the modem. Also note that when no data profiles are
- // available, for example when SIM is not present, we send the empty list to the modem.
- // Also we always send the data profiles once after boot up.
- if (mLastDataProfileList == null
- || dataProfileList.size() != mLastDataProfileList.size()
- || !mLastDataProfileList.containsAll(dataProfileList)) {
- mDataServiceManager.setDataProfile(dataProfileList,
+ if (dps.size() > 0) {
+ mDataServiceManager.setDataProfile(dps,
mPhone.getServiceState().getDataRoamingFromRegistration(), null);
- mLastDataProfileList = dataProfileList;
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
index 114a4b4..339fa9a 100644
--- a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
+++ b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
@@ -52,9 +52,12 @@
private final HashMap<NetworkRequest, LocalLog> mSpecificRequests =
new HashMap<NetworkRequest, LocalLog>();
- private int mPhoneId;
+ private final int mPhoneId;
+ // Only when this network factory is active, it will apply any network requests.
private boolean mIsActive;
- private boolean mIsDefault;
+ // Whether this network factory is active and should handle default network requests.
+ // Default network requests are those that don't specify subscription ID.
+ private boolean mIsActiveForDefault;
private int mSubscriptionId;
private final static int TELEPHONY_NETWORK_SCORE = 50;
@@ -62,9 +65,8 @@
private final Handler mInternalHandler;
private static final int EVENT_ACTIVE_PHONE_SWITCH = 1;
private static final int EVENT_SUBSCRIPTION_CHANGED = 2;
- private static final int EVENT_DEFAULT_SUBSCRIPTION_CHANGED = 3;
- private static final int EVENT_NETWORK_REQUEST = 4;
- private static final int EVENT_NETWORK_RELEASE = 5;
+ private static final int EVENT_NETWORK_REQUEST = 3;
+ private static final int EVENT_NETWORK_RELEASE = 4;
public TelephonyNetworkFactory(PhoneSwitcher phoneSwitcher,
SubscriptionController subscriptionController, SubscriptionMonitor subscriptionMonitor,
@@ -90,9 +92,7 @@
mSubscriptionMonitor.registerForSubscriptionChanged(mPhoneId, mInternalHandler,
EVENT_SUBSCRIPTION_CHANGED, null);
- mIsDefault = false;
- mSubscriptionMonitor.registerForDefaultDataSubscriptionChanged(mPhoneId, mInternalHandler,
- EVENT_DEFAULT_SUBSCRIPTION_CHANGED, null);
+ mIsActiveForDefault = false;
register();
}
@@ -138,10 +138,6 @@
onSubIdChange();
break;
}
- case EVENT_DEFAULT_SUBSCRIPTION_CHANGED: {
- onDefaultChange();
- break;
- }
case EVENT_NETWORK_REQUEST: {
onNeedNetworkFor(msg);
break;
@@ -155,34 +151,51 @@
}
private static final int REQUEST_LOG_SIZE = 40;
- private static final boolean REQUEST = true;
- private static final boolean RELEASE = false;
- private void applyRequests(HashMap<NetworkRequest, LocalLog> requestMap, boolean action,
- String logStr) {
+ private static final int ACTION_NO_OP = 0;
+ private static final int ACTION_REQUEST = 1;
+ private static final int ACTION_RELEASE = 2;
+
+ private void applyRequests(HashMap<NetworkRequest, LocalLog> requestMap,
+ int action, String logStr) {
+ if (action == ACTION_NO_OP) return;
+
for (NetworkRequest networkRequest : requestMap.keySet()) {
LocalLog localLog = requestMap.get(networkRequest);
localLog.log(logStr);
- if (action == REQUEST) {
+ if (action == ACTION_REQUEST) {
mDcTracker.requestNetwork(networkRequest, localLog);
- } else {
+ } else if (action == ACTION_RELEASE) {
mDcTracker.releaseNetwork(networkRequest, localLog);
}
}
}
+ private static int getAction(boolean wasActive, boolean isActive) {
+ if (!wasActive && isActive) {
+ return ACTION_REQUEST;
+ } else if (wasActive && !isActive) {
+ return ACTION_RELEASE;
+ } else {
+ return ACTION_NO_OP;
+ }
+ }
+
// apply or revoke requests if our active-ness changes
private void onActivePhoneSwitch() {
final boolean newIsActive = mPhoneSwitcher.isPhoneActive(mPhoneId);
- if (mIsActive != newIsActive) {
- mIsActive = newIsActive;
- String logString = "onActivePhoneSwitch(" + mIsActive + ", " + mIsDefault + ")";
- if (DBG) log(logString);
- if (mIsDefault) {
- applyRequests(mDefaultRequests, (mIsActive ? REQUEST : RELEASE), logString);
- }
- applyRequests(mSpecificRequests, (mIsActive ? REQUEST : RELEASE), logString);
- }
+ final boolean newIsActiveForDefault = mPhoneSwitcher.isActiveForDefaultRequests(mPhoneId);
+
+ String logString = "onActivePhoneSwitch(newIsActive " + newIsActive + ", "
+ + "newIsActive " + newIsActiveForDefault + ")";
+ if (DBG) log(logString);
+
+ applyRequests(mSpecificRequests, getAction(mIsActive, newIsActive), logString);
+ applyRequests(mDefaultRequests, getAction(mIsActiveForDefault, newIsActiveForDefault),
+ logString);
+
+ mIsActive = newIsActive;
+ mIsActiveForDefault = newIsActiveForDefault;
}
// watch for phone->subId changes, reapply new filter and let
@@ -196,21 +209,6 @@
}
}
- // watch for default-data changes (could be side effect of
- // phoneId->subId map change or direct change of default subId)
- // and apply/revoke default-only requests.
- private void onDefaultChange() {
- final int newDefaultSubscriptionId = mSubscriptionController.getDefaultDataSubId();
- final boolean newIsDefault = (newDefaultSubscriptionId == mSubscriptionId);
- if (newIsDefault != mIsDefault) {
- mIsDefault = newIsDefault;
- String logString = "onDefaultChange(" + mIsActive + "," + mIsDefault + ")";
- if (DBG) log(logString);
- if (mIsActive == false) return;
- applyRequests(mDefaultRequests, (mIsDefault ? REQUEST : RELEASE), logString);
- }
- }
-
@Override
public void needNetworkFor(NetworkRequest networkRequest, int score) {
Message msg = mInternalHandler.obtainMessage(EVENT_NETWORK_REQUEST);
@@ -229,23 +227,23 @@
localLog = new LocalLog(REQUEST_LOG_SIZE);
localLog.log("created for " + networkRequest);
mDefaultRequests.put(networkRequest, localLog);
- isApplicable = mIsDefault;
+ isApplicable = mIsActiveForDefault;
}
} else {
localLog = mSpecificRequests.get(networkRequest);
if (localLog == null) {
localLog = new LocalLog(REQUEST_LOG_SIZE);
mSpecificRequests.put(networkRequest, localLog);
- isApplicable = true;
+ isApplicable = mIsActive;
}
}
- if (mIsActive && isApplicable) {
+ if (isApplicable) {
String s = "onNeedNetworkFor";
localLog.log(s);
log(s + " " + networkRequest);
mDcTracker.requestNetwork(networkRequest, localLog);
} else {
- String s = "not acting - isApp=" + isApplicable + ", isAct=" + mIsActive;
+ String s = "not acting - isApplicable=" + isApplicable + ", mIsActive=" + mIsActive;
localLog.log(s);
log(s + " " + networkRequest);
}
@@ -264,19 +262,19 @@
boolean isApplicable = false;
if (networkRequest.networkCapabilities.getNetworkSpecifier() == null) {
// request only for the default network
+ isApplicable = mDefaultRequests.containsKey(networkRequest) && mIsActiveForDefault;
localLog = mDefaultRequests.remove(networkRequest);
- isApplicable = (localLog != null) && mIsDefault;
} else {
+ isApplicable = mSpecificRequests.containsKey(networkRequest) && mIsActive;
localLog = mSpecificRequests.remove(networkRequest);
- isApplicable = (localLog != null);
}
- if (mIsActive && isApplicable) {
+ if (isApplicable) {
String s = "onReleaseNetworkFor";
localLog.log(s);
log(s + " " + networkRequest);
mDcTracker.releaseNetwork(networkRequest, localLog);
} else {
- String s = "not releasing - isApp=" + isApplicable + ", isAct=" + mIsActive;
+ String s = "not releasing - isApplicable=" + isApplicable + ", mIsActive=" + mIsActive;
localLog.log(s);
log(s + " " + networkRequest);
}
@@ -289,7 +287,7 @@
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
pw.println(LOG_TAG + " mSubId=" + mSubscriptionId + " mIsActive=" +
- mIsActive + " mIsDefault=" + mIsDefault);
+ mIsActive + " mIsActiveForDefault=" + mIsActiveForDefault);
pw.println("Default Requests:");
pw.increaseIndent();
for (NetworkRequest nr : mDefaultRequests.keySet()) {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index a75b4ce..dc8c126 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -365,7 +365,12 @@
}
public boolean canDial() {
- return mCT.canDial();
+ try {
+ mCT.checkForDialIssues();
+ } catch (CallStateException cse) {
+ return false;
+ }
+ return true;
}
@Override
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index c61af38..82f4702 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -934,9 +934,9 @@
throw new CallStateException("service not available");
}
- if (!canDial()) {
- throw new CallStateException("cannot dial in current state");
- }
+ // See if there are any issues which preclude placing a call; throw a CallStateException
+ // if there is.
+ checkForDialIssues();
if (isPhoneInEcmMode && isEmergencyNumber) {
handleEcmTimer(ImsPhone.CANCEL_ECM_TIMER);
@@ -957,8 +957,9 @@
// there on hold
if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) {
if (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE) {
- //we should have failed in !canDial() above before we get here
- throw new CallStateException("cannot dial in current state");
+ //we should have failed in checkForDialIssues above before we get here
+ throw new CallStateException(CallStateException.ERROR_TOO_MANY_CALLS,
+ "Already too many ongoing calls.");
}
// foreground call is empty for the newly dialed connection
holdBeforeDial = true;
@@ -1517,18 +1518,30 @@
&& !mForegroundCall.isFull();
}
- public boolean canDial() {
- boolean ret;
+ /**
+ * Determines if there are issues which would preclude dialing an outgoing call. Throws a
+ * {@link CallStateException} if there is an issue.
+ * @throws CallStateException
+ */
+ public void checkForDialIssues() throws CallStateException {
String disableCall = SystemProperties.get(
TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
-
- ret = mPendingMO == null
- && !mRingingCall.isRinging()
- && !disableCall.equals("true")
- && (!mForegroundCall.getState().isAlive()
- || !mBackgroundCall.getState().isAlive());
-
- return ret;
+ if (disableCall.equals("true")) {
+ throw new CallStateException(CallStateException.ERROR_CALLING_DISABLED,
+ "ro.telephony.disable-call has been used to disable calling.");
+ }
+ if (mPendingMO != null) {
+ throw new CallStateException(CallStateException.ERROR_ALREADY_DIALING,
+ "Another outgoing call is already being dialed.");
+ }
+ if (mRingingCall.isRinging()) {
+ throw new CallStateException(CallStateException.ERROR_CALL_RINGING,
+ "Can't place a call while another is ringing.");
+ }
+ if (mForegroundCall.getState().isAlive() & mBackgroundCall.getState().isAlive()) {
+ throw new CallStateException(CallStateException.ERROR_TOO_MANY_CALLS,
+ "Already an active foreground and background call.");
+ }
}
public boolean
@@ -2769,7 +2782,15 @@
}
}
+ if (isHandoverToWifi && mIsViLteDataMetered) {
+ conn.setLocalVideoCapable(true);
+ }
+
if (isHandoverFromWifi && imsCall.isVideoCall()) {
+ if (mIsViLteDataMetered) {
+ conn.setLocalVideoCapable(mIsDataEnabled);
+ }
+
if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) {
if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
log("onCallHandover :: notifying of WIFI to LTE handover.");
@@ -3755,7 +3776,9 @@
// Inform connections that data has been disabled to ensure we turn off video capability
// if this is an LTE call.
for (ImsPhoneConnection conn : mConnections) {
- conn.handleDataEnabledChange(enabled);
+ ImsCall imsCall = conn.getImsCall();
+ boolean isLocalVideoCapable = enabled || (imsCall != null && imsCall.isWifiCall());
+ conn.setLocalVideoCapable(isLocalVideoCapable);
}
int reasonCode;
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index d24b256..b707761 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -121,7 +121,7 @@
* currently available, but mobile data is off and the carrier is metering data for video
* calls.
*/
- private boolean mIsVideoEnabled = true;
+ private boolean mIsLocalVideoCapable = true;
//***** Event Constants
private static final int EVENT_DTMF_DONE = 1;
@@ -264,7 +264,7 @@
capabilities = removeCapability(capabilities,
Connection.Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
- if (!mIsVideoEnabled) {
+ if (!mIsLocalVideoCapable) {
Rlog.i(LOG_TAG, "applyLocalCallCapabilities - disabling video (overidden)");
return capabilities;
}
@@ -981,9 +981,24 @@
setVideoState(newVideoState);
}
- public void sendRttModifyRequest(android.telecom.Connection.RttTextStream textStream) {
- getImsCall().sendRttModifyRequest();
- setCurrentRttTextStream(textStream);
+
+ /**
+ * Send a RTT upgrade request to the remote party.
+ * @param textStream RTT text stream to use
+ */
+ public void startRtt(android.telecom.Connection.RttTextStream textStream) {
+ ImsCall imsCall = getImsCall();
+ if (imsCall != null) {
+ getImsCall().sendRttModifyRequest(true);
+ setCurrentRttTextStream(textStream);
+ }
+ }
+
+ /**
+ * Terminate the current RTT session.
+ */
+ public void stopRtt() {
+ getImsCall().sendRttModifyRequest(false);
}
/**
@@ -996,11 +1011,13 @@
boolean accept = textStream != null;
ImsCall imsCall = getImsCall();
- imsCall.sendRttModifyResponse(accept);
- if (accept) {
- setCurrentRttTextStream(textStream);
- } else {
- Rlog.e(LOG_TAG, "sendRttModifyResponse: foreground call has no connections");
+ if (imsCall != null) {
+ imsCall.sendRttModifyResponse(accept);
+ if (accept) {
+ setCurrentRttTextStream(textStream);
+ } else {
+ Rlog.e(LOG_TAG, "sendRttModifyResponse: foreground call has no connections");
+ }
}
}
@@ -1055,7 +1072,12 @@
// Make sure to synchronize on ImsPhoneConnection.this before calling.
private void createRttTextHandler() {
mRttTextHandler = new ImsRttTextHandler(Looper.getMainLooper(),
- (message) -> getImsCall().sendRttMessage(message));
+ (message) -> {
+ ImsCall imsCall = getImsCall();
+ if (imsCall != null) {
+ imsCall.sendRttMessage(message);
+ }
+ });
mRttTextHandler.initialize(mRttTextStream);
}
@@ -1080,7 +1102,7 @@
}
private void updateEmergencyCallFromExtras(Bundle extras) {
- if (extras.getBoolean(ImsCallProfile.EXTRA_E_CALL)) {
+ if (extras.getBoolean(ImsCallProfile.EXTRA_EMERGENCY_CALL)) {
setIsNetworkIdentifiedEmergencyCall(true);
}
}
@@ -1330,9 +1352,9 @@
mShouldIgnoreVideoStateChanges = false;
}
- public void handleDataEnabledChange(boolean isDataEnabled) {
- mIsVideoEnabled = isDataEnabled;
- Rlog.i(LOG_TAG, "handleDataEnabledChange: isDataEnabled=" + isDataEnabled
+ public void setLocalVideoCapable(boolean isVideoEnabled) {
+ mIsLocalVideoCapable = isVideoEnabled;
+ Rlog.i(LOG_TAG, "setLocalVideoCapable: mIsLocalVideoCapable = " + mIsLocalVideoCapable
+ "; updating local video availability.");
updateMediaCapabilities(getImsCall());
}
diff --git a/src/java/com/android/internal/telephony/uicc/IccIoResult.java b/src/java/com/android/internal/telephony/uicc/IccIoResult.java
index b1b6e75..01d270c 100644
--- a/src/java/com/android/internal/telephony/uicc/IccIoResult.java
+++ b/src/java/com/android/internal/telephony/uicc/IccIoResult.java
@@ -187,9 +187,14 @@
@Override
public String toString() {
- return "IccIoResult sw1:0x" + Integer.toHexString(sw1) + " sw2:0x"
- + Integer.toHexString(sw2) + " Payload: "
- + ((Build.IS_DEBUGGABLE && Build.IS_ENG) ? payload : "*******")
+ return "IccIoResult sw1:0x"
+ + Integer.toHexString(sw1)
+ + " sw2:0x"
+ + Integer.toHexString(sw2)
+ + " Payload: "
+ + ((Build.IS_DEBUGGABLE && Build.IS_ENG)
+ ? IccUtils.bytesToHexString(payload)
+ : "*******")
+ ((!success()) ? " Error: " + getErrorString() : "");
}
diff --git a/tests/telephonytests/Android.bp b/tests/telephonytests/Android.bp
index 960c892..f772c49 100644
--- a/tests/telephonytests/Android.bp
+++ b/tests/telephonytests/Android.bp
@@ -6,7 +6,6 @@
libs: [
"android.test.runner",
"ims-common",
- "bouncycastle",
"android.test.base",
"android.test.mock",
],
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index c671f4f..6313633 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -259,6 +259,8 @@
public String getSystemServiceName(Class<?> serviceClass) {
if (serviceClass == SubscriptionManager.class) {
return Context.TELEPHONY_SUBSCRIPTION_SERVICE;
+ } else if (serviceClass == AppOpsManager.class) {
+ return Context.APP_OPS_SERVICE;
}
return super.getSystemServiceName(serviceClass);
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
index 86c41c9..b794b4d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -18,11 +18,13 @@
import android.content.ContentUris;
import android.content.ContentValues;
+import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.os.Bundle;
+import android.provider.BaseColumns;
import android.support.test.InstrumentationRegistry;
import android.telephony.SubscriptionManager;
import android.test.mock.MockContentProvider;
@@ -139,8 +141,17 @@
}
@Override
- public final int update(Uri uri, ContentValues values, String where,
- String[] selectionArgs) {
- return mDbHelper.getWritableDatabase().update("siminfo", values, where, selectionArgs);
+ public final int update(Uri uri, ContentValues values, String where, String[] selectionArgs) {
+ // handle URI with appended subId
+ final int urlSimInfoSubId = 0;
+ UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
+ matcher.addURI("telephony", "siminfo/#", urlSimInfoSubId);
+ if (matcher.match(uri) == urlSimInfoSubId) {
+ where = BaseColumns._ID + "=" + uri.getLastPathSegment();
+ }
+
+ int count = mDbHelper.getWritableDatabase().update("siminfo", values, where,
+ selectionArgs);
+ return count;
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
index fae1a25..4f450d0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
@@ -16,37 +16,38 @@
package com.android.internal.telephony;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.StringNetworkSpecifier;
-import android.os.AsyncResult;
-import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.Looper;
import android.os.Message;
-import android.telephony.Rlog;
+import android.os.Messenger;
+import android.telephony.SubscriptionManager;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.internal.telephony.mocks.ConnectivityServiceMock;
-import com.android.internal.telephony.mocks.SubscriptionControllerMock;
-import com.android.internal.telephony.mocks.TelephonyRegistryMock;
-import com.android.internal.telephony.test.SimulatedCommands;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
+import org.mockito.Mock;
public class PhoneSwitcherTest extends TelephonyTest {
- private static final String LOG_TAG = "PhoneSwitcherTest";
-
private static final String[] sNetworkAttributes = new String[] {
"mobile,0,0,0,-1,true", "mobile_mms,2,0,2,60000,true",
"mobile_supl,3,0,2,60000,true", "mobile_dun,4,0,2,60000,true",
@@ -54,132 +55,30 @@
"mobile_ims,11,0,2,60000,true", "mobile_cbs,12,0,2,60000,true",
"mobile_ia,14,0,2,-1,true", "mobile_emergency,15,0,2,-1,true"};
- static void failAndStack(String str) {
- fail(str + "\n" + SubscriptionMonitorTest.stack());
- }
+ private static final int ACTIVE_PHONE_SWITCH = 1;
- static String stack() {
- StringBuilder sb = new StringBuilder();
- for(StackTraceElement e : Thread.currentThread().getStackTrace()) {
- sb.append(e.toString()).append("\n");
- }
- return sb.toString();
- }
+ @Mock
+ private ITelephonyRegistry.Stub mTelRegistryMock;
+ @Mock
+ private CommandsInterface mCommandsInterface0;
+ @Mock
+ private CommandsInterface mCommandsInterface1;
+ @Mock
+ private Phone mPhone2; // mPhone as phone 1 is already defined in TelephonyTest.
+ @Mock
+ private Handler mActivePhoneSwitchHandler;
- private static class TestHandler extends Handler {
- public final static int ACTIVE_PHONE_SWITCH = 1;
- public final static int IN_IDLE = 2;
-
- HandlerThread handlerThread;
-
- public TestHandler(Looper looper) {
- super(looper);
- }
-
- public void die() {
- if(handlerThread != null) {
- handlerThread.quit();
- handlerThread = null;
- }
- }
-
- public void blockTilIdle() {
- Object lock = new Object();
- synchronized (lock) {
- Message msg = this.obtainMessage(IN_IDLE, lock);
- msg.sendToTarget();
- try {
- lock.wait();
- } catch (InterruptedException e) {}
- }
- }
-
- public static TestHandler makeHandler() {
- final HandlerThread handlerThread = new HandlerThread("TestHandler");
- handlerThread.start();
- final TestHandler result = new TestHandler(handlerThread.getLooper());
- result.handlerThread = handlerThread;
- return result;
- }
-
- private boolean objectEquals(Object o1, Object o2) {
- if (o1 == null) return (o2 == null);
- return o1.equals(o2);
- }
-
- private void failAndStack(String str) {
- SubscriptionMonitorTest.failAndStack(str);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case ACTIVE_PHONE_SWITCH: {
- AsyncResult ar = (AsyncResult)(msg.obj);
- if (objectEquals(ar.userObj, mActivePhoneSwitchObject.get()) == false) {
- failAndStack("Active Phone Switch object is incorrect!");
- }
- int count = mActivePhoneSwitchCount.incrementAndGet();
- Rlog.d(LOG_TAG, "ACTIVE_PHONE_SWITCH, inc to " + count);
- break;
- }
- case IN_IDLE: {
- Object lock = msg.obj;
- synchronized (lock) {
- lock.notify();
- }
- break;
- }
- }
- }
-
- private final AtomicInteger mActivePhoneSwitchCount = new AtomicInteger(0);
- private final AtomicReference<Object> mActivePhoneSwitchObject =
- new AtomicReference<Object>();
-
- public void reset() {
- mActivePhoneSwitchCount.set(0);
- mActivePhoneSwitchObject.set(null);
- }
-
- public void setActivePhoneSwitchObject(Object o) {
- mActivePhoneSwitchObject.set(o);
- }
-
- public int getActivePhoneSwitchCount() {
- return mActivePhoneSwitchCount.get();
- }
- }
-
- private void waitABit() {
- try {
- Thread.sleep(250);
- } catch (Exception e) {}
- }
-
- private String mTestName = "";
-
- private void log(String str) {
- Rlog.d(LOG_TAG + " " + mTestName, str);
- }
-
- private NetworkRequest makeSubSpecificDefaultRequest(ConnectivityServiceMock cs, int subId) {
- NetworkCapabilities netCap = (new NetworkCapabilities()).
- addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).
- addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED).
- addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- netCap.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(subId)));
- return cs.requestNetwork(netCap, null, 0, new Binder(), -1);
- }
-
- private NetworkRequest makeSubSpecificMmsRequest(ConnectivityServiceMock cs, int subId) {
- NetworkCapabilities netCap = (new NetworkCapabilities()).
- addCapability(NetworkCapabilities.NET_CAPABILITY_MMS).
- addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED).
- addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- netCap.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(subId)));
- return cs.requestNetwork(netCap, null, 0, new Binder(), -1);
- }
+ // The thread that mPhoneSwitcher will handle events in.
+ private HandlerThread mHandlerThread;
+ private PhoneSwitcher mPhoneSwitcher;
+ private IOnSubscriptionsChangedListener mSubChangedListener;
+ private ConnectivityManager mConnectivityManager;
+ // The messenger of PhoneSwitcher used to receive network requests.
+ private Messenger mNetworkFactoryMessenger = null;
+ private int mDefaultDataSub = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private CommandsInterface[] mCommandsInterfaces;
+ private int[][] mSlotIndexToSubId;
+ private boolean[] mDataAllowed;
@Before
public void setUp() throws Exception {
@@ -197,87 +96,51 @@
@Test
@SmallTest
public void testRegister() throws Exception {
- mTestName = "testRegister";
final int numPhones = 2;
final int maxActivePhones = 1;
- final HandlerThread handlerThread = new HandlerThread("PhoneSwitcherTestThread");
- handlerThread.start();
- mContextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
- sNetworkAttributes);
- final Context contextMock = mContextFixture.getTestDouble();
- final ConnectivityServiceMock connectivityServiceMock =
- new ConnectivityServiceMock(contextMock);
- final ConnectivityManager cm =
- new ConnectivityManager(contextMock, connectivityServiceMock);
- mContextFixture.setSystemService(Context.CONNECTIVITY_SERVICE, cm);
- final ITelephonyRegistry.Stub telRegistryMock = new TelephonyRegistryMock();
- final SubscriptionControllerMock subControllerMock =
- new SubscriptionControllerMock(contextMock, telRegistryMock, numPhones);
- final SimulatedCommands[] commandsInterfaces = new SimulatedCommands[numPhones];
- final PhoneMock[] phones = new PhoneMock[numPhones];
- for (int i = 0; i < numPhones; i++) {
- commandsInterfaces[i] = new SimulatedCommands();
- // phones[i] = new PhoneMock(contextMock, commandsInterfaces[i]);
- }
-
- PhoneSwitcher phoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones,
- contextMock, subControllerMock, handlerThread.getLooper(), telRegistryMock,
- commandsInterfaces, phones);
+ initialize(numPhones, maxActivePhones);
// verify nothing has been done while there are no inputs
- if (commandsInterfaces[0].isDataAllowed()) fail("data allowed initially");
- if (phoneSwitcher.isPhoneActive(0)) fail("phone active initially");
+ assertFalse("data allowed initially", mDataAllowed[0]);
+ assertFalse("data allowed initially", mDataAllowed[0]);
+ assertFalse("phone active initially", mPhoneSwitcher.isPhoneActive(0));
- connectivityServiceMock.addDefaultRequest();
+ NetworkRequest internetNetworkRequest = addInternetNetworkRequest(null, 50);
waitABit();
- if (commandsInterfaces[0].isDataAllowed()) fail("data allowed after request");
- if (phoneSwitcher.isPhoneActive(0)) fail("phone active after request");
-
- TestHandler testHandler = TestHandler.makeHandler();
- Object activePhoneSwitchObject = new Object();
- testHandler.setActivePhoneSwitchObject(activePhoneSwitchObject);
-
- testHandler.blockTilIdle();
+ assertFalse("data allowed after request", mDataAllowed[0]);
+ assertFalse("phone active after request", mPhoneSwitcher.isPhoneActive(0));
// not registered yet - shouldn't inc
- if (testHandler.getActivePhoneSwitchCount() != 0) {
- fail("pretest of ActivePhoneSwitchCount");
- }
+ verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong());
+
boolean threw = false;
try {
// should throw
- phoneSwitcher.registerForActivePhoneSwitch(2, testHandler,
- TestHandler.ACTIVE_PHONE_SWITCH, activePhoneSwitchObject);
+ mPhoneSwitcher.registerForActivePhoneSwitch(2, mActivePhoneSwitchHandler,
+ ACTIVE_PHONE_SWITCH, null);
} catch (IllegalArgumentException e) {
threw = true;
}
- if (threw == false) fail("register with bad phoneId didn't throw");
+ assertTrue("register with bad phoneId didn't throw", threw);
- phoneSwitcher.registerForActivePhoneSwitch(0, testHandler,
- TestHandler.ACTIVE_PHONE_SWITCH,
- activePhoneSwitchObject);
- testHandler.blockTilIdle();
+ mPhoneSwitcher.registerForActivePhoneSwitch(0, mActivePhoneSwitchHandler,
+ ACTIVE_PHONE_SWITCH, null);
- if (testHandler.getActivePhoneSwitchCount() != 1) {
- fail("post register of ActivePhoneSwitchCount not 1!");
- }
+ verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
- subControllerMock.setDefaultDataSubId(0);
- testHandler.blockTilIdle();
- if (testHandler.getActivePhoneSwitchCount() != 1) {
- fail("after set of default to 0, ActivePhoneSwitchCount not 1!");
- }
- if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
+ setDefaultDataSubId(0);
- subControllerMock.setSlotSubId(0, 0);
+ verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+ assertFalse("data allowed", mDataAllowed[0]);
+
+ setSlotIndexToSubId(0, 0);
+ mSubChangedListener.onSubscriptionsChanged();
waitABit();
- if (testHandler.getActivePhoneSwitchCount() != 2) {
- fail("after mapping of 0 to 0, ActivePhoneSwitchCount not 2!");
- }
- if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
+ verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
+ assertTrue("data not allowed", mDataAllowed[0]);
// now try various things that should cause the active phone to switch:
// 1 lose default via default sub change
@@ -292,93 +155,87 @@
// 10 don't switch phones when in emergency mode
// 1 lose default via default sub change
- subControllerMock.setDefaultDataSubId(1);
+ setDefaultDataSubId(1);
waitABit();
- if (testHandler.getActivePhoneSwitchCount() != 3) {
- fail("after set of default to 1, ActivePhoneSwitchCount not 3!");
- }
- if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
- subControllerMock.setSlotSubId(1, 1);
+ verify(mActivePhoneSwitchHandler, times(3)).sendMessageAtTime(any(), anyLong());
+ assertFalse("data allowed", mDataAllowed[0]);
+
+ setSlotIndexToSubId(1, 1);
+ mSubChangedListener.onSubscriptionsChanged();
waitABit();
- if (testHandler.getActivePhoneSwitchCount() != 3) {
- fail("after mapping of 1 to 1, ActivePhoneSwitchCount not 3!");
- }
- if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
- if (commandsInterfaces[1].isDataAllowed() == false) fail("data not allowed");
+
+ verify(mActivePhoneSwitchHandler, times(3)).sendMessageAtTime(any(), anyLong());
+ assertFalse("data allowed", mDataAllowed[0]);
+ assertTrue("data not allowed", mDataAllowed[1]);
// 2 gain default via default sub change
- subControllerMock.setDefaultDataSubId(0);
+ setDefaultDataSubId(0);
waitABit();
- if (testHandler.getActivePhoneSwitchCount() != 4) {
- fail("after set of default to 0, ActivePhoneSwitchCount not 4!");
- }
- if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
- if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
+
+ verify(mActivePhoneSwitchHandler, times(4)).sendMessageAtTime(any(), anyLong());
+ assertFalse("data allowed", mDataAllowed[1]);
+ assertTrue("data not allowed", mDataAllowed[0]);
// 3 lose default via sub->phone change
- subControllerMock.setSlotSubId(0, 2);
+ setSlotIndexToSubId(0, 2);
+ mSubChangedListener.onSubscriptionsChanged();
waitABit();
- if (testHandler.getActivePhoneSwitchCount() != 5) {
- fail("after mapping of 0 to 2, ActivePhoneSwitchCount not 5!");
- }
- if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
- if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
+ verify(mActivePhoneSwitchHandler, times(5)).sendMessageAtTime(any(), anyLong());
+ assertFalse("data allowed", mDataAllowed[0]);
+ assertFalse("data allowed", mDataAllowed[1]);
// 4 gain default via sub->phone change
- subControllerMock.setSlotSubId(0, 0);
+ setSlotIndexToSubId(0, 0);
+ mSubChangedListener.onSubscriptionsChanged();
waitABit();
- if (testHandler.getActivePhoneSwitchCount() != 6) {
- fail("after mapping of 0 to 0, ActivePhoneSwitchCount not 6!");
- }
- if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
- if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
+
+ verify(mActivePhoneSwitchHandler, times(6)).sendMessageAtTime(any(), anyLong());
+ assertTrue("data not allowed", mDataAllowed[0]);
+ assertFalse("data allowed", mDataAllowed[1]);
// 5 lose default network request
- connectivityServiceMock.removeDefaultRequest();
+ releaseNetworkRequest(internetNetworkRequest);
waitABit();
- if (testHandler.getActivePhoneSwitchCount() != 7) {
- fail("after loss of network request, ActivePhoneSwitchCount not 7!");
- }
- if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
- if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
+
+ verify(mActivePhoneSwitchHandler, times(7)).sendMessageAtTime(any(), anyLong());
+ assertFalse("data allowed", mDataAllowed[0]);
+ assertFalse("data allowed", mDataAllowed[1]);
// 6 gain subscription-specific request
- NetworkRequest request = makeSubSpecificDefaultRequest(connectivityServiceMock, 0);
+ NetworkRequest specificInternetRequest = addInternetNetworkRequest(0, 50);
waitABit();
- if (testHandler.getActivePhoneSwitchCount() != 8) {
- fail("after gain of network request, ActivePhoneSwitchCount not 8!");
- }
- if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
- if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
+
+ verify(mActivePhoneSwitchHandler, times(8)).sendMessageAtTime(any(), anyLong());
+ assertTrue("data not allowed", mDataAllowed[0]);
+ assertFalse("data allowed", mDataAllowed[1]);
// 7 lose via sub->phone change
- subControllerMock.setSlotSubId(0, 1);
+ setSlotIndexToSubId(0, 1);
+ mSubChangedListener.onSubscriptionsChanged();
waitABit();
- if (testHandler.getActivePhoneSwitchCount() != 9) {
- fail("after loss of request due to subId map change, ActivePhoneSwitchCount not 9!");
- }
- if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
- if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
+
+ verify(mActivePhoneSwitchHandler, times(9)).sendMessageAtTime(any(), anyLong());
+ assertFalse("data allowed", mDataAllowed[0]);
+ assertFalse("data allowed", mDataAllowed[1]);
// 8 gain via sub->phone change
- subControllerMock.setSlotSubId(0, 0);
+ setSlotIndexToSubId(0, 0);
+ mSubChangedListener.onSubscriptionsChanged();
waitABit();
- if (testHandler.getActivePhoneSwitchCount() != 10) {
- fail("after gain of request due to subId map change, ActivePhoneSwitchCount not 10!");
- }
- if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
- if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
+
+ verify(mActivePhoneSwitchHandler, times(10)).sendMessageAtTime(any(), anyLong());
+ assertTrue("data not allowed", mDataAllowed[0]);
+ assertFalse("data allowed", mDataAllowed[1]);
// 9 lose subscription-specific request
- connectivityServiceMock.releaseNetworkRequest(request);
+ releaseNetworkRequest(specificInternetRequest);
waitABit();
- if (testHandler.getActivePhoneSwitchCount() != 11) {
- fail("after release of request, ActivePhoneSwitchCount not 11!");
- }
- if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
- if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
+
+ verify(mActivePhoneSwitchHandler, times(11)).sendMessageAtTime(any(), anyLong());
+ assertFalse("data allowed", mDataAllowed[0]);
+ assertFalse("data allowed", mDataAllowed[1]);
// 10 don't switch phones when in emergency mode
// not ready yet - Phone turns out to be hard to stub out
@@ -400,13 +257,7 @@
// if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
// if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
- for (int i = 0; i < numPhones; i++) {
- commandsInterfaces[i].dispose();
- }
-
- connectivityServiceMock.die();
- testHandler.die();
- handlerThread.quit();
+ mHandlerThread.quit();
}
/**
@@ -428,68 +279,34 @@
@Test
@SmallTest
public void testPrioritization() throws Exception {
- mTestName = "testPrioritization";
final int numPhones = 2;
final int maxActivePhones = 1;
- final HandlerThread handlerThread = new HandlerThread("PhoneSwitcherTestThread");
- handlerThread.start();
- mContextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
- sNetworkAttributes);
- final Context contextMock = mContextFixture.getTestDouble();
- final ConnectivityServiceMock connectivityServiceMock =
- new ConnectivityServiceMock(contextMock);
- final ConnectivityManager cm =
- new ConnectivityManager(contextMock, connectivityServiceMock);
- mContextFixture.setSystemService(Context.CONNECTIVITY_SERVICE, cm);
- final ITelephonyRegistry.Stub telRegistryMock = new TelephonyRegistryMock();
- final SubscriptionControllerMock subControllerMock =
- new SubscriptionControllerMock(contextMock, telRegistryMock, numPhones);
- final SimulatedCommands[] commandsInterfaces = new SimulatedCommands[numPhones];
- final PhoneMock[] phones = new PhoneMock[numPhones];
- for (int i = 0; i < numPhones; i++) {
- commandsInterfaces[i] = new SimulatedCommands();
- }
+ initialize(numPhones, maxActivePhones);
- PhoneSwitcher phoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones,
- contextMock, subControllerMock, handlerThread.getLooper(), telRegistryMock,
- commandsInterfaces, phones);
-
- TestHandler testHandler = TestHandler.makeHandler();
- Object activePhoneSwitchObject = new Object();
- testHandler.setActivePhoneSwitchObject(activePhoneSwitchObject);
-
- connectivityServiceMock.addDefaultRequest();
- subControllerMock.setSlotSubId(0, 0);
- subControllerMock.setSlotSubId(1, 1);
- subControllerMock.setDefaultDataSubId(0);
+ addInternetNetworkRequest(null, 50);
+ setSlotIndexToSubId(0, 0);
+ setSlotIndexToSubId(1, 1);
+ setDefaultDataSubId(0);
waitABit();
- phoneSwitcher.registerForActivePhoneSwitch(0, testHandler, TestHandler.ACTIVE_PHONE_SWITCH,
- activePhoneSwitchObject);
+ mPhoneSwitcher.registerForActivePhoneSwitch(0, mActivePhoneSwitchHandler,
+ ACTIVE_PHONE_SWITCH, null);
waitABit();
// verify initial conditions
- if (testHandler.getActivePhoneSwitchCount() != 1) {
- fail("Initial conditions not met: ActivePhoneSwitchCount not 1! " +
- testHandler.getActivePhoneSwitchCount());
- }
- if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
- if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
+ verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+
+ assertTrue("data not allowed", mDataAllowed[0]);
+ assertFalse("data allowed", mDataAllowed[1]);
// now start a higher priority conneciton on the other sub
- NetworkRequest request = makeSubSpecificMmsRequest(connectivityServiceMock, 1);
+ addMmsNetworkRequest(1);
waitABit();
- if (testHandler.getActivePhoneSwitchCount() != 2) {
- fail("after gain of network request, ActivePhoneSwitchCount not 2!");
- }
- if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
- if (commandsInterfaces[1].isDataAllowed() == false) fail("data not allowed");
- for (int i = 0; i < numPhones; i++) {
- commandsInterfaces[i].dispose();
- }
+ // After gain of network request, mActivePhoneSwitchHandler should be notified 2 times.
+ verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
+ assertFalse("data allowed", mDataAllowed[0]);
+ assertTrue("data not allowed", mDataAllowed[1]);
- connectivityServiceMock.die();
- testHandler.die();
- handlerThread.quit();
+ mHandlerThread.quit();
}
/**
@@ -499,78 +316,274 @@
@Test
@SmallTest
public void testHigherPriorityDefault() throws Exception {
- mTestName = "testPrioritization";
final int numPhones = 2;
final int maxActivePhones = 1;
- final HandlerThread handlerThread = new HandlerThread("PhoneSwitcherTestThread");
- handlerThread.start();
- mContextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
- sNetworkAttributes);
- final Context contextMock = mContextFixture.getTestDouble();
- final ConnectivityServiceMock connectivityServiceMock =
- new ConnectivityServiceMock(contextMock);
- final ConnectivityManager cm =
- new ConnectivityManager(contextMock, connectivityServiceMock);
- mContextFixture.setSystemService(Context.CONNECTIVITY_SERVICE, cm);
- final ITelephonyRegistry.Stub telRegistryMock = new TelephonyRegistryMock();
- final SubscriptionControllerMock subControllerMock =
- new SubscriptionControllerMock(contextMock, telRegistryMock, numPhones);
- final SimulatedCommands[] commandsInterfaces = new SimulatedCommands[numPhones];
- final PhoneMock[] phones = new PhoneMock[numPhones];
- for (int i = 0; i < numPhones; i++) {
- commandsInterfaces[i] = new SimulatedCommands();
- }
+ initialize(numPhones, maxActivePhones);
- PhoneSwitcher phoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones,
- contextMock, subControllerMock, handlerThread.getLooper(), telRegistryMock,
- commandsInterfaces, phones);
-
- TestHandler testHandler = TestHandler.makeHandler();
- Object activePhoneSwitchObject = new Object();
- testHandler.setActivePhoneSwitchObject(activePhoneSwitchObject);
-
- connectivityServiceMock.addDefaultRequest();
- subControllerMock.setSlotSubId(0, 0);
- subControllerMock.setSlotSubId(1, 1);
- subControllerMock.setDefaultDataSubId(0);
+ addInternetNetworkRequest(null, 50);
+ setSlotIndexToSubId(0, 0);
+ setSlotIndexToSubId(1, 1);
+ setDefaultDataSubId(0);
waitABit();
// Phone 0 should be active
- if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
- if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
+ assertTrue("data not allowed", mDataAllowed[0]);
+ assertFalse("data allowed", mDataAllowed[1]);
- connectivityServiceMock.setCurrentScoreForRequest(connectivityServiceMock.defaultRequest,
- 100);
+ addInternetNetworkRequest(null, 100);
waitABit();
// should be no change
- if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
- if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
+ assertTrue("data not allowed", mDataAllowed[0]);
+ assertFalse("data allowed", mDataAllowed[1]);
- connectivityServiceMock.setCurrentScoreForRequest(connectivityServiceMock.defaultRequest,
- 0);
+ addInternetNetworkRequest(null, 0);
waitABit();
// should be no change
- if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
- if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
+ assertTrue("data not allowed", mDataAllowed[0]);
+ assertFalse("data allowed", mDataAllowed[1]);
- for (int i = 0; i < numPhones; i++) {
- commandsInterfaces[i].dispose();
- }
-
- connectivityServiceMock.die();
- testHandler.die();
- handlerThread.quit();
+ mHandlerThread.quit();
}
/**
- * Test MSMA testing prioritiziation
- * - leave multiple on (up to the limit)
- * - tear down lowest priority phone when new request comes in
- * - tear down low priority phone when sub change causes split
- * - bring up low priority phone when sub change causes join
- * - don't switch phones when in emergency mode
+ * Verify testSetPreferredData.
+ * When preferredData is set, it overwrites defaultData sub to be active sub in single
+ * active phone mode. If it's unset (to DEFAULT_SUBSCRIPTION_ID), defaultData sub becomes
+ * active one.
*/
+ @Test
+ @SmallTest
+ public void testSetPreferredData() throws Exception {
+ final int numPhones = 2;
+ final int maxActivePhones = 1;
+ initialize(numPhones, maxActivePhones);
+ // Phone 0 has sub 1, phone 1 has sub 2.
+ // Sub 1 is default data sub.
+ // Both are active subscriptions are active sub, as they are in both active slots.
+ setSlotIndexToSubId(0, 1);
+ setSlotIndexToSubId(1, 2);
+ setDefaultDataSubId(1);
+
+ // Notify phoneSwitcher about default data sub and default network request.
+ addInternetNetworkRequest(null, 50);
+ waitABit();
+ // Phone 0 (sub 1) should be activated as it has default data sub.
+ assertTrue(mDataAllowed[0]);
+
+ // Set sub 2 as preferred sub should make phone 1 activated and phone 0 deactivated.
+ mPhoneSwitcher.setPreferredData(2);
+ waitABit();
+ assertFalse(mDataAllowed[0]);
+ assertTrue(mDataAllowed[1]);
+
+ // Unset preferred sub should make default data sub (phone 0 / sub 1) activated again.
+ mPhoneSwitcher.setPreferredData(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+ waitABit();
+ assertTrue(mDataAllowed[0]);
+ assertFalse(mDataAllowed[1]);
+
+ mHandlerThread.quit();
+ }
+
+ /* Private utility methods start here */
+
+ private void sendDefaultDataSubChanged() {
+ final Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void initialize(int numPhones, int maxActivePhones) throws Exception {
+ mHandlerThread = new HandlerThread("PhoneSwitcherTestThread");
+ mHandlerThread.start();
+
+ mContextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
+ sNetworkAttributes);
+
+ setNumPhones(numPhones);
+
+ initializeSubControllerMock();
+ initializeCommandInterfacesMock(numPhones);
+ initializeTelRegistryMock();
+ initializeConnManagerMock();
+
+ mPhoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones,
+ mContext, mSubscriptionController, mHandlerThread.getLooper(),
+ mTelRegistryMock, mCommandsInterfaces, mPhones);
+
+ verify(mTelRegistryMock).addOnSubscriptionsChangedListener(
+ eq(mContext.getOpPackageName()), any());
+ }
+
+ /**
+ * Certain variables needs initialized depending on number of phones.
+ */
+ private void setNumPhones(int numPhones) {
+ mDataAllowed = new boolean[numPhones];
+ mSlotIndexToSubId = new int[numPhones][];
+ for (int i = 0; i < numPhones; i++) {
+ mSlotIndexToSubId[i] = new int[1];
+ mSlotIndexToSubId[i][0] = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ if (numPhones == 1) {
+ mCommandsInterfaces = new CommandsInterface[] {mCommandsInterface0};
+ mPhones = new Phone[] {mPhone};
+ } else if (numPhones == 2) {
+ mCommandsInterfaces =
+ new CommandsInterface[] {mCommandsInterface0, mCommandsInterface1};
+ mPhones = new Phone[] {mPhone, mPhone2};
+ }
+ }
+
+ private void initializeCommandInterfacesMock(int numPhones) {
+ // Tell PhoneSwitcher that radio is on.
+ doAnswer(invocation -> {
+ Handler handler = (Handler) invocation.getArguments()[0];
+ int message = (int) invocation.getArguments()[1];
+ Object obj = invocation.getArguments()[2];
+ handler.obtainMessage(message, obj).sendToTarget();
+ return null;
+ }).when(mCommandsInterface0).registerForAvailable(any(), anyInt(), any());
+
+ // Store values of dataAllowed in mDataAllowed[] for easier checking.
+ doAnswer(invocation -> {
+ mDataAllowed[0] = (boolean) invocation.getArguments()[0];
+ return null;
+ }).when(mCommandsInterface0).setDataAllowed(anyBoolean(), any());
+
+ if (numPhones == 2) {
+ doAnswer(invocation -> {
+ mDataAllowed[1] = (boolean) invocation.getArguments()[0];
+ return null;
+ }).when(mCommandsInterface1).setDataAllowed(anyBoolean(), any());
+ }
+ }
+
+ /**
+ * Store subChangedListener of PhoneSwitcher so that testing can notify
+ * PhoneSwitcher of sub change.
+ */
+ private void initializeTelRegistryMock() throws Exception {
+ doAnswer(invocation -> {
+ IOnSubscriptionsChangedListener subChangedListener =
+ (IOnSubscriptionsChangedListener) invocation.getArguments()[1];
+ mSubChangedListener = subChangedListener;
+ mSubChangedListener.onSubscriptionsChanged();
+ return null;
+ }).when(mTelRegistryMock).addOnSubscriptionsChangedListener(any(), any());
+ }
+
+ /**
+ * Capture mNetworkFactoryMessenger so that testing can request or release
+ * network requests on PhoneSwitcher.
+ */
+ private void initializeConnManagerMock() {
+ mConnectivityManager = (ConnectivityManager)
+ mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ doAnswer(invocation -> {
+ mNetworkFactoryMessenger = invocation.getArgument(0);
+ return null;
+ }).when(mConnectivityManager).registerNetworkFactory(any(), any());
+ }
+
+ /**
+ * Capture mNetworkFactoryMessenger so that testing can request or release
+ * network requests on PhoneSwitcher.
+ */
+ private void initializeSubControllerMock() {
+ doReturn(mDefaultDataSub).when(mSubscriptionController).getDefaultDataSubId();
+ doAnswer(invocation -> {
+ int phoneId = (int) invocation.getArguments()[0];
+ return mSlotIndexToSubId[phoneId][0];
+ }).when(mSubscriptionController).getSubIdUsingPhoneId(anyInt());
+
+ doAnswer(invocation -> {
+ int subId = (int) invocation.getArguments()[0];
+
+ if (!SubscriptionManager.isUsableSubIdValue(subId)) return false;
+
+ for (int i = 0; i < mSlotIndexToSubId.length; i++) {
+ if (mSlotIndexToSubId[i][0] == subId) return true;
+ }
+ return false;
+ }).when(mSubscriptionController).isActiveSubId(anyInt());
+ }
+
+ private void setDefaultDataSubId(int defaultDataSub) {
+ mDefaultDataSub = defaultDataSub;
+ doReturn(mDefaultDataSub).when(mSubscriptionController).getDefaultDataSubId();
+ sendDefaultDataSubChanged();
+ }
+
+ private void setSlotIndexToSubId(int slotId, int subId) {
+ mSlotIndexToSubId[slotId][0] = subId;
+ }
+
+ /**
+ * Create an internet PDN network request and send it to PhoneSwitcher.
+ */
+ private NetworkRequest addInternetNetworkRequest(Integer subId, int score) throws Exception {
+ NetworkCapabilities netCap = (new NetworkCapabilities())
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ if (subId != null) {
+ netCap.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(subId)));
+ }
+ NetworkRequest networkRequest = new NetworkRequest(netCap, ConnectivityManager.TYPE_NONE,
+ 0, NetworkRequest.Type.REQUEST);
+
+ Message message = Message.obtain();
+ message.what = android.net.NetworkFactory.CMD_REQUEST_NETWORK;
+ message.arg1 = score;
+ message.obj = networkRequest;
+ mNetworkFactoryMessenger.send(message);
+
+ return networkRequest;
+ }
+
+ /**
+ * Create a mms PDN network request and send it to PhoneSwitcher.
+ */
+ private NetworkRequest addMmsNetworkRequest(Integer subId) throws Exception {
+ NetworkCapabilities netCap = (new NetworkCapabilities())
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ netCap.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(subId)));
+ if (subId != null) {
+ netCap.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(subId)));
+ }
+ NetworkRequest networkRequest = new NetworkRequest(netCap, ConnectivityManager.TYPE_NONE,
+ 1, NetworkRequest.Type.REQUEST);
+
+ Message message = Message.obtain();
+ message.what = android.net.NetworkFactory.CMD_REQUEST_NETWORK;
+ message.arg1 = 50; // Score
+ message.obj = networkRequest;
+ mNetworkFactoryMessenger.send(message);
+
+ return networkRequest;
+ }
+
+ /**
+ * Tell PhoneSwitcher to release a network request.
+ */
+ private void releaseNetworkRequest(NetworkRequest networkRequest) throws Exception {
+ Message message = Message.obtain();
+ message.what = android.net.NetworkFactory.CMD_CANCEL_REQUEST;
+ message.obj = networkRequest;
+ mNetworkFactoryMessenger.send(message);
+ }
+
+ private void waitABit() {
+ try {
+ Thread.sleep(250);
+ } catch (Exception e) {
+ }
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 188b8b7..e631269 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.nullable;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyObject;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
@@ -216,6 +217,18 @@
@Test
@MediumTest
+ public void testSetRadioPowerOffUnderDataConnected() {
+ sst.setRadioPower(true);
+ waitForMs(100);
+ doReturn(false).when(mDct).isDisconnected();
+ sst.setRadioPower(false);
+ waitForMs(200);
+ verify(this.mProxyController, times(1)).registerForAllDataDisconnected(anyInt(),
+ eq(sst), anyInt(), anyObject());
+ }
+
+ @Test
+ @MediumTest
public void testSetRadioPowerFromCarrier() {
// Carrier disable radio power
sst.setRadioPowerFromCarrier(false);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
new file mode 100644
index 0000000..42a8ad1
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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 android.telephony.PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.HandlerThread;
+import android.os.ServiceManager;
+import android.telephony.PhoneCapability;
+import android.telephony.PhoneStateListener;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.TelephonyRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class TelephonyRegistryTest extends TelephonyTest {
+ @Mock
+ private ISub.Stub mISubStub;
+ private PhoneStateListener mPhoneStateListener;
+ private TelephonyRegistry mTelephonyRegistry;
+ private PhoneCapability mPhoneCapability;
+
+ public class PhoneStateListenerWrapper extends PhoneStateListener {
+ @Override
+ public void onPhoneCapabilityChanged(PhoneCapability capability) {
+ mPhoneCapability = capability;
+ setReady(true);
+ }
+ }
+
+ private void addTelephonyRegistryService() {
+ mServiceManagerMockedServices.put("telephony.registry", mTelephonyRegistry.asBinder());
+ }
+
+ private HandlerThread mHandlerThread = new HandlerThread("ListenerThread") {
+ @Override
+ public void onLooperPrepared() {
+ mTelephonyRegistry = new TelephonyRegistry(mContext);
+ addTelephonyRegistryService();
+ mPhoneStateListener = new PhoneStateListenerWrapper();
+ setReady(true);
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp("TelephonyRegistryTest");
+ mServiceManagerMockedServices.put("isub", mISubStub);
+ mHandlerThread.start();
+ waitUntilReady();
+ assertEquals(mTelephonyRegistry.asBinder(),
+ ServiceManager.getService("telephony.registry"));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mTelephonyRegistry = null;
+ mHandlerThread.quit();
+ super.tearDown();
+ }
+
+ @Test @SmallTest
+ public void testPhoneCapabilityChanged() {
+ // mTelephonyRegistry.listen with notifyNow = true should trigger callback immediately.
+ setReady(false);
+ PhoneCapability phoneCapability = new PhoneCapability(1, 2, 3, null);
+ mTelephonyRegistry.notifyPhoneCapabilityChanged(phoneCapability);
+ mTelephonyRegistry.listen(mContext.getOpPackageName(),
+ mPhoneStateListener.callback,
+ LISTEN_PHONE_CAPABILITY_CHANGE, true);
+ waitUntilReady();
+ assertEquals(phoneCapability, mPhoneCapability);
+
+ // notifyPhoneCapabilityChanged with a new capability. Callback should be triggered.
+ setReady(false);
+ phoneCapability = new PhoneCapability(3, 2, 2, null);
+ mTelephonyRegistry.notifyPhoneCapabilityChanged(phoneCapability);
+ waitUntilReady();
+ assertEquals(phoneCapability, mPhoneCapability);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 6e4257e..2cf880b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -185,6 +185,10 @@
@Mock
protected AppSmsManager mAppSmsManager;
@Mock
+ protected IccSmsInterfaceManager mIccSmsInterfaceManager;
+ @Mock
+ protected SmsDispatchersController mSmsDispatchersController;
+ @Mock
protected DeviceStateMonitor mDeviceStateMonitor;
@Mock
protected AccessNetworksManager mAccessNetworksManager;
@@ -213,7 +217,7 @@
private Object mLock = new Object();
private boolean mReady;
protected HashMap<String, IBinder> mServiceManagerMockedServices = new HashMap<>();
- private Phone[] mPhones;
+ protected Phone[] mPhones;
protected HashMap<Integer, ImsManager> mImsManagerInstances = new HashMap<>();
@@ -386,6 +390,8 @@
doReturn(mCarrierSignalAgent).when(mPhone).getCarrierSignalAgent();
doReturn(mCarrierActionAgent).when(mPhone).getCarrierActionAgent();
doReturn(mAppSmsManager).when(mPhone).getAppSmsManager();
+ doReturn(mIccSmsInterfaceManager).when(mPhone).getIccSmsInterfaceManager();
+ mIccSmsInterfaceManager.mDispatchersController = mSmsDispatchersController;
mPhone.mEriManager = mEriManager;
//mUiccController
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
index 6d1cf8b..d99bbc1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
@@ -24,7 +24,6 @@
import android.os.Binder;
import android.os.HandlerThread;
import android.os.Looper;
-import android.os.Message;
import android.support.test.filters.FlakyTest;
import android.telephony.Rlog;
import android.test.AndroidTestCase;
@@ -38,8 +37,6 @@
import com.android.internal.telephony.mocks.SubscriptionMonitorMock;
import com.android.internal.telephony.mocks.TelephonyRegistryMock;
-import org.junit.Ignore;
-
public class TelephonyNetworkFactoryTest extends AndroidTestCase {
private final static String LOG_TAG = "TelephonyNetworkFactoryTest";
@@ -153,10 +150,10 @@
makeTnf(phoneId, ts);
+ ts.phoneSwitcherMock.setPreferredDataPhoneId(phoneId);
ts.subscriptionControllerMock.setDefaultDataSubId(subId);
ts.subscriptionControllerMock.setSlotSubId(phoneId, subId);
ts.subscriptionMonitorMock.notifySubscriptionChanged(phoneId);
- ts.subscriptionMonitorMock.notifyDefaultSubscriptionChanged(phoneId);
log("addDefaultRequest");
ts.connectivityServiceMock.addDefaultRequest();
@@ -248,10 +245,10 @@
makeTnf(phoneId, ts);
+ ts.phoneSwitcherMock.setPreferredDataPhoneId(phoneId);
ts.subscriptionControllerMock.setDefaultDataSubId(subId);
ts.subscriptionControllerMock.setSlotSubId(phoneId, subId);
ts.subscriptionMonitorMock.notifySubscriptionChanged(phoneId);
- ts.subscriptionMonitorMock.notifyDefaultSubscriptionChanged(phoneId);
waitABit();
if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
@@ -276,9 +273,10 @@
fail("test 4, LiveRequests != 1");
}
+ ts.phoneSwitcherMock.setPreferredDataPhoneId(altPhoneId);
ts.subscriptionControllerMock.setDefaultDataSubId(altSubId);
- ts.subscriptionMonitorMock.notifyDefaultSubscriptionChanged(phoneId);
- ts.subscriptionMonitorMock.notifyDefaultSubscriptionChanged(altPhoneId);
+ ts.phoneSwitcherMock.notifyActivePhoneChange(phoneId);
+
waitABit();
if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
fail("test 5, LiveRequests != 0");
@@ -311,9 +309,8 @@
}
ts.subscriptionControllerMock.setDefaultDataSubId(subId);
- ts.subscriptionMonitorMock.notifyDefaultSubscriptionChanged(phoneId);
- ts.subscriptionMonitorMock.notifyDefaultSubscriptionChanged(altPhoneId);
- ts.subscriptionMonitorMock.notifyDefaultSubscriptionChanged(phoneId);
+ ts.phoneSwitcherMock.setPreferredDataPhoneId(phoneId);
+ ts.phoneSwitcherMock.notifyActivePhoneChange(phoneId);
waitABit();
if (ts.dcTrackerMock.getNumberOfLiveRequests() != 3) {
fail("test 10, LiveRequests != 3");
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTest.java
new file mode 100644
index 0000000..694d610
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.emergency;
+
+import android.telephony.emergency.EmergencyNumber;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class EmergencyNumberTest extends TestCase {
+ public void testEmergencyNumberUnspecified() throws Exception {
+ EmergencyNumber number = new EmergencyNumber(
+ "911",
+ "us",
+ // EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED
+ 0,
+ // EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALLING
+ 1);
+ assertEquals(number.getNumber(), "911");
+ assertEquals(number.getCountryIso(), "us");
+ assertTrue(number.isInEmergencyServiceCategories(
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED));
+ assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE));
+ assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE));
+ assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE));
+ assertFalse(number.isInEmergencyServiceCategories(
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD));
+ assertFalse(number.isInEmergencyServiceCategories(
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE));
+ assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC));
+ assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC));
+ assertEquals(0, number.getEmergencyServiceCategoryBitmask());
+
+ List<Integer> categories = number.getEmergencyServiceCategories();
+ assertEquals(1, categories.size());
+ assertEquals(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ (int) categories.get(0));
+
+ assertTrue(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING));
+ assertFalse(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM));
+ assertFalse(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG));
+ assertFalse(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT));
+ assertEquals(1, number.getEmergencyNumberSourceBitmask());
+
+ List<Integer> sources = number.getEmergencyNumberSources();
+ assertEquals(1, sources.size());
+ assertEquals(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ (int) sources.get(0));
+ }
+
+ public void testEmergencyNumberSpecificService() throws Exception {
+ EmergencyNumber number = new EmergencyNumber(
+ "911",
+ "us",
+ // EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD
+ 8,
+ // EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALLING
+ // EMERGENCY_NUMBER_SOURCE_MODEM
+ 5);
+ assertEquals(number.getNumber(), "911");
+ assertEquals(number.getCountryIso(), "us");
+ assertFalse(number.isInEmergencyServiceCategories(
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED));
+ assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE));
+ assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE));
+ assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE));
+ assertTrue(number.isInEmergencyServiceCategories(
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD));
+ assertFalse(number.isInEmergencyServiceCategories(
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE));
+ assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC));
+ assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC));
+ assertEquals(8, number.getEmergencyServiceCategoryBitmask());
+
+ List<Integer> categories = number.getEmergencyServiceCategories();
+ assertEquals(1, categories.size());
+ assertEquals(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD,
+ (int) categories.get(0));
+
+ assertTrue(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING));
+ assertFalse(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM));
+ assertTrue(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG));
+ assertFalse(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT));
+ assertEquals(5, number.getEmergencyNumberSourceBitmask());
+
+ List<Integer> sources = number.getEmergencyNumberSources();
+ assertEquals(2, sources.size());
+ Collections.sort(sources);
+ List<Integer> sourcesToVerify = new ArrayList<Integer>();
+ sourcesToVerify.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
+ sourcesToVerify.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
+ Collections.sort(sourcesToVerify);
+ assertTrue(sourcesToVerify.equals(sources));
+ }
+
+ public void testEmergencyNumberMultipleServices() throws Exception {
+ EmergencyNumber number = new EmergencyNumber(
+ "110",
+ "jp",
+ // EMERGENCY_SERVICE_CATEGORY_POLICE
+ // EMERGENCY_SERVICE_CATEGORY_AMBULANCE
+ // EMERGENCY_SERVICE_CATEGORY_MIEC
+ 35,
+ // EMERGENCY_NUMBER_SOURCE_NETWORK_SINGALING
+ // EMERGENCY_NUMBER_SOURCE_SIM
+ // EMERGENCY_NUMBER_SOURCE_DEFAULT
+ 11);
+ assertEquals(number.getNumber(), "110");
+ assertEquals(number.getCountryIso(), "jp");
+ assertFalse(number.isInEmergencyServiceCategories(
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED));
+ assertTrue(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE));
+ assertTrue(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE));
+ assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE));
+ assertFalse(number.isInEmergencyServiceCategories(
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD));
+ assertFalse(number.isInEmergencyServiceCategories(
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE));
+ assertTrue(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC));
+ assertFalse(number.isInEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC));
+ assertEquals(35, number.getEmergencyServiceCategoryBitmask());
+
+ List<Integer> categories = number.getEmergencyServiceCategories();
+ assertEquals(3, categories.size());
+ Collections.sort(categories);
+ List<Integer> categoriesToVerify = new ArrayList<Integer>();
+ categoriesToVerify.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE);
+ categoriesToVerify.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE);
+ categoriesToVerify.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC);
+ Collections.sort(categoriesToVerify);
+ assertTrue(categoriesToVerify.equals(categories));
+
+ assertTrue(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING));
+ assertTrue(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM));
+ assertFalse(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG));
+ assertTrue(number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT));
+ assertEquals(11, number.getEmergencyNumberSourceBitmask());
+
+ List<Integer> sources = number.getEmergencyNumberSources();
+ assertEquals(3, sources.size());
+ Collections.sort(sources);
+ List<Integer> sourcesToVerify = new ArrayList<Integer>();
+ sourcesToVerify.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
+ sourcesToVerify.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM);
+ sourcesToVerify.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT);
+ Collections.sort(sourcesToVerify);
+ assertTrue(sourcesToVerify.equals(sources));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index 851a217..1e29908 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -570,6 +570,10 @@
verify(mContext, never()).sendBroadcast(any(Intent.class));
// verify there's only 1 of the segments in the db (other should be discarded as dup)
assertEquals(1, mContentProvider.getNumRows());
+ // verify the first one is discarded, and second message is present in the db
+ Cursor c = mContentProvider.query(sRawUri, null, null, null, null);
+ c.moveToFirst();
+ assertEquals(mMessageBodyPart2, c.getString(c.getColumnIndex("message_body")));
// State machine should go back to idle
assertEquals("IdleState", getCurrentState().getName());
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index bad56e7..759309c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -515,8 +515,16 @@
@Test
@SmallTest
public void testImsMOCallDial() {
+ startOutgoingCall();
+ //call established
+ mImsCallListener.onCallProgressing(mSecondImsCall);
+ assertEquals(Call.State.ALERTING, mCTUT.mForegroundCall.getState());
+ }
+
+ private void startOutgoingCall() {
assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState());
assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
+
try {
mCTUT.dial("+17005554141", ImsCallProfile.CALL_TYPE_VOICE, null);
verify(mImsManager, times(1)).makeCall(eq(mImsCallProfile),
@@ -527,9 +535,6 @@
}
assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
assertEquals(Call.State.DIALING, mCTUT.mForegroundCall.getState());
- //call established
- mImsCallListener.onCallProgressing(mSecondImsCall);
- assertEquals(Call.State.ALERTING, mCTUT.mForegroundCall.getState());
}
@FlakyTest
@@ -846,5 +851,88 @@
mCTUT.getDisconnectCauseFromReasonInfo(
new ImsReasonInfo(ImsReasonInfo.CODE_SIP_NOT_FOUND, 0), Call.State.ACTIVE));
}
+
+ @Test
+ @SmallTest
+ public void testCantMakeCallWhileRinging() {
+ testImsMTCall();
+ try {
+ mCTUT.dial("6505551212", VideoProfile.STATE_AUDIO_ONLY, new Bundle());
+ } catch (CallStateException e) {
+ // We expect a call state exception!
+ assertEquals(CallStateException.ERROR_CALL_RINGING, e.getError());
+ return;
+ }
+ Assert.fail("Expected CallStateException");
+ }
+
+ @Test
+ @SmallTest
+ public void testCantMakeCallWhileDialing() {
+ startOutgoingCall();
+ try {
+ mCTUT.dial("6505551212", VideoProfile.STATE_AUDIO_ONLY, new Bundle());
+ } catch (CallStateException e) {
+ // We expect a call state exception!
+ assertEquals(CallStateException.ERROR_ALREADY_DIALING, e.getError());
+ return;
+ }
+ Assert.fail("Expected CallStateException");
+ }
+
+ @Test
+ @SmallTest
+ public void testCantMakeCallTooMany() {
+ // Place a call.
+ placeCallAndMakeActive();
+
+ // Place another call
+ placeCallAndMakeActive();
+
+ // Finally, dial a third.
+ try {
+ mCTUT.dial("6505551212", VideoProfile.STATE_AUDIO_ONLY, new Bundle());
+ } catch (CallStateException e) {
+ // We expect a call state exception!
+ assertEquals(CallStateException.ERROR_TOO_MANY_CALLS, e.getError());
+ return;
+ }
+ Assert.fail("Expected CallStateException");
+ }
+
+ private void placeCallAndMakeActive() {
+ try {
+ doAnswer(new Answer<ImsCall>() {
+ @Override
+ public ImsCall answer(InvocationOnMock invocation) throws Throwable {
+ mImsCallListener =
+ (ImsCall.Listener) invocation.getArguments()[2];
+ ImsCall imsCall = spy(new ImsCall(mContext, mImsCallProfile));
+ imsCall.setListener(mImsCallListener);
+ imsCallMocking(imsCall);
+ return imsCall;
+ }
+ }).when(mImsManager).makeCall(eq(mImsCallProfile), (String[]) any(),
+ (ImsCall.Listener) any());
+ } catch (ImsException ie) {
+ }
+
+ ImsPhoneConnection connection = null;
+ try {
+ connection = (ImsPhoneConnection) mCTUT.dial("+16505551212",
+ ImsCallProfile.CALL_TYPE_VOICE, null);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ Assert.fail("unexpected exception thrown" + ex.getMessage());
+ }
+ if (connection == null) {
+ Assert.fail("connection is null");
+ }
+ ImsCall imsCall = connection.getImsCall();
+ imsCall.getImsCallSessionListenerProxy().callSessionProgressing(imsCall.getSession(),
+ new ImsStreamMediaProfile());
+ imsCall.getImsCallSessionListenerProxy().callSessionStarted(imsCall.getSession(),
+ new ImsCallProfile());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
index e5bad6d..97e6691 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -28,7 +28,9 @@
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.nullable;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -58,6 +60,7 @@
import com.android.ims.ImsManager;
import com.android.ims.ImsUtInterface;
import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
@@ -303,10 +306,11 @@
assertEquals(true, mImsPhoneUT.canConference());
verify(mImsCT, times(2)).canConference();
- assertEquals(false, mImsPhoneUT.canDial());
- doReturn(true).when(mImsCT).canDial();
+ doNothing().when(mImsCT).checkForDialIssues();
assertEquals(true, mImsPhoneUT.canDial());
- verify(mImsCT, times(2)).canDial();
+ doThrow(CallStateException.class).when(mImsCT).checkForDialIssues();
+ assertEquals(false, mImsPhoneUT.canDial());
+ verify(mImsCT, times(2)).checkForDialIssues();
mImsPhoneUT.conference();
verify(mImsCT).conference();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneSwitcherMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneSwitcherMock.java
index ca7e680..7f995f9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneSwitcherMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneSwitcherMock.java
@@ -77,7 +77,15 @@
public void setPhoneActive(int phoneId, boolean active) {
validatePhoneId(phoneId);
if (mIsActive[phoneId].getAndSet(active) != active) {
- mActivePhoneRegistrants[phoneId].notifyRegistrants();
+ notifyActivePhoneChange(phoneId);
}
}
+
+ public void setPreferredDataPhoneId(int preferredDataPhoneId) {
+ mPreferredDataPhoneId = preferredDataPhoneId;
+ }
+
+ public void notifyActivePhoneChange(int phoneId) {
+ mActivePhoneRegistrants[phoneId].notifyRegistrants();
+ }
}
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 1697abd..beeba62 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
@@ -202,10 +202,6 @@
}
@Override
public int getPhoneId(int subId) {
- if (subId == DEFAULT_SUBSCRIPTION_ID) {
- subId = getDefaultSubId();
- }
-
if (subId <= INVALID_SUBSCRIPTION_ID) return INVALID_PHONE_INDEX;
for (int i = 0; i < mSlotIndexToSubId.length; i++) {
@@ -259,7 +255,7 @@
}
@Override
public boolean isActiveSubId(int subId) {
- throw new RuntimeException("not implemented");
+ return getPhoneId(subId) != INVALID_PHONE_INDEX;
}
@Override
public int getSimStateForSlotIndex(int slotIndex) {