ContextFixture.java revision bc2eba9036e430ea40a47ab708c0386fa567ec9c
1/*
2 * Copyright (C) 2016 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.internal.telephony;
18
19import com.google.common.collect.ArrayListMultimap;
20import com.google.common.collect.Multimap;
21
22import org.mockito.MockitoAnnotations;
23import org.mockito.invocation.InvocationOnMock;
24import org.mockito.stubbing.Answer;
25
26import android.app.AlarmManager;
27import android.app.AppOpsManager;
28import android.app.NotificationManager;
29import android.content.BroadcastReceiver;
30import android.content.ComponentName;
31import android.content.ContentProvider;
32import android.content.ContentResolver;
33import android.content.ContentValues;
34import android.content.Context;
35import android.content.Intent;
36import android.content.IntentFilter;
37import android.content.ServiceConnection;
38import android.content.SharedPreferences;
39import android.content.pm.PackageManager;
40import android.content.pm.ResolveInfo;
41import android.content.pm.ServiceInfo;
42import android.content.res.Configuration;
43import android.content.res.Resources;
44import android.database.Cursor;
45import android.net.ConnectivityManager;
46import android.net.Uri;
47import android.net.wifi.WifiManager;
48import android.os.Bundle;
49import android.os.Handler;
50import android.os.IInterface;
51import android.os.PersistableBundle;
52import android.os.UserHandle;
53import android.os.UserManager;
54import android.preference.PreferenceManager;
55import android.provider.Settings;
56import android.provider.Telephony;
57import android.telephony.CarrierConfigManager;
58import android.telephony.SubscriptionManager;
59import android.telephony.TelephonyManager;
60import android.test.mock.MockContentProvider;
61import android.test.mock.MockContentResolver;
62import android.test.mock.MockContext;
63import android.util.Log;
64
65import java.util.ArrayList;
66import java.util.Arrays;
67import java.util.Collection;
68import java.util.HashMap;
69import java.util.List;
70import java.util.Locale;
71import java.util.Map;
72
73import static org.mockito.Matchers.anyBoolean;
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 * Contains Fake<Component> classes like FakeContext for components that require complex and
89 * reusable stubbing. Others can be mocked using Mockito functions in tests or constructor/public
90 * methods of this class.
91 */
92public class ContextFixture implements TestFixture<Context> {
93    private static final String TAG = "ContextFixture";
94
95    public class FakeContentProvider extends MockContentProvider {
96        @Override
97        public int delete(Uri uri, String selection, String[] selectionArgs) {
98            return 0;
99        }
100
101        @Override
102        public Uri insert(Uri uri, ContentValues values) {
103            if (uri.compareTo(Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw")) == 0) {
104                return Uri.withAppendedPath(uri, "1");
105            }
106            return null;
107        }
108
109        @Override
110        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
111                            String sortOrder) {
112            return null;
113        }
114
115        @Override
116        public Bundle call(String method, String request, Bundle args) {
117            return null;
118        }
119    }
120
121    private final HashMap<String, Object> mSystemServices = new HashMap<String, Object>();
122
123    public void setSystemService(String name, Object service) {
124        synchronized(mSystemServices) {
125            mSystemServices.put(name, service);
126        }
127    }
128
129    public class FakeContext extends MockContext {
130        @Override
131        public PackageManager getPackageManager() {
132            return mPackageManager;
133        }
134
135        @Override
136        public boolean bindService(
137                Intent serviceIntent,
138                ServiceConnection connection,
139                int flags) {
140            if (mServiceByServiceConnection.containsKey(connection)) {
141                throw new RuntimeException("ServiceConnection already bound: " + connection);
142            }
143            IInterface service = mServiceByComponentName.get(serviceIntent.getComponent());
144            if (service == null) {
145                throw new RuntimeException("ServiceConnection not found: "
146                        + serviceIntent.getComponent());
147            }
148            mServiceByServiceConnection.put(connection, service);
149            connection.onServiceConnected(serviceIntent.getComponent(), service.asBinder());
150            return true;
151        }
152
153        @Override
154        public void unbindService(
155                ServiceConnection connection) {
156            IInterface service = mServiceByServiceConnection.remove(connection);
157            if (service == null) {
158                throw new RuntimeException("ServiceConnection not found: " + connection);
159            }
160            connection.onServiceDisconnected(mComponentNameByService.get(service));
161        }
162
163        @Override
164        public Object getSystemService(String name) {
165            switch (name) {
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.USER_SERVICE:
173                    return mUserManager;
174                case Context.CARRIER_CONFIG_SERVICE:
175                    return mCarrierConfigManager;
176                case Context.POWER_SERVICE:
177                    // PowerManager is a final class so cannot be mocked, return real service
178                    return TestApplication.getAppContext().getSystemService(name);
179                case Context.TELEPHONY_SUBSCRIPTION_SERVICE:
180                    return mSubscriptionManager;
181                case Context.WIFI_SERVICE:
182                    return mWifiManager;
183                case Context.ALARM_SERVICE:
184                    return mAlarmManager;
185                case Context.CONNECTIVITY_SERVICE:
186                    return mConnectivityManager;
187                default:
188                    synchronized(mSystemServices) {
189                        return mSystemServices.get(name);
190                    }
191            }
192        }
193
194        @Override
195        public int getUserId() {
196            return 0;
197        }
198
199        @Override
200        public Resources getResources() {
201            return mResources;
202        }
203
204        @Override
205        public String getOpPackageName() {
206            return "com.android.internal.telephony";
207        }
208
209        @Override
210        public ContentResolver getContentResolver() {
211            return mContentResolver;
212        }
213
214        @Override
215        public void unregisterReceiver(BroadcastReceiver receiver) {
216        }
217
218        @Override
219        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
220            Intent result = null;
221            synchronized (mBroadcastReceiversByAction) {
222                for (int i = 0 ; i < filter.countActions(); i++) {
223                    mBroadcastReceiversByAction.put(filter.getAction(i), receiver);
224                    if (result == null) {
225                        result = mStickyBroadcastByAction.get(filter.getAction(i));
226                    }
227                }
228            }
229
230            return result;
231        }
232
233        @Override
234        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
235                String broadcastPermission, Handler scheduler) {
236            return registerReceiver(receiver, filter);
237        }
238
239        @Override
240        public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
241                IntentFilter filter, String broadcastPermission, Handler scheduler) {
242            return registerReceiver(receiver, filter);
243        }
244
245        @Override
246        public void sendBroadcast(Intent intent) {
247            logd("sendBroadcast called for " + intent.getAction());
248            synchronized (mBroadcastReceiversByAction) {
249                for (BroadcastReceiver broadcastReceiver :
250                        mBroadcastReceiversByAction.get(intent.getAction())) {
251                    broadcastReceiver.onReceive(mContext, intent);
252                }
253            }
254        }
255
256        @Override
257        public void sendBroadcast(Intent intent, String receiverPermission) {
258            logd("sendBroadcast called for " + intent.getAction());
259            sendBroadcast(intent);
260        }
261
262        @Override
263        public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
264                String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
265                int initialCode, String initialData, Bundle initialExtras) {
266            logd("sendOrderedBroadcastAsUser called for " + intent.getAction());
267            sendBroadcast(intent);
268            if (resultReceiver != null) {
269                synchronized (mOrderedBroadcastReceivers) {
270                    mOrderedBroadcastReceivers.put(intent, resultReceiver);
271                }
272            }
273        }
274
275        @Override
276        public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
277                String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
278                Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
279            logd("sendOrderedBroadcastAsUser called for " + intent.getAction());
280            sendBroadcast(intent);
281            if (resultReceiver != null) {
282                synchronized (mOrderedBroadcastReceivers) {
283                    mOrderedBroadcastReceivers.put(intent, resultReceiver);
284                }
285            }
286        }
287
288        @Override
289        public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
290                String receiverPermission, int appOp, Bundle options,
291                BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
292                String initialData, Bundle initialExtras) {
293            logd("sendOrderedBroadcastAsUser called for " + intent.getAction());
294            sendBroadcast(intent);
295            if (resultReceiver != null) {
296                synchronized (mOrderedBroadcastReceivers) {
297                    mOrderedBroadcastReceivers.put(intent, resultReceiver);
298                }
299            }
300        }
301
302        @Override
303        public void sendStickyBroadcast(Intent intent) {
304            logd("sendStickyBroadcast called for " + intent.getAction());
305            synchronized (mBroadcastReceiversByAction) {
306                sendBroadcast(intent);
307                mStickyBroadcastByAction.put(intent.getAction(), intent);
308            }
309        }
310
311        @Override
312        public void sendStickyBroadcastAsUser(Intent intent, UserHandle ignored) {
313            logd("sendStickyBroadcastAsUser called for " + intent.getAction());
314            sendStickyBroadcast(intent);
315        }
316
317        @Override
318        public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
319                throws PackageManager.NameNotFoundException {
320            return this;
321        }
322
323        @Override
324        public void enforceCallingOrSelfPermission(String permission, String message) {
325            // Don't bother enforcing anything in mock.
326        }
327
328        @Override
329        public SharedPreferences getSharedPreferences(String name, int mode) {
330            return mSharedPreferences;
331        }
332
333        @Override
334        public String getPackageName() {
335            return "com.android.internal.telephony";
336        }
337
338        public boolean testMethod() {
339            return true;
340        }
341
342        public int testMethod1() {
343            return 0;
344        }
345    }
346
347    private final Multimap<String, ComponentName> mComponentNamesByAction =
348            ArrayListMultimap.create();
349    private final Map<ComponentName, IInterface> mServiceByComponentName =
350            new HashMap<ComponentName, IInterface>();
351    private final Map<ComponentName, ServiceInfo> mServiceInfoByComponentName =
352            new HashMap<ComponentName, ServiceInfo>();
353    private final Map<IInterface, ComponentName> mComponentNameByService =
354            new HashMap<IInterface, ComponentName>();
355    private final Map<ServiceConnection, IInterface> mServiceByServiceConnection =
356            new HashMap<ServiceConnection, IInterface>();
357    private final Multimap<String, BroadcastReceiver> mBroadcastReceiversByAction =
358            ArrayListMultimap.create();
359    private final HashMap<String, Intent> mStickyBroadcastByAction =
360            new HashMap<String, Intent>();
361    private final Multimap<Intent, BroadcastReceiver> mOrderedBroadcastReceivers =
362            ArrayListMultimap.create();
363
364    // The application context is the most important object this class provides to the system
365    // under test.
366    private final Context mContext = spy(new FakeContext());
367
368    // We then create a spy on the application context allowing standard Mockito-style
369    // when(...) logic to be used to add specific little responses where needed.
370
371    private final Resources mResources = mock(Resources.class);
372    private final PackageManager mPackageManager = mock(PackageManager.class);
373    private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
374    private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class);
375    private final NotificationManager mNotificationManager = mock(NotificationManager.class);
376    private final UserManager mUserManager = mock(UserManager.class);
377    private final CarrierConfigManager mCarrierConfigManager = mock(CarrierConfigManager.class);
378    private final SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
379    private final AlarmManager mAlarmManager = mock(AlarmManager.class);
380    private final ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
381    private final WifiManager mWifiManager = mock(WifiManager.class);
382
383    private final ContentProvider mContentProvider = spy(new FakeContentProvider());
384
385    private final Configuration mConfiguration = new Configuration();
386    private final SharedPreferences mSharedPreferences = PreferenceManager.
387            getDefaultSharedPreferences(TestApplication.getAppContext());
388    private final MockContentResolver mContentResolver = new MockContentResolver();
389    private final PersistableBundle mBundle = new PersistableBundle();
390
391    public ContextFixture() {
392        MockitoAnnotations.initMocks(this);
393
394        doAnswer(new Answer<List<ResolveInfo>>() {
395            @Override
396            public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable {
397                return doQueryIntentServices(
398                        (Intent) invocation.getArguments()[0],
399                        (Integer) invocation.getArguments()[1]);
400            }
401        }).when(mPackageManager).queryIntentServices((Intent) any(), anyInt());
402
403        doAnswer(new Answer<List<ResolveInfo>>() {
404            @Override
405            public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable {
406                return doQueryIntentServices(
407                        (Intent) invocation.getArguments()[0],
408                        (Integer) invocation.getArguments()[1]);
409            }
410        }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt());
411
412        doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
413
414        mConfiguration.locale = Locale.US;
415        doReturn(mConfiguration).when(mResources).getConfiguration();
416
417        mContentResolver.addProvider(Telephony.Sms.CONTENT_URI.getAuthority(), mContentProvider);
418        mContentResolver.addProvider(Settings.System.CONTENT_URI.getAuthority(), mContentProvider);
419    }
420
421    @Override
422    public Context getTestDouble() {
423        return mContext;
424    }
425
426    public void putResource(int id, final String value) {
427        when(mResources.getText(eq(id))).thenReturn(value);
428        when(mResources.getString(eq(id))).thenReturn(value);
429        when(mResources.getString(eq(id), any())).thenAnswer(new Answer<String>() {
430            @Override
431            public String answer(InvocationOnMock invocation) {
432                Object[] args = invocation.getArguments();
433                return String.format(value, Arrays.copyOfRange(args, 1, args.length));
434            }
435        });
436    }
437
438    public void putBooleanResource(int id, boolean value) {
439        when(mResources.getBoolean(eq(id))).thenReturn(value);
440    }
441
442    public void putStringArrayResource(int id, String[] values) {
443        doReturn(values).when(mResources).getStringArray(eq(id));
444    }
445
446    public void putIntArrayResource(int id, int[] values) {
447        doReturn(values).when(mResources).getIntArray(eq(id));
448    }
449
450    public PersistableBundle getCarrierConfigBundle() {
451        return mBundle;
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<ResolveInfo>();
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    public void sendBroadcastToOrderedBroadcastReceivers() {
471        synchronized (mOrderedBroadcastReceivers) {
472            // having a map separate from mOrderedBroadcastReceivers is helpful here as onReceive()
473            // call within the loop may lead to sendOrderedBroadcast() which can add to
474            // mOrderedBroadcastReceivers
475            Collection<Map.Entry<Intent, BroadcastReceiver>> map =
476                    mOrderedBroadcastReceivers.entries();
477            for (Map.Entry<Intent, BroadcastReceiver> entry : map) {
478                entry.getValue().onReceive(mContext, entry.getKey());
479                mOrderedBroadcastReceivers.remove(entry.getKey(), entry.getValue());
480            }
481        }
482    }
483
484    private static void logd(String s) {
485        Log.d(TAG, s);
486    }
487}
488