Android SystemUI组件(09)唤醒亮屏 锁屏处理流程

news2024/10/3 8:30:30

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


本章关键点总结 & 说明:

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

Power按键的处理逻辑最终是由PhoneWindowManager来完成。想了解更多可参考输入子系统的相关文章。整理如下:

Android Framework 输入子系统(01)核心机制 inotify和epoll

Android Framework 输入子系统(02)核心机制 双向通信(socketpair+binder)

Android Framework 输入子系统(03)输入系统框架

Android Framework 输入子系统(04)InputReader解读

Android Framework 输入子系统(05)InputDispatcher解读

Android Framework 输入子系统(06)Global Key 一键启动 应用程序案例

Android Framework 输入子系统(07)APP建立联系

Android Framework 输入子系统(08)View基础(activity window decor view)

Android Framework 输入子系统(09)InputStage解读

Android Framework 输入子系统(10)Input命令解读

Android Framework 输入子系统(11)sendevent与getevent命令解读

本章我们只关注与Power按键相关的内容,InputManagerService处理的按键事件,最终会传递到PhoneWindowManager的interceptKeyBeforeQueueing方法中处理。我们就从这里入手逐步分析。

1 从PhoneWindowManager到PowerManagerService的处理

从PhoneWindowManager的interceptKeyBeforeQueueing方法入口开始分析,代码实现如下:

public class PhoneWindowManager implements WindowManagerPolicy {
    static final String TAG = "WindowManager";
    static final boolean DEBUG = false;
    //...
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        //...
        //表示屏幕是否点亮
        final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
        final boolean canceled = event.isCanceled();
        //获取按键编码
        final int keyCode = event.getKeyCode();
        final boolean keyguardActive = (mKeyguardDelegate == null ? false :
                                            (interactive ?
                                                isKeyguardShowingAndNotOccluded() :
                                                mKeyguardDelegate.isShowing()));
        boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
                || event.isWakeKey();
        //...

        // Handle special keys.
        switch (keyCode) {
            //...
            case KeyEvent.KEYCODE_POWER: {
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) {
                    //亮屏 step1 按下power按键,亮屏流程
                    interceptPowerKeyDown(event, interactive);
                } else {
                    //抬起power按键
                    interceptPowerKeyUp(event, interactive, canceled);
                }
                break;
            }

            case KeyEvent.KEYCODE_SLEEP: {
                result &= ~ACTION_PASS_TO_USER;
                if (!mPowerManager.isInteractive()) {
                    useHapticFeedback = false; // suppress feedback if already non-interactive
                }
                mPowerManager.goToSleep(event.getEventTime(),
                        PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                isWakeKey = false;
                break;
            }

            case KeyEvent.KEYCODE_WAKEUP: {
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = true;
                break;
            }
            //...
        }
        //...
        if (isWakeKey) {
            wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey);
        }

        return result;
    }

    private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
        //...
        mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
                || mScreenshotChordVolumeUpKeyTriggered;
        if (!mPowerKeyHandled) {
            //交互模式,表示现在亮屏,点击后会走灭屏流程
            //当然,灭屏sleep的流程更多的处理在interceptPowerKeyUp中
            if (interactive) {
                if (hasLongPressOnPowerBehavior()) {
                    Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageDelayed(msg,
                            ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
                }
            } else {
                //亮屏 step2:非交互模式,表示现在灭屏,点击后会走亮屏流程
                wakeUpFromPowerKey(event.getDownTime());
                final int maxCount = getMaxMultiPressPowerCount();

                if (maxCount <= 1) {
                    mPowerKeyHandled = true;
                } else {
                    mBeganFromNonInteractive = true;
                }
            }
        }
    }

    private void wakeUpFromPowerKey(long eventTime) {
        //亮屏 step3 调用wakeUp方法
        wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey);
    }

    private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode) {
        if (!wakeInTheaterMode && isTheaterModeEnabled()) {
            return false;
        }
        //亮屏 step4 走到PowerManagerService中的WakeUp方法中
        mPowerManager.wakeUp(wakeTime);
        return true;
    }
}

2 从PowerManagerService到Notifier的处理

从PhoneWindowManager的interceptKeyBeforeQueueing方法入口开始分析,最终会到达PowerManagerService的wakeUp方法(从PowerManager到PowerManagerService的调用就不在分析了,这个属于binder通信的范畴),对应的代码实现如下:

