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