ContextFixture.java revision 6bfc71d2b5340f6274b3e63926a7068e364fc9ff
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            return null;
104        }
105
106        @Override
107        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
108                            String sortOrder) {
109            return null;
110        }
111
112        @Override
113        public Bundle call(String method, String request, Bundle args) {
114            return null;
115        }
116    }
117
118    private final HashMap<String, Object> mSystemServices = new HashMap<String, Object>();
119
120    public void setSystemService(String name, Object service) {
121        synchronized(mSystemServices) {
122            mSystemServices.put(name, service);
123        }
124    }
125
126    public class FakeContext extends MockContext {
127        @Override
128        public PackageManager getPackageManager() {
129            return mPackageManager;
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.TELEPHONY_SERVICE:
164                    return mTelephonyManager;
165                case Context.APP_OPS_SERVICE:
166                    return mAppOpsManager;
167                case Context.NOTIFICATION_SERVICE:
168                    return mNotificationManager;
169                case Context.USER_SERVICE:
170                    return mUserManager;
171                case Context.CARRIER_CONFIG_SERVICE:
172                    return mCarrierConfigManager;
173                case Context.POWER_SERVICE:
174                    // PowerManager is a final class so cannot be mocked, return real service
175                    return TestApplication.getAppContext().getSystemService(name);
176                case Context.TELEPHONY_SUBSCRIPTION_SERVICE:
177                    return mSubscriptionManager;
178                case Context.WIFI_SERVICE:
179                    return mWifiManager;
180                case Context.ALARM_SERVICE:
181                    return mAlarmManager;
182                case Context.CONNECTIVITY_SERVICE:
183                    return mConnectivityManager;
184                default:
185                    synchronized(mSystemServices) {
186                        return mSystemServices.get(name);
187                    }
188            }
189        }
190
191        @Override
192        public int getUserId() {
193            return 0;
194        }
195
196        @Override
197        public Resources getResources() {
198            return mResources;
199        }
200
201        @Override
202        public String getOpPackageName() {
203            return "com.android.internal.telephony";
204        }
205
206        @Override
207        public ContentResolver getContentResolver() {
208            return mContentResolver;
209        }
210
211        @Override
212        public void unregisterReceiver(BroadcastReceiver receiver) {
213        }
214
215        @Override
216        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
217            Intent result = null;
218            synchronized (mBroadcastReceiversByAction) {
219                for (int i = 0 ; i < filter.countActions(); i++) {
220                    mBroadcastReceiversByAction.put(filter.getAction(i), receiver);
221                    if (result == null) {
222                        result = mStickyBroadcastByAction.get(filter.getAction(i));
223                    }
224                }
225            }
226
227            return result;
228        }
229
230        @Override
231        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
232                String broadcastPermission, Handler scheduler) {
233            return registerReceiver(receiver, filter);
234        }
235
236        @Override
237        public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
238                IntentFilter filter, String broadcastPermission, Handler scheduler) {
239            return registerReceiver(receiver, filter);
240        }
241
242        @Override
243        public void sendBroadcast(Intent intent) {
244            logd("sendBroadcast called for " + intent.getAction());
245            synchronized (mBroadcastReceiversByAction) {
246                for (BroadcastReceiver broadcastReceiver :
247                        mBroadcastReceiversByAction.get(intent.getAction())) {
248                    broadcastReceiver.onReceive(mContext, intent);
249                }
250            }
251        }
252
253        @Override
254        public void sendBroadcast(Intent intent, String receiverPermission) {
255            logd("sendBroadcast called for " + intent.getAction());
256            sendBroadcast(intent);
257        }
258
259        @Override
260        public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
261                String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
262                int initialCode, String initialData, Bundle initialExtras) {
263            logd("sendOrderedBroadcastAsUser called for " + intent.getAction());
264            sendBroadcast(intent);
265            if (resultReceiver != null) {
266                synchronized (mOrderedBroadcastReceivers) {
267                    mOrderedBroadcastReceivers.put(intent, resultReceiver);
268                }
269            }
270        }
271
272        @Override
273        public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
274                String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
275                Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
276            logd("sendOrderedBroadcastAsUser called for " + intent.getAction());
277            sendBroadcast(intent);
278            if (resultReceiver != null) {
279                synchronized (mOrderedBroadcastReceivers) {
280                    mOrderedBroadcastReceivers.put(intent, resultReceiver);
281                }
282            }
283        }
284
285        @Override
286        public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
287                String receiverPermission, int appOp, Bundle options,
288                BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
289                String initialData, Bundle initialExtras) {
290            logd("sendOrderedBroadcastAsUser called for " + intent.getAction());
291            sendBroadcast(intent);
292            if (resultReceiver != null) {
293                synchronized (mOrderedBroadcastReceivers) {
294                    mOrderedBroadcastReceivers.put(intent, resultReceiver);
295                }
296            }
297        }
298
299        @Override
300        public void sendStickyBroadcast(Intent intent) {
301            logd("sendStickyBroadcast called for " + intent.getAction());
302            synchronized (mBroadcastReceiversByAction) {
303                sendBroadcast(intent);
304                mStickyBroadcastByAction.put(intent.getAction(), intent);
305            }
306        }
307
308        @Override
309        public void sendStickyBroadcastAsUser(Intent intent, UserHandle ignored) {
310            logd("sendStickyBroadcastAsUser called for " + intent.getAction());
311            sendStickyBroadcast(intent);
312        }
313
314        @Override
315        public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
316                throws PackageManager.NameNotFoundException {
317            return this;
318        }
319
320        @Override
321        public void enforceCallingOrSelfPermission(String permission, String message) {
322            // Don't bother enforcing anything in mock.
323        }
324
325        @Override
326        public SharedPreferences getSharedPreferences(String name, int mode) {
327            return mSharedPreferences;
328        }
329
330        @Override
331        public String getPackageName() {
332            return "com.android.internal.telephony";
333        }
334
335        public boolean testMethod() {
336            return true;
337        }
338
339        public int testMethod1() {
340            return 0;
341        }
342    }
343
344    private final Multimap<String, ComponentName> mComponentNamesByAction =
345            ArrayListMultimap.create();
346    private final Map<ComponentName, IInterface> mServiceByComponentName =
347            new HashMap<ComponentName, IInterface>();
348    private final Map<ComponentName, ServiceInfo> mServiceInfoByComponentName =
349            new HashMap<ComponentName, ServiceInfo>();
350    private final Map<IInterface, ComponentName> mComponentNameByService =
351            new HashMap<IInterface, ComponentName>();
352    private final Map<ServiceConnection, IInterface> mServiceByServiceConnection =
353            new HashMap<ServiceConnection, IInterface>();
354    private final Multimap<String, BroadcastReceiver> mBroadcastReceiversByAction =
355            ArrayListMultimap.create();
356    private final HashMap<String, Intent> mStickyBroadcastByAction =
357            new HashMap<String, Intent>();
358    private final Multimap<Intent, BroadcastReceiver> mOrderedBroadcastReceivers =
359            ArrayListMultimap.create();
360
361    // The application context is the most important object this class provides to the system
362    // under test.
363    private final Context mContext = spy(new FakeContext());
364
365    // We then create a spy on the application context allowing standard Mockito-style
366    // when(...) logic to be used to add specific little responses where needed.
367
368    private final Resources mResources = mock(Resources.class);
369    private final PackageManager mPackageManager = mock(PackageManager.class);
370    private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
371    private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class);
372    private final NotificationManager mNotificationManager = mock(NotificationManager.class);
373    private final UserManager mUserManager = mock(UserManager.class);
374    private final CarrierConfigManager mCarrierConfigManager = mock(CarrierConfigManager.class);
375    private final SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
376    private final AlarmManager mAlarmManager = mock(AlarmManager.class);
377    private final ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
378    private final WifiManager mWifiManager = mock(WifiManager.class);
379
380    private final ContentProvider mContentProvider = spy(new FakeContentProvider());
381
382    private final Configuration mConfiguration = new Configuration();
383    private final SharedPreferences mSharedPreferences = PreferenceManager.
384            getDefaultSharedPreferences(TestApplication.getAppContext());
385    private final MockContentResolver mContentResolver = new MockContentResolver();
386    private final PersistableBundle mBundle = new PersistableBundle();
387
388    public ContextFixture() {
389        MockitoAnnotations.initMocks(this);
390
391        doAnswer(new Answer<List<ResolveInfo>>() {
392            @Override
393            public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable {
394                return doQueryIntentServices(
395                        (Intent) invocation.getArguments()[0],
396                        (Integer) invocation.getArguments()[1]);
397            }
398        }).when(mPackageManager).queryIntentServices((Intent) any(), anyInt());
399
400        doAnswer(new Answer<List<ResolveInfo>>() {
401            @Override
402            public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable {
403                return doQueryIntentServices(
404                        (Intent) invocation.getArguments()[0],
405                        (Integer) invocation.getArguments()[1]);
406            }
407        }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt());
408
409        doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
410
411        mConfiguration.locale = Locale.US;
412        doReturn(mConfiguration).when(mResources).getConfiguration();
413
414        mContentResolver.addProvider(Settings.System.CONTENT_URI.getAuthority(), mContentProvider);
415    }
416
417    @Override
418    public Context getTestDouble() {
419        return mContext;
420    }
421
422    public void putResource(int id, final String value) {
423        when(mResources.getText(eq(id))).thenReturn(value);
424        when(mResources.getString(eq(id))).thenReturn(value);
425        when(mResources.getString(eq(id), any())).thenAnswer(new Answer<String>() {
426            @Override
427            public String answer(InvocationOnMock invocation) {
428                Object[] args = invocation.getArguments();
429                return String.format(value, Arrays.copyOfRange(args, 1, args.length));
430            }
431        });
432    }
433
434    public void putBooleanResource(int id, boolean value) {
435        when(mResources.getBoolean(eq(id))).thenReturn(value);
436    }
437
438    public void putStringArrayResource(int id, String[] values) {
439        doReturn(values).when(mResources).getStringArray(eq(id));
440    }
441
442    public void putIntArrayResource(int id, int[] values) {
443        doReturn(values).when(mResources).getIntArray(eq(id));
444    }
445
446    public PersistableBundle getCarrierConfigBundle() {
447        return mBundle;
448    }
449
450    private void addService(String action, ComponentName name, IInterface service) {
451        mComponentNamesByAction.put(action, name);
452        mServiceByComponentName.put(name, service);
453        mComponentNameByService.put(service, name);
454    }
455
456    private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) {
457        List<ResolveInfo> result = new ArrayList<ResolveInfo>();
458        for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) {
459            ResolveInfo resolveInfo = new ResolveInfo();
460            resolveInfo.serviceInfo = mServiceInfoByComponentName.get(componentName);
461            result.add(resolveInfo);
462        }
463        return result;
464    }
465
466    public void sendBroadcastToOrderedBroadcastReceivers() {
467        synchronized (mOrderedBroadcastReceivers) {
468            // having a map separate from mOrderedBroadcastReceivers is helpful here as onReceive()
469            // call within the loop may lead to sendOrderedBroadcast() which can add to
470            // mOrderedBroadcastReceivers
471            Collection<Map.Entry<Intent, BroadcastReceiver>> map =
472                    mOrderedBroadcastReceivers.entries();
473            for (Map.Entry<Intent, BroadcastReceiver> entry : map) {
474                entry.getValue().onReceive(mContext, entry.getKey());
475                mOrderedBroadcastReceivers.remove(entry.getKey(), entry.getValue());
476            }
477        }
478    }
479
480    private static void logd(String s) {
481        Log.d(TAG, s);
482    }
483}
484