Android SystemUI组件(07)锁屏KeyguardViewMediator分析

该系列文章总纲链接:专题分纲目录 Android SystemUI组件


本章关键点总结 & 说明:

说明:本章节持续迭代之前章节的思维导图,主要关注左侧上方锁屏分析部分即可。

为了更好理解本文的内容,优先说明下SystemUI中与Keyguard(锁屏)相关的类,主要包括:

  • KeyguardViewMediator:这是一个中介类,它负责管理锁屏的状态,包括锁屏的显示和隐藏,以及与锁屏相关的各种事件处理。它还负责在系统启动时初始化锁屏界面,并在设备准备好时启动锁屏。
  • KeyguardService:这个类是锁屏服务的入口点,它接收来自系统其他部分的请求,如启动锁屏或结束锁屏动画。
  • StatusBarKeyguardViewManager:负责管理状态栏中的锁屏视图,包括显示和隐藏锁屏界面。
  • KeyguardUpdateMonitor:监控系统的各种状态变化,如电池状态、时间变化等,并在这些变化发生时更新锁屏界面。
  • KeyguardBouncer:负责显示解锁界面,包括图案、PIN、密码等解锁方式。它处理用户的解锁尝试,并在成功或失败时提供反馈。
  • KeyguardHostView:是锁屏界面的根视图,它承载了所有的解锁方式视图。
  • KeyguardSecurityContainer:包含解锁方式的容器,如图案、PIN、密码等。
  • KeyguardPatternViewKeyguardPINViewKeyguardPasswordView:这些类分别对应不同的解锁方式,它们实现了用户与锁屏界面的交互。
  • KeyguardManager:系统服务,用于管理系统锁屏的状态,如启用或禁用锁屏。

这些类共同工作,提供了一个完整的锁屏体验,包括显示锁屏界面、处理用户解锁尝试、以及在设备准备好时启动锁屏。这些类构成了锁屏功能的核心。

接下来我们从KeyguardViewMediator.start开始分析,一直到KeyguardViewMediator的关键逻辑:doKeyguardLocked方法的内部实现,想了解为什么要从这里开始分析的,可以查看文章:Android SystemUI组件(01)SystemUIService启动。

1 KeyguardViewMediator start启动分析

锁屏KeyguardViewMediator是在systemUI中启动的,从KeyguardViewMediator.start()开始分析。对应代码实现如下:

public class KeyguardViewMediator extends SystemUI {
    private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
    private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;

    private static final boolean DEBUG = KeyguardConstants.DEBUG;
    private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
    private final static boolean DBG_WAKE = false;

    private final static String TAG = "KeyguardViewMediator";
    //...
    @Override
    public void start() {
        synchronized (this) {
            setupLocked();//初始化与锁屏相关的状态和组件
        }
        //将当前实例(this)注册到一个组件管理系统中,以便其他部分的代码可以访问和使用这个实例
        putComponent(KeyguardViewMediator.class, this);
    }
    //...
}

其中关于putComponent方法具体解释如下:

  1. 组件管理putComponent方法通常是一个用于注册组件的工具方法。它将当前的KeyguardViewMediator实例存储在一个全局或共享的组件容器中,使得其他类或模块可以通过这个容器来获取这个实例。

  2. 依赖注入:这种做法类似于依赖注入(Dependency Injection),允许系统中的其他部分在需要时获取KeyguardViewMediator的实例,而不需要直接创建它。这样可以减少类之间的耦合,提高代码的可维护性。

  3. 方便访问:通过将KeyguardViewMediator实例放入组件管理系统,其他类可以方便地访问锁屏的相关功能和状态。例如,其他类可能会调用getComponent(KeyguardViewMediator.class)来获取当前的锁屏管理器实例,以便进行锁屏状态的检查或更新。

接下来我们详细分析代码setupLocked的实现,具体如下:

