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

该系列文章总纲链接:专题分纲目录 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方法用于隐藏解锁界面。它会取消任何即将执行的显示操作,并根据传入的参数决定是销毁视图还是仅仅将其设置为不可见。

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

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

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

相关文章

AI学习记录 - L2正则化详细解释(权重衰减)

大白话&#xff1a; 通过让反向传播的损失值变得比原来更大&#xff0c;并且加入的损失值关联到神经网络全部权重的大小&#xff0c;当出现权重的平方变大的时候&#xff0c;也就是网络权重往更加负或者更加正的方向走的时候&#xff0c;损失就越大&#xff0c;从而控制极大正…

【PostgreSQL】PG数据库表“膨胀”粗浅学习

文章目录 1 为什么需要关注表膨胀&#xff1f;2 如何确定是否发生了表膨胀&#xff1f;2.1 通过查询表的死亡元组占比情况来判断膨胀率2.1.1 指定数据库和表名2.1.2 查询数据库里面所有表的膨胀情况 3 膨胀的原理3.1 什么是膨胀&#xff1f;膨胀率&#xff1f;3.2 哪些数据库元…

Unity Asset Store的默认下载位置及更改下载路径的方法

修改Unity Asset Store的默认下载路径 Unity Asset Store默认下载位置 Unity Asset Store里下载资源&#xff0c;默认是下载到C盘里的&#xff0c;如果你不想做C盘战士的话&#xff0c;记得将下载的资源转移到其他盘。 Unity商城默认下载路径是C:\用户\用户名&#xff08;一般…

晶体管最佳效率区域随频率逆时针旋转原因分析

晶体管最佳效率区域随频率逆时针旋转原因分析 在功率放大器的设计时&#xff0c;晶体管最佳区域随频率逆时针旋转。但是&#xff0c;对于一般的微带电路&#xff0c;匹配阻抗区域是随着频率顺时针旋转的&#xff08;也有称这个特性是Foster特性&#xff09;&#xff0c;因此功…

UE5学习笔记23-给角色添加血量,添加收到伤害的功能

零、一些游戏框架知识 1.UE5中包含游戏模式类(Game Mode)、游戏状态类(Game State)、玩家状态类(Player State)、玩家控制器类(Player Controller)、所有的可以被控制的实体或角色类(Pawn)、窗口类(HUD/Widget) Game Mode&#xff1a;存在在服务器上&#xff0c;当我们在客户端…

Redis实现每日签到(大数据量)

PHP语言使用Redis NoSQL服务器二进制数据类型实现大数据情况下签到功能 目录 问题 解决方式 封装签到类 功能调用 总结 问题 实现用户每日签到功能不难&#xff0c;但随着用户量上升之后&#xff0c;不论是存储还是判断对数据量来说都很麻烦&#xff1b;假如每天有100万用…

【STM32开发环境搭建】-4-在STM32CubeMX中新增Keil(MDK-ARM) 5的工程目录(包含指定路径的C和H文件)

案例背景&#xff1a; 由于Keil(MDK-ARM)5工程&#xff1a;DEMO_STM32F030C8T6.uvprojx是由STM32CubeMX工具生成的&#xff0c;如果我们在Keil工程中手动添加了一些c文件和h文件的Include Path包含路径&#xff0c;会在STM32CubeMX下一次生成uvprojx文件时&#xff0c;被删除&…

C语言复习概要(四)

本文 1. 操作符的分类算术操作符关系操作符逻辑操作符 2. 二进制制和进制转换二进制与十六进制的表示进制转换算法 3. 原码、反码和补码原码反码补码 1. 操作符的分类 C语言中的操作符种类繁多&#xff0c;常用的主要操作符可以按照其功能进行如下分类&#xff1a; 算术操作符…

Emergency Stop (ES)

文章目录 1. 介绍2. Feature List3. 紧急停止信号触发方式3.1 Port触发紧急停止信号3.2 SMU事件触发紧急停止信号3.3 软件触发紧急停止信号 4. 应用场景4.1 Port4.2 MSC 1. 介绍 Emergency Stop (ES)是Ifx System Control Units (SCU)六大模块之一。详细信息可以参考Infineon-…

