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.companiondevicesupport.feature.trust.ui;
18 
19 import static com.android.car.companiondevicesupport.activity.AssociationActivity.ACTION_ASSOCIATION_SETTING;
20 import static com.android.car.companiondevicesupport.activity.AssociationActivity.ASSOCIATED_DEVICE_DATA_NAME_EXTRA;
21 import static com.android.car.connecteddevice.util.SafeLog.logd;
22 import static com.android.car.connecteddevice.util.SafeLog.loge;
23 import static com.android.car.connecteddevice.util.SafeLog.logw;
24 
25 import android.annotation.Nullable;
26 import android.app.AlertDialog;
27 import android.app.Dialog;
28 import android.app.KeyguardManager;
29 import android.content.ComponentName;
30 import android.content.Context;
31 import android.content.DialogInterface;
32 import android.content.Intent;
33 import android.content.ServiceConnection;
34 import android.os.Bundle;
35 import android.os.IBinder;
36 import android.os.RemoteException;
37 import android.os.UserHandle;
38 import android.text.Html;
39 import android.text.Spanned;
40 import android.widget.Toast;
41 
42 import androidx.fragment.app.DialogFragment;
43 import androidx.fragment.app.FragmentActivity;
44 import androidx.lifecycle.ViewModelProviders;
45 
46 import com.android.car.companiondevicesupport.R;
47 import com.android.car.companiondevicesupport.api.external.AssociatedDevice;
48 import com.android.car.companiondevicesupport.api.external.CompanionDevice;
49 import com.android.car.companiondevicesupport.api.external.IDeviceAssociationCallback;
50 import com.android.car.companiondevicesupport.api.internal.trust.ITrustedDeviceEnrollmentCallback;
51 import com.android.car.companiondevicesupport.api.internal.trust.ITrustedDeviceCallback;
52 import com.android.car.companiondevicesupport.api.internal.trust.ITrustedDeviceManager;
53 import com.android.car.companiondevicesupport.api.internal.trust.TrustedDevice;
54 import com.android.car.companiondevicesupport.feature.trust.TrustedDeviceConstants;
55 import com.android.car.companiondevicesupport.feature.trust.TrustedDeviceManagerService;
56 import com.android.car.ui.toolbar.Toolbar;
57 
58 import java.util.List;
59 import java.util.concurrent.atomic.AtomicBoolean;
60 
61 
62 /** Activity for enrolling and viewing trusted devices. */
63 public class TrustedDeviceActivity extends FragmentActivity {
64 
65     private static final String TAG = "TrustedDeviceActivity";
66 
67     private static final int ACTIVATE_TOKEN_REQUEST_CODE = 1;
68 
69     private static final int CREATE_LOCK_REQUEST_CODE = 2;
70 
71     private static final int RETRIEVE_ASSOCIATED_DEVICE_REQUEST_CODE = 3;
72 
73     private static final String ACTION_LOCK_SETTINGS = "android.car.settings.SCREEN_LOCK_ACTIVITY";
74 
75     private static final String DEVICE_DETAIL_FRAGMENT_TAG = "TrustedDeviceDetailFragmentTag";
76 
77     private static final String DEVICE_NOT_CONNECTED_DIALOG_TAG =
78             "DeviceNotConnectedDialogFragmentTag";
79 
80     private static final String CREATE_PROFILE_LOCK_DIALOG_TAG =
81             "CreateProfileLockDialogFragmentTag";
82 
83     private static final String UNLOCK_PROFILE_TO_FINISH_DIALOG_TAG =
84             "UnlockProfileToFinishDialogFragmentTag";
85 
86     private static final String CREATE_PHONE_LOCK_DIALOG_TAG = "CreatePhoneLockDialogFragmentTag";
87 
88     private static final String ENROLLMENT_ERROR_DIALOG_TAG = "EnrollmentErrorDialogFragmentTag";
89 
90     /** {@code true} if a PIN/Pattern/Password has just been set as a screen lock. */
91     private final AtomicBoolean mIsScreenLockNewlyCreated = new AtomicBoolean(false);
92 
93     private final AtomicBoolean mIsStartedForEnrollment = new AtomicBoolean(false);
94 
95     private final AtomicBoolean mHasPendingCredential = new AtomicBoolean(false);
96 
97     /**
98      * {@code true} if this activity is relaunched for enrollment and the activity needs to be
99      * finished after enrollment has completed.
100      */
101     private final AtomicBoolean mWasRelaunched = new AtomicBoolean(false);
102 
103     private KeyguardManager mKeyguardManager;
104 
105     private ITrustedDeviceManager mTrustedDeviceManager;
106 
107     private Toolbar mToolbar;
108 
109     private TrustedDeviceViewModel mModel;
110 
111     @Override
onCreate(Bundle savedInstanceState)112     protected void onCreate(Bundle savedInstanceState) {
113         super.onCreate(savedInstanceState);
114         setContentView(R.layout.base_activity);
115         observeViewModel();
116         resumePreviousState(savedInstanceState);
117         mToolbar = findViewById(R.id.toolbar);
118         mToolbar.setTitle(R.string.trusted_device_feature_title);
119         mToolbar.showProgressBar();
120         mIsScreenLockNewlyCreated.set(false);
121         mIsStartedForEnrollment.set(false);
122         mHasPendingCredential.set(false);
123         mWasRelaunched.set(false);
124         Intent intent = new Intent(this, TrustedDeviceManagerService.class);
125         bindServiceAsUser(intent, mServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
126     }
127 
128     @Override
onActivityResult(int requestCode, int resultCode, Intent data)129     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
130         super.onActivityResult(requestCode, resultCode, data);
131 
132         switch (requestCode) {
133             case ACTIVATE_TOKEN_REQUEST_CODE:
134                 if (resultCode != RESULT_OK) {
135                     loge(TAG, "Lock screen was unsuccessful. Returned result code: " +
136                             resultCode + ".");
137                     finishEnrollment();
138                     return;
139                 }
140                 logd(TAG, "Credentials accepted. Waiting for TrustAgent to activate " +
141                         "token.");
142                 break;
143             case CREATE_LOCK_REQUEST_CODE:
144                 if (!isDeviceSecure()) {
145                     loge(TAG, "Set up new lock unsuccessful. Returned result code: "
146                             + resultCode + ".");
147                     mIsScreenLockNewlyCreated.set(false);
148                     return;
149                 }
150 
151                 if (mHasPendingCredential.get()) {
152                     showUnlockProfileDialogFragment();
153                 }
154                 break;
155             case RETRIEVE_ASSOCIATED_DEVICE_REQUEST_CODE:
156                 AssociatedDevice device = data.getParcelableExtra(ASSOCIATED_DEVICE_DATA_NAME_EXTRA);
157                 if (device == null) {
158                     loge(TAG, "No valid associated device.");
159                     return;
160                 }
161                 mModel.setAssociatedDevice(device);
162                 Intent incomingIntent = getIntent();
163                 if (isStartedForEnrollment(incomingIntent)) {
164                     processEnrollment();
165                     return;
166                 }
167                 showTrustedDeviceDetailFragment(device);
168                 break;
169             default:
170                 logw(TAG, "Unrecognized activity result. Request code: " + requestCode
171                         + ". Ignoring.");
172                 break;
173         }
174     }
175 
176     @Override
onNewIntent(Intent intent)177     protected void onNewIntent(Intent intent) {
178         super.onNewIntent(intent);
179         mWasRelaunched.set(true);
180         if (isStartedForEnrollment(intent)) {
181             processEnrollment();
182         }
183     }
184 
185     @Override
onDestroy()186     protected void onDestroy() {
187         try {
188             unregisterCallbacks();
189         } catch (RemoteException e) {
190             loge(TAG, "Error while disconnecting from service.", e);
191         }
192         unbindService(mServiceConnection);
193         super.onDestroy();
194     }
195 
resumePreviousState(Bundle saveInstanceState)196     private void resumePreviousState(Bundle saveInstanceState) {
197         if (saveInstanceState == null) {
198             return;
199         }
200         CreateProfileLockDialogFragment createProfileLockDialogFragment =
201                 (CreateProfileLockDialogFragment) getSupportFragmentManager()
202                 .findFragmentByTag(CREATE_PROFILE_LOCK_DIALOG_TAG);
203         if (createProfileLockDialogFragment != null) {
204             createProfileLockDialogFragment.setOnConfirmListener((d, w) -> createScreenLock());
205         }
206 
207         UnlockProfileDialogFragment unlockProfileDialogFragment =
208                 (UnlockProfileDialogFragment) getSupportFragmentManager()
209                 .findFragmentByTag(UNLOCK_PROFILE_TO_FINISH_DIALOG_TAG);
210         if (unlockProfileDialogFragment != null) {
211             unlockProfileDialogFragment.setOnConfirmListener((d, w) -> validateCredentials());
212         }
213     }
214 
observeViewModel()215     private void observeViewModel() {
216         mModel = ViewModelProviders.of(this).get(TrustedDeviceViewModel.class);
217         mModel.getDeviceToDisable().observe(this, trustedDevice -> {
218             if (trustedDevice == null) {
219                 return;
220             }
221             mModel.setDeviceToDisable(null);
222             if (mTrustedDeviceManager == null) {
223                 loge(TAG, "Failed to remove trusted device. service not connected.");
224                 return;
225             }
226             try {
227                 logd(TAG, "calling removeTrustedDevice");
228                 mTrustedDeviceManager.removeTrustedDevice(trustedDevice);
229             } catch (RemoteException e) {
230                 loge(TAG, "Failed to remove trusted device.", e);
231             }
232         });
233         mModel.getDeviceToEnable().observe(this, associatedDevice -> {
234             if (associatedDevice == null) {
235                 return;
236             }
237             mModel.setDeviceToEnable(null);
238             attemptInitiatingEnrollment(associatedDevice);
239         });
240     }
241 
hasAssociatedDevice()242     private boolean hasAssociatedDevice() {
243         Intent intent = getIntent();
244         String action = intent.getAction();
245         if (!TrustedDeviceConstants.INTENT_ACTION_TRUSTED_DEVICE_SETTING.equals(action)) {
246             return false;
247         }
248         AssociatedDevice device = intent.getParcelableExtra(ASSOCIATED_DEVICE_DATA_NAME_EXTRA);
249         if (device == null) {
250             loge(TAG, "No valid associated device.");
251             return false;
252         }
253         mModel.setAssociatedDevice(device);
254         showTrustedDeviceDetailFragment(device);
255         return true;
256     }
257 
attemptInitiatingEnrollment(AssociatedDevice device)258     private void attemptInitiatingEnrollment(AssociatedDevice device) {
259         if (!isCompanionDeviceConnected(device.getDeviceId())) {
260             DeviceNotConnectedDialogFragment fragment = new DeviceNotConnectedDialogFragment();
261             fragment.show(getSupportFragmentManager(), DEVICE_NOT_CONNECTED_DIALOG_TAG);
262             return;
263         }
264         try {
265             mTrustedDeviceManager.initiateEnrollment(device.getDeviceId());
266         } catch (RemoteException e) {
267             loge(TAG, "Failed to initiate enrollment. ", e);
268         }
269     }
270 
isCompanionDeviceConnected(String deviceId)271     private boolean isCompanionDeviceConnected(String deviceId) {
272         if (mTrustedDeviceManager == null) {
273             loge(TAG, "Failed to check connection status for device: " + deviceId +
274                     "Service not connected.");
275             return false;
276         }
277         List<CompanionDevice> devices = null;
278         try {
279             devices = mTrustedDeviceManager.getActiveUserConnectedDevices();
280         } catch (RemoteException e) {
281             loge(TAG, "Failed to check connection status for device: " + deviceId, e);
282             return false;
283         }
284         if (devices == null || devices.isEmpty()) {
285             return false;
286         }
287         for (CompanionDevice device: devices) {
288             if (device.getDeviceId().equals(deviceId)) {
289                 return true;
290             }
291         }
292         return false;
293     }
294 
validateCredentials()295     private void validateCredentials() {
296         logd(TAG, "Validating credentials to activate token.");
297         KeyguardManager keyguardManager = getKeyguardManager();
298         if (keyguardManager == null) {
299             return;
300         }
301         if (!mIsStartedForEnrollment.get()) {
302             mHasPendingCredential.set(true);
303             return;
304         }
305         if (mIsScreenLockNewlyCreated.get()) {
306             showUnlockProfileDialogFragment();
307             return;
308         }
309         @SuppressWarnings("deprecation") // Car does not support Biometric lock as of now.
310         Intent confirmIntent = keyguardManager.createConfirmDeviceCredentialIntent(
311                 "PLACEHOLDER PROMPT TITLE", "PLACEHOLDER PROMPT MESSAGE");
312         if (confirmIntent == null) {
313             loge(TAG, "User either has no lock screen, or a token is already registered.");
314             return;
315         }
316         mHasPendingCredential.set(false);
317         mIsStartedForEnrollment.set(false);
318         logd(TAG, "Prompting user to validate credentials.");
319         startActivityForResult(confirmIntent, ACTIVATE_TOKEN_REQUEST_CODE);
320     }
321 
processEnrollment()322     private void processEnrollment() {
323         mIsStartedForEnrollment.set(true);
324         if (mHasPendingCredential.get()) {
325             validateCredentials();
326             return;
327         }
328         maybePromptToCreatePassword();
329     }
330 
isStartedForEnrollment(Intent intent)331     private boolean isStartedForEnrollment(Intent intent) {
332         return intent != null && intent.getBooleanExtra(
333                 TrustedDeviceConstants.INTENT_EXTRA_ENROLL_NEW_TOKEN, false);
334     }
335 
finishEnrollment()336     private void finishEnrollment() {
337         if (!mWasRelaunched.get()) {
338             // If the activity is not relaunched for enrollment, it needs to be finished to make the
339             // foreground return to the previous screen.
340             finish();
341         }
342     }
343 
maybePromptToCreatePassword()344     private void maybePromptToCreatePassword() {
345         if (isDeviceSecure()) {
346             return;
347         }
348 
349         CreateProfileLockDialogFragment fragment = CreateProfileLockDialogFragment.newInstance(
350                 (d, w) -> createScreenLock());
351         fragment.show(getSupportFragmentManager(), CREATE_PROFILE_LOCK_DIALOG_TAG);
352     }
353 
createScreenLock()354     private void createScreenLock() {
355         if (isDeviceSecure()) {
356             return;
357         }
358         logd(TAG, "User has not set a lock screen. Redirecting to set up.");
359         Intent intent = new Intent(ACTION_LOCK_SETTINGS);
360         mIsScreenLockNewlyCreated.set(true);
361         startActivityForResult(intent, CREATE_LOCK_REQUEST_CODE);
362     }
363 
isDeviceSecure()364     private boolean isDeviceSecure() {
365         KeyguardManager keyguardManager = getKeyguardManager();
366         if (keyguardManager == null) {
367             return false;
368         }
369         return keyguardManager.isDeviceSecure();
370     }
371 
retrieveAssociatedDevice()372     private void retrieveAssociatedDevice() {
373         Intent intent = new Intent(ACTION_ASSOCIATION_SETTING);
374         startActivityForResult(intent, RETRIEVE_ASSOCIATED_DEVICE_REQUEST_CODE);
375     }
376 
showTrustedDeviceDetailFragment(AssociatedDevice device)377     private void showTrustedDeviceDetailFragment(AssociatedDevice device) {
378         mToolbar.hideProgressBar();
379         TrustedDeviceDetailFragment fragment = TrustedDeviceDetailFragment.newInstance(device);
380         getSupportFragmentManager().beginTransaction()
381                 .replace(R.id.fragment_container, fragment, DEVICE_DETAIL_FRAGMENT_TAG)
382                 .commit();
383     }
384 
showUnlockProfileDialogFragment()385     private void showUnlockProfileDialogFragment() {
386         mIsScreenLockNewlyCreated.set(false);
387         UnlockProfileDialogFragment fragment = UnlockProfileDialogFragment.newInstance((d, w) ->
388             validateCredentials());
389         fragment.show(getSupportFragmentManager(), UNLOCK_PROFILE_TO_FINISH_DIALOG_TAG);
390     }
391 
showEnrollmentSuccessToast(TrustedDevice device)392     private void showEnrollmentSuccessToast(TrustedDevice device) {
393         AssociatedDevice addedDevice = mModel.getAssociatedDevice().getValue();
394         if (addedDevice == null) {
395             loge(TAG, "No associated device retrieved when a trusted device has been added.");
396             return;
397         }
398         if (!addedDevice.getDeviceId().equals(device.getDeviceId())) {
399             loge(TAG, "Id of the enrolled trusted device doesn't match id of the current device");
400             return;
401         }
402         String message = getString(R.string.trusted_device_enrollment_success_message,
403                 addedDevice.getDeviceName());
404         Spanned styledMessage = Html.fromHtml(message, Html.FROM_HTML_MODE_LEGACY);
405         runOnUiThread(() ->
406                 Toast.makeText(getApplicationContext(), styledMessage, Toast.LENGTH_SHORT).show());
407     }
408 
showEnrollmentErrorDialogFragment(int error)409     private void showEnrollmentErrorDialogFragment(int error) {
410         switch (error) {
411             case TrustedDeviceConstants.TRUSTED_DEVICE_ERROR_DEVICE_NOT_SECURED:
412                 CreatePhoneLockDialogFragment createPhoneLockDialogFragment =
413                         new CreatePhoneLockDialogFragment();
414                 createPhoneLockDialogFragment.show(getSupportFragmentManager(),
415                         CREATE_PHONE_LOCK_DIALOG_TAG);
416                 break;
417             case TrustedDeviceConstants.TRUSTED_DEVICE_ERROR_MESSAGE_TYPE_UNKNOWN:
418             case TrustedDeviceConstants.TRUSTED_DEVICE_ERROR_UNKNOWN:
419                 EnrollmentErrorDialogFragment enrollmentErrorDialogFragment =
420                         new EnrollmentErrorDialogFragment();
421                 enrollmentErrorDialogFragment.show(getSupportFragmentManager(),
422                         ENROLLMENT_ERROR_DIALOG_TAG);
423                 break;
424             default:
425                 loge(TAG, "Encountered unexpected error: " + error + ".");
426         }
427     }
428 
registerCallbacks()429     private void registerCallbacks() throws RemoteException {
430         if (mTrustedDeviceManager == null) {
431             loge(TAG, "Server not connected when attempting to register callbacks.");
432             return;
433         }
434         mTrustedDeviceManager.registerTrustedDeviceEnrollmentCallback(
435                 mTrustedDeviceEnrollmentCallback);
436         mTrustedDeviceManager.registerTrustedDeviceCallback(mTrustedDeviceCallback);
437         mTrustedDeviceManager.registerAssociatedDeviceCallback(mDeviceAssociationCallback);
438     }
439 
unregisterCallbacks()440     private void unregisterCallbacks() throws RemoteException {
441         if (mTrustedDeviceManager == null) {
442             loge(TAG, "Server not connected when attempting to unregister callbacks.");
443             return;
444         }
445         mTrustedDeviceManager.unregisterTrustedDeviceEnrollmentCallback(
446                 mTrustedDeviceEnrollmentCallback);
447         mTrustedDeviceManager.unregisterTrustedDeviceCallback(mTrustedDeviceCallback);
448         mTrustedDeviceManager.unregisterAssociatedDeviceCallback(mDeviceAssociationCallback);
449     }
450 
451     private final ServiceConnection mServiceConnection = new ServiceConnection() {
452         @Override
453         public void onServiceConnected(ComponentName name, IBinder service) {
454             mTrustedDeviceManager = ITrustedDeviceManager.Stub.asInterface(service);
455             try {
456                 registerCallbacks();
457                 mModel.setTrustedDevices(mTrustedDeviceManager.getTrustedDevicesForActiveUser());
458             } catch (RemoteException e) {
459                 loge(TAG, "Error while connecting to service.");
460             }
461 
462             logd(TAG, "Successfully connected to TrustedDeviceManager.");
463 
464             if (!hasAssociatedDevice()) {
465                 retrieveAssociatedDevice();
466             }
467         }
468 
469         @Override
470         public void onServiceDisconnected(ComponentName name) {
471         }
472     };
473 
474     private final ITrustedDeviceCallback mTrustedDeviceCallback =
475             new ITrustedDeviceCallback.Stub() {
476         @Override
477         public void onTrustedDeviceAdded(TrustedDevice device) {
478             logd(TAG, "Added trusted device: " + device + ".");
479             mModel.setEnabledDevice(device);
480             showEnrollmentSuccessToast(device);
481             finishEnrollment();
482         }
483 
484         @Override
485         public void onTrustedDeviceRemoved(TrustedDevice device) {
486             logd(TAG, "Removed trusted device: " + device +".");
487             mModel.setDisabledDevice(device);
488         }
489     };
490 
491     private final IDeviceAssociationCallback mDeviceAssociationCallback =
492             new IDeviceAssociationCallback.Stub() {
493         @Override
494         public void onAssociatedDeviceAdded(AssociatedDevice device) { }
495 
496         @Override
497         public void onAssociatedDeviceRemoved(AssociatedDevice device) {
498             AssociatedDevice currentDevice = mModel.getAssociatedDevice().getValue();
499             if (device.equals(currentDevice)) {
500                 finish();
501             }
502         }
503 
504         @Override
505         public void onAssociatedDeviceUpdated(AssociatedDevice device) {
506             if (device != null) {
507                 mModel.setAssociatedDevice(device);
508             }
509         }
510     };
511 
512     @Nullable
getKeyguardManager()513     private KeyguardManager getKeyguardManager() {
514         if (mKeyguardManager == null) {
515             mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
516         }
517         if (mKeyguardManager == null) {
518             loge(TAG, "Unable to get KeyguardManager.");
519         }
520         return mKeyguardManager;
521     }
522 
523     private ITrustedDeviceEnrollmentCallback mTrustedDeviceEnrollmentCallback =
524             new ITrustedDeviceEnrollmentCallback.Stub() {
525 
526         @Override
527         public void onValidateCredentialsRequest() {
528             validateCredentials();
529         }
530 
531         @Override
532         public void onTrustedDeviceEnrollmentError(int error) {
533             loge(TAG, "Failed to enroll trusted device, encountered error: " + error + ".");
534             showEnrollmentErrorDialogFragment(error);
535         }
536     };
537 
538     /** Dialog Fragment to notify that the device is not actively connected. */
539     public static class DeviceNotConnectedDialogFragment extends DialogFragment {
540         @Override
onCreateDialog(Bundle savedInstanceState)541         public Dialog onCreateDialog(Bundle savedInstanceState) {
542             return new AlertDialog.Builder(getActivity())
543                     .setTitle(getString(R.string.device_not_connected_dialog_title))
544                     .setMessage(getString(R.string.device_not_connected_dialog_message))
545                     .setNegativeButton(getString(R.string.ok), null)
546                     .setCancelable(true)
547                     .create();
548         }
549     }
550 
551     /** Dialog Fragment to notify that a profile lock is needed to continue enrollment. */
552     public static class CreateProfileLockDialogFragment extends DialogFragment {
553         private DialogInterface.OnClickListener mOnConfirmListener;
554 
newInstance( DialogInterface.OnClickListener listener)555         static CreateProfileLockDialogFragment newInstance(
556                 DialogInterface.OnClickListener listener) {
557             CreateProfileLockDialogFragment fragment = new CreateProfileLockDialogFragment();
558             fragment.setOnConfirmListener(listener);
559             return fragment;
560         }
561 
562         @Override
onCreateDialog(Bundle savedInstanceState)563         public Dialog onCreateDialog(Bundle savedInstanceState) {
564             return new AlertDialog.Builder(getActivity())
565                     .setTitle(getString(R.string.create_profile_lock_dialog_title))
566                     .setMessage(getString(R.string.create_profile_lock_dialog_message))
567                     .setNegativeButton(getString(R.string.cancel), null)
568                     .setPositiveButton(getString(R.string.continue_button), mOnConfirmListener)
569                     .setCancelable(true)
570                     .create();
571         }
572 
setOnConfirmListener(DialogInterface.OnClickListener onConfirmListener)573         void setOnConfirmListener(DialogInterface.OnClickListener onConfirmListener) {
574             mOnConfirmListener = onConfirmListener;
575         }
576     }
577 
578     /** Dialog Fragment to notify that the user needs to unlock again to finish enrollment. */
579     public static class UnlockProfileDialogFragment extends DialogFragment {
580         private DialogInterface.OnClickListener mOnConfirmListener;
581 
newInstance(DialogInterface.OnClickListener listener)582         static UnlockProfileDialogFragment newInstance(DialogInterface.OnClickListener listener) {
583             UnlockProfileDialogFragment fragment = new UnlockProfileDialogFragment();
584             fragment.setOnConfirmListener(listener);
585             return fragment;
586         }
587 
588         @Override
onCreateDialog(Bundle savedInstanceState)589         public Dialog onCreateDialog(Bundle savedInstanceState) {
590             return new AlertDialog.Builder(getActivity())
591                     .setTitle(getString(R.string.unlock_profile_to_finish_title))
592                     .setMessage(getString(R.string.unlock_profile_to_finish_message))
593                     .setNegativeButton(getString(R.string.cancel), null)
594                     .setPositiveButton(getString(R.string.continue_button), mOnConfirmListener)
595                     .setCancelable(true)
596                     .create();
597         }
598 
setOnConfirmListener(DialogInterface.OnClickListener onConfirmListener)599         void setOnConfirmListener(DialogInterface.OnClickListener onConfirmListener) {
600             mOnConfirmListener = onConfirmListener;
601         }
602     }
603 
604     /** Dialog Fragment to notify that the user needs to set up phone unlock before enrollment.*/
605     public static class CreatePhoneLockDialogFragment extends DialogFragment {
606         @Override
onCreateDialog(Bundle savedInstanceState)607         public Dialog onCreateDialog(Bundle savedInstanceState) {
608             return new AlertDialog.Builder(getActivity())
609                     .setTitle(getString(R.string.create_phone_lock_dialog_title))
610                     .setMessage(getString(R.string.create_phone_lock_dialog_message))
611                     .setPositiveButton(getString(R.string.ok), null)
612                     .setCancelable(true)
613                     .create();
614         }
615     }
616 
617     /** Dialog Fragment to notify error during enrollment.*/
618     public static class EnrollmentErrorDialogFragment extends DialogFragment {
619         @Override
onCreateDialog(Bundle savedInstanceState)620         public Dialog onCreateDialog(Bundle savedInstanceState) {
621             return new AlertDialog.Builder(getActivity())
622                     .setTitle(getString(R.string.trusted_device_enrollment_error_dialog_title))
623                     .setMessage(getString(R.string.trusted_device_enrollment_error_dialog_message))
624                     .setPositiveButton(getString(R.string.ok), null)
625                     .setCancelable(true)
626                     .create();
627         }
628     }
629 }
630