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 static com.android.server.uwb.data.UwbConfig.CONTROLEE_AND_RESPONDER; 20 import static com.android.server.uwb.data.UwbConfig.OOB_TYPE_BLE; 21 import static com.android.server.uwb.data.UwbConfig.PERIPHERAL; 22 23 import static com.google.uwb.support.fira.FiraParams.MULTI_NODE_MODE_ONE_TO_MANY; 24 25 import android.bluetooth.le.AdvertisingSetParameters; 26 import android.content.AttributionSource; 27 import android.content.Context; 28 import android.os.Handler; 29 import android.os.Message; 30 import android.os.RemoteException; 31 import android.util.Log; 32 import android.uwb.IUwbRangingCallbacks; 33 import android.uwb.SessionHandle; 34 35 import com.android.internal.util.State; 36 import com.android.modules.utils.HandlerExecutor; 37 import com.android.server.uwb.UwbInjector; 38 import com.android.server.uwb.data.ServiceProfileData.ServiceProfileInfo; 39 import com.android.server.uwb.data.UwbConfig; 40 import com.android.server.uwb.discovery.DiscoveryAdvertiseProvider; 41 import com.android.server.uwb.discovery.DiscoveryProvider; 42 import com.android.server.uwb.discovery.DiscoveryProviderFactory; 43 import com.android.server.uwb.discovery.TransportProviderFactory; 44 import com.android.server.uwb.discovery.TransportServerProvider; 45 import com.android.server.uwb.discovery.ble.DiscoveryAdvertisement; 46 import com.android.server.uwb.discovery.info.AdvertiseInfo; 47 import com.android.server.uwb.discovery.info.DiscoveryInfo; 48 import com.android.server.uwb.secure.SecureFactory; 49 import com.android.server.uwb.secure.SecureSession; 50 import com.android.server.uwb.secure.csml.ControleeInfo; 51 import com.android.server.uwb.secure.csml.SessionData; 52 import com.android.server.uwb.secure.csml.UwbCapability; 53 54 import com.google.uwb.support.fira.FiraSpecificationParams; 55 import com.google.uwb.support.generic.GenericSpecificationParams; 56 57 import java.util.Optional; 58 59 /** Session for PACS profile controlee */ 60 public class PacsControleeSession extends RangingSessionController { 61 private static final String TAG = "PacsControleeSession"; 62 private final PacsAdvertiseCallback mAdvertiseCallback; 63 private final PacsControleeSessionCallback mControleeSessionCallback; 64 private final TransportServerProvider.TransportServerCallback mServerCallback; 65 PacsControleeSession( SessionHandle sessionHandle, AttributionSource attributionSource, Context context, UwbInjector uwbInjector, ServiceProfileInfo serviceProfileInfo, IUwbRangingCallbacks rangingCallbacks, Handler handler, String chipId)66 public PacsControleeSession( 67 SessionHandle sessionHandle, 68 AttributionSource attributionSource, 69 Context context, 70 UwbInjector uwbInjector, 71 ServiceProfileInfo serviceProfileInfo, 72 IUwbRangingCallbacks rangingCallbacks, 73 Handler handler, 74 String chipId) { 75 super( 76 sessionHandle, 77 attributionSource, 78 context, 79 uwbInjector, 80 serviceProfileInfo, 81 rangingCallbacks, 82 handler, 83 chipId); 84 mAdvertiseCallback = new PacsAdvertiseCallback(this); 85 mControleeSessionCallback = new PacsControleeSessionCallback(this); 86 mServerCallback = null; 87 } 88 89 @Override getIdleState()90 public State getIdleState() { 91 return new IdleState(); 92 } 93 94 @Override getDiscoveryState()95 public State getDiscoveryState() { 96 return new DiscoveryState(); 97 } 98 99 @Override getTransportState()100 public State getTransportState() { 101 return new TransportState(); 102 } 103 104 @Override getSecureState()105 public State getSecureState() { 106 return new SecureSessionState(); 107 } 108 109 @Override getRangingState()110 public State getRangingState() { 111 return new RangingState(); 112 } 113 114 @Override getEndingState()115 public State getEndingState() { 116 return new EndSessionState(); 117 } 118 119 private DiscoveryProvider mDiscoveryProvider; 120 private DiscoveryInfo mDiscoveryInfo; 121 private TransportServerProvider mTransportServerProvider; 122 private SecureSession mSecureSession; 123 private DiscoveryAdvertisement mDiscoveryAdvertisement; 124 private AdvertisingSetParameters mAdvertisingSetParameters; 125 setDiscoveryAdvertisement(DiscoveryAdvertisement discoveryAdvertisement)126 public void setDiscoveryAdvertisement(DiscoveryAdvertisement discoveryAdvertisement) { 127 mDiscoveryAdvertisement = discoveryAdvertisement; 128 } 129 setAdvertisingSetParameters(AdvertisingSetParameters advertisingSetParameters)130 public void setAdvertisingSetParameters(AdvertisingSetParameters advertisingSetParameters) { 131 mAdvertisingSetParameters = advertisingSetParameters; 132 } 133 134 /** Advertise capabilities */ startAdvertising()135 public void startAdvertising() { 136 AdvertiseInfo advertiseInfo = 137 new AdvertiseInfo(mAdvertisingSetParameters, mDiscoveryAdvertisement); 138 139 mDiscoveryInfo = 140 new DiscoveryInfo( 141 DiscoveryInfo.TransportType.BLE, 142 Optional.empty(), 143 Optional.of(advertiseInfo), 144 Optional.empty()); 145 146 mDiscoveryProvider = 147 DiscoveryProviderFactory.createAdvertiser( 148 mSessionInfo.mAttributionSource, 149 mSessionInfo.mContext, 150 new HandlerExecutor(mHandler), 151 mDiscoveryInfo, 152 mAdvertiseCallback); 153 mDiscoveryProvider.start(); 154 // sendMessage(TRANSPORT_INIT); 155 } 156 157 /** Stop advertising on ranging stopped or closed */ stopAdvertising()158 public void stopAdvertising() { 159 if (mDiscoveryProvider != null) { 160 mDiscoveryProvider.stop(); 161 } 162 } 163 164 /** Initialize Transport server */ transportServerInit()165 public void transportServerInit() { 166 mTransportServerProvider = 167 TransportProviderFactory.createServer( 168 mSessionInfo.mAttributionSource, 169 mSessionInfo.mContext, 170 // TODO: Transport server supports auto assigning secid. 171 /*secid placeholder*/ 2, 172 mDiscoveryInfo, 173 mServerCallback); 174 sendMessage(TRANSPORT_STARTED); 175 } 176 177 /** Start Transport server */ transportServerStart()178 public void transportServerStart() { 179 mTransportServerProvider.start(); 180 } 181 182 /** Stop Transport server */ transportServerStop()183 public void transportServerStop() { 184 mTransportServerProvider.stop(); 185 } 186 187 /** Initialize controlee responder session */ secureSessionInit()188 private void secureSessionInit() { 189 try { 190 mSecureSession = 191 SecureFactory.makeResponderSecureSession( 192 mSessionInfo.mContext, 193 mHandler.getLooper(), 194 mControleeSessionCallback, 195 getRunningProfileSessionInfo(), 196 mTransportServerProvider, 197 /* isController= */ false); 198 mSecureSession.startSession(); 199 } catch (IllegalStateException e) { 200 Log.e(TAG, "secure session init failed as " + e); 201 stopSession(); 202 } 203 } 204 205 @Override getUwbConfig()206 public UwbConfig getUwbConfig() { 207 // PACS controlee config 208 UwbConfig.Builder builder = new UwbConfig.Builder() 209 .setUwbRole(CONTROLEE_AND_RESPONDER) 210 .setMultiNodeMode(MULTI_NODE_MODE_ONE_TO_MANY) 211 .setTofReport(true) 212 .setOobType(OOB_TYPE_BLE) 213 .setOobBleRole(PERIPHERAL); 214 // Config received in sessionData 215 if (mSessionInfo.mSessionData != null) { 216 mSessionInfo.setSessionId(mSessionInfo.mSessionData.mSessionId); 217 mSessionInfo.mSessionData.mSubSessionId.ifPresent( 218 integer -> mSessionInfo.setSubSessionId(integer)); 219 return UwbConfig.fromSessionData(builder, mSessionInfo.mSessionData); 220 } 221 return builder.build(); 222 } 223 224 /** Implements callback of DiscoveryAdvertiseProvider */ 225 public static class PacsAdvertiseCallback 226 implements DiscoveryAdvertiseProvider.DiscoveryAdvertiseCallback { 227 228 public final PacsControleeSession mPacsControleeSession; 229 PacsAdvertiseCallback(PacsControleeSession pacsControleeSession)230 public PacsAdvertiseCallback(PacsControleeSession pacsControleeSession) { 231 mPacsControleeSession = pacsControleeSession; 232 } 233 234 @Override onDiscoveryFailed(int errorCode)235 public void onDiscoveryFailed(int errorCode) { 236 Log.e(TAG, "Advertising failed with error code: " + errorCode); 237 mPacsControleeSession.sendMessage(DISCOVERY_FAILED); 238 } 239 } 240 getRunningProfileSessionInfo()241 private RunningProfileSessionInfo getRunningProfileSessionInfo() { 242 243 GenericSpecificationParams genericSpecificationParams = getSpecificationInfo(); 244 if (genericSpecificationParams == null 245 || genericSpecificationParams.getFiraSpecificationParams() == null) { 246 throw new IllegalStateException("UwbCapability is not available."); 247 } 248 FiraSpecificationParams firaSpecificationParams = 249 genericSpecificationParams.getFiraSpecificationParams(); 250 UwbCapability uwbCapability = 251 UwbCapability.fromFiRaSpecificationParam(firaSpecificationParams); 252 ControleeInfo controleeInfo = 253 new ControleeInfo.Builder().setUwbCapability(uwbCapability).build(); 254 255 return new RunningProfileSessionInfo.Builder(uwbCapability, 256 mSessionInfo.mServiceProfileInfo.getServiceAdfOid().get()) 257 .setControleeInfo(controleeInfo) 258 .build(); 259 } 260 261 /** Pacs profile controlee implementation of SecureSession.Callback. */ 262 public static class PacsControleeSessionCallback implements SecureSession.Callback { 263 public final PacsControleeSession mPacsControleeSession; 264 PacsControleeSessionCallback(PacsControleeSession pacsControleeSession)265 public PacsControleeSessionCallback(PacsControleeSession pacsControleeSession) { 266 mPacsControleeSession = pacsControleeSession; 267 } 268 269 @Override onSessionDataReady( int updatedSessionId, Optional<SessionData> sessionData, boolean isSessionTerminated)270 public void onSessionDataReady( 271 int updatedSessionId, Optional<SessionData> sessionData, 272 boolean isSessionTerminated) { 273 mPacsControleeSession.sendMessage( 274 RANGING_INIT, updatedSessionId, 0, sessionData.get()); 275 } 276 277 @Override onSessionAborted()278 public void onSessionAborted() { 279 Log.w(TAG, "Secure Session aborted"); 280 mPacsControleeSession.stopSession(); 281 } 282 283 @Override onSessionTerminated()284 public void onSessionTerminated() { 285 Log.w(TAG, "Secure Session terminated"); 286 } 287 } 288 289 public class IdleState extends State { 290 @Override enter()291 public void enter() { 292 if (mVerboseLoggingEnabled) { 293 log("Enter IdleState"); 294 } 295 getSpecificationInfo(); 296 } 297 298 @Override exit()299 public void exit() { 300 if (mVerboseLoggingEnabled) { 301 log("Exit IdleState"); 302 } 303 } 304 305 @Override processMessage(Message message)306 public boolean processMessage(Message message) { 307 if (mVerboseLoggingEnabled) { 308 log("No message handled in IdleState"); 309 } 310 switch (message.what) { 311 case SESSION_INITIALIZED: 312 if (mVerboseLoggingEnabled) { 313 log("Pacs controlee session initialized"); 314 } 315 break; 316 case SESSION_START: 317 if (mVerboseLoggingEnabled) { 318 log("Starting OOB Discovery"); 319 } 320 transitionTo(mDiscoveryState); 321 break; 322 default: 323 if (mVerboseLoggingEnabled) { 324 log(message.toString() + " not handled in IdleState"); 325 } 326 } 327 return true; 328 } 329 } 330 331 public class DiscoveryState extends State { 332 333 @Override enter()334 public void enter() { 335 if (mVerboseLoggingEnabled) { 336 log("Enter DiscoveryState"); 337 } 338 sendMessage(DISCOVERY_STARTED); 339 } 340 341 @Override exit()342 public void exit() { 343 if (mVerboseLoggingEnabled) { 344 log("Exit DiscoveryState"); 345 } 346 } 347 348 @Override processMessage(Message message)349 public boolean processMessage(Message message) { 350 switch (message.what) { 351 case DISCOVERY_FAILED: 352 log("Failed to advertise"); 353 break; 354 case DISCOVERY_STARTED: 355 case SESSION_START: 356 startAdvertising(); 357 if (mVerboseLoggingEnabled) { 358 log("Started advertising"); 359 } 360 break; 361 case SESSION_STOP: 362 stopAdvertising(); 363 if (mVerboseLoggingEnabled) { 364 log("Stopped advertising"); 365 } 366 transitionTo(mEndSessionState); 367 break; 368 case TRANSPORT_INIT: 369 transitionTo(mTransportState); 370 break; 371 } 372 return true; 373 } 374 } 375 376 public class TransportState extends State { 377 @Override enter()378 public void enter() { 379 if (mVerboseLoggingEnabled) { 380 log("Enter TransportState"); 381 } 382 transportServerInit(); 383 } 384 385 @Override exit()386 public void exit() { 387 if (mVerboseLoggingEnabled) { 388 log("Exit TransportState"); 389 } 390 } 391 392 @Override processMessage(Message message)393 public boolean processMessage(Message message) { 394 switch (message.what) { 395 case TRANSPORT_STARTED: 396 transportServerStart(); 397 break; 398 case SESSION_STOP: 399 transportServerStop(); 400 return false; 401 case TRANSPORT_COMPLETED: 402 stopAdvertising(); 403 transportServerStop(); 404 transitionTo(mSecureSessionState); 405 break; 406 } 407 return true; 408 } 409 } 410 411 public class SecureSessionState extends State { 412 413 @Override enter()414 public void enter() { 415 if (mVerboseLoggingEnabled) { 416 log("Enter SecureSessionState"); 417 } 418 sendMessage(SECURE_SESSION_INIT); 419 } 420 421 @Override exit()422 public void exit() { 423 if (mVerboseLoggingEnabled) { 424 log("Exit SecureSessionState"); 425 } 426 } 427 428 @Override processMessage(Message message)429 public boolean processMessage(Message message) { 430 switch (message.what) { 431 case SECURE_SESSION_INIT: 432 secureSessionInit(); 433 break; 434 case SECURE_SESSION_ESTABLISHED: 435 transitionTo(mRangingState); 436 break; 437 case SESSION_STOP: 438 mSecureSession.terminateSession(); 439 // continue handle 440 return false; 441 } 442 return true; 443 } 444 } 445 446 public class RangingState extends State { 447 @Override enter()448 public void enter() { 449 if (mVerboseLoggingEnabled) { 450 log("Enter RangingState"); 451 } 452 } 453 454 @Override exit()455 public void exit() { 456 if (mVerboseLoggingEnabled) { 457 log("Exit RangingState"); 458 } 459 } 460 461 @Override processMessage(Message message)462 public boolean processMessage(Message message) { 463 switch (message.what) { 464 case RANGING_INIT: 465 // get sessionId from session data ? 466 if (message.obj == null) { 467 Log.e(TAG, "Session data is not available."); 468 stopSession(); 469 break; 470 } 471 mSessionInfo.setSessionId(message.arg1); 472 mSessionInfo.mSessionData = (SessionData) message.obj; 473 474 try { 475 Log.i(TAG, "Starting ranging session"); 476 openRangingSession(); 477 } catch (RemoteException e) { 478 Log.e(TAG, "Ranging session start failed"); 479 stopSession(); 480 e.printStackTrace(); 481 } 482 stopAdvertising(); 483 break; 484 case SESSION_START: 485 case RANGING_OPENED: 486 startRanging(); 487 if (mVerboseLoggingEnabled) { 488 log("Started ranging"); 489 } 490 break; 491 492 case SESSION_STOP: 493 stopRanging(); 494 if (mVerboseLoggingEnabled) { 495 log("Stopped ranging session"); 496 } 497 break; 498 499 case RANGING_ENDED: 500 closeRanging(); 501 transitionTo(mEndSessionState); 502 break; 503 } 504 return true; 505 } 506 } 507 508 public class EndSessionState extends State { 509 510 @Override enter()511 public void enter() { 512 if (mVerboseLoggingEnabled) { 513 log("Enter EndSessionState"); 514 } 515 stopAdvertising(); 516 if (mSecureSession != null) { 517 mSecureSession.terminateSession(); 518 } 519 } 520 521 @Override exit()522 public void exit() { 523 if (mVerboseLoggingEnabled) { 524 log("Exit EndSessionState"); 525 } 526 } 527 528 @Override processMessage(Message message)529 public boolean processMessage(Message message) { 530 return true; 531 } 532 } 533 } 534