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