public class KeyguardViewMediator extends SystemUI {
    //...
    private void setupLocked() {
        // 初始化系统服务和锁屏相关的WakeLock
        mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        mWM = WindowManagerGlobal.getWindowManagerService();
        mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
        mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
        mShowKeyguardWakeLock.setReferenceCounted(false);

        // 注册一个广播接收器,用于处理延迟锁屏的动作
        mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(DELAYED_KEYGUARD_ACTION));

        // 初始化锁屏显示管理器和闹钟管理器服务
        mKeyguardDisplayManager = new KeyguardDisplayManager(mContext);
        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);

        // 获取锁屏更新监控实例和锁屏图案工具
        mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
        mLockPatternUtils = new LockPatternUtils(mContext);
        mLockPatternUtils.setCurrentUser(ActivityManager.getCurrentUser());

        // 根据设备设置和用户状态决定是否显示锁屏,并通知信任管理器锁屏显示状态已改变
        setShowingLocked(!shouldWaitForProvisioning() && !mLockPatternUtils.isLockScreenDisabled());
        mTrustManager.reportKeyguardShowingChanged();

        // 创建状态栏锁屏视图管理器,用于管理锁屏界面的显示和交互
        mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(mContext,
                mViewMediatorCallback, mLockPatternUtils);
        
        // 获取内容解析器,用于访问系统设置
        final ContentResolver cr = mContext.getContentResolver();

        // 检查屏幕是否已经开启
        mScreenOn = mPM.isScreenOn();

        // 加载锁屏声音等
        mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0);
        String soundPath = Settings.Global.getString(cr, Settings.Global.LOCK_SOUND);
        if (soundPath != null) {
            mLockSoundId = mLockSounds.load(soundPath, 1);
        }

        if (soundPath == null || mLockSoundId == 0) {
            Log.w(TAG, "failed to load lock sound from " + soundPath);
        }
        soundPath = Settings.Global.getString(cr, Settings.Global.UNLOCK_SOUND);
        if (soundPath != null) {
            mUnlockSoundId = mLockSounds.load(soundPath, 1);
        }

        if (soundPath == null || mUnlockSoundId == 0) {
            Log.w(TAG, "failed to load unlock sound from " + soundPath);
        }
        soundPath = Settings.Global.getString(cr, Settings.Global.TRUSTED_SOUND);
        if (soundPath != null) {
            mTrustedSoundId = mLockSounds.load(soundPath, 1);
        }

        if (soundPath == null || mTrustedSoundId == 0) {
            Log.w(TAG, "failed to load trusted sound from " + soundPath);
        }
        int lockSoundDefaultAttenuation = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_lockSoundVolumeDb);
        mLockSoundVolume = (float)Math.pow(10, (float)lockSoundDefaultAttenuation/20);

        // 加载锁屏隐藏动画资源
        mHideAnimation = AnimationUtils.loadAnimation(mContext,
                com.android.internal.R.anim.lock_screen_behind_enter);
    }
    //...
}

setupLocked()方法的主要目的是为KeyguardViewMediator的正常运行做好准备,确保所有必要的组件和状态都已初始化,以便在设备锁屏和解锁时能够正确响应用户的操作和系统事件。具体来说,关键部分主要包括:

  1. 初始化系统服务:该方法会获取系统服务,如PowerManagerWindowManagerTrustManager,这些服务在锁屏和解锁过程中是必不可少的。

  2. 创建锁屏状态监控:它会实例化KeyguardUpdateMonitor,用于监控与锁屏相关的各种状态变化,如电池状态、SIM卡状态等。

  3. 设置锁屏和解锁声音setupLocked()还会加载锁屏和解锁时的声音设置,以便在用户进行解锁操作时提供反馈。

  4. 注册广播接收器:该方法会注册一些广播接收器,以便监听特定的系统事件,如延迟锁屏等。

  5. 创建视图管理器:通过SystemUIFactory创建StatusBarKeyguardViewManager,用于管理锁屏界面的显示和隐藏。