public final class PowerManagerService extends SystemService
        implements Watchdog.Monitor {
    private static final String TAG = "PowerManagerService";

    private static final boolean DEBUG = false;
    //...
    private final class BinderService extends IPowerManager.Stub {
        //...
        @Override // Binder call
        public void wakeUp(long eventTime) {
            if (eventTime > SystemClock.uptimeMillis()) {
                throw new IllegalArgumentException("event time must not be in the future");
            }

            mContext.enforceCallingOrSelfPermission(
                    android.Manifest.permission.DEVICE_POWER, null);

            final int uid = Binder.getCallingUid();
            final long ident = Binder.clearCallingIdentity();
            try {
                wakeUpInternal(eventTime, uid);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
        //...
    }
    //...
    private void wakeUpInternal(long eventTime, int uid) {
        synchronized (mLock) {
            if (wakeUpNoUpdateLocked(eventTime, uid)) {
                updatePowerStateLocked();
            }
        }
    }
    //...
    private boolean wakeUpNoUpdateLocked(long eventTime, int uid) {
        if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
                || !mBootCompleted || !mSystemReady) {
            return false;
        }

        Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp");
        try {
            switch (mWakefulness) {
                case WAKEFULNESS_ASLEEP:
                    Slog.i(TAG, "Waking up from sleep (uid " + uid +")...");
                    break;
                case WAKEFULNESS_DREAMING:
                    Slog.i(TAG, "Waking up from dream (uid " + uid +")...");
                    break;
                case WAKEFULNESS_DOZING:
                    Slog.i(TAG, "Waking up from dozing (uid " + uid +")...");
                    break;
            }

            mLastWakeTime = eventTime;
            setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);

            userActivityNoUpdateLocked(
                    eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_POWER);
        }
        return true;
    }
    //...
    private void setWakefulnessLocked(int wakefulness, int reason) {
        if (mWakefulness != wakefulness) {
            finishWakefulnessChangeLocked();

            mWakefulness = wakefulness;
            mWakefulnessChanging = true;
            mDirty |= DIRTY_WAKEFULNESS;
            mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
        }
    }
    //...
}

3 Notifier的处理

Notifier的处理包含2个层面,一个是发送SCREEN_ON的广播,通知其他子系统亮屏的消息。一个是通过PhoneWindowManager的处理来逐层执行对应回调onScreenTurnedOn方法。

3.1 从Notifier最终发送SCREEN_ON广播

针对发送广播的逻辑处理流程,代码逻辑流程如下:

