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