Android SystemUI组件(10)禁用/重启锁屏流程分析

news2024/10/4 4:48:20

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


本章关键点总结 & 说明:

说明:本章节持续迭代之前章节的思维导图,主要关注左侧上方锁屏分析部分 应用入口处理流程解读 即可。

在 Android 系统中,禁用锁屏(Keyguard)通常需要 DISABLE_KEYGUARD 权限。但是,这个权限属于签名权限,只能由系统应用或者具有系统签名的应用使用,比如Setting、Launcher等。

对于普通应用来说,通常不允许禁用锁屏,因为这会降低设备的安全性。然而,有些情况下,比如在设置应用或者需要解锁功能的其他预装应用中,可能需要这个权限。在 AndroidManifest.xml 文件中声明权限的示例:

<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />

但是,即使声明了这个权限,普通应用也无法获得这个权限,系统会忽略它们的请求。对于系统应用或者具有系统权限的应用,使用 KeyguardManager 禁用锁屏和重新启用锁屏的代码如下:

//获取系统服务
KeyguardManager keyguardManager = (KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE);

//创建一个KeyguardLock对象,这个对象提供了控制锁屏(Keyguard)行为的方法。
//参数"UNIQUE_LOCK_INSTANCE"是一个标识符,用于区分不同的锁屏控制实例
KeyguardManager.KeyguardLock keyguardLock = keyguardManager.newKeyguardLock("UNIQUE_LOCK_INSTANCE");

//常用操作:禁用锁屏
keyguardLock.disableKeyguard();

//常用操作:重新启用锁屏
keyguardLock.reenableKeyguard();

接下来从KeyguardManager的2个关键API disableKeyguard和reenableKeyguard来继续分析。

1 disableKeyguard和reenableKeyguard方法入手分析

disableKeyguard和reenableKeyguard的代码实现如下:

public class KeyguardManager {
    private IWindowManager mWM;
    private ITrustManager mTrustManager;
    //...
    public void disableKeyguard() {
        try {
            mWM.disableKeyguard(mToken, mTag);
        } catch (RemoteException ex) {
        }
    }

    public void reenableKeyguard() {
        try {
            mWM.reenableKeyguard(mToken);
        } catch (RemoteException ex) {
        }
    }
    //...
}

IWindowManager是一个Binder接口,它定义了一系列管理窗口的方法。在Android系统中,服务通常是通过Binder IPC(进程间通信)机制进行通信的。IWindowManager接口的实现类是WindowManagerService,它运行在系统的服务端,并管理所有窗口的状态和行为。

KeyguardManagerdisableKeyguard()reenableKeyguard()方法被调用时,它们会通过mWM(即IWindowManager的实例)向WindowManagerService发送请求,以禁用或重新启用锁屏。具体代码实现如下:

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    static final String TAG = "WindowManager";
    //...
    @Override
    public void disableKeyguard(IBinder token, String tag) {
        //权限检查
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
            != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Requires DISABLE_KEYGUARD permission");
        }

        if (token == null) {
            throw new IllegalArgumentException("token == null");
        }
        
        mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
                KeyguardDisableHandler.KEYGUARD_DISABLE, new Pair<IBinder, String>(token, tag)));
    }

    @Override
    public void reenableKeyguard(IBinder token) {
        //权限检查
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
            != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Requires DISABLE_KEYGUARD permission");
        }

        if (token == null) {
            throw new IllegalArgumentException("token == null");
        }

        mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
                KeyguardDisableHandler.KEYGUARD_REENABLE, token));
    }
    //...
}

针对这KeyguardDisableHandler的sendMessage的消息处理,代码实现如下:

public class KeyguardDisableHandler extends Handler {
    private static final String TAG = "KeyguardDisableHandler";
    //...
    // Message.what constants
    static final int KEYGUARD_DISABLE = 1;
    static final int KEYGUARD_REENABLE = 2;
    static final int KEYGUARD_POLICY_CHANGED = 3;
    //...
    @Override
    public void handleMessage(Message msg) {
        if (mKeyguardTokenWatcher == null) {
            mKeyguardTokenWatcher = new KeyguardTokenWatcher(this);
        }

        switch (msg.what) {
            case KEYGUARD_DISABLE:
                final Pair<IBinder, String> pair = (Pair<IBinder, String>)msg.obj;
                mKeyguardTokenWatcher.acquire(pair.first, pair.second);
                break;

            case KEYGUARD_REENABLE:
                mKeyguardTokenWatcher.release((IBinder)msg.obj);
                break;

            case KEYGUARD_POLICY_CHANGED:
                mAllowDisableKeyguard = ALLOW_DISABLE_UNKNOWN;
                if (mKeyguardTokenWatcher.isAcquired()) {
                    // If we are currently disabled we need to know if the keyguard
                    // should be re-enabled, so determine the allow state immediately.
                    mKeyguardTokenWatcher.updateAllowState();
                    if (mAllowDisableKeyguard != ALLOW_DISABLE_YES) {
                        mPolicy.enableKeyguard(true);
                    }
                } else {
                    // lazily evaluate this next time we're asked to disable keyguard
                    mPolicy.enableKeyguard(true);
                }
                break;
        }
    }
    //...
}

接下来着重分析mKeyguardTokenWatcher.acquire和mKeyguardTokenWatcher.release的实现,代码具体实现如下:

class KeyguardTokenWatcher extends TokenWatcher {

    public KeyguardTokenWatcher(final Handler handler) {
        super(handler, TAG);
    }

    //更新是否允许禁用锁屏的状态。这是通过查询DevicePolicyManager服务来实现的,
    //它管理设备策略,包括密码质量要求等。
    public void updateAllowState() {
        DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
                Context.DEVICE_POLICY_SERVICE);
        if (dpm != null) {
            try {
                //检查当前用户是否有密码质量要求,如果返回PASSWORD_QUALITY_UNSPECIFIED,
                //则表示没有密码要求,允许禁用锁屏(ALLOW_DISABLE_YES);
                //否则,不允许禁用锁屏(ALLOW_DISABLE_NO)。
                mAllowDisableKeyguard = dpm.getPasswordQuality(null,
                        ActivityManagerNative.getDefault().getCurrentUser().id)
                        == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED ?
                                ALLOW_DISABLE_YES : ALLOW_DISABLE_NO;
            } catch (RemoteException re) {
                // Nothing much we can do
            }
        }
    }

    //获取到一个令牌时(请求禁用锁屏)
    @Override
    public void acquired() {
        if (mAllowDisableKeyguard == ALLOW_DISABLE_UNKNOWN) {
            updateAllowState();
        }
        if (mAllowDisableKeyguard == ALLOW_DISABLE_YES) {
            mPolicy.enableKeyguard(false);
        } else {
            Log.v(TAG, "Not disabling keyguard since device policy is enforced");
        }
    }

    //释放一个令牌时(请求要启用锁屏)
    @Override
    public void released() {
        mPolicy.enableKeyguard(true);
    }
}

这里的关键方法为:mPolicy.enableKeyguard,代码具体实现如下:

public class PhoneWindowManager implements WindowManagerPolicy {
    static final String TAG = "WindowManager";
    static final boolean DEBUG = false;
    //...
    /** {@inheritDoc} */
    @Override
    public void enableKeyguard(boolean enabled) {
        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.setKeyguardEnabled(enabled);
        }
    }
    //...
}

这里继续分析mKeyguardDelegate.setKeyguardEnabled,代码实现如下:

public class KeyguardServiceDelegate {
    public static final String KEYGUARD_PACKAGE = "com.android.systemui";
    public static final String KEYGUARD_CLASS = "com.android.systemui.keyguard.KeyguardService";

