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