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