    private static final String TAG = "KeyguardServiceDelegate";
    private static final boolean DEBUG = true;
    protected KeyguardServiceWrapper mKeyguardService;

    //...
    public void setKeyguardEnabled(boolean enabled) {
        if (mKeyguardService != null) {
            mKeyguardService.setKeyguardEnabled(enabled);
        }
        mKeyguardState.enabled = enabled;
    }
    //...
}

注意:这里的mKeyguardState.enabled状态的目的是为了维护一个本地副本,以便快速检查锁屏(Keyguard)是否当前被启用或禁用,而不需要每次都调用KeyguardService来获取这个状态。接下来继续分析mKeyguardService.setKeyguardEnabled,代码实现如下:

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

继续分析mService.setKeyguardEnabled,代码实现如下:

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 setKeyguardEnabled(boolean enabled) {
            checkPermission();//权限检查
            mKeyguardViewMediator.setKeyguardEnabled(enabled);
        }
        //...
    }
    //...
}

KeyguardViewMediator的setKeyguardEnabled,终于到了真正实现功能的部分了。

2 功能实现 KeyguardViewMediator.setKeyguardEnabled方法

接下来继续分析代码,主要关注逻辑功能的实现,代码实现如下:

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;
    //...
    public void setKeyguardEnabled(boolean enabled) {
        synchronized (this) {
            // 设置锁屏是否被外部启用
            mExternallyEnabled = enabled;

            // 如果请求禁用锁屏(enabled为false)且锁屏当前正在显示
            if (!enabled && mShowing) {
                // 如果当前有正在进行的解锁操作,则忽略禁用请求
                if (mExitSecureCallback != null) {
                    return;
                }

                // 标记需要在锁屏重新启用时重新显示
                mNeedToReshowWhenReenabled = true;
                // 更新输入限制状态
                updateInputRestrictedLocked();
                // 隐藏锁屏
                hideLocked();
            } else if (enabled && mNeedToReshowWhenReenabled) {
                // 如果请求启用锁屏且之前有标记需要重新显示
                mNeedToReshowWhenReenabled = false;
                // 更新输入限制状态
                updateInputRestrictedLocked();

                // 如果当前有解锁操作的回调,则处理解锁结果
                if (mExitSecureCallback != null) {
                    try {
                        mExitSecureCallback.onKeyguardExitResult(false);
                    } catch (RemoteException e) {
                        Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
                    }
                    mExitSecureCallback = null;
                    // 重置状态
                    resetStateLocked();
                } else {
                    // 显示锁屏
                    showLocked(null);
                    // 标记正在等待锁屏变为可见状态
                    mWaitingUntilKeyguardVisible = true;
                    // 延迟发送消息,以便在超时后继续执行
                    mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_DRAWING, KEYGUARD_DONE_DRAWING_TIMEOUT_MS);
                    // 等待锁屏变为可见状态
                    while (mWaitingUntilKeyguardVisible) {
                        try {
                            wait();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                }
            }
        }
    }
    //...
}

这段代码的逻辑确保了锁屏的显示和隐藏能够根据请求和当前状态正确地进行,同时处理了解锁操作的回调和状态同步。整体逻辑可拆解为4个部分:

  1. 锁屏状态更新:首先,方法同步并更新锁屏的外部启用状态。

  2. 处理禁用请求:如果请求是禁用锁屏,并且锁屏当前正在显示,会检查是否有正在进行的解锁操作。如果没有,将标记需要在锁屏重新启用时重新显示,并隐藏锁屏。

  3. 处理启用请求:如果请求是启用锁屏,并且之前有标记需要重新显示,会清除该标记,并根据是否有解锁操作的回调来处理。如果有回调,会处理解锁结果并重置状态。如果没有回调,会显示锁屏,并等待锁屏变为可见状态。

  4. 等待锁屏可见:在启用锁屏后,通过发送延迟消息和等待机制确保锁屏已经绘制完成,避免在锁屏未完全显示时用户操作导致的问题。

