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 android.nfc; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.FlaggedApi; 21 import android.annotation.NonNull; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SystemApi; 24 import android.content.Context; 25 import android.os.Binder; 26 import android.os.RemoteException; 27 import android.util.Log; 28 29 import java.util.concurrent.Executor; 30 31 /** 32 * Used for OEM extension APIs. 33 * This class holds all the APIs and callbacks defined for OEMs/vendors to extend the NFC stack 34 * for their proprietary features. 35 * 36 * @hide 37 */ 38 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 39 @SystemApi 40 public final class NfcOemExtension { 41 private static final String TAG = "NfcOemExtension"; 42 private static final int OEM_EXTENSION_RESPONSE_THRESHOLD_MS = 2000; 43 private final NfcAdapter mAdapter; 44 private final NfcOemExtensionCallback mOemNfcExtensionCallback; 45 private final Context mContext; 46 private Executor mExecutor = null; 47 private Callback mCallback = null; 48 private final Object mLock = new Object(); 49 50 /** 51 * Interface for Oem extensions for NFC. 52 */ 53 public interface Callback { 54 /** 55 * Notify Oem to tag is connected or not 56 * ex - if tag is connected notify cover and Nfctest app if app is in testing mode 57 * 58 * @param connected status of the tag true if tag is connected otherwise false 59 * @param tag Tag details 60 */ onTagConnected(boolean connected, @NonNull Tag tag)61 void onTagConnected(boolean connected, @NonNull Tag tag); 62 } 63 64 65 /** 66 * Constructor to be used only by {@link NfcAdapter}. 67 * @hide 68 */ NfcOemExtension(@onNull Context context, @NonNull NfcAdapter adapter)69 public NfcOemExtension(@NonNull Context context, @NonNull NfcAdapter adapter) { 70 mContext = context; 71 mAdapter = adapter; 72 mOemNfcExtensionCallback = new NfcOemExtensionCallback(); 73 } 74 75 /** 76 * Register an {@link Callback} to listen for UWB oem extension callbacks 77 * <p>The provided callback will be invoked by the given {@link Executor}. 78 * 79 * @param executor an {@link Executor} to execute given callback 80 * @param callback oem implementation of {@link Callback} 81 */ 82 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 83 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) registerCallback(@onNull @allbackExecutor Executor executor, @NonNull Callback callback)84 public void registerCallback(@NonNull @CallbackExecutor Executor executor, 85 @NonNull Callback callback) { 86 synchronized (mLock) { 87 if (mCallback != null) { 88 Log.e(TAG, "Callback already registered. Unregister existing callback before" 89 + "registering"); 90 throw new IllegalArgumentException(); 91 } 92 try { 93 NfcAdapter.sService.registerOemExtensionCallback(mOemNfcExtensionCallback); 94 mCallback = callback; 95 mExecutor = executor; 96 } catch (RemoteException e) { 97 mAdapter.attemptDeadServiceRecovery(e); 98 } 99 } 100 } 101 102 /** 103 * Unregister the specified {@link Callback} 104 * 105 * <p>The same {@link Callback} object used when calling 106 * {@link #registerCallback(Executor, Callback)} must be used. 107 * 108 * <p>Callbacks are automatically unregistered when an application process goes away 109 * 110 * @param callback oem implementation of {@link Callback} 111 */ 112 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 113 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) unregisterCallback(@onNull Callback callback)114 public void unregisterCallback(@NonNull Callback callback) { 115 synchronized (mLock) { 116 if (mCallback == null || mCallback != callback) { 117 Log.e(TAG, "Callback not registered"); 118 throw new IllegalArgumentException(); 119 } 120 try { 121 NfcAdapter.sService.unregisterOemExtensionCallback(mOemNfcExtensionCallback); 122 mCallback = null; 123 mExecutor = null; 124 } catch (RemoteException e) { 125 mAdapter.attemptDeadServiceRecovery(e); 126 } 127 } 128 } 129 130 /** 131 * Clear NfcService preference, interface method to clear NFC preference values on OEM specific 132 * events. For ex: on soft reset, Nfc default values needs to be overridden by OEM defaults. 133 */ 134 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 135 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) clearPreference()136 public void clearPreference() { 137 try { 138 NfcAdapter.sService.clearPreference(); 139 } catch (RemoteException e) { 140 mAdapter.attemptDeadServiceRecovery(e); 141 } 142 } 143 144 /** 145 * Get the screen state from system and set it to current screen state. 146 */ 147 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 148 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) synchronizeScreenState()149 public void synchronizeScreenState() { 150 try { 151 NfcAdapter.sService.setScreenState(); 152 } catch (RemoteException e) { 153 mAdapter.attemptDeadServiceRecovery(e); 154 } 155 } 156 157 /** 158 * Check if the firmware needs updating. 159 * 160 * <p>If an update is needed, a firmware will be triggered when NFC is disabled. 161 */ 162 @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) 163 @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) maybeTriggerFirmwareUpdate()164 public void maybeTriggerFirmwareUpdate() { 165 try { 166 NfcAdapter.sService.checkFirmware(); 167 } catch (RemoteException e) { 168 mAdapter.attemptDeadServiceRecovery(e); 169 } 170 } 171 172 private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub { 173 @Override onTagConnected(boolean connected, Tag tag)174 public void onTagConnected(boolean connected, Tag tag) throws RemoteException { 175 synchronized (mLock) { 176 if (mCallback == null || mExecutor == null) { 177 return; 178 } 179 final long identity = Binder.clearCallingIdentity(); 180 try { 181 mExecutor.execute(() -> mCallback.onTagConnected(connected, tag)); 182 } finally { 183 Binder.restoreCallingIdentity(identity); 184 } 185 } 186 } 187 } 188 } 189