• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 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.emulator;
17 
18 import android.content.ComponentName;
19 import android.nfc.cardemulation.CardEmulation;
20 import android.nfc.cardemulation.PollingFrame;
21 import android.os.Bundle;
22 import android.util.Log;
23 
24 import com.android.nfc.service.PaymentService1;
25 
26 import java.util.ArrayList;
27 import java.util.HexFormat;
28 import java.util.List;
29 import java.util.regex.Pattern;
30 
31 public class ExitFrameEmulatorActivity extends BaseEmulatorActivity {
32     private static final String TAG = "ExitFrameEmulatorActivity";
33 
34     public static final String EXIT_FRAME_KEY = "EXIT_FRAME";
35     public static final String REGISTER_PATTERNS_KEY = "REGISTER_PATTERNS";
36     public static final String WAIT_FOR_TRANSACTION_KEY = "WAIT_FOR_TRANSACTION";
37 
38     private String mReceivedExitFrame = null;
39     private String mIntendedExitFrameData = null;
40     private final List<String> mRegisteredPatterns = new ArrayList<>();
41     private boolean mWaitForTransaction = true;
42     private ComponentName mServiceName = null;
43 
44     private final CardEmulation.NfcEventCallback mEventListener =
45             new CardEmulation.NfcEventCallback() {
46                 public void onObserveModeDisabledInFirmware(PollingFrame exitFrame) {
47                     if (exitFrame != null) {
48                         mReceivedExitFrame = HexFormat.of().formatHex(exitFrame.getData());
49                         Log.d(TAG, "Received exit frame: " + mReceivedExitFrame);
50                     }
51 
52                     if (!mWaitForTransaction) {
53                         verifyExitFrameAndPassTest();
54                     }
55                 }
56             };
57 
58     @Override
onCreate(Bundle savedInstanceState)59     protected void onCreate(Bundle savedInstanceState) {
60         super.onCreate(savedInstanceState);
61         mIntendedExitFrameData = getIntent().getStringExtra(EXIT_FRAME_KEY);
62         mWaitForTransaction = getIntent().getBooleanExtra(WAIT_FOR_TRANSACTION_KEY, true);
63 
64         setupServices(PaymentService1.COMPONENT);
65         makeDefaultWalletRoleHolder();
66     }
67 
onResume()68     public void onResume() {
69         super.onResume();
70         mServiceName =
71                 new ComponentName(this.getApplicationContext(), PaymentService1.class);
72         mCardEmulation.setPreferredService(this, mServiceName);
73         waitForPreferredService();
74 
75         mReceivedExitFrame = null;
76         registerEventListener(mEventListener);
77 
78         if (getIntent().hasExtra(REGISTER_PATTERNS_KEY)) {
79             getIntent().getStringArrayListExtra(REGISTER_PATTERNS_KEY).forEach(
80                     plpf -> {
81                         mCardEmulation.registerPollingLoopPatternFilterForService(mServiceName,
82                                 plpf, true);
83                         mRegisteredPatterns.add(plpf);
84                     }
85             );
86         }
87     }
88 
89     @Override
onPause()90     public void onPause() {
91         super.onPause();
92 
93         mRegisteredPatterns.forEach(
94                 plpf -> mCardEmulation.removePollingLoopPatternFilterForService(mServiceName,
95                         plpf));
96         mCardEmulation.unsetPreferredService(this);
97     }
98 
99     @Override
onApduSequenceComplete(ComponentName component, long duration)100     public void onApduSequenceComplete(ComponentName component, long duration) {
101         verifyExitFrameAndPassTest();
102     }
103 
104     @Override
onDestroy()105     protected void onDestroy() {
106         super.onDestroy();
107         mCardEmulation.unregisterNfcEventCallback(mEventListener);
108     }
109 
110     @Override
getPreferredServiceComponent()111     public ComponentName getPreferredServiceComponent() {
112         return PaymentService1.COMPONENT;
113     }
114 
verifyExitFrameAndPassTest()115     private void verifyExitFrameAndPassTest() {
116         if (mIntendedExitFrameData == null || mReceivedExitFrame == null) {
117             return;
118         }
119 
120         boolean success =
121                 Pattern.compile(mIntendedExitFrameData).matcher(mReceivedExitFrame).matches();
122 
123         if (success) {
124             setTestPassed();
125         }
126     }
127 }