ContextFixture.java revision bc2eba9036e430ea40a47ab708c0386fa567ec9c
1/* 2 * Copyright (C) 2016 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 17package com.android.internal.telephony; 18 19import com.google.common.collect.ArrayListMultimap; 20import com.google.common.collect.Multimap; 21 22import org.mockito.MockitoAnnotations; 23import org.mockito.invocation.InvocationOnMock; 24import org.mockito.stubbing.Answer; 25 26import android.app.AlarmManager; 27import android.app.AppOpsManager; 28import android.app.NotificationManager; 29import android.content.BroadcastReceiver; 30import android.content.ComponentName; 31import android.content.ContentProvider; 32import android.content.ContentResolver; 33import android.content.ContentValues; 34import android.content.Context; 35import android.content.Intent; 36import android.content.IntentFilter; 37import android.content.ServiceConnection; 38import android.content.SharedPreferences; 39import android.content.pm.PackageManager; 40import android.content.pm.ResolveInfo; 41import android.content.pm.ServiceInfo; 42import android.content.res.Configuration; 43import android.content.res.Resources; 44import android.database.Cursor; 45import android.net.ConnectivityManager; 46import android.net.Uri; 47import android.net.wifi.WifiManager; 48import android.os.Bundle; 49import android.os.Handler; 50import android.os.IInterface; 51import android.os.PersistableBundle; 52import android.os.UserHandle; 53import android.os.UserManager; 54import android.preference.PreferenceManager; 55import android.provider.Settings; 56import android.provider.Telephony; 57import android.telephony.CarrierConfigManager; 58import android.telephony.SubscriptionManager; 59import android.telephony.TelephonyManager; 60import android.test.mock.MockContentProvider; 61import android.test.mock.MockContentResolver; 62import android.test.mock.MockContext; 63import android.util.Log; 64 65import java.util.ArrayList; 66import java.util.Arrays; 67import java.util.Collection; 68import java.util.HashMap; 69import java.util.List; 70import java.util.Locale; 71import java.util.Map; 72 73import static org.mockito.Matchers.anyBoolean; 74import static org.mockito.Matchers.anyString; 75import static org.mockito.Mockito.any; 76import static org.mockito.Mockito.anyInt; 77import static org.mockito.Mockito.doAnswer; 78import static org.mockito.Mockito.doReturn; 79import static org.mockito.Mockito.eq; 80import static org.mockito.Mockito.mock; 81import static org.mockito.Mockito.spy; 82import static org.mockito.Mockito.when; 83 84/** 85 * Controls a test {@link Context} as would be provided by the Android framework to an 86 * {@code Activity}, {@code Service} or other system-instantiated component. 87 * 88 * Contains Fake<Component> classes like FakeContext for components that require complex and 89 * reusable stubbing. Others can be mocked using Mockito functions in tests or constructor/public 90 * methods of this class. 91 */ 92public class ContextFixture implements TestFixture<Context> { 93 private static final String TAG = "ContextFixture"; 94 95 public class FakeContentProvider extends MockContentProvider { 96 @Override 97 public int delete(Uri uri, String selection, String[] selectionArgs) { 98 return 0; 99 } 100 101 @Override 102 public Uri insert(Uri uri, ContentValues values) { 103 if (uri.compareTo(Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw")) == 0) { 104 return Uri.withAppendedPath(uri, "1"); 105 } 106 return null; 107 } 108 109 @Override 110 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 111 String sortOrder) { 112 return null; 113 } 114 115 @Override 116 public Bundle call(String method, String request, Bundle args) { 117 return null; 118 } 119 } 120 121 private final HashMap<String, Object> mSystemServices = new HashMap<String, Object>(); 122 123 public void setSystemService(String name, Object service) { 124 synchronized(mSystemServices) { 125 mSystemServices.put(name, service); 126 } 127 } 128 129 public class FakeContext extends MockContext { 130 @Override 131 public PackageManager getPackageManager() { 132 return mPackageManager; 133 } 134 135 @Override 136 public boolean bindService( 137 Intent serviceIntent, 138 ServiceConnection connection, 139 int flags) { 140 if (mServiceByServiceConnection.containsKey(connection)) { 141 throw new RuntimeException("ServiceConnection already bound: " + connection); 142 } 143 IInterface service = mServiceByComponentName.get(serviceIntent.getComponent()); 144 if (service == null) { 145 throw new RuntimeException("ServiceConnection not found: " 146 + serviceIntent.getComponent()); 147 } 148 mServiceByServiceConnection.put(connection, service); 149 connection.onServiceConnected(serviceIntent.getComponent(), service.asBinder()); 150 return true; 151 } 152 153 @Override 154 public void unbindService( 155 ServiceConnection connection) { 156 IInterface service = mServiceByServiceConnection.remove(connection); 157 if (service == null) { 158 throw new RuntimeException("ServiceConnection not found: " + connection); 159 } 160 connection.onServiceDisconnected(mComponentNameByService.get(service)); 161 } 162 163 @Override 164 public Object getSystemService(String name) { 165 switch (name) { 166 case Context.TELEPHONY_SERVICE: 167 return mTelephonyManager; 168 case Context.APP_OPS_SERVICE: 169 return mAppOpsManager; 170 case Context.NOTIFICATION_SERVICE: 171 return mNotificationManager; 172 case Context.USER_SERVICE: 173 return mUserManager; 174 case Context.CARRIER_CONFIG_SERVICE: 175 return mCarrierConfigManager; 176 case Context.POWER_SERVICE: 177 // PowerManager is a final class so cannot be mocked, return real service 178 return TestApplication.getAppContext().getSystemService(name); 179 case Context.TELEPHONY_SUBSCRIPTION_SERVICE: 180 return mSubscriptionManager; 181 case Context.WIFI_SERVICE: 182 return mWifiManager; 183 case Context.ALARM_SERVICE: 184 return mAlarmManager; 185 case Context.CONNECTIVITY_SERVICE: 186 return mConnectivityManager; 187 default: 188 synchronized(mSystemServices) { 189 return mSystemServices.get(name); 190 } 191 } 192 } 193 194 @Override 195 public int getUserId() { 196 return 0; 197 } 198 199 @Override 200 public Resources getResources() { 201 return mResources; 202 } 203 204 @Override 205 public String getOpPackageName() { 206 return "com.android.internal.telephony"; 207 } 208 209 @Override 210 public ContentResolver getContentResolver() { 211 return mContentResolver; 212 } 213 214 @Override 215 public void unregisterReceiver(BroadcastReceiver receiver) { 216 } 217 218 @Override 219 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { 220 Intent result = null; 221 synchronized (mBroadcastReceiversByAction) { 222 for (int i = 0 ; i < filter.countActions(); i++) { 223 mBroadcastReceiversByAction.put(filter.getAction(i), receiver); 224 if (result == null) { 225 result = mStickyBroadcastByAction.get(filter.getAction(i)); 226 } 227 } 228 } 229 230 return result; 231 } 232 233 @Override 234 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, 235 String broadcastPermission, Handler scheduler) { 236 return registerReceiver(receiver, filter); 237 } 238 239 @Override 240 public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, 241 IntentFilter filter, String broadcastPermission, Handler scheduler) { 242 return registerReceiver(receiver, filter); 243 } 244 245 @Override 246 public void sendBroadcast(Intent intent) { 247 logd("sendBroadcast called for " + intent.getAction()); 248 synchronized (mBroadcastReceiversByAction) { 249 for (BroadcastReceiver broadcastReceiver : 250 mBroadcastReceiversByAction.get(intent.getAction())) { 251 broadcastReceiver.onReceive(mContext, intent); 252 } 253 } 254 } 255 256 @Override 257 public void sendBroadcast(Intent intent, String receiverPermission) { 258 logd("sendBroadcast called for " + intent.getAction()); 259 sendBroadcast(intent); 260 } 261 262 @Override 263 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 264 String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, 265 int initialCode, String initialData, Bundle initialExtras) { 266 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 267 sendBroadcast(intent); 268 if (resultReceiver != null) { 269 synchronized (mOrderedBroadcastReceivers) { 270 mOrderedBroadcastReceivers.put(intent, resultReceiver); 271 } 272 } 273 } 274 275 @Override 276 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 277 String receiverPermission, int appOp, BroadcastReceiver resultReceiver, 278 Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { 279 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 280 sendBroadcast(intent); 281 if (resultReceiver != null) { 282 synchronized (mOrderedBroadcastReceivers) { 283 mOrderedBroadcastReceivers.put(intent, resultReceiver); 284 } 285 } 286 } 287 288 @Override 289 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 290 String receiverPermission, int appOp, Bundle options, 291 BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, 292 String initialData, Bundle initialExtras) { 293 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 294 sendBroadcast(intent); 295 if (resultReceiver != null) { 296 synchronized (mOrderedBroadcastReceivers) { 297 mOrderedBroadcastReceivers.put(intent, resultReceiver); 298 } 299 } 300 } 301 302 @Override 303 public void sendStickyBroadcast(Intent intent) { 304 logd("sendStickyBroadcast called for " + intent.getAction()); 305 synchronized (mBroadcastReceiversByAction) { 306 sendBroadcast(intent); 307 mStickyBroadcastByAction.put(intent.getAction(), intent); 308 } 309 } 310 311 @Override 312 public void sendStickyBroadcastAsUser(Intent intent, UserHandle ignored) { 313 logd("sendStickyBroadcastAsUser called for " + intent.getAction()); 314 sendStickyBroadcast(intent); 315 } 316 317 @Override 318 public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) 319 throws PackageManager.NameNotFoundException { 320 return this; 321 } 322 323 @Override 324 public void enforceCallingOrSelfPermission(String permission, String message) { 325 // Don't bother enforcing anything in mock. 326 } 327 328 @Override 329 public SharedPreferences getSharedPreferences(String name, int mode) { 330 return mSharedPreferences; 331 } 332 333 @Override 334 public String getPackageName() { 335 return "com.android.internal.telephony"; 336 } 337 338 public boolean testMethod() { 339 return true; 340 } 341 342 public int testMethod1() { 343 return 0; 344 } 345 } 346 347 private final Multimap<String, ComponentName> mComponentNamesByAction = 348 ArrayListMultimap.create(); 349 private final Map<ComponentName, IInterface> mServiceByComponentName = 350 new HashMap<ComponentName, IInterface>(); 351 private final Map<ComponentName, ServiceInfo> mServiceInfoByComponentName = 352 new HashMap<ComponentName, ServiceInfo>(); 353 private final Map<IInterface, ComponentName> mComponentNameByService = 354 new HashMap<IInterface, ComponentName>(); 355 private final Map<ServiceConnection, IInterface> mServiceByServiceConnection = 356 new HashMap<ServiceConnection, IInterface>(); 357 private final Multimap<String, BroadcastReceiver> mBroadcastReceiversByAction = 358 ArrayListMultimap.create(); 359 private final HashMap<String, Intent> mStickyBroadcastByAction = 360 new HashMap<String, Intent>(); 361 private final Multimap<Intent, BroadcastReceiver> mOrderedBroadcastReceivers = 362 ArrayListMultimap.create(); 363 364 // The application context is the most important object this class provides to the system 365 // under test. 366 private final Context mContext = spy(new FakeContext()); 367 368 // We then create a spy on the application context allowing standard Mockito-style 369 // when(...) logic to be used to add specific little responses where needed. 370 371 private final Resources mResources = mock(Resources.class); 372 private final PackageManager mPackageManager = mock(PackageManager.class); 373 private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class); 374 private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class); 375 private final NotificationManager mNotificationManager = mock(NotificationManager.class); 376 private final UserManager mUserManager = mock(UserManager.class); 377 private final CarrierConfigManager mCarrierConfigManager = mock(CarrierConfigManager.class); 378 private final SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class); 379 private final AlarmManager mAlarmManager = mock(AlarmManager.class); 380 private final ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class); 381 private final WifiManager mWifiManager = mock(WifiManager.class); 382 383 private final ContentProvider mContentProvider = spy(new FakeContentProvider()); 384 385 private final Configuration mConfiguration = new Configuration(); 386 private final SharedPreferences mSharedPreferences = PreferenceManager. 387 getDefaultSharedPreferences(TestApplication.getAppContext()); 388 private final MockContentResolver mContentResolver = new MockContentResolver(); 389 private final PersistableBundle mBundle = new PersistableBundle(); 390 391 public ContextFixture() { 392 MockitoAnnotations.initMocks(this); 393 394 doAnswer(new Answer<List<ResolveInfo>>() { 395 @Override 396 public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable { 397 return doQueryIntentServices( 398 (Intent) invocation.getArguments()[0], 399 (Integer) invocation.getArguments()[1]); 400 } 401 }).when(mPackageManager).queryIntentServices((Intent) any(), anyInt()); 402 403 doAnswer(new Answer<List<ResolveInfo>>() { 404 @Override 405 public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable { 406 return doQueryIntentServices( 407 (Intent) invocation.getArguments()[0], 408 (Integer) invocation.getArguments()[1]); 409 } 410 }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt()); 411 412 doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt()); 413 414 mConfiguration.locale = Locale.US; 415 doReturn(mConfiguration).when(mResources).getConfiguration(); 416 417 mContentResolver.addProvider(Telephony.Sms.CONTENT_URI.getAuthority(), mContentProvider); 418 mContentResolver.addProvider(Settings.System.CONTENT_URI.getAuthority(), mContentProvider); 419 } 420 421 @Override 422 public Context getTestDouble() { 423 return mContext; 424 } 425 426 public void putResource(int id, final String value) { 427 when(mResources.getText(eq(id))).thenReturn(value); 428 when(mResources.getString(eq(id))).thenReturn(value); 429 when(mResources.getString(eq(id), any())).thenAnswer(new Answer<String>() { 430 @Override 431 public String answer(InvocationOnMock invocation) { 432 Object[] args = invocation.getArguments(); 433 return String.format(value, Arrays.copyOfRange(args, 1, args.length)); 434 } 435 }); 436 } 437 438 public void putBooleanResource(int id, boolean value) { 439 when(mResources.getBoolean(eq(id))).thenReturn(value); 440 } 441 442 public void putStringArrayResource(int id, String[] values) { 443 doReturn(values).when(mResources).getStringArray(eq(id)); 444 } 445 446 public void putIntArrayResource(int id, int[] values) { 447 doReturn(values).when(mResources).getIntArray(eq(id)); 448 } 449 450 public PersistableBundle getCarrierConfigBundle() { 451 return mBundle; 452 } 453 454 private void addService(String action, ComponentName name, IInterface service) { 455 mComponentNamesByAction.put(action, name); 456 mServiceByComponentName.put(name, service); 457 mComponentNameByService.put(service, name); 458 } 459 460 private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) { 461 List<ResolveInfo> result = new ArrayList<ResolveInfo>(); 462 for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) { 463 ResolveInfo resolveInfo = new ResolveInfo(); 464 resolveInfo.serviceInfo = mServiceInfoByComponentName.get(componentName); 465 result.add(resolveInfo); 466 } 467 return result; 468 } 469 470 public void sendBroadcastToOrderedBroadcastReceivers() { 471 synchronized (mOrderedBroadcastReceivers) { 472 // having a map separate from mOrderedBroadcastReceivers is helpful here as onReceive() 473 // call within the loop may lead to sendOrderedBroadcast() which can add to 474 // mOrderedBroadcastReceivers 475 Collection<Map.Entry<Intent, BroadcastReceiver>> map = 476 mOrderedBroadcastReceivers.entries(); 477 for (Map.Entry<Intent, BroadcastReceiver> entry : map) { 478 entry.getValue().onReceive(mContext, entry.getKey()); 479 mOrderedBroadcastReceivers.remove(entry.getKey(), entry.getValue()); 480 } 481 } 482 } 483 484 private static void logd(String s) { 485 Log.d(TAG, s); 486 } 487} 488