2 开机锁屏流程分析

当设备启动或用户按下电源键时,系统均显示锁屏界面。这个过程涉及KeyguardViewMediator类,它负责协调锁屏的显示和隐藏。接下来分2个部分来解读,一个是从系统的SystemServer启动到KeyguardViewMediator.onSystemReady的过程解读,另一个是SystemServer的SystemReady执行后KeyguardViewMediator具体做了哪些事情。

2.1 从SystemServer启动到KeyguardViewMediator的onSystemReady方法执行

系统SystemServer启动后执行startCoreServices,这里会执行WindowManagerService的systemReady方法,代码实现如下:

public final class SystemServer {
    private static final String TAG = "SystemServer";
    //...
    private void startCoreServices() {
    	WindowManagerService wm = null;
    	//...
    	try {
            wm.systemReady();
        } catch (Throwable e) {
            reportWtf("making Window Manager Service ready", e);
        }
        //...
    }
    //...
}

这里调用了WindowManagerService的systemReady方法,代码具体实现如下:

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    static final String TAG = "WindowManager";
    //这里实际上mPolicy就是PhoneWindowManager对象
    final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();
    //...
    public void systemReady() {
        mPolicy.systemReady();
    }
    //...
}

这里实际上就是调用了PhoneWindowManager的systemReady方法,代码具体实现如下:

public class PhoneWindowManager implements WindowManagerPolicy {
    static final String TAG = "WindowManager";
    static final boolean DEBUG = false;
    //...
    @Override
    public void systemReady() {
        mKeyguardDelegate = new KeyguardServiceDelegate(mContext);
        mKeyguardDelegate.onSystemReady();

        readCameraLensCoverState();
        updateUiMode();
        synchronized (mLock) {
            updateOrientationListenerLp();
            mSystemReady = true;
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    updateSettings();
                }
            });
        }
    }
    //...
}

这里调用了mKeyguardDelegate的onSystemReady方法,代码具体实现如下:

public class KeyguardServiceDelegate {
    public static final String KEYGUARD_PACKAGE = "com.android.systemui";
    protected KeyguardServiceWrapper mKeyguardService;
    //...
    public void onSystemReady() {
        if (mKeyguardService != null) {
            mKeyguardService.onSystemReady();
        } else {
            mKeyguardState.systemIsReady = true;
        }
    }
    //...
}

这里调用了KeyguardServiceWrapper的onSystemReady方法,代码具体实现如下:

public class KeyguardServiceWrapper implements IKeyguardService {
    private KeyguardStateMonitor mKeyguardStateMonitor;
    private IKeyguardService mService;
    private String TAG = "KeyguardServiceWrapper";
    //...
    @Override // Binder interface
    public void onSystemReady() {
        try {
            mService.onSystemReady();
        } catch (RemoteException e) {
            Slog.w(TAG , "Remote Exception", e);
        }
    }
    //...
}

这里调用了KeyguardService的onSystemReady方法,代码具体实现如下:

public class KeyguardService extends Service {
    static final String TAG = "KeyguardService";
    static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
    //...
    private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {

        @Override // Binder interface
        public void addStateMonitorCallback(IKeyguardStateCallback callback) {
            checkPermission();
            mKeyguardViewMediator.addStateMonitorCallback(callback);
        }
        //...
        @Override // Binder interface
        public void onSystemReady() {
            checkPermission();
            mKeyguardViewMediator.onSystemReady();
        }
        //...
    }
}

这里调用了KeyguardViewMediator的onSystemReady方法,代码具体实现如下:

