WifiAwareServiceImpl.java revision c29acea6ceda3aa4ee537c05ce7d05dac2655cf9
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 17package com.android.server.wifi.aware; 18 19import android.content.Context; 20import android.content.pm.PackageManager; 21import android.net.wifi.RttManager; 22import android.net.wifi.aware.ConfigRequest; 23import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback; 24import android.net.wifi.aware.IWifiAwareEventCallback; 25import android.net.wifi.aware.IWifiAwareManager; 26import android.net.wifi.aware.PublishConfig; 27import android.net.wifi.aware.SubscribeConfig; 28import android.net.wifi.aware.WifiAwareCharacteristics; 29import android.net.wifi.aware.WifiAwareDiscoveryBaseSession; 30import android.os.Binder; 31import android.os.HandlerThread; 32import android.os.IBinder; 33import android.os.RemoteException; 34import android.util.Log; 35import android.util.SparseArray; 36import android.util.SparseIntArray; 37 38import java.io.FileDescriptor; 39import java.io.PrintWriter; 40import java.util.Arrays; 41 42/** 43 * Implementation of the IWifiAwareManager AIDL interface. Performs validity 44 * (permission and clientID-UID mapping) checks and delegates execution to the 45 * WifiAwareStateManager singleton handler. Limited state to feedback which has to 46 * be provided instantly: client and session IDs. 47 */ 48public class WifiAwareServiceImpl extends IWifiAwareManager.Stub { 49 private static final String TAG = "WifiAwareService"; 50 private static final boolean DBG = false; 51 private static final boolean VDBG = false; // STOPSHIP if true 52 53 private Context mContext; 54 private WifiAwareStateManager mStateManager; 55 56 private final Object mLock = new Object(); 57 private final SparseArray<IBinder.DeathRecipient> mDeathRecipientsByClientId = 58 new SparseArray<>(); 59 private int mNextClientId = 1; 60 private int mNextRangingId = 1; 61 private final SparseIntArray mUidByClientId = new SparseIntArray(); 62 63 public WifiAwareServiceImpl(Context context) { 64 mContext = context.getApplicationContext(); 65 mStateManager = WifiAwareStateManager.getInstance(); 66 } 67 68 /** 69 * Proxy for the final native call of the parent class. Enables mocking of 70 * the function. 71 */ 72 public int getMockableCallingUid() { 73 return getCallingUid(); 74 } 75 76 /** 77 * Start the service: allocate a new thread (for now), start the handlers of 78 * the components of the service. 79 */ 80 public void start() { 81 Log.i(TAG, "Starting Wi-Fi Aware service"); 82 83 // TODO: share worker thread with other Wi-Fi handlers (b/27924886) 84 HandlerThread wifiAwareThread = new HandlerThread("wifiAwareService"); 85 wifiAwareThread.start(); 86 87 mStateManager.start(mContext, wifiAwareThread.getLooper()); 88 } 89 90 /** 91 * Start/initialize portions of the service which require the boot stage to be complete. 92 */ 93 public void startLate() { 94 Log.i(TAG, "Late initialization of Wi-Fi Aware service"); 95 96 mStateManager.startLate(); 97 } 98 99 @Override 100 public void enableUsage() { 101 enforceAccessPermission(); 102 enforceChangePermission(); 103 enforceConnectivityInternalPermission(); 104 105 mStateManager.enableUsage(); 106 } 107 108 @Override 109 public void disableUsage() { 110 enforceAccessPermission(); 111 enforceChangePermission(); 112 enforceConnectivityInternalPermission(); 113 114 mStateManager.disableUsage(); 115 116 /* 117 * Potential leak (b/27796984) since we keep app information here (uid, 118 * binder-link-to-death), while clearing all state information. However: 119 * (1) can't clear all information since don't have binder, (2) 120 * information will clear once app dies, (3) allows us to do security 121 * checks in the future. 122 */ 123 } 124 125 @Override 126 public boolean isUsageEnabled() { 127 enforceAccessPermission(); 128 129 return mStateManager.isUsageEnabled(); 130 } 131 132 @Override 133 public WifiAwareCharacteristics getCharacteristics() { 134 enforceAccessPermission(); 135 136 return mStateManager.getCapabilities() == null ? null 137 : mStateManager.getCapabilities().toPublicCharacteristics(); 138 } 139 140 @Override 141 public void connect(final IBinder binder, String callingPackage, 142 IWifiAwareEventCallback callback, ConfigRequest configRequest, 143 boolean notifyOnIdentityChanged) { 144 enforceAccessPermission(); 145 enforceChangePermission(); 146 if (callback == null) { 147 throw new IllegalArgumentException("Callback must not be null"); 148 } 149 if (binder == null) { 150 throw new IllegalArgumentException("Binder must not be null"); 151 } 152 153 if (notifyOnIdentityChanged) { 154 enforceLocationPermission(); 155 } 156 157 if (configRequest != null) { 158 enforceConnectivityInternalPermission(); 159 } else { 160 configRequest = new ConfigRequest.Builder().build(); 161 } 162 configRequest.validate(); 163 164 final int uid = getMockableCallingUid(); 165 int pid = getCallingPid(); 166 167 final int clientId; 168 synchronized (mLock) { 169 clientId = mNextClientId++; 170 } 171 172 if (VDBG) { 173 Log.v(TAG, "connect: uid=" + uid + ", clientId=" + clientId + ", configRequest" 174 + configRequest + ", notifyOnIdentityChanged=" + notifyOnIdentityChanged); 175 } 176 177 IBinder.DeathRecipient dr = new IBinder.DeathRecipient() { 178 @Override 179 public void binderDied() { 180 if (DBG) Log.d(TAG, "binderDied: clientId=" + clientId); 181 binder.unlinkToDeath(this, 0); 182 183 synchronized (mLock) { 184 mDeathRecipientsByClientId.delete(clientId); 185 mUidByClientId.delete(clientId); 186 } 187 188 mStateManager.disconnect(clientId); 189 } 190 }; 191 192 try { 193 binder.linkToDeath(dr, 0); 194 } catch (RemoteException e) { 195 Log.e(TAG, "Error on linkToDeath - " + e); 196 try { 197 callback.onConnectFail(WifiAwareNative.AWARE_STATUS_ERROR); 198 } catch (RemoteException e1) { 199 Log.e(TAG, "Error on onConnectFail()"); 200 } 201 return; 202 } 203 204 synchronized (mLock) { 205 mDeathRecipientsByClientId.put(clientId, dr); 206 mUidByClientId.put(clientId, uid); 207 } 208 209 mStateManager.connect(clientId, uid, pid, callingPackage, callback, configRequest, 210 notifyOnIdentityChanged); 211 } 212 213 @Override 214 public void disconnect(int clientId, IBinder binder) { 215 enforceAccessPermission(); 216 enforceChangePermission(); 217 218 int uid = getMockableCallingUid(); 219 enforceClientValidity(uid, clientId); 220 if (VDBG) Log.v(TAG, "disconnect: uid=" + uid + ", clientId=" + clientId); 221 222 if (binder == null) { 223 throw new IllegalArgumentException("Binder must not be null"); 224 } 225 226 synchronized (mLock) { 227 IBinder.DeathRecipient dr = mDeathRecipientsByClientId.get(clientId); 228 if (dr != null) { 229 binder.unlinkToDeath(dr, 0); 230 mDeathRecipientsByClientId.delete(clientId); 231 } 232 mUidByClientId.delete(clientId); 233 } 234 235 mStateManager.disconnect(clientId); 236 } 237 238 @Override 239 public void terminateSession(int clientId, int sessionId) { 240 enforceAccessPermission(); 241 enforceChangePermission(); 242 243 int uid = getMockableCallingUid(); 244 enforceClientValidity(uid, clientId); 245 if (VDBG) { 246 Log.v(TAG, "terminateSession: sessionId=" + sessionId + ", uid=" + uid + ", clientId=" 247 + clientId); 248 } 249 250 mStateManager.terminateSession(clientId, sessionId); 251 } 252 253 @Override 254 public void publish(int clientId, PublishConfig publishConfig, 255 IWifiAwareDiscoverySessionCallback callback) { 256 enforceAccessPermission(); 257 enforceChangePermission(); 258 enforceLocationPermission(); 259 260 if (callback == null) { 261 throw new IllegalArgumentException("Callback must not be null"); 262 } 263 if (publishConfig == null) { 264 throw new IllegalArgumentException("PublishConfig must not be null"); 265 } 266 publishConfig.assertValid(mStateManager.getCharacteristics()); 267 268 int uid = getMockableCallingUid(); 269 enforceClientValidity(uid, clientId); 270 if (VDBG) { 271 Log.v(TAG, "publish: uid=" + uid + ", clientId=" + clientId + ", publishConfig=" 272 + publishConfig + ", callback=" + callback); 273 } 274 275 mStateManager.publish(clientId, publishConfig, callback); 276 } 277 278 @Override 279 public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) { 280 enforceAccessPermission(); 281 enforceChangePermission(); 282 283 if (publishConfig == null) { 284 throw new IllegalArgumentException("PublishConfig must not be null"); 285 } 286 publishConfig.assertValid(mStateManager.getCharacteristics()); 287 288 int uid = getMockableCallingUid(); 289 enforceClientValidity(uid, clientId); 290 if (VDBG) { 291 Log.v(TAG, "updatePublish: uid=" + uid + ", clientId=" + clientId + ", sessionId=" 292 + sessionId + ", config=" + publishConfig); 293 } 294 295 mStateManager.updatePublish(clientId, sessionId, publishConfig); 296 } 297 298 @Override 299 public void subscribe(int clientId, SubscribeConfig subscribeConfig, 300 IWifiAwareDiscoverySessionCallback callback) { 301 enforceAccessPermission(); 302 enforceChangePermission(); 303 enforceLocationPermission(); 304 305 if (callback == null) { 306 throw new IllegalArgumentException("Callback must not be null"); 307 } 308 if (subscribeConfig == null) { 309 throw new IllegalArgumentException("SubscribeConfig must not be null"); 310 } 311 subscribeConfig.assertValid(mStateManager.getCharacteristics()); 312 313 int uid = getMockableCallingUid(); 314 enforceClientValidity(uid, clientId); 315 if (VDBG) { 316 Log.v(TAG, "subscribe: uid=" + uid + ", clientId=" + clientId + ", config=" 317 + subscribeConfig + ", callback=" + callback); 318 } 319 320 mStateManager.subscribe(clientId, subscribeConfig, callback); 321 } 322 323 @Override 324 public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) { 325 enforceAccessPermission(); 326 enforceChangePermission(); 327 328 if (subscribeConfig == null) { 329 throw new IllegalArgumentException("SubscribeConfig must not be null"); 330 } 331 subscribeConfig.assertValid(mStateManager.getCharacteristics()); 332 333 int uid = getMockableCallingUid(); 334 enforceClientValidity(uid, clientId); 335 if (VDBG) { 336 Log.v(TAG, "updateSubscribe: uid=" + uid + ", clientId=" + clientId + ", sessionId=" 337 + sessionId + ", config=" + subscribeConfig); 338 } 339 340 mStateManager.updateSubscribe(clientId, sessionId, subscribeConfig); 341 } 342 343 @Override 344 public void sendMessage(int clientId, int sessionId, int peerId, byte[] message, int messageId, 345 int retryCount) { 346 enforceAccessPermission(); 347 enforceChangePermission(); 348 349 if (message != null 350 && message.length > mStateManager.getCharacteristics().getMaxServiceNameLength()) { 351 throw new IllegalArgumentException( 352 "Message length longer than supported by device characteristics"); 353 } 354 if (retryCount < 0 || retryCount > WifiAwareDiscoveryBaseSession.getMaxSendRetryCount()) { 355 throw new IllegalArgumentException("Invalid 'retryCount' must be non-negative " 356 + "and <= WifiAwareDiscoveryBaseSession.MAX_SEND_RETRY_COUNT"); 357 } 358 359 int uid = getMockableCallingUid(); 360 enforceClientValidity(uid, clientId); 361 if (VDBG) { 362 Log.v(TAG, 363 "sendMessage: sessionId=" + sessionId + ", uid=" + uid + ", clientId=" 364 + clientId + ", peerId=" + peerId + ", messageId=" + messageId 365 + ", retryCount=" + retryCount); 366 } 367 368 mStateManager.sendMessage(clientId, sessionId, peerId, message, messageId, retryCount); 369 } 370 371 @Override 372 public int startRanging(int clientId, int sessionId, RttManager.ParcelableRttParams params) { 373 enforceAccessPermission(); 374 enforceLocationPermission(); 375 376 int uid = getMockableCallingUid(); 377 enforceClientValidity(uid, clientId); 378 if (VDBG) { 379 Log.v(TAG, "startRanging: clientId=" + clientId + ", sessionId=" + sessionId + ", " 380 + ", parms=" + Arrays.toString(params.mParams)); 381 } 382 383 if (params.mParams.length == 0) { 384 throw new IllegalArgumentException("Empty ranging parameters"); 385 } 386 387 int rangingId; 388 synchronized (mLock) { 389 rangingId = mNextRangingId++; 390 } 391 mStateManager.startRanging(clientId, sessionId, params.mParams, rangingId); 392 return rangingId; 393 } 394 395 @Override 396 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 397 if (mContext.checkCallingOrSelfPermission( 398 android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { 399 pw.println("Permission Denial: can't dump WifiAwareService from pid=" 400 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); 401 return; 402 } 403 pw.println("Wi-Fi Aware Service"); 404 synchronized (mLock) { 405 pw.println(" mNextClientId: " + mNextClientId); 406 pw.println(" mDeathRecipientsByClientId: " + mDeathRecipientsByClientId); 407 pw.println(" mUidByClientId: " + mUidByClientId); 408 } 409 mStateManager.dump(fd, pw, args); 410 } 411 412 private void enforceClientValidity(int uid, int clientId) { 413 synchronized (mLock) { 414 int uidIndex = mUidByClientId.indexOfKey(clientId); 415 if (uidIndex < 0 || mUidByClientId.valueAt(uidIndex) != uid) { 416 throw new SecurityException("Attempting to use invalid uid+clientId mapping: uid=" 417 + uid + ", clientId=" + clientId); 418 } 419 } 420 } 421 422 private void enforceAccessPermission() { 423 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, TAG); 424 } 425 426 private void enforceChangePermission() { 427 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG); 428 } 429 430 private void enforceLocationPermission() { 431 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, 432 TAG); 433 } 434 435 private void enforceConnectivityInternalPermission() { 436 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL, 437 TAG); 438 } 439} 440