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