public class KeyguardViewMediator extends SystemUI {
    // 默认的锁屏显示超时延迟时间(30秒)
    private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
    // 锁屏完成挂起的超时时间(3秒)
    private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
    //...
    public void onSystemReady() {
        mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
        synchronized (this) {
            mSystemReady = true;
            // 注册更新监控的回调
            mUpdateMonitor.registerCallback(mUpdateCallback);
            
            // 检查是否使用弱生物识别(如指纹)且相关硬件已安装
            if (mLockPatternUtils.usingBiometricWeak()
                    && mLockPatternUtils.isBiometricWeakInstalled()) {
                // 如果使用弱生物识别,则禁用备用解锁方式
                mUpdateMonitor.setAlternateUnlockEnabled(false);
            } else {// 否则,启用备用解锁方式
                mUpdateMonitor.setAlternateUnlockEnabled(true);
            }
            // 执行锁屏逻辑
            doKeyguardLocked(null);
        }
        // 可能发送用户已出现的广播
        maybeSendUserPresentBroadcast();
    }
    //...
}

至此,执行到KeyguardViewMediator.onSystemReady的方法中。这里最关键的便是锁屏逻辑对应的doKeyguardLocked方法。接下来继续分析关键逻辑doKeyguardLocked方法。

2.2 KeyguardViewMediator关键逻辑:doKeyguardLocked方法

doKeyguardLocked的代码分析具体如下所示:

public class KeyguardViewMediator extends SystemUI {
    private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
    private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
    //...
    private void doKeyguardLocked(Bundle options) {
        // 检查是否允许外部控制锁屏的显示
        if (!mExternallyEnabled) {
            return;
        }

        // 如果锁屏已经显示,重置状态并返回
        if (mStatusBarKeyguardViewManager.isShowing()) {
            resetStateLocked();
            return;
        }

        // 检查是否需要SIM卡,如果需要但SIM卡缺失或被禁用,则不显示锁屏
        final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", false);
        final boolean absent = SubscriptionManager.isValidSubscriptionId(
                mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.ABSENT));
        final boolean disabled = SubscriptionManager.isValidSubscriptionId(
                mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.PERM_DISABLED));
        final boolean lockedOrMissing = mUpdateMonitor.isSimPinSecure()
                || ((absent || disabled) && requireSim);

        // 如果SIM卡锁定或缺失,并且设备还未完成初始设置向导,则不显示锁屏
        if (!lockedOrMissing && shouldWaitForProvisioning()) {
            return;
        }

        // 如果锁屏被禁用,并且SIM卡没有被锁定或缺失,则不显示锁屏
        if (mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {
            return;
        }

        // 如果设备设置了Vold密码,并且当前用户需要输入Vold密码,则不显示锁屏
        if (mLockPatternUtils.checkVoldPassword()) {
            setShowingLocked(false);
            hideLocked();
            return;
        }

        // 如果所有条件都满足,则显示锁屏
        showLocked(options);
    }
    //...
    //...handler 发送消息 SHOW/HIDE
    private void showLocked(Bundle options) {
        if (DEBUG) Log.d(TAG, "showLocked");
        // ensure we stay awake until we are finished displaying the keyguard
        mShowKeyguardWakeLock.acquire();
        Message msg = mHandler.obtainMessage(SHOW, options);
        mHandler.sendMessage(msg);
    }

    //...handler处理
    private Handler mHandler = new Handler(Looper.myLooper(), null, true /*async*/) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SHOW:
                    handleShow((Bundle) msg.obj);
                    break;
                case HIDE:
                    handleHide();
                    break;
                //...
            }
        }
    };
    //handler关键处理方法handleShow
    private void handleShow(Bundle options) {
        synchronized (KeyguardViewMediator.this) {
            // 如果系统还没有准备好,忽略显示锁屏的请求
            if (!mSystemReady) {
                return;
            }

            // 标记锁屏为显示状态
            setShowingLocked(true);
            // 调用状态栏锁屏视图管理器显示锁屏
            mStatusBarKeyguardViewManager.show(options);
            // 标记锁屏不是在隐藏状态
            mHiding = false;
            // 重置锁屏完成挂起的状态
            resetKeyguardDonePendingLocked();
            // 标记没有运行隐藏动画
            mHideAnimationRun = false;
            // 更新活动锁屏状态
            updateActivityLockScreenState();
            // 调整状态栏
            adjustStatusBarLocked();
            // 用户活动事件
            userActivity();

            // 在最后执行,以免延迟锁屏显示
            playSounds(true);

            // 释放锁屏显示时持有的WakeLock
            mShowKeyguardWakeLock.release();
        }
        // 显示锁屏管理器
        mKeyguardDisplayManager.show();
    }

