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