1 /*
2  * Copyright (C) 2019 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 android.car.trust;
18 
19 import static android.car.Car.PERMISSION_CAR_ENROLL_TRUST;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.RequiresPermission;
25 import android.annotation.SystemApi;
26 import android.bluetooth.BluetoothDevice;
27 import android.car.Car;
28 import android.car.CarManagerBase;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.Looper;
33 import android.os.Message;
34 import android.os.RemoteException;
35 import android.util.Log;
36 
37 import com.android.internal.annotations.GuardedBy;
38 
39 import java.lang.annotation.Retention;
40 import java.lang.annotation.RetentionPolicy;
41 import java.lang.ref.WeakReference;
42 import java.util.Collections;
43 import java.util.List;
44 
45 
46 /**
47  * APIs to help enroll a remote device as a trusted device that can be used to authenticate a user
48  * in the head unit.
49  * <p>
50  * The call sequence to add a new trusted device from the client should be as follows:
51  * <ol>
52  * <li> setEnrollmentCallback()
53  * <li> setBleCallback(bleCallback)
54  * <li> startEnrollmentAdvertising()
55  * <li> wait for onEnrollmentAdvertisingStarted() or
56  * <li> wait for onBleEnrollmentDeviceConnected() and check if the device connected is the right
57  * one.
58  * <li> initiateEnrollmentHandshake()
59  * <li> wait for onAuthStringAvailable() to get the pairing code to display to the user
60  * <li> enrollmentHandshakeAccepted() after user confirms the pairing code
61  * <li> wait for onEscrowTokenAdded()
62  * <li> Authenticate user's credentials by showing the lock screen
63  * <li> activateToken()
64  * <li> wait for onEscrowTokenActiveStateChanged() to add the device as a trusted device and show
65  * in the list
66  * </ol>
67  *
68  * @hide
69  *
70  * @deprecated Enrollment of a trusted device is no longer a supported feature and these APIs will
71  * be removed in the next Android release.
72  */
73 @Deprecated
74 @SystemApi
75 public final class CarTrustAgentEnrollmentManager extends CarManagerBase {
76     private static final String TAG = "CarTrustEnrollMgr";
77     private static final String KEY_HANDLE = "handle";
78     private static final String KEY_ACTIVE = "active";
79     private static final int MSG_ENROLL_ADVERTISING_STARTED = 0;
80     private static final int MSG_ENROLL_ADVERTISING_FAILED = 1;
81     private static final int MSG_ENROLL_DEVICE_CONNECTED = 2;
82     private static final int MSG_ENROLL_DEVICE_DISCONNECTED = 3;
83     private static final int MSG_ENROLL_HANDSHAKE_FAILURE = 4;
84     private static final int MSG_ENROLL_AUTH_STRING_AVAILABLE = 5;
85     private static final int MSG_ENROLL_TOKEN_ADDED = 6;
86     private static final int MSG_ENROLL_TOKEN_STATE_CHANGED = 7;
87     private static final int MSG_ENROLL_TOKEN_REMOVED = 8;
88 
89     private final ICarTrustAgentEnrollment mEnrollmentService;
90     private Object mListenerLock = new Object();
91     @GuardedBy("mListenerLock")
92     private CarTrustAgentEnrollmentCallback mEnrollmentCallback;
93     @GuardedBy("mListenerLock")
94     private CarTrustAgentBleCallback mBleCallback;
95     @GuardedBy("mListenerLock")
96     private final ListenerToEnrollmentService mListenerToEnrollmentService =
97             new ListenerToEnrollmentService(this);
98     private final ListenerToBleService mListenerToBleService = new ListenerToBleService(this);
99     private final EventCallbackHandler mEventCallbackHandler;
100 
101     /**
102      * Enrollment Handshake failed.
103      */
104     public static final int ENROLLMENT_HANDSHAKE_FAILURE = 1;
105     /**
106      * Enrollment of a new device is not allowed.  This happens when either the whole feature is
107      * disabled or just the enrollment is disabled.  Useful when feature needs to be disabled
108      * in a lost/stolen phone scenario.
109      */
110     public static final int ENROLLMENT_NOT_ALLOWED = 2;
111 
112     /** @hide */
113     @Retention(RetentionPolicy.SOURCE)
114     @IntDef({ENROLLMENT_HANDSHAKE_FAILURE,
115             ENROLLMENT_NOT_ALLOWED})
116     public @interface TrustedDeviceEnrollmentError {
117     }
118 
119 
120     /** @hide */
CarTrustAgentEnrollmentManager(Car car, IBinder service)121     public CarTrustAgentEnrollmentManager(Car car, IBinder service) {
122         super(car);
123         mEnrollmentService = ICarTrustAgentEnrollment.Stub.asInterface(service);
124         mEventCallbackHandler = new EventCallbackHandler(this, getEventHandler().getLooper());
125     }
126 
127     /** @hide */
128     @Override
onCarDisconnected()129     public synchronized void onCarDisconnected() {
130     }
131 
132     /**
133      * Starts broadcasting enrollment UUID on BLE.
134      * Phones can scan and connect for the enrollment process to begin.
135      */
136     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
startEnrollmentAdvertising()137     public void startEnrollmentAdvertising() {
138         try {
139             mEnrollmentService.startEnrollmentAdvertising();
140         } catch (RemoteException e) {
141             handleRemoteExceptionFromCarService(e);
142         }
143     }
144 
145     /**
146      * Stops Enrollment advertising.
147      */
148     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
stopEnrollmentAdvertising()149     public void stopEnrollmentAdvertising() {
150         try {
151             mEnrollmentService.stopEnrollmentAdvertising();
152         } catch (RemoteException e) {
153             handleRemoteExceptionFromCarService(e);
154         }
155     }
156 
157     /**
158      * Confirms that the enrollment handshake has been accepted by the user. This should be called
159      * after the user has confirmed the verification code displayed on the UI.
160      *
161      * @param device the remote Bluetooth device that will receive the signal.
162      */
163     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
enrollmentHandshakeAccepted(BluetoothDevice device)164     public void enrollmentHandshakeAccepted(BluetoothDevice device) {
165         try {
166             mEnrollmentService.enrollmentHandshakeAccepted(device);
167         } catch (RemoteException e) {
168             handleRemoteExceptionFromCarService(e);
169         }
170     }
171 
172     /**
173      * Provides an option to quit enrollment if the pairing code doesn't match for example.
174      */
175     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
terminateEnrollmentHandshake()176     public void terminateEnrollmentHandshake() {
177         try {
178             mEnrollmentService.terminateEnrollmentHandshake();
179         } catch (RemoteException e) {
180             handleRemoteExceptionFromCarService(e);
181         }
182     }
183 
184     /**
185      * Returns {@code true} if the escrow token associated with the given handle is active.
186      * <p>
187      * When a new escrow token has been added as part of the Trusted device enrollment, the client
188      * will receive {@link CarTrustAgentEnrollmentCallback#onEscrowTokenAdded(long)} and
189      * {@link CarTrustAgentEnrollmentCallback#onEscrowTokenActiveStateChanged(long, boolean)}
190      * callbacks.  This method provides a way to query for the token state at a later point of time.
191      *
192      * @param handle the handle corresponding to the escrow token
193      * @param uid    user id associated with the token
194      * @return true if the token is active, false if not
195      */
196     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
isEscrowTokenActive(long handle, int uid)197     public boolean isEscrowTokenActive(long handle, int uid) {
198         try {
199             return mEnrollmentService.isEscrowTokenActive(handle, uid);
200         } catch (RemoteException e) {
201             return handleRemoteExceptionFromCarService(e, false);
202         }
203     }
204 
205     /**
206      * Remove the escrow token that is associated with the given handle and uid.
207      *
208      * @param handle the handle associated with the escrow token
209      * @param uid    user id associated with the token
210      */
211     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
removeEscrowToken(long handle, int uid)212     public void removeEscrowToken(long handle, int uid) {
213         try {
214             mEnrollmentService.removeEscrowToken(handle, uid);
215         } catch (RemoteException e) {
216             handleRemoteExceptionFromCarService(e);
217         }
218     }
219 
220     /**
221      * Remove all of the trusted devices associated with the given user.
222      *
223      * @param uid User id to remove the devices for
224      */
225     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
removeAllTrustedDevices(int uid)226     public void removeAllTrustedDevices(int uid) {
227         try {
228             mEnrollmentService.removeAllTrustedDevices(uid);
229         } catch (RemoteException e) {
230             handleRemoteExceptionFromCarService(e);
231         }
232     }
233 
234     /**
235      * Enable or Disable Trusted device enrollment.  Once disabled, head unit will not broadcast
236      * for enrollment until enabled back.
237      *
238      * @param isEnabled {@code true} enables enrollment.
239      */
240     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
setTrustedDeviceEnrollmentEnabled(boolean isEnabled)241     public void setTrustedDeviceEnrollmentEnabled(boolean isEnabled) {
242         try {
243             mEnrollmentService.setTrustedDeviceEnrollmentEnabled(isEnabled);
244         } catch (RemoteException e) {
245             handleRemoteExceptionFromCarService(e);
246         }
247     }
248 
249     /**
250      * Enable or disable Unlocking with a trusted device. Once disabled, head unit will not
251      * broadcast until enabled back.
252      *
253      * @param isEnabled {@code true} enables unlock.
254      */
255     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
setTrustedDeviceUnlockEnabled(boolean isEnabled)256     public void setTrustedDeviceUnlockEnabled(boolean isEnabled) {
257         try {
258             mEnrollmentService.setTrustedDeviceUnlockEnabled(isEnabled);
259         } catch (RemoteException e) {
260             handleRemoteExceptionFromCarService(e);
261         }
262     }
263 
264     /**
265      * Register for enrollment event callbacks.
266      *
267      * @param callback The callback methods to call, null to unregister
268      */
269     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
setEnrollmentCallback(@ullable CarTrustAgentEnrollmentCallback callback)270     public void setEnrollmentCallback(@Nullable CarTrustAgentEnrollmentCallback callback) {
271         if (callback == null) {
272             unregisterEnrollmentCallback();
273         } else {
274             registerEnrollmentCallback(callback);
275         }
276     }
277 
registerEnrollmentCallback(CarTrustAgentEnrollmentCallback callback)278     private void registerEnrollmentCallback(CarTrustAgentEnrollmentCallback callback) {
279         synchronized (mListenerLock) {
280             if (callback != null && mEnrollmentCallback == null) {
281                 try {
282                     mEnrollmentService.registerEnrollmentCallback(mListenerToEnrollmentService);
283                     mEnrollmentCallback = callback;
284                 } catch (RemoteException e) {
285                     handleRemoteExceptionFromCarService(e);
286                 }
287             }
288         }
289     }
290 
unregisterEnrollmentCallback()291     private void unregisterEnrollmentCallback() {
292         synchronized (mListenerLock) {
293             if (mEnrollmentCallback != null) {
294                 try {
295                     mEnrollmentService.unregisterEnrollmentCallback(mListenerToEnrollmentService);
296                 } catch (RemoteException e) {
297                     handleRemoteExceptionFromCarService(e);
298                 }
299                 mEnrollmentCallback = null;
300             }
301         }
302     }
303 
304     /**
305      * Register for general BLE callbacks
306      *
307      * @param callback The callback methods to call, null to unregister
308      */
309     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
setBleCallback(@ullable CarTrustAgentBleCallback callback)310     public void setBleCallback(@Nullable CarTrustAgentBleCallback callback) {
311         if (callback == null) {
312             unregisterBleCallback();
313         } else {
314             registerBleCallback(callback);
315         }
316     }
317 
registerBleCallback(CarTrustAgentBleCallback callback)318     private void registerBleCallback(CarTrustAgentBleCallback callback) {
319         synchronized (mListenerLock) {
320             if (callback != null && mBleCallback == null) {
321                 try {
322                     mEnrollmentService.registerBleCallback(mListenerToBleService);
323                     mBleCallback = callback;
324                 } catch (RemoteException e) {
325                     handleRemoteExceptionFromCarService(e);
326                 }
327             }
328         }
329     }
330 
unregisterBleCallback()331     private void unregisterBleCallback() {
332         synchronized (mListenerLock) {
333             if (mBleCallback != null) {
334                 try {
335                     mEnrollmentService.unregisterBleCallback(mListenerToBleService);
336                 } catch (RemoteException e) {
337                     handleRemoteExceptionFromCarService(e);
338                 }
339                 mBleCallback = null;
340             }
341         }
342     }
343 
344     /**
345      * Provides a list that contains information about the enrolled devices for the given user id.
346      * <p>
347      * Each enrollment handle corresponds to a trusted device for the given user.
348      *
349      * @param uid user id.
350      * @return list of the Enrollment handles and user names for the user id.
351      */
352     @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
353     @NonNull
getEnrolledDeviceInfoForUser(int uid)354     public List<TrustedDeviceInfo> getEnrolledDeviceInfoForUser(int uid) {
355         try {
356             return mEnrollmentService.getEnrolledDeviceInfosForUser(uid);
357         } catch (RemoteException e) {
358             return handleRemoteExceptionFromCarService(e, Collections.emptyList());
359         }
360     }
361 
getEventCallbackHandler()362     private Handler getEventCallbackHandler() {
363         return mEventCallbackHandler;
364     }
365 
366     /**
367      * Callback interface for Trusted device enrollment applications to implement.  The applications
368      * get notified on various enrollment state change events.
369      */
370     public interface CarTrustAgentEnrollmentCallback {
371         /**
372          * Communicate about failure/timeouts in the handshake process.  BluetoothDevice will be
373          * null when the returned error code is {@link #ENROLLMENT_NOT_ALLOWED}.
374          *
375          * @param device    the remote device trying to enroll
376          * @param errorCode information on what failed.
377          */
onEnrollmentHandshakeFailure(@ullable BluetoothDevice device, @TrustedDeviceEnrollmentError int errorCode)378         void onEnrollmentHandshakeFailure(@Nullable BluetoothDevice device,
379                 @TrustedDeviceEnrollmentError int errorCode);
380 
381         /**
382          * Present the pairing/authentication string to the user.
383          *
384          * @param device     the remote device trying to enroll
385          * @param authString the authentication string to show to the user to confirm across
386          *                   both devices
387          */
onAuthStringAvailable(BluetoothDevice device, String authString)388         void onAuthStringAvailable(BluetoothDevice device, String authString);
389 
390         /**
391          * Escrow token was received and the Trust Agent framework has generated a corresponding
392          * handle.
393          *
394          * @param handle the handle associated with the escrow token.
395          */
onEscrowTokenAdded(long handle)396         void onEscrowTokenAdded(long handle);
397 
398         /**
399          * Escrow token was removed as a result of a call to {@link #removeEscrowToken(long handle,
400          * int uid)}. The peer device associated with this token is not trusted for authentication
401          * anymore.
402          *
403          * @param handle the handle associated with the escrow token.
404          */
onEscrowTokenRemoved(long handle)405         void onEscrowTokenRemoved(long handle);
406 
407 
408         /**
409          * Escrow token's active state changed.
410          *
411          * @param handle the handle associated with the escrow token
412          * @param active True if token has been activated, false if not.
413          */
onEscrowTokenActiveStateChanged(long handle, boolean active)414         void onEscrowTokenActiveStateChanged(long handle, boolean active);
415     }
416 
417     /**
418      * Callback interface for Trusted device enrollment applications to implement.  The applications
419      * get notified on various BLE state change events that happen during trusted device enrollment.
420      */
421     public interface CarTrustAgentBleCallback {
422         /**
423          * Indicates a remote device connected on BLE.
424          */
onBleEnrollmentDeviceConnected(BluetoothDevice device)425         void onBleEnrollmentDeviceConnected(BluetoothDevice device);
426 
427         /**
428          * Indicates a remote device disconnected on BLE.
429          */
onBleEnrollmentDeviceDisconnected(BluetoothDevice device)430         void onBleEnrollmentDeviceDisconnected(BluetoothDevice device);
431 
432         /**
433          * Indicates that the device is broadcasting for trusted device enrollment on BLE.
434          */
onEnrollmentAdvertisingStarted()435         void onEnrollmentAdvertisingStarted();
436 
437         /**
438          * Indicates a failure in BLE broadcasting for enrollment.
439          */
onEnrollmentAdvertisingFailed()440         void onEnrollmentAdvertisingFailed();
441     }
442 
443     private static final class ListenerToEnrollmentService extends
444             ICarTrustAgentEnrollmentCallback.Stub {
445         private final WeakReference<CarTrustAgentEnrollmentManager> mMgr;
446 
ListenerToEnrollmentService(CarTrustAgentEnrollmentManager mgr)447         ListenerToEnrollmentService(CarTrustAgentEnrollmentManager mgr) {
448             mMgr = new WeakReference<>(mgr);
449         }
450 
451         /**
452          * Communicate about failure/timeouts in the handshake process.
453          */
454         @Override
onEnrollmentHandshakeFailure(BluetoothDevice device, @TrustedDeviceEnrollmentError int errorCode)455         public void onEnrollmentHandshakeFailure(BluetoothDevice device,
456                 @TrustedDeviceEnrollmentError int errorCode) {
457             CarTrustAgentEnrollmentManager enrollmentManager = mMgr.get();
458             if (enrollmentManager == null) {
459                 return;
460             }
461             enrollmentManager.getEventCallbackHandler().sendMessage(
462                     enrollmentManager.getEventCallbackHandler().obtainMessage(
463                             MSG_ENROLL_HANDSHAKE_FAILURE, new AuthInfo(device, null, errorCode)));
464         }
465 
466         /**
467          * Present the pairing/authentication string to the user.
468          */
469         @Override
onAuthStringAvailable(BluetoothDevice device, String authString)470         public void onAuthStringAvailable(BluetoothDevice device, String authString) {
471             CarTrustAgentEnrollmentManager enrollmentManager = mMgr.get();
472             if (enrollmentManager == null) {
473                 return;
474             }
475             enrollmentManager.getEventCallbackHandler().sendMessage(
476                     enrollmentManager.getEventCallbackHandler().obtainMessage(
477                             MSG_ENROLL_AUTH_STRING_AVAILABLE, new AuthInfo(device, authString, 0)));
478         }
479 
480         /**
481          * Escrow token was received and the Trust Agent framework has generated a corresponding
482          * handle.
483          */
484         @Override
onEscrowTokenAdded(long handle)485         public void onEscrowTokenAdded(long handle) {
486             CarTrustAgentEnrollmentManager enrollmentManager = mMgr.get();
487             if (enrollmentManager == null) {
488                 return;
489             }
490             Message message = enrollmentManager.getEventCallbackHandler().obtainMessage(
491                     MSG_ENROLL_TOKEN_ADDED);
492             Bundle data = new Bundle();
493             data.putLong(KEY_HANDLE, handle);
494             message.setData(data);
495             enrollmentManager.getEventCallbackHandler().sendMessage(message);
496         }
497 
498         /**
499          * Escrow token was removed.
500          */
501         @Override
onEscrowTokenRemoved(long handle)502         public void onEscrowTokenRemoved(long handle) {
503             CarTrustAgentEnrollmentManager enrollmentManager = mMgr.get();
504             if (enrollmentManager == null) {
505                 return;
506             }
507             Message message = enrollmentManager.getEventCallbackHandler().obtainMessage(
508                     MSG_ENROLL_TOKEN_REMOVED);
509             Bundle data = new Bundle();
510             data.putLong(KEY_HANDLE, handle);
511             message.setData(data);
512             enrollmentManager.getEventCallbackHandler().sendMessage(message);
513         }
514 
515         /**
516          * Escrow token's active state changed.
517          */
518         @Override
onEscrowTokenActiveStateChanged(long handle, boolean active)519         public void onEscrowTokenActiveStateChanged(long handle, boolean active) {
520             CarTrustAgentEnrollmentManager enrollmentManager = mMgr.get();
521             if (enrollmentManager == null) {
522                 return;
523             }
524             Message message = enrollmentManager.getEventCallbackHandler().obtainMessage(
525                     MSG_ENROLL_TOKEN_STATE_CHANGED);
526             Bundle data = new Bundle();
527             data.putLong(KEY_HANDLE, handle);
528             data.putBoolean(KEY_ACTIVE, active);
529             message.setData(data);
530             enrollmentManager.getEventCallbackHandler().sendMessage(message);
531         }
532     }
533 
534     private static final class ListenerToBleService extends ICarTrustAgentBleCallback.Stub {
535         private final WeakReference<CarTrustAgentEnrollmentManager> mMgr;
536 
ListenerToBleService(CarTrustAgentEnrollmentManager mgr)537         ListenerToBleService(CarTrustAgentEnrollmentManager mgr) {
538             mMgr = new WeakReference<>(mgr);
539         }
540 
541         /**
542          * Called when the GATT server is started and BLE is successfully advertising for
543          * enrollment.
544          */
onEnrollmentAdvertisingStarted()545         public void onEnrollmentAdvertisingStarted() {
546             CarTrustAgentEnrollmentManager enrollmentManager = mMgr.get();
547             if (enrollmentManager == null) {
548                 return;
549             }
550             enrollmentManager.getEventCallbackHandler().sendMessage(
551                     enrollmentManager.getEventCallbackHandler().obtainMessage(
552                             MSG_ENROLL_ADVERTISING_STARTED));
553         }
554 
555         /**
556          * Called when the BLE enrollment advertisement fails to start.
557          * see AdvertiseCallback#ADVERTISE_FAILED_* for possible error codes.
558          */
onEnrollmentAdvertisingFailed()559         public void onEnrollmentAdvertisingFailed() {
560             CarTrustAgentEnrollmentManager enrollmentManager = mMgr.get();
561             if (enrollmentManager == null) {
562                 return;
563             }
564             enrollmentManager.getEventCallbackHandler().sendMessage(
565                     enrollmentManager.getEventCallbackHandler().obtainMessage(
566                             MSG_ENROLL_ADVERTISING_FAILED));
567         }
568 
569         /**
570          * Called when a remote device is connected on BLE.
571          */
onBleEnrollmentDeviceConnected(BluetoothDevice device)572         public void onBleEnrollmentDeviceConnected(BluetoothDevice device) {
573             CarTrustAgentEnrollmentManager enrollmentManager = mMgr.get();
574             if (enrollmentManager == null) {
575                 return;
576             }
577             enrollmentManager.getEventCallbackHandler().sendMessage(
578                     enrollmentManager.getEventCallbackHandler().obtainMessage(
579                             MSG_ENROLL_DEVICE_CONNECTED, device));
580         }
581 
582         /**
583          * Called when a remote device is disconnected on BLE.
584          */
onBleEnrollmentDeviceDisconnected(BluetoothDevice device)585         public void onBleEnrollmentDeviceDisconnected(BluetoothDevice device) {
586             CarTrustAgentEnrollmentManager enrollmentManager = mMgr.get();
587             if (enrollmentManager == null) {
588                 return;
589             }
590             enrollmentManager.getEventCallbackHandler().sendMessage(
591                     enrollmentManager.getEventCallbackHandler().obtainMessage(
592                             MSG_ENROLL_DEVICE_DISCONNECTED, device));
593         }
594     }
595 
596     /**
597      * Callback Handler to handle dispatching the enrollment state changes to the corresponding
598      * listeners
599      */
600     private static final class EventCallbackHandler extends Handler {
601         private final WeakReference<CarTrustAgentEnrollmentManager> mEnrollmentManager;
602 
EventCallbackHandler(CarTrustAgentEnrollmentManager manager, Looper looper)603         EventCallbackHandler(CarTrustAgentEnrollmentManager manager, Looper looper) {
604             super(looper);
605             mEnrollmentManager = new WeakReference<>(manager);
606         }
607 
608         @Override
handleMessage(Message message)609         public void handleMessage(Message message) {
610             CarTrustAgentEnrollmentManager enrollmentManager = mEnrollmentManager.get();
611             if (enrollmentManager == null) {
612                 return;
613             }
614             switch (message.what) {
615                 case MSG_ENROLL_ADVERTISING_STARTED:
616                 case MSG_ENROLL_ADVERTISING_FAILED:
617                 case MSG_ENROLL_DEVICE_CONNECTED:
618                 case MSG_ENROLL_DEVICE_DISCONNECTED:
619                     enrollmentManager.dispatchBleCallback(message);
620                     break;
621                 case MSG_ENROLL_HANDSHAKE_FAILURE:
622                 case MSG_ENROLL_AUTH_STRING_AVAILABLE:
623                 case MSG_ENROLL_TOKEN_ADDED:
624                 case MSG_ENROLL_TOKEN_STATE_CHANGED:
625                 case MSG_ENROLL_TOKEN_REMOVED:
626                     enrollmentManager.dispatchEnrollmentCallback(message);
627                     break;
628                 default:
629                     Log.e(TAG, "Unknown message:" + message.what);
630                     break;
631             }
632         }
633     }
634 
635     /**
636      * Dispatch BLE related state change callbacks
637      *
638      * @param message Message to handle and dispatch
639      */
dispatchBleCallback(Message message)640     private void dispatchBleCallback(Message message) {
641         CarTrustAgentBleCallback bleCallback;
642         synchronized (mListenerLock) {
643             bleCallback = mBleCallback;
644         }
645         if (bleCallback == null) {
646             return;
647         }
648         switch (message.what) {
649             case MSG_ENROLL_ADVERTISING_STARTED:
650                 bleCallback.onEnrollmentAdvertisingStarted();
651                 break;
652             case MSG_ENROLL_ADVERTISING_FAILED:
653                 bleCallback.onEnrollmentAdvertisingFailed();
654                 break;
655             case MSG_ENROLL_DEVICE_CONNECTED:
656                 bleCallback.onBleEnrollmentDeviceConnected((BluetoothDevice) message.obj);
657                 break;
658             case MSG_ENROLL_DEVICE_DISCONNECTED:
659                 bleCallback.onBleEnrollmentDeviceDisconnected((BluetoothDevice) message.obj);
660                 break;
661             default:
662                 break;
663         }
664     }
665 
666     /**
667      * Dispatch Enrollment related state changes to the listener.
668      *
669      * @param message Message to handle and dispatch
670      */
dispatchEnrollmentCallback(Message message)671     private void dispatchEnrollmentCallback(Message message) {
672         CarTrustAgentEnrollmentCallback enrollmentCallback;
673         synchronized (mListenerLock) {
674             enrollmentCallback = mEnrollmentCallback;
675         }
676         if (enrollmentCallback == null) {
677             return;
678         }
679         AuthInfo auth;
680         Bundle data;
681         switch (message.what) {
682             case MSG_ENROLL_HANDSHAKE_FAILURE:
683                 auth = (AuthInfo) message.obj;
684                 enrollmentCallback.onEnrollmentHandshakeFailure(auth.mDevice, auth.mErrorCode);
685                 break;
686             case MSG_ENROLL_AUTH_STRING_AVAILABLE:
687                 auth = (AuthInfo) message.obj;
688                 if (auth.mDevice != null && auth.mAuthString != null) {
689                     enrollmentCallback.onAuthStringAvailable(auth.mDevice, auth.mAuthString);
690                 }
691                 break;
692             case MSG_ENROLL_TOKEN_ADDED:
693                 data = message.getData();
694                 if (data == null) {
695                     break;
696                 }
697                 enrollmentCallback.onEscrowTokenAdded(data.getLong(KEY_HANDLE));
698                 break;
699             case MSG_ENROLL_TOKEN_STATE_CHANGED:
700                 data = message.getData();
701                 if (data == null) {
702                     break;
703                 }
704                 enrollmentCallback.onEscrowTokenActiveStateChanged(data.getLong(KEY_HANDLE),
705                         data.getBoolean(KEY_ACTIVE));
706                 break;
707             case MSG_ENROLL_TOKEN_REMOVED:
708                 data = message.getData();
709                 if (data == null) {
710                     break;
711                 }
712                 enrollmentCallback.onEscrowTokenRemoved(data.getLong(KEY_HANDLE));
713                 break;
714             default:
715                 break;
716         }
717     }
718 
719     /**
720      * Container class to pass information through a Message to the handler.
721      */
722     private static class AuthInfo {
723         final BluetoothDevice mDevice;
724         @Nullable
725         final String mAuthString;
726         final int mErrorCode;
727 
AuthInfo(BluetoothDevice device, @Nullable String authString, @TrustedDeviceEnrollmentError int errorCode)728         AuthInfo(BluetoothDevice device, @Nullable String authString,
729                 @TrustedDeviceEnrollmentError int errorCode) {
730             mDevice = device;
731             mAuthString = authString;
732             mErrorCode = errorCode;
733         }
734     }
735 }
736