这里调用了mStatusBarKeyguardViewManager的show方法,代码具体实现如下:

public class StatusBarKeyguardViewManager {
    //step1 显示锁屏
    public void show(Bundle options) {
        mShowing = true; // 标记锁屏为显示状态
        mStatusBarWindowManager.setKeyguardShowing(true); // 通知状态栏窗口管理器锁屏正在显示
        reset(); // 调用reset方法来重置锁屏状态
    }
    //step2 重置锁屏状态
    public void reset() {
        if (mShowing) {
            if (mOccluded) {
                mPhoneStatusBar.hideKeyguard(); // 如果锁屏被遮挡,隐藏锁屏
                mBouncer.hide(false /* destroyView */); // 隐藏解锁界面(Bouncer)
            } else {
                showBouncerOrKeyguard(); // 显示解锁界面或锁屏
            }
            updateStates(); // 更新锁屏状态
        }
    }
    //step3 显示解锁界面或锁屏
    private void showBouncerOrKeyguard() {
        if (mBouncer.needsFullscreenBouncer()) {
            mPhoneStatusBar.hideKeyguard(); // 需要全屏解锁界面时,隐藏锁屏
            mBouncer.show(true); // 显示解锁界面(Bouncer)
        } else {
            mPhoneStatusBar.showKeyguard(); // 不需要全屏解锁界面时,显示锁屏
            mBouncer.hide(false); // 隐藏解锁界面(Bouncer)
            mBouncer.prepare(); // 准备解锁界面(Bouncer)
        }
    }
}

接下来主要解读mBouncer.show和hide的实现及相关流程。主要以show方法(加载视图)为主。接下来继续分析KeyguardBouncer的show方法和hide方法流程,代码具体实现如下:

public class KeyguardBouncer {
    private Context mContext;
	//...
    private final Runnable mShowRunnable = new Runnable() {
        @Override
        public void run() {
            // 设置锁屏视图的可见性为可见
            mRoot.setVisibility(View.VISIBLE);
            // 恢复锁屏视图的活动状态
            mKeyguardView.onResume();
            // 开始锁屏视图的显示动画
            mKeyguardView.startAppearAnimation();
            // 清除锁屏即将显示的标志
            mShowingSoon = false;
        }
    };
    //...
    //加载及锁屏界面关键流程
	//step1 显示锁屏界面
    public void show() {
		// 确保锁屏视图已经创建,锁屏View的加载
		ensureView();
		// 如果锁屏视图已经是可见的或者即将显示,则不需要再次显示
		if (mRoot.getVisibility() == View.VISIBLE || mShowingSoon) {
			// 更新当前的安全方法,这在锁屏已经显示但当前安全方法发生变化时是必要的
			mKeyguardView.show();
			return;
		}

		// 尝试dismiss锁屏。如果没有设置安全模式,这将dismiss整个锁屏。
		// 如果需要认证,则显示解锁界面(Bouncer)。
		if (!mKeyguardView.dismiss()) {
			// 设置标志,表示锁屏即将显示
			mShowingSoon = true;

			// 在多个帧上分散工作
			mChoreographer.postCallbackDelayed(Choreographer.CALLBACK_ANIMATION, mShowRunnable,
					null, 48);
		}
	}
	//step2 确保锁屏视图已经创建
    private void ensureView() {
		//检查mRoot(锁屏界面的根视图)是否已经存在。
        if (mRoot == null) {
			//加载锁屏界面
            inflateView();
        }
    }
	//step3 加载锁屏界面
    private void inflateView() {
		// 如果之前已经添加过锁屏视图,先将其移除
		removeView();

		// 通过LayoutInflater从keyguard_bouncer布局文件中加载锁屏界面布局
		mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null);

