1/* 2 * Copyright (C) 2015 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.server.telecom.tests; 18 19import com.google.common.collect.ArrayListMultimap; 20import com.google.common.collect.Multimap; 21 22import com.android.internal.telecom.IConnectionService; 23import com.android.internal.telecom.IInCallService; 24 25import org.mockito.MockitoAnnotations; 26import org.mockito.invocation.InvocationOnMock; 27import org.mockito.stubbing.Answer; 28 29import android.app.AppOpsManager; 30import android.app.NotificationManager; 31import android.app.StatusBarManager; 32import android.content.BroadcastReceiver; 33import android.content.ComponentName; 34import android.content.ContentResolver; 35import android.content.Context; 36import android.content.IContentProvider; 37import android.content.Intent; 38import android.content.IntentFilter; 39import android.content.ServiceConnection; 40import android.content.pm.ApplicationInfo; 41import android.content.pm.PackageManager; 42import android.content.pm.ResolveInfo; 43import android.content.pm.ServiceInfo; 44import android.content.res.Configuration; 45import android.content.res.Resources; 46import android.location.Country; 47import android.location.CountryDetector; 48import android.media.AudioManager; 49import android.os.Bundle; 50import android.os.Handler; 51import android.os.IInterface; 52import android.os.UserHandle; 53import android.os.UserManager; 54import android.telecom.CallAudioState; 55import android.telecom.ConnectionService; 56import android.telecom.Log; 57import android.telecom.InCallService; 58import android.telecom.PhoneAccount; 59import android.telecom.TelecomManager; 60import android.telephony.CarrierConfigManager; 61import android.telephony.SubscriptionManager; 62import android.telephony.TelephonyManager; 63import android.test.mock.MockContext; 64 65import java.io.File; 66import java.io.IOException; 67import java.util.ArrayList; 68import java.util.Arrays; 69import java.util.HashMap; 70import java.util.List; 71import java.util.Locale; 72import java.util.Map; 73 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 * The {@link Context} created by this object is "hollow" but its {@code applicationContext} 89 * property points to an application context implementing all the nontrivial functionality. 90 */ 91public class ComponentContextFixture implements TestFixture<Context> { 92 93 public class FakeApplicationContext extends MockContext { 94 @Override 95 public PackageManager getPackageManager() { 96 return mPackageManager; 97 } 98 99 @Override 100 public String getPackageName() { 101 return "com.android.server.telecom.tests"; 102 } 103 104 @Override 105 public String getPackageResourcePath() { 106 return "/tmp/i/dont/know"; 107 } 108 109 @Override 110 public Context getApplicationContext() { 111 return mApplicationContextSpy; 112 } 113 114 @Override 115 public File getFilesDir() { 116 try { 117 return File.createTempFile("temp", "temp").getParentFile(); 118 } catch (IOException e) { 119 throw new RuntimeException(e); 120 } 121 } 122 123 @Override 124 public boolean bindServiceAsUser( 125 Intent serviceIntent, 126 ServiceConnection connection, 127 int flags, 128 UserHandle userHandle) { 129 // TODO: Implement "as user" functionality 130 return bindService(serviceIntent, connection, flags); 131 } 132 133 @Override 134 public boolean bindService( 135 Intent serviceIntent, 136 ServiceConnection connection, 137 int flags) { 138 if (mServiceByServiceConnection.containsKey(connection)) { 139 throw new RuntimeException("ServiceConnection already bound: " + connection); 140 } 141 IInterface service = mServiceByComponentName.get(serviceIntent.getComponent()); 142 if (service == null) { 143 throw new RuntimeException("ServiceConnection not found: " 144 + serviceIntent.getComponent()); 145 } 146 mServiceByServiceConnection.put(connection, service); 147 connection.onServiceConnected(serviceIntent.getComponent(), service.asBinder()); 148 return true; 149 } 150 151 @Override 152 public void unbindService( 153 ServiceConnection connection) { 154 IInterface service = mServiceByServiceConnection.remove(connection); 155 if (service == null) { 156 throw new RuntimeException("ServiceConnection not found: " + connection); 157 } 158 connection.onServiceDisconnected(mComponentNameByService.get(service)); 159 } 160 161 @Override 162 public Object getSystemService(String name) { 163 switch (name) { 164 case Context.AUDIO_SERVICE: 165 return mAudioManager; 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.STATUS_BAR_SERVICE: 173 return mStatusBarManager; 174 case Context.USER_SERVICE: 175 return mUserManager; 176 case Context.TELEPHONY_SUBSCRIPTION_SERVICE: 177 return mSubscriptionManager; 178 case Context.TELECOM_SERVICE: 179 return mTelecomManager; 180 case Context.CARRIER_CONFIG_SERVICE: 181 return mCarrierConfigManager; 182 case Context.COUNTRY_DETECTOR: 183 return mCountryDetector; 184 default: 185 return null; 186 } 187 } 188 189 @Override 190 public String getSystemServiceName(Class<?> svcClass) { 191 if (svcClass == UserManager.class) { 192 return Context.USER_SERVICE; 193 } 194 throw new UnsupportedOperationException(); 195 } 196 197 @Override 198 public int getUserId() { 199 return 0; 200 } 201 202 @Override 203 public Resources getResources() { 204 return mResources; 205 } 206 207 @Override 208 public String getOpPackageName() { 209 return "com.android.server.telecom.tests"; 210 } 211 212 @Override 213 public ApplicationInfo getApplicationInfo() { 214 return mTestApplicationInfo; 215 } 216 217 @Override 218 public ContentResolver getContentResolver() { 219 return new ContentResolver(mApplicationContextSpy) { 220 @Override 221 protected IContentProvider acquireProvider(Context c, String name) { 222 Log.i(this, "acquireProvider %s", name); 223 return getOrCreateProvider(name); 224 } 225 226 @Override 227 public boolean releaseProvider(IContentProvider icp) { 228 return true; 229 } 230 231 @Override 232 protected IContentProvider acquireUnstableProvider(Context c, String name) { 233 Log.i(this, "acquireUnstableProvider %s", name); 234 return getOrCreateProvider(name); 235 } 236 237 private IContentProvider getOrCreateProvider(String name) { 238 if (!mIContentProviderByUri.containsKey(name)) { 239 mIContentProviderByUri.put(name, mock(IContentProvider.class)); 240 } 241 return mIContentProviderByUri.get(name); 242 } 243 244 @Override 245 public boolean releaseUnstableProvider(IContentProvider icp) { 246 return false; 247 } 248 249 @Override 250 public void unstableProviderDied(IContentProvider icp) { 251 } 252 }; 253 } 254 255 @Override 256 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { 257 // TODO -- this is called by WiredHeadsetManager!!! 258 return null; 259 } 260 261 @Override 262 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, 263 String broadcastPermission, Handler scheduler) { 264 return null; 265 } 266 267 @Override 268 public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle handle, 269 IntentFilter filter, String broadcastPermission, Handler scheduler) { 270 return null; 271 } 272 273 @Override 274 public void sendBroadcast(Intent intent) { 275 // TODO -- need to ensure this is captured 276 } 277 278 @Override 279 public void sendBroadcast(Intent intent, String receiverPermission) { 280 // TODO -- need to ensure this is captured 281 } 282 283 @Override 284 public void sendBroadcastAsUser(Intent intent, UserHandle userHandle) { 285 // TODO -- need to ensure this is captured 286 } 287 288 @Override 289 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 290 String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, 291 int initialCode, String initialData, Bundle initialExtras) { 292 // TODO -- need to ensure this is captured 293 } 294 295 @Override 296 public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, 297 String receiverPermission, int appOp, BroadcastReceiver resultReceiver, 298 Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { 299 } 300 301 @Override 302 public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) 303 throws PackageManager.NameNotFoundException { 304 return this; 305 } 306 307 @Override 308 public int checkCallingOrSelfPermission(String permission) { 309 return PackageManager.PERMISSION_GRANTED; 310 } 311 312 @Override 313 public void enforceCallingOrSelfPermission(String permission, String message) { 314 // Don't bother enforcing anything in mock. 315 } 316 317 @Override 318 public void enforcePermission( 319 String permission, int pid, int uid, String message) { 320 // By default, don't enforce anything in mock. 321 } 322 323 @Override 324 public void startActivityAsUser(Intent intent, UserHandle userHandle) { 325 // For capturing 326 } 327 } 328 329 public class FakeAudioManager extends AudioManager { 330 331 private boolean mMute = false; 332 private boolean mSpeakerphoneOn = false; 333 private int mAudioStreamValue = 1; 334 private int mMode = AudioManager.MODE_NORMAL; 335 private int mRingerMode = AudioManager.RINGER_MODE_NORMAL; 336 337 public FakeAudioManager(Context context) { 338 super(context); 339 } 340 341 @Override 342 public void setMicrophoneMute(boolean value) { 343 mMute = value; 344 } 345 346 @Override 347 public boolean isMicrophoneMute() { 348 return mMute; 349 } 350 351 @Override 352 public void setSpeakerphoneOn(boolean value) { 353 mSpeakerphoneOn = value; 354 } 355 356 @Override 357 public boolean isSpeakerphoneOn() { 358 return mSpeakerphoneOn; 359 } 360 361 @Override 362 public void setMode(int mode) { 363 mMode = mode; 364 } 365 366 @Override 367 public int getMode() { 368 return mMode; 369 } 370 371 @Override 372 public void setRingerModeInternal(int ringerMode) { 373 mRingerMode = ringerMode; 374 } 375 376 @Override 377 public int getRingerModeInternal() { 378 return mRingerMode; 379 } 380 381 @Override 382 public void setStreamVolume(int streamTypeUnused, int index, int flagsUnused){ 383 mAudioStreamValue = index; 384 } 385 386 @Override 387 public int getStreamVolume(int streamValueUnused) { 388 return mAudioStreamValue; 389 } 390 } 391 392 private final Multimap<String, ComponentName> mComponentNamesByAction = 393 ArrayListMultimap.create(); 394 private final Map<ComponentName, IInterface> mServiceByComponentName = new HashMap<>(); 395 private final Map<ComponentName, ServiceInfo> mServiceInfoByComponentName = new HashMap<>(); 396 private final Map<IInterface, ComponentName> mComponentNameByService = new HashMap<>(); 397 private final Map<ServiceConnection, IInterface> mServiceByServiceConnection = new HashMap<>(); 398 399 private final Context mContext = new MockContext() { 400 @Override 401 public Context getApplicationContext() { 402 return mApplicationContextSpy; 403 } 404 405 @Override 406 public Resources getResources() { 407 return mResources; 408 } 409 }; 410 411 // The application context is the most important object this class provides to the system 412 // under test. 413 private final Context mApplicationContext = new FakeApplicationContext(); 414 415 // We then create a spy on the application context allowing standard Mockito-style 416 // when(...) logic to be used to add specific little responses where needed. 417 418 private final Resources mResources = mock(Resources.class); 419 private final Context mApplicationContextSpy = spy(mApplicationContext); 420 private final PackageManager mPackageManager = mock(PackageManager.class); 421 private final AudioManager mAudioManager = spy(new FakeAudioManager(mContext)); 422 private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class); 423 private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class); 424 private final NotificationManager mNotificationManager = mock(NotificationManager.class); 425 private final UserManager mUserManager = mock(UserManager.class); 426 private final StatusBarManager mStatusBarManager = mock(StatusBarManager.class); 427 private final SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class); 428 private final CarrierConfigManager mCarrierConfigManager = mock(CarrierConfigManager.class); 429 private final CountryDetector mCountryDetector = mock(CountryDetector.class); 430 private final Map<String, IContentProvider> mIContentProviderByUri = new HashMap<>(); 431 private final Configuration mResourceConfiguration = new Configuration(); 432 private final ApplicationInfo mTestApplicationInfo = new ApplicationInfo(); 433 434 private TelecomManager mTelecomManager = null; 435 436 public ComponentContextFixture() { 437 MockitoAnnotations.initMocks(this); 438 when(mResources.getConfiguration()).thenReturn(mResourceConfiguration); 439 mResourceConfiguration.setLocale(Locale.TAIWAN); 440 441 // TODO: Move into actual tests 442 doReturn(false).when(mAudioManager).isWiredHeadsetOn(); 443 444 doAnswer(new Answer<List<ResolveInfo>>() { 445 @Override 446 public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable { 447 return doQueryIntentServices( 448 (Intent) invocation.getArguments()[0], 449 (Integer) invocation.getArguments()[1]); 450 } 451 }).when(mPackageManager).queryIntentServices((Intent) any(), anyInt()); 452 453 doAnswer(new Answer<List<ResolveInfo>>() { 454 @Override 455 public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable { 456 return doQueryIntentServices( 457 (Intent) invocation.getArguments()[0], 458 (Integer) invocation.getArguments()[1]); 459 } 460 }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt()); 461 462 when(mTelephonyManager.getSubIdForPhoneAccount((PhoneAccount) any())).thenReturn(1); 463 464 when(mTelephonyManager.getNetworkOperatorName()).thenReturn("label1"); 465 466 doAnswer(new Answer<Void>(){ 467 @Override 468 public Void answer(InvocationOnMock invocation) throws Throwable { 469 return null; 470 } 471 }).when(mAppOpsManager).checkPackage(anyInt(), anyString()); 472 473 when(mNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true); 474 475 when(mUserManager.getSerialNumberForUser(any(UserHandle.class))).thenReturn(-1L); 476 477 doReturn(null).when(mApplicationContextSpy).registerReceiver(any(BroadcastReceiver.class), 478 any(IntentFilter.class)); 479 } 480 481 @Override 482 public Context getTestDouble() { 483 return mContext; 484 } 485 486 public void addConnectionService( 487 ComponentName componentName, 488 IConnectionService service) 489 throws Exception { 490 addService(ConnectionService.SERVICE_INTERFACE, componentName, service); 491 ServiceInfo serviceInfo = new ServiceInfo(); 492 serviceInfo.permission = android.Manifest.permission.BIND_CONNECTION_SERVICE; 493 serviceInfo.packageName = componentName.getPackageName(); 494 serviceInfo.name = componentName.getClassName(); 495 mServiceInfoByComponentName.put(componentName, serviceInfo); 496 } 497 498 public void addInCallService( 499 ComponentName componentName, 500 IInCallService service) 501 throws Exception { 502 addService(InCallService.SERVICE_INTERFACE, componentName, service); 503 ServiceInfo serviceInfo = new ServiceInfo(); 504 serviceInfo.permission = android.Manifest.permission.BIND_INCALL_SERVICE; 505 serviceInfo.packageName = componentName.getPackageName(); 506 serviceInfo.name = componentName.getClassName(); 507 mServiceInfoByComponentName.put(componentName, serviceInfo); 508 } 509 510 public void putResource(int id, final String value) { 511 when(mResources.getText(eq(id))).thenReturn(value); 512 when(mResources.getString(eq(id))).thenReturn(value); 513 when(mResources.getString(eq(id), any())).thenAnswer(new Answer<String>() { 514 @Override 515 public String answer(InvocationOnMock invocation) { 516 Object[] args = invocation.getArguments(); 517 return String.format(value, Arrays.copyOfRange(args, 1, args.length)); 518 } 519 }); 520 } 521 522 public void putBooleanResource(int id, boolean value) { 523 when(mResources.getBoolean(eq(id))).thenReturn(value); 524 } 525 526 public void setTelecomManager(TelecomManager telecomManager) { 527 mTelecomManager = telecomManager; 528 } 529 530 private void addService(String action, ComponentName name, IInterface service) { 531 mComponentNamesByAction.put(action, name); 532 mServiceByComponentName.put(name, service); 533 mComponentNameByService.put(service, name); 534 } 535 536 private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) { 537 List<ResolveInfo> result = new ArrayList<>(); 538 for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) { 539 ResolveInfo resolveInfo = new ResolveInfo(); 540 resolveInfo.serviceInfo = mServiceInfoByComponentName.get(componentName); 541 resolveInfo.serviceInfo.metaData = new Bundle(); 542 resolveInfo.serviceInfo.metaData.putBoolean( 543 TelecomManager.METADATA_INCLUDE_EXTERNAL_CALLS, true); 544 result.add(resolveInfo); 545 } 546 return result; 547 } 548} 549