diff options
| author | 2019-08-14 12:03:12 -0700 | |
|---|---|---|
| committer | 2019-08-14 12:04:17 -0700 | |
| commit | 16258e3e98e0e648840171727f321fd9436a548d (patch) | |
| tree | 787dd9b84e4fc0c85c92db2bb85093b790cbca70 | |
| parent | c7487391b0870e59a5c9d44d8e479bb55984ac5f (diff) | |
Create dialog to show detecting Batmobile device & unlocking
Bug: 136049501
Test: Manually on IHU
Change-Id: I47b55e3032c814623aa5bf5c5c3ec1899c03e98e
11 files changed, 489 insertions, 10 deletions
diff --git a/packages/CarSystemUI/AndroidManifest.xml b/packages/CarSystemUI/AndroidManifest.xml index 195d4fee5162..261b9f508ccd 100644 --- a/packages/CarSystemUI/AndroidManifest.xml +++ b/packages/CarSystemUI/AndroidManifest.xml @@ -21,4 +21,8 @@ coreApp="true"> <!-- This permission is required to monitor car power state. --> <uses-permission android:name="android.car.permission.CAR_POWER" /> + <!-- This permission is required to get the trusted device list of a user. --> + <uses-permission android:name="android.car.permission.CAR_ENROLL_TRUST"/> + <!-- This permission is required to get bluetooth broadcast. --> + <uses-permission android:name="android.permission.BLUETOOTH" /> </manifest> diff --git a/packages/CarSystemUI/res/drawable/unlock_dialog_background.xml b/packages/CarSystemUI/res/drawable/unlock_dialog_background.xml new file mode 100644 index 000000000000..bec6ba7b7c4f --- /dev/null +++ b/packages/CarSystemUI/res/drawable/unlock_dialog_background.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@color/unlock_dialog_background_color"/> + <padding + android:bottom="@*android:dimen/car_padding_2" + android:left="@*android:dimen/car_padding_2" + android:right="@*android:dimen/car_padding_2" + android:top="@*android:dimen/car_padding_2"/> + <corners + android:radius="@dimen/unlock_dialog_radius"/> +</shape>
\ No newline at end of file diff --git a/packages/CarSystemUI/res/layout/trust_agent_unlock_dialog.xml b/packages/CarSystemUI/res/layout/trust_agent_unlock_dialog.xml new file mode 100644 index 000000000000..2d9901c30d02 --- /dev/null +++ b/packages/CarSystemUI/res/layout/trust_agent_unlock_dialog.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center"> + + <LinearLayout + android:layout_width="@dimen/unlock_dialog_width" + android:layout_height="wrap_content" + android:gravity="center" + android:layout_gravity="center" + android:orientation="vertical" + android:background="@drawable/unlock_dialog_background" + android:padding="@*android:dimen/car_padding_2"> + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + <ProgressBar + android:layout_gravity="center" + android:layout_width="@dimen/unlock_dialog_progress_bar_size" + android:layout_height="@dimen/unlock_dialog_progress_bar_size" /> + <ImageView + android:id="@+id/avatar" + android:layout_gravity="center" + android:layout_width="@dimen/unlock_dialog_avatar_size" + android:layout_height="@dimen/unlock_dialog_avatar_size" + android:scaleType="fitCenter"/> + </FrameLayout> + + <TextView + android:id="@+id/user_name" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/unlock_dialog_default_user_name" + android:textSize="@*android:dimen/car_body1_size" + android:textColor="@android:color/white"/> + + <TextView + android:id="@+id/unlocking_text" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_marginTop="@*android:dimen/car_padding_1" + android:text="@string/unlock_dialog_message_default" + android:textSize="@*android:dimen/car_body4_size" + android:textColor="@color/unlock_dialog_message_text_default"/> + + <Button + android:id="@+id/enter_pin_button" + android:layout_marginTop="@*android:dimen/car_padding_1" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/unlock_dialog_button_text_pin" + style="@style/UnlockDialogButton"/> + </LinearLayout> +</FrameLayout>
\ No newline at end of file diff --git a/packages/CarSystemUI/res/values/colors_car.xml b/packages/CarSystemUI/res/values/colors_car.xml index 69ab3f3cf957..0a3f7aa92d84 100644 --- a/packages/CarSystemUI/res/values/colors_car.xml +++ b/packages/CarSystemUI/res/values/colors_car.xml @@ -26,4 +26,9 @@ <color name="car_user_switcher_add_user_background_color">@*android:color/car_dark_blue_grey_600</color> <color name="car_user_switcher_add_user_add_sign_color">@*android:color/car_body1_light</color> + <!-- colors for unlock dialog --> + <color name="unlock_dialog_background_color">#ff282a2d</color> + <color name="unlock_dialog_message_text_default">@*android:color/car_grey_400</color> + <color name="unlock_dialog_enter_pin_text_color">#ff66b5ff</color> + </resources> diff --git a/packages/CarSystemUI/res/values/dimens_car.xml b/packages/CarSystemUI/res/values/dimens_car.xml index 42a764959545..9cb09c942781 100644 --- a/packages/CarSystemUI/res/values/dimens_car.xml +++ b/packages/CarSystemUI/res/values/dimens_car.xml @@ -43,4 +43,10 @@ <!-- This must be the negative of car_user_switcher_container_height for the animation. --> <dimen name="car_user_switcher_container_anim_height">-420dp</dimen> + <!-- dimensions for the unlock dialog --> + <dimen name="unlock_dialog_width">500dp</dimen> + <dimen name="unlock_dialog_radius">16dp</dimen> + <dimen name="unlock_dialog_avatar_size">100dp</dimen> + <dimen name="unlock_dialog_progress_bar_size">140dp</dimen> + </resources> diff --git a/packages/CarSystemUI/res/values/integers_car.xml b/packages/CarSystemUI/res/values/integers_car.xml index be2cb0d8d900..862ba751aa55 100644 --- a/packages/CarSystemUI/res/values/integers_car.xml +++ b/packages/CarSystemUI/res/values/integers_car.xml @@ -31,5 +31,7 @@ <!--Percentage of the screen height, from the bottom, that a notification panel being peeked at will result in remaining closed the panel if released--> <integer name="notification_settle_close_percentage">80</integer> + <!-- The delay before the unlock dialog pops up --> + <integer name="unlock_dialog_delay_ms">3000</integer> </resources> diff --git a/packages/CarSystemUI/res/values/strings_car.xml b/packages/CarSystemUI/res/values/strings_car.xml index 83e91c57ccc3..717692e2f02f 100644 --- a/packages/CarSystemUI/res/values/strings_car.xml +++ b/packages/CarSystemUI/res/values/strings_car.xml @@ -29,4 +29,19 @@ <string name="user_add_user_message_setup">When you add a new user, that person needs to set up their space.</string> <!-- Message to inform user that the newly created user will have permissions to update apps for all other users. [CHAR LIMIT=100] --> <string name="user_add_user_message_update">Any user can update apps for all other users.</string> + <!-- Default messages displayed on the unlock dialog before unlock advertising started. [CHAR LIMIT=30]--> + <string name="unlock_dialog_message_default">Waiting\u2026</string> + <!-- Message to inform user that the IHU is looking for trusted device. [CHAR LIMIT=30] --> + <string name="unlock_dialog_message_start">Looking for trusted device\u2026</string> + + <!-- Cancel Button text for user who has PIN as security lock. [CHAR LIMIT=30] --> + <string name="unlock_dialog_button_text_pin">Enter PIN instead</string> + <!-- Cancel Button text for user who has Pattern as security lock. [CHAR LIMIT=30] --> + <string name="unlock_dialog_button_text_pattern">Enter Pattern instead</string> + <!-- Cancel Button text for user who has Password as security lock. [CHAR LIMIT=30] --> + <string name="unlock_dialog_button_text_password">Enter Password instead</string> + <!-- Default user name shows on unlock dialog --> + <string name="unlock_dialog_default_user_name">Default Name</string> + <!-- Default title for unlock dialog --> + <string name="unlock_dialog_title">Unlock Dialogue</string> </resources> diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml index 371bebdebc86..a9423bf6f260 100644 --- a/packages/CarSystemUI/res/values/styles.xml +++ b/packages/CarSystemUI/res/values/styles.xml @@ -46,4 +46,12 @@ <item name="android:layout_width">96dp</item> <item name="android:background">@drawable/nav_button_background</item> </style> + + <style name="UnlockDialogButton"> + <item name="android:background">?android:attr/selectableItemBackground</item> + <item name="android:textAlignment">center</item> + <item name="android:textColor">@color/unlock_dialog_enter_pin_text_color</item> + <item name="android:paddingHorizontal">16dp</item> + <item name="android:textAllCaps">false</item> + </style> </resources>
\ No newline at end of file diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index b1f979708615..97fbea6ea237 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -26,6 +26,7 @@ import android.car.Car; import android.car.drivingstate.CarDrivingStateEvent; import android.car.drivingstate.CarUxRestrictionsManager; import android.car.hardware.power.CarPowerManager.CarPowerStateListener; +import android.car.trust.CarTrustAgentEnrollmentManager; import android.content.Context; import android.content.res.Configuration; import android.graphics.PixelFormat; @@ -957,8 +958,12 @@ public class CarStatusBar extends StatusBar implements UserSwitcherController userSwitcherController = Dependency.get(UserSwitcherController.class); if (userSwitcherController.useFullscreenUserSwitcher()) { + Car car = Car.createCar(mContext); + CarTrustAgentEnrollmentManager enrollmentManager = (CarTrustAgentEnrollmentManager) car + .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE); mFullscreenUserSwitcher = new FullscreenUserSwitcher(this, - mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext); + mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), + enrollmentManager, mContext); } else { super.createUserSwitcher(); } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java new file mode 100644 index 000000000000..78bb1bcf24a8 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.car; + +import android.app.admin.DevicePolicyManager; +import android.bluetooth.BluetoothAdapter; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.PixelFormat; +import android.os.Handler; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.internal.widget.LockPatternUtils; +import com.android.systemui.R; + +/** + * A helper class displays an unlock dialog and receives broadcast about detecting trusted device + * & unlocking state to show the appropriate message on the dialog. + */ +class CarTrustAgentUnlockDialogHelper extends BroadcastReceiver{ + private static final String TAG = CarTrustAgentUnlockDialogHelper.class.getSimpleName(); + + private final Context mContext; + private final WindowManager mWindowManager; + private final UserManager mUserManager; + private final WindowManager.LayoutParams mParams; + /** + * Not using Dialog because context passed from {@link FullscreenUserSwitcher} is not an + * activity. + */ + private final View mUnlockDialog; + private final TextView mUnlockingText; + private final Button mButton; + private final IntentFilter mFilter; + private int mUid; + private boolean mIsDialogShowing; + private OnHideListener mOnHideListener; + + CarTrustAgentUnlockDialogHelper(Context context) { + mContext = context; + mUserManager = mContext.getSystemService(UserManager.class); + mWindowManager = mContext.getSystemService(WindowManager.class); + mParams = createLayoutParams(); + mFilter = getIntentFilter(); + + mParams.packageName = mContext.getPackageName(); + mParams.setTitle(mContext.getString(R.string.unlock_dialog_title)); + + mUnlockDialog = LayoutInflater.from(mContext).inflate( + R.layout.trust_agent_unlock_dialog, null); + mUnlockDialog.setLayoutParams(mParams); + + mUnlockingText = mUnlockDialog.findViewById(R.id.unlocking_text); + mButton = mUnlockDialog.findViewById(R.id.enter_pin_button); + mButton.setOnClickListener(v -> { + hideUnlockDialog(/* notifyOnHideListener= */true); + // TODO(b/138250105) Stop unlock advertising + }); + + BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + if (bluetoothAdapter != null + && bluetoothAdapter.getLeState() == BluetoothAdapter.STATE_BLE_ON) { + mUnlockingText.setText(R.string.unlock_dialog_message_start); + } + } + + /** + * This filter is listening on: + * {@link BluetoothAdapter#ACTION_BLE_STATE_CHANGED} for starting unlock advertising; + * {@link Intent#ACTION_USER_UNLOCKED} for IHU unlocked + */ + private IntentFilter getIntentFilter() { + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_BLE_STATE_CHANGED); + filter.addAction(Intent.ACTION_USER_UNLOCKED); + return filter; + } + + /** + * Show dialog for the given user + */ + void showUnlockDialog(int uid, OnHideListener listener) { + showUnlockDialogAfterDelay(uid, 0, listener); + } + + /** + * Show dialog for the given user after the certain time of delay has elapsed + * + * @param uid the user to unlock + * @param listener listener that listens to dialog hide + */ + void showUnlockDialogAfterDelay(int uid, OnHideListener listener) { + long delayMillis = mContext.getResources().getInteger(R.integer.unlock_dialog_delay_ms); + showUnlockDialogAfterDelay(uid, delayMillis, listener); + } + + /** + * Show dialog for the given user after the supplied delay has elapsed + */ + private void showUnlockDialogAfterDelay(int uid, long delayMillis, OnHideListener listener) { + setUid(uid); + mOnHideListener = listener; + if (!mIsDialogShowing) { + logd("Receiver registered"); + mContext.registerReceiverAsUser(this, UserHandle.ALL, mFilter, + /* broadcastPermission= */ null, + /* scheduler= */ null); + new Handler().postDelayed(() -> { + if (!mUserManager.isUserUnlocked(uid)) { + logd("Showed unlock dialog for user: " + uid + " after " + delayMillis + + " delay."); + mWindowManager.addView(mUnlockDialog, mParams); + } + }, delayMillis); + } + mIsDialogShowing = true; + } + + private void setUid(int uid) { + mUid = uid; + TextView userName = mUnlockDialog.findViewById(R.id.user_name); + userName.setText(mUserManager.getUserInfo(mUid).name); + ImageView avatar = mUnlockDialog.findViewById(R.id.avatar); + avatar.setImageBitmap(mUserManager.getUserIcon(mUid)); + setButtonText(); + } + + private void hideUnlockDialog(boolean notifyOnHideListener) { + if (!mIsDialogShowing) { + return; + } + mWindowManager.removeView(mUnlockDialog); + logd("Receiver unregistered"); + mContext.unregisterReceiver(this); + if (notifyOnHideListener && mOnHideListener != null) { + mOnHideListener.onHide(); + } + mIsDialogShowing = false; + } + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action == null) { + return; + } + switch (action) { + case BluetoothAdapter.ACTION_BLE_STATE_CHANGED: + logd("Received ACTION_BLE_STATE_CHANGED"); + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); + if (state == BluetoothAdapter.STATE_BLE_ON) { + logd("Received BLE_ON"); + mUnlockingText.setText(R.string.unlock_dialog_message_start); + } + break; + case Intent.ACTION_USER_UNLOCKED: + int uid = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + if (uid == mUid) { + logd("IHU unlocked"); + hideUnlockDialog(/* notifyOnHideListener= */false); + } else { + Log.e(TAG, "Received ACTION_USER_UNLOCKED for unexpected uid: " + uid); + } + break; + default: + Log.e(TAG, "Encountered unexpected action when attempting to set " + + "unlock state message: " + action); + } + } + + // Set button text based on security lock type + private void setButtonText() { + LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext); + int passwordQuality = lockPatternUtils.getActivePasswordQuality(mUid); + switch (passwordQuality) { + // PIN + case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: + // Pattern + case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: + mButton.setText(R.string.unlock_dialog_button_text_pattern); + break; + // Password + case DevicePolicyManager.PASSWORD_QUALITY_MANAGED: + mButton.setText(R.string.unlock_dialog_button_text_password); + break; + default: + Log.e(TAG, "Encountered unexpected security type when attempting to set " + + "button text:" + passwordQuality); + } + } + + private WindowManager.LayoutParams createLayoutParams() { + return new WindowManager.LayoutParams( + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, + WindowManager.LayoutParams.FLAG_FULLSCREEN + | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS + | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, + PixelFormat.TRANSLUCENT + ); + } + + private void logd(String message) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, message); + } + } + + /** + * Listener used to notify when the dialog is hidden + */ + interface OnHideListener { + void onHide(); + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java index 0a167d9acf98..7cd6adbb3952 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java @@ -18,29 +18,60 @@ package com.android.systemui.statusbar.car; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.car.trust.CarTrustAgentEnrollmentManager; +import android.car.userlib.CarUserManagerHelper; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.UserInfo; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.Log; import android.view.View; import android.view.ViewStub; import androidx.recyclerview.widget.GridLayoutManager; import com.android.systemui.R; +import com.android.systemui.statusbar.car.UserGridRecyclerView.UserRecord; /** * Manages the fullscreen user switcher. */ public class FullscreenUserSwitcher { + private static final String TAG = FullscreenUserSwitcher.class.getSimpleName(); + // Because user 0 is headless, user count for single user is 2 + private static final int NUMBER_OF_BACKGROUND_USERS = 1; private final UserGridRecyclerView mUserGridView; private final View mParent; private final int mShortAnimDuration; private final CarStatusBar mStatusBar; + private final Context mContext; + private final UserManager mUserManager; + private final CarTrustAgentEnrollmentManager mEnrollmentManager; + private CarTrustAgentUnlockDialogHelper mUnlockDialogHelper; + private UserGridRecyclerView.UserRecord mSelectedUser; + private CarUserManagerHelper mCarUserManagerHelper; + private final BroadcastReceiver mUserUnlockReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "user 0 is unlocked, SharedPreference is accessible."); + } + showDialogForInitialUser(); + mContext.unregisterReceiver(mUserUnlockReceiver); + } + }; + - public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) { + public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, + CarTrustAgentEnrollmentManager enrollmentManager, Context context) { mStatusBar = statusBar; mParent = containerStub.inflate(); - // Hide the user grid by default. It will only be made visible by clicking on a cancel - // button in a bouncer. - hide(); + mEnrollmentManager = enrollmentManager; + mContext = context; + View container = mParent.findViewById(R.id.container); // Initialize user grid. @@ -50,9 +81,51 @@ public class FullscreenUserSwitcher { mUserGridView.setLayoutManager(layoutManager); mUserGridView.buildAdapter(); mUserGridView.setUserSelectionListener(this::onUserSelected); + mCarUserManagerHelper = new CarUserManagerHelper(context); + mUnlockDialogHelper = new CarTrustAgentUnlockDialogHelper(mContext); + mUserManager = mContext.getSystemService(UserManager.class); mShortAnimDuration = container.getResources() .getInteger(android.R.integer.config_shortAnimTime); + IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED); + if (mUserManager.isUserUnlocked(UserHandle.USER_SYSTEM)) { + // User0 is unlocked, switched to the initial user + showDialogForInitialUser(); + } else { + // listen to USER_UNLOCKED + mContext.registerReceiverAsUser(mUserUnlockReceiver, + UserHandle.getUserHandleForUid(UserHandle.USER_SYSTEM), + filter, + /* broadcastPermission= */ null, + /* scheduler */ null); + } + } + + private void showDialogForInitialUser() { + int initialUser = mCarUserManagerHelper.getInitialUser(); + UserInfo initialUserInfo = mUserManager.getUserInfo(initialUser); + mSelectedUser = new UserRecord(initialUserInfo, + /* isStartGuestSession= */ false, + /* isAddUser= */ false, + /* isForeground= */ true); + // For single user without trusted device, hide the user switcher. + if (!hasMultipleUsers() && !hasTrustedDevice(initialUser)) { + dismissUserSwitcher(); + return; + } + // Show unlock dialog for initial user + if (hasTrustedDevice(initialUser)) { + mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser, + () -> dismissUserSwitcher()); + } + } + + /** + * Check if there is only one possible user to login in. + * In a Multi-User system there is always one background user (user 0) + */ + private boolean hasMultipleUsers() { + return mUserManager.getUserCount() > NUMBER_OF_BACKGROUND_USERS + 1; } /** @@ -77,14 +150,33 @@ public class FullscreenUserSwitcher { } /** - * Every time user clicks on an item in the switcher, we hide the switcher, either - * gradually or immediately. + * Every time user clicks on an item in the switcher, if the clicked user has no trusted device, + * we hide the switcher, either gradually or immediately. + * + * If the user has trusted device, we show an unlock dialog to notify user the unlock state. + * When the unlock dialog is dismissed by user, we hide the unlock dialog and the switcher. * - * We dismiss the entire keyguard if user clicked on the foreground user (user we're already - * logged in as). + * We dismiss the entire keyguard when we hide the switcher if user clicked on the foreground + * user (user we're already logged in as). */ private void onUserSelected(UserGridRecyclerView.UserRecord record) { - if (record.mIsForeground) { + mSelectedUser = record; + if (hasTrustedDevice(record.mInfo.id)) { + mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, () -> dismissUserSwitcher()); + return; + } + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "no trusted device enrolled for uid: " + record.mInfo.id); + } + dismissUserSwitcher(); + } + + private void dismissUserSwitcher() { + if (mSelectedUser == null) { + Log.e(TAG, "Request to dismiss user switcher, but no user selected"); + return; + } + if (mSelectedUser.mIsForeground) { hide(); mStatusBar.dismissKeyguard(); return; @@ -106,4 +198,8 @@ public class FullscreenUserSwitcher { }); } + + private boolean hasTrustedDevice(int uid) { + return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty(); + } } |