		// 从加载的布局中获取KeyguardViewBase实例
		mKeyguardView = (KeyguardViewBase) mRoot.findViewById(R.id.keyguard_host_view);

		// 为锁屏视图设置锁图案工具,用于处理锁屏图案相关逻辑
		mKeyguardView.setLockPatternUtils(mLockPatternUtils);

		// 为锁屏视图设置ViewMediatorCallback,用于处理锁屏界面的回调事件
		mKeyguardView.setViewMediatorCallback(mCallback);

		// 将锁屏视图添加到容器视图中,确保它在容器的最后面
		mContainer.addView(mRoot, mContainer.getChildCount());

		// 初始时将锁屏视图的可见性设置为不可见
		mRoot.setVisibility(View.INVISIBLE);

		// 设置系统UI可见性,禁用HOME按钮,这样用户在锁屏界面上不会看到HOME按钮
		mRoot.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME);
	}
	//...
    //隐藏锁屏界面关键流程
    //step1 隐藏锁屏界面
	public void hide(boolean destroyView) {
		// 取消任何即将执行的显示锁屏的操作
		cancelShowRunnable();
		// 如果锁屏视图不为空,则进行清理
		if (mKeyguardView != null) {
			// 移除锁屏视图上的解散动作,即用户不再能通过这个视图解散锁屏
			mKeyguardView.setOnDismissAction(null);
			// 清理锁屏视图,这可能包括重置状态、停止动画等
			mKeyguardView.cleanUp();
		}
		// 如果传入的参数destroyView为true,则完全移除锁屏视图
		if (destroyView) {
			removeView();
		} else if (mRoot != null) {
			// 如果不销毁视图,只是将其设置为不可见
			mRoot.setVisibility(View.INVISIBLE);
		}
	}
    //step2 锁屏界面不显示,取消线程
	private void cancelShowRunnable() {
		// 从Choreographer中移除之前安排的动画帧更新回调
		mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, mShowRunnable, null);
		// 将mShowingSoon标志设置为false,表示锁屏界面不再即将显示
		mShowingSoon = false;
	}
}

最后,总结下整个流程,具体如下:

KeyguardViewMediator类中的doKeyguardLocked方法是用来控制锁屏界面显示的核心逻辑。这个方法会根据设备的当前状态和设置来决定是否显示锁屏。以下是该方法的概要流程:

  1. 检查锁屏状态:首先,方法会检查锁屏是否已经被外部控制(例如通过setKeyguardEnabled方法)禁用。如果锁屏已被禁用,则不会显示锁屏。

  2. 检查锁屏是否已显示:如果锁屏已经显示在屏幕上,方法会重置锁屏状态并返回,不会重复显示锁屏。

  3. 检查SIM卡状态:方法会检查SIM卡是否缺失或被禁用,如果设备需要SIM卡且SIM卡状态不正常,则不会显示锁屏。

  4. 检查设备设置:如果设备还未完成初始设置向导,且SIM卡被锁定或缺失,则不会显示锁屏。

  5. 检查锁屏设置:如果锁屏被设置为禁用,或者设备设置了Vold密码,方法会隐藏锁屏并返回。

  6. 显示锁屏:如果所有条件都满足,方法会调用showLocked方法来显示锁屏界面。

StatusBarKeyguardViewManager类中的show方法负责实际显示锁屏界面。它首先将锁屏的显示状态设置为true,然后调用reset方法来重置锁屏状态。reset方法会根据锁屏是否被遮挡来决定是显示解锁界面(Bouncer)还是锁屏界面。

KeyguardBouncer类中的show方法用于显示解锁界面(Bouncer)。如果需要全屏解锁界面,它会隐藏锁屏并显示解锁界面。否则,它会显示锁屏并隐藏解锁界面,并准备解锁界面以供用户输入。

