1 /*
2  * Copyright (C) 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 package com.android.nfc.emulator;
17 
18 
19 import android.content.BroadcastReceiver;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.nfc.cardemulation.PollingFrame;
25 import android.os.Bundle;
26 import android.util.Log;
27 
28 import com.android.nfc.service.PollingLoopService;
29 import com.android.nfc.service.PollingLoopService2;
30 
31 import java.util.ArrayDeque;
32 import java.util.HexFormat;
33 import java.util.List;
34 import java.util.Queue;
35 
36 public class TwoPollingFrameEmulatorActivity extends BaseEmulatorActivity {
37     private static final String TAG = "TwoPollingFrameActivity";
38 
39     private static final String SERVICE_2_FRAME = "bbbbbbbb";
40 
41     // Number of loops to track in queue
42     public static final String SEEN_CORRECT_POLLING_LOOP_ACTION =
43             PACKAGE_NAME + ".SEEN_CORRECT_POLLING_LOOP_ACTION";
44 
45     private int mService1Count = 0;
46     private boolean mService2Matched = false;
47 
48     @Override
onCreate(Bundle savedInstanceState)49     protected void onCreate(Bundle savedInstanceState) {
50         super.onCreate(savedInstanceState);
51         setupServices(PollingLoopService.COMPONENT, PollingLoopService2.COMPONENT);
52     }
53 
54     @Override
onResume()55     protected void onResume() {
56         super.onResume();
57         IntentFilter filter = new IntentFilter(PollingLoopService.POLLING_FRAME_ACTION);
58         registerReceiver(mFieldStateReceiver, filter, RECEIVER_EXPORTED);
59         ComponentName serviceName1 =
60                 new ComponentName(this.getApplicationContext(), PollingLoopService.class);
61         mCardEmulation.setShouldDefaultToObserveModeForService(serviceName1, true);
62 
63         ComponentName serviceName2 =
64                 new ComponentName(this.getApplicationContext(), PollingLoopService2.class);
65         mCardEmulation.setShouldDefaultToObserveModeForService(serviceName2, true);
66 
67         mCardEmulation.setPreferredService(this, serviceName1);
68         waitForService();
69         waitForObserveModeEnabled(true);
70     }
71 
72     @Override
onPause()73     public void onPause() {
74         super.onPause();
75         Log.e(TAG, "onPause");
76         unregisterReceiver(mFieldStateReceiver);
77         mCardEmulation.unsetPreferredService(this);
78     }
79 
80     @Override
getPreferredServiceComponent()81     public ComponentName getPreferredServiceComponent() {
82         return PollingLoopService.COMPONENT;
83     }
84 
processPollingFrames(List<PollingFrame> frames, String serviceName)85     void processPollingFrames(List<PollingFrame> frames, String serviceName) {
86         Log.d(TAG, "processPollingFrames of size " + frames.size());
87         for (PollingFrame frame : frames) {
88             processPollingFrame(frame, serviceName);
89         }
90     }
91 
processPollingFrame(PollingFrame frame, String serviceName)92     void processPollingFrame(PollingFrame frame, String serviceName) {
93         Log.d(TAG, "processPollingFrame: " + (char) (frame.getType()) + " service: " + serviceName);
94 
95         if (frame.getType() == PollingFrame.POLLING_LOOP_TYPE_UNKNOWN) {
96             Log.e(TAG, "got custom frame: " + HexFormat.of().formatHex(frame.getData()));
97             byte[] data = frame.getData();
98             if (serviceName.equals(PollingLoopService2.class.getName()) &&
99                     SERVICE_2_FRAME.equals(HexFormat.of().formatHex(data))) {
100                 Intent intent = new Intent(SEEN_CORRECT_POLLING_LOOP_ACTION);
101                 sendBroadcast(intent);
102                 mService2Matched = true;
103                 Log.d(TAG, "Correct custom polling frame seen. Sent broadcast");
104             }
105         } else if (mService2Matched && serviceName.equals(PollingLoopService.class.getName())) {
106             mService1Count++;
107             if (mService1Count >= 6) {
108                 Intent intent = new Intent(SEEN_CORRECT_POLLING_LOOP_ACTION);
109                 sendBroadcast(intent);
110             }
111         }
112     }
113 
114     final BroadcastReceiver mFieldStateReceiver =
115             new BroadcastReceiver() {
116                 @Override
117                 public void onReceive(Context context, Intent intent) {
118                     String action = intent.getAction();
119                     if (action.equals(PollingLoopService.POLLING_FRAME_ACTION)) {
120                         processPollingFrames(
121                                 intent.getParcelableArrayListExtra(
122                                         PollingLoopService.POLLING_FRAME_EXTRA,
123                                         PollingFrame.class),
124                                 intent.getStringExtra(PollingLoopService.SERVICE_NAME_EXTRA));
125                     }
126                 }
127             };
128 }
129