ComponentContextFixture.java revision ada0301a8190568d90adba04bd1f555b7894a0e1
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 File getFilesDir() {
98            try {
99                return File.createTempFile("temp", "temp").getParentFile();
100            } catch (IOException e) {
101                throw new RuntimeException(e);
102            }
103        }
104
105        @Override
106        public boolean bindServiceAsUser(
107                Intent serviceIntent,
108                ServiceConnection connection,
109                int flags,
110                UserHandle userHandle) {
111            // TODO: Implement "as user" functionality
112            return bindService(serviceIntent, connection, flags);
113        }
114
115        @Override
116        public boolean bindService(
117                Intent serviceIntent,
118                ServiceConnection connection,
119                int flags) {
120            if (mServiceByServiceConnection.containsKey(connection)) {
121                throw new RuntimeException("ServiceConnection already bound: " + connection);
122            }
123            IInterface service = mServiceByComponentName.get(serviceIntent.getComponent());
124            if (service == null) {
125                throw new RuntimeException("ServiceConnection not found: "
126                        + serviceIntent.getComponent());
127            }
128            mServiceByServiceConnection.put(connection, service);
129            connection.onServiceConnected(serviceIntent.getComponent(), service.asBinder());
130            return true;
131        }
132
133        @Override
134        public void unbindService(
135                ServiceConnection connection) {
136            IInterface service = mServiceByServiceConnection.remove(connection);
137            if (service == null) {
138                throw new RuntimeException("ServiceConnection not found: " + connection);
139            }
140            connection.onServiceDisconnected(mComponentNameByService.get(service));
141        }
142
143        @Override
144        public Object getSystemService(String name) {
145            switch (name) {
146                case Context.AUDIO_SERVICE:
147                    return mAudioManager;
148                case Context.TELEPHONY_SERVICE:
149                    return mTelephonyManager;
150                case Context.APP_OPS_SERVICE:
151                    return mAppOpsManager;
152                case Context.NOTIFICATION_SERVICE:
153                    return mNotificationManager;
154                case Context.STATUS_BAR_SERVICE:
155                    return mStatusBarManager;
156                case Context.USER_SERVICE:
157                    return mUserManager;
158                case Context.TELEPHONY_SUBSCRIPTION_SERVICE:
159                    return mSubscriptionManager;
160                case Context.TELECOM_SERVICE:
161                    return mTelecomManager;
162                case Context.CARRIER_CONFIG_SERVICE:
163                    return mCarrierConfigManager;
164                default:
165                    return null;
166            }
167        }
168
169        @Override
170        public int getUserId() {
171            return 0;
172        }
173
174        @Override
175        public Resources getResources() {
176            return mResources;
177        }
178
179        @Override
180        public String getOpPackageName() {
181            return "com.android.server.telecom.tests";
182        }
183
184        @Override
185        public ContentResolver getContentResolver() {
186            return new ContentResolver(mApplicationContextSpy) {
187                @Override
188                protected IContentProvider acquireProvider(Context c, String name) {
189                    Log.i(this, "acquireProvider %s", name);
190                    return mContentProvider;
191                }
192
193                @Override
194                public boolean releaseProvider(IContentProvider icp) {
195                    return true;
196                }
197
198                @Override
199                protected IContentProvider acquireUnstableProvider(Context c, String name) {
200                    Log.i(this, "acquireUnstableProvider %s", name);
201                    return mContentProvider;
202                }
203
204                @Override
205                public boolean releaseUnstableProvider(IContentProvider icp) {
206                    return false;
207                }
208
209                @Override
210                public void unstableProviderDied(IContentProvider icp) {
211                }
212            };
213        }
214
215        @Override
216        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
217            // TODO -- this is called by WiredHeadsetManager!!!
218            return null;
219        }
220
221        @Override
222        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
223                String broadcastPermission, Handler scheduler) {
224            return null;
225        }
226
227        @Override
228        public void sendBroadcast(Intent intent) {
229            // TODO -- need to ensure this is captured
230        }
231
232        @Override
233        public void sendBroadcast(Intent intent, String receiverPermission) {
234            // TODO -- need to ensure this is captured
235        }
236
237        @Override
238        public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
239                String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
240                int initialCode, String initialData, Bundle initialExtras) {
241            // TODO -- need to ensure this is captured
242        }
243
244        @Override
245        public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
246                String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
247                Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
248        }
249
250        @Override
251        public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
252                throws PackageManager.NameNotFoundException {
253            return this;
254        }
255    };
256
257    public class FakeAudioManager extends AudioManager {
258
259        private boolean mMute = false;
260        private boolean mSpeakerphoneOn = false;
261        private int mAudioStreamValue = 1;
262        private int mMode = AudioManager.MODE_NORMAL;
263        private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
264
265        public FakeAudioManager(Context context) {
266            super(context);
267        }
268
269        @Override
270        public void setMicrophoneMute(boolean value) {
271            mMute = value;
272        }
273
274        @Override
275        public boolean isMicrophoneMute() {
276            return mMute;
277        }
278
279        @Override
280        public void setSpeakerphoneOn(boolean value) {
281            mSpeakerphoneOn = value;
282        }
283
284        @Override
285        public boolean isSpeakerphoneOn() {
286            return mSpeakerphoneOn;
287        }
288
289        @Override
290        public void setMode(int mode) {
291            mMode = mode;
292        }
293
294        @Override
295        public int getMode() {
296            return mMode;
297        }
298
299        @Override
300        public void setRingerModeInternal(int ringerMode) {
301            mRingerMode = ringerMode;
302        }
303
304        @Override
305        public int getRingerModeInternal() {
306            return mRingerMode;
307        }
308
309        @Override
310        public void setStreamVolume(int streamTypeUnused, int index, int flagsUnused){
311            mAudioStreamValue = index;
312        }
313
314        @Override
315        public int getStreamVolume(int streamValueUnused) {
316            return mAudioStreamValue;
317        }
318    }
319
320    private final Multimap<String, ComponentName> mComponentNamesByAction =
321            ArrayListMultimap.create();
322    private final Map<ComponentName, IInterface> mServiceByComponentName = new HashMap<>();
323    private final Map<ComponentName, ServiceInfo> mServiceInfoByComponentName = new HashMap<>();
324    private final Map<IInterface, ComponentName> mComponentNameByService = new HashMap<>();
325    private final Map<ServiceConnection, IInterface> mServiceByServiceConnection = new HashMap<>();
326
327    private final Context mContext = new MockContext() {
328        @Override
329        public Context getApplicationContext() {
330            return mApplicationContextSpy;
331        }
332
333        @Override
334        public Resources getResources() {
335            return mResources;
336        }
337    };
338
339    // The application context is the most important object this class provides to the system
340    // under test.
341    private final Context mApplicationContext = new FakeApplicationContext();
342
343    // We then create a spy on the application context allowing standard Mockito-style
344    // when(...) logic to be used to add specific little responses where needed.
345
346    private final Resources mResources = mock(Resources.class);
347    private final Context mApplicationContextSpy = spy(mApplicationContext);
348    private final PackageManager mPackageManager = mock(PackageManager.class);
349    private final AudioManager mAudioManager = spy(new FakeAudioManager(mContext));
350    private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
351    private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class);
352    private final NotificationManager mNotificationManager = mock(NotificationManager.class);
353    private final UserManager mUserManager = mock(UserManager.class);
354    private final StatusBarManager mStatusBarManager = mock(StatusBarManager.class);
355    private final SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
356    private final CarrierConfigManager mCarrierConfigManager = mock(CarrierConfigManager.class);
357    private final IContentProvider mContentProvider = mock(IContentProvider.class);
358    private final Configuration mResourceConfiguration = new Configuration();
359
360    private TelecomManager mTelecomManager = null;
361
362    public ComponentContextFixture() {
363        MockitoAnnotations.initMocks(this);
364        when(mResources.getConfiguration()).thenReturn(mResourceConfiguration);
365        mResourceConfiguration.setLocale(Locale.TAIWAN);
366
367        // TODO: Move into actual tests
368        when(mAudioManager.isWiredHeadsetOn()).thenReturn(false);
369
370        doAnswer(new Answer<List<ResolveInfo>>() {
371            @Override
372            public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable {
373                return doQueryIntentServices(
374                        (Intent) invocation.getArguments()[0],
375                        (Integer) invocation.getArguments()[1]);
376            }
377        }).when(mPackageManager).queryIntentServices((Intent) any(), anyInt());
378
379        doAnswer(new Answer<List<ResolveInfo>>() {
380            @Override
381            public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable {
382                return doQueryIntentServices(
383                        (Intent) invocation.getArguments()[0],
384                        (Integer) invocation.getArguments()[1]);
385            }
386        }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt());
387
388        when(mTelephonyManager.getSubIdForPhoneAccount((PhoneAccount) any())).thenReturn(1);
389
390        doAnswer(new Answer<Void>(){
391            @Override
392            public Void answer(InvocationOnMock invocation) throws Throwable {
393                return null;
394            }
395        }).when(mAppOpsManager).checkPackage(anyInt(), anyString());
396
397        when(mNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
398
399        when(mUserManager.getSerialNumberForUser(any(UserHandle.class))).thenReturn(-1L);
400
401        doReturn(null).when(mApplicationContextSpy).registerReceiver(any(BroadcastReceiver.class),
402                any(IntentFilter.class));
403    }
404
405    @Override
406    public Context getTestDouble() {
407        return mContext;
408    }
409
410    public void addConnectionService(
411            ComponentName componentName,
412            IConnectionService service)
413            throws Exception {
414        addService(ConnectionService.SERVICE_INTERFACE, componentName, service);
415        ServiceInfo serviceInfo = new ServiceInfo();
416        serviceInfo.permission = android.Manifest.permission.BIND_CONNECTION_SERVICE;
417        serviceInfo.packageName = componentName.getPackageName();
418        serviceInfo.name = componentName.getClassName();
419        mServiceInfoByComponentName.put(componentName, serviceInfo);
420    }
421
422    public void addInCallService(
423            ComponentName componentName,
424            IInCallService service)
425            throws Exception {
426        addService(InCallService.SERVICE_INTERFACE, componentName, service);
427        ServiceInfo serviceInfo = new ServiceInfo();
428        serviceInfo.permission = android.Manifest.permission.BIND_INCALL_SERVICE;
429        serviceInfo.packageName = componentName.getPackageName();
430        serviceInfo.name = componentName.getClassName();
431        mServiceInfoByComponentName.put(componentName, serviceInfo);
432    }
433
434    public void putResource(int id, final String value) {
435        when(mResources.getText(eq(id))).thenReturn(value);
436        when(mResources.getString(eq(id))).thenReturn(value);
437        when(mResources.getString(eq(id), any())).thenAnswer(new Answer<String>() {
438            @Override
439            public String answer(InvocationOnMock invocation) {
440                Object[] args = invocation.getArguments();
441                return String.format(value, Arrays.copyOfRange(args, 1, args.length));
442            }
443        });
444    }
445
446    public void putBooleanResource(int id, boolean value) {
447        when(mResources.getBoolean(eq(id))).thenReturn(value);
448    }
449
450    public void setTelecomManager(TelecomManager telecomManager) {
451        mTelecomManager = telecomManager;
452    }
453
454    private void addService(String action, ComponentName name, IInterface service) {
455        mComponentNamesByAction.put(action, name);
456        mServiceByComponentName.put(name, service);
457        mComponentNameByService.put(service, name);
458    }
459
460    private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) {
461        List<ResolveInfo> result = new ArrayList<>();
462        for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) {
463            ResolveInfo resolveInfo = new ResolveInfo();
464            resolveInfo.serviceInfo = mServiceInfoByComponentName.get(componentName);
465            result.add(resolveInfo);
466        }
467        return result;
468    }
469}
470