接下来分析两个重要的锁屏功能API:显示锁屏showLocked 和 隐藏锁屏hideLocked。这部分代码向下的分析参考了文章Android SystemUI组件(07)锁屏KeyguardViewMediator分析 的后半部分。代码实现如下:

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;

    //...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);
    }

    private void hideLocked() {
        if (DEBUG) Log.d(TAG, "hideLocked");
        Message msg = mHandler.obtainMessage(HIDE);
        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();
    }

    private final Runnable mKeyguardGoingAwayRunnable = new Runnable() {
        @Override
        public void run() {
            try {
                // 通知窗口管理器锁屏正在消失
                mWM.keyguardGoingAway(
                        mStatusBarKeyguardViewManager.shouldDisableWindowAnimationsForUnlock(),
                        mStatusBarKeyguardViewManager.isGoingToNotificationShade());
            } catch (RemoteException e) {
                Log.e(TAG, "Error while calling WindowManager", e);
            }
        }
    };
    //handler关键处理方法handleHide
    private void handleHide() {
        synchronized (KeyguardViewMediator.this) {
            mHiding = true; // 标记锁屏正在隐藏

            // 如果锁屏当前正在显示并且没有被遮挡
            if (mShowing && !mOccluded) {
                // 如果还没有运行隐藏动画
                if (!mHideAnimationRun) {
                    // 开始执行预隐藏动画,并在动画结束后执行mKeyguardGoingAwayRunnable
                    mStatusBarKeyguardViewManager.startPreHideAnimation(mKeyguardGoingAwayRunnable);
                } else {
                    // 如果已经在运行隐藏动画,则直接执行mKeyguardGoingAwayRunnable
                    mKeyguardGoingAwayRunnable.run();
                }
            } else {
                // 如果锁屏没有显示,或者被遮挡,不依赖于WindowManager来启动退出动画
                // 直接处理开始退出动画
                handleStartKeyguardExitAnimation(
                        SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
                        mHideAnimation.getDuration());
            }
        }
    }
    //...
}

接下来总结下2个关键流程内容:

handleShow方法,它负责处理显示锁屏的逻辑。具体如下:

  1. 设置锁屏状态:如果系统已准备好,标记锁屏为显示状态,并调用mStatusBarKeyguardViewManager.show(options)来显示锁屏。
  2. 重置状态:重置锁屏完成挂起的状态,标记没有运行隐藏动画,更新活动锁屏状态,调整状态栏,记录用户活动事件。
  3. 播放声音:在锁屏显示后播放声音,这通常包括锁屏出现的声音。
  4. 释放WakeLock:释放在显示锁屏时持有的mShowKeyguardWakeLock,以允许设备在锁屏显示后进入休眠状态。
  5. 显示锁屏管理器:调用mKeyguardDisplayManager.show()来显示锁屏管理器。

handleShow方法的目的是确保在需要时显示锁屏界面,更新系统状态以反映锁屏的显示,并通过播放声音或振动向用户提供反馈,同时保持设备的安全性和良好的用户体验。

handleHide方法,它负责处理隐藏锁屏的逻辑。具体如下:

  1. 设置隐藏标志:将mHiding标志设置为true,表示锁屏正在被隐藏。
  2. 检查锁屏状态:检查锁屏是否正在显示并且没有被遮挡。
  3. 处理没有显示的锁屏:如果锁屏没有显示或者被遮挡,不依赖于WindowManager来启动退出动画,而是直接调用handleStartKeyguardExitAnimation方法来处理开始退出动画。

handleHide方法的目的是确保在锁屏隐藏时能够正确地执行动画和相关的清理工作。它根据锁屏的当前状态来决定是否启动动画,以及是否直接处理退出动画。这种方法确保了锁屏隐藏过程的平滑和一致性,同时避免了不必要的依赖和潜在的冲突。

接下来我们专注分析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;
	}
}

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

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++初学者指南-5.标准库(第二部分)–随机数生成

