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 android.content.Context; 20 import android.content.pm.PackageManager; 21 import android.hardware.wifi.V1_0.NanStatusType; 22 import android.net.wifi.RttManager; 23 import android.net.wifi.aware.Characteristics; 24 import android.net.wifi.aware.ConfigRequest; 25 import android.net.wifi.aware.DiscoverySession; 26 import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback; 27 import android.net.wifi.aware.IWifiAwareEventCallback; 28 import android.net.wifi.aware.IWifiAwareManager; 29 import android.net.wifi.aware.PublishConfig; 30 import android.net.wifi.aware.SubscribeConfig; 31 import android.os.Binder; 32 import android.os.HandlerThread; 33 import android.os.IBinder; 34 import android.os.RemoteException; 35 import android.util.Log; 36 import android.util.SparseArray; 37 import android.util.SparseIntArray; 38 39 import java.io.FileDescriptor; 40 import java.io.PrintWriter; 41 import 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 */ 49 public 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 boolean isUsageEnabled() { 98 enforceAccessPermission(); 99 100 return mStateManager.isUsageEnabled(); 101 } 102 103 @Override 104 public Characteristics getCharacteristics() { 105 enforceAccessPermission(); 106 107 return mStateManager.getCapabilities() == null ? null 108 : mStateManager.getCapabilities().toPublicCharacteristics(); 109 } 110 111 @Override 112 public void connect(final IBinder binder, String callingPackage, 113 IWifiAwareEventCallback callback, ConfigRequest configRequest, 114 boolean notifyOnIdentityChanged) { 115 enforceAccessPermission(); 116 enforceChangePermission(); 117 if (callback == null) { 118 throw new IllegalArgumentException("Callback must not be null"); 119 } 120 if (binder == null) { 121 throw new IllegalArgumentException("Binder must not be null"); 122 } 123 124 if (notifyOnIdentityChanged) { 125 enforceLocationPermission(); 126 } 127 128 if (configRequest != null) { 129 enforceConnectivityInternalPermission(); 130 } else { 131 configRequest = new ConfigRequest.Builder().build(); 132 } 133 configRequest.validate(); 134 135 final int uid = getMockableCallingUid(); 136 int pid = getCallingPid(); 137 138 final int clientId; 139 synchronized (mLock) { 140 clientId = mNextClientId++; 141 } 142 143 if (VDBG) { 144 Log.v(TAG, "connect: uid=" + uid + ", clientId=" + clientId + ", configRequest" 145 + configRequest + ", notifyOnIdentityChanged=" + notifyOnIdentityChanged); 146 } 147 148 IBinder.DeathRecipient dr = new IBinder.DeathRecipient() { 149 @Override 150 public void binderDied() { 151 if (DBG) Log.d(TAG, "binderDied: clientId=" + clientId); 152 binder.unlinkToDeath(this, 0); 153 154 synchronized (mLock) { 155 mDeathRecipientsByClientId.delete(clientId); 156 mUidByClientId.delete(clientId); 157 } 158 159 mStateManager.disconnect(clientId); 160 } 161 }; 162 163 try { 164 binder.linkToDeath(dr, 0); 165 } catch (RemoteException e) { 166 Log.e(TAG, "Error on linkToDeath - " + e); 167 try { 168 callback.onConnectFail(NanStatusType.INTERNAL_FAILURE); 169 } catch (RemoteException e1) { 170 Log.e(TAG, "Error on onConnectFail()"); 171 } 172 return; 173 } 174 175 synchronized (mLock) { 176 mDeathRecipientsByClientId.put(clientId, dr); 177 mUidByClientId.put(clientId, uid); 178 } 179 180 mStateManager.connect(clientId, uid, pid, callingPackage, callback, configRequest, 181 notifyOnIdentityChanged); 182 } 183 184 @Override 185 public void disconnect(int clientId, IBinder binder) { 186 enforceAccessPermission(); 187 enforceChangePermission(); 188 189 int uid = getMockableCallingUid(); 190 enforceClientValidity(uid, clientId); 191 if (VDBG) Log.v(TAG, "disconnect: uid=" + uid + ", clientId=" + clientId); 192 193 if (binder == null) { 194 throw new IllegalArgumentException("Binder must not be null"); 195 } 196 197 synchronized (mLock) { 198 IBinder.DeathRecipient dr = mDeathRecipientsByClientId.get(clientId); 199 if (dr != null) { 200 binder.unlinkToDeath(dr, 0); 201 mDeathRecipientsByClientId.delete(clientId); 202 } 203 mUidByClientId.delete(clientId); 204 } 205 206 mStateManager.disconnect(clientId); 207 } 208 209 @Override 210 public void terminateSession(int clientId, int sessionId) { 211 enforceAccessPermission(); 212 enforceChangePermission(); 213 214 int uid = getMockableCallingUid(); 215 enforceClientValidity(uid, clientId); 216 if (VDBG) { 217 Log.v(TAG, "terminateSession: sessionId=" + sessionId + ", uid=" + uid + ", clientId=" 218 + clientId); 219 } 220 221 mStateManager.terminateSession(clientId, sessionId); 222 } 223 224 @Override 225 public void publish(int clientId, PublishConfig publishConfig, 226 IWifiAwareDiscoverySessionCallback callback) { 227 enforceAccessPermission(); 228 enforceChangePermission(); 229 enforceLocationPermission(); 230 231 if (callback == null) { 232 throw new IllegalArgumentException("Callback must not be null"); 233 } 234 if (publishConfig == null) { 235 throw new IllegalArgumentException("PublishConfig must not be null"); 236 } 237 publishConfig.assertValid(mStateManager.getCharacteristics()); 238 239 int uid = getMockableCallingUid(); 240 enforceClientValidity(uid, clientId); 241 if (VDBG) { 242 Log.v(TAG, "publish: uid=" + uid + ", clientId=" + clientId + ", publishConfig=" 243 + publishConfig + ", callback=" + callback); 244 } 245 246 mStateManager.publish(clientId, publishConfig, callback); 247 } 248 249 @Override 250 public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) { 251 enforceAccessPermission(); 252 enforceChangePermission(); 253 254 if (publishConfig == null) { 255 throw new IllegalArgumentException("PublishConfig must not be null"); 256 } 257 publishConfig.assertValid(mStateManager.getCharacteristics()); 258 259 int uid = getMockableCallingUid(); 260 enforceClientValidity(uid, clientId); 261 if (VDBG) { 262 Log.v(TAG, "updatePublish: uid=" + uid + ", clientId=" + clientId + ", sessionId=" 263 + sessionId + ", config=" + publishConfig); 264 } 265 266 mStateManager.updatePublish(clientId, sessionId, publishConfig); 267 } 268 269 @Override 270 public void subscribe(int clientId, SubscribeConfig subscribeConfig, 271 IWifiAwareDiscoverySessionCallback callback) { 272 enforceAccessPermission(); 273 enforceChangePermission(); 274 enforceLocationPermission(); 275 276 if (callback == null) { 277 throw new IllegalArgumentException("Callback must not be null"); 278 } 279 if (subscribeConfig == null) { 280 throw new IllegalArgumentException("SubscribeConfig must not be null"); 281 } 282 subscribeConfig.assertValid(mStateManager.getCharacteristics()); 283 284 int uid = getMockableCallingUid(); 285 enforceClientValidity(uid, clientId); 286 if (VDBG) { 287 Log.v(TAG, "subscribe: uid=" + uid + ", clientId=" + clientId + ", config=" 288 + subscribeConfig + ", callback=" + callback); 289 } 290 291 mStateManager.subscribe(clientId, subscribeConfig, callback); 292 } 293 294 @Override 295 public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) { 296 enforceAccessPermission(); 297 enforceChangePermission(); 298 299 if (subscribeConfig == null) { 300 throw new IllegalArgumentException("SubscribeConfig must not be null"); 301 } 302 subscribeConfig.assertValid(mStateManager.getCharacteristics()); 303 304 int uid = getMockableCallingUid(); 305 enforceClientValidity(uid, clientId); 306 if (VDBG) { 307 Log.v(TAG, "updateSubscribe: uid=" + uid + ", clientId=" + clientId + ", sessionId=" 308 + sessionId + ", config=" + subscribeConfig); 309 } 310 311 mStateManager.updateSubscribe(clientId, sessionId, subscribeConfig); 312 } 313 314 @Override 315 public void sendMessage(int clientId, int sessionId, int peerId, byte[] message, int messageId, 316 int retryCount) { 317 enforceAccessPermission(); 318 enforceChangePermission(); 319 320 if (retryCount != 0) { 321 enforceConnectivityInternalPermission(); 322 } 323 324 if (message != null 325 && message.length > mStateManager.getCharacteristics().getMaxServiceNameLength()) { 326 throw new IllegalArgumentException( 327 "Message length longer than supported by device characteristics"); 328 } 329 if (retryCount < 0 || retryCount > DiscoverySession.getMaxSendRetryCount()) { 330 throw new IllegalArgumentException("Invalid 'retryCount' must be non-negative " 331 + "and <= DiscoverySession.MAX_SEND_RETRY_COUNT"); 332 } 333 334 int uid = getMockableCallingUid(); 335 enforceClientValidity(uid, clientId); 336 if (VDBG) { 337 Log.v(TAG, 338 "sendMessage: sessionId=" + sessionId + ", uid=" + uid + ", clientId=" 339 + clientId + ", peerId=" + peerId + ", messageId=" + messageId 340 + ", retryCount=" + retryCount); 341 } 342 343 mStateManager.sendMessage(clientId, sessionId, peerId, message, messageId, retryCount); 344 } 345 346 @Override 347 public int startRanging(int clientId, int sessionId, RttManager.ParcelableRttParams params) { 348 enforceAccessPermission(); 349 enforceLocationPermission(); 350 351 // TODO: b/35676064 restricts access to this API until decide if will open. 352 enforceConnectivityInternalPermission(); 353 354 int uid = getMockableCallingUid(); 355 enforceClientValidity(uid, clientId); 356 if (VDBG) { 357 Log.v(TAG, "startRanging: clientId=" + clientId + ", sessionId=" + sessionId + ", " 358 + ", parms=" + Arrays.toString(params.mParams)); 359 } 360 361 if (params.mParams.length == 0) { 362 throw new IllegalArgumentException("Empty ranging parameters"); 363 } 364 365 int rangingId; 366 synchronized (mLock) { 367 rangingId = mNextRangingId++; 368 } 369 mStateManager.startRanging(clientId, sessionId, params.mParams, rangingId); 370 return rangingId; 371 } 372 373 @Override 374 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 375 if (mContext.checkCallingOrSelfPermission( 376 android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { 377 pw.println("Permission Denial: can't dump WifiAwareService from pid=" 378 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); 379 return; 380 } 381 pw.println("Wi-Fi Aware Service"); 382 synchronized (mLock) { 383 pw.println(" mNextClientId: " + mNextClientId); 384 pw.println(" mDeathRecipientsByClientId: " + mDeathRecipientsByClientId); 385 pw.println(" mUidByClientId: " + mUidByClientId); 386 } 387 mStateManager.dump(fd, pw, args); 388 } 389 390 private void enforceClientValidity(int uid, int clientId) { 391 synchronized (mLock) { 392 int uidIndex = mUidByClientId.indexOfKey(clientId); 393 if (uidIndex < 0 || mUidByClientId.valueAt(uidIndex) != uid) { 394 throw new SecurityException("Attempting to use invalid uid+clientId mapping: uid=" 395 + uid + ", clientId=" + clientId); 396 } 397 } 398 } 399 400 private void enforceAccessPermission() { 401 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, TAG); 402 } 403 404 private void enforceChangePermission() { 405 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG); 406 } 407 408 private void enforceLocationPermission() { 409 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, 410 TAG); 411 } 412 413 private void enforceConnectivityInternalPermission() { 414 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL, 415 TAG); 416 } 417 } 418