1 /* 2 * Copyright (C) 2015 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.cardemulation; 17 18 import android.app.ActivityManager; 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.nfc.cardemulation.NfcFServiceInfo; 22 import android.nfc.cardemulation.Utils; 23 import android.os.Handler; 24 import android.os.Looper; 25 import android.os.UserHandle; 26 import android.sysprop.NfcProperties; 27 import android.util.Log; 28 import android.util.proto.ProtoOutputStream; 29 30 import androidx.annotation.VisibleForTesting; 31 32 import com.android.nfc.ForegroundUtils; 33 34 import java.io.FileDescriptor; 35 import java.io.PrintWriter; 36 37 public class EnabledNfcFServices implements com.android.nfc.ForegroundUtils.Callback { 38 static final String TAG = "EnabledNfcFCardEmulationServices"; 39 static final boolean DBG = NfcProperties.debug_enabled().orElse(true); 40 41 final Context mContext; 42 final RegisteredNfcFServicesCache mNfcFServiceCache; 43 final RegisteredT3tIdentifiersCache mT3tIdentifiersCache; 44 final Callback mCallback; 45 final ForegroundUtils mForegroundUtils; 46 final Handler mHandler = new Handler(Looper.getMainLooper()); 47 48 final Object mLock = new Object(); 49 // Variables below synchronized on mLock 50 ComponentName mForegroundComponent = null; // The computed enabled foreground component 51 ComponentName mForegroundRequested = null; // The component requested to be enabled by fg app 52 int mForegroundUid = -1; // The UID of the fg app, or -1 if fg app didn't request 53 54 boolean mComputeFgRequested = false; 55 boolean mActivated = false; 56 57 public interface Callback { 58 /** 59 * Notify when enabled foreground NfcF service is changed. 60 */ onEnabledForegroundNfcFServiceChanged(int userId, ComponentName service)61 void onEnabledForegroundNfcFServiceChanged(int userId, ComponentName service); 62 } 63 EnabledNfcFServices(Context context, RegisteredNfcFServicesCache nfcFServiceCache, RegisteredT3tIdentifiersCache t3tIdentifiersCache, Callback callback)64 public EnabledNfcFServices(Context context, 65 RegisteredNfcFServicesCache nfcFServiceCache, 66 RegisteredT3tIdentifiersCache t3tIdentifiersCache, Callback callback) { 67 if (DBG) Log.d(TAG, "EnabledNfcFServices"); 68 mContext = context; 69 mForegroundUtils = ForegroundUtils.getInstance( 70 context.getSystemService(ActivityManager.class)); 71 mNfcFServiceCache = nfcFServiceCache; 72 mT3tIdentifiersCache = t3tIdentifiersCache; 73 mCallback = callback; 74 } 75 computeEnabledForegroundService()76 void computeEnabledForegroundService() { 77 if (DBG) Log.d(TAG, "computeEnabledForegroundService"); 78 ComponentName foregroundRequested = null; 79 boolean changed = false; 80 synchronized (mLock) { 81 if (mActivated) { 82 Log.d(TAG, "computeEnabledForegroundService: configuration will be postponed " 83 + "until deactivation"); 84 mComputeFgRequested = true; 85 return; 86 } 87 mComputeFgRequested = false; 88 foregroundRequested = mForegroundRequested; 89 if (mForegroundRequested != null && 90 (mForegroundComponent == null || 91 !mForegroundRequested.equals(mForegroundComponent))) { 92 mForegroundComponent = mForegroundRequested; 93 changed = true; 94 } else if (mForegroundRequested == null && mForegroundComponent != null) { 95 mForegroundComponent = mForegroundRequested; 96 changed = true; 97 } 98 } 99 // Notify if anything changed 100 if (changed) { 101 int userId = UserHandle.getUserHandleForUid(mForegroundUid).getIdentifier(); 102 mCallback.onEnabledForegroundNfcFServiceChanged(userId, foregroundRequested); 103 } 104 } 105 onServicesUpdated()106 public void onServicesUpdated() { 107 if (DBG) Log.d(TAG, "onServicesUpdated"); 108 // If enabled foreground service is set, remove it 109 boolean changed = false; 110 synchronized (mLock) { 111 if (mForegroundComponent != null) { 112 Log.d(TAG, "onServicesUpdated: Removing foreground enabled service because of" 113 + " service update"); 114 mForegroundRequested = null; 115 mForegroundUid = -1; 116 changed = true; 117 } 118 } 119 if (changed) { 120 computeEnabledForegroundService(); 121 } 122 } 123 registerEnabledForegroundService(ComponentName service, int callingUid)124 public boolean registerEnabledForegroundService(ComponentName service, int callingUid) { 125 if (DBG) Log.d(TAG, "registerEnabledForegroundService"); 126 boolean success = false; 127 synchronized (mLock) { 128 int userId = UserHandle.getUserHandleForUid(callingUid).getIdentifier(); 129 NfcFServiceInfo serviceInfo = mNfcFServiceCache.getService( 130 userId, service); 131 if (serviceInfo == null) { 132 return false; 133 } else { 134 if (serviceInfo.getSystemCode().equalsIgnoreCase("NULL") || 135 serviceInfo.getNfcid2().equalsIgnoreCase("NULL") || 136 serviceInfo.getT3tPmm().equalsIgnoreCase("NULL")) { 137 return false; 138 } 139 } 140 if (service.equals(mForegroundRequested) && mForegroundUid == callingUid) { 141 Log.e(TAG, "registerEnabledForegroundService: The service is already requested " 142 + "to the foreground service"); 143 return true; 144 } 145 if (mForegroundUtils.registerUidToBackgroundCallback(this, callingUid)) { 146 mForegroundRequested = service; 147 mForegroundUid = callingUid; 148 success = true; 149 } else { 150 Log.e(TAG, "registerEnabledForegroundService: Calling UID is not in " 151 + "the foreground, ignorning!"); 152 } 153 } 154 if (success) { 155 computeEnabledForegroundService(); 156 } 157 return success; 158 } 159 unregisterForegroundService(int uid)160 boolean unregisterForegroundService(int uid) { 161 if (DBG) Log.d(TAG, "unregisterForegroundService"); 162 boolean success = false; 163 synchronized (mLock) { 164 if (mForegroundUid == uid) { 165 mForegroundRequested = null; 166 mForegroundUid = -1; 167 success = true; 168 } // else, other UID in foreground 169 } 170 if (success) { 171 computeEnabledForegroundService(); 172 } 173 return success; 174 } 175 unregisteredEnabledForegroundService(int callingUid)176 public boolean unregisteredEnabledForegroundService(int callingUid) { 177 if (DBG) Log.d(TAG, "unregisterEnabledForegroundService"); 178 // Verify the calling UID is in the foreground 179 if (mForegroundUtils.isInForeground(callingUid)) { 180 return unregisterForegroundService(callingUid); 181 } else { 182 Log.e(TAG, "unregisterEnabledForegroundService: Calling UID is not in " 183 + "the foreground, ignorning!"); 184 return false; 185 } 186 } 187 188 @Override onUidToBackground(int uid)189 public void onUidToBackground(int uid) { 190 if (DBG) Log.d(TAG, "onUidToBackground"); 191 unregisterForegroundService(uid); 192 } 193 onHostEmulationActivated()194 public void onHostEmulationActivated() { 195 if (DBG) Log.d(TAG, "onHostEmulationActivated"); 196 synchronized (mLock) { 197 mActivated = true; 198 } 199 } 200 onHostEmulationDeactivated()201 public void onHostEmulationDeactivated() { 202 if (DBG) Log.d(TAG, "onHostEmulationDeactivated"); 203 boolean needComputeFg = false; 204 synchronized (mLock) { 205 mActivated = false; 206 if (mComputeFgRequested) { 207 needComputeFg = true; 208 } 209 } 210 if (needComputeFg) { 211 Log.d(TAG, "onHostEmulationDeactivated: do postponed configuration"); 212 computeEnabledForegroundService(); 213 } 214 } 215 onNfcDisabled()216 public void onNfcDisabled() { 217 synchronized (mLock) { 218 mForegroundComponent = null; 219 mForegroundRequested = null; 220 mActivated = false; 221 mComputeFgRequested = false; 222 mForegroundUid = -1; 223 } 224 } 225 onUserSwitched(int userId)226 public void onUserSwitched(int userId) { 227 synchronized (mLock) { 228 mForegroundComponent = null; 229 mForegroundRequested = null; 230 mActivated = false; 231 mComputeFgRequested = false; 232 mForegroundUid = -1; 233 } 234 } 235 dump(FileDescriptor fd, PrintWriter pw, String[] args)236 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 237 } 238 239 /** 240 * Dump debugging information as a EnabledNfcFServicesProto 241 * 242 * Note: 243 * See proto definition in frameworks/base/core/proto/android/nfc/card_emulation.proto 244 * When writing a nested message, must call {@link ProtoOutputStream#start(long)} before and 245 * {@link ProtoOutputStream#end(long)} after. 246 * Never reuse a proto field number. When removing a field, mark it as reserved. 247 */ dumpDebug(ProtoOutputStream proto)248 public void dumpDebug(ProtoOutputStream proto) { 249 synchronized (mLock) { 250 if (mForegroundComponent != null) { 251 Utils.dumpDebugComponentName( 252 mForegroundComponent, proto, EnabledNfcFServicesProto.FOREGROUND_COMPONENT); 253 } 254 if (mForegroundRequested != null) { 255 Utils.dumpDebugComponentName( 256 mForegroundRequested, proto, EnabledNfcFServicesProto.FOREGROUND_REQUESTED); 257 } 258 proto.write(EnabledNfcFServicesProto.ACTIVATED, mActivated); 259 proto.write(EnabledNfcFServicesProto.COMPUTE_FG_REQUESTED, mComputeFgRequested); 260 proto.write(EnabledNfcFServicesProto.FOREGROUND_UID, mForegroundUid); 261 } 262 } 263 264 @VisibleForTesting isActivated()265 public boolean isActivated() { 266 return mActivated; 267 } 268 269 @VisibleForTesting isNfcDisabled()270 public boolean isNfcDisabled() { 271 return !mActivated && mForegroundUid == -1; 272 } 273 274 @VisibleForTesting isUserSwitched()275 public boolean isUserSwitched() { 276 return !mActivated && mForegroundUid == -1 && !mComputeFgRequested; 277 } 278 } 279