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