blob: b5ec3200ea50bf9969e512e32ad00697ce3441f4 [file] [log] [blame]
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.telephony;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.StringNetworkSpecifier;
import android.os.AsyncResult;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.telephony.Rlog;
import android.test.AndroidTestCase;
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 java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class PhoneSwitcherTest extends AndroidTestCase {
private final static String LOG_TAG = "PhoneSwitcherTest";
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 void waitABit() {
try {
Thread.sleep(250);
} catch (Exception e) {}
}
private String mTestName = "";
private void log(String str) {
Rlog.d(LOG_TAG + " " + mTestName, str);
}
private NetworkRequest makeSubSpecificDefaultRequest(ConnectivityServiceMock cs, int subId) {
NetworkCapabilities netCap = (new NetworkCapabilities()).
addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).
addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED).
addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
netCap.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(subId)));
return cs.requestNetwork(netCap, null, 0, new Binder(), -1);
}
private NetworkRequest makeSubSpecificMmsRequest(ConnectivityServiceMock cs, int subId) {
NetworkCapabilities netCap = (new NetworkCapabilities()).
addCapability(NetworkCapabilities.NET_CAPABILITY_MMS).
addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED).
addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
netCap.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(subId)));
return cs.requestNetwork(netCap, null, 0, new Binder(), -1);
}
private Context makeContext() {
final ContextFixture contextFixture = new ContextFixture();
String[] networkConfigString = getContext().getResources().getStringArray(
com.android.internal.R.array.networkAttributes);
contextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
networkConfigString);
return contextFixture.getTestDouble();
}
/**
* Test that a single phone case results in our phone being active and the RIL called
*/
@SmallTest
public void testRegister() throws Exception {
mTestName = "testRegister";
final int numPhones = 2;
final int maxActivePhones = 1;
final HandlerThread handlerThread = new HandlerThread("PhoneSwitcherTestThread");
handlerThread.start();
final ContextFixture contextFixture = new ContextFixture();
String[] networkConfigString = getContext().getResources().getStringArray(
com.android.internal.R.array.networkAttributes);
contextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
networkConfigString);
final Context contextMock = contextFixture.getTestDouble();
final ConnectivityServiceMock connectivityServiceMock =
new ConnectivityServiceMock(contextMock);
final ConnectivityManager cm =
new ConnectivityManager(contextMock, connectivityServiceMock);
contextFixture.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);
// 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");
connectivityServiceMock.addDefaultRequest();
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();
// not registered yet - shouldn't inc
if (testHandler.getActivePhoneSwitchCount() != 0) {
fail("pretest of ActivePhoneSwitchCount");
}
boolean threw = false;
try {
// should throw
phoneSwitcher.registerForActivePhoneSwitch(2, testHandler,
TestHandler.ACTIVE_PHONE_SWITCH, activePhoneSwitchObject);
} catch (IllegalArgumentException e) {
threw = true;
}
if (threw == false) fail("register with bad phoneId didn't throw");
phoneSwitcher.registerForActivePhoneSwitch(0, testHandler,
TestHandler.ACTIVE_PHONE_SWITCH,
activePhoneSwitchObject);
testHandler.blockTilIdle();
if (testHandler.getActivePhoneSwitchCount() != 1) {
fail("post register of ActivePhoneSwitchCount not 1!");
}
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");
subControllerMock.setSlotSubId(0, 0);
waitABit();
if (testHandler.getActivePhoneSwitchCount() != 2) {
fail("after mapping of 0 to 0, ActivePhoneSwitchCount not 2!");
}
if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
// now try various things that should cause the active phone to switch:
// 1 lose default via default sub change
// 2 gain default via default sub change
// 3 lose default via sub->phone change
// 4 gain default via sub->phone change
// 5 lose default network request
// 6 gain subscription-specific request
// 7 lose via sub->phone change
// 8 gain via sub->phone change
// 9 lose subscription-specific request
// 10 don't switch phones when in emergency mode
// 1 lose default via default sub change
subControllerMock.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);
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");
// 2 gain default via default sub change
subControllerMock.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");
// 3 lose default via sub->phone change
subControllerMock.setSlotSubId(0, 2);
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");
// 4 gain default via sub->phone change
subControllerMock.setSlotSubId(0, 0);
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");
// 5 lose default network request
connectivityServiceMock.removeDefaultRequest();
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");
// 6 gain subscription-specific request
NetworkRequest request = makeSubSpecificDefaultRequest(connectivityServiceMock, 0);
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");
// 7 lose via sub->phone change
subControllerMock.setSlotSubId(0, 1);
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");
// 8 gain via sub->phone change
subControllerMock.setSlotSubId(0, 0);
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");
// 9 lose subscription-specific request
connectivityServiceMock.releaseNetworkRequest(request);
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");
// 10 don't switch phones when in emergency mode
// not ready yet - Phone turns out to be hard to stub out
// phones[0].setInEmergencyCall(true);
// connectivityServiceMock.addDefaultRequest();
// 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");
//
// phones[0].setInEmergencyCall(false);
// connectivityServiceMock.addDefaultRequest();
// waitABit();
// if (testHandler.getActivePhoneSwitchCount() != 12) {
// fail("after release of request, ActivePhoneSwitchCount not 11!");
// }
// 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();
}
/**
* Test a multi-sim case with limited active phones:
* - lose default via default sub change
* - lose default via sub->phone change
* - gain default via sub->phone change
* - gain default via default sub change
* - lose default network request
* - gain subscription-specific request
* - lose via sub->phone change
* - gain via sub->phone change
* - lose subscription-specific request
* - tear down low priority phone when new request comes in
* - tear down low priority phone when sub change causes split
* - bring up low priority phone when sub change causes join
* - don't switch phones when in emergency mode
*/
@SmallTest
public void testPrioritization() throws Exception {
mTestName = "testPrioritization";
final int numPhones = 2;
final int maxActivePhones = 1;
final HandlerThread handlerThread = new HandlerThread("PhoneSwitcherTestThread");
handlerThread.start();
final ContextFixture contextFixture = new ContextFixture();
String[] networkConfigString = getContext().getResources().getStringArray(
com.android.internal.R.array.networkAttributes);
contextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
networkConfigString);
final Context contextMock = contextFixture.getTestDouble();
final ConnectivityServiceMock connectivityServiceMock =
new ConnectivityServiceMock(contextMock);
final ConnectivityManager cm =
new ConnectivityManager(contextMock, connectivityServiceMock);
contextFixture.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);
connectivityServiceMock.addDefaultRequest();
subControllerMock.setSlotSubId(0, 0);
subControllerMock.setSlotSubId(1, 1);
subControllerMock.setDefaultDataSubId(0);
waitABit();
phoneSwitcher.registerForActivePhoneSwitch(0, testHandler, TestHandler.ACTIVE_PHONE_SWITCH,
activePhoneSwitchObject);
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");
// now start a higher priority conneciton on the other sub
NetworkRequest request = makeSubSpecificMmsRequest(connectivityServiceMock, 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();
}
connectivityServiceMock.die();
testHandler.die();
handlerThread.quit();
}
/**
* Verify we don't send spurious DATA_ALLOWED calls when another NetworkFactory
* wins (ie, switch to wifi).
*/
@SmallTest
public void testHigherPriorityDefault() throws Exception {
mTestName = "testPrioritization";
final int numPhones = 2;
final int maxActivePhones = 1;
final HandlerThread handlerThread = new HandlerThread("PhoneSwitcherTestThread");
handlerThread.start();
final ContextFixture contextFixture = new ContextFixture();
String[] networkConfigString = getContext().getResources().getStringArray(
com.android.internal.R.array.networkAttributes);
contextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
networkConfigString);
final Context contextMock = contextFixture.getTestDouble();
final ConnectivityServiceMock connectivityServiceMock =
new ConnectivityServiceMock(contextMock);
final ConnectivityManager cm =
new ConnectivityManager(contextMock, connectivityServiceMock);
contextFixture.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);
connectivityServiceMock.addDefaultRequest();
subControllerMock.setSlotSubId(0, 0);
subControllerMock.setSlotSubId(1, 1);
subControllerMock.setDefaultDataSubId(0);
waitABit();
// Phone 0 should be active
if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
connectivityServiceMock.setCurrentScoreForRequest(connectivityServiceMock.defaultRequest,
100);
waitABit();
// should be no change
if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
connectivityServiceMock.setCurrentScoreForRequest(connectivityServiceMock.defaultRequest,
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();
}
connectivityServiceMock.die();
testHandler.die();
handlerThread.quit();
}
/**
* Test MSMA testing prioritiziation
* - leave multiple on (up to the limit)
* - tear down lowest priority phone when new request comes in
* - tear down low priority phone when sub change causes split
* - bring up low priority phone when sub change causes join
* - don't switch phones when in emergency mode
*/
}