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.app.usage.UsageStatsManager;
30import android.content.BroadcastReceiver;
31import android.content.ComponentName;
32import android.content.ContentProvider;
33import android.content.ContentResolver;
34import android.content.ContentValues;
35import android.content.Context;
36import android.content.Intent;
37import android.content.IntentFilter;
38import android.content.ServiceConnection;
39import android.content.SharedPreferences;
40import android.content.pm.PackageManager;
41import android.content.pm.ResolveInfo;
42import android.content.pm.ServiceInfo;
43import android.content.res.Configuration;
44import android.content.res.Resources;
45import android.database.Cursor;
46import android.database.MatrixCursor;
47import android.net.ConnectivityManager;
48import android.net.Uri;
49import android.net.wifi.WifiManager;
50import android.os.Bundle;
51import android.os.Handler;
52import android.os.IInterface;
53import android.os.PersistableBundle;
54import android.os.UserHandle;
55import android.os.UserManager;
56import android.preference.PreferenceManager;
57import android.provider.Settings;
58import android.telephony.CarrierConfigManager;
59import android.telephony.SubscriptionManager;
60import android.telephony.TelephonyManager;
61import android.test.mock.MockContentProvider;
62import android.test.mock.MockContentResolver;
63import android.test.mock.MockContext;
64import android.util.Log;
65
66import java.util.ArrayList;
67import java.util.Arrays;
68import java.util.Collection;
69import java.util.HashMap;
70import java.util.HashSet;
71import java.util.List;
72import java.util.Locale;
73import java.util.Map;
74
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    public static final String PERMISSION_ENABLE_ALL = "android.permission.STUB_PERMISSION";
95
96    public class FakeContentProvider extends MockContentProvider {
97        private String[] mColumns = {"name", "value"};
98        private HashMap<String, String> mKeyValuePairs = new HashMap<String, String>();
99        private int mNumKeyValuePairs = 0;
100
101        @Override
102        public int delete(Uri uri, String selection, String[] selectionArgs) {
103            return 0;
104        }
105
106        @Override
107        public Uri insert(Uri uri, ContentValues values) {
108            Uri newUri = null;
109            if (values != null) {
110                mKeyValuePairs.put(values.getAsString("name"), values.getAsString("value"));
111                mNumKeyValuePairs++;
112                newUri = Uri.withAppendedPath(uri, "" + mNumKeyValuePairs);
113            }
114            logd("insert called, new mNumKeyValuePairs: " + mNumKeyValuePairs + " uri: " + uri +
115                    " newUri: " + newUri);
116            return newUri;
117        }
118
119        @Override
120        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
121                            String sortOrder) {
122            //assuming query will always be of the form 'name = ?'
123            logd("query called, mNumKeyValuePairs: " + mNumKeyValuePairs + " uri: " + uri);
124            if (mKeyValuePairs.containsKey(selectionArgs[0])) {
125                MatrixCursor cursor = new MatrixCursor(projection);
126                cursor.addRow(new String[]{mKeyValuePairs.get(selectionArgs[0])});
127                return cursor;
128            }
129            return null;
130        }
131
132        @Override
133        public Bundle call(String method, String request, Bundle args) {
134            logd("call called, mNumKeyValuePairs: " + mNumKeyValuePairs + " method: " + method +
135                    " request: " + request);
136            switch(method) {
137                case Settings.CALL_METHOD_GET_GLOBAL:
138                case Settings.CALL_METHOD_GET_SECURE:
139                case Settings.CALL_METHOD_GET_SYSTEM:
140                    if (mKeyValuePairs.containsKey(request)) {
141                        Bundle b = new Bundle(1);
142                        b.putCharSequence("value", mKeyValuePairs.get(request));
143                        logd("returning value pair: " + mKeyValuePairs.get(request) + " for " +
144                                request);
145                        return b;
146                    }
147                    break;
148                case Settings.CALL_METHOD_PUT_GLOBAL:
149                case Settings.CALL_METHOD_PUT_SECURE:
150                case Settings.CALL_METHOD_PUT_SYSTEM:
151                    logd("adding key-value pair: " + request + "-" + (String)args.get("value"));
152                    mKeyValuePairs.put(request, (String)args.get("value"));
153                    mNumKeyValuePairs++;
154                    break;
155            }
156            return null;
157        }
158    }
159
160    private final HashMap<String, Object> mSystemServices = new HashMap<String, Object>();
161
162    public void setSystemService(String name, Object service) {
163        synchronized (mSystemServices) {
164            mSystemServices.put(name, service);
165        }
166    }
167
168    public class FakeContext extends MockContext {
169        @Override
170        public PackageManager getPackageManager() {
171            return mPackageManager;
172        }
173
174        @Override
175        public boolean bindService(
176                Intent serviceIntent,
177                ServiceConnection connection,
178                int flags) {
179            if (mServiceByServiceConnection.containsKey(connection)) {
180                throw new RuntimeException("ServiceConnection already bound: " + connection);
181            }
182            IInterface service = mServiceByComponentName.get(serviceIntent.getComponent());
183            if (service == null) {
184                throw new RuntimeException("ServiceConnection not found: "
185                        + serviceIntent.getComponent());
186            }
187            mServiceByServiceConnection.put(connection, service);
188            connection.onServiceConnected(serviceIntent.getComponent(), service.asBinder());
189            return true;
190        }
191
192        @Override
193        public void unbindService(
194                ServiceConnection connection) {
195            IInterface service = mServiceByServiceConnection.remove(connection);
196            if (service == null) {
197                throw new RuntimeException("ServiceConnection not found: " + connection);
198            }
199            connection.onServiceDisconnected(mComponentNameByService.get(service));
200        }
201
202        @Override
203        public Object getSystemService(String name) {
204            synchronized (mSystemServices) {
205                Object service = mSystemServices.get(name);
206                if (service != null) return service;
207            }
208            switch (name) {
209                case Context.TELEPHONY_SERVICE:
210                    return mTelephonyManager;
211                case Context.APP_OPS_SERVICE:
212                    return mAppOpsManager;
213                case Context.NOTIFICATION_SERVICE:
214                    return mNotificationManager;
215                case Context.USER_SERVICE:
216                    return mUserManager;
217                case Context.CARRIER_CONFIG_SERVICE:
218                    return mCarrierConfigManager;
219                case Context.POWER_SERVICE:
220                    // PowerManager is a final class so cannot be mocked, return real service
221                    return TestApplication.getAppContext().getSystemService(name);
222                case Context.TELEPHONY_SUBSCRIPTION_SERVICE:
223                    return mSubscriptionManager;
224                case Context.WIFI_SERVICE:
225                    return mWifiManager;
226                case Context.ALARM_SERVICE:
227                    return mAlarmManager;
228                case Context.CONNECTIVITY_SERVICE:
229                    return mConnectivityManager;
230                case Context.USAGE_STATS_SERVICE:
231                    return mUsageStatManager;
232                default:
233                    return null;
234            }
235        }
236
237        @Override
238        public int getUserId() {
239            return 0;
240        }
241
242        @Override
243        public Resources getResources() {
244            return mResources;
245        }
246
247        @Override
248        public String getOpPackageName() {
249            return "com.android.internal.telephony";
250        }
251
252        @Override
253        public ContentResolver getContentResolver() {
254            return mContentResolver;
255        }
256
257        @Override
258        public void unregisterReceiver(BroadcastReceiver receiver) {
259        }
260
261        @Override
262        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
263            return registerReceiver(receiver, filter, null, null);
264        }
265
266        @Override
267        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
268                String broadcastPermission, Handler scheduler) {
269            return registerReceiverAsUser(receiver, null, filter, broadcastPermission, scheduler);
270        }
271
272        @Override
273        public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
274                IntentFilter filter, String broadcastPermission, Handler scheduler) {
275            Intent result = null;
276            synchronized (mBroadcastReceiversByAction) {
277                for (int i = 0 ; i < filter.countActions() ; i++) {
278                    mBroadcastReceiversByAction.put(filter.getAction(i), receiver);
279                    if (result == null) {
280                        result = mStickyBroadcastByAction.get(filter.getAction(i));
281                    }
282                }
283            }
284
285            return result;
286        }
287
288        @Override
289        public void sendBroadcast(Intent intent) {
290            logd("sendBroadcast called for " + intent.getAction());
291            synchronized (mBroadcastReceiversByAction) {
292                for (BroadcastReceiver broadcastReceiver :
293                        mBroadcastReceiversByAction.get(intent.getAction())) {
294                    broadcastReceiver.onReceive(mContext, intent);
295                }
296            }
297        }
298
299        @Override
300        public void sendBroadcast(Intent intent, String receiverPermission) {
301            logd("sendBroadcast called for " + intent.getAction());
302            sendBroadcast(intent);
303        }
304
305        @Override
306        public void sendOrderedBroadcast(Intent intent, String receiverPermission) {
307            logd("sendOrderedBroadcast called for " + intent.getAction());
308            sendBroadcast(intent);
309        }
310
311        @Override
312        public void sendOrderedBroadcast(Intent intent, String receiverPermission,
313                BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
314                String initialData, Bundle initialExtras) {
315            sendOrderedBroadcast(intent, receiverPermission);
316            if (resultReceiver != null) {
317                synchronized (mOrderedBroadcastReceivers) {
318                    mOrderedBroadcastReceivers.put(intent, resultReceiver);
319                }
320            }
321        }
322
323        @Override
324        public void sendOrderedBroadcast(Intent intent, String receiverPermission, Bundle options,
325                BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
326                String initialData, Bundle initialExtras) {
327            sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler,
328                    initialCode, initialData, initialExtras);
329        }
330
331        @Override
332        public void sendOrderedBroadcast(Intent intent, String receiverPermission, int appOp,
333                BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
334                String initialData, Bundle initialExtras) {
335            sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler,
336                    initialCode, initialData, initialExtras);
337        }
338
339        @Override
340        public void sendBroadcastAsUser(Intent intent, UserHandle user) {
341            sendBroadcast(intent);
342        }
343
344        @Override
345        public void sendBroadcastAsUser(Intent intent, UserHandle user,
346                                        String receiverPermission) {
347            sendBroadcast(intent);
348        }
349
350        @Override
351        public void sendBroadcastAsUser(Intent intent, UserHandle user,
352                                        String receiverPermission, int appOp) {
353            sendBroadcast(intent);
354        }
355
356        @Override
357        public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
358                String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
359                int initialCode, String initialData, Bundle initialExtras) {
360            logd("sendOrderedBroadcastAsUser called for " + intent.getAction());
361            sendBroadcast(intent);
362            if (resultReceiver != null) {
363                synchronized (mOrderedBroadcastReceivers) {
364                    mOrderedBroadcastReceivers.put(intent, resultReceiver);
365                }
366            }
367        }
368
369        @Override
370        public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
371                String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
372                Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
373            logd("sendOrderedBroadcastAsUser called for " + intent.getAction());
374            sendBroadcast(intent);
375            if (resultReceiver != null) {
376                synchronized (mOrderedBroadcastReceivers) {
377                    mOrderedBroadcastReceivers.put(intent, resultReceiver);
378                }
379            }
380        }
381
382        @Override
383        public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
384                String receiverPermission, int appOp, Bundle options,
385                BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
386                String initialData, Bundle initialExtras) {
387            logd("sendOrderedBroadcastAsUser called for " + intent.getAction());
388            sendBroadcast(intent);
389            if (resultReceiver != null) {
390                synchronized (mOrderedBroadcastReceivers) {
391                    mOrderedBroadcastReceivers.put(intent, resultReceiver);
392                }
393            }
394        }
395
396        @Override
397        public void sendStickyBroadcast(Intent intent) {
398            logd("sendStickyBroadcast called for " + intent.getAction());
399            synchronized (mBroadcastReceiversByAction) {
400                sendBroadcast(intent);
401                mStickyBroadcastByAction.put(intent.getAction(), intent);
402            }
403        }
404
405        @Override
406        public void sendStickyBroadcastAsUser(Intent intent, UserHandle ignored) {
407            logd("sendStickyBroadcastAsUser called for " + intent.getAction());
408            sendStickyBroadcast(intent);
409        }
410
411        @Override
412        public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
413                throws PackageManager.NameNotFoundException {
414            return this;
415        }
416
417        @Override
418        public void enforceCallingOrSelfPermission(String permission, String message) {
419            if (mPermissionTable.contains(permission)
420                    || mPermissionTable.contains(PERMISSION_ENABLE_ALL)) {
421                return;
422            }
423            logd("requested permission: " + permission + " got denied");
424            throw new SecurityException(permission + " denied: " + message);
425        }
426
427        @Override
428        public int checkCallingOrSelfPermission(String permission) {
429            return PackageManager.PERMISSION_GRANTED;
430        }
431
432        @Override
433        public SharedPreferences getSharedPreferences(String name, int mode) {
434            return mSharedPreferences;
435        }
436
437        @Override
438        public String getPackageName() {
439            return "com.android.internal.telephony";
440        }
441    }
442
443    private final Multimap<String, ComponentName> mComponentNamesByAction =
444            ArrayListMultimap.create();
445    private final Map<ComponentName, IInterface> mServiceByComponentName =
446            new HashMap<ComponentName, IInterface>();
447    private final Map<ComponentName, ServiceInfo> mServiceInfoByComponentName =
448            new HashMap<ComponentName, ServiceInfo>();
449    private final Map<IInterface, ComponentName> mComponentNameByService =
450            new HashMap<IInterface, ComponentName>();
451    private final Map<ServiceConnection, IInterface> mServiceByServiceConnection =
452            new HashMap<ServiceConnection, IInterface>();
453    private final Multimap<String, BroadcastReceiver> mBroadcastReceiversByAction =
454            ArrayListMultimap.create();
455    private final HashMap<String, Intent> mStickyBroadcastByAction =
456            new HashMap<String, Intent>();
457    private final Multimap<Intent, BroadcastReceiver> mOrderedBroadcastReceivers =
458            ArrayListMultimap.create();
459    private final HashSet<String> mPermissionTable = new HashSet<>();
460
461
462
463    // The application context is the most important object this class provides to the system
464    // under test.
465    private final Context mContext = spy(new FakeContext());
466
467    // We then create a spy on the application context allowing standard Mockito-style
468    // when(...) logic to be used to add specific little responses where needed.
469
470    private final Resources mResources = mock(Resources.class);
471    private final PackageManager mPackageManager = mock(PackageManager.class);
472    private final TelephonyManager mTelephonyManager = mock(TelephonyManager.class);
473    private final AppOpsManager mAppOpsManager = mock(AppOpsManager.class);
474    private final NotificationManager mNotificationManager = mock(NotificationManager.class);
475    private final UserManager mUserManager = mock(UserManager.class);
476    private final CarrierConfigManager mCarrierConfigManager = mock(CarrierConfigManager.class);
477    private final SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
478    private final AlarmManager mAlarmManager = mock(AlarmManager.class);
479    private final ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
480    private final UsageStatsManager mUsageStatManager = null;
481    private final WifiManager mWifiManager = mock(WifiManager.class);
482
483    private final ContentProvider mContentProvider = spy(new FakeContentProvider());
484
485    private final Configuration mConfiguration = new Configuration();
486    private final SharedPreferences mSharedPreferences = PreferenceManager
487            .getDefaultSharedPreferences(TestApplication.getAppContext());
488    private final MockContentResolver mContentResolver = new MockContentResolver();
489    private final PersistableBundle mBundle = new PersistableBundle();
490
491    public ContextFixture() {
492        MockitoAnnotations.initMocks(this);
493
494        doAnswer(new Answer<List<ResolveInfo>>() {
495            @Override
496            public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable {
497                return doQueryIntentServices(
498                        (Intent) invocation.getArguments()[0],
499                        (Integer) invocation.getArguments()[1]);
500            }
501        }).when(mPackageManager).queryIntentServices((Intent) any(), anyInt());
502
503        doAnswer(new Answer<List<ResolveInfo>>() {
504            @Override
505            public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable {
506                return doQueryIntentServices(
507                        (Intent) invocation.getArguments()[0],
508                        (Integer) invocation.getArguments()[1]);
509            }
510        }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt());
511
512        doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
513        //doReturn(mBundle).when(mCarrierConfigManager).getConfig(anyInt());
514        doReturn(mBundle).when(mCarrierConfigManager).getConfig();
515
516        mConfiguration.locale = Locale.US;
517        doReturn(mConfiguration).when(mResources).getConfiguration();
518
519        mContentResolver.addProvider(Settings.AUTHORITY, mContentProvider);
520        mPermissionTable.add(PERMISSION_ENABLE_ALL);
521    }
522
523    @Override
524    public Context getTestDouble() {
525        return mContext;
526    }
527
528    public void putResource(int id, final String value) {
529        when(mResources.getText(eq(id))).thenReturn(value);
530        when(mResources.getString(eq(id))).thenReturn(value);
531        when(mResources.getString(eq(id), any())).thenAnswer(new Answer<String>() {
532            @Override
533            public String answer(InvocationOnMock invocation) {
534                Object[] args = invocation.getArguments();
535                return String.format(value, Arrays.copyOfRange(args, 1, args.length));
536            }
537        });
538    }
539
540    public void putBooleanResource(int id, boolean value) {
541        when(mResources.getBoolean(eq(id))).thenReturn(value);
542    }
543
544    public void putStringArrayResource(int id, String[] values) {
545        doReturn(values).when(mResources).getStringArray(eq(id));
546    }
547
548    public void putIntArrayResource(int id, int[] values) {
549        doReturn(values).when(mResources).getIntArray(eq(id));
550    }
551
552    public PersistableBundle getCarrierConfigBundle() {
553        return mBundle;
554    }
555
556    private void addService(String action, ComponentName name, IInterface service) {
557        mComponentNamesByAction.put(action, name);
558        mServiceByComponentName.put(name, service);
559        mComponentNameByService.put(service, name);
560    }
561
562    private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) {
563        List<ResolveInfo> result = new ArrayList<ResolveInfo>();
564        for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) {
565            ResolveInfo resolveInfo = new ResolveInfo();
566            resolveInfo.serviceInfo = mServiceInfoByComponentName.get(componentName);
567            result.add(resolveInfo);
568        }
569        return result;
570    }
571
572    public void sendBroadcastToOrderedBroadcastReceivers() {
573        synchronized (mOrderedBroadcastReceivers) {
574            // having a map separate from mOrderedBroadcastReceivers is helpful here as onReceive()
575            // call within the loop may lead to sendOrderedBroadcast() which can add to
576            // mOrderedBroadcastReceivers
577            Collection<Map.Entry<Intent, BroadcastReceiver>> map =
578                    mOrderedBroadcastReceivers.entries();
579            for (Map.Entry<Intent, BroadcastReceiver> entry : map) {
580                entry.getValue().onReceive(mContext, entry.getKey());
581                mOrderedBroadcastReceivers.remove(entry.getKey(), entry.getValue());
582            }
583        }
584    }
585
586    public void addCallingOrSelfPermission(String permission) {
587        synchronized (mPermissionTable) {
588            if (mPermissionTable != null && permission != null) {
589                mPermissionTable.remove(PERMISSION_ENABLE_ALL);
590                mPermissionTable.add(permission);
591            }
592        }
593    }
594
595    public void removeCallingOrSelfPermission(String permission) {
596        synchronized (mPermissionTable) {
597            if (mPermissionTable != null && permission != null) {
598                mPermissionTable.remove(permission);
599            }
600        }
601    }
602
603    private static void logd(String s) {
604        Log.d(TAG, s);
605    }
606}
607