final class Notifier {
    private static final String TAG = "PowerManagerNotifier";
    private static final boolean DEBUG = false;
    //...
    public void onWakefulnessChangeStarted(int wakefulness, int reason) {
        final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
        if (interactive) {
            handleWakefulnessChange(wakefulness, interactive, reason);
        } else {
            mLastReason = reason;
        }

        // Start input as soon as we start waking up or going to sleep.
        mInputManagerInternal.setInteractive(interactive);
    }
    //...
    private void handleWakefulnessChange(final int wakefulness, boolean interactive,
            final int reason) {
        //...
        // Handle changes in the overall interactive state.
        boolean interactiveChanged = false;
        synchronized (mLock) {
            // Broadcast interactive state changes.
            if (interactive) {
                // Waking up...
                interactiveChanged = (mActualInteractiveState != INTERACTIVE_STATE_AWAKE);
                if (interactiveChanged) {
                    mActualInteractiveState = INTERACTIVE_STATE_AWAKE;
                    mPendingWakeUpBroadcast = true;
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
                            mPolicy.wakingUp();
                        }
                    });
                    updatePendingBroadcastLocked();
                }
            } else {
                //...
            }
        }
        //...
    }
    //...
    private void updatePendingBroadcastLocked() {
        if (!mBroadcastInProgress
                && mActualInteractiveState != INTERACTIVE_STATE_UNKNOWN
                && (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
                        || mActualInteractiveState != mBroadcastedInteractiveState)) {
            mBroadcastInProgress = true;
            mSuspendBlocker.acquire();
            Message msg = mHandler.obtainMessage(MSG_BROADCAST);
            msg.setAsynchronous(true);
            mHandler.sendMessage(msg);
        }
    }
    //...
     private final class NotifierHandler extends Handler {
        public NotifierHandler(Looper looper) {
            super(looper, null, true /*async*/);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_BROADCAST:
                    sendNextBroadcast();
                    break;
                //...
            }
        }
    }
    //当设备从睡眠状态唤醒时,会发送 ACTION_SCREEN_ON 广播;当设备准备进入睡眠状态时,会发送 ACTION_SCREEN_OFF 广播。
    private void sendNextBroadcast() {
        final int powerState;
        synchronized (mLock) {
            //mBroadcastedInteractiveState相关处理
            //...
            mBroadcastStartTime = SystemClock.uptimeMillis();
            powerState = mBroadcastedInteractiveState;
        }
        EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1);
        // 根据电源状态发送相应的广播
        if (powerState == INTERACTIVE_STATE_AWAKE) {
            sendWakeUpBroadcast(); //发送唤醒广播
        } else {
            sendGoToSleepBroadcast(); //发送睡眠广播
        }
    }
    //...
    private void sendWakeUpBroadcast() {
        if (ActivityManagerNative.isSystemReady()) {
            // 发送广播关键API,参数解读如下:
            // mScreenOnIntent 是一个 Intent,包含了唤醒屏幕的信息
            // UserHandle.ALL 表示这个广播会发送给所有用户
            // mWakeUpBroadcastDone 是一个 BroadcastReceiver,用于在广播完成后接收回调
            // mHandler 是一个 Handler,用于处理广播完成后的回调
            // 0 是一个 flags,表示广播的权限
            // 最后的 null, null 是额外的参数,这里没有使用
            mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,
                    mWakeUpBroadcastDone, mHandler, 0, null, null);
        } else {
            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
            sendNextBroadcast();
        }
    }
    //...
}

3.2 PhoneWindowManager.wakingUp方法逐层处理亮屏回调方法

这里主要是从到PhoneWindowManager的wakingUp(policy.wakingUp)方法,再到各层的onScreenTurnedOn方法的处理逻辑流程,从onWakefulnessChangeStarted处开始,代码实现如下:

//Notifier处理
final class Notifier {
    private static final String TAG = "PowerManagerNotifier";
    private static final boolean DEBUG = false;
    //...
    public void onWakefulnessChangeStarted(int wakefulness, int reason) {
        final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
        if (interactive) {
            handleWakefulnessChange(wakefulness, interactive, reason);
        } else {
            mLastReason = reason;
        }

        // Start input as soon as we start waking up or going to sleep.
        mInputManagerInternal.setInteractive(interactive);
    }
    //...
    private void handleWakefulnessChange(final int wakefulness, boolean interactive,
            final int reason) {
        //...
        // Handle changes in the overall interactive state.
        boolean interactiveChanged = false;
        synchronized (mLock) {
            // Broadcast interactive state changes.
            if (interactive) {
                // Waking up...
                interactiveChanged = (mActualInteractiveState != INTERACTIVE_STATE_AWAKE);
                if (interactiveChanged) {
                    mActualInteractiveState = INTERACTIVE_STATE_AWAKE;
                    mPendingWakeUpBroadcast = true;
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
                            mPolicy.wakingUp();//这里是处理的关键
                        }
                    });
                    updatePendingBroadcastLocked();
                }
            } else {
                //...
            }
        }
        //...
    }
    //...
}

这里会调用到PhoneWindowManager的wakingUp方法。代码实现如下:

//phonewindowmanager
    @Override
    public void wakingUp() {
        synchronized (mLock) {
            mAwake = true;
            mKeyguardDrawComplete = false;
            if (mKeyguardDelegate != null) {
                mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
                mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, 1000);
            }

            updateWakeGestureListenerLp();
            updateOrientationListenerLp();
            updateLockScreenTimeout();
        }

        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.onScreenTurnedOn(mKeyguardDelegateCallback);
            // ... eventually calls finishKeyguardDrawn
        } else {
            finishKeyguardDrawn();
        }
    }

这里会调用到KeyguardDelegate的onScreenTurnedOn方法。代码实现如下:

