1 /*
2  * Copyright (C) 2019 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.cellbroadcastservice.tests;
18 
19 import static org.mockito.ArgumentMatchers.any;
20 import static org.mockito.ArgumentMatchers.anyBoolean;
21 import static org.mockito.ArgumentMatchers.anyInt;
22 import static org.mockito.ArgumentMatchers.anyString;
23 import static org.mockito.Mockito.doAnswer;
24 import static org.mockito.Mockito.doReturn;
25 import static org.mockito.Mockito.eq;
26 import static org.mockito.Mockito.mock;
27 
28 import android.content.BroadcastReceiver;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.SharedPreferences;
33 import android.content.pm.PackageManager;
34 import android.content.res.Resources;
35 import android.location.LocationManager;
36 import android.os.Handler;
37 import android.os.IPowerManager;
38 import android.os.IThermalService;
39 import android.os.PowerManager;
40 import android.telephony.SubscriptionManager;
41 import android.telephony.TelephonyManager;
42 import android.test.mock.MockContentResolver;
43 import android.testing.TestableLooper;
44 
45 import com.google.common.collect.ArrayListMultimap;
46 import com.google.common.collect.Multimap;
47 
48 import junit.framework.TestCase;
49 
50 import org.mockito.Mock;
51 import org.mockito.MockitoAnnotations;
52 import org.mockito.stubbing.Answer;
53 
54 import java.lang.reflect.Field;
55 import java.util.HashMap;
56 import java.util.Iterator;
57 import java.util.LinkedList;
58 
59 /**
60  * This is the test base class can be extended by all cell broadcast service unit test classes.
61  */
62 public class CellBroadcastServiceTestBase extends TestCase {
63 
64     @Mock
65     protected Context mMockedContext;
66 
67     @Mock
68     protected Resources mMockedResources;
69 
70     @Mock
71     protected SubscriptionManager mMockedSubscriptionManager;
72 
73     @Mock
74     protected TelephonyManager mMockedTelephonyManager;
75 
76     @Mock
77     protected LocationManager mMockedLocationManager;
78 
79     @Mock
80     protected PackageManager mMockedPackageManager;
81 
82     @Mock
83     protected SharedPreferences mSharedPreference;
84 
85     @Mock
86     protected SharedPreferences.Editor mEditor;
87 
88     protected final MockContentResolver mMockedContentResolver = new MockContentResolver();
89 
90     private final Multimap<String, BroadcastReceiver> mBroadcastReceiversByAction =
91             ArrayListMultimap.create();
92 
93     private final HashMap<InstanceKey, Object> mOldInstances = new HashMap<>();
94 
95     private final LinkedList<InstanceKey> mInstanceKeys = new LinkedList<>();
96 
97     protected static final int FAKE_SUBID = 1;
98 
99     private static class InstanceKey {
100         final Class mClass;
101         final String mInstName;
102         final Object mObj;
103 
InstanceKey(final Class c, final String instName, final Object obj)104         InstanceKey(final Class c, final String instName, final Object obj) {
105             mClass = c;
106             mInstName = instName;
107             mObj = obj;
108         }
109 
110         @Override
hashCode()111         public int hashCode() {
112             return (mClass.getName().hashCode() * 31 + mInstName.hashCode()) * 31;
113         }
114 
115         @Override
equals(Object obj)116         public boolean equals(Object obj) {
117             if (obj == null || obj.getClass() != getClass()) {
118                 return false;
119             }
120 
121             InstanceKey other = (InstanceKey) obj;
122             return (other.mClass == mClass && other.mInstName.equals(mInstName)
123                     && other.mObj == mObj);
124         }
125     }
126 
setUp()127     protected void setUp() throws Exception {
128         MockitoAnnotations.initMocks(this);
129         doReturn(mMockedContentResolver).when(mMockedContext).getContentResolver();
130         doReturn(mMockedResources).when(mMockedContext).getResources();
131 
132         // Can't directly mock power manager because it's final.
133         PowerManager powerManager = new PowerManager(mMockedContext, mock(IPowerManager.class),
134                 mock(IThermalService.class),
135                 new Handler(TestableLooper.get(CellBroadcastServiceTestBase.this).getLooper()));
136         doReturn(powerManager).when(mMockedContext).getSystemService(Context.POWER_SERVICE);
137         doReturn(mMockedTelephonyManager).when(mMockedContext)
138                 .getSystemService(Context.TELEPHONY_SERVICE);
139         doReturn(Context.TELEPHONY_SERVICE).when(mMockedContext)
140                 .getSystemServiceName(TelephonyManager.class);
141         doReturn(mMockedSubscriptionManager).when(mMockedContext)
142                 .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
143         doReturn(Context.TELEPHONY_SUBSCRIPTION_SERVICE).when(mMockedContext).getSystemServiceName(
144                 SubscriptionManager.class);
145         doReturn(mMockedLocationManager).when(mMockedContext)
146                 .getSystemService(Context.LOCATION_SERVICE);
147         doReturn(true).when(mMockedLocationManager)
148                 .isLocationEnabled();
149         doReturn(mMockedPackageManager).when(mMockedContext)
150                 .getPackageManager();
151         doReturn(mMockedContext).when(mMockedContext).createContextAsUser(any(), anyInt());
152         doReturn(new int[]{FAKE_SUBID}).when(mMockedSubscriptionManager)
153                 .getSubscriptionIds(anyInt());
154         doReturn(mMockedTelephonyManager).when(mMockedTelephonyManager)
155                 .createForSubscriptionId(anyInt());
156         Answer<Intent> registerReceiverAnswer = invocation -> {
157             BroadcastReceiver receiver = invocation.getArgument(0);
158             IntentFilter intentFilter = invocation.getArgument(1);
159             for (int i = 0; i < intentFilter.countActions(); i++) {
160                 mBroadcastReceiversByAction.put(intentFilter.getAction(i), receiver);
161             }
162             return null;
163         };
164         doAnswer(registerReceiverAnswer).when(mMockedContext).registerReceiver(
165                 any(BroadcastReceiver.class), any(IntentFilter.class));
166         doAnswer(registerReceiverAnswer).when(mMockedContext).registerReceiver(
167                 any(BroadcastReceiver.class), any(IntentFilter.class), any(int.class));
168         doAnswer(registerReceiverAnswer).when(mMockedContext).registerReceiver(
169                 any(BroadcastReceiver.class), any(IntentFilter.class), any(), any());
170         doAnswer(registerReceiverAnswer).when(mMockedContext).registerReceiver(
171                 any(BroadcastReceiver.class), any(IntentFilter.class),
172                 any(), any(), any(int.class));
173 
174         doReturn(mSharedPreference).when(mMockedContext)
175                 .getSharedPreferences(anyString(), anyInt());
176         doReturn(mEditor).when(mSharedPreference).edit();
177         doReturn(false).when(mSharedPreference).getBoolean(anyString(), anyBoolean());
178     }
179 
tearDown()180     protected void tearDown() throws Exception {
181         restoreInstances();
182     }
183 
sendBroadcast(Intent intent)184     void sendBroadcast(Intent intent) {
185         if (mBroadcastReceiversByAction.containsKey(intent.getAction())) {
186             for (BroadcastReceiver receiver : mBroadcastReceiversByAction.get(intent.getAction())) {
187                 receiver.onReceive(mMockedContext, intent);
188             }
189         }
190     }
191 
putResources(int id, Object value)192     void putResources(int id, Object value) {
193         if (value instanceof String[]) {
194             doReturn(value).when(mMockedResources).getStringArray(eq(id));
195         } else if (value instanceof Boolean) {
196             doReturn(value).when(mMockedResources).getBoolean(eq(id));
197         } else if (value instanceof Integer) {
198             doReturn(value).when(mMockedResources).getInteger(eq(id));
199         } else if (value instanceof Integer[]) {
200             doReturn(value).when(mMockedResources).getIntArray(eq(id));
201         } else if (value instanceof int[]) {
202             doReturn(value).when(mMockedResources).getIntArray(eq(id));
203         } else if (value instanceof String) {
204             doReturn(value).when(mMockedResources).getString(eq(id));
205         }
206     }
207 
replaceInstance(final Class c, final String instanceName, final Object obj, final Object newValue)208     synchronized void replaceInstance(final Class c, final String instanceName,
209                                               final Object obj, final Object newValue)
210             throws Exception {
211         Field field = c.getDeclaredField(instanceName);
212         field.setAccessible(true);
213 
214         InstanceKey key = new InstanceKey(c, instanceName, obj);
215         if (!mOldInstances.containsKey(key)) {
216             mOldInstances.put(key, field.get(obj));
217             mInstanceKeys.add(key);
218         }
219         field.set(obj, newValue);
220     }
221 
restoreInstances()222     private synchronized void restoreInstances() throws Exception {
223         Iterator<InstanceKey> it = mInstanceKeys.descendingIterator();
224 
225         while (it.hasNext()) {
226             InstanceKey key = it.next();
227             Field field = key.mClass.getDeclaredField(key.mInstName);
228             field.setAccessible(true);
229             field.set(key.mObj, mOldInstances.get(key));
230         }
231 
232         mInstanceKeys.clear();
233         mOldInstances.clear();
234     }
235 
236     /**
237      * Converts a hex String to a byte array.
238      *
239      * @param s A string of hexadecimal characters, must be an even number of
240      *          chars long
241      *
242      * @return byte array representation
243      *
244      * @throws RuntimeException on invalid format
245      */
hexStringToBytes(String s)246     public static byte[] hexStringToBytes(String s) {
247         byte[] ret;
248 
249         if (s == null) return null;
250 
251         int sz = s.length();
252 
253         ret = new byte[sz / 2];
254 
255         for (int i = 0; i < sz; i += 2) {
256             ret[i / 2] = (byte) ((hexCharToInt(s.charAt(i)) << 4) | hexCharToInt(s.charAt(i + 1)));
257         }
258 
259         return ret;
260     }
261 
262     /**
263      * Converts a hex char to its integer value
264      *
265      * @param c A single hexadecimal character. Must be in one of these ranges:
266      *          - '0' to '9', or
267      *          - 'a' to 'f', or
268      *          - 'A' to 'F'
269      *
270      * @return the integer representation of {@code c}
271      *
272      * @throws RuntimeException on invalid character
273      */
hexCharToInt(char c)274     private static int hexCharToInt(char c) {
275         if (c >= '0' && c <= '9') return (c - '0');
276         if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
277         if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
278 
279         throw new RuntimeException("invalid hex char '" + c + "'");
280     }
281 }
282