C初学者指南-5.标准库(第二部分)–随机数生成 文章目录 C初学者指南-5.标准库(第二部分)–随机数生成基本概念例子统一随机数布尔值&#xff08;“抛硬币”&#xff09;正态分布具有独立概率的整数 怎么做种子引擎使用自定义生成器 shuffle算法分布类型概述通用接口均匀分布采样…

虚拟机 VMware 安装 macOS

macOS 界面 MAC OS IOS下载&#xff1a; amacOS Monterey by Techrechard.comwmacOS Monterey by Techrechard.com 下载&#xff1a;Unlocker-v2.0.1-x64 Mac OS X 虚拟机中更改屏幕分辨率 终端输入命令&#xff1a; sudo defaults write /Library/Preferences/com.apple.w…

C++11bind、function、lambda详细讲解

一.lambda表达式 关于lambda表达式&#xff0c;我之前是详细讲过的&#xff0c;现在我们只来做重点讲解&#xff08;如果存在疑问可以回看我之前的作品&#xff09;。 固定格式&#xff1a; []()->返回值{};([capture-list] (parameters) mutable -> return-type { state…

UE行为树编辑器图文笔记

对UE的编辑器实现有点好奇&#xff0c;于是从比较熟悉的行为树编辑器着手分析。以下为阅读UE源码后的个人理解&#xff0c;如有错误请指正。 编辑器基础 扩展编辑器的几种方式 MenuBar 菜单栏ToolBar 工具栏DetailCustomization 自定义细节面板&#xff0c;支持两种方式&…

西安做网站如何打造出色的企业网站

西安做网站如何打造出色的企业网站 随着数字化时代的到来&#xff0c;企业网站已成为展示企业形象、传播品牌价值的重要平台。在西安&#xff0c;如何打造出色的企业网站呢&#xff1f;以下几点建议可以帮助企业在激烈的竞争中脱颖而出。 **1. 清晰的网站定位** 首先&#xff…

【Godot4.3】匀速和匀变速直线运动粒子

概述 本篇论述&#xff0c;如何用加速度在Godot中控制粒子运动。 匀速和匀变速直线运动的统一 以下是匀变速运动的速度和位移公式&#xff1a; v t v 0 a t x t v 0 t 1 2 a t 2 v_tv_0 at \\ x_tv_0t \frac{1}{2}at^2 vt​v0​atxt​v0​t21​at2 当a 0 时&#xf…

计算机科学英语词汇汇总(下)Computer Science English Complete Vocabulary )

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…

0基础学习QT——配置开发环境

大纲 安装Qt配置Visual Studio 2022安装插件配置 测试 Qt框架&#xff0c;以其跨平台、高性能以及丰富的UI组件库而著称&#xff0c;是开发图形用户界面应用程序的理想选择。Visual Studio 2022提供了对Qt项目的深度支持&#xff0c;包括智能代码提示、代码导航、调试工具等&am…

(14)MATLAB莱斯(Rician)衰落信道仿真4

文章目录 前言一、改写莱斯分布概率密度函数的理论值二、仿真代码三、仿真结果总结 前言 本文通过将接收信号总功率设置为1&#xff0c;重写了莱斯衰落信道上接收信号幅度的理论PDF式。然后用MATLAB代码生成了在具有不同莱斯因子K的Ricean平坦衰落信道下接收到的信号样本&…

容器适配器-stack、queue、priority_queue和仿函数

目录 1.什么是适配器 2.deque 1.简单了解结构 2.deque的缺陷 3.为什么选择deque作为stack和queue的底层默认容器 3.stack&#xff08;栈&#xff09; 4.queue&#xff08;队列&#xff09; 5.仿函数 6.priority_queue&#xff08;优先级队列&#xff09;&#xff08;堆…

PlantUML中的实体关系图