//KeyguardDelegate
    public void onScreenTurnedOn(final ShowListener showListener) {
        if (mKeyguardService != null) {
            mKeyguardService.onScreenTurnedOn(new KeyguardShowDelegate(showListener));
        } else {
            // try again when we establish a connection
            Slog.w(TAG, "onScreenTurnedOn(): no keyguard service!");
            // This shouldn't happen, but if it does, show the scrim immediately and
            // invoke the listener's callback after the service actually connects.
            mShowListenerWhenConnect = showListener;
            showScrim();
        }
        mKeyguardState.screenIsOn = true;
    }

这里会调用到KeyguardServiceWrapper的onScreenTurnedOn方法。代码实现如下:

//wrapper
    @Override // Binder interface
    public void onScreenTurnedOn(IKeyguardShowCallback result) {
        try {
            mService.onScreenTurnedOn(result);
        } catch (RemoteException e) {
            Slog.w(TAG , "Remote Exception", e);
        }
    }

这里会调用到KeyguardService的onScreenTurnedOn方法。代码实现如下:

//KeyguardService
    //binder
        @Override // Binder interface
        public void onScreenTurnedOn(IKeyguardShowCallback callback) {
            checkPermission();
            mKeyguardViewMediator.onScreenTurnedOn(callback);
        }

这里会调用到KeyguardViewMediator的onScreenTurnedOn方法。这里才是真正的逻辑实现,代码实现如下:

//KeyguardViewMediator
public void onScreenTurnedOn(IKeyguardShowCallback callback) {
    synchronized (this) {
        // 将屏幕状态设置为开启
        mScreenOn = true;
        
        // 取消任何计划的延迟显示锁屏任务
        cancelDoKeyguardLaterLocked();
        
        // 如果提供了回调,通知屏幕已点亮
        if (callback != null) {
            notifyScreenOnLocked(callback);
        }
    }
    
    // 通知KeyguardUpdateMonitor屏幕已被点亮
    KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurnedOn();
    
    // 可能发送一个用户存在的广播,表示用户已经与设备交互
    maybeSendUserPresentBroadcast();
}

这里总结下,onScreenTurnedOn关键逻辑解读如下:

  1. 当屏幕再次打开时,这个方法会被调用。
  2. 首先会调用 cancelDoKeyguardLaterLocked 方法,这个方法会取消任何之前计划的延迟显示锁屏任务。
  3. 如果提供了回调接口,会通过 notifyScreenOnLocked 方法通知系统屏幕已经打开。
  4. KeyguardUpdateMonitor 会分派屏幕已打开的事件。
  5. maybeSendUserPresentBroadcast 方法可能会发送一个用户存在的广播,这通常意味着设备已解锁。

3.3 cancelDoKeyguardLaterLocked的处理

 cancelDoKeyguardLaterLocked的代码实现如下:

//KeyguardViewMediator
    private void cancelDoKeyguardLaterLocked() {
        mDelayedShowingSequence++;
    }

注意,在灭屏处理的时候有可能会调用到doKeyguardLaterLocked方法,代码实现如下:

//KeyguardViewMediator
    // 在锁定屏幕之前给予一定延迟的方法
	private void doKeyguardLaterLocked() {
		final ContentResolver cr = mContext.getContentResolver();

		// 读取显示设置中的屏幕关闭超时时间
		long displayTimeout = Settings.System.getInt(cr, SCREEN_OFF_TIMEOUT,
				KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT);

		// 读取安全设置中的锁屏超时时间
		final long lockAfterTimeout = Settings.Secure.getInt(cr,
				Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
				KEYGUARD_LOCK_AFTER_DELAY_DEFAULT);

		// 从设备策略管理器获取设备策略超时时间
		final long policyTimeout = mLockPatternUtils.getDevicePolicyManager()
				.getMaximumTimeToLock(null, mLockPatternUtils.getCurrentUser());

		long timeout;
		if (policyTimeout > 0) {
			// 如果设备策略超时时间有效,则取屏幕关闭超时时间和设备策略超时时间的较小值
			// 但不超过锁屏超时时间
			displayTimeout = Math.max(displayTimeout, 0); // 忽略负值
			timeout = Math.min(policyTimeout - displayTimeout, lockAfterTimeout);
		} else {
			// 如果没有设备策略限制,则使用锁屏超时时间
			timeout = lockAfterTimeout;
		}

		if (timeout <= 0) {
			// 如果计算出的延迟时间小于等于0,立即锁定屏幕
			mSuppressNextLockSound = true; // 抑制锁屏声音
			doKeyguardLocked(null); // 立即执行锁屏
		} else {
			// 如果延迟时间大于0,则设置一个闹钟,在将来的某个时间点锁定屏幕
			long when = SystemClock.elapsedRealtime() + timeout; // 计算闹钟触发的时间
			Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
			intent.putExtra("seq", mDelayedShowingSequence);
			// 创建一个PendingIntent,当闹钟触发时,系统将通过这个PendingIntent发送一个广播
			PendingIntent sender = PendingIntent.getBroadcast(mContext,
					0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
			mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender); // 设置闹钟
		}
	}

