1 /*
2  * Copyright (C) 2022 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 package com.android.nfc;
17 
18 import static com.android.nfc.NfcDispatcher.DISPATCH_SUCCESS;
19 
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.ArgumentMatchers.anyInt;
22 import static org.mockito.ArgumentMatchers.eq;
23 import static org.mockito.Mockito.mockingDetails;
24 import static org.mockito.Mockito.when;
25 
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.content.BroadcastReceiver;
29 import android.content.Context;
30 import android.content.ContextWrapper;
31 import android.content.Intent;
32 import android.content.IntentFilter;
33 import android.content.pm.ActivityInfo;
34 import android.content.pm.PackageManager;
35 import android.content.pm.PackageManager.ResolveInfoFlags;
36 import android.content.pm.ResolveInfo;
37 import android.content.res.Resources;
38 import android.nfc.Tag;
39 import android.os.Bundle;
40 import android.os.Handler;
41 import android.os.PowerManager;
42 import android.os.UserHandle;
43 import android.util.Log;
44 import androidx.test.core.content.pm.ApplicationInfoBuilder;
45 import androidx.test.ext.junit.runners.AndroidJUnit4;
46 import androidx.test.platform.app.InstrumentationRegistry;
47 
48 import com.android.dx.mockito.inline.extended.ExtendedMockito;
49 import com.android.nfc.handover.HandoverDataParser;
50 
51 import java.util.ArrayList;
52 import java.util.List;
53 import java.util.concurrent.atomic.AtomicReference;
54 
55 import org.junit.After;
56 import org.junit.Assert;
57 import org.junit.Before;
58 import org.junit.Test;
59 import org.junit.runner.RunWith;
60 import org.mockito.Mockito;
61 import org.mockito.MockitoSession;
62 import org.mockito.quality.Strictness;
63 
64 @RunWith(AndroidJUnit4.class)
65 public final class NfcReaderConflictOccurredTest {
66 
67     private static final String TAG = NfcReaderConflictOccurredTest.class.getSimpleName();
68     private boolean mNfcSupported;
69 
70     private MockitoSession mStaticMockSession;
71     private NfcDispatcher mNfcDispatcher;
72 
73     @Before
setUp()74     public void setUp() {
75         mStaticMockSession = ExtendedMockito.mockitoSession()
76                 .mockStatic(NfcStatsLog.class)
77                 .strictness(Strictness.LENIENT)
78                 .startMocking();
79 
80         Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
81         PackageManager mPackageManager = context.getPackageManager();
82         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC_ANY)) {
83             mNfcSupported = false;
84             return;
85         }
86         mNfcSupported = true;
87 
88         PackageManager mockPackageManager = Mockito.mock(PackageManager.class);
89         // multiple resolveInfos for Tag
90         when(mockPackageManager.queryIntentActivitiesAsUser(
91                 any(Intent.class),
92                 any(ResolveInfoFlags.class),
93                 any(UserHandle.class))).thenReturn(constructConflictingResolveInfos());
94         PowerManager mockPowerManager = Mockito.mock(PowerManager.class);
95         when(mockPowerManager.isInteractive()).thenReturn(false);
96         Resources mockResources = Mockito.mock(Resources.class);
97         when(mockResources.getBoolean(eq(R.bool.tag_intent_app_pref_supported)))
98                 .thenReturn(false);
99 
100         Context mockContext = new ContextWrapper(context) {
101             @Override
102             public PackageManager getPackageManager() {
103                 Log.i(TAG, "[Mock] getPackageManager");
104                 return mockPackageManager;
105             }
106 
107             @Override
108             public Object getSystemService(String name) {
109               if (Context.POWER_SERVICE.equals(name)) {
110                   Log.i(TAG, "[Mock] mockPowerManager");
111                   return mockPowerManager;
112               }
113               return super.getSystemService(name);
114             }
115 
116             @Override
117             public Resources getResources() {
118                 Log.i(TAG, "[Mock] getResources");
119                 return mockResources;
120             }
121             @Override
122             public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
123                     @NonNull IntentFilter filter, @Nullable String broadcastPermission,
124                     @Nullable Handler scheduler) {
125                 Log.i(TAG, "[Mock] getIntent");
126                 return Mockito.mock(Intent.class);
127             }
128         };
129 
130         InstrumentationRegistry.getInstrumentation().runOnMainSync(
131               () -> mNfcDispatcher = new NfcDispatcher(
132                       mockContext, new HandoverDataParser(), false));
133         Assert.assertNotNull(mNfcDispatcher);
134     }
135 
136     @After
tearDown()137     public void tearDown() {
138         mStaticMockSession.finishMocking();
139     }
140 
141     @Test
testLogReaderConflict()142     public void testLogReaderConflict() {
143         if (!mNfcSupported) return;
144 
145         Tag tag = Tag.createMockTag(null, new int[0], new Bundle[0], 0L);
146         int result = mNfcDispatcher.dispatchTag(tag);
147         ExtendedMockito.verify(() -> NfcStatsLog.write(
148                 NfcStatsLog.NFC_READER_CONFLICT_OCCURRED));
149     }
150 
151 
152     @Test
testLogReaderSuccess()153     public void testLogReaderSuccess() {
154         if (!mNfcSupported) return;
155 
156         Tag tag = Tag.createMockTag(null, new int[0], new Bundle[0], 0L);
157         int result = mNfcDispatcher.dispatchTag(tag);
158         Assert.assertEquals(result,DISPATCH_SUCCESS);
159     }
160 
constructConflictingResolveInfos()161     public List<ResolveInfo> constructConflictingResolveInfos() {
162         List<ResolveInfo> mockResolves = new ArrayList<>();
163         mockResolves.add(constructResolveInfo("appName1", "packageName1", 111));
164         mockResolves.add(constructResolveInfo("appName2", "packageName2", 112));
165         return mockResolves;
166     }
167 
constructResolveInfo(String appName, String packageName, int uid)168     public ResolveInfo constructResolveInfo(String appName, String packageName, int uid) {
169         ResolveInfo resolveInfo = new ResolveInfo();
170         resolveInfo.activityInfo = new ActivityInfo();
171         resolveInfo.activityInfo.applicationInfo =
172             ApplicationInfoBuilder.newBuilder()
173                 .setName(appName)
174                 .setPackageName(packageName)
175                 .build();
176         resolveInfo.activityInfo.applicationInfo.uid = uid;
177         resolveInfo.activityInfo.exported = true;
178         return resolveInfo;
179     }
180 }
181