1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.telephony;
18
19import com.android.internal.telephony.MccTable;
20import com.android.internal.telephony.mocks.ConnectivityServiceMock;
21import com.android.internal.telephony.mocks.SubscriptionControllerMock;
22import com.android.internal.telephony.mocks.TelephonyRegistryMock;
23import com.android.internal.telephony.test.SimulatedCommands;
24
25import android.content.Context;
26import android.os.AsyncResult;
27import android.os.Binder;
28import android.os.Handler;
29import android.os.HandlerThread;
30import android.os.Looper;
31import android.os.Message;
32import android.net.ConnectivityManager;
33import android.net.IConnectivityManager;
34import android.net.NetworkCapabilities;
35import android.net.NetworkRequest;
36
37import android.test.AndroidTestCase;
38import android.test.suitebuilder.annotation.SmallTest;
39
40import android.telephony.Rlog;
41
42import java.util.concurrent.atomic.AtomicInteger;
43import java.util.concurrent.atomic.AtomicReference;
44
45public class PhoneSwitcherTest extends AndroidTestCase {
46    private final static String LOG_TAG = "PhoneSwitcherTest";
47
48    static void failAndStack(String str) {
49        fail(str + "\n" + SubscriptionMonitorTest.stack());
50    }
51
52    static String stack() {
53        StringBuilder sb = new StringBuilder();
54        for(StackTraceElement e : Thread.currentThread().getStackTrace()) {
55            sb.append(e.toString()).append("\n");
56        }
57        return sb.toString();
58    }
59
60    private static class TestHandler extends Handler {
61        public final static int ACTIVE_PHONE_SWITCH = 1;
62        public final static int IN_IDLE = 2;
63
64        HandlerThread handlerThread;
65
66        public TestHandler(Looper looper) {
67            super(looper);
68        }
69
70        public void die() {
71            if(handlerThread != null) {
72                handlerThread.quit();
73                handlerThread = null;
74            }
75        }
76
77        public void blockTilIdle() {
78            Object lock = new Object();
79            synchronized (lock) {
80                Message msg = this.obtainMessage(IN_IDLE, lock);
81                msg.sendToTarget();
82                try {
83                    lock.wait();
84                } catch (InterruptedException e) {}
85            }
86        }
87
88        public static TestHandler makeHandler() {
89            final HandlerThread handlerThread = new HandlerThread("TestHandler");
90            handlerThread.start();
91            final TestHandler result = new TestHandler(handlerThread.getLooper());
92            result.handlerThread = handlerThread;
93            return result;
94        }
95
96        private boolean objectEquals(Object o1, Object o2) {
97            if (o1 == null) return (o2 == null);
98            return o1.equals(o2);
99        }
100
101        private void failAndStack(String str) {
102            SubscriptionMonitorTest.failAndStack(str);
103        }
104
105        @Override
106        public void handleMessage(Message msg) {
107            switch (msg.what) {
108                case ACTIVE_PHONE_SWITCH: {
109                    AsyncResult ar = (AsyncResult)(msg.obj);
110                    if (objectEquals(ar.userObj, mActivePhoneSwitchObject.get()) == false) {
111                        failAndStack("Active Phone Switch object is incorrect!");
112                    }
113                    int count = mActivePhoneSwitchCount.incrementAndGet();
114                    Rlog.d(LOG_TAG, "ACTIVE_PHONE_SWITCH, inc to " + count);
115                    break;
116                }
117                case IN_IDLE: {
118                    Object lock = msg.obj;
119                    synchronized (lock) {
120                        lock.notify();
121                    }
122                    break;
123                }
124            }
125        }
126
127        private final AtomicInteger mActivePhoneSwitchCount = new AtomicInteger(0);
128        private final AtomicReference<Object> mActivePhoneSwitchObject =
129                new AtomicReference<Object>();
130
131        public void reset() {
132            mActivePhoneSwitchCount.set(0);
133            mActivePhoneSwitchObject.set(null);
134        }
135
136        public void setActivePhoneSwitchObject(Object o) {
137            mActivePhoneSwitchObject.set(o);
138        }
139
140        public int getActivePhoneSwitchCount() {
141            return mActivePhoneSwitchCount.get();
142        }
143    }
144
145    private void waitABit() {
146        try {
147            Thread.sleep(250);
148        } catch (Exception e) {}
149    }
150
151    private String mTestName = "";
152
153    private void log(String str) {
154        Rlog.d(LOG_TAG + " " + mTestName, str);
155    }
156
157    private NetworkRequest makeSubSpecificDefaultRequest(ConnectivityServiceMock cs, int subId) {
158        NetworkCapabilities netCap = (new NetworkCapabilities()).
159                addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).
160                addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED).
161                addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
162        netCap.setNetworkSpecifier(Integer.toString(subId));
163        return cs.requestNetwork(netCap, null, 0, new Binder(), -1);
164    }
165
166    private NetworkRequest makeSubSpecificMmsRequest(ConnectivityServiceMock cs, int subId) {
167        NetworkCapabilities netCap = (new NetworkCapabilities()).
168                addCapability(NetworkCapabilities.NET_CAPABILITY_MMS).
169                addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED).
170                addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
171        netCap.setNetworkSpecifier(Integer.toString(subId));
172        return cs.requestNetwork(netCap, null, 0, new Binder(), -1);
173    }
174
175    private Context makeContext() {
176        final ContextFixture contextFixture = new ContextFixture();
177        String[] networkConfigString = getContext().getResources().getStringArray(
178                com.android.internal.R.array.networkAttributes);
179        contextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
180                networkConfigString);
181        return contextFixture.getTestDouble();
182    }
183
184    /**
185     * Test that a single phone case results in our phone being active and the RIL called
186     */
187    @SmallTest
188    public void testRegister() throws Exception {
189        mTestName = "testRegister";
190        final int numPhones = 2;
191        final int maxActivePhones = 1;
192        final HandlerThread handlerThread = new HandlerThread("PhoneSwitcherTestThread");
193        handlerThread.start();
194        final ContextFixture contextFixture = new ContextFixture();
195        String[] networkConfigString = getContext().getResources().getStringArray(
196                com.android.internal.R.array.networkAttributes);
197        contextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
198                networkConfigString);
199        final Context contextMock = contextFixture.getTestDouble();
200        final ConnectivityServiceMock connectivityServiceMock =
201                new ConnectivityServiceMock(contextMock);
202        final ConnectivityManager cm =
203                new ConnectivityManager(contextMock, connectivityServiceMock);
204        contextFixture.setSystemService(Context.CONNECTIVITY_SERVICE, cm);
205        final ITelephonyRegistry.Stub telRegistryMock = new TelephonyRegistryMock();
206        final SubscriptionControllerMock subControllerMock =
207                new SubscriptionControllerMock(contextMock, telRegistryMock, numPhones);
208        final SimulatedCommands[] commandsInterfaces = new SimulatedCommands[numPhones];
209        final PhoneMock[] phones = new PhoneMock[numPhones];
210        for (int i = 0; i < numPhones; i++) {
211            commandsInterfaces[i] = new SimulatedCommands();
212        //    phones[i] = new PhoneMock(contextMock, commandsInterfaces[i]);
213        }
214
215        PhoneSwitcher phoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones,
216                contextMock, subControllerMock, handlerThread.getLooper(), telRegistryMock,
217                commandsInterfaces, phones);
218
219        // verify nothing has been done while there are no inputs
220        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed initially");
221        if (phoneSwitcher.isPhoneActive(0))        fail("phone active initially");
222
223        connectivityServiceMock.addDefaultRequest();
224        waitABit();
225
226        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed after request");
227        if (phoneSwitcher.isPhoneActive(0))        fail("phone active after request");
228
229        TestHandler testHandler = TestHandler.makeHandler();
230        Object activePhoneSwitchObject = new Object();
231        testHandler.setActivePhoneSwitchObject(activePhoneSwitchObject);
232
233        testHandler.blockTilIdle();
234
235        // not registered yet - shouldn't inc
236        if (testHandler.getActivePhoneSwitchCount() != 0) {
237            fail("pretest of ActivePhoneSwitchCount");
238        }
239        boolean threw = false;
240        try {
241            // should throw
242            phoneSwitcher.registerForActivePhoneSwitch(2, testHandler,
243                    TestHandler.ACTIVE_PHONE_SWITCH, activePhoneSwitchObject);
244        } catch (IllegalArgumentException e) {
245            threw = true;
246        }
247        if (threw == false) fail("register with bad phoneId didn't throw");
248
249        phoneSwitcher.registerForActivePhoneSwitch(0, testHandler,
250                TestHandler.ACTIVE_PHONE_SWITCH,
251                activePhoneSwitchObject);
252        testHandler.blockTilIdle();
253
254        if (testHandler.getActivePhoneSwitchCount() != 1) {
255            fail("post register of ActivePhoneSwitchCount not 1!");
256        }
257
258        subControllerMock.setDefaultDataSubId(0);
259        testHandler.blockTilIdle();
260
261        if (testHandler.getActivePhoneSwitchCount() != 1) {
262            fail("after set of default to 0, ActivePhoneSwitchCount not 1!");
263        }
264        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
265
266        subControllerMock.setSlotSubId(0, 0);
267        waitABit();
268
269        if (testHandler.getActivePhoneSwitchCount() != 2) {
270            fail("after mapping of 0 to 0, ActivePhoneSwitchCount not 2!");
271        }
272        if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
273
274        // now try various things that should cause the active phone to switch:
275        // 1 lose default via default sub change
276        // 2 gain default via default sub change
277        // 3 lose default via sub->phone change
278        // 4 gain default via sub->phone change
279        // 5 lose default network request
280        // 6 gain subscription-specific request
281        // 7 lose via sub->phone change
282        // 8 gain via sub->phone change
283        // 9 lose subscription-specific request
284        // 10 don't switch phones when in emergency mode
285
286        // 1 lose default via default sub change
287        subControllerMock.setDefaultDataSubId(1);
288        waitABit();
289        if (testHandler.getActivePhoneSwitchCount() != 3) {
290            fail("after set of default to 1, ActivePhoneSwitchCount not 3!");
291        }
292        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
293
294        subControllerMock.setSlotSubId(1, 1);
295        waitABit();
296        if (testHandler.getActivePhoneSwitchCount() != 3) {
297            fail("after mapping of 1 to 1, ActivePhoneSwitchCount not 3!");
298        }
299        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
300        if (commandsInterfaces[1].isDataAllowed() == false) fail("data not allowed");
301
302        // 2 gain default via default sub change
303        subControllerMock.setDefaultDataSubId(0);
304        waitABit();
305        if (testHandler.getActivePhoneSwitchCount() != 4) {
306            fail("after set of default to 0, ActivePhoneSwitchCount not 4!");
307        }
308        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
309        if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
310
311        // 3 lose default via sub->phone change
312        subControllerMock.setSlotSubId(0, 2);
313        waitABit();
314
315        if (testHandler.getActivePhoneSwitchCount() != 5) {
316            fail("after mapping of 0 to 2, ActivePhoneSwitchCount not 5!");
317        }
318        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
319        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
320
321        // 4 gain default via sub->phone change
322        subControllerMock.setSlotSubId(0, 0);
323        waitABit();
324        if (testHandler.getActivePhoneSwitchCount() != 6) {
325            fail("after mapping of 0 to 0, ActivePhoneSwitchCount not 6!");
326        }
327        if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
328        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
329
330        // 5 lose default network request
331        connectivityServiceMock.removeDefaultRequest();
332        waitABit();
333        if (testHandler.getActivePhoneSwitchCount() != 7) {
334            fail("after loss of network request, ActivePhoneSwitchCount not 7!");
335        }
336        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
337        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
338
339        // 6 gain subscription-specific request
340        NetworkRequest request = makeSubSpecificDefaultRequest(connectivityServiceMock, 0);
341        waitABit();
342        if (testHandler.getActivePhoneSwitchCount() != 8) {
343            fail("after gain of network request, ActivePhoneSwitchCount not 8!");
344        }
345        if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
346        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
347
348        // 7 lose via sub->phone change
349        subControllerMock.setSlotSubId(0, 1);
350        waitABit();
351        if (testHandler.getActivePhoneSwitchCount() != 9) {
352            fail("after loss of request due to subId map change, ActivePhoneSwitchCount not 9!");
353        }
354        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
355        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
356
357        // 8 gain via sub->phone change
358        subControllerMock.setSlotSubId(0, 0);
359        waitABit();
360        if (testHandler.getActivePhoneSwitchCount() != 10) {
361            fail("after gain of request due to subId map change, ActivePhoneSwitchCount not 10!");
362        }
363        if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
364        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
365
366        // 9 lose subscription-specific request
367        connectivityServiceMock.releaseNetworkRequest(request);
368        waitABit();
369        if (testHandler.getActivePhoneSwitchCount() != 11) {
370            fail("after release of request, ActivePhoneSwitchCount not 11!");
371        }
372        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
373        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
374
375        // 10 don't switch phones when in emergency mode
376        // not ready yet - Phone turns out to be hard to stub out
377//        phones[0].setInEmergencyCall(true);
378//        connectivityServiceMock.addDefaultRequest();
379//        waitABit();
380//        if (testHandler.getActivePhoneSwitchCount() != 11) {
381//            fail("after release of request, ActivePhoneSwitchCount not 11!");
382//        }
383//        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
384//        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
385//
386//        phones[0].setInEmergencyCall(false);
387//        connectivityServiceMock.addDefaultRequest();
388//        waitABit();
389//        if (testHandler.getActivePhoneSwitchCount() != 12) {
390//            fail("after release of request, ActivePhoneSwitchCount not 11!");
391//        }
392//        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
393//        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
394        testHandler.die();
395        handlerThread.quit();
396    }
397
398    /**
399     * Test a multi-sim case with limited active phones:
400     * - lose default via default sub change
401     * - lose default via sub->phone change
402     * - gain default via sub->phone change
403     * - gain default via default sub change
404     * - lose default network request
405     * - gain subscription-specific request
406     * - lose via sub->phone change
407     * - gain via sub->phone change
408     * - lose subscription-specific request
409     * - tear down low priority phone when new request comes in
410     * - tear down low priority phone when sub change causes split
411     * - bring up low priority phone when sub change causes join
412     * - don't switch phones when in emergency mode
413     */
414    @SmallTest
415    public void testPrioritization() throws Exception {
416        mTestName = "testPrioritization";
417        final int numPhones = 2;
418        final int maxActivePhones = 1;
419        final HandlerThread handlerThread = new HandlerThread("PhoneSwitcherTestThread");
420        handlerThread.start();
421        final ContextFixture contextFixture = new ContextFixture();
422        String[] networkConfigString = getContext().getResources().getStringArray(
423                com.android.internal.R.array.networkAttributes);
424        contextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
425                networkConfigString);
426        final Context contextMock = contextFixture.getTestDouble();
427        final ConnectivityServiceMock connectivityServiceMock =
428            new ConnectivityServiceMock(contextMock);
429        final ConnectivityManager cm =
430                new ConnectivityManager(contextMock, connectivityServiceMock);
431        contextFixture.setSystemService(Context.CONNECTIVITY_SERVICE, cm);
432        final ITelephonyRegistry.Stub telRegistryMock = new TelephonyRegistryMock();
433        final SubscriptionControllerMock subControllerMock =
434                new SubscriptionControllerMock(contextMock, telRegistryMock, numPhones);
435        final SimulatedCommands[] commandsInterfaces = new SimulatedCommands[numPhones];
436        final PhoneMock[] phones = new PhoneMock[numPhones];
437        for (int i = 0; i < numPhones; i++) {
438            commandsInterfaces[i] = new SimulatedCommands();
439        }
440
441        PhoneSwitcher phoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones,
442                contextMock, subControllerMock, handlerThread.getLooper(), telRegistryMock,
443                commandsInterfaces, phones);
444
445        TestHandler testHandler = TestHandler.makeHandler();
446        Object activePhoneSwitchObject = new Object();
447        testHandler.setActivePhoneSwitchObject(activePhoneSwitchObject);
448
449        connectivityServiceMock.addDefaultRequest();
450        subControllerMock.setSlotSubId(0, 0);
451        subControllerMock.setSlotSubId(1, 1);
452        subControllerMock.setDefaultDataSubId(0);
453        waitABit();
454        phoneSwitcher.registerForActivePhoneSwitch(0, testHandler, TestHandler.ACTIVE_PHONE_SWITCH,
455                activePhoneSwitchObject);
456        waitABit();
457        // verify initial conditions
458        if (testHandler.getActivePhoneSwitchCount() != 1) {
459            fail("Initial conditions not met: ActivePhoneSwitchCount not 1! " +
460                    testHandler.getActivePhoneSwitchCount());
461        }
462        if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
463        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
464
465        // now start a higher priority conneciton on the other sub
466        NetworkRequest request = makeSubSpecificMmsRequest(connectivityServiceMock, 1);
467        waitABit();
468        if (testHandler.getActivePhoneSwitchCount() != 2) {
469            fail("after gain of network request, ActivePhoneSwitchCount not 2!");
470        }
471        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
472        if (commandsInterfaces[1].isDataAllowed() == false) fail("data not allowed");
473
474        testHandler.die();
475        handlerThread.quit();
476    }
477
478    /**
479     * Verify we don't send spurious DATA_ALLOWED calls when another NetworkFactory
480     * wins (ie, switch to wifi).
481     */
482    @SmallTest
483    public void testHigherPriorityDefault() throws Exception {
484        mTestName = "testPrioritization";
485        final int numPhones = 2;
486        final int maxActivePhones = 1;
487        final HandlerThread handlerThread = new HandlerThread("PhoneSwitcherTestThread");
488        handlerThread.start();
489        final ContextFixture contextFixture = new ContextFixture();
490        String[] networkConfigString = getContext().getResources().getStringArray(
491                com.android.internal.R.array.networkAttributes);
492        contextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
493                networkConfigString);
494        final Context contextMock = contextFixture.getTestDouble();
495        final ConnectivityServiceMock connectivityServiceMock =
496                new ConnectivityServiceMock(contextMock);
497        final ConnectivityManager cm =
498                new ConnectivityManager(contextMock, connectivityServiceMock);
499        contextFixture.setSystemService(Context.CONNECTIVITY_SERVICE, cm);
500        final ITelephonyRegistry.Stub telRegistryMock = new TelephonyRegistryMock();
501        final SubscriptionControllerMock subControllerMock =
502                new SubscriptionControllerMock(contextMock, telRegistryMock, numPhones);
503        final SimulatedCommands[] commandsInterfaces = new SimulatedCommands[numPhones];
504        final PhoneMock[] phones = new PhoneMock[numPhones];
505        for (int i = 0; i < numPhones; i++) {
506            commandsInterfaces[i] = new SimulatedCommands();
507        }
508
509        PhoneSwitcher phoneSwitcher = new PhoneSwitcher(maxActivePhones, numPhones,
510                contextMock, subControllerMock, handlerThread.getLooper(), telRegistryMock,
511                commandsInterfaces, phones);
512
513        TestHandler testHandler = TestHandler.makeHandler();
514        Object activePhoneSwitchObject = new Object();
515        testHandler.setActivePhoneSwitchObject(activePhoneSwitchObject);
516
517        connectivityServiceMock.addDefaultRequest();
518        subControllerMock.setSlotSubId(0, 0);
519        subControllerMock.setSlotSubId(1, 1);
520        subControllerMock.setDefaultDataSubId(0);
521        waitABit();
522
523        // Phone 0 should be active
524        if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
525        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
526
527        connectivityServiceMock.setCurrentScoreForRequest(connectivityServiceMock.defaultRequest,
528                100);
529        waitABit();
530
531        // should be no change
532        if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
533        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
534
535        connectivityServiceMock.setCurrentScoreForRequest(connectivityServiceMock.defaultRequest,
536                0);
537        waitABit();
538
539        // should be no change
540        if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
541        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
542
543        testHandler.die();
544        handlerThread.quit();
545    }
546
547    /**
548     * Test MSMA testing prioritiziation
549     * - leave multiple on (up to the limit)
550     * - tear down lowest priority phone when new request comes in
551     * - tear down low priority phone when sub change causes split
552     * - bring up low priority phone when sub change causes join
553     * - don't switch phones when in emergency mode
554     */
555
556}
557