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