在这段代码逻辑的最后,会创建一个PendingIntent,当闹钟触发时,系统将通过这个PendingIntent发送一个广播DELAYED_KEYGUARD_ACTION。然后通过一个广播接收器来处理该广播消息,处理相关代码如下:

//KeyguardViewMediator
    //systemReady后开始执行
    private void setup() {
		//...
        注册接收DELAYED_KEYGUARD_ACTION广播消息的Receiver
		mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(DELAYED_KEYGUARD_ACTION));
		//...
	}
	
	// 定义一个广播接收器,用于接收特定动作的广播
	private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			// 检查接收到的Intent是否是延迟锁屏的动作
			if (DELAYED_KEYGUARD_ACTION.equals(intent.getAction())) {
				// 从Intent中获取序列号
				final int sequence = intent.getIntExtra("seq", 0);
				synchronized (KeyguardViewMediator.this) {
					// 检查接收到的序列号是否与当前的延迟显示序列号相匹配
					if (mDelayedShowingSequence == sequence) {
						// 如果序列号匹配,表示该广播是有效的,需要显示锁屏
						// 标记不播放锁屏音效,因为这是自动锁屏,不是由用户直接触发的
						mSuppressNextLockSound = true;
						// 调用doKeyguardLocked方法显示锁屏
						doKeyguardLocked(null);
					}
				}
			}
		}
	};
	//...

在Android系统中,这里的BroadcastReceiver是异步的,这意味着它可能会在任何时候接收到广播消息,而且接收顺序并不是完全可以预测的。有可能在屏幕关闭后,系统发送了延迟显示锁屏的广播,而这个广播在屏幕亮起并且onScreenTurnedOn方法被调用之后才被接收到。这里是可能发生的情况解读如下:

  1. 屏幕关闭onScreenTurnedOff被调用,可能会通过doKeyguardLaterLocked方法发送一个延迟显示锁屏的广播。

  2. 延迟广播发送:系统安排了一个延迟任务,将在一定时间后发送DELAYED_KEYGUARD_ACTION的广播。

  3. 屏幕亮起:用户按下电源键或其他方式唤醒屏幕,onScreenTurnedOn被调用。

  4. 取消延迟任务onScreenTurnedOn中调用cancelDoKeyguardLaterLocked,这通常会增加mDelayedShowingSequence的值,使得之前的延迟显示锁屏任务失效。

  5. 广播接收:如果在onScreenTurnedOn调用cancelDoKeyguardLaterLocked之后,但锁屏显示之前,之前发送的DELAYED_KEYGUARD_ACTION广播被接收,那么BroadcastReceiver中的代码会检查序列号。

  6. 序列号检查:如果mDelayedShowingSequence的值没有变化,或者变化没有被正确地传递到BroadcastReceiver,那么if (mDelayedShowingSequence == sequence)的条件仍然成立,doKeyguardLocked(null)会被调用,锁屏界面会显示。

  7. 锁屏显示:如果序列号匹配,即使onScreenTurnedOn已经调用,锁屏界面仍然会显示。

这个流程说明,尽管onScreenTurnedOn方法被调用且可能取消了延迟显示锁屏的任务,但如果广播在取消之后才被接收,锁屏界面仍有可能显示。这就是为什么在实际设备上,您可能会遇到即使屏幕很快被点亮,锁屏界面仍然会显示的情况。

3.4 关于亮屏后锁屏处理的总结

