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