概述 实体关系图&#xff08;Entity Relationship Diagrams&#xff0c;ERD&#xff09;是一种被广泛用于数据库建模的图。 1976年美籍华裔计算机科学家陈品山&#xff08;Peter Chen&#xff09;首次提出了Entity Relationship Modeling&#xff08;实体关系建模&#xff09…

数据结构-单链表的反转

一直在路上 目录 前言一、普通方法二、头插法三、递归法总结 前言 本篇文章介绍反转单链表的三种方法&#xff0c;分别为普通方法、头插法、递归法。 一、普通方法 普通方法是从第一个结点开始反转&#xff0c;然后反转剩余的结点。 普通方法需要保存当前结点的前驱和后继&a…

DevExpress WinForms v24.1新版亮点:富文本编辑器、电子表格组件功能升级

DevExpress WinForms拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序&#xff0c;无论是Office风格的界面&#xff0c;还是分析处理大批量的业务数据&#xff0c;它都能轻松胜…

自动驾驶-轨迹拼接

在进行自动驾驶的规划之前&#xff0c;要确定当前帧轨迹规划的起点&#xff0c;这个起点常被误认为是当前车辆的位置&#xff0c;即每次以车辆的当前位置进行轨迹规划&#xff1b;其实不是这样的&#xff0c;直观上&#xff0c;这会导致本次次规划的轨迹同上次规划的轨迹之间是…

如何计算服务需要部署多少台机器?

写在前面 遇到流量激增的性能问题&#xff0c;相信绝大多数人的第一反应不是优化代码而是加机器&#xff01;比如隔壁微博一旦出现爆炸性吃瓜&#xff0c;就会紧急扩机器&#xff0c;防止自己服务被打挂&#xff08;虽然经常被打挂 这篇文章我们就来讲一下如何 计算出一个服务…

项目配置说明

文章目录 一、下载 vscode 并安装相应扩展1.1 下载 vscode1.2 安装扩展 二、git 项目三、git 提交流程3.1 确定要提交的代码 四、git 拉新流程 一、下载 vscode 并安装相应扩展 1.1 下载 vscode vscode 我已经发群里了&#xff0c;或者自己去官网下载也行 1.2 安装扩展 打开…

四舵轮车辆中的舵轮角度计算

对于四舵轮车辆&#xff0c;或者对角线安装的双舵轮车辆来说&#xff0c;当同时存在线速度与角速度的时候&#xff0c;它的两个轮子的角度值是不一样的&#xff0c;而它的角度值与其当时的瞬心相关&#xff08;机器人模型与ICR(Instantaneous Center of Rotation)&#xff09;。…

IP6537_C_30W20V--移动设备快充的得力助手,集成 14 种快充协议的降压 SoC

IP6537_C_30W20V是一款集成同步开关的降压转换器、支 持 14 种输出快充协议、支持 Type-C 输出和 USB PD2.0/PD3.0(PPS)协议的 SoC&#xff0c;为车载充电器、 快充适配器、智能排插提供完整的解决方案。 IP6537_C_30W20V支持 USB Type-C 或者 USB A 输出&#xff0c; 5V 输出功…

火语言RPA流程组件介绍--模拟键盘输入

&#x1f6a9;【组件功能】&#xff1a;在浏览器网页中使用键盘操作模拟输入值 配置预览 配置说明 按键间隔(ms) 支持T或# 输入仅支持整型 两次输入按键的间隔,单位毫秒 输入内容 支持T或# 默认FLOW输入项 需要输入的内容 超时时间 支持T或# 输入仅支持整型 输入的超时时…

我们的赞赏码

每一位粉丝的认可&#xff0c;都是我们前进的动力。欢迎为我们点赞、转发和分享&#xff0c;让我们一起传递美好与快乐&#xff01; 我们真诚地邀请您来赞赏我们&#xff0c;您的认可是我们前进的动力&#xff01; 赞赏我们只要0.99&#xff0c;让我们一起在CSDN增长知识&…