KeyguardBouncer类中的hide方法用于隐藏解锁界面。它会取消任何即将执行的显示操作,并根据传入的参数决定是销毁视图还是仅仅将其设置为不可见。

这些方法共同工作,确保了锁屏界面能够在适当的时机显示或隐藏,同时提供了用户反馈和设备安全性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/883154.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CoreDNS实现跨集群service解析实践

CoreDNS实现跨集群service解析实践 背景介绍使用条件实现方案 CoreDNS是一款使用Go语言实现的专为云原生应用而生的DNS服务器。本文介绍CoreDNS在特定实际场景下的一种进阶使用实践,也许能为其他也在使用CoreDNS做服务发现的同学提供一些启发和思考。 背景介绍 在…

luceda ipkiss教程 76:设计光栅耦合器

案例分享:设计光栅耦合器 全部代码如下: from si_fab import all as pdk from ipkiss3 import all as i3 import numpy as npclass grating_coupler(i3.PCell):"""SOI grating coupler."""_name_prefix "grating_c…

mysql 05 InnoDB数据页结构

01.数据页结构的快速浏览 02.记录在页中的存储 在页的7个组成部分中,我们自己存储的记录会按照我们指定的 行格式 存储到 User Records 部分。但是在一开始生成页的时候,其实并没有 User Records 这个部分,每当我们插入一条记录&#xff0c…

单词记忆的化境:用思想的流水去淹没坚硬的石块

其实,鹅卵石通常都是很硬的。但是河底的石子,几乎大多都成了鹅卵石,它们被流水淹没,日复一日、夜以继日的冲刷着,没有了棱角。 在单词的记忆过程中,我们有太多的人,都有着不堪回首的往事&#x…

面试系列-携程暑期实习一面

Java 基础 1、Java 中有哪些常见的数据结构? 图片来源于:JavaGuide Java集合框架图 Java 中常见的数据结构包含了 List、Set、Map、Queue,在回答的时候,只要把经常使用的数据结构给说出来即可,不需要全部记住 如下&…

爬虫逆向学习(九):记录一个集cookie、请求参数、请求体、响应文本加密的站点反爬

此分享只用于学习用途,不作商业用途,若有冒犯,请联系处理 反爬前置信息 站点:aHR0cHM6Ly96d2Z3LmNxLmdvdi5jbi9pY2l0eS9pY2l0eS9lbmdpbmVlcmluZy9uYXZpZ2F0aW9u 接口:/icity/api-v2/cq.app.icity.engineering.Engine…

江科大51单片机

文章目录 led灯led点亮led闪烁流水灯 独立按键按键点灯按键消抖按键实现二进制流水灯按键实现流水灯 数码管静态数码管显示动态数码管显示 矩阵键盘定时器/中断串口通信led点阵屏DS1302实时时钟蜂鸣器AT24C02DS18B20LCD1602直流电机驱动AD/DA红外遥控 led灯 创建项目&#xff…

打印沙漏(最蠢的办法)

直接给代码&#xff0c;很好理解的 #include<bits/stdc.h> using namespace std; int s(int b){if(b<1)return 0;if(b2)return 1;for(int i3;i<sqrt(b);i){if(b%i0)return 0;}return 1; } int main(){int n;cin>>n;char c;cin>>c;vector<int>s;…

网络原理之IP协议(网络层)

目录 前言 什么是IP协议&#xff1f; IP协议的协议头格式 16位总长度&#xff08;字节数&#xff09; 16位标识、3位标志位和13位片偏移 8位生存时间 IP地址管理 1.动态分配IP 2.NAT机制&#xff08;网络地址转换&#xff09; NAT机制是如何工作的 NAT机制的优缺点…

解决启动docker desktop报The network name cannot be found的问题

