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 17 package com.android.internal.telephony; 18 19 import com.google.common.collect.ArrayListMultimap; 20 import com.google.common.collect.Multimap; 21 22 import org.mockito.MockitoAnnotations; 23 import org.mockito.invocation.InvocationOnMock; 24 import org.mockito.stubbing.Answer; 25 26 import android.app.AlarmManager; 27 import android.app.AppOpsManager; 28 import android.app.NotificationManager; 29 import android.app.usage.UsageStatsManager; 30 import android.content.BroadcastReceiver; 31 import android.content.ComponentName; 32 import android.content.ContentProvider; 33 import android.content.ContentResolver; 34 import android.content.ContentValues; 35 import android.content.Context; 36 import android.content.Intent; 37 import android.content.IntentFilter; 38 import android.content.ServiceConnection; 39 import android.content.SharedPreferences; 40 import android.content.pm.PackageManager; 41 import android.content.pm.ResolveInfo; 42 import android.content.pm.ServiceInfo; 43 import android.content.res.Configuration; 44 import android.content.res.Resources; 45 import android.database.Cursor; 46 import android.database.MatrixCursor; 47 import android.net.ConnectivityManager; 48 import android.net.Uri; 49 import android.net.wifi.WifiManager; 50 import android.os.Bundle; 51 import android.os.Handler; 52 import android.os.IInterface; 53 import android.os.PersistableBundle; 54 import android.os.UserHandle; 55 import android.os.UserManager; 56 import android.preference.PreferenceManager; 57 import android.provider.Settings; 58 import android.telephony.CarrierConfigManager; 59 import android.telephony.SubscriptionManager; 60 import android.telephony.TelephonyManager; 61 import android.test.mock.MockContentProvider; 62 import android.test.mock.MockContentResolver; 63 import android.test.mock.MockContext; 64 import android.util.Log; 65 66 import java.util.ArrayList; 67 import java.util.Arrays; 68 import java.util.Collection; 69 import java.util.HashMap; 70 import java.util.HashSet; 71 import java.util.List; 72 import java.util.Locale; 73 import java.util.Map; 74 75 import static org.mockito.Mockito.any; 76 import static org.mockito.Mockito.anyInt; 77 import static org.mockito.Mockito.doAnswer; 78 import static org.mockito.Mockito.doReturn; 79 import static org.mockito.Mockito.eq; 80 import static org.mockito.Mockito.mock; 81 import static org.mockito.Mockito.spy; 82 import 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 */ 92 public 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 return registerReceiver(receiver, filter, null, null); 264 } 265 266 @Override 267 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, 268 String broadcastPermission, Handler scheduler) { 269 return registerReceiverAsUser(receiver, null, filter, broadcastPermission, scheduler); 270 } 271 272 @Override 273 public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, 274 IntentFilter filter, String broadcastPermission, Handler scheduler) { 275 Intent result = null; 276 synchronized (mBroadcastReceiversByAction) { 277 for (int i = 0 ; i < filter.countActions() ; i++) { 278 mBroadcastReceiversByAction.put(filter.getAction(i), receiver); 279 if (result == null) { 280 result = mStickyBroadcastByAction.get(filter.getAction(i)); 281 } 282 } 283 } 284 285 return result; 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 sendOrderedBroadcast(Intent intent, String receiverPermission) { 307 logd("sendOrderedBroadcast called for " + intent.getAction()); 308 sendBroadcast(intent); 309 } 310 311 @Override 312 public void sendOrderedBroadcast(Intent intent, String receiverPermission, 313 BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, 314 String initialData, Bundle initialExtras) { 315 sendOrderedBroadcast(intent, receiverPermission); 316 if (resultReceiver != null) { 317 synchronized (mOrderedBroadcastReceivers) { 318 mOrderedBroadcastReceivers.put(intent, resultReceiver); 319 } 320 } 321 } 322 323 @Override 324 public void sendOrderedBroadcast(Intent intent, String receiverPermission, Bundle options, 325 BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, 326 String initialData, Bundle initialExtras) { 327 sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler, 328 initialCode, initialData, initialExtras); 329 } 330 331 @Override 332 public void sendOrderedBroadcast(Intent intent, String receiverPermission, int appOp, 333 BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, 334 String initialData, Bundle initialExtras) { 335 sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler, 336 initialCode, initialData, initialExtras); 337 } 338 339 @Override 340 public void sendBroadcastAsUser(Intent intent, UserHandle user) { 341 sendBroadcast(intent); 342 } 343 344 @Override 345 public void sendBroadcastAsUser(Intent intent, UserHandle user, 346 String receiverPermission) { 347 sendBroadcast(intent); 348 } 349 350 @Override 351 public void sendBroadcastAsUser(Intent intent, UserHandle user, 352 String receiverPermission, int appOp) { 353 sendBroadcast(intent); 354 } 355 356 @Override 357 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 358 String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, 359 int initialCode, String initialData, Bundle initialExtras) { 360 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 361 sendBroadcast(intent); 362 if (resultReceiver != null) { 363 synchronized (mOrderedBroadcastReceivers) { 364 mOrderedBroadcastReceivers.put(intent, resultReceiver); 365 } 366 } 367 } 368 369 @Override 370 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 371 String receiverPermission, int appOp, BroadcastReceiver resultReceiver, 372 Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { 373 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 374 sendBroadcast(intent); 375 if (resultReceiver != null) { 376 synchronized (mOrderedBroadcastReceivers) { 377 mOrderedBroadcastReceivers.put(intent, resultReceiver); 378 } 379 } 380 } 381 382 @Override 383 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 384 String receiverPermission, int appOp, Bundle options, 385 BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, 386 String initialData, Bundle initialExtras) { 387 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 388 sendBroadcast(intent); 389 if (resultReceiver != null) { 390 synchronized (mOrderedBroadcastReceivers) { 391 mOrderedBroadcastReceivers.put(intent, resultReceiver); 392 } 393 } 394 } 395 396 @Override 397 public void sendStickyBroadcast(Intent intent) { 398 logd("sendStickyBroadcast called for " + intent.getAction()); 399 synchronized (mBroadcastReceiversByAction) { 400 sendBroadcast(intent); 401 mStickyBroadcastByAction.put(intent.getAction(), intent); 402 } 403 } 404 405 @Override 406 public void sendStickyBroadcastAsUser(Intent intent, UserHandle ignored) { 407 logd("sendStickyBroadcastAsUser called for " + intent.getAction()); 408 sendStickyBroadcast(intent); 409 } 410 411 @Override 412 public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) 413 throws PackageManager.NameNotFoundException { 414 return this; 415 } 416 417 @Override 418 public void enforceCallingOrSelfPermission(String permission, String message) { 419 if (mPermissionTable.contains(permission) 420 || mPermissionTable.contains(PERMISSION_ENABLE_ALL)) { 421 return; 422 } 423 logd("requested permission: " + permission + " got denied"); 424 throw new SecurityException(permission + " denied: " + message); 425 } 426 427 @Override 428 public int checkCallingOrSelfPermission(String permission) { 429 return PackageManager.PERMISSION_GRANTED; 430 } 431 432 @Override 433 public SharedPreferences getSharedPreferences(String name, int mode) { 434 return mSharedPreferences; 435 } 436 437 @Override 438 public String getPackageName() { 439 return "com.android.internal.telephony"; 440 } 441 } 442 443 private final Multimap<String, ComponentName> mComponentNamesByAction = 444 ArrayListMultimap.create(); 445 private final Map<ComponentName, IInterface> mServiceByComponentName = 446 new HashMap<ComponentName, IInterface>(); 447 private final Map<ComponentName, ServiceInfo> mServiceInfoByComponentName = 448 new HashMap<ComponentName, ServiceInfo>(); 449 private final Map<IInterface, ComponentName> mComponentNameByService = 450 new HashMap<IInterface, ComponentName>(); 451 private final Map<ServiceConnection, IInterface> mServiceByServiceConnection = 452 new HashMap<ServiceConnection, IInterface>(); 453 private final Multimap<String, BroadcastReceiver> mBroadcastReceiversByAction = 454 ArrayListMultimap.create(); 455 private final HashMap<String, Intent> mStickyBroadcastByAction = 456 new HashMap<String, Intent>(); 457 private final Multimap<Intent, BroadcastReceiver> mOrderedBroadcastReceivers = 458 ArrayListMultimap.create(); 459 private final HashSet<String> mPermissionTable = new HashSet<>(); 460 461 462 463 // The application context is the most important object this class provides to the system 464 // under test. 465 private final Context mContext = spy(new FakeContext()); 466 467 // We then create a spy on the application context allowing standard Mockito-style 468 // when(...) logic to be used to add specific little responses where needed. 469 470 private final Resources mResources = mock(Resources.class); 471 private final PackageManager mPackageManager = mock(PackageManager.class); 472 private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class); 473 private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class); 474 private final NotificationManager mNotificationManager = mock(NotificationManager.class); 475 private final UserManager mUserManager = mock(UserManager.class); 476 private final CarrierConfigManager mCarrierConfigManager = mock(CarrierConfigManager.class); 477 private final SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class); 478 private final AlarmManager mAlarmManager = mock(AlarmManager.class); 479 private final ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class); 480 private final UsageStatsManager mUsageStatManager = null; 481 private final WifiManager mWifiManager = mock(WifiManager.class); 482 483 private final ContentProvider mContentProvider = spy(new FakeContentProvider()); 484 485 private final Configuration mConfiguration = new Configuration(); 486 private final SharedPreferences mSharedPreferences = PreferenceManager 487 .getDefaultSharedPreferences(TestApplication.getAppContext()); 488 private final MockContentResolver mContentResolver = new MockContentResolver(); 489 private final PersistableBundle mBundle = new PersistableBundle(); 490 491 public ContextFixture() { 492 MockitoAnnotations.initMocks(this); 493 494 doAnswer(new Answer<List<ResolveInfo>>() { 495 @Override 496 public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable { 497 return doQueryIntentServices( 498 (Intent) invocation.getArguments()[0], 499 (Integer) invocation.getArguments()[1]); 500 } 501 }).when(mPackageManager).queryIntentServices((Intent) any(), anyInt()); 502 503 doAnswer(new Answer<List<ResolveInfo>>() { 504 @Override 505 public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable { 506 return doQueryIntentServices( 507 (Intent) invocation.getArguments()[0], 508 (Integer) invocation.getArguments()[1]); 509 } 510 }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt()); 511 512 doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt()); 513 //doReturn(mBundle).when(mCarrierConfigManager).getConfig(anyInt()); 514 doReturn(mBundle).when(mCarrierConfigManager).getConfig(); 515 516 mConfiguration.locale = Locale.US; 517 doReturn(mConfiguration).when(mResources).getConfiguration(); 518 519 mContentResolver.addProvider(Settings.AUTHORITY, mContentProvider); 520 mPermissionTable.add(PERMISSION_ENABLE_ALL); 521 } 522 523 @Override 524 public Context getTestDouble() { 525 return mContext; 526 } 527 528 public void putResource(int id, final String value) { 529 when(mResources.getText(eq(id))).thenReturn(value); 530 when(mResources.getString(eq(id))).thenReturn(value); 531 when(mResources.getString(eq(id), any())).thenAnswer(new Answer<String>() { 532 @Override 533 public String answer(InvocationOnMock invocation) { 534 Object[] args = invocation.getArguments(); 535 return String.format(value, Arrays.copyOfRange(args, 1, args.length)); 536 } 537 }); 538 } 539 540 public void putBooleanResource(int id, boolean value) { 541 when(mResources.getBoolean(eq(id))).thenReturn(value); 542 } 543 544 public void putStringArrayResource(int id, String[] values) { 545 doReturn(values).when(mResources).getStringArray(eq(id)); 546 } 547 548 public void putIntArrayResource(int id, int[] values) { 549 doReturn(values).when(mResources).getIntArray(eq(id)); 550 } 551 552 public PersistableBundle getCarrierConfigBundle() { 553 return mBundle; 554 } 555 556 private void addService(String action, ComponentName name, IInterface service) { 557 mComponentNamesByAction.put(action, name); 558 mServiceByComponentName.put(name, service); 559 mComponentNameByService.put(service, name); 560 } 561 562 private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) { 563 List<ResolveInfo> result = new ArrayList<ResolveInfo>(); 564 for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) { 565 ResolveInfo resolveInfo = new ResolveInfo(); 566 resolveInfo.serviceInfo = mServiceInfoByComponentName.get(componentName); 567 result.add(resolveInfo); 568 } 569 return result; 570 } 571 572 public void sendBroadcastToOrderedBroadcastReceivers() { 573 synchronized (mOrderedBroadcastReceivers) { 574 // having a map separate from mOrderedBroadcastReceivers is helpful here as onReceive() 575 // call within the loop may lead to sendOrderedBroadcast() which can add to 576 // mOrderedBroadcastReceivers 577 Collection<Map.Entry<Intent, BroadcastReceiver>> map = 578 mOrderedBroadcastReceivers.entries(); 579 for (Map.Entry<Intent, BroadcastReceiver> entry : map) { 580 entry.getValue().onReceive(mContext, entry.getKey()); 581 mOrderedBroadcastReceivers.remove(entry.getKey(), entry.getValue()); 582 } 583 } 584 } 585 586 public void addCallingOrSelfPermission(String permission) { 587 synchronized (mPermissionTable) { 588 if (mPermissionTable != null && permission != null) { 589 mPermissionTable.remove(PERMISSION_ENABLE_ALL); 590 mPermissionTable.add(permission); 591 } 592 } 593 } 594 595 public void removeCallingOrSelfPermission(String permission) { 596 synchronized (mPermissionTable) { 597 if (mPermissionTable != null && permission != null) { 598 mPermissionTable.remove(permission); 599 } 600 } 601 } 602 603 private static void logd(String s) { 604 Log.d(TAG, s); 605 } 606 } 607