diff options
author | android-build-prod (mdb) <android-build-team-robot@google.com> | 2018-10-12 02:16:13 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2018-10-12 02:16:13 +0000 |
commit | 47c16d0a9e35efe1995818700959c9eb27895a19 (patch) | |
tree | 514740a5cc90bf2f5b7497b9d42bd308cd936eff | |
parent | 59f6f2956f369bc8491ce78315ef1a5a95261878 (diff) | |
parent | 40e39f0c48696cc6ff5b7c5cbdc1f57a71b02efa (diff) | |
download | telephony-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
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()); } |