ContextFixture.java revision 8b0a1cbd24977a1abbba79736cf9f481ced0703a
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.app.usage.UsageStatsManager; 30import android.content.BroadcastReceiver; 31import android.content.ComponentName; 32import android.content.ContentProvider; 33import android.content.ContentResolver; 34import android.content.ContentValues; 35import android.content.Context; 36import android.content.Intent; 37import android.content.IntentFilter; 38import android.content.ServiceConnection; 39import android.content.SharedPreferences; 40import android.content.pm.PackageManager; 41import android.content.pm.ResolveInfo; 42import android.content.pm.ServiceInfo; 43import android.content.res.Configuration; 44import android.content.res.Resources; 45import android.database.Cursor; 46import android.database.MatrixCursor; 47import android.net.ConnectivityManager; 48import android.net.Uri; 49import android.net.wifi.WifiManager; 50import android.os.Bundle; 51import android.os.Handler; 52import android.os.IInterface; 53import android.os.PersistableBundle; 54import android.os.UserHandle; 55import android.os.UserManager; 56import android.preference.PreferenceManager; 57import android.provider.Settings; 58import android.telephony.CarrierConfigManager; 59import android.telephony.SubscriptionManager; 60import android.telephony.TelephonyManager; 61import android.test.mock.MockContentProvider; 62import android.test.mock.MockContentResolver; 63import android.test.mock.MockContext; 64import android.util.Log; 65 66import java.util.ArrayList; 67import java.util.Arrays; 68import java.util.Collection; 69import java.util.HashMap; 70import java.util.HashSet; 71import java.util.List; 72import java.util.Locale; 73import java.util.Map; 74 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 public static final String PERMISSION_ENABLE_ALL = "android.permission.STUB_PERMISSION"; 95 96 public class FakeContentProvider extends MockContentProvider { 97 private String[] mColumns = {"name", "value"}; 98 private HashMap<String, String> mKeyValuePairs = new HashMap<String, String>(); 99 private int mNumKeyValuePairs = 0; 100 101 @Override 102 public int delete(Uri uri, String selection, String[] selectionArgs) { 103 return 0; 104 } 105 106 @Override 107 public Uri insert(Uri uri, ContentValues values) { 108 Uri newUri = null; 109 if (values != null) { 110 mKeyValuePairs.put(values.getAsString("name"), values.getAsString("value")); 111 mNumKeyValuePairs++; 112 newUri = Uri.withAppendedPath(uri, "" + mNumKeyValuePairs); 113 } 114 logd("insert called, new mNumKeyValuePairs: " + mNumKeyValuePairs + " uri: " + uri + 115 " newUri: " + newUri); 116 return newUri; 117 } 118 119 @Override 120 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 121 String sortOrder) { 122 //assuming query will always be of the form 'name = ?' 123 logd("query called, mNumKeyValuePairs: " + mNumKeyValuePairs + " uri: " + uri); 124 if (mKeyValuePairs.containsKey(selectionArgs[0])) { 125 MatrixCursor cursor = new MatrixCursor(projection); 126 cursor.addRow(new String[]{mKeyValuePairs.get(selectionArgs[0])}); 127 return cursor; 128 } 129 return null; 130 } 131 132 @Override 133 public Bundle call(String method, String request, Bundle args) { 134 logd("call called, mNumKeyValuePairs: " + mNumKeyValuePairs + " method: " + method + 135 " request: " + request); 136 switch(method) { 137 case Settings.CALL_METHOD_GET_GLOBAL: 138 case Settings.CALL_METHOD_GET_SECURE: 139 case Settings.CALL_METHOD_GET_SYSTEM: 140 if (mKeyValuePairs.containsKey(request)) { 141 Bundle b = new Bundle(1); 142 b.putCharSequence("value", mKeyValuePairs.get(request)); 143 logd("returning value pair: " + mKeyValuePairs.get(request) + " for " + 144 request); 145 return b; 146 } 147 break; 148 case Settings.CALL_METHOD_PUT_GLOBAL: 149 case Settings.CALL_METHOD_PUT_SECURE: 150 case Settings.CALL_METHOD_PUT_SYSTEM: 151 logd("adding key-value pair: " + request + "-" + (String)args.get("value")); 152 mKeyValuePairs.put(request, (String)args.get("value")); 153 mNumKeyValuePairs++; 154 break; 155 } 156 return null; 157 } 158 } 159 160 private final HashMap<String, Object> mSystemServices = new HashMap<String, Object>(); 161 162 public void setSystemService(String name, Object service) { 163 synchronized (mSystemServices) { 164 mSystemServices.put(name, service); 165 } 166 } 167 168 public class FakeContext extends MockContext { 169 @Override 170 public PackageManager getPackageManager() { 171 return mPackageManager; 172 } 173 174 @Override 175 public boolean bindService( 176 Intent serviceIntent, 177 ServiceConnection connection, 178 int flags) { 179 if (mServiceByServiceConnection.containsKey(connection)) { 180 throw new RuntimeException("ServiceConnection already bound: " + connection); 181 } 182 IInterface service = mServiceByComponentName.get(serviceIntent.getComponent()); 183 if (service == null) { 184 throw new RuntimeException("ServiceConnection not found: " 185 + serviceIntent.getComponent()); 186 } 187 mServiceByServiceConnection.put(connection, service); 188 connection.onServiceConnected(serviceIntent.getComponent(), service.asBinder()); 189 return true; 190 } 191 192 @Override 193 public void unbindService( 194 ServiceConnection connection) { 195 IInterface service = mServiceByServiceConnection.remove(connection); 196 if (service == null) { 197 throw new RuntimeException("ServiceConnection not found: " + connection); 198 } 199 connection.onServiceDisconnected(mComponentNameByService.get(service)); 200 } 201 202 @Override 203 public Object getSystemService(String name) { 204 synchronized (mSystemServices) { 205 Object service = mSystemServices.get(name); 206 if (service != null) return service; 207 } 208 switch (name) { 209 case Context.TELEPHONY_SERVICE: 210 return mTelephonyManager; 211 case Context.APP_OPS_SERVICE: 212 return mAppOpsManager; 213 case Context.NOTIFICATION_SERVICE: 214 return mNotificationManager; 215 case Context.USER_SERVICE: 216 return mUserManager; 217 case Context.CARRIER_CONFIG_SERVICE: 218 return mCarrierConfigManager; 219 case Context.POWER_SERVICE: 220 // PowerManager is a final class so cannot be mocked, return real service 221 return TestApplication.getAppContext().getSystemService(name); 222 case Context.TELEPHONY_SUBSCRIPTION_SERVICE: 223 return mSubscriptionManager; 224 case Context.WIFI_SERVICE: 225 return mWifiManager; 226 case Context.ALARM_SERVICE: 227 return mAlarmManager; 228 case Context.CONNECTIVITY_SERVICE: 229 return mConnectivityManager; 230 case Context.USAGE_STATS_SERVICE: 231 return mUsageStatManager; 232 default: 233 return null; 234 } 235 } 236 237 @Override 238 public int getUserId() { 239 return 0; 240 } 241 242 @Override 243 public Resources getResources() { 244 return mResources; 245 } 246 247 @Override 248 public String getOpPackageName() { 249 return "com.android.internal.telephony"; 250 } 251 252 @Override 253 public ContentResolver getContentResolver() { 254 return mContentResolver; 255 } 256 257 @Override 258 public void unregisterReceiver(BroadcastReceiver receiver) { 259 } 260 261 @Override 262 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { 263 Intent result = null; 264 synchronized (mBroadcastReceiversByAction) { 265 for (int i = 0 ; i < filter.countActions() ; i++) { 266 mBroadcastReceiversByAction.put(filter.getAction(i), receiver); 267 if (result == null) { 268 result = mStickyBroadcastByAction.get(filter.getAction(i)); 269 } 270 } 271 } 272 273 return result; 274 } 275 276 @Override 277 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, 278 String broadcastPermission, Handler scheduler) { 279 return registerReceiver(receiver, filter); 280 } 281 282 @Override 283 public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, 284 IntentFilter filter, String broadcastPermission, Handler scheduler) { 285 return registerReceiver(receiver, filter); 286 } 287 288 @Override 289 public void sendBroadcast(Intent intent) { 290 logd("sendBroadcast called for " + intent.getAction()); 291 synchronized (mBroadcastReceiversByAction) { 292 for (BroadcastReceiver broadcastReceiver : 293 mBroadcastReceiversByAction.get(intent.getAction())) { 294 broadcastReceiver.onReceive(mContext, intent); 295 } 296 } 297 } 298 299 @Override 300 public void sendBroadcast(Intent intent, String receiverPermission) { 301 logd("sendBroadcast called for " + intent.getAction()); 302 sendBroadcast(intent); 303 } 304 305 @Override 306 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 307 String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, 308 int initialCode, String initialData, Bundle initialExtras) { 309 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 310 sendBroadcast(intent); 311 if (resultReceiver != null) { 312 synchronized (mOrderedBroadcastReceivers) { 313 mOrderedBroadcastReceivers.put(intent, resultReceiver); 314 } 315 } 316 } 317 318 @Override 319 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 320 String receiverPermission, int appOp, BroadcastReceiver resultReceiver, 321 Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { 322 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 323 sendBroadcast(intent); 324 if (resultReceiver != null) { 325 synchronized (mOrderedBroadcastReceivers) { 326 mOrderedBroadcastReceivers.put(intent, resultReceiver); 327 } 328 } 329 } 330 331 @Override 332 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 333 String receiverPermission, int appOp, Bundle options, 334 BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, 335 String initialData, Bundle initialExtras) { 336 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 337 sendBroadcast(intent); 338 if (resultReceiver != null) { 339 synchronized (mOrderedBroadcastReceivers) { 340 mOrderedBroadcastReceivers.put(intent, resultReceiver); 341 } 342 } 343 } 344 345 @Override 346 public void sendStickyBroadcast(Intent intent) { 347 logd("sendStickyBroadcast called for " + intent.getAction()); 348 synchronized (mBroadcastReceiversByAction) { 349 sendBroadcast(intent); 350 mStickyBroadcastByAction.put(intent.getAction(), intent); 351 } 352 } 353 354 @Override 355 public void sendStickyBroadcastAsUser(Intent intent, UserHandle ignored) { 356 logd("sendStickyBroadcastAsUser called for " + intent.getAction()); 357 sendStickyBroadcast(intent); 358 } 359 360 @Override 361 public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) 362 throws PackageManager.NameNotFoundException { 363 return this; 364 } 365 366 @Override 367 public void enforceCallingOrSelfPermission(String permission, String message) { 368 if (mPermissionTable.contains(permission) 369 || mPermissionTable.contains(PERMISSION_ENABLE_ALL)) { 370 return; 371 } 372 logd("requested permission: " + permission + " got denied"); 373 throw new SecurityException(permission + " denied: " + message); 374 } 375 376 @Override 377 public int checkCallingOrSelfPermission(String permission) { 378 return PackageManager.PERMISSION_GRANTED; 379 } 380 381 @Override 382 public SharedPreferences getSharedPreferences(String name, int mode) { 383 return mSharedPreferences; 384 } 385 386 @Override 387 public String getPackageName() { 388 return "com.android.internal.telephony"; 389 } 390 391 public boolean testMethod() { 392 return true; 393 } 394 395 public boolean testMethod1() { 396 return true; 397 } 398 } 399 400 private final Multimap<String, ComponentName> mComponentNamesByAction = 401 ArrayListMultimap.create(); 402 private final Map<ComponentName, IInterface> mServiceByComponentName = 403 new HashMap<ComponentName, IInterface>(); 404 private final Map<ComponentName, ServiceInfo> mServiceInfoByComponentName = 405 new HashMap<ComponentName, ServiceInfo>(); 406 private final Map<IInterface, ComponentName> mComponentNameByService = 407 new HashMap<IInterface, ComponentName>(); 408 private final Map<ServiceConnection, IInterface> mServiceByServiceConnection = 409 new HashMap<ServiceConnection, IInterface>(); 410 private final Multimap<String, BroadcastReceiver> mBroadcastReceiversByAction = 411 ArrayListMultimap.create(); 412 private final HashMap<String, Intent> mStickyBroadcastByAction = 413 new HashMap<String, Intent>(); 414 private final Multimap<Intent, BroadcastReceiver> mOrderedBroadcastReceivers = 415 ArrayListMultimap.create(); 416 private final HashSet<String> mPermissionTable = new HashSet<>(); 417 418 419 420 // The application context is the most important object this class provides to the system 421 // under test. 422 private final Context mContext = spy(new FakeContext()); 423 424 // We then create a spy on the application context allowing standard Mockito-style 425 // when(...) logic to be used to add specific little responses where needed. 426 427 private final Resources mResources = mock(Resources.class); 428 private final PackageManager mPackageManager = mock(PackageManager.class); 429 private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class); 430 private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class); 431 private final NotificationManager mNotificationManager = mock(NotificationManager.class); 432 private final UserManager mUserManager = mock(UserManager.class); 433 private final CarrierConfigManager mCarrierConfigManager = mock(CarrierConfigManager.class); 434 private final SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class); 435 private final AlarmManager mAlarmManager = mock(AlarmManager.class); 436 private final ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class); 437 private final UsageStatsManager mUsageStatManager = null; 438 private final WifiManager mWifiManager = mock(WifiManager.class); 439 440 private final ContentProvider mContentProvider = spy(new FakeContentProvider()); 441 442 private final Configuration mConfiguration = new Configuration(); 443 private final SharedPreferences mSharedPreferences = PreferenceManager 444 .getDefaultSharedPreferences(TestApplication.getAppContext()); 445 private final MockContentResolver mContentResolver = new MockContentResolver(); 446 private final PersistableBundle mBundle = new PersistableBundle(); 447 448 public ContextFixture() { 449 MockitoAnnotations.initMocks(this); 450 451 doAnswer(new Answer<List<ResolveInfo>>() { 452 @Override 453 public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable { 454 return doQueryIntentServices( 455 (Intent) invocation.getArguments()[0], 456 (Integer) invocation.getArguments()[1]); 457 } 458 }).when(mPackageManager).queryIntentServices((Intent) any(), anyInt()); 459 460 doAnswer(new Answer<List<ResolveInfo>>() { 461 @Override 462 public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable { 463 return doQueryIntentServices( 464 (Intent) invocation.getArguments()[0], 465 (Integer) invocation.getArguments()[1]); 466 } 467 }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt()); 468 469 doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt()); 470 doReturn(mBundle).when(mCarrierConfigManager).getConfig(anyInt()); 471 472 mConfiguration.locale = Locale.US; 473 doReturn(mConfiguration).when(mResources).getConfiguration(); 474 475 mContentResolver.addProvider(Settings.AUTHORITY, mContentProvider); 476 mPermissionTable.add(PERMISSION_ENABLE_ALL); 477 } 478 479 @Override 480 public Context getTestDouble() { 481 return mContext; 482 } 483 484 public void putResource(int id, final String value) { 485 when(mResources.getText(eq(id))).thenReturn(value); 486 when(mResources.getString(eq(id))).thenReturn(value); 487 when(mResources.getString(eq(id), any())).thenAnswer(new Answer<String>() { 488 @Override 489 public String answer(InvocationOnMock invocation) { 490 Object[] args = invocation.getArguments(); 491 return String.format(value, Arrays.copyOfRange(args, 1, args.length)); 492 } 493 }); 494 } 495 496 public void putBooleanResource(int id, boolean value) { 497 when(mResources.getBoolean(eq(id))).thenReturn(value); 498 } 499 500 public void putStringArrayResource(int id, String[] values) { 501 doReturn(values).when(mResources).getStringArray(eq(id)); 502 } 503 504 public void putIntArrayResource(int id, int[] values) { 505 doReturn(values).when(mResources).getIntArray(eq(id)); 506 } 507 508 public PersistableBundle getCarrierConfigBundle() { 509 return mBundle; 510 } 511 512 private void addService(String action, ComponentName name, IInterface service) { 513 mComponentNamesByAction.put(action, name); 514 mServiceByComponentName.put(name, service); 515 mComponentNameByService.put(service, name); 516 } 517 518 private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) { 519 List<ResolveInfo> result = new ArrayList<ResolveInfo>(); 520 for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) { 521 ResolveInfo resolveInfo = new ResolveInfo(); 522 resolveInfo.serviceInfo = mServiceInfoByComponentName.get(componentName); 523 result.add(resolveInfo); 524 } 525 return result; 526 } 527 528 public void sendBroadcastToOrderedBroadcastReceivers() { 529 synchronized (mOrderedBroadcastReceivers) { 530 // having a map separate from mOrderedBroadcastReceivers is helpful here as onReceive() 531 // call within the loop may lead to sendOrderedBroadcast() which can add to 532 // mOrderedBroadcastReceivers 533 Collection<Map.Entry<Intent, BroadcastReceiver>> map = 534 mOrderedBroadcastReceivers.entries(); 535 for (Map.Entry<Intent, BroadcastReceiver> entry : map) { 536 entry.getValue().onReceive(mContext, entry.getKey()); 537 mOrderedBroadcastReceivers.remove(entry.getKey(), entry.getValue()); 538 } 539 } 540 } 541 542 public void addCallingOrSelfPermission(String permission) { 543 synchronized (mPermissionTable) { 544 if (mPermissionTable != null && permission != null) { 545 mPermissionTable.remove(PERMISSION_ENABLE_ALL); 546 mPermissionTable.add(permission); 547 } 548 } 549 } 550 551 public void removeCallingOrSelfPermission(String permission) { 552 synchronized (mPermissionTable) { 553 if (mPermissionTable != null && permission != null) { 554 mPermissionTable.remove(permission); 555 } 556 } 557 } 558 559 private static void logd(String s) { 560 Log.d(TAG, s); 561 } 562} 563