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