• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 
17 package com.android;
18 
19 import static org.junit.Assert.assertNotNull;
20 import static org.mockito.Mockito.spy;
21 import static org.mockito.Mockito.doCallRealMethod;
22 import static org.mockito.Mockito.doReturn;
23 
24 import android.content.ContextWrapper;
25 import android.content.res.Resources;
26 import android.os.Handler;
27 import android.os.HandlerThread;
28 import android.os.Looper;
29 import android.os.TestLooperManager;
30 import android.util.Log;
31 
32 import androidx.test.InstrumentationRegistry;
33 
34 import com.android.internal.telephony.GsmCdmaPhone;
35 import com.android.internal.telephony.Phone;
36 import com.android.internal.telephony.PhoneConfigurationManager;
37 import com.android.internal.telephony.PhoneFactory;
38 import com.android.internal.telephony.data.DataConfigManager;
39 import com.android.internal.telephony.data.DataNetworkController;
40 import com.android.internal.telephony.metrics.MetricsCollector;
41 import com.android.internal.telephony.metrics.PersistAtomsStorage;
42 import com.android.internal.telephony.satellite.SatelliteController;
43 import com.android.phone.PhoneGlobals;
44 import com.android.phone.PhoneInterfaceManager;
45 
46 import org.junit.After;
47 import org.junit.Before;
48 import org.junit.Rule;
49 import org.mockito.Mock;
50 import org.mockito.Mockito;
51 import org.mockito.junit.MockitoJUnit;
52 import org.mockito.junit.MockitoRule;
53 
54 import java.lang.reflect.Field;
55 import java.util.Collections;
56 import java.util.HashMap;
57 import java.util.Iterator;
58 import java.util.LinkedList;
59 import java.util.concurrent.CountDownLatch;
60 import java.util.concurrent.Executor;
61 import java.util.concurrent.TimeUnit;
62 
63 /**
64  * Helper class to load Mockito Resources into Telephony unit tests.
65  */
66 public class TelephonyTestBase {
67     @Rule public final MockitoRule mocks = MockitoJUnit.rule();
68 
69     protected TestContext mContext;
70     @Mock protected PhoneGlobals mPhoneGlobals;
71     @Mock protected GsmCdmaPhone mPhone;
72     @Mock protected DataNetworkController mDataNetworkController;
73     @Mock private MetricsCollector mMetricsCollector;
74 
75     private HandlerThread mTestHandlerThread;
76     protected Looper mTestLooper;
77     protected TestLooperManager mLooperManager;
78 
79     private final HashMap<InstanceKey, Object> mOldInstances = new HashMap<>();
80     private final LinkedList<InstanceKey> mInstanceKeys = new LinkedList<>();
81 
82     @Before
setUp()83     public void setUp() throws Exception {
84         if (Looper.myLooper() == null) {
85             Looper.prepare();
86         }
87 
88         doCallRealMethod().when(mPhoneGlobals).getBaseContext();
89         doCallRealMethod().when(mPhoneGlobals).getResources();
90         doCallRealMethod().when(mPhoneGlobals).getSystemService(Mockito.anyString());
91         doCallRealMethod().when(mPhoneGlobals).getSystemService(Mockito.any(Class.class));
92         doCallRealMethod().when(mPhoneGlobals).getSystemServiceName(Mockito.any(Class.class));
93         doCallRealMethod().when(mPhone).getServiceState();
94 
95         mContext = spy(new TestContext());
96         doReturn(mContext).when(mPhone).getContext();
97         replaceInstance(ContextWrapper.class, "mBase", mPhoneGlobals, mContext);
98 
99         Resources resources = InstrumentationRegistry.getTargetContext().getResources();
100         assertNotNull(resources);
101         doReturn(resources).when(mContext).getResources();
102 
103         replaceInstance(Handler.class, "mLooper", mPhone, Looper.myLooper());
104         replaceInstance(PhoneFactory.class, "sMadeDefaults", null, true);
105         replaceInstance(PhoneFactory.class, "sPhone", null, mPhone);
106         replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone});
107         replaceInstance(PhoneGlobals.class, "sMe", null, mPhoneGlobals);
108         replaceInstance(PhoneFactory.class, "sMetricsCollector", null, mMetricsCollector);
109         replaceInstance(SatelliteController.class, "sInstance", null,
110                 Mockito.mock(SatelliteController.class));
111 
112         doReturn(Mockito.mock(PersistAtomsStorage.class)).when(mMetricsCollector).getAtomsStorage();
113 
114         doReturn(mDataNetworkController).when(mPhone).getDataNetworkController();
115         doReturn(Collections.emptyList()).when(mDataNetworkController)
116                 .getInternetDataDisallowedReasons();
117         doReturn(Mockito.mock(DataConfigManager.class)).when(mDataNetworkController)
118                 .getDataConfigManager();
119 
120         mPhoneGlobals.phoneMgr = Mockito.mock(PhoneInterfaceManager.class);
121     }
122 
123     @After
tearDown()124     public void tearDown() throws Exception {
125         // Ensure there are no static references to handlers after test completes.
126         PhoneConfigurationManager.unregisterAllMultiSimConfigChangeRegistrants();
127         cleanupTestLooper();
128         restoreInstances();
129     }
130 
setupTestLooper()131     protected void setupTestLooper() {
132         mTestHandlerThread = new HandlerThread("TestHandlerThread");
133         mTestHandlerThread.start();
134         mTestLooper = mTestHandlerThread.getLooper();
135         mLooperManager = new TestLooperManager(mTestLooper);
136     }
137 
cleanupTestLooper()138     private void cleanupTestLooper() {
139         mTestLooper = null;
140         if (mLooperManager != null) {
141             mLooperManager.release();
142             mLooperManager = null;
143         }
144         if (mTestHandlerThread != null) {
145             mTestHandlerThread.quit();
146             try {
147                 mTestHandlerThread.join();
148             } catch (InterruptedException ex) {
149                 Log.w("TelephonyTestBase", "HandlerThread join interrupted", ex);
150             }
151             mTestHandlerThread = null;
152         }
153     }
154 
processOneMessage()155     protected void processOneMessage() {
156         var msg = mLooperManager.next();
157         mLooperManager.execute(msg);
158         mLooperManager.recycle(msg);
159     }
160 
processAllMessages()161     protected void processAllMessages() {
162         for (var msg = mLooperManager.poll(); msg != null && msg.getTarget() != null;) {
163             mLooperManager.execute(msg);
164             mLooperManager.recycle(msg);
165         }
166     }
167 
waitForExecutorAction(Executor executor, long timeoutMillis)168     protected final boolean waitForExecutorAction(Executor executor, long timeoutMillis) {
169         final CountDownLatch lock = new CountDownLatch(1);
170         executor.execute(() -> {
171             lock.countDown();
172         });
173         while (lock.getCount() > 0) {
174             try {
175                 return lock.await(timeoutMillis, TimeUnit.MILLISECONDS);
176             } catch (InterruptedException e) {
177                 // do nothing
178             }
179         }
180         return true;
181     }
182 
waitForHandlerAction(Handler h, long timeoutMillis)183     protected final void waitForHandlerAction(Handler h, long timeoutMillis) {
184         final CountDownLatch lock = new CountDownLatch(1);
185         h.post(lock::countDown);
186         while (lock.getCount() > 0) {
187             try {
188                 lock.await(timeoutMillis, TimeUnit.MILLISECONDS);
189             } catch (InterruptedException e) {
190                 // do nothing
191             }
192         }
193     }
194 
waitForHandlerActionDelayed(Handler h, long timeoutMillis, long delayMs)195     protected final void waitForHandlerActionDelayed(Handler h, long timeoutMillis, long delayMs) {
196         final CountDownLatch lock = new CountDownLatch(1);
197         h.postDelayed(lock::countDown, delayMs);
198         while (lock.getCount() > 0) {
199             try {
200                 lock.await(timeoutMillis, TimeUnit.MILLISECONDS);
201             } catch (InterruptedException e) {
202                 // do nothing
203             }
204         }
205     }
206 
waitForMs(long ms)207     protected void waitForMs(long ms) {
208         try {
209             Thread.sleep(ms);
210         } catch (InterruptedException e) {
211             Log.e("TelephonyTestBase", "InterruptedException while waiting: " + e);
212         }
213     }
214 
replaceInstance(final Class c, final String instanceName, final Object obj, final Object newValue)215     protected synchronized void replaceInstance(final Class c, final String instanceName,
216             final Object obj, final Object newValue)
217             throws Exception {
218         Field field = c.getDeclaredField(instanceName);
219         field.setAccessible(true);
220 
221         InstanceKey key = new InstanceKey(c, instanceName, obj);
222         if (!mOldInstances.containsKey(key)) {
223             mOldInstances.put(key, field.get(obj));
224             mInstanceKeys.add(key);
225         }
226         field.set(obj, newValue);
227     }
228 
restoreInstances()229     private synchronized void restoreInstances() throws Exception {
230         Iterator<InstanceKey> it = mInstanceKeys.descendingIterator();
231 
232         while (it.hasNext()) {
233             InstanceKey key = it.next();
234             Field field = key.mClass.getDeclaredField(key.mInstName);
235             field.setAccessible(true);
236             field.set(key.mObj, mOldInstances.get(key));
237         }
238 
239         mInstanceKeys.clear();
240         mOldInstances.clear();
241     }
242 
243     private static class InstanceKey {
244         public final Class mClass;
245         public final String mInstName;
246         public final Object mObj;
InstanceKey(final Class c, final String instName, final Object obj)247         InstanceKey(final Class c, final String instName, final Object obj) {
248             mClass = c;
249             mInstName = instName;
250             mObj = obj;
251         }
252 
253         @Override
hashCode()254         public int hashCode() {
255             return (mClass.getName().hashCode() * 31 + mInstName.hashCode()) * 31;
256         }
257 
258         @Override
equals(Object obj)259         public boolean equals(Object obj) {
260             if (obj == null || obj.getClass() != getClass()) {
261                 return false;
262             }
263 
264             InstanceKey other = (InstanceKey) obj;
265             return (other.mClass == mClass && other.mInstName.equals(mInstName)
266                     && other.mObj == mObj);
267         }
268     }
269 
getTestContext()270     protected TestContext getTestContext() {
271         return mContext;
272     }
273 }
274