如果在屏幕关闭时没有立即锁屏,然后在屏幕打开时执行 onScreenTurnedOn:

  • mScreenOn 会被设置为 true,表示屏幕现在是开启状态。
  • cancelDoKeyguardLaterLocked 会被调用,取消任何延迟显示锁屏的任务。
  • 如果没有其他条件阻止锁屏显示(例如,用户正在解锁设备或设备已解锁),则锁屏界面通常会显示。

在 onScreenTurnedOn 方法中,没有直接调用 doKeyguardLocked方法来显示锁屏。但是,如果系统配置要求在屏幕打开时显示锁屏(例如,设备是安全的且用户没有在解锁过程中),则锁屏界面可能会通过其他机制显示。

总结来说,如果在屏幕关闭时没有锁屏,然后在屏幕打开时执行 onScreenTurnedOn 方法,并且没有其他条件阻止锁屏显示,那么锁屏界面应该会显示。这是为了保证设备的安全性,特别是在设备已经配置了安全措施(如PIN、密码、图案)的情况下。

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

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

相关文章

Leecode刷题之路第六天之Z字形变换

题目出处 06-Z字形变换 题目描述 个人解法 思路&#xff1a; todo 代码示例&#xff1a;&#xff08;Java&#xff09; todo复杂度分析 todo 官方解法 06-Z字形变换官方解法 方法1&#xff1a;利用二维矩阵模拟 思路&#xff1a; 代码示例&#xff1a;&#xff08;Java&am…

Microsoft 更新 Copilot AI,未來將能使用語音並看到你瀏覽的網頁

不過受到 Recall 事件的影響&#xff0c;更新的推出將更緩慢謹慎。 Microsoft 也同步對其網頁版及行動版的 Copilot AI 進行大改版。這主要是為網頁版換上了一個較為簡單乾淨的介面&#xff0c;並增加了一些新的功能&#xff0c;像是 Copilot Voice 能讓你與 AI 助手進行對話式…

Ansible Playbook原理与实践(Principles and Practice of Ansible Playbook)

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

环绕航线规划软件,适配大疆御3e、M300/350适用大疆机型: 经纬M300 rtk、M350rtk、御3e等行业机,能支持kml,㎞z导入。

环绕航线规划软件,适配大疆御3e、M300/350 适用大疆机型: 经纬M300 rtk、M350rtk、御3e等行业机,能支持kml,㎞z导入。 环绕航线规划软件介绍 名称 环绕航线规划软件 (Orbit Flight Planning Software) 适用机型 大疆经纬 M300 RTK大疆经纬 M350 RTK大疆御 3E 行业机功能特…

【技术详解】SpringMVC框架全面解析:从入门到精通(SpringMVC)

文章目录 【技术详解】SpringMVC框架全面解析&#xff1a;从入门到精通(SpringMVC)SpringMVC概述1. 三层架构与MVC架构区别1.1 三层架构1.2 MVC架构1.3前后端分离开发模式 2. SpringMVC环境搭建2.1 注解启动方式2.2 xml启动方式2.3 SpringMVC PostMan工具使用 3. SpringMVC 请求…

electron出现乱码和使用cmd出现乱码

第一种出现乱码。这种可以通过chcp 65001&#xff0c;设置为utf-8的编码。第二种&#xff0c;是执行exec的时候出现乱码&#xff0c;这个时候需要设置一些编码格式&#xff0c;可以通过iconv-lite进行解决&#xff0c;这个方法是node自带的&#xff0c;所以不需要导入。使用方法…

scrapy爬取汽车、车评数据【上】

这个爬虫我想分三期来写&#xff1a; ✅ 第一期写如何爬取汽车的车型信息&#xff1b; ✅ 第二期写如何爬取汽车的车评&#xff1b; ✅ 第三期写如何对车评嵌入情感分析结果&#xff0c;以及用简单的方法把数据插入mysql中&#xff1b; 技术基于scrapy框架、BERT语言模型、mysq…

【springboot】整合沙箱支付

目录 1. 配置沙箱应用环境2. 配置springboot项目1. 引入依赖2. 配置文件注册下载ngrok 3. 创建支付宝支付服务类4. 支付界面模板5. 控制类实现支付6. 测试 1. 配置沙箱应用环境 使用支付宝账号登录到开放平台控制台。 使用支付宝登录后&#xff0c;看到以下页面&#xff0c;下…

2、.Net 前端框架:OpenAuth.Net - .Net宣传系列文章

