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