1 /* 2 * Copyright (C) 2022 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.uwb.pm; 18 19 import android.bluetooth.le.ScanFilter; 20 import android.bluetooth.le.ScanSettings; 21 import android.content.AttributionSource; 22 import android.content.Context; 23 import android.os.Handler; 24 import android.os.Message; 25 import android.os.RemoteException; 26 import android.util.Log; 27 import android.uwb.IUwbRangingCallbacks; 28 import android.uwb.SessionHandle; 29 30 import com.android.internal.util.State; 31 import com.android.modules.utils.HandlerExecutor; 32 import com.android.server.uwb.UwbInjector; 33 import com.android.server.uwb.data.ServiceProfileData.ServiceProfileInfo; 34 import com.android.server.uwb.data.UwbConfig; 35 import com.android.server.uwb.discovery.DiscoveryProvider; 36 import com.android.server.uwb.discovery.DiscoveryProviderFactory; 37 import com.android.server.uwb.discovery.DiscoveryScanProvider; 38 import com.android.server.uwb.discovery.TransportClientProvider; 39 import com.android.server.uwb.discovery.TransportProviderFactory; 40 import com.android.server.uwb.discovery.info.DiscoveryInfo; 41 import com.android.server.uwb.discovery.info.FiraConnectorCapabilities; 42 import com.android.server.uwb.discovery.info.ScanInfo; 43 import com.android.server.uwb.discovery.info.TransportClientInfo; 44 import com.android.server.uwb.secure.SecureFactory; 45 import com.android.server.uwb.secure.SecureSession; 46 import com.android.server.uwb.secure.csml.SessionData; 47 import com.android.server.uwb.secure.csml.UwbCapability; 48 49 import com.google.uwb.support.fira.FiraSpecificationParams; 50 import com.google.uwb.support.generic.GenericSpecificationParams; 51 52 import java.util.List; 53 import java.util.Optional; 54 55 /** Session for PACS profile controller */ 56 public class PacsControllerSession extends RangingSessionController { 57 private static final String TAG = "PACSControllerSession"; 58 private final ScanCallback mScanCallback; 59 private final PacsControllerSessionCallback mControllerSessionCallback; 60 private final TransportClientProvider.TransportClientCallback mClientCallback; 61 PacsControllerSession( SessionHandle sessionHandle, AttributionSource attributionSource, Context context, UwbInjector uwbInjector, ServiceProfileInfo serviceProfileInfo, IUwbRangingCallbacks rangingCallbacks, Handler handler, String chipId)62 public PacsControllerSession( 63 SessionHandle sessionHandle, 64 AttributionSource attributionSource, 65 Context context, 66 UwbInjector uwbInjector, 67 ServiceProfileInfo serviceProfileInfo, 68 IUwbRangingCallbacks rangingCallbacks, 69 Handler handler, 70 String chipId) { 71 super( 72 sessionHandle, 73 attributionSource, 74 context, 75 uwbInjector, 76 serviceProfileInfo, 77 rangingCallbacks, 78 handler, 79 chipId); 80 mScanCallback = new ScanCallback(this); 81 mControllerSessionCallback = new PacsControllerSessionCallback(this); 82 mClientCallback = null; 83 } 84 85 @Override getIdleState()86 public State getIdleState() { 87 return new IdleState(); 88 } 89 90 @Override getDiscoveryState()91 public State getDiscoveryState() { 92 return new DiscoveryState(); 93 } 94 95 @Override getTransportState()96 public State getTransportState() { 97 return new TransportState(); 98 } 99 100 @Override getSecureState()101 public State getSecureState() { 102 return new SecureSessionState(); 103 } 104 105 @Override getRangingState()106 public State getRangingState() { 107 return new RangingState(); 108 } 109 110 @Override getEndingState()111 public State getEndingState() { 112 return new EndSessionState(); 113 } 114 115 private DiscoveryProvider mDiscoveryProvider; 116 private DiscoveryInfo mDiscoveryInfo; 117 private TransportClientProvider mTransportClientProvider; 118 private SecureSession mSecureSession; 119 120 private List<ScanFilter> mScanFilterList; 121 private ScanSettings mScanSettings; 122 setScanFilterList(List<ScanFilter> scanFilterList)123 public void setScanFilterList(List<ScanFilter> scanFilterList) { 124 mScanFilterList = scanFilterList; 125 } 126 setScanSettings(ScanSettings scanSettings)127 public void setScanSettings(ScanSettings scanSettings) { 128 mScanSettings = scanSettings; 129 } 130 setTransportclientInfo(TransportClientInfo transportClientInfo)131 public void setTransportclientInfo(TransportClientInfo transportClientInfo) { 132 mDiscoveryInfo.transportClientInfo = Optional.of(transportClientInfo); 133 } 134 135 /** Scan for devices */ startScan()136 public void startScan() { 137 ScanInfo scanInfo = new ScanInfo(mScanFilterList, mScanSettings); 138 mDiscoveryInfo = 139 new DiscoveryInfo( 140 DiscoveryInfo.TransportType.BLE, 141 Optional.of(scanInfo), 142 Optional.empty(), 143 Optional.empty()); 144 145 mDiscoveryProvider = 146 DiscoveryProviderFactory.createScanner( 147 mSessionInfo.mAttributionSource, 148 mSessionInfo.mContext, 149 new HandlerExecutor(mHandler), 150 mDiscoveryInfo, 151 mScanCallback); 152 mDiscoveryProvider.start(); 153 } 154 155 /** Stop scanning on ranging stopped or closed */ stopScan()156 public void stopScan() { 157 if (mDiscoveryProvider != null) { 158 mDiscoveryProvider.stop(); 159 } 160 } 161 162 /** Initialize transport client with updated TransportClientInfo */ transportClientInit()163 public void transportClientInit() { 164 mTransportClientProvider = 165 TransportProviderFactory.createClient( 166 mSessionInfo.mAttributionSource, 167 mSessionInfo.mContext, 168 new HandlerExecutor(mHandler), 169 /*secid placeholder*/ 2, 170 mDiscoveryInfo, 171 mClientCallback); 172 173 FiraConnectorCapabilities firaConnectorCapabilities = 174 new FiraConnectorCapabilities.Builder().build(); 175 mTransportClientProvider.setCapabilites(firaConnectorCapabilities); 176 sendMessage(TRANSPORT_STARTED); 177 } 178 179 /** Start Transport client */ transportClientStart()180 public void transportClientStart() { 181 mTransportClientProvider.start(); 182 } 183 184 /** Stop Transport client */ transportClientStop()185 public void transportClientStop() { 186 mTransportClientProvider.stop(); 187 } 188 189 /** Initialize controller initiator session */ secureSessionInit()190 public void secureSessionInit() { 191 mSecureSession = 192 SecureFactory.makeInitiatorSecureSession( 193 mSessionInfo.mContext, 194 mHandler.getLooper(), 195 mControllerSessionCallback, 196 getRunningProfileSessionInfo(), 197 mTransportClientProvider, 198 /* isController= */ true); 199 } 200 201 @Override getUwbConfig()202 public UwbConfig getUwbConfig() { 203 return PacsProfile.getPacsControllerProfile(); 204 } 205 206 /** Implements callback of DiscoveryScanProvider */ 207 public static class ScanCallback implements DiscoveryScanProvider.DiscoveryScanCallback { 208 209 public final PacsControllerSession mPacsControllerSession; 210 ScanCallback(PacsControllerSession pacsControllerSession)211 public ScanCallback(PacsControllerSession pacsControllerSession) { 212 mPacsControllerSession = pacsControllerSession; 213 } 214 215 @Override onDiscovered(DiscoveryScanProvider.DiscoveryResult result)216 public void onDiscovered(DiscoveryScanProvider.DiscoveryResult result) { 217 TransportClientInfo transportClientInfo = new TransportClientInfo(result.scanResult); 218 mPacsControllerSession.setTransportclientInfo(transportClientInfo); 219 mPacsControllerSession.sendMessage(TRANSPORT_INIT); 220 } 221 222 @Override onDiscoveryFailed(int errorCode)223 public void onDiscoveryFailed(int errorCode) { 224 Log.e(TAG, "Discovery failed with error code: " + errorCode); 225 mPacsControllerSession.sendMessage(DISCOVERY_FAILED); 226 } 227 } 228 229 /** Pacs profile controller implementation of RunningProfileSessionInfo. */ getRunningProfileSessionInfo()230 private RunningProfileSessionInfo getRunningProfileSessionInfo() { 231 GenericSpecificationParams genericSpecificationParams = getSpecificationInfo(); 232 if (genericSpecificationParams == null 233 || genericSpecificationParams.getFiraSpecificationParams() == null) { 234 throw new IllegalStateException("UwbCapability is not available."); 235 } 236 FiraSpecificationParams firaSpecificationParams = 237 genericSpecificationParams.getFiraSpecificationParams(); 238 UwbCapability uwbCapability = 239 UwbCapability.fromFiRaSpecificationParam(firaSpecificationParams); 240 241 return new RunningProfileSessionInfo.Builder(uwbCapability, 242 mSessionInfo.mServiceProfileInfo.getServiceAdfOid().get()) 243 .setSharedPrimarySessionIdAndSessionKeyInfo( 244 mSessionInfo.getSessionId(), mSessionInfo.getSharedSessionKeyInfo()) 245 .build(); 246 } 247 248 /** Pacs profile controller implementation of SecureSession.Callback. */ 249 public static class PacsControllerSessionCallback implements SecureSession.Callback { 250 251 public final PacsControllerSession mPacsControllerSession; 252 PacsControllerSessionCallback(PacsControllerSession pacsControllerSession)253 public PacsControllerSessionCallback(PacsControllerSession pacsControllerSession) { 254 mPacsControllerSession = pacsControllerSession; 255 } 256 257 @Override onSessionDataReady( int updatedSessionId, Optional<SessionData> sessionData, boolean isSessionTerminated)258 public void onSessionDataReady( 259 int updatedSessionId, Optional<SessionData> sessionData, 260 boolean isSessionTerminated) { 261 mPacsControllerSession.sendMessage(RANGING_INIT); 262 } 263 264 @Override onSessionAborted()265 public void onSessionAborted() {} 266 267 @Override onSessionTerminated()268 public void onSessionTerminated() {} 269 } 270 271 public class IdleState extends State { 272 @Override enter()273 public void enter() { 274 if (mVerboseLoggingEnabled) { 275 log("Enter IdleState"); 276 } 277 transitionTo(mDiscoveryState); 278 } 279 280 @Override exit()281 public void exit() { 282 if (mVerboseLoggingEnabled) { 283 log("Exit IdleState"); 284 } 285 } 286 287 @Override processMessage(Message message)288 public boolean processMessage(Message message) { 289 switch (message.what) { 290 case SESSION_INITIALIZED: 291 if (mVerboseLoggingEnabled) { 292 log("Pacs controller session initialized"); 293 } 294 break; 295 case SESSION_START: 296 if (mVerboseLoggingEnabled) { 297 log("Starting OOB Discovery"); 298 } 299 transitionTo(mDiscoveryState); 300 break; 301 default: 302 if (mVerboseLoggingEnabled) { 303 log(message.toString() + " not handled in IdleState"); 304 } 305 } 306 return true; 307 } 308 } 309 310 public class DiscoveryState extends State { 311 @Override enter()312 public void enter() { 313 if (mVerboseLoggingEnabled) { 314 log("Enter DiscoveryState"); 315 } 316 startScan(); 317 sendMessage(DISCOVERY_STARTED); 318 } 319 320 @Override exit()321 public void exit() { 322 if (mVerboseLoggingEnabled) { 323 log("Exit DiscoveryState"); 324 } 325 } 326 327 @Override processMessage(Message message)328 public boolean processMessage(Message message) { 329 switch (message.what) { 330 case DISCOVERY_FAILED: 331 if (mVerboseLoggingEnabled) { 332 log("Scanning failed "); 333 } 334 break; 335 case SESSION_START: 336 startScan(); 337 if (mVerboseLoggingEnabled) { 338 log("Started scanning"); 339 } 340 break; 341 case SESSION_STOP: 342 stopScan(); 343 if (mVerboseLoggingEnabled) { 344 log("Stopped scanning"); 345 } 346 break; 347 case TRANSPORT_INIT: 348 transitionTo(mTransportState); 349 break; 350 } 351 return true; 352 } 353 } 354 355 public class TransportState extends State { 356 @Override enter()357 public void enter() { 358 if (mVerboseLoggingEnabled) { 359 log("Enter TransportState"); 360 } 361 transportClientInit(); 362 } 363 364 @Override exit()365 public void exit() { 366 if (mVerboseLoggingEnabled) { 367 log("Exit TransportState"); 368 } 369 } 370 371 @Override processMessage(Message message)372 public boolean processMessage(Message message) { 373 switch (message.what) { 374 case TRANSPORT_STARTED: 375 transportClientStart(); 376 break; 377 case SESSION_STOP: 378 case TRANSPORT_COMPLETED: 379 stopScan(); 380 transportClientStop(); 381 transitionTo(mSecureSessionState); 382 break; 383 } 384 return true; 385 } 386 } 387 388 public class SecureSessionState extends State { 389 390 @Override enter()391 public void enter() { 392 if (mVerboseLoggingEnabled) { 393 log("Enter SecureSessionState"); 394 } 395 sendMessage(SECURE_SESSION_INIT); 396 } 397 398 @Override exit()399 public void exit() { 400 if (mVerboseLoggingEnabled) { 401 log("Exit SecureSessionState"); 402 } 403 } 404 405 @Override processMessage(Message message)406 public boolean processMessage(Message message) { 407 switch (message.what) { 408 case SECURE_SESSION_INIT: 409 secureSessionInit(); 410 break; 411 case SECURE_SESSION_ESTABLISHED: 412 transitionTo(mRangingState); 413 break; 414 } 415 return true; 416 } 417 } 418 419 public class RangingState extends State { 420 @Override enter()421 public void enter() { 422 if (mVerboseLoggingEnabled) { 423 log("Enter RangingState"); 424 } 425 } 426 427 @Override exit()428 public void exit() { 429 if (mVerboseLoggingEnabled) { 430 log("Exit RangingState"); 431 } 432 } 433 434 /** 435 * TODO Once ranging starts with a client, controller should continue to scan for other 436 * devices as this is a multicast session. Transition to discovery state after session is 437 * started and add new devices discovered. 438 */ 439 @Override processMessage(Message message)440 public boolean processMessage(Message message) { 441 switch (message.what) { 442 case RANGING_INIT: 443 try { 444 Log.i(TAG, "Starting ranging session"); 445 openRangingSession(); 446 } catch (RemoteException e) { 447 Log.e(TAG, "Ranging session start failed"); 448 e.printStackTrace(); 449 } 450 break; 451 452 case SESSION_START: 453 case RANGING_OPENED: 454 startScan(); 455 startRanging(); 456 break; 457 458 case SESSION_STOP: 459 stopRanging(); 460 stopScan(); 461 if (mVerboseLoggingEnabled) { 462 log("Stopped ranging session"); 463 } 464 break; 465 466 case RANGING_ENDED: 467 closeRanging(); 468 transitionTo(mEndSessionState); 469 break; 470 } 471 return true; 472 } 473 } 474 475 public class EndSessionState extends State { 476 477 @Override enter()478 public void enter() { 479 if (mVerboseLoggingEnabled) { 480 log("Enter EndSessionState"); 481 } 482 stopScan(); 483 } 484 485 @Override exit()486 public void exit() { 487 if (mVerboseLoggingEnabled) { 488 log("Exit EndSessionState"); 489 } 490 } 491 492 @Override processMessage(Message message)493 public boolean processMessage(Message message) { 494 return true; 495 } 496 } 497 } 498