1 /* 2 * Copyright (C) 2016 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.server.wifi.aware; 18 19 import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_VERBOSE_LOGGING_ENABLED; 20 21 import android.Manifest; 22 import android.annotation.NonNull; 23 import android.app.AppOpsManager; 24 import android.content.Context; 25 import android.content.pm.PackageManager; 26 import android.hardware.wifi.V1_0.NanStatusType; 27 import android.net.wifi.aware.AwareResources; 28 import android.net.wifi.aware.Characteristics; 29 import android.net.wifi.aware.ConfigRequest; 30 import android.net.wifi.aware.DiscoverySession; 31 import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback; 32 import android.net.wifi.aware.IWifiAwareEventCallback; 33 import android.net.wifi.aware.IWifiAwareMacAddressProvider; 34 import android.net.wifi.aware.IWifiAwareManager; 35 import android.net.wifi.aware.PublishConfig; 36 import android.net.wifi.aware.SubscribeConfig; 37 import android.os.Binder; 38 import android.os.Handler; 39 import android.os.HandlerThread; 40 import android.os.IBinder; 41 import android.os.ParcelFileDescriptor; 42 import android.os.RemoteException; 43 import android.util.Log; 44 import android.util.SparseArray; 45 import android.util.SparseIntArray; 46 47 import com.android.server.wifi.Clock; 48 import com.android.server.wifi.WifiSettingsConfigStore; 49 import com.android.server.wifi.util.NetdWrapper; 50 import com.android.server.wifi.util.WifiPermissionsUtil; 51 import com.android.server.wifi.util.WifiPermissionsWrapper; 52 53 import java.io.FileDescriptor; 54 import java.io.PrintWriter; 55 import java.util.List; 56 57 /** 58 * Implementation of the IWifiAwareManager AIDL interface. Performs validity 59 * (permission and clientID-UID mapping) checks and delegates execution to the 60 * WifiAwareStateManager singleton handler. Limited state to feedback which has to 61 * be provided instantly: client and session IDs. 62 */ 63 public class WifiAwareServiceImpl extends IWifiAwareManager.Stub { 64 private static final String TAG = "WifiAwareService"; 65 private boolean mDbg = false; 66 67 private Context mContext; 68 private AppOpsManager mAppOps; 69 private WifiPermissionsUtil mWifiPermissionsUtil; 70 private WifiAwareStateManager mStateManager; 71 private WifiAwareShellCommand mShellCommand; 72 private Handler mHandler; 73 74 private final Object mLock = new Object(); 75 private final SparseArray<IBinder.DeathRecipient> mDeathRecipientsByClientId = 76 new SparseArray<>(); 77 private int mNextClientId = 1; 78 private final SparseIntArray mUidByClientId = new SparseIntArray(); 79 WifiAwareServiceImpl(Context context)80 public WifiAwareServiceImpl(Context context) { 81 mContext = context; 82 mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 83 } 84 85 /** 86 * Proxy for the final native call of the parent class. Enables mocking of 87 * the function. 88 */ getMockableCallingUid()89 public int getMockableCallingUid() { 90 return getCallingUid(); 91 } 92 93 /** 94 * Start the service: allocate a new thread (for now), start the handlers of 95 * the components of the service. 96 */ start(HandlerThread handlerThread, WifiAwareStateManager awareStateManager, WifiAwareShellCommand awareShellCommand, WifiAwareMetrics awareMetrics, WifiPermissionsUtil wifiPermissionsUtil, WifiPermissionsWrapper permissionsWrapper, WifiSettingsConfigStore settingsConfigStore, WifiAwareNativeManager wifiAwareNativeManager, WifiAwareNativeApi wifiAwareNativeApi, WifiAwareNativeCallback wifiAwareNativeCallback, NetdWrapper netdWrapper)97 public void start(HandlerThread handlerThread, WifiAwareStateManager awareStateManager, 98 WifiAwareShellCommand awareShellCommand, WifiAwareMetrics awareMetrics, 99 WifiPermissionsUtil wifiPermissionsUtil, WifiPermissionsWrapper permissionsWrapper, 100 WifiSettingsConfigStore settingsConfigStore, 101 WifiAwareNativeManager wifiAwareNativeManager, WifiAwareNativeApi wifiAwareNativeApi, 102 WifiAwareNativeCallback wifiAwareNativeCallback, NetdWrapper netdWrapper) { 103 Log.i(TAG, "Starting Wi-Fi Aware service"); 104 105 mWifiPermissionsUtil = wifiPermissionsUtil; 106 mStateManager = awareStateManager; 107 mShellCommand = awareShellCommand; 108 mHandler = new Handler(handlerThread.getLooper()); 109 110 mHandler.post(() -> { 111 mStateManager.start(mContext, handlerThread.getLooper(), awareMetrics, 112 wifiPermissionsUtil, permissionsWrapper, new Clock(), netdWrapper); 113 114 settingsConfigStore.registerChangeListener( 115 WIFI_VERBOSE_LOGGING_ENABLED, 116 (key, newValue) -> enableVerboseLogging(newValue, awareStateManager, 117 wifiAwareNativeManager, wifiAwareNativeApi, wifiAwareNativeCallback), 118 mHandler); 119 enableVerboseLogging(settingsConfigStore.get(WIFI_VERBOSE_LOGGING_ENABLED), 120 awareStateManager, 121 wifiAwareNativeManager, wifiAwareNativeApi, 122 wifiAwareNativeCallback); 123 }); 124 } 125 enableVerboseLogging(boolean dbg, WifiAwareStateManager awareStateManager, WifiAwareNativeManager wifiAwareNativeManager, WifiAwareNativeApi wifiAwareNativeApi, WifiAwareNativeCallback wifiAwareNativeCallback)126 private void enableVerboseLogging(boolean dbg, WifiAwareStateManager awareStateManager, 127 WifiAwareNativeManager wifiAwareNativeManager, WifiAwareNativeApi wifiAwareNativeApi, 128 WifiAwareNativeCallback wifiAwareNativeCallback) { 129 mDbg = dbg; 130 awareStateManager.enableVerboseLogging(dbg); 131 if (awareStateManager.mDataPathMgr != null) { // needed for unit tests 132 awareStateManager.mDataPathMgr.enableVerboseLogging(dbg); 133 } 134 wifiAwareNativeCallback.enableVerboseLogging(dbg); 135 wifiAwareNativeManager.enableVerboseLogging(dbg); 136 wifiAwareNativeApi.enableVerboseLogging(dbg); 137 } 138 139 /** 140 * Start/initialize portions of the service which require the boot stage to be complete. 141 */ startLate()142 public void startLate() { 143 Log.i(TAG, "Late initialization of Wi-Fi Aware service"); 144 145 mHandler.post(() -> mStateManager.startLate()); 146 } 147 148 @Override isUsageEnabled()149 public boolean isUsageEnabled() { 150 enforceAccessPermission(); 151 152 return mStateManager.isUsageEnabled(); 153 } 154 155 @Override getCharacteristics()156 public Characteristics getCharacteristics() { 157 enforceAccessPermission(); 158 159 return mStateManager.getCapabilities() == null ? null 160 : mStateManager.getCapabilities().toPublicCharacteristics(); 161 } 162 163 @Override getAvailableAwareResources()164 public AwareResources getAvailableAwareResources() { 165 enforceAccessPermission(); 166 return mStateManager.getAvailableAwareResources(); 167 } 168 169 @Override isDeviceAttached()170 public boolean isDeviceAttached() { 171 enforceAccessPermission(); 172 return mDeathRecipientsByClientId.size() != 0; 173 } 174 175 @Override enableInstantCommunicationMode(String callingPackage, boolean enable)176 public void enableInstantCommunicationMode(String callingPackage, boolean enable) { 177 if (!mWifiPermissionsUtil.isSystem(callingPackage, Binder.getCallingUid())) { 178 Log.i(TAG, "enableInstantCommunicationMode not allowed for uid=" 179 + Binder.getCallingUid()); 180 return; 181 } 182 enforceChangePermission(); 183 mStateManager.enableInstantCommunicationMode(enable); 184 } 185 186 @Override isInstantCommunicationModeEnabled()187 public boolean isInstantCommunicationModeEnabled() { 188 enforceAccessPermission(); 189 return mStateManager.isInstantCommunicationModeEnabled(); 190 } 191 192 @Override connect(final IBinder binder, String callingPackage, String callingFeatureId, IWifiAwareEventCallback callback, ConfigRequest configRequest, boolean notifyOnIdentityChanged)193 public void connect(final IBinder binder, String callingPackage, String callingFeatureId, 194 IWifiAwareEventCallback callback, ConfigRequest configRequest, 195 boolean notifyOnIdentityChanged) { 196 enforceAccessPermission(); 197 enforceChangePermission(); 198 199 final int uid = getMockableCallingUid(); 200 mAppOps.checkPackage(uid, callingPackage); 201 202 if (callback == null) { 203 throw new IllegalArgumentException("Callback must not be null"); 204 } 205 if (binder == null) { 206 throw new IllegalArgumentException("Binder must not be null"); 207 } 208 209 if (notifyOnIdentityChanged) { 210 enforceLocationPermission(callingPackage, callingFeatureId, getMockableCallingUid()); 211 } 212 213 if (configRequest != null) { 214 enforceNetworkStackPermission(); 215 } else { 216 configRequest = new ConfigRequest.Builder().build(); 217 } 218 configRequest.validate(); 219 220 221 int pid = getCallingPid(); 222 223 final int clientId; 224 synchronized (mLock) { 225 clientId = mNextClientId++; 226 } 227 228 if (mDbg) { 229 Log.v(TAG, "connect: uid=" + uid + ", clientId=" + clientId + ", configRequest" 230 + configRequest + ", notifyOnIdentityChanged=" + notifyOnIdentityChanged); 231 } 232 233 IBinder.DeathRecipient dr = new IBinder.DeathRecipient() { 234 @Override 235 public void binderDied() { 236 if (mDbg) Log.v(TAG, "binderDied: clientId=" + clientId); 237 binder.unlinkToDeath(this, 0); 238 239 synchronized (mLock) { 240 mDeathRecipientsByClientId.delete(clientId); 241 mUidByClientId.delete(clientId); 242 } 243 244 mStateManager.disconnect(clientId); 245 } 246 }; 247 248 try { 249 binder.linkToDeath(dr, 0); 250 } catch (RemoteException e) { 251 Log.e(TAG, "Error on linkToDeath - " + e); 252 try { 253 callback.onConnectFail(NanStatusType.INTERNAL_FAILURE); 254 } catch (RemoteException e1) { 255 Log.e(TAG, "Error on onConnectFail()"); 256 } 257 return; 258 } 259 260 synchronized (mLock) { 261 mDeathRecipientsByClientId.put(clientId, dr); 262 mUidByClientId.put(clientId, uid); 263 } 264 265 mStateManager.connect(clientId, uid, pid, callingPackage, callingFeatureId, callback, 266 configRequest, notifyOnIdentityChanged); 267 } 268 269 @Override disconnect(int clientId, IBinder binder)270 public void disconnect(int clientId, IBinder binder) { 271 enforceAccessPermission(); 272 enforceChangePermission(); 273 274 int uid = getMockableCallingUid(); 275 enforceClientValidity(uid, clientId); 276 if (mDbg) Log.v(TAG, "disconnect: uid=" + uid + ", clientId=" + clientId); 277 278 if (binder == null) { 279 throw new IllegalArgumentException("Binder must not be null"); 280 } 281 282 synchronized (mLock) { 283 IBinder.DeathRecipient dr = mDeathRecipientsByClientId.get(clientId); 284 if (dr != null) { 285 binder.unlinkToDeath(dr, 0); 286 mDeathRecipientsByClientId.delete(clientId); 287 } 288 mUidByClientId.delete(clientId); 289 } 290 291 mStateManager.disconnect(clientId); 292 } 293 294 @Override terminateSession(int clientId, int sessionId)295 public void terminateSession(int clientId, int sessionId) { 296 enforceAccessPermission(); 297 enforceChangePermission(); 298 299 int uid = getMockableCallingUid(); 300 enforceClientValidity(uid, clientId); 301 if (mDbg) { 302 Log.v(TAG, "terminateSession: sessionId=" + sessionId + ", uid=" + uid + ", clientId=" 303 + clientId); 304 } 305 306 mStateManager.terminateSession(clientId, sessionId); 307 } 308 309 @Override publish(String callingPackage, String callingFeatureId, int clientId, PublishConfig publishConfig, IWifiAwareDiscoverySessionCallback callback)310 public void publish(String callingPackage, String callingFeatureId, int clientId, 311 PublishConfig publishConfig, IWifiAwareDiscoverySessionCallback callback) { 312 enforceAccessPermission(); 313 enforceChangePermission(); 314 315 int uid = getMockableCallingUid(); 316 mAppOps.checkPackage(uid, callingPackage); 317 318 enforceLocationPermission(callingPackage, callingFeatureId, getMockableCallingUid()); 319 320 if (callback == null) { 321 throw new IllegalArgumentException("Callback must not be null"); 322 } 323 if (publishConfig == null) { 324 throw new IllegalArgumentException("PublishConfig must not be null"); 325 } 326 publishConfig.assertValid(mStateManager.getCharacteristics(), 327 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)); 328 329 enforceClientValidity(uid, clientId); 330 if (mDbg) { 331 Log.v(TAG, "publish: uid=" + uid + ", clientId=" + clientId + ", publishConfig=" 332 + publishConfig + ", callback=" + callback); 333 } 334 335 mStateManager.publish(clientId, publishConfig, callback); 336 } 337 338 @Override updatePublish(int clientId, int sessionId, PublishConfig publishConfig)339 public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) { 340 enforceAccessPermission(); 341 enforceChangePermission(); 342 343 if (publishConfig == null) { 344 throw new IllegalArgumentException("PublishConfig must not be null"); 345 } 346 publishConfig.assertValid(mStateManager.getCharacteristics(), 347 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)); 348 349 int uid = getMockableCallingUid(); 350 enforceClientValidity(uid, clientId); 351 if (mDbg) { 352 Log.v(TAG, "updatePublish: uid=" + uid + ", clientId=" + clientId + ", sessionId=" 353 + sessionId + ", config=" + publishConfig); 354 } 355 356 mStateManager.updatePublish(clientId, sessionId, publishConfig); 357 } 358 359 @Override subscribe(String callingPackage, String callingFeatureId, int clientId, SubscribeConfig subscribeConfig, IWifiAwareDiscoverySessionCallback callback)360 public void subscribe(String callingPackage, String callingFeatureId, int clientId, 361 SubscribeConfig subscribeConfig, IWifiAwareDiscoverySessionCallback callback) { 362 enforceAccessPermission(); 363 enforceChangePermission(); 364 365 int uid = getMockableCallingUid(); 366 mAppOps.checkPackage(uid, callingPackage); 367 368 enforceLocationPermission(callingPackage, callingFeatureId, getMockableCallingUid()); 369 370 if (callback == null) { 371 throw new IllegalArgumentException("Callback must not be null"); 372 } 373 if (subscribeConfig == null) { 374 throw new IllegalArgumentException("SubscribeConfig must not be null"); 375 } 376 subscribeConfig.assertValid(mStateManager.getCharacteristics(), 377 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)); 378 379 enforceClientValidity(uid, clientId); 380 if (mDbg) { 381 Log.v(TAG, "subscribe: uid=" + uid + ", clientId=" + clientId + ", config=" 382 + subscribeConfig + ", callback=" + callback); 383 } 384 385 mStateManager.subscribe(clientId, subscribeConfig, callback); 386 } 387 388 @Override updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig)389 public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) { 390 enforceAccessPermission(); 391 enforceChangePermission(); 392 393 if (subscribeConfig == null) { 394 throw new IllegalArgumentException("SubscribeConfig must not be null"); 395 } 396 subscribeConfig.assertValid(mStateManager.getCharacteristics(), 397 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)); 398 399 int uid = getMockableCallingUid(); 400 enforceClientValidity(uid, clientId); 401 if (mDbg) { 402 Log.v(TAG, "updateSubscribe: uid=" + uid + ", clientId=" + clientId + ", sessionId=" 403 + sessionId + ", config=" + subscribeConfig); 404 } 405 406 mStateManager.updateSubscribe(clientId, sessionId, subscribeConfig); 407 } 408 409 @Override sendMessage(int clientId, int sessionId, int peerId, byte[] message, int messageId, int retryCount)410 public void sendMessage(int clientId, int sessionId, int peerId, byte[] message, int messageId, 411 int retryCount) { 412 enforceAccessPermission(); 413 enforceChangePermission(); 414 415 if (retryCount != 0) { 416 enforceNetworkStackPermission(); 417 } 418 419 if (message != null && message.length 420 > mStateManager.getCharacteristics().getMaxServiceSpecificInfoLength()) { 421 throw new IllegalArgumentException( 422 "Message length longer than supported by device characteristics"); 423 } 424 if (retryCount < 0 || retryCount > DiscoverySession.getMaxSendRetryCount()) { 425 throw new IllegalArgumentException("Invalid 'retryCount' must be non-negative " 426 + "and <= DiscoverySession.MAX_SEND_RETRY_COUNT"); 427 } 428 429 int uid = getMockableCallingUid(); 430 enforceClientValidity(uid, clientId); 431 if (mDbg) { 432 Log.v(TAG, 433 "sendMessage: sessionId=" + sessionId + ", uid=" + uid + ", clientId=" 434 + clientId + ", peerId=" + peerId + ", messageId=" + messageId 435 + ", retryCount=" + retryCount); 436 } 437 438 mStateManager.sendMessage(uid, clientId, sessionId, peerId, message, messageId, retryCount); 439 } 440 441 @Override requestMacAddresses(int uid, List peerIds, IWifiAwareMacAddressProvider callback)442 public void requestMacAddresses(int uid, List peerIds, IWifiAwareMacAddressProvider callback) { 443 enforceNetworkStackPermission(); 444 445 mStateManager.requestMacAddresses(uid, peerIds, callback); 446 } 447 448 @Override handleShellCommand(@onNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args)449 public int handleShellCommand(@NonNull ParcelFileDescriptor in, 450 @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, 451 @NonNull String[] args) { 452 return mShellCommand.exec( 453 this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), 454 args); 455 } 456 457 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)458 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 459 if (mContext.checkCallingOrSelfPermission( 460 android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { 461 pw.println("Permission Denial: can't dump WifiAwareService from pid=" 462 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); 463 return; 464 } 465 pw.println("Wi-Fi Aware Service"); 466 synchronized (mLock) { 467 pw.println(" mNextClientId: " + mNextClientId); 468 pw.println(" mDeathRecipientsByClientId: " + mDeathRecipientsByClientId); 469 pw.println(" mUidByClientId: " + mUidByClientId); 470 } 471 mStateManager.dump(fd, pw, args); 472 } 473 enforceClientValidity(int uid, int clientId)474 private void enforceClientValidity(int uid, int clientId) { 475 synchronized (mLock) { 476 int uidIndex = mUidByClientId.indexOfKey(clientId); 477 if (uidIndex < 0 || mUidByClientId.valueAt(uidIndex) != uid) { 478 throw new SecurityException("Attempting to use invalid uid+clientId mapping: uid=" 479 + uid + ", clientId=" + clientId); 480 } 481 } 482 } 483 enforceAccessPermission()484 private void enforceAccessPermission() { 485 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, TAG); 486 } 487 enforceChangePermission()488 private void enforceChangePermission() { 489 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG); 490 } 491 enforceLocationPermission(String callingPackage, String callingFeatureId, int uid)492 private void enforceLocationPermission(String callingPackage, String callingFeatureId, 493 int uid) { 494 mWifiPermissionsUtil.enforceLocationPermission(callingPackage, callingFeatureId, uid); 495 } 496 enforceNetworkStackPermission()497 private void enforceNetworkStackPermission() { 498 mContext.enforceCallingOrSelfPermission(Manifest.permission.NETWORK_STACK, TAG); 499 } 500 } 501