毕设 大数据抖音短视频数据分析与可视化(源码)

文章目录 0 前言1 课题背景2 数据清洗3 数据可视化地区-用户观看时间分界线每周观看观看路径发布地点视频时长整体点赞、完播 4 进阶分析相关性分析留存率 5 深度分析客户价值判断 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕…

以太网交换安全:MAC地址表安全

一、MAC地址表安全 MAC地址表安全是网络安全中的一个重要方面&#xff0c;它涉及到网络设备的MAC地址表的管理和保护。以下是对MAC地址表安全的详细介绍&#xff1a; &#xff08;1&#xff09;基本概念 定义&#xff1a;MAC地址表是网络设备&#xff08;如交换机&#xff0…

阿里云域名注册购买和备案

文章目录 1、阿里云首页搜索 域名注册2、点击 控制台3、域名控制台 1、阿里云首页搜索 域名注册 2、点击 控制台 3、域名控制台

关系数据库和关系模型(1)

昨天补充了DBMS概况的存储管理层面&#xff0c;今天来详细介绍数据库管理系统中常见且应用广泛的关系型数据库&#xff08;简称关系数据库&#xff09;&#xff0c;比如postgreSQL、MySQL、Oracle Database。 关系模型 关系模型是数据库中如何进行存储和组织数据的理论基础。…

掌控物体运动艺术:图扑 Easing 函数实践应用

现如今&#xff0c;前端开发除了构建功能性的网站和应用程序外&#xff0c;还需要创建具有吸引力且尤为流畅交互的用户界面&#xff0c;其中动画技术在其中发挥着至关重要的作用。在数字孪生领域&#xff0c;动画的应用显得尤为重要。数字孪生技术通过精确模拟现实世界中的对象…

虚幻引擎GAS入门学习笔记(一)

虚幻引擎GAS入门(一) Gameplay Ability System&#xff08;GAS&#xff09; 是一个模块化且强大的框架&#xff0c;用于管理虚幻引擎中的游戏玩法逻辑。它的核心组成部分包括 Gameplay Ability&#xff08;定义和执行能力&#xff09;、Gameplay Effect&#xff08;应用和管理…

mit6824-01-MapReduce详解

文章目录 MapReduce简述编程模型执行流程执行流程排序保证Combiner函数Master数据结构 容错性Worker故障Master故障 性能提升定制分区函数局部性执行缓慢的worker(slow workers) 常见问题总结回顾参考链接 MapReduce简述 MapReduce是一个在多台机器上并行计算大规模数据的软件架…

Electron 主进程与渲染进程、预加载preload.js

在 Electron 中&#xff0c;主要控制两类进程&#xff1a; 主进程 、 渲染进程 。 Electron 应⽤的结构如下图&#xff1a; 如果需要更深入的了解electron进程&#xff0c;可以访问官网 流程模型 文档。 主进程 每个 Electron 应用都有一个单一的主进程&#xff0c;作为应用…

webpack插件 --- webpack-bundle-analyzer【查看包体积】

const UglifyJsPlugin require(uglifyjs-webpack-plugin) // 清除注释 const CompressionWebpackPlugin require(compression-webpack-plugin); // 开启压缩// 是否为生产环境 const isProduction process.env.NODE_ENV production; const { BundleAnalyzerPlugin } requi…

【AI大模型】使用Embedding API

一、使用OpenAI API 目前GPT embedding mode有三种&#xff0c;性能如下所示&#xff1a; 模型每美元页数MTEB得分MIRACL得分text-embedding-3-large9,61554.964.6text-embedding-3-small62,50062.344.0text-embedding-ada-00212,50061.031.4 MTEB得分为embedding model分类…

rabbitMQ 简单使用

安装 rabbitMQ 下载地址&#xff1a;rabbitmq-3.12.0 安装 windows rabbitMQ 需要的命令 进入 rabbitMQ 的 sbin 目录后 cmd &#xff08;需要管理员权限&#xff09; rabbitmq-plugins.bat enable rabbitmq_management随后重启 rabbitMQ #关闭服务 net stop rabbitmq #开…