现象 deploying WSL2 distributions ensuring main distro is deployed: checking if main distro is up to date: checking main distro bootstrap version: getting main distro bootstrap version: open \wsl$\docker-desktop\etc\wsl_bootstrap_version: The network name…

Windows驱动调试方法

单步调试驱动 驱动的调试不能直接在本机上进行&#xff0c;而是要放在虚拟机&#xff08;或其它设备&#xff09;中。这是因为在内核模式下&#xff0c;一个断点的触发将会停下整个系统而不只是单个进程。 在前面的文章里&#xff0c;使用了DbgPrint函数来进行日志的输出&…

基于python+django+vue的旅游景点数据分析系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

Elasticsearch 分片迁移与移除集群节点操作

Elasticsearch 分片迁移与移除集群节点操作 问题背景 在单台服务器上部署了 7 个 Elasticsearch 节点&#xff0c;分别为 es-node1 到 es-node7&#xff0c;端口从 9201 到 9207。每个节点都承载大量数据&#xff0c;但没有设置副本分片。由于多个节点共享同一台服务器的硬件…

论文阅读 - SWATTING Spambots: Real-time Detection of Malicious Bots on X

https://web.archive.org/web/20240523035749id_/https://dl.acm.org/doi/pdf/10.1145/3589335.3651564 目录 ABSTRACT INTRODUCTION METHODOLOGY 3 RESULTS ABSTRACT 在 X&#xff08;前身为 Twitter&#xff09;等社交网络平台上&#xff0c;垃圾邮件机器人的活动日益…

【SpringBoot整合Redis测试Redis集群案例】

1、第一步&#xff0c;创建springboot项目&#xff0c;并导入依赖 如图&#xff0c;创建项目遇到的第一个问题就是&#xff0c;当type选择maven&#xff0c;jdk选择1.8时&#xff0c;java部分没办法选择1.8的版本&#xff0c;这怎么办呢&#xff1f; 原因&#xff1a;搜了一下…

【windows 11 安装maven】

从下载网址下载maven 解压&#xff08;路径无中文&#xff09; 配置本地仓库&#xff0c;用来存储jar包 配置仓库路径 配置文件路径&#xff1a;./conf/settings.xml(conf文件夹中的settings.xml文件中&#xff09; 定位到53行修改 <!-- localRepository| The path to the l…

第 1 章:Vue 核心

1. Vue 简介 1.1. 官网 英文官网: https://vuejs.org/中文官网: https://cn.vuejs.org/&#xff1a;中文官网里面【教程】和【API】是比较重要的。用到api就去查询&#xff0c;实践当中记忆更牢靠。 风格指南&#xff1a;官方推荐写的一个代码风格cookbook&#xff1a;编写v…

从更底层的角度理解网站的访问过程

文章目录 1.示例&#xff0c;访问www.baidu.com是如何返回数据的1.输入www.baidu.com回车2.检查本机的C:\Windows\System32\drivers\etc\hosts配置文件夹下有没有这个域名对应的映射&#xff1a; 1.示例&#xff0c;访问www.baidu.com是如何返回数据的 1.输入www.baidu.com回车…

光伏开发:一分钟生成光伏项目报告

传统光伏项目报告的编制往往需要收集大量数据、进行复杂计算与分析&#xff0c;耗时长且易受人为因素影响。自动生成光伏项目报告&#xff0c;依托大数据、云计算、人工智能等先进信息技术&#xff0c;实现了对光伏项目关键参数的快速分析、评估与预测。 一、核心功能与流程 1…

【C++笔试强训】如何成为算法糕手Day2

学习编程就得循环渐进&#xff0c;扎实基础&#xff0c;勿在浮沙筑高台 循环渐进Forward-CSDN博客 目录 循环渐进Forward-CSDN博客 第一题&#xff1a;牛牛的快递 第二题&#xff1a;最小花费爬楼梯 第三题&#xff1a;数组中两个字符串的最小距离 补充0x3f3f3f3f 第一题…