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 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 sendBroadcastAsUser(Intent intent, UserHandle user) { 307 sendBroadcast(intent); 308 } 309 310 @Override 311 public void sendBroadcastAsUser(Intent intent, UserHandle user, 312 String receiverPermission) { 313 sendBroadcast(intent); 314 } 315 316 @Override 317 public void sendBroadcastAsUser(Intent intent, UserHandle user, 318 String receiverPermission, int appOp) { 319 sendBroadcast(intent); 320 } 321 322 @Override 323 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 324 String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, 325 int initialCode, String initialData, Bundle initialExtras) { 326 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 327 sendBroadcast(intent); 328 if (resultReceiver != null) { 329 synchronized (mOrderedBroadcastReceivers) { 330 mOrderedBroadcastReceivers.put(intent, resultReceiver); 331 } 332 } 333 } 334 335 @Override 336 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 337 String receiverPermission, int appOp, BroadcastReceiver resultReceiver, 338 Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { 339 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 340 sendBroadcast(intent); 341 if (resultReceiver != null) { 342 synchronized (mOrderedBroadcastReceivers) { 343 mOrderedBroadcastReceivers.put(intent, resultReceiver); 344 } 345 } 346 } 347 348 @Override 349 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 350 String receiverPermission, int appOp, Bundle options, 351 BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, 352 String initialData, Bundle initialExtras) { 353 logd("sendOrderedBroadcastAsUser called for " + intent.getAction()); 354 sendBroadcast(intent); 355 if (resultReceiver != null) { 356 synchronized (mOrderedBroadcastReceivers) { 357 mOrderedBroadcastReceivers.put(intent, resultReceiver); 358 } 359 } 360 } 361 362 @Override 363 public void sendStickyBroadcast(Intent intent) { 364 logd("sendStickyBroadcast called for " + intent.getAction()); 365 synchronized (mBroadcastReceiversByAction) { 366 sendBroadcast(intent); 367 mStickyBroadcastByAction.put(intent.getAction(), intent); 368 } 369 } 370 371 @Override 372 public void sendStickyBroadcastAsUser(Intent intent, UserHandle ignored) { 373 logd("sendStickyBroadcastAsUser called for " + intent.getAction()); 374 sendStickyBroadcast(intent); 375 } 376 377 @Override 378 public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) 379 throws PackageManager.NameNotFoundException { 380 return this; 381 } 382 383 @Override 384 public void enforceCallingOrSelfPermission(String permission, String message) { 385 if (mPermissionTable.contains(permission) 386 || mPermissionTable.contains(PERMISSION_ENABLE_ALL)) { 387 return; 388 } 389 logd("requested permission: " + permission + " got denied"); 390 throw new SecurityException(permission + " denied: " + message); 391 } 392 393 @Override 394 public int checkCallingOrSelfPermission(String permission) { 395 return PackageManager.PERMISSION_GRANTED; 396 } 397 398 @Override 399 public SharedPreferences getSharedPreferences(String name, int mode) { 400 return mSharedPreferences; 401 } 402 403 @Override 404 public String getPackageName() { 405 return "com.android.internal.telephony"; 406 } 407 408 public boolean testMethod() { 409 return true; 410 } 411 412 public boolean testMethod1() { 413 return true; 414 } 415 416 public boolean testMethod2() { 417 return true; 418 } 419 } 420 421 private final Multimap<String, ComponentName> mComponentNamesByAction = 422 ArrayListMultimap.create(); 423 private final Map<ComponentName, IInterface> mServiceByComponentName = 424 new HashMap<ComponentName, IInterface>(); 425 private final Map<ComponentName, ServiceInfo> mServiceInfoByComponentName = 426 new HashMap<ComponentName, ServiceInfo>(); 427 private final Map<IInterface, ComponentName> mComponentNameByService = 428 new HashMap<IInterface, ComponentName>(); 429 private final Map<ServiceConnection, IInterface> mServiceByServiceConnection = 430 new HashMap<ServiceConnection, IInterface>(); 431 private final Multimap<String, BroadcastReceiver> mBroadcastReceiversByAction = 432 ArrayListMultimap.create(); 433 private final HashMap<String, Intent> mStickyBroadcastByAction = 434 new HashMap<String, Intent>(); 435 private final Multimap<Intent, BroadcastReceiver> mOrderedBroadcastReceivers = 436 ArrayListMultimap.create(); 437 private final HashSet<String> mPermissionTable = new HashSet<>(); 438 439 440 441 // The application context is the most important object this class provides to the system 442 // under test. 443 private final Context mContext = spy(new FakeContext()); 444 445 // We then create a spy on the application context allowing standard Mockito-style 446 // when(...) logic to be used to add specific little responses where needed. 447 448 private final Resources mResources = mock(Resources.class); 449 private final PackageManager mPackageManager = mock(PackageManager.class); 450 private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class); 451 private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class); 452 private final NotificationManager mNotificationManager = mock(NotificationManager.class); 453 private final UserManager mUserManager = mock(UserManager.class); 454 private final CarrierConfigManager mCarrierConfigManager = mock(CarrierConfigManager.class); 455 private final SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class); 456 private final AlarmManager mAlarmManager = mock(AlarmManager.class); 457 private final ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class); 458 private final UsageStatsManager mUsageStatManager = null; 459 private final WifiManager mWifiManager = mock(WifiManager.class); 460 461 private final ContentProvider mContentProvider = spy(new FakeContentProvider()); 462 463 private final Configuration mConfiguration = new Configuration(); 464 private final SharedPreferences mSharedPreferences = PreferenceManager 465 .getDefaultSharedPreferences(TestApplication.getAppContext()); 466 private final MockContentResolver mContentResolver = new MockContentResolver(); 467 private final PersistableBundle mBundle = new PersistableBundle(); 468 469 public ContextFixture() { 470 MockitoAnnotations.initMocks(this); 471 472 doAnswer(new Answer<List<ResolveInfo>>() { 473 @Override 474 public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable { 475 return doQueryIntentServices( 476 (Intent) invocation.getArguments()[0], 477 (Integer) invocation.getArguments()[1]); 478 } 479 }).when(mPackageManager).queryIntentServices((Intent) any(), anyInt()); 480 481 doAnswer(new Answer<List<ResolveInfo>>() { 482 @Override 483 public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable { 484 return doQueryIntentServices( 485 (Intent) invocation.getArguments()[0], 486 (Integer) invocation.getArguments()[1]); 487 } 488 }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt()); 489 490 doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt()); 491 492 mConfiguration.locale = Locale.US; 493 doReturn(mConfiguration).when(mResources).getConfiguration(); 494 495 mContentResolver.addProvider(Settings.AUTHORITY, mContentProvider); 496 mPermissionTable.add(PERMISSION_ENABLE_ALL); 497 } 498 499 @Override 500 public Context getTestDouble() { 501 return mContext; 502 } 503 504 public void putResource(int id, final String value) { 505 when(mResources.getText(eq(id))).thenReturn(value); 506 when(mResources.getString(eq(id))).thenReturn(value); 507 when(mResources.getString(eq(id), any())).thenAnswer(new Answer<String>() { 508 @Override 509 public String answer(InvocationOnMock invocation) { 510 Object[] args = invocation.getArguments(); 511 return String.format(value, Arrays.copyOfRange(args, 1, args.length)); 512 } 513 }); 514 } 515 516 public void putBooleanResource(int id, boolean value) { 517 when(mResources.getBoolean(eq(id))).thenReturn(value); 518 } 519 520 public void putStringArrayResource(int id, String[] values) { 521 doReturn(values).when(mResources).getStringArray(eq(id)); 522 } 523 524 public void putIntArrayResource(int id, int[] values) { 525 doReturn(values).when(mResources).getIntArray(eq(id)); 526 } 527 528 public PersistableBundle getCarrierConfigBundle() { 529 return mBundle; 530 } 531 532 private void addService(String action, ComponentName name, IInterface service) { 533 mComponentNamesByAction.put(action, name); 534 mServiceByComponentName.put(name, service); 535 mComponentNameByService.put(service, name); 536 } 537 538 private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) { 539 List<ResolveInfo> result = new ArrayList<ResolveInfo>(); 540 for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) { 541 ResolveInfo resolveInfo = new ResolveInfo(); 542 resolveInfo.serviceInfo = mServiceInfoByComponentName.get(componentName); 543 result.add(resolveInfo); 544 } 545 return result; 546 } 547 548 public void sendBroadcastToOrderedBroadcastReceivers() { 549 synchronized (mOrderedBroadcastReceivers) { 550 // having a map separate from mOrderedBroadcastReceivers is helpful here as onReceive() 551 // call within the loop may lead to sendOrderedBroadcast() which can add to 552 // mOrderedBroadcastReceivers 553 Collection<Map.Entry<Intent, BroadcastReceiver>> map = 554 mOrderedBroadcastReceivers.entries(); 555 for (Map.Entry<Intent, BroadcastReceiver> entry : map) { 556 entry.getValue().onReceive(mContext, entry.getKey()); 557 mOrderedBroadcastReceivers.remove(entry.getKey(), entry.getValue()); 558 } 559 } 560 } 561 562 public void addCallingOrSelfPermission(String permission) { 563 synchronized (mPermissionTable) { 564 if (mPermissionTable != null && permission != null) { 565 mPermissionTable.remove(PERMISSION_ENABLE_ALL); 566 mPermissionTable.add(permission); 567 } 568 } 569 } 570 571 public void removeCallingOrSelfPermission(String permission) { 572 synchronized (mPermissionTable) { 573 if (mPermissionTable != null && permission != null) { 574 mPermissionTable.remove(permission); 575 } 576 } 577 } 578 579 private static void logd(String s) { 580 Log.d(TAG, s); 581 } 582} 583