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 com.android.car.settings.security;
18 
19 import android.bluetooth.BluetoothDevice;
20 import android.car.Car;
21 import android.car.trust.CarTrustAgentEnrollmentManager;
22 import android.car.userlib.CarUserManagerHelper;
23 import android.os.Bundle;
24 import android.widget.Toast;
25 
26 import androidx.annotation.Nullable;
27 import androidx.annotation.VisibleForTesting;
28 import androidx.fragment.app.Fragment;
29 
30 import com.android.car.settings.R;
31 import com.android.car.settings.common.BaseCarSettingsActivity;
32 import com.android.car.settings.common.Logger;
33 
34 /**
35  * Activity which manages the enrollment process and communicates between
36  * CarTrustAgentEnrollmentService and fragments.
37  *
38  * <p>The flow when user want to enroll a trusted device should be as follows:
39  * <ol>
40  * <li> {@link CarTrustAgentEnrollmentManager#setEnrollmentCallback(
41  *CarTrustAgentEnrollmentManager.CarTrustAgentEnrollmentCallback)}
42  * <li> {@link CarTrustAgentEnrollmentManager#setBleCallback(
43  *CarTrustAgentEnrollmentManager.CarTrustAgentBleCallback)}
44  * <li> {@link CarTrustAgentEnrollmentManager#startEnrollmentAdvertising()}
45  * <li> wait for {@link CarTrustAgentEnrollmentManager.CarTrustAgentBleCallback#
46  * onBleEnrollmentDeviceDisconnected(BluetoothDevice)}
47  * <li>  {@link CarTrustAgentEnrollmentManager#stopEnrollmentAdvertising()}
48  * <li> wait for
49  * {@link CarTrustAgentEnrollmentManager.CarTrustAgentEnrollmentCallback#onAuthStringAvailable(
50  *BluetoothDevice, String)} to show the pairing code dialog to user
51  * <li> {@link CarTrustAgentEnrollmentManager#enrollmentHandshakeAccepted(BluetoothDevice)} after
52  * user confirms the pairing code
53  * <li> wait for
54  * {@link CarTrustAgentEnrollmentManager.CarTrustAgentEnrollmentCallback#onEscrowTokenAdded(long)}
55  * <li> {@link #getCheckLockFragment()}, wait user to input the password
56  * <li> After user enter the correct password, wait for
57  * {@link CarTrustAgentEnrollmentManager.CarTrustAgentEnrollmentCallback#
58  * onEscrowTokenActiveStateChanged(long, boolean)}
59  * <li> After get the results, finish the activity
60  * </ol>
61  */
62 public class AddTrustedDeviceActivity extends BaseCarSettingsActivity implements CheckLockListener {
63     private static final Logger LOG = new Logger(AddTrustedDeviceActivity.class);
64     private static final String BLUETOOTH_DEVICE_KEY = "bluetoothDevice";
65     private static final String CURRENT_HANDLE_KEY = "currentHandle";
66     private Car mCar;
67     private BluetoothDevice mBluetoothDevice;
68     private long mHandle;
69     private CarUserManagerHelper mCarUserManagerHelper;
70     @Nullable
71     private CarTrustAgentEnrollmentManager mCarTrustAgentEnrollmentManager;
72 
73     private final CarTrustAgentEnrollmentManager.CarTrustAgentEnrollmentCallback
74             mCarTrustAgentEnrollmentCallback =
75             new CarTrustAgentEnrollmentManager.CarTrustAgentEnrollmentCallback() {
76 
77                 @Override
78                 public void onEnrollmentHandshakeFailure(BluetoothDevice device, int errorCode) {
79                     LOG.e("Trust agent service time out");
80                 }
81 
82                 @Override
83                 public void onAuthStringAvailable(BluetoothDevice device, String authString) {
84                     ConfirmPairingCodeDialog dialog = ConfirmPairingCodeDialog.newInstance(
85                             authString);
86                     dialog.setConfirmPairingCodeListener(mConfirmParingCodeListener);
87                     showDialog(dialog, ConfirmPairingCodeDialog.TAG);
88                 }
89 
90                 @Override
91                 public void onEscrowTokenAdded(long handle) {
92                     // User need to enter the correct authentication of the car to activate the
93                     // added token.
94                     mHandle = handle;
95                     launchFragment(getCheckLockFragment());
96                 }
97 
98                 @Override
99                 public void onEscrowTokenRemoved(long handle) {
100                 }
101 
102                 @Override
103                 public void onEscrowTokenActiveStateChanged(long handle, boolean active) {
104                     if (active) {
105                         onDeviceAddedSuccessfully();
106                     } else {
107                         LOG.d(handle + " has not been activated");
108                     }
109                     finish();
110                 }
111             };
112 
113     private final CarTrustAgentEnrollmentManager.CarTrustAgentBleCallback
114             mCarTrustAgentBleCallback =
115             new CarTrustAgentEnrollmentManager.CarTrustAgentBleCallback() {
116                 @Override
117                 public void onBleEnrollmentDeviceConnected(BluetoothDevice device) {
118                     mBluetoothDevice = device;
119                     mCarTrustAgentEnrollmentManager.stopEnrollmentAdvertising();
120                 }
121 
122                 @Override
123                 public void onBleEnrollmentDeviceDisconnected(BluetoothDevice device) {
124                     Toast.makeText(AddTrustedDeviceActivity.this, getResources().getString(
125                             R.string.trusted_device_disconnected_toast),
126                             Toast.LENGTH_SHORT).show();
127                     mBluetoothDevice = null;
128                     finish();
129                 }
130 
131                 @Override
132                 public void onEnrollmentAdvertisingStarted() {
133                 }
134 
135                 @Override
136                 public void onEnrollmentAdvertisingFailed() {
137                     finish();
138                 }
139             };
140 
141     @VisibleForTesting
142     final ConfirmPairingCodeDialog.ConfirmPairingCodeListener mConfirmParingCodeListener =
143             new ConfirmPairingCodeDialog.ConfirmPairingCodeListener() {
144                 public void onConfirmPairingCode() {
145                     mCarTrustAgentEnrollmentManager.enrollmentHandshakeAccepted(mBluetoothDevice);
146                 }
147 
148                 public void onDialogCancelled() {
149                     finish();
150                 }
151             };
152 
153     @Override
onCreate(Bundle savedInstanceState)154     public void onCreate(Bundle savedInstanceState) {
155         super.onCreate(savedInstanceState);
156         mCar = Car.createCar(this);
157         mCarTrustAgentEnrollmentManager = (CarTrustAgentEnrollmentManager) mCar.getCarManager(
158                 Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE);
159         if (mCarTrustAgentEnrollmentManager == null) {
160             LOG.e("CarTrustAgentEnrollmentManager is null");
161             finish();
162         }
163         mCarUserManagerHelper = new CarUserManagerHelper(this);
164         if (savedInstanceState != null) {
165             mBluetoothDevice = savedInstanceState.getParcelable(BLUETOOTH_DEVICE_KEY);
166             mHandle = savedInstanceState.getLong(CURRENT_HANDLE_KEY);
167         }
168         ConfirmPairingCodeDialog dialog =
169                 (ConfirmPairingCodeDialog) findDialogByTag(ConfirmPairingCodeDialog.TAG);
170         if (dialog != null) {
171             dialog.setConfirmPairingCodeListener(mConfirmParingCodeListener);
172         }
173     }
174 
175 
176     @Override
onStart()177     protected void onStart() {
178         super.onStart();
179         if (mHandle != 0) {
180             if (mCarTrustAgentEnrollmentManager.isEscrowTokenActive(mHandle,
181                     mCarUserManagerHelper.getCurrentProcessUserId())) {
182                 onDeviceAddedSuccessfully();
183                 finish();
184             }
185         }
186         if (mBluetoothDevice == null) {
187             mCarTrustAgentEnrollmentManager.startEnrollmentAdvertising();
188         }
189         mCarTrustAgentEnrollmentManager.setEnrollmentCallback(mCarTrustAgentEnrollmentCallback);
190         mCarTrustAgentEnrollmentManager.setBleCallback(mCarTrustAgentBleCallback);
191 
192     }
193 
194     @Override
onPause()195     protected void onPause() {
196         super.onPause();
197         // When activity is pausing not because of a configuration change
198         if (getChangingConfigurations() == 0) {
199             mCarTrustAgentEnrollmentManager.terminateEnrollmentHandshake();
200         }
201     }
202 
203     @Override
onStop()204     protected void onStop() {
205         super.onStop();
206         mCarTrustAgentEnrollmentManager.setBleCallback(null);
207         mCarTrustAgentEnrollmentManager.setEnrollmentCallback(null);
208         mCarTrustAgentEnrollmentManager.stopEnrollmentAdvertising();
209     }
210 
211     @Override
onSaveInstanceState(Bundle savedInstanceState)212     public void onSaveInstanceState(Bundle savedInstanceState) {
213         super.onSaveInstanceState(savedInstanceState);
214         savedInstanceState.putParcelable(BLUETOOTH_DEVICE_KEY, mBluetoothDevice);
215         savedInstanceState.putLong(CURRENT_HANDLE_KEY, mHandle);
216     }
217 
218 
219     @Override
220     @Nullable
getInitialFragment()221     protected Fragment getInitialFragment() {
222         Fragment currentFragment = getSupportFragmentManager().findFragmentById(
223                 R.id.fragment_container);
224         return currentFragment == null ? new AddTrustedDeviceProgressFragment() : currentFragment;
225     }
226 
getCheckLockFragment()227     private Fragment getCheckLockFragment() {
228         return ConfirmPasswordFragmentFactory.getFragment(/* context= */ this);
229     }
230 
231     @Override
onLockVerified(byte[] lock)232     public void onLockVerified(byte[] lock) {
233         getSupportFragmentManager().popBackStack();
234     }
235 
236     @Override
onBackPressed()237     public void onBackPressed() {
238         finish();
239     }
240 
onDeviceAddedSuccessfully()241     private void onDeviceAddedSuccessfully() {
242         Toast.makeText(this,
243                 getResources().getString(R.string.trusted_device_success_enrollment_toast),
244                 Toast.LENGTH_LONG).show();
245     }
246 }
247