ComponentContextFixture.java revision 877267255abf10ed14ccc88bf5201e48ab867f81
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 mock(IContentProvider.class);
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 mock(IContentProvider.class);
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 Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
229                String broadcastPermission, Handler scheduler) {
230            return null;
231        }
232
233        @Override
234        public void sendBroadcast(Intent intent) {
235            // TODO -- need to ensure this is captured
236        }
237
238        @Override
239        public void sendBroadcast(Intent intent, String receiverPermission) {
240            // TODO -- need to ensure this is captured
241        }
242
243        @Override
244        public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
245                String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
246                int initialCode, String initialData, Bundle initialExtras) {
247            // TODO -- need to ensure this is captured
248        }
249
250        @Override
251        public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
252                String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
253                Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
254        }
255
256        @Override
257        public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
258                throws PackageManager.NameNotFoundException {
259            return this;
260        }
261    };
262
263    public class FakeAudioManager extends AudioManager {
264
265        private boolean mMute = false;
266        private boolean mSpeakerphoneOn = false;
267        private int mMode = AudioManager.MODE_NORMAL;
268
269        public FakeAudioManager(Context context) {
270            super(context);
271        }
272
273        @Override
274        public void setMicrophoneMute(boolean value) {
275            mMute = value;
276        }
277
278        @Override
279        public boolean isMicrophoneMute() {
280            return mMute;
281        }
282
283        @Override
284        public void setSpeakerphoneOn(boolean value) {
285            mSpeakerphoneOn = value;
286        }
287
288        @Override
289        public boolean isSpeakerphoneOn() {
290            return mSpeakerphoneOn;
291        }
292
293        @Override
294        public void setMode(int mode) {
295            mMode = mode;
296        }
297
298        @Override
299        public int getMode() {
300            return mMode;
301        }
302    }
303
304    private final Multimap<String, ComponentName> mComponentNamesByAction =
305            ArrayListMultimap.create();
306    private final Map<ComponentName, IInterface> mServiceByComponentName = new HashMap<>();
307    private final Map<ComponentName, ServiceInfo> mServiceInfoByComponentName = new HashMap<>();
308    private final Map<IInterface, ComponentName> mComponentNameByService = new HashMap<>();
309    private final Map<ServiceConnection, IInterface> mServiceByServiceConnection = new HashMap<>();
310
311    private final Context mContext = new MockContext() {
312        @Override
313        public Context getApplicationContext() {
314            return mApplicationContextSpy;
315        }
316
317        @Override
318        public Resources getResources() {
319            return mResources;
320        }
321    };
322
323    // The application context is the most important object this class provides to the system
324    // under test.
325    private final Context mApplicationContext = new FakeApplicationContext();
326
327    // We then create a spy on the application context allowing standard Mockito-style
328    // when(...) logic to be used to add specific little responses where needed.
329
330    private final Resources mResources = mock(Resources.class);
331    private final Context mApplicationContextSpy = spy(mApplicationContext);
332    private final PackageManager mPackageManager = mock(PackageManager.class);
333    private final AudioManager mAudioManager = spy(new FakeAudioManager(mContext));
334    private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
335    private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class);
336    private final NotificationManager mNotificationManager = mock(NotificationManager.class);
337    private final UserManager mUserManager = mock(UserManager.class);
338    private final StatusBarManager mStatusBarManager = mock(StatusBarManager.class);
339    private final SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
340    private final CarrierConfigManager mCarrierConfigManager = mock(CarrierConfigManager.class);
341    private final Configuration mResourceConfiguration = new Configuration();
342
343    private TelecomManager mTelecomManager = null;
344
345    public ComponentContextFixture() {
346        MockitoAnnotations.initMocks(this);
347        when(mResources.getConfiguration()).thenReturn(mResourceConfiguration);
348        mResourceConfiguration.setLocale(Locale.TAIWAN);
349
350        // TODO: Move into actual tests
351        when(mAudioManager.isWiredHeadsetOn()).thenReturn(false);
352
353        doAnswer(new Answer<List<ResolveInfo>>() {
354            @Override
355            public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable {
356                return doQueryIntentServices(
357                        (Intent) invocation.getArguments()[0],
358                        (Integer) invocation.getArguments()[1]);
359            }
360        }).when(mPackageManager).queryIntentServices((Intent) any(), anyInt());
361
362        doAnswer(new Answer<List<ResolveInfo>>() {
363            @Override
364            public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable {
365                return doQueryIntentServices(
366                        (Intent) invocation.getArguments()[0],
367                        (Integer) invocation.getArguments()[1]);
368            }
369        }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt());
370
371        when(mTelephonyManager.getSubIdForPhoneAccount((PhoneAccount) any())).thenReturn(1);
372
373        doAnswer(new Answer<Void>(){
374            @Override
375            public Void answer(InvocationOnMock invocation) throws Throwable {
376                return null;
377            }
378        }).when(mAppOpsManager).checkPackage(anyInt(), anyString());
379
380        when(mNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
381
382        when(mUserManager.getSerialNumberForUser(any(UserHandle.class))).thenReturn(-1L);
383
384        doReturn(null).when(mApplicationContextSpy).registerReceiver(any(BroadcastReceiver.class),
385                any(IntentFilter.class));
386    }
387
388    @Override
389    public Context getTestDouble() {
390        return mContext;
391    }
392
393    public void addConnectionService(
394            ComponentName componentName,
395            IConnectionService service)
396            throws Exception {
397        addService(ConnectionService.SERVICE_INTERFACE, componentName, service);
398        ServiceInfo serviceInfo = new ServiceInfo();
399        serviceInfo.permission = android.Manifest.permission.BIND_CONNECTION_SERVICE;
400        serviceInfo.packageName = componentName.getPackageName();
401        serviceInfo.name = componentName.getClassName();
402        mServiceInfoByComponentName.put(componentName, serviceInfo);
403    }
404
405    public void addInCallService(
406            ComponentName componentName,
407            IInCallService service)
408            throws Exception {
409        addService(InCallService.SERVICE_INTERFACE, componentName, service);
410        ServiceInfo serviceInfo = new ServiceInfo();
411        serviceInfo.permission = android.Manifest.permission.BIND_INCALL_SERVICE;
412        serviceInfo.packageName = componentName.getPackageName();
413        serviceInfo.name = componentName.getClassName();
414        mServiceInfoByComponentName.put(componentName, serviceInfo);
415    }
416
417    public void putResource(int id, final String value) {
418        when(mResources.getText(eq(id))).thenReturn(value);
419        when(mResources.getString(eq(id))).thenReturn(value);
420        when(mResources.getString(eq(id), any())).thenAnswer(new Answer<String>() {
421            @Override
422            public String answer(InvocationOnMock invocation) {
423                Object[] args = invocation.getArguments();
424                return String.format(value, Arrays.copyOfRange(args, 1, args.length));
425            }
426        });
427    }
428
429    public void setTelecomManager(TelecomManager telecomManager) {
430        mTelecomManager = telecomManager;
431    }
432
433    private void addService(String action, ComponentName name, IInterface service) {
434        mComponentNamesByAction.put(action, name);
435        mServiceByComponentName.put(name, service);
436        mComponentNameByService.put(service, name);
437    }
438
439    private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) {
440        List<ResolveInfo> result = new ArrayList<>();
441        for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) {
442            ResolveInfo resolveInfo = new ResolveInfo();
443            resolveInfo.serviceInfo = mServiceInfoByComponentName.get(componentName);
444            result.add(resolveInfo);
445        }
446        return result;
447    }
448}
449