OpenAuth.Net 是一个开源的身份验证框架&#xff0c;由开发者 Yubaolee 创建&#xff0c;它旨在简化 Web 应用和服务的安全授权过程。这个框架以其强大的功能和易用性&#xff0c;为开发人员提供了一种高效的方式来处理用户认证和授权问题。 OpenAuth.Net 的关键特性包括&#…

无头双向不循环链表的模拟

总共由四部分组成&#xff0c;一个拥有总体方法名参数 的接口、一个异常类、一个方法类、一个测试类 先看我们写入的接口 public interface ILinkList {// 2、无头双向链表实现//头插法void addFirst(int val);//尾插法void addLast(int val);//任意位置插入,第一个数据节点为…

广联达 Linkworks办公OA Service.asmx接口存在信息泄露漏洞

漏洞描述 广联达科技股份有限公司以建设工程领域专业应用为核心基础支撑&#xff0c;提供一百余款基于“端云大数据”产品/服务&#xff0c;提供产业大数据、产业新金融等增值服务的数字建筑平台服务商。广联达OA存在信息泄露漏洞&#xff0c;由于某些接口没有鉴权&#xff0c…

Unity Input System自动生成配置

参考视频 创建及配置新输入系统 New Input System&#xff5c;Unity2022.2 最新教程《勇士传说》入门到进阶&#xff5c;4K_哔哩哔哩_bilibili ProjectSettings设置 Unity编辑器菜单栏选择Edit->Project Settings->Player->Other Settings,将Api Compatibility Level…

Ascend C 算子运行资源管理简介

Ascend C 算子运行资源管理简介 在 Ascend C 平台上开发深度学习应用时&#xff0c;运行资源的管理是一个核心知识点。通过合理管理算子执行中的计算资源&#xff0c;能够有效提升模型的执行效率和硬件利用率。本文将简要介绍 Ascend C 算子运行时的资源管理。 1. AscendCL 初…

draw.io创建自定义形状

Create custom shapes in draw.io using the text editor Reference draw怎么创建和编辑复杂的自定义形状 https://blog.csdn.net/u012028275/article/details/113828875 Create custom shapes in draw.io using the text editor

AMD发布首个AI小语言模型:6900亿token、推测解码提速3.88倍

AMD发布了自己的首个小语言模型(SLM)&#xff0c;名为“AMD-135M”。相比于越来越庞大的大语言模型(LLM)&#xff0c;它体积小巧&#xff0c;更加灵活&#xff0c;更有针对性&#xff0c;非常适合私密性、专业性很强的企业部署。 AMD-135小模型隶属于Llama家族&#xff0c;有两…

5分钟学会SPI

SPI 定义&#xff1a;SPI 是一种机制&#xff0c;允许用户在不修改现有代码的情况下扩展和替换特定服务的实现。它定义了一组接口&#xff08;Service Interfaces&#xff09;和一组实现&#xff08;Service Providers&#xff09;&#xff0c;使得应用程序可以动态加载和使用…

netty之Netty与SpringBoot整合

前言 在实际的开发中&#xff0c;我们需要对netty服务进行更多的操作&#xff0c;包括&#xff1b;获取它的状态信息、启动/停止、对客户端用户强制下线等等&#xff0c;为此我们需要把netty服务加入到web系统中。 MyChannelInitializer public class MyChannelInitializer ex…

[C++]使用C++部署yolov11目标检测的tensorrt模型支持图片视频推理windows测试通过

官方框架&#xff1a; https://github.com/ultralytics/ultralytics yolov8官方最近推出yolov11框架&#xff0c;标志着目标检测又多了一个检测利器&#xff0c;于是尝试在windows下部署yolov11的tensorrt模型&#xff0c;并最终成功。 重要说明&#xff1a;安装环境视为最基…

边缘自适应粒子滤波(Edge-Adaptive Particle Filter)的MATLAB函数示例,以及相应的讲解

目录 讲解 初始化 预测步骤 观测模拟 权重更新 重采样 状态估计 总结 下面是一个简单的边缘自适应粒子滤波&#xff08;&#xff09;的函数示例&#xff0c;以及相应的讲解。 程序源代码&#xff1a; function X_est edgeAdaptiveParticleFilter(numParticles, numS…