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