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 
17 package com.android.nfc;
18 
19 import android.util.Log;
20 
21 import java.util.Arrays;
22 
23 public class NfcProprietaryCaps {
24     private static final String TAG = "NfcProprietaryCaps";
25     private static final int PASSIVE_OBSERVE_MODE = 0;
26     private static final int POLLING_FRAME_NTF = 1;
27     private static final int POWER_SAVING_MODE = 2;
28     private static final int AUTOTRANSACT_POLLING_LOOP_FILTER = 3;
29     private final PassiveObserveMode mPassiveObserveMode;
30     private final boolean mIsPollingFrameNotificationSupported;
31     private final boolean mIsPowerSavingModeSupported;
32     private final boolean mIsAutotransactPollingLoopFilterSupported;
33 
34     public enum PassiveObserveMode {
35         NOT_SUPPORTED,
36         SUPPORT_WITH_RF_DEACTIVATION,
37         SUPPORT_WITHOUT_RF_DEACTIVATION,
38     }
39 
getPassiveObserveMode()40     public PassiveObserveMode getPassiveObserveMode() {
41         return mPassiveObserveMode;
42     }
43 
isPollingFrameNotificationSupported()44     public boolean isPollingFrameNotificationSupported() {
45         return mIsPollingFrameNotificationSupported;
46     }
47 
isPowerSavingModeSupported()48     public boolean isPowerSavingModeSupported() {
49         return mIsPowerSavingModeSupported;
50     }
51 
isAutotransactPollingLoopFilterSupported()52     public boolean isAutotransactPollingLoopFilterSupported() {
53         return mIsAutotransactPollingLoopFilterSupported;
54     }
55 
NfcProprietaryCaps(PassiveObserveMode passiveObserveMode, boolean isPollingFrameNotificationSupported, boolean isPowerSavingModeSupported, boolean isAutotransactPollingLoopFilterSupported)56     public NfcProprietaryCaps(PassiveObserveMode passiveObserveMode,
57             boolean isPollingFrameNotificationSupported, boolean isPowerSavingModeSupported,
58             boolean isAutotransactPollingLoopFilterSupported) {
59         mPassiveObserveMode = passiveObserveMode;
60         mIsPollingFrameNotificationSupported = isPollingFrameNotificationSupported;
61         mIsPowerSavingModeSupported = isPowerSavingModeSupported;
62         mIsAutotransactPollingLoopFilterSupported = isAutotransactPollingLoopFilterSupported;
63     }
64 
createFromByteArray(byte[] caps)65     public static NfcProprietaryCaps createFromByteArray(byte[] caps) {
66         Log.i(TAG, "parsing proprietary caps: " + Arrays.toString(caps));
67         PassiveObserveMode passiveObserveMode = PassiveObserveMode.NOT_SUPPORTED;
68         boolean isPollingFrameNotificationSupported = false;
69         boolean isPowerSavingModeSupported = false;
70         boolean isAutotransactPollingLoopFilterSupported  = false;
71         int offset = 0;
72         while ((offset + 2) < caps.length) {
73             int id = caps[offset++];
74             int value_len = caps[offset++];
75             int value_offset = offset;
76             offset += value_len;
77 
78             // value bounds check
79             // all caps have minimum length of 1, check this bound
80             // here to simplify match cases.
81             if (value_len < 1 || offset > caps.length) {
82                 break;
83             }
84             switch (id) {
85                 case PASSIVE_OBSERVE_MODE:
86                     passiveObserveMode = switch (caps[value_offset]) {
87                         case 0 -> PassiveObserveMode.NOT_SUPPORTED;
88                         case 1 -> PassiveObserveMode.SUPPORT_WITH_RF_DEACTIVATION;
89                         case 2 -> PassiveObserveMode.SUPPORT_WITHOUT_RF_DEACTIVATION;
90                         default -> passiveObserveMode;
91                     };
92                     break;
93                 case POLLING_FRAME_NTF:
94                     isPollingFrameNotificationSupported = caps[value_offset] == 0x1;
95                     break;
96                 case POWER_SAVING_MODE:
97                     isPowerSavingModeSupported = caps[value_offset] == 0x1;
98                     break;
99                 case AUTOTRANSACT_POLLING_LOOP_FILTER:
100                     isAutotransactPollingLoopFilterSupported = caps[value_offset] == 0x1;
101                     break;
102             }
103         }
104         return new NfcProprietaryCaps(passiveObserveMode, isPollingFrameNotificationSupported,
105                 isPowerSavingModeSupported, isAutotransactPollingLoopFilterSupported);
106     }
107 
108     @Override
toString()109     public String toString() {
110         return "NfcProprietaryCaps{"
111                 + "passiveObserveMode="
112                 + mPassiveObserveMode
113                 + ", isPollingFrameNotificationSupported="
114                 + mIsPollingFrameNotificationSupported
115                 + ", isPowerSavingModeSupported="
116                 + mIsPowerSavingModeSupported
117                 + ", isAutotransactPollingLoopFilterSupported="
118                 + mIsAutotransactPollingLoopFilterSupported
119                 + '}';
120     }
121 }
122