1 /* 2 * Copyright 2024 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 17 package android.nfc.test; 18 19 import static org.junit.Assume.assumeFalse; 20 21 import android.app.Activity; 22 import android.app.ActivityManager; 23 import android.app.Instrumentation; 24 import android.app.KeyguardManager; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.pm.PackageManager; 29 import android.nfc.NfcAdapter; 30 import android.nfc.cardemulation.CardEmulation; 31 import android.nfc.cardemulation.PollingFrame; 32 import android.nfc.cardemulation.PollingFrame.PollingFrameType; 33 import android.os.Bundle; 34 import android.os.PowerManager; 35 import android.os.UserManager; 36 import android.util.Log; 37 import android.view.KeyEvent; 38 import androidx.test.core.app.ApplicationProvider; 39 import androidx.test.platform.app.InstrumentationRegistry; 40 import com.android.compatibility.common.util.CommonTestUtils; 41 import com.android.compatibility.common.util.SystemUtil; 42 import java.util.ArrayList; 43 import java.util.List; 44 import org.junit.Assert; 45 46 public class TestUtils { supportsHardware()47 static boolean supportsHardware() { 48 final PackageManager pm = InstrumentationRegistry.getInstrumentation().getContext() 49 .getPackageManager(); 50 return pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION); 51 } 52 createAndResumeActivity()53 static Activity createAndResumeActivity() { 54 return createAndResumeActivity(NfcFCardEmulationActivity.class); 55 } 56 createAndResumeActivity(Class<? extends Activity> activityClass)57 static Activity createAndResumeActivity(Class<? extends Activity> activityClass) { 58 ensureUnlocked(); 59 Context context = ApplicationProvider.getApplicationContext(); 60 Intent intent = new Intent(context, activityClass); 61 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 62 Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent); 63 InstrumentationRegistry.getInstrumentation().callActivityOnResume(activity); 64 ComponentName topComponentName = context.getSystemService(ActivityManager.class) 65 .getRunningTasks(1).get(0).topActivity; 66 Assert.assertEquals("Foreground activity not in the foreground", 67 activityClass.getName(), topComponentName.getClassName()); 68 return activity; 69 } 70 ensurePreferredService(Class serviceClass, Context context)71 static void ensurePreferredService(Class serviceClass, Context context) { 72 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(context); 73 final CardEmulation cardEmulation = CardEmulation.getInstance(adapter); 74 int resId = 75 serviceClass == CustomHostApduService.class 76 ? android.nfc.test.R.string.CustomPaymentService 77 : -1; 78 final String desc = context.getResources().getString(resId); 79 ensurePreferredService(desc, context); 80 } 81 ensureUnlocked()82 static void ensureUnlocked() { 83 final Context context = InstrumentationRegistry.getInstrumentation().getContext(); 84 final UserManager userManager = context.getSystemService(UserManager.class); 85 assumeFalse(userManager.isHeadlessSystemUserMode()); 86 final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); 87 final PowerManager pm = context.getSystemService(PowerManager.class); 88 final KeyguardManager km = context.getSystemService(KeyguardManager.class); 89 try { 90 if (pm != null && !pm.isInteractive()) { 91 SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP"); 92 CommonTestUtils.waitUntil( 93 "Device does not wake up after 5 seconds", 5, () -> pm != null && pm.isInteractive()); 94 } 95 if (km != null && km.isKeyguardLocked()) { 96 CommonTestUtils.waitUntil( 97 "Device does not unlock after 30 seconds", 98 30, 99 () -> { 100 SystemUtil.runWithShellPermissionIdentity( 101 () -> instrumentation.sendKeyDownUpSync((KeyEvent.KEYCODE_MENU))); 102 return km != null && !km.isKeyguardLocked(); 103 }); 104 } 105 } catch (InterruptedException|AssertionError e) { 106 } 107 } 108 ensurePreferredService(String serviceDesc, Context context)109 static void ensurePreferredService(String serviceDesc, Context context) { 110 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(context); 111 final CardEmulation cardEmulation = CardEmulation.getInstance(adapter); 112 try { 113 CommonTestUtils.waitUntil( 114 "Default service hasn't updated", 115 6, 116 () -> serviceDesc.equals(cardEmulation.getDescriptionForPreferredPaymentService())); 117 } catch (InterruptedException|AssertionError e) { 118 } 119 } 120 121 static PollLoopReceiver sCurrentPollLoopReceiver = null; 122 123 static class PollLoopReceiver { 124 int mFrameIndex = 0; 125 ArrayList<PollingFrame> mFrames; 126 String mServiceName; 127 ArrayList<PollingFrame> mReceivedFrames; 128 String mReceivedServiceName; 129 ArrayList<String> mReceivedServiceNames; 130 PollLoopReceiver(ArrayList<PollingFrame> frames, String serviceName)131 PollLoopReceiver(ArrayList<PollingFrame> frames, String serviceName) { 132 mFrames = frames; 133 mServiceName = serviceName; 134 mReceivedFrames = new ArrayList<PollingFrame>(); 135 mReceivedServiceNames = new ArrayList<String>(); 136 } 137 notifyPollingLoop(String className, List<PollingFrame> receivedFrames)138 void notifyPollingLoop(String className, List<PollingFrame> receivedFrames) { 139 if (receivedFrames == null) { 140 return; 141 } 142 mReceivedFrames.addAll(receivedFrames); 143 mReceivedServiceName = className; 144 mReceivedServiceNames.add(className); 145 if (mReceivedFrames.size() < mFrames.size()) { 146 return; 147 } 148 synchronized (this) { 149 this.notify(); 150 } 151 } 152 test()153 void test() { 154 if (mReceivedFrames.size() > mFrames.size()) { 155 Assert.fail("received more frames than sent"); 156 } else if (mReceivedFrames.size() < mFrames.size()) { 157 Assert.fail("received fewer frames than sent"); 158 } 159 for (PollingFrame receivedFrame : mReceivedFrames) { 160 Assert.assertEquals(mFrames.get(mFrameIndex).getType(), receivedFrame.getType()); 161 Assert.assertEquals( 162 mFrames.get(mFrameIndex).getVendorSpecificGain(), 163 receivedFrame.getVendorSpecificGain()); 164 Assert.assertEquals(mFrames.get(mFrameIndex).getTimestamp(), receivedFrame.getTimestamp()); 165 Assert.assertArrayEquals(mFrames.get(mFrameIndex).getData(), receivedFrame.getData()); 166 mFrameIndex++; 167 } 168 if (mServiceName != null) { 169 Assert.assertEquals(mServiceName, mReceivedServiceName); 170 } 171 } 172 } 173 notifyPollingLoopAndWait( ArrayList<PollingFrame> frames, String serviceName, Context context)174 static List<PollingFrame> notifyPollingLoopAndWait( 175 ArrayList<PollingFrame> frames, String serviceName, Context context) { 176 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(context); 177 sCurrentPollLoopReceiver = new PollLoopReceiver(frames, serviceName); 178 for (PollingFrame frame : frames) { 179 adapter.notifyPollingLoop(frame); 180 } 181 synchronized (sCurrentPollLoopReceiver) { 182 try { 183 sCurrentPollLoopReceiver.wait(5000); 184 } catch (InterruptedException ie) { 185 Assert.assertNull(ie); 186 } 187 } 188 sCurrentPollLoopReceiver.test(); 189 Assert.assertEquals(frames.size(), sCurrentPollLoopReceiver.mFrameIndex); 190 List<PollingFrame> receivedFrames = sCurrentPollLoopReceiver.mReceivedFrames; 191 sCurrentPollLoopReceiver = null; 192 return receivedFrames; 193 } 194 createFrame(@ollingFrameType int type)195 static PollingFrame createFrame(@PollingFrameType int type) { 196 if (type == PollingFrame.POLLING_LOOP_TYPE_ON || type == PollingFrame.POLLING_LOOP_TYPE_OFF) { 197 return new PollingFrame( 198 type, 199 new byte[] {((type == PollingFrame.POLLING_LOOP_TYPE_ON) ? (byte) 0x01 : (byte) 0x00)}, 200 8, 201 0, 202 false); 203 } 204 return new PollingFrame(type, null, 8, 0, false); 205 } 206 createFrameWithData(@ollingFrameType int type, byte[] data)207 static PollingFrame createFrameWithData(@PollingFrameType int type, byte[] data) { 208 return new PollingFrame(type, data, 8, (long) Integer.MAX_VALUE + 1L, false); 209 } 210 211 public abstract static class CommandApduProcessor { processCommandApdu(String serviceName, byte[] apdu, Bundle extras)212 public abstract byte[] processCommandApdu(String serviceName, byte[] apdu, Bundle extras); 213 } 214 215 static CommandApduProcessor sCurrentCommandApduProcessor = null; 216 killNfcService()217 public static void killNfcService() { 218 Log.w(TAG, "Attempting to kill the NFC service..."); 219 220 SystemUtil.runShellCommand("killall com.android.nfc"); 221 } 222 223 private static final String TAG = "TestUtils"; 224 } 225