aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-prod (mdb) <android-build-team-robot@google.com>2018-10-12 02:16:13 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2018-10-12 02:16:13 +0000
commit47c16d0a9e35efe1995818700959c9eb27895a19 (patch)
tree514740a5cc90bf2f5b7497b9d42bd308cd936eff
parent59f6f2956f369bc8491ce78315ef1a5a95261878 (diff)
parent40e39f0c48696cc6ff5b7c5cbdc1f57a71b02efa (diff)
downloadtelephony-master-cuttlefish-testing-release.tar.gz
Merge "Snap for 5061196 from b657863b70bd57803fc1c67109046c6fe30457b6 to master-cuttlefish-testing-release" into master-cuttlefish-testing-releasemaster-cuttlefish-testing-release
-rwxr-xr-xsrc/java/com/android/internal/telephony/GsmCdmaCallTracker.java3
-rw-r--r--src/java/com/android/internal/telephony/InboundSmsHandler.java128
-rw-r--r--src/java/com/android/internal/telephony/InboundSmsTracker.java85
-rw-r--r--src/java/com/android/internal/telephony/PhoneFactory.java12
-rw-r--r--src/java/com/android/internal/telephony/RadioResponse.java69
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java2
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java755
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java100
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java4
9 files changed, 629 insertions, 529 deletions
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index 2b399bab11..025e31c0f5 100755
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -617,11 +617,10 @@ public class GsmCdmaCallTracker extends CallTracker {
* @throws CallStateException
*/
public void checkForDialIssues() throws CallStateException {
- int serviceState = mPhone.getServiceState().getState();
String disableCall = SystemProperties.get(
TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
- if (serviceState == ServiceState.STATE_POWER_OFF) {
+ if (!mCi.getRadioState().isOn()) {
throw new CallStateException(CallStateException.ERROR_POWER_OFF,
"Modem not powered");
}
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index b7792992dd..b2c3e898c3 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.SubscriptionManager;
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 @@ import java.util.Map;
*/
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 abstract class InboundSmsHandler extends StateMachine {
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=?";
@@ -1183,55 +1193,47 @@ public abstract class InboundSmsHandler extends StateMachine {
}
/**
- * 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 +1241,49 @@ public abstract class InboundSmsHandler extends StateMachine {
}
}
+ // 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 +1297,7 @@ public abstract class InboundSmsHandler extends StateMachine {
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 36c699610d..50c84c86a9 100644
--- a/src/java/com/android/internal/telephony/InboundSmsTracker.java
+++ b/src/java/com/android/internal/telephony/InboundSmsTracker.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony;
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 @@ public class InboundSmsTracker {
+ "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 @@ public class InboundSmsTracker {
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 @@ public class InboundSmsTracker {
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 4c16f272de..b5eb9a7508 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.imsphone.ImsPhoneFactory;
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;
@@ -458,17 +457,6 @@ public class PhoneFactory {
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/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index 0fc527903b..dda8b605bc 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -1313,37 +1313,36 @@ public class RadioResponse extends IRadioResponse.Stub {
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 class RadioResponse extends IRadioResponse.Stub {
*/
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/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index c671f4fe26..6313633cc3 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 class ContextFixture implements TestFixture<Context> {
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/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
index 23af2e511b..4f450d0612 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
@@ -16,47 +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.reset;
+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.IConnectivityManager;
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 org.mockito.Mock;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-
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",
@@ -64,110 +55,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
"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());
- }
-
- static String stack() {
- StringBuilder sb = new StringBuilder();
- for(StackTraceElement e : Thread.currentThread().getStackTrace()) {
- sb.append(e.toString()).append("\n");
- }
- return sb.toString();
- }
-
- 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 static final int ACTIVE_PHONE_SWITCH = 1;
- private void waitABit() {
- try {
- Thread.sleep(250);
- } catch (Exception e) {}
- }
-
- private String mTestName = "";
@Mock
private ITelephonyRegistry.Stub mTelRegistryMock;
@Mock
@@ -175,35 +64,21 @@ public class PhoneSwitcherTest extends TelephonyTest {
@Mock
private CommandsInterface mCommandsInterface1;
@Mock
- private IConnectivityManager.Stub mConnectivityService;
-
-
- private void log(String str) {
- Rlog.d(LOG_TAG + " " + mTestName, str);
- }
-
- private NetworkRequest makeDefaultRequest(ConnectivityServiceMock cs, Integer subId) {
- 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)));
- }
- return cs.requestNetwork(netCap, null, 0, new Binder(), -1);
- }
-
- private NetworkRequest makeMmsRequest(ConnectivityServiceMock cs, Integer 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)));
- if (subId != null) {
- netCap.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(subId)));
- }
- return cs.requestNetwork(netCap, null, 0, new Binder(), -1);
- }
+ private Phone mPhone2; // mPhone as phone 1 is already defined in TelephonyTest.
+ @Mock
+ private Handler mActivePhoneSwitchHandler;
+
+ // 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 {
@@ -221,87 +96,51 @@ public class PhoneSwitcherTest extends TelephonyTest {
@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);
+
+ verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+ assertFalse("data allowed", mDataAllowed[0]);
- subControllerMock.setSlotSubId(0, 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
@@ -316,93 +155,87 @@ public class PhoneSwitcherTest extends TelephonyTest {
// 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 = makeDefaultRequest(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
@@ -424,13 +257,7 @@ public class PhoneSwitcherTest extends TelephonyTest {
// 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();
}
/**
@@ -452,68 +279,34 @@ public class PhoneSwitcherTest extends TelephonyTest {
@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();
- }
-
- PhoneSwitcher phoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones,
- contextMock, subControllerMock, handlerThread.getLooper(), telRegistryMock,
- commandsInterfaces, phones);
-
- TestHandler testHandler = TestHandler.makeHandler();
- Object activePhoneSwitchObject = new Object();
- testHandler.setActivePhoneSwitchObject(activePhoneSwitchObject);
+ initialize(numPhones, maxActivePhones);
- 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 = makeMmsRequest(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();
}
/**
@@ -523,69 +316,35 @@ public class PhoneSwitcherTest extends TelephonyTest {
@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");
-
- for (int i = 0; i < numPhones; i++) {
- commandsInterfaces[i].dispose();
- }
+ assertTrue("data not allowed", mDataAllowed[0]);
+ assertFalse("data allowed", mDataAllowed[1]);
- connectivityServiceMock.die();
- testHandler.die();
- handlerThread.quit();
+ mHandlerThread.quit();
}
/**
@@ -597,70 +356,234 @@ public class PhoneSwitcherTest extends TelephonyTest {
@Test
@SmallTest
public void testSetPreferredData() throws Exception {
- mTestName = "testSetPreferredData";
final int numPhones = 2;
final int maxActivePhones = 1;
- mContextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
- sNetworkAttributes);
- final CommandsInterface[] commandsInterfaces = new CommandsInterface[numPhones];
- commandsInterfaces[0] = mCommandsInterface0;
- commandsInterfaces[1] = mCommandsInterface1;
-
- final ConnectivityServiceMock connectivityServiceMock =
- new ConnectivityServiceMock(mContext);
- final ConnectivityManager cm =
- new ConnectivityManager(mContext, connectivityServiceMock);
- mContextFixture.setSystemService(Context.CONNECTIVITY_SERVICE, cm);
- final HandlerThread handlerThread = new HandlerThread("PhoneSwitcherTestThread");
- final PhoneMock[] phones = new PhoneMock[numPhones];
+ 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.
- doReturn(1).when(mSubscriptionController)
- .getDefaultDataSubId();
- doReturn(1).when(mSubscriptionController)
- .getSubIdUsingPhoneId(0);
- doReturn(2).when(mSubscriptionController)
- .getSubIdUsingPhoneId(1);
- doReturn(true).when(mSubscriptionController)
- .isActiveSubId(1);
- doReturn(true).when(mSubscriptionController)
- .isActiveSubId(2);
-
- handlerThread.start();
-
- PhoneSwitcher phoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones,
- mContext, mSubscriptionController, handlerThread.getLooper(), mTelRegistryMock,
- commandsInterfaces, phones);
+ setSlotIndexToSubId(0, 1);
+ setSlotIndexToSubId(1, 2);
+ setDefaultDataSubId(1);
// Notify phoneSwitcher about default data sub and default network request.
- sendDefaultDataSubChanged();
- NetworkRequest request = makeDefaultRequest(connectivityServiceMock, null);
+ addInternetNetworkRequest(null, 50);
waitABit();
// Phone 0 (sub 1) should be activated as it has default data sub.
- verify(mCommandsInterface0).setDataAllowed(eq(true), any());
- reset(mCommandsInterface0);
+ assertTrue(mDataAllowed[0]);
// Set sub 2 as preferred sub should make phone 1 activated and phone 0 deactivated.
- phoneSwitcher.setPreferredData(2);
+ mPhoneSwitcher.setPreferredData(2);
waitABit();
- verify(mCommandsInterface0).setDataAllowed(eq(false), any());
- verify(mCommandsInterface1).setDataAllowed(eq(true), any());
- reset(mCommandsInterface0);
- reset(mCommandsInterface1);
+ assertFalse(mDataAllowed[0]);
+ assertTrue(mDataAllowed[1]);
// Unset preferred sub should make default data sub (phone 0 / sub 1) activated again.
- phoneSwitcher.setPreferredData(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+ mPhoneSwitcher.setPreferredData(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
waitABit();
- verify(mCommandsInterface0, times(1)).setDataAllowed(eq(true), any());
- verify(mCommandsInterface1, times(1)).setDataAllowed(eq(false), any());
+ assertTrue(mDataAllowed[0]);
+ assertFalse(mDataAllowed[1]);
- handlerThread.quit();
+ 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/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
new file mode 100644
index 0000000000..42a8ad1a74
--- /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/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index 851a2175c0..1e29908990 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 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
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());
}