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