Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ed34083b authored by Felipe Leme's avatar Felipe Leme Committed by Automerger Merge Worker
Browse files

Merge "Remove unlock dialog from sysui" into rvc-dev am: 94fbff18 am: f06152ba

Change-Id: I950f3780ca9568fc211f492688ac87698bfc0c15
parents 0ef155c9 f06152ba
Loading
Loading
Loading
Loading
+0 −268
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 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.car.userswitcher;

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.content.res.Resources;
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;
import com.android.systemui.dagger.qualifiers.Main;

import javax.inject.Inject;
import javax.inject.Singleton;

/**
 * A helper class displays an unlock dialog and receives broadcast about detecting trusted device
 * & unlocking state to show the appropriate message on the dialog.
 */
@Singleton
class CarTrustAgentUnlockDialogHelper extends BroadcastReceiver{
    private static final String TAG = CarTrustAgentUnlockDialogHelper.class.getSimpleName();

    private final Context mContext;
    private final Resources mResources;
    private final WindowManager mWindowManager;
    private final UserManager mUserManager;
    private final WindowManager.LayoutParams mParams;
    /**
     * Not using Dialog because context passed from {@link FullscreenUserSwitcherViewMediator}
     * is not an activity.
     */
    private final View mUnlockDialogLayout;
    private final TextView mUnlockingText;
    private final Button mButton;
    private final IntentFilter mFilter;
    private int mUid;
    private boolean mIsDialogShowing;
    private OnHideListener mOnHideListener;

    @Inject
    CarTrustAgentUnlockDialogHelper(Context context, @Main Resources resources,
            UserManager userManager, WindowManager windowManager) {
        mContext = context;
        mResources = resources;
        mUserManager = userManager;
        mWindowManager = windowManager;
        mParams = createLayoutParams();
        mFilter = getIntentFilter();

        mParams.packageName = mContext.getPackageName();
        mParams.setTitle(mContext.getString(R.string.unlock_dialog_title));

        mUnlockDialogLayout = LayoutInflater.from(mContext).inflate(
            R.layout.trust_agent_unlock_dialog, null);
        mUnlockDialogLayout.setLayoutParams(mParams);

        View dialogParent = mUnlockDialogLayout.findViewById(R.id.unlock_dialog_parent);
        dialogParent.setOnTouchListener((v, event)-> {
            hideUnlockDialog(/* dismissUserSwitcher= */ false);
            return true;
        });
        View unlockDialog = mUnlockDialogLayout.findViewById(R.id.unlock_dialog);
        unlockDialog.setOnTouchListener((v, event) -> {
            // If the person taps inside the unlock dialog, the touch event will be intercepted here
            // and the dialog will not exit
            return true;
        });
        mUnlockingText = mUnlockDialogLayout.findViewById(R.id.unlocking_text);
        mButton = mUnlockDialogLayout.findViewById(R.id.enter_pin_button);
        mButton.setOnClickListener(v -> {
            hideUnlockDialog(/* dismissUserSwitcher= */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 = mResources.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(mUnlockDialogLayout, mParams);
                }
            }, delayMillis);
        }
        mIsDialogShowing = true;
    }

    private void setUid(int uid) {
        mUid = uid;
        TextView userName = mUnlockDialogLayout.findViewById(R.id.user_name);
        userName.setText(mUserManager.getUserInfo(mUid).name);
        ImageView avatar = mUnlockDialogLayout.findViewById(R.id.avatar);
        avatar.setImageBitmap(mUserManager.getUserIcon(mUid));
        setButtonText();
    }

    private void hideUnlockDialog(boolean dismissUserSwitcher) {
        if (!mIsDialogShowing) {
            return;
        }
        mWindowManager.removeView(mUnlockDialogLayout);
        logd("Receiver unregistered");
        mContext.unregisterReceiver(this);
        if (mOnHideListener != null) {
            mOnHideListener.onHide(dismissUserSwitcher);
        }
        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 screen lock type
    private void setButtonText() {
        LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
        int passwordQuality = lockPatternUtils.getActivePasswordQuality(mUid);
        switch (passwordQuality) {
            // PIN
            case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
            case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
                mButton.setText(R.string.unlock_dialog_button_text_pin);
                break;
            // Pattern
            case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
                mButton.setText(R.string.unlock_dialog_button_text_pattern);
                break;
            // Password
            case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
            case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
            case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
                mButton.setText(R.string.unlock_dialog_button_text_password);
                break;
            default:
                Log.e(TAG, "Encountered unexpected screen lock type when attempting to set "
                        + "button text:" + passwordQuality);
        }
    }

    private WindowManager.LayoutParams createLayoutParams() {
        final WindowManager.LayoutParams attrs = 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
        );
        attrs.setFitInsetsTypes(0 /* types */);
        return attrs;
    }

    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(boolean dismissUserSwitcher);
    }
}
+7 −124
Original line number Original line Diff line number Diff line
@@ -16,23 +16,14 @@


package com.android.systemui.car.userswitcher;
package com.android.systemui.car.userswitcher;


import android.car.Car;
import android.car.trust.CarTrustAgentEnrollmentManager;
import android.car.userlib.CarUserManagerHelper;
import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;


import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
import com.android.systemui.R;
import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -52,9 +43,6 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator {
    private static final String TAG = FullscreenUserSwitcherViewMediator.class.getSimpleName();
    private static final String TAG = FullscreenUserSwitcherViewMediator.class.getSimpleName();


    private final Context mContext;
    private final Context mContext;
    private final UserManager mUserManager;
    private final CarServiceProvider mCarServiceProvider;
    private final CarTrustAgentUnlockDialogHelper mUnlockDialogHelper;
    private final CarStatusBarKeyguardViewManager mCarStatusBarKeyguardViewManager;
    private final CarStatusBarKeyguardViewManager mCarStatusBarKeyguardViewManager;
    private final Handler mMainHandler;
    private final Handler mMainHandler;
    private final StatusBarStateController mStatusBarStateController;
    private final StatusBarStateController mStatusBarStateController;
@@ -62,39 +50,12 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator {
    private final ScreenLifecycle mScreenLifecycle;
    private final ScreenLifecycle mScreenLifecycle;
    private final CarStatusBar mCarStatusBar;
    private final CarStatusBar mCarStatusBar;
    private final boolean mIsUserSwitcherEnabled;
    private final boolean mIsUserSwitcherEnabled;
    private final CarUserManagerHelper mCarUserManagerHelper;

    private CarTrustAgentEnrollmentManager mEnrollmentManager;
    private UserGridRecyclerView.UserRecord mSelectedUser;
    private final CarTrustAgentUnlockDialogHelper.OnHideListener mOnHideListener =
            dismissUserSwitcher -> {
                if (dismissUserSwitcher) {
                    dismissUserSwitcher();
                } else {
                    // Re-draw the parent view, otherwise the unlock dialog will not be removed
                    // from the screen immediately.
                    invalidateFullscreenUserSwitcherView();
                }
            };
    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);
        }
    };


    @Inject
    @Inject
    public FullscreenUserSwitcherViewMediator(
    public FullscreenUserSwitcherViewMediator(
            Context context,
            Context context,
            @Main Resources resources,
            @Main Resources resources,
            @Main Handler mainHandler,
            @Main Handler mainHandler,
            UserManager userManager,
            CarServiceProvider carServiceProvider,
            CarTrustAgentUnlockDialogHelper carTrustAgentUnlockDialogHelper,
            CarStatusBarKeyguardViewManager carStatusBarKeyguardViewManager,
            CarStatusBarKeyguardViewManager carStatusBarKeyguardViewManager,
            CarStatusBar carStatusBar,
            CarStatusBar carStatusBar,
            StatusBarStateController statusBarStateController,
            StatusBarStateController statusBarStateController,
@@ -105,21 +66,12 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator {
        mIsUserSwitcherEnabled = resources.getBoolean(R.bool.config_enableFullscreenUserSwitcher);
        mIsUserSwitcherEnabled = resources.getBoolean(R.bool.config_enableFullscreenUserSwitcher);


        mMainHandler = mainHandler;
        mMainHandler = mainHandler;
        mUserManager = userManager;

        mCarServiceProvider = carServiceProvider;
        mCarServiceProvider.addListener(
                car -> mEnrollmentManager = (CarTrustAgentEnrollmentManager) car.getCarManager(
                        Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE));


        mUnlockDialogHelper = carTrustAgentUnlockDialogHelper;
        mCarStatusBarKeyguardViewManager = carStatusBarKeyguardViewManager;
        mCarStatusBarKeyguardViewManager = carStatusBarKeyguardViewManager;
        mCarStatusBar = carStatusBar;
        mCarStatusBar = carStatusBar;
        mStatusBarStateController = statusBarStateController;
        mStatusBarStateController = statusBarStateController;
        mFullScreenUserSwitcherViewController = fullScreenUserSwitcherViewController;
        mFullScreenUserSwitcherViewController = fullScreenUserSwitcherViewController;
        mScreenLifecycle = screenLifecycle;
        mScreenLifecycle = screenLifecycle;

        mCarUserManagerHelper = new CarUserManagerHelper(mContext);
    }
    }


    @Override
    @Override
@@ -127,18 +79,6 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator {
        registerUserSwitcherShowListeners();
        registerUserSwitcherShowListeners();
        registerUserSwitcherHideListeners();
        registerUserSwitcherHideListeners();
        registerHideKeyguardListeners();
        registerHideKeyguardListeners();

        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),
                    new IntentFilter(Intent.ACTION_USER_UNLOCKED),
                    /* broadcastPermission= */ null,
                    /* scheduler= */ null);
        }
    }
    }


    private void registerUserSwitcherShowListeners() {
    private void registerUserSwitcherShowListeners() {
@@ -194,26 +134,16 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator {
    }
    }


    /**
    /**
     * Every time user clicks on an item in the switcher, if the clicked user has no trusted
     * Every time user clicks on an item in the switcher, we hide the switcher.
     * 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
     * We dismiss the entire keyguard if user clicked on the foreground user (user we're already
     * state.
     * logged in as).
     * When the unlock dialog is dismissed by user, we hide the unlock dialog and the switcher.
     * 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) {
    private void onUserSelected(UserGridRecyclerView.UserRecord record) {
        mSelectedUser = record;
        hide();
        if (record.mInfo != null) {
        if (record.mType == UserGridRecyclerView.UserRecord.FOREGROUND_USER) {
            if (hasScreenLock(record.mInfo.id) && hasTrustedDevice(record.mInfo.id)) {
            mCarStatusBar.dismissKeyguard();
                mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener);
                return;
            }
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "no trusted device enrolled for uid: " + record.mInfo.id);
            }
        }
        }
        dismissUserSwitcher();
    }
    }


    // We automatically dismiss keyguard unless user switcher is being shown above the keyguard.
    // We automatically dismiss keyguard unless user switcher is being shown above the keyguard.
@@ -234,53 +164,6 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator {
        }
        }
    }
    }


    private boolean hasScreenLock(int uid) {
        LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
        return lockPatternUtils.getCredentialTypeForUser(uid)
                != LockPatternUtils.CREDENTIAL_TYPE_NONE;
    }

    private boolean hasTrustedDevice(int uid) {
        if (mEnrollmentManager == null) { // car service not ready, so it cannot be available.
            return false;
        }
        return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty();
    }

    private void dismissUserSwitcher() {
        if (mSelectedUser == null) {
            Log.e(TAG, "Request to dismiss user switcher, but no user selected");
            return;
        }
        if (mSelectedUser.mType == UserGridRecyclerView.UserRecord.FOREGROUND_USER) {
            hide();
            mCarStatusBar.dismissKeyguard();
            return;
        }
        hide();
    }

    private void showDialogForInitialUser() {
        int initialUser = mCarUserManagerHelper.getInitialUser();
        UserInfo initialUserInfo = mUserManager.getUserInfo(initialUser);
        mSelectedUser = new UserGridRecyclerView.UserRecord(initialUserInfo,
                UserGridRecyclerView.UserRecord.FOREGROUND_USER);

        // If the initial user has screen lock and trusted device, display the unlock dialog on the
        // keyguard.
        if (hasScreenLock(initialUser) && hasTrustedDevice(initialUser)) {
            mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser,
                    mOnHideListener);
        } else {
            // If no trusted device, dismiss the keyguard.
            dismissUserSwitcher();
        }
    }

    private void invalidateFullscreenUserSwitcherView() {
        mFullScreenUserSwitcherViewController.invalidate();
    }

    private void hide() {
    private void hide() {
        mFullScreenUserSwitcherViewController.stop();
        mFullScreenUserSwitcherViewController.stop();
    }
    }