Android 电源键事件流程分析
Android 电源键事件流程分析
电源按键流程处理逻辑在 PhoneWindowManager.java类中的 dispatchUnhandledKey 方法中
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
从dispatchUnhandledKey方法开始分析
@Overridepublic KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags) {// Note: This method is only called if the initial down was unhandled.if (DEBUG_INPUT) {final KeyInterceptionInfo info =mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);final String title = info == null ? "<unknown>" : info.windowTitle;Slog.d(TAG, "Unhandled key: inputToken=" + focusedToken+ ", title=" + title+ ", action=" + event.getAction()+ ", flags=" + event.getFlags()+ ", keyCode=" + event.getKeyCode()+ ", scanCode=" + event.getScanCode()+ ", metaState=" + event.getMetaState()+ ", repeatCount=" + event.getRepeatCount()+ ", policyFlags=" + policyFlags);}KeyEvent fallbackEvent = null;if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {final KeyCharacterMap kcm = event.getKeyCharacterMap();final int keyCode = event.getKeyCode();final int metaState = event.getMetaState();final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN&& event.getRepeatCount() == 0;// Check for fallback actions specified by the key character map.final FallbackAction fallbackAction;if (initialDown) {fallbackAction = kcm.getFallbackAction(keyCode, metaState);} else {fallbackAction = mFallbackActions.get(keyCode);}if (fallbackAction != null) {if (DEBUG_INPUT) {Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode+ " metaState=" + Integer.toHexString(fallbackAction.metaState));}final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;fallbackEvent = KeyEvent.obtain(event.getDownTime(), event.getEventTime(),event.getAction(), fallbackAction.keyCode,event.getRepeatCount(), fallbackAction.metaState,event.getDeviceId(), event.getScanCode(),flags, event.getSource(), event.getDisplayId(), null);//核心代码if (!interceptFallback(focusedToken, fallbackEvent, policyFlags)) {fallbackEvent.recycle();fallbackEvent = null;}if (initialDown) {mFallbackActions.put(keyCode, fallbackAction);} else if (event.getAction() == KeyEvent.ACTION_UP) {mFallbackActions.remove(keyCode);fallbackAction.recycle();}}}if (DEBUG_INPUT) {if (fallbackEvent == null) {Slog.d(TAG, "No fallback.");} else {Slog.d(TAG, "Performing fallback: " + fallbackEvent);}}return fallbackEvent;}
关于电源按键的核心逻辑在interceptFallback方法里的interceptKeyBeforeQueueing方法里
@Overridepublic int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {...if (!mSystemBooted) {// If we have not yet booted, don't let key events do anything.// Exception: Wake and power key events are forwarded to PowerManager to allow it to// wake from quiescent mode during boot.if (down && (keyCode == KeyEvent.KEYCODE_POWER|| keyCode == KeyEvent.KEYCODE_TV_POWER)) {wakeUpFromPowerKey(event.getDownTime());} else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP)&& isWakeKeyWhenScreenOff(keyCode)) {wakeUpFromWakeKey(event);}return 0;}....// Handle special keys.switch (keyCode) {...case KeyEvent.KEYCODE_POWER: {EventLogTags.writeInterceptPower(KeyEvent.actionToString(event.getAction()),mPowerKeyHandled ? 1 : 0,mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));// Any activity on the power button stops the accessibility shortcutresult &= ~ACTION_PASS_TO_USER;isWakeKey = false; // wake-up will be handled separatelyif (down) {interceptPowerKeyDown(event, interactiveAndOn);} else {interceptPowerKeyUp(event, canceled);}break;}}}return result;}
当按下的时候,执行interceptPowerKeyDown方法
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {// Hold a wake lock until the power key is released.if (!mPowerKeyWakeLock.isHeld()) {mPowerKeyWakeLock.acquire();}mWindowManagerFuncs.onPowerKeyDown(interactive);// Stop ringing or end call if configured to do so when power is pressed.TelecomManager telecomManager = getTelecommService();boolean hungUp = false;if (telecomManager != null) {if (telecomManager.isRinging()) {// Pressing Power while there's a ringing incoming// call should silence the ringer.telecomManager.silenceRinger();} else if ((mIncallPowerBehavior& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0&& telecomManager.isInCall() && interactive) {// Otherwise, if "Power button ends call" is enabled,// the Power button will hang up any current active call.hungUp = telecomManager.endCall();}}final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);// Inform the StatusBar; but do not allow it to consume the event.sendSystemKeyToStatusBarAsync(event.getKeyCode());// If the power key has still not yet been handled, then detect short// press, long press, or multi press and decide what to do.mPowerKeyHandled = mPowerKeyHandled || hungUp|| handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();if (!mPowerKeyHandled) {if (!interactive) {wakeUpFromPowerKey(event.getDownTime());}} else {// handled by another power key policy.if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {mSingleKeyGestureDetector.reset();}}}
1、Android 按电源键亮屏/息屏流程
1、亮屏
1.1、PhoneWindowManager.java 中的 interceptKeyBeforeQueueing 方法
@Overridepublic int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {// Handle special keys.switch (keyCode) {case KeyEvent.KEYCODE_POWER: {EventLogTags.writeInterceptPower(KeyEvent.actionToString(event.getAction()),mPowerKeyHandled ? 1 : 0,mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));// Any activity on the power button stops the accessibility shortcutresult &= ~ACTION_PASS_TO_USER;isWakeKey = false; // wake-up will be handled separatelyif (down) {// 按下power键调用interceptPowerKeyDown(event, interactiveAndOn);} else {interceptPowerKeyUp(event, canceled);}break;}}}
1.2、息屏时按下power键调用 interceptPowerKeyDown() 方法
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {// Hold a wake lock until the power key is released.if (!mPowerKeyWakeLock.isHeld()) {mPowerKeyWakeLock.acquire();}mWindowManagerFuncs.onPowerKeyDown(interactive);// Stop ringing or end call if configured to do so when power is pressed.TelecomManager telecomManager = getTelecommService();boolean hungUp = false;if (telecomManager != null) {if (telecomManager.isRinging()) {// Pressing Power while there's a ringing incoming// call should silence the ringer.telecomManager.silenceRinger();} else if ((mIncallPowerBehavior& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0&& telecomManager.isInCall() && interactive) {// Otherwise, if "Power button ends call" is enabled,// the Power button will hang up any current active call.hungUp = telecomManager.endCall();}}final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);// Inform the StatusBar; but do not allow it to consume the event.sendSystemKeyToStatusBarAsync(event.getKeyCode());// If the power key has still not yet been handled, then detect short// press, long press, or multi press and decide what to do.mPowerKeyHandled = mPowerKeyHandled || hungUp|| handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();if (!mPowerKeyHandled) {if (!interactive) {// 表示当前未交互或者息屏状态下wakeUpFromPowerKey(event.getDownTime());}} else {// handled by another power key policy.if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {mSingleKeyGestureDetector.reset();}}}
1.3、wakeUpFromPowerKey()
可以看到,在按下电源键的时候,如果判断,当前是息屏状态,则执行wakeUpFromPowerKey()方法,
private void wakeUpFromPowerKey(long eventTime) {if (wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey,PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER")) {// Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeoutif (shouldWakeUpWithHomeIntent()) {startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ false, /*wakenFromDreams*/ true,PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_POWER_BUTTON));}}
}
调用 wakeUp() 方法
private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason,String details) {final boolean theaterModeEnabled = isTheaterModeEnabled();if (!wakeInTheaterMode && theaterModeEnabled) {return false;}if (theaterModeEnabled) {Settings.Global.putInt(mContext.getContentResolver(),Settings.Global.THEATER_MODE_ON, 0);}//核心代码mPowerManager.wakeUp(wakeTime, reason, details);return true;
}
1.4、PowerManager 中的 WakeUp() 方法
// android.os.PowerManager
public void wakeUp(long time, @WakeReason int reason, String details) {try {mService.wakeUp(time, reason, details, mContext.getOpPackageName());} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}
1.5、PowerManagerService 中 wakeUp() 方法
通过跨进程通信,我们知道真正的操作是在PowerManagerService类里的wakeUp()方法
@Override // Binder call
public void wakeUp(long eventTime, @WakeReason int reason, String details,String opPackageName) {if (eventTime > mClock.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 {wakeDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, details, uid,opPackageName, uid);} finally {Binder.restoreCallingIdentity(ident);}
}
调用 wakeDisplayGroup() 方法
private void wakeDisplayGroup(int groupId, long eventTime, @WakeReason int reason,String details, int uid, String opPackageName, int opUid) {synchronized (mLock) {if (wakeDisplayGroupNoUpdateLocked(groupId, eventTime, reason, details, uid,opPackageName, opUid)) {updatePowerStateLocked();}}
}
1.6、先分析 wakeDisplayGroupNoUpdateLocked() 方法,如果它返回true,则执行updatePowerStateLocked方法
private boolean wakeDisplayGroupNoUpdateLocked(int groupId, long eventTime,@WakeReason int reason, String details, int uid, String opPackageName, int opUid) {if (DEBUG_SPEW) {Slog.d(TAG, "wakeDisplayGroupNoUpdateLocked: eventTime=" + eventTime+ ", groupId=" + groupId + ", uid=" + uid);}if (eventTime < mLastSleepTime || mForceSuspendActive || !mSystemReady) {return false;}final int currentState = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);if (currentState == WAKEFULNESS_AWAKE) {if (!mBootCompleted && sQuiescent) {mDirty |= DIRTY_QUIESCENT;return true;}return false;}Trace.traceBegin(Trace.TRACE_TAG_POWER, "powerOnDisplay");try {Slog.i(TAG, "Powering on display group from"+ PowerManagerInternal.wakefulnessToString(currentState)+ " (groupId=" + groupId+ ", uid=" + uid+ ", reason=" + PowerManager.wakeReasonToString(reason)+ ", details=" + details+ ")...");Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId);//更新屏幕亮屏超时时间setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid,opPackageName, details);mDisplayGroupPowerStateMapper.setLastPowerOnTimeLocked(groupId, eventTime);mDisplayGroupPowerStateMapper.setPoweringOnLocked(groupId, true);} finally {Trace.traceEnd(Trace.TRACE_TAG_POWER);}return true;
}
先来看看setWakefulnessLocked方法
@VisibleForTesting
void setWakefulnessLocked(int groupId, int wakefulness, long eventTime, int uid, int reason,int opUid, String opPackageName, String details) {if (mDisplayGroupPowerStateMapper.setWakefulnessLocked(groupId, wakefulness)) {mDirty |= DIRTY_DISPLAY_GROUP_WAKEFULNESS;setGlobalWakefulnessLocked(mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(),eventTime, reason, uid, opUid, opPackageName, details);if (wakefulness == WAKEFULNESS_AWAKE) {// Kick user activity to prevent newly awake group from timing out instantly.userActivityNoUpdateLocked(groupId, eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);}}
}
setGlobalWakefulnessLocked 方法
private void setGlobalWakefulnessLocked(int wakefulness, long eventTime, int reason, int uid,int opUid, String opPackageName, String details) {if (getWakefulnessLocked() == wakefulness) {return;}// Phase 1: Handle pre-wakefulness change bookkeeping.final String traceMethodName;switch (wakefulness) {case WAKEFULNESS_ASLEEP:traceMethodName = "reallyGoToSleep";Slog.i(TAG, "Sleeping (uid " + uid + ")...");break;case WAKEFULNESS_AWAKE:traceMethodName = "wakeUp";Slog.i(TAG, "Waking up from "+ PowerManagerInternal.wakefulnessToString(getWakefulnessLocked())+ " (uid=" + uid+ ", reason=" + PowerManager.wakeReasonToString(reason)+ ", details=" + details+ ")...");mLastWakeTime = eventTime;mLastWakeReason = reason;break;case WAKEFULNESS_DREAMING:traceMethodName = "nap";Slog.i(TAG, "Nap time (uid " + uid + ")...");break;case WAKEFULNESS_DOZING:traceMethodName = "goToSleep";Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason)+ " (uid " + uid + ")...");mLastSleepTime = eventTime;mLastSleepReason = reason;mDozeStartInProgress = true;break;default:throw new IllegalArgumentException("Unexpected wakefulness: " + wakefulness);}Trace.traceBegin(Trace.TRACE_TAG_POWER, traceMethodName);try {// Phase 2: Handle wakefulness change and bookkeeping.// Under lock, invalidate before set ensures caches won't return stale values.mInjector.invalidateIsInteractiveCaches();mWakefulnessRaw = wakefulness;mWakefulnessChanging = true;mDirty |= DIRTY_WAKEFULNESS;// This is only valid while we are in wakefulness dozing. Set to false otherwise.mDozeStartInProgress &= (getWakefulnessLocked() == WAKEFULNESS_DOZING);if (mNotifier != null) {mNotifier.onWakefulnessChangeStarted(wakefulness, reason, eventTime);}mAttentionDetector.onWakefulnessChangeStarted(wakefulness);// Phase 3: Handle post-wakefulness change bookkeeping.switch (wakefulness) {case WAKEFULNESS_AWAKE:mNotifier.onWakeUp(reason, details, uid, opPackageName, opUid);if (sQuiescent) {mDirty |= DIRTY_QUIESCENT;}break;case WAKEFULNESS_DOZING:// Report the number of wake locks that will be cleared by going to sleep.int numWakeLocksCleared = 0;final int numWakeLocks = mWakeLocks.size();for (int i = 0; i < numWakeLocks; i++) {final WakeLock wakeLock = mWakeLocks.get(i);switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {case PowerManager.FULL_WAKE_LOCK:case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:case PowerManager.SCREEN_DIM_WAKE_LOCK:numWakeLocksCleared += 1;break;}}EventLogTags.writePowerSleepRequested(numWakeLocksCleared);break;}} finally {Trace.traceEnd(Trace.TRACE_TAG_POWER);}
}
继续往下追,查看Notifier类里的onWakefulnessChangeStarted方法
//com.android.server.power.Notifier
/*** Notifies that the device is changing wakefulness.* This function may be called even if the previous change hasn't finished in* which case it will assume that the state did not fully converge before the* next transition began and will recover accordingly.*/
public void onWakefulnessChangeStarted(final int wakefulness, int reason, long eventTime) {final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);if (DEBUG) {Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness+ ", reason=" + reason + ", interactive=" + interactive);}// Tell the activity manager about changes in wakefulness, not just interactivity.// It needs more granularity than other components.mHandler.post(new Runnable() {@Overridepublic void run() {mActivityManagerInternal.onWakefulnessChanged(wakefulness);}});// Handle any early interactive state changes.// Finish pending incomplete ones from a previous cycle.if (mInteractive != interactive) {// Finish up late behaviors if needed.if (mInteractiveChanging) {handleLateInteractiveChange();}// Start input as soon as we start waking up or going to sleep.mInputManagerInternal.setInteractive(interactive);mInputMethodManagerInternal.setInteractive(interactive);// Notify battery stats.try {mBatteryStats.noteInteractive(interactive);} catch (RemoteException ex) { }FrameworkStatsLog.write(FrameworkStatsLog.INTERACTIVE_STATE_CHANGED,interactive ? FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON :FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__OFF);// Handle early behaviors.mInteractive = interactive;mInteractiveChangeReason = reason;mInteractiveChangeStartTime = eventTime;mInteractiveChanging = true;handleEarlyInteractiveChange();}
}
继续看 handleEarlyInteractiveChange方法
/*** Handle early interactive state changes such as getting applications or the lock* screen running and ready for the user to see (such as when turning on the screen).*/
private void handleEarlyInteractiveChange() {synchronized (mLock) {if (mInteractive) {// Waking up...mHandler.post(() -> mPolicy.startedWakingUp(mInteractiveChangeReason));// Send interactive broadcast.mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;mPendingWakeUpBroadcast = true;updatePendingBroadcastLocked();} else {// Going to sleep...// Tell the policy that we started going to sleep.mHandler.post(() -> mPolicy.startedGoingToSleep(mInteractiveChangeReason));}}
}
继续看 updatePendingBroadcastLocked 方法
private void updatePendingBroadcastLocked() {if (!mBroadcastInProgress&& mPendingInteractiveState != INTERACTIVE_STATE_UNKNOWN&& (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast|| mPendingInteractiveState != mBroadcastedInteractiveState)) {mBroadcastInProgress = true;mSuspendBlocker.acquire();//发送广播Message msg = mHandler.obtainMessage(MSG_BROADCAST);msg.setAsynchronous(true);mHandler.sendMessage(msg);}
}
继续看 Handler 调用的方法
private final class NotifierHandler extends Handler {public NotifierHandler(Looper looper) {super(looper, null, true /*async*/);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {...case MSG_BROADCAST:sendNextBroadcast();break;....}}
}
在 sendNextBroadcast() 方法中 调用了 sendWakeUpBroadcast() 方法
private void sendWakeUpBroadcast() {if (DEBUG) {Slog.d(TAG, "Sending wake up broadcast.");}if (mActivityManagerInternal.isSystemReady()) {mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,mWakeUpBroadcastDone, mHandler, 0, null, null);} else {EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);sendNextBroadcast();}
}
1.7、在 分析 updatePowerStateLocked 方法,此方法目的是更新PackageManagerService全局状态
/*** Updates the global power state based on dirty bits recorded in mDirty.** This is the main function that performs power state transitions.* We centralize them here so that we can recompute the power state completely* each time something important changes, and ensure that we do it the same* way each time. The point is to gather all of the transition logic here.*/
private void updatePowerStateLocked() {if (!mSystemReady || mDirty == 0) {return;}if (!Thread.holdsLock(mLock)) {Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked");}Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState");try {// Phase 0: Basic state updates.updateIsPoweredLocked(mDirty);updateStayOnLocked(mDirty);updateScreenBrightnessBoostLocked(mDirty);// Phase 1: Update wakefulness.// Loop because the wake lock and user activity computations are influenced// by changes in wakefulness.final long now = mClock.uptimeMillis();int dirtyPhase2 = 0;for (;;) {int dirtyPhase1 = mDirty;dirtyPhase2 |= dirtyPhase1;mDirty = 0;updateWakeLockSummaryLocked(dirtyPhase1);updateUserActivitySummaryLocked(now, dirtyPhase1);updateAttentiveStateLocked(now, dirtyPhase1);if (!updateWakefulnessLocked(dirtyPhase1)) {break;}}// Phase 2: Lock profiles that became inactive/not kept awake.updateProfilesLocked(now);// Phase 3: Update display power state./*** 异步更新显示器电源状态。更新完成后,mDisplayReady 将设置为 true。显示控制器会发布一条消息,告诉我们实际显示电源状态何时 * 更新,因此我们回到这里仔细检查并完成。此功能每次重新计算显示器电源状态。返回:如果显示已准备好,则为true。* 经过这一步后,会将系统的亮度、显示状态等全部设置完毕,此时屏幕已经亮了*/final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);// Phase 4: Update dream state (depends on display ready signal).updateDreamLocked(dirtyPhase2, displayBecameReady);// Phase 5: Send notifications, if needed.finishWakefulnessChangeIfNeededLocked();// Phase 6: Update suspend blocker.// Because we might release the last suspend blocker here, we need to make sure// we finished everything else first!updateSuspendBlockerLocked();} finally {Trace.traceEnd(Trace.TRACE_TAG_POWER);}
}
再分析finishWakefulnessChangeIfNeededLocked() 方法,看看亮屏收尾工作都做了那些
private void finishWakefulnessChangeIfNeededLocked() {if (mWakefulnessChanging && mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {if (getWakefulnessLocked() == WAKEFULNESS_DOZING&& (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {return; // wait until dream has enabled dozing} else {// Doze wakelock acquired (doze started) or device is no longer dozing.mDozeStartInProgress = false;}if (getWakefulnessLocked() == WAKEFULNESS_DOZING|| getWakefulnessLocked() == WAKEFULNESS_ASLEEP) {logSleepTimeoutRecapturedLocked();}mWakefulnessChanging = false;mNotifier.onWakefulnessChangeFinished();}
}
继续看 Notifier类里的onWakefulnessChangeFinished() 方法
//com.android.server.power.Notifier
/*** Notifies that the device has finished changing wakefulness.*/
public void onWakefulnessChangeFinished() {if (DEBUG) {Slog.d(TAG, "onWakefulnessChangeFinished");}if (mInteractiveChanging) {mInteractiveChanging = false;handleLateInteractiveChange();}
}
看调用的handleLateInteractiveChange() 方法
/*** Handle late interactive state changes once they are finished so that the system can* finish pending transitions (such as turning the screen off) before causing* applications to change state visibly.*/
private void handleLateInteractiveChange() {synchronized (mLock) {final int interactiveChangeLatency =(int) (SystemClock.uptimeMillis() - mInteractiveChangeStartTime);if (mInteractive) {// Finished waking up...mHandler.post(() -> {LogMaker log = new LogMaker(MetricsEvent.SCREEN);log.setType(MetricsEvent.TYPE_OPEN);log.setSubtype(WindowManagerPolicyConstants.translateWakeReasonToOnReason(mInteractiveChangeReason));log.setLatency(interactiveChangeLatency);log.addTaggedData(MetricsEvent.FIELD_SCREEN_WAKE_REASON, mInteractiveChangeReason);MetricsLogger.action(log);EventLogTags.writePowerScreenState(1, 0, 0, 0, interactiveChangeLatency);//通知PhoneWindowManager唤醒操作结束mPolicy.finishedWakingUp(mInteractiveChangeReason);});} else {.....}}
}
2、息屏
同唤醒操作,首先执行PhoneWindowManager.java中的 interceptKeyBeforeQueueing() 方法中 KEYCODE_POWER
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {....// Handle special keys.switch (keyCode) {....case KeyEvent.KEYCODE_POWER: {EventLogTags.writeInterceptPower(KeyEvent.actionToString(event.getAction()),mPowerKeyHandled ? 1 : 0,mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));// Any activity on the power button stops the accessibility shortcutresult &= ~ACTION_PASS_TO_USER;isWakeKey = false; // wake-up will be handled separatelyif (down) {interceptPowerKeyDown(event, interactiveAndOn);} else {//手指抬起interceptPowerKeyUp(event, canceled);}break;}}....return result;
}private void interceptPowerKeyUp(KeyEvent event, boolean canceled) {final boolean handled = canceled || mPowerKeyHandled;if (!handled) {if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) == 0) {// Abort possibly stuck animations only when power key up without long press case.mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);}} else {// handled by single key or another power key policy.if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {mSingleKeyGestureDetector.reset();}}finishPowerKeyPress();
}
再 PhoneWindowManager 类中 init() 方法中调用了 initSingleKeyGestureRules() -> PowerKeyRule() -> powerPress()
private void powerPress(long eventTime, int count, boolean beganFromNonInteractive) {if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {Slog.i(TAG, "Suppressed redundant power key press while "+ "already in the process of turning the screen on.");return;}final boolean interactive = Display.isOnState(mDefaultDisplay.getState());Slog.d(TAG, "powerPress: eventTime=" + eventTime + " interactive=" + interactive+ " count=" + count + " beganFromNonInteractive=" + beganFromNonInteractive+ " mShortPressOnPowerBehavior=" + mShortPressOnPowerBehavior);if (count == 2) {powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);} else if (count == 3) {powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);} else if (interactive && !beganFromNonInteractive) {if (mSideFpsEventHandler.onSinglePressDetected(eventTime)) {Slog.i(TAG, "Suppressing power key because the user is interacting with the "+ "fingerprint sensor");return;}switch (mShortPressOnPowerBehavior) {case SHORT_PRESS_POWER_NOTHING:break;case SHORT_PRESS_POWER_GO_TO_SLEEP:sleepDefaultDisplayFromPowerButton(eventTime, 0);break;case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:sleepDefaultDisplayFromPowerButton(eventTime,PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);break;.....}}
}
查看 mShortPressOnPowerBehavior 赋值
mShortPressOnPowerBehavior = mContext.getResources().getInteger(com.android.internal.R.integer.config_shortPressOnPowerBehavior);//查看配置文件
<!-- Control the behavior when the user short presses the power button.0 - Nothing1 - Go to sleep (doze)2 - Really go to sleep (don't doze)3 - Really go to sleep and go home (don't doze)4 - Go to home5 - Dismiss IME if shown. Otherwise go to home
-->
<integer name="config_shortPressOnPowerBehavior">1</integer>
执行 case SHORT_PRESS_POWER_GO_TO_SLEEP 方法,即 sleepDefaultDisplayFromPowerButton()
/*** Sends the default display to sleep as a result of a power button press.** @return {@code true} if the device was sent to sleep, {@code false} if the device did not* sleep.*/
//由于按下电源按钮,设备进入睡眠状态。返回:如果设备被发送到睡眠状态,则返回 true,如果睡眠被抑制,则返回 false
private boolean sleepDefaultDisplayFromPowerButton(long eventTime, int flags) {// Before we actually go to sleep, we check the last wakeup reason.// If the device very recently woke up from a gesture (like user lifting their device)// then ignore the sleep instruction. This is because users have developed// a tendency to hit the power button immediately when they pick up their device, and we// don't want to put the device back to sleep in those cases.final PowerManager.WakeData lastWakeUp = mPowerManagerInternal.getLastWakeup();if (lastWakeUp != null && lastWakeUp.wakeReason == PowerManager.WAKE_REASON_GESTURE) {final int gestureDelayMillis = Settings.Global.getInt(mContext.getContentResolver(),Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS);final long now = SystemClock.uptimeMillis();if (mPowerButtonSuppressionDelayMillis > 0&& (now < lastWakeUp.wakeTime + mPowerButtonSuppressionDelayMillis)) {Slog.i(TAG, "Sleep from power button suppressed. Time since gesture: "+ (now - lastWakeUp.wakeTime) + "ms");return false;}}sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);return true;
}
private void sleepDefaultDisplay(long eventTime, int reason, int flags) {mRequestedOrSleepingDefaultDisplay = true;mPowerManager.goToSleep(eventTime, reason, flags);
}
继续往下追PowerManagerService 的goToSleep()
@Override // Binder call
public void goToSleep(long eventTime, int reason, int flags) {if (eventTime > mClock.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 {sleepDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, flags, uid);} finally {Binder.restoreCallingIdentity(ident);}
}private void sleepDisplayGroup(int groupId, long eventTime, int reason, int flags,int uid) {synchronized (mLock) {if (sleepDisplayGroupNoUpdateLocked(groupId, eventTime, reason, flags, uid)) {updatePowerStateLocked(); }}
}
先分析 sleepDisplayGroupNoUpdateLocked()
private boolean sleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int reason,int flags, int uid) {if (DEBUG_SPEW) {Slog.d(TAG, "sleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime+ ", groupId=" + groupId + ", reason=" + reason + ", flags=" + flags+ ", uid=" + uid);}if (eventTime < mLastWakeTime|| !PowerManagerInternal.isInteractive(getWakefulnessLocked())|| !mSystemReady|| !mBootCompleted) {return false;}final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);if (!PowerManagerInternal.isInteractive(wakefulness)) {return false;}Trace.traceBegin(Trace.TRACE_TAG_POWER, "powerOffDisplay");try {reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX,Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN));Slog.i(TAG, "Powering off display group due to "+ PowerManager.sleepReasonToString(reason) + " (groupId= " + groupId+ ", uid= " + uid + ")...");mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true);//核心代码setWakefulnessLocked(groupId, WAKEFULNESS_DOZING, eventTime, uid, reason,/* opUid= */ 0, /* opPackageName= */ null, /* details= */ null);if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {reallySleepDisplayGroupNoUpdateLocked(groupId, eventTime, uid);}} finally {Trace.traceEnd(Trace.TRACE_TAG_POWER);}return true;
}
由setWakefulnessLocked() -> setGlobalWakefulnessLocked() -> mNotifier.onWakefulnessChangeStarted()
同wakeup一样,会走到Notifer类的onWakefulnessChangeStarted()方法
/*** Notifies that the device is changing wakefulness.* This function may be called even if the previous change hasn't finished in* which case it will assume that the state did not fully converge before the* next transition began and will recover accordingly.*/
public void onWakefulnessChangeStarted(final int wakefulness, int reason, long eventTime) {final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);if (DEBUG) {Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness+ ", reason=" + reason + ", interactive=" + interactive);}// Tell the activity manager about changes in wakefulness, not just interactivity.// It needs more granularity than other components.mHandler.post(new Runnable() {@Overridepublic void run() {mActivityManagerInternal.onWakefulnessChanged(wakefulness);}});// Handle any early interactive state changes.// Finish pending incomplete ones from a previous cycle.if (mInteractive != interactive) {// Finish up late behaviors if needed.if (mInteractiveChanging) {handleLateInteractiveChange();}// Start input as soon as we start waking up or going to sleep.mInputManagerInternal.setInteractive(interactive);mInputMethodManagerInternal.setInteractive(interactive);// Notify battery stats.try {mBatteryStats.noteInteractive(interactive);} catch (RemoteException ex) { }FrameworkStatsLog.write(FrameworkStatsLog.INTERACTIVE_STATE_CHANGED,interactive ? FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON :FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__OFF);// Handle early behaviors.mInteractive = interactive;mInteractiveChangeReason = reason;mInteractiveChangeStartTime = eventTime;mInteractiveChanging = true;handleEarlyInteractiveChange();}
}
继续看 handleErarlyInteractiveChange 方法
/*** Handle early interactive state changes such as getting applications or the lock* screen running and ready for the user to see (such as when turning on the screen).*/
private void handleEarlyInteractiveChange() {synchronized (mLock) {if (mInteractive) {// Waking up...mHandler.post(() -> mPolicy.startedWakingUp(mInteractiveChangeReason));// Send interactive broadcast.mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;mPendingWakeUpBroadcast = true;updatePendingBroadcastLocked();} else {// Going to sleep...// Tell the policy that we started going to sleep.mHandler.post(() -> mPolicy.startedGoingToSleep(mInteractiveChangeReason));}}
}
再分析 updatePowerStateLocked(), 唤醒的时候,也会走到这里
/*** Updates the global power state based on dirty bits recorded in mDirty.** This is the main function that performs power state transitions.* We centralize them here so that we can recompute the power state completely* each time something important changes, and ensure that we do it the same* way each time. The point is to gather all of the transition logic here.*/
private void updatePowerStateLocked() {if (!mSystemReady || mDirty == 0) {return;}if (!Thread.holdsLock(mLock)) {Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked");}Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState");try {// Phase 0: Basic state updates.updateIsPoweredLocked(mDirty);updateStayOnLocked(mDirty);updateScreenBrightnessBoostLocked(mDirty);// Phase 1: Update wakefulness.// Loop because the wake lock and user activity computations are influenced// by changes in wakefulness.final long now = mClock.uptimeMillis();int dirtyPhase2 = 0;for (;;) {int dirtyPhase1 = mDirty;dirtyPhase2 |= dirtyPhase1;mDirty = 0;updateWakeLockSummaryLocked(dirtyPhase1);updateUserActivitySummaryLocked(now, dirtyPhase1);updateAttentiveStateLocked(now, dirtyPhase1);if (!updateWakefulnessLocked(dirtyPhase1)) {break;}}// Phase 2: Lock profiles that became inactive/not kept awake.updateProfilesLocked(now);// Phase 3: Update display power state.final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);// Phase 4: Update dream state (depends on display ready signal).updateDreamLocked(dirtyPhase2, displayBecameReady);// Phase 5: Send notifications, if needed.finishWakefulnessChangeIfNeededLocked();// Phase 6: Update suspend blocker.// Because we might release the last suspend blocker here, we need to make sure// we finished everything else first!updateSuspendBlockerLocked();} finally {Trace.traceEnd(Trace.TRACE_TAG_POWER);}
}
当息屏执行结束,我们分析finishWakefulnessChangeIfNeededLocked方法
private void finishWakefulnessChangeIfNeededLocked() {if (mWakefulnessChanging && mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {if (getWakefulnessLocked() == WAKEFULNESS_DOZING&& (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {return; // wait until dream has enabled dozing} else {// Doze wakelock acquired (doze started) or device is no longer dozing.mDozeStartInProgress = false;}if (getWakefulnessLocked() == WAKEFULNESS_DOZING|| getWakefulnessLocked() == WAKEFULNESS_ASLEEP) {logSleepTimeoutRecapturedLocked();}mWakefulnessChanging = false;mNotifier.onWakefulnessChangeFinished();}
}
我们继续来分析Notifier类的onWakefulnessChangeFinished方法
/*** Notifies that the device has finished changing wakefulness.*/
public void onWakefulnessChangeFinished() {if (DEBUG) {Slog.d(TAG, "onWakefulnessChangeFinished");}if (mInteractiveChanging) {mInteractiveChanging = false;handleLateInteractiveChange();}
}
/*** Handle late interactive state changes once they are finished so that the system can* finish pending transitions (such as turning the screen off) before causing* applications to change state visibly.*/
private void handleLateInteractiveChange() {synchronized (mLock) {final int interactiveChangeLatency =(int) (SystemClock.uptimeMillis() - mInteractiveChangeStartTime);if (mInteractive) {// Finished waking up...mHandler.post(() -> {LogMaker log = new LogMaker(MetricsEvent.SCREEN);log.setType(MetricsEvent.TYPE_OPEN);log.setSubtype(WindowManagerPolicyConstants.translateWakeReasonToOnReason(mInteractiveChangeReason));log.setLatency(interactiveChangeLatency);log.addTaggedData(MetricsEvent.FIELD_SCREEN_WAKE_REASON, mInteractiveChangeReason);MetricsLogger.action(log);EventLogTags.writePowerScreenState(1, 0, 0, 0, interactiveChangeLatency);mPolicy.finishedWakingUp(mInteractiveChangeReason);});} else {// Finished going to sleep...// This is a good time to make transitions that we don't want the user to see,// such as bringing the key guard to focus. There's no guarantee for this// however because the user could turn the device on again at any time.// Some things may need to be protected by other mechanisms that defer screen on.// Cancel pending user activity.if (mUserActivityPending) {mUserActivityPending = false;mHandler.removeMessages(MSG_USER_ACTIVITY);}// Tell the policy we finished going to sleep.final int offReason = WindowManagerPolicyConstants.translateSleepReasonToOffReason(mInteractiveChangeReason);mHandler.post(() -> {LogMaker log = new LogMaker(MetricsEvent.SCREEN);log.setType(MetricsEvent.TYPE_CLOSE);log.setSubtype(offReason);log.setLatency(interactiveChangeLatency);log.addTaggedData(MetricsEvent.FIELD_SCREEN_SLEEP_REASON, mInteractiveChangeReason);MetricsLogger.action(log);EventLogTags.writePowerScreenState(0, offReason, 0, 0, interactiveChangeLatency);mPolicy.finishedGoingToSleep(mInteractiveChangeReason);});// Send non-interactive broadcast.mPendingInteractiveState = INTERACTIVE_STATE_ASLEEP;mPendingGoToSleepBroadcast = true;//发送息屏广播,和唤醒类似,就不再赘述了updatePendingBroadcastLocked();}}
}
好了,从我们分析源码得知
唤醒的时候,唤醒广播是在handleEarlyInteractiveChange方法,就是在通知PhoneWindowManager 去执行startedWakingUp方法,紧跟着就执行updatePendingBroadcastLocked方法,去发送唤醒广播
而在息屏的时候,在handleEarlyInteractiveChange里,我们只是通知PhoneWindowManager去执行startedGoingToSleep方法,最后在updatePowerStateLocked==>finishWakefulnessChangeIfNeededLocked==>Notifier==>onWakefulnessChangeFinished==》handleLateInteractiveChange方法里,在通知PhoneWindowManager执行finishedGoingToSleep()之后,才执行updatePendingBroadcastLocked()方法去发送息屏广播(Intent.ACTION_SCREEN_OFF),这个时机需要特别注意一下。
参考 : https://blog.csdn.net/jwg1988/article/details/123899706
2、长按Power事件
2.1、PhoneWindowManager.java -> powerLongPress() 方法
private void powerLongPress(long eventTime) {final int behavior = getResolvedLongPressOnPowerBehavior();Slog.d(TAG, "powerLongPress: eventTime=" + eventTime+ " mLongPressOnPowerBehavior=" + mLongPressOnPowerBehavior);switch (behavior) {case LONG_PRESS_POWER_NOTHING:break;case LONG_PRESS_POWER_GLOBAL_ACTIONS:mPowerKeyHandled = true;performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,"Power - Long Press - Global Actions");showGlobalActions();break;case LONG_PRESS_POWER_SHUT_OFF:case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:mPowerKeyHandled = true;performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,"Power - Long Press - Shut Off");sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);break;case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:mPowerKeyHandled = true;performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,"Power - Long Press - Go To Voice Assist");// Some devices allow the voice assistant intent during setup (and use that intent// to launch something else, like Settings). So we explicitly allow that via the// config_allowStartActivityForLongPressOnPowerInSetup resource in config.xml.launchVoiceAssist(mAllowStartActivityForLongPressOnPowerDuringSetup);break;case LONG_PRESS_POWER_ASSISTANT:mPowerKeyHandled = true;performHapticFeedback(HapticFeedbackConstants.ASSISTANT_BUTTON, false,"Power - Long Press - Go To Assistant");final int powerKeyDeviceId = Integer.MIN_VALUE;launchAssistAction(null, powerKeyDeviceId, eventTime,AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS);break;}
}
2.2、getResolvedLongPressOnPowerBehavior()
解析长按电源键行为的方法
private int getResolvedLongPressOnPowerBehavior() {if (FactoryTest.isLongPressOnPowerOffEnabled()) {return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;}// If the config indicates the assistant behavior but the device isn't yet provisioned, show// global actions instead.if (mLongPressOnPowerBehavior == LONG_PRESS_POWER_ASSISTANT && !isDeviceProvisioned()) {return LONG_PRESS_POWER_GLOBAL_ACTIONS;}return mLongPressOnPowerBehavior; //最后返回这个行为
}
而 mLongPressOnPowerBehavior 在init()方法中调用的是资源文件
mLongPressOnPowerBehavior = mContext.getResources().getInteger(com.android.internal.R.integer.config_longPressOnPowerBehavior);
默认配置文件中
<!-- Control the behavior when the user long presses the power button.0 - Nothing1 - Global actions menu2 - Power off (with confirmation)3 - Power off (without confirmation)4 - Go to voice assist5 - Go to assistant (Settings.Secure.ASSISTANT)--><integer name="config_longPressOnPowerBehavior">5</integer> //可以看到默认值为5
但在overlay中设置的内容默认值为1
/os/la.qssi12/vendor/qcom/proprietary/commonsys/resource-overlay/common/Frameworks/res/values/config.xml
<integer name="config_longPressOnPowerBehavior">1</integer>
2.3、showGlobalActions()
设置的 config_longPressOnPowerBehavior = 1;在powerLongPress方法中走 LONG_PRESS_POWER_GLOBAL_ACTIONS,调用 showGlobalActions()
@Override
public void showGlobalActions() {mHandler.removeMessages(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS);mHandler.sendEmptyMessage(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS);
}
2.4、showGlobalActionsInternal()
MSG_DISPATCH_SHOW_GLOBAL_ACTIONS -> showGlobalActionsInternal()
void showGlobalActionsInternal() {if (mGlobalActions == null) {mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);}final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());// since it took two seconds of long press to bring this up,// poke the wake lock so they have some time to see the dialog.mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
2.5、showDialog()
//com.android.server.policy.GlobalActions
public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned);if (mGlobalActionsProvider != null && mGlobalActionsProvider.isGlobalActionsDisabled()) {return;}mKeyguardShowing = keyguardShowing;mDeviceProvisioned = deviceProvisioned;mShowing = true;if (mGlobalActionsAvailable) {mHandler.postDelayed(mShowTimeout, 5000);mGlobalActionsProvider.showGlobalActions();} else {// SysUI isn't alive, show legacy menu.ensureLegacyCreated();mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned);}
}
2.6、mLegacyGlobalActions.showDialog()
//com.android.server.policy.LegacyGlobalActions
/*** Show the global actions dialog (creating if necessary)* @param keyguardShowing True if keyguard is showing*/
public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {mKeyguardShowing = keyguardShowing;mDeviceProvisioned = isDeviceProvisioned;if (mDialog != null) {mDialog.dismiss();mDialog = null;// Show delayed, so that the dismiss of the previous dialog completesmHandler.sendEmptyMessage(MESSAGE_SHOW);} else {handleShow();}
}
private void handleShow() {awakenIfNecessary();mDialog = createDialog();prepareDialog();// If we only have 1 item and it's a simple press action, just do this action.if (mAdapter.getCount() == 1&& mAdapter.getItem(0) instanceof SinglePressAction&& !(mAdapter.getItem(0) instanceof LongPressAction)) {((SinglePressAction) mAdapter.getItem(0)).onPress();} else {if (mDialog != null) {WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();attrs.setTitle("LegacyGlobalActions");mDialog.getWindow().setAttributes(attrs);mDialog.show();mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);}}
}
2.7、createDialog() 创建dialog
/*** Create the global actions dialog.* @return A new dialog.*/
private ActionsDialog createDialog() {// Simple toggle style if there's no vibrator, otherwise use a tri-stateif (!mHasVibrator) {mSilentModeAction = new SilentModeToggleAction();} else {mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);}mAirplaneModeOn = new ToggleAction(R.drawable.ic_lock_airplane_mode,R.drawable.ic_lock_airplane_mode_off,R.string.global_actions_toggle_airplane_mode,R.string.global_actions_airplane_mode_on_status,R.string.global_actions_airplane_mode_off_status) {@Overridepublic void onToggle(boolean on) {if (mHasTelephony && TelephonyProperties.in_ecm_mode().orElse(false)) {mIsWaitingForEcmExit = true;// Launch ECM exit dialogIntent ecmDialogIntent =new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);mContext.startActivity(ecmDialogIntent);} else {changeAirplaneModeSystemSetting(on);}}@Overrideprotected void changeStateFromPress(boolean buttonOn) {if (!mHasTelephony) return;// In ECM mode airplane state cannot be changedif (!TelephonyProperties.in_ecm_mode().orElse(false)) {mState = buttonOn ? State.TurningOn : State.TurningOff;mAirplaneState = mState;}}@Overridepublic boolean showDuringKeyguard() {return true;}@Overridepublic boolean showBeforeProvisioning() {return false;}};onAirplaneModeChanged();mItems = new ArrayList<Action>();String[] defaultActions = mContext.getResources().getStringArray(com.android.internal.R.array.config_globalActionsList);ArraySet<String> addedKeys = new ArraySet<String>();for (int i = 0; i < defaultActions.length; i++) {String actionKey = defaultActions[i];if (addedKeys.contains(actionKey)) {// If we already have added this, don't add it again.continue;}if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {mItems.add(new PowerAction(mContext, mWindowManagerFuncs));} else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {mItems.add(mAirplaneModeOn);} else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {if (Settings.Global.getInt(mContext.getContentResolver(),Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {mItems.add(new BugReportAction());}} else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {if (mShowSilentToggle) {mItems.add(mSilentModeAction);}} else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {addUsersToMenu(mItems);}} else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {mItems.add(getSettingsAction());} else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {mItems.add(getLockdownAction());} else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {mItems.add(getVoiceAssistAction());} else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {mItems.add(getAssistAction());} else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {mItems.add(new RestartAction(mContext, mWindowManagerFuncs));} else {Log.e(TAG, "Invalid global action key " + actionKey);}// Add here so we don't add more than one.addedKeys.add(actionKey);}if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {mItems.add(getEmergencyAction());}mAdapter = new ActionsAdapter(mContext, mItems,() -> mDeviceProvisioned, () -> mKeyguardShowing);AlertController.AlertParams params = new AlertController.AlertParams(mContext);params.mAdapter = mAdapter;params.mOnClickListener = this;params.mForceInverseBackground = true;ActionsDialog dialog = new ActionsDialog(mContext, params);dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.dialog.getListView().setItemsCanFocus(true);dialog.getListView().setLongClickable(true);dialog.getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view, int position,long id) {final Action action = mAdapter.getItem(position);if (action instanceof LongPressAction) {return ((LongPressAction) action).onLongPress();}return false;}});dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);// Don't acquire soft keyboard focus, to avoid destroying state when capturing bugreportsdialog.getWindow().setFlags(FLAG_ALT_FOCUSABLE_IM, FLAG_ALT_FOCUSABLE_IM);dialog.setOnDismissListener(this);return dialog;
}
通过 String[] defaultActions = mContext.getResources().getStringArray(
com.android.internal.R.array.config_globalActionsList); 来查看配置文件
<!-- Defines the default set of global actions. Actions may still be disabled or hidden basedon the current state of the device.Each item must be one of the following strings:"power" = Power off"settings" = An action to launch settings"airplane" = Airplane mode toggle"bugreport" = Take bug report, if available"silent" = silent mode"users" = list of users"restart" = restart device"emergency" = Launch emergency dialer"lockdown" = Lock down device until the user authenticates"logout" = Logout the current user--><string-array translatable="false" name="config_globalActionsList"><item>emergency</item><item>lockdown</item><item>power</item><item>restart</item><item>logout</item><item>screenshot</item><item>bugreport</item></string-array>
最后展示出来,那么既然得到了关机界面,假如我们点击了其中的关机键,那么会如何执行呢
我们发现在构造弹框的时候
if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {mItems.add(new PowerAction(mContext, mWindowManagerFuncs));}
那么点击item,就会执行PowerAction 的onPress()方法
@Override
public void onPress() {// shutdown by making sure radio and power are handled accordingly.mWindowManagerFuncs.shutdown(false /* confirm */);
}
mWindowManagerFuncs 由 PowerAction构造传进来的,往上追 ==> LegacyGlobalActions构造传入的 ==> GlobalActions构造传入 ==> PhoneWindowManager init()方法初始化的,最终我们是在WindowManagerService里找到PhoneWindowManager初始化的地方
//com.android.server.wm.WindowManagerService
private void initPolicy() {UiThread.getHandler().runWithScissors(new Runnable() {@Overridepublic void run() {WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);}}, 0);
}
我们也知道其实mWindowManagerFuncs就是一个WindowManagerService对象,那么上面的shutdown其实是执行的是WindowManagerService的shutdown
// Called by window manager policy. Not exposed externally.
@Override
public void shutdown(boolean confirm) {// Pass in the UI context, since ShutdownThread requires it (to show UI).ShutdownThread.shutdown(ActivityThread.currentActivityThread().getSystemUiContext(),PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
}
继续往下追 ShutdownThread 类里的shutdown方法
/*** Request a clean shutdown, waiting for subsystems to clean up their* state etc. Must be called from a Looper thread in which its UI* is shown.** @param context Context used to display the shutdown progress dialog. This must be a context* suitable for displaying UI (aka Themable).* @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.* @param confirm true if user confirmation is needed before shutting down.*/
public static void shutdown(final Context context, String reason, boolean confirm) {mReboot = false;mRebootSafeMode = false;mReason = reason;shutdownInner(context, confirm);
}
继续执行shutdownInner 方法
private static void shutdownInner(final Context context, boolean confirm) {// ShutdownThread is called from many places, so best to verify here that the context passed// in is themed.context.assertRuntimeOverlayThemable();// ensure that only one thread is trying to power down.// any additional calls are just returnedsynchronized (sIsStartedGuard) {if (sIsStarted) {Log.d(TAG, "Request to shutdown already running, returning.");return;}}// Add checkpoint for this shutdown attempt. The user might still cancel the dialog, but// this point preserves the system trace of the trigger point of the ShutdownThread.ShutdownCheckPoints.recordCheckPoint(/* reason= */ null);final int longPressBehavior = context.getResources().getInteger(com.android.internal.R.integer.config_longPressOnPowerBehavior);final int resourceId = mRebootSafeMode? com.android.internal.R.string.reboot_safemode_confirm: (longPressBehavior == 2? com.android.internal.R.string.shutdown_confirm_question: com.android.internal.R.string.shutdown_confirm);Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);if (confirm) {final CloseDialogReceiver closer = new CloseDialogReceiver(context);if (sConfirmDialog != null) {sConfirmDialog.dismiss();}sConfirmDialog = new AlertDialog.Builder(context).setTitle(mRebootSafeMode? com.android.internal.R.string.reboot_safemode_title: com.android.internal.R.string.power_off).setMessage(resourceId).setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {beginShutdownSequence(context);}}).setNegativeButton(com.android.internal.R.string.no, null).create();closer.dialog = sConfirmDialog;sConfirmDialog.setOnDismissListener(closer);sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);sConfirmDialog.show();} else {beginShutdownSequence(context);}}
是否需要弹出关机确认框,如果需要则弹出,通过上面我们传入的是false,所以直接执行
2.8、beginShutdownSequence(context);
private static void beginShutdownSequence(Context context) {synchronized (sIsStartedGuard) {if (sIsStarted) {Log.d(TAG, "Shutdown sequence already running, returning.");return;}sIsStarted = true;}sInstance.mProgressDialog = showShutdownDialog(context);sInstance.mContext = context;sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);// make sure we never fall asleep againsInstance.mCpuWakeLock = null;try {sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");sInstance.mCpuWakeLock.setReferenceCounted(false);sInstance.mCpuWakeLock.acquire();} catch (SecurityException e) {Log.w(TAG, "No permission to acquire wake lock", e);sInstance.mCpuWakeLock = null;}// also make sure the screen stays on for better user experiencesInstance.mScreenWakeLock = null;if (sInstance.mPowerManager.isScreenOn()) {try {sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG + "-screen");sInstance.mScreenWakeLock.setReferenceCounted(false);sInstance.mScreenWakeLock.acquire();} catch (SecurityException e) {Log.w(TAG, "No permission to acquire wake lock", e);sInstance.mScreenWakeLock = null;}}if (SecurityLog.isLoggingEnabled()) {SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);}// start the thread that initiates shutdownsInstance.mHandler = new Handler() {};sInstance.start();
}
首先初始化一个关机页面showShutdownDialog,弹出页面,然后sInstance.start();执行真正的关机操作
/*** Makes sure we handle the shutdown gracefully.* Shuts off power regardless of radio state if the allotted time has passed.*/
public void run() {TimingsTraceLog shutdownTimingLog = newTimingsLog();shutdownTimingLog.traceBegin("SystemServerShutdown");metricShutdownStart();metricStarted(METRIC_SYSTEM_SERVER);// Start dumping check points for this shutdown in a separate thread.Thread dumpCheckPointsThread = ShutdownCheckPoints.newDumpThread(new File(CHECK_POINTS_FILE_BASENAME));dumpCheckPointsThread.start();BroadcastReceiver br = new BroadcastReceiver() {@Override public void onReceive(Context context, Intent intent) {// We don't allow apps to cancel this, so ignore the result.actionDone();}};/** Write a system property in case the system_server reboots before we* get to the actual hardware restart. If that happens, we'll retry at* the beginning of the SystemServer startup.*/{String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : "");SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);}/** If we are rebooting into safe mode, write a system property* indicating so.*/if (mRebootSafeMode) {SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");}shutdownTimingLog.traceBegin("DumpPreRebootInfo");try {Slog.i(TAG, "Logging pre-reboot information...");PreRebootLogger.log(mContext);} catch (Exception e) {Slog.e(TAG, "Failed to log pre-reboot information", e);}shutdownTimingLog.traceEnd(); // DumpPreRebootInfometricStarted(METRIC_SEND_BROADCAST);shutdownTimingLog.traceBegin("SendShutdownBroadcast");Log.i(TAG, "Sending shutdown broadcast...");// First send the high-level shut down broadcast.mActionDone = false;Intent intent = new Intent(Intent.ACTION_SHUTDOWN);intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);mContext.sendOrderedBroadcastAsUser(intent,UserHandle.ALL, null, br, mHandler, 0, null, null);final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;synchronized (mActionDoneSync) {while (!mActionDone) {long delay = endTime - SystemClock.elapsedRealtime();if (delay <= 0) {Log.w(TAG, "Shutdown broadcast timed out");break;} else if (mRebootHasProgressBar) {int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);sInstance.setRebootProgress(status, null);}try {mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS));} catch (InterruptedException e) {}}}if (mRebootHasProgressBar) {sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);}shutdownTimingLog.traceEnd(); // SendShutdownBroadcastmetricEnded(METRIC_SEND_BROADCAST);Log.i(TAG, "Shutting down activity manager...");shutdownTimingLog.traceBegin("ShutdownActivityManager");metricStarted(METRIC_AM);final IActivityManager am =IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));if (am != null) {try {am.shutdown(MAX_BROADCAST_TIME);} catch (RemoteException e) {}}if (mRebootHasProgressBar) {sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);}shutdownTimingLog.traceEnd();// ShutdownActivityManagermetricEnded(METRIC_AM);Log.i(TAG, "Shutting down package manager...");shutdownTimingLog.traceBegin("ShutdownPackageManager");metricStarted(METRIC_PM);final PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");if (pm != null) {pm.shutdown();}if (mRebootHasProgressBar) {sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);}shutdownTimingLog.traceEnd(); // ShutdownPackageManagermetricEnded(METRIC_PM);// Shutdown radios.shutdownTimingLog.traceBegin("ShutdownRadios");metricStarted(METRIC_RADIOS);shutdownRadios(MAX_RADIO_WAIT_TIME);if (mRebootHasProgressBar) {sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);}shutdownTimingLog.traceEnd(); // ShutdownRadiosmetricEnded(METRIC_RADIOS);if (mRebootHasProgressBar) {sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);// If it's to reboot to install an update and uncrypt hasn't been// done yet, trigger it now.uncrypt();}// Wait for the check points dump thread to finish, or kill it if not finished in time.shutdownTimingLog.traceBegin("ShutdownCheckPointsDumpWait");try {dumpCheckPointsThread.join(MAX_CHECK_POINTS_DUMP_WAIT_TIME);} catch (InterruptedException ex) {}shutdownTimingLog.traceEnd(); // ShutdownCheckPointsDumpWaitshutdownTimingLog.traceEnd(); // SystemServerShutdownmetricEnded(METRIC_SYSTEM_SERVER);saveMetrics(mReboot, mReason);// Remaining work will be done by init, including vold shutdownrebootOrShutdown(mContext, mReboot, mReason);
}
可以看到发出Intent.ACTION_SHUTDOWN广播,并且执行关机操作。
总结:
点击电源键,执行PhoneWindowManager里的dispatchUnhandledKey方法
然后在dispatchUnhandledKey进行处理,当判断是长按事件时,会执行powerLongPress方法
然后在执行showGlobalActionsInternal()弹出(飞行模式,关机,重启等)
在我们点击关机的时候,会执行PowerAction的onPress方法,执行WindowManagerService的shutdown方法,进而执行ShutdownThread的shutdown方法,在里面会执行真正的关机操作,并且可以看到,系统在关机之前会发出一个Intent.ACTION_SHUTDOWN广播后执行关机。
链接:https://blog.csdn.net/jwg1988/article/details/123631476?spm=1001.2014.3001.5501
相关文章:
Android 电源键事件流程分析
Android 电源键事件流程分析 电源按键流程处理逻辑在 PhoneWindowManager.java类中的 dispatchUnhandledKey 方法中 frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java从dispatchUnhandledKey方法开始分析 Overridepublic KeyEvent dis…...
游戏搬砖简述-1
游戏搬砖是一种在游戏中通过重复性的任务来获取游戏内货币或物品的行为。这种行为在游戏中非常普遍,尤其是在一些MMORPG游戏中。虽然游戏搬砖看起来很无聊,但是它确实是一种可以赚钱的方式,而且对于一些玩家来说,游戏搬砖也是一种…...
多线程基础总结
1. 为什么要有多线程? 线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中实际运行单位。 进程:进程是程序的基本执行实体。 什么是多线程? 有了多线程,我们就可以让程序同时做…...
视频理解AI模型分类与汇总
人工智能领域视频模型大体也经历了从传统手工特征,到卷积神经网络、双流网络(2014年-2017年)、3D卷积网络、transformer的发展脉络。为了时序信息,有的模型也结合用LSTM。 视频的技术大多借鉴图像处理技术,只是视频比…...
【Linux】多线程 --- 线程同步与互斥+生产消费模型
人生总是那么痛苦吗?还是只有小时候是这样? —总是如此 文章目录 一、线程互斥1.多线程共享资源访问的不安全问题2.提出解决方案:加锁(局部和静态锁的两种初始化/销毁方案)2.1 对于锁的初步理解和实现2.2 局部和全局锁…...
17.模型的定义
学习要点: 1.默认设置 2.模型定义 本节课我们来开始学习数据库的模型部分的定义和默认值的设置。 一.默认设置 1. 框架可以使用 Eloquent ORM 进行数据库交互,也就是关系对象模型; 2. 在数据库入门阶段,我们已经创建了…...
golang 记录交叉编译sqlite的报错信息 go build -ldflags
go build -ldflags ‘-s -w --extldflags “-static -fpic”’ -o go-web main.go [gos20230512]# CGO_ENABLED1 CCaarch64-linux-gnu-gcc CXXaarch64-linux-gnu-g GOOSlinux GOARCHarm64 go build -ldflags -s -w --extldflags "-static -fpic" -o go-web m…...
ChatGPT AI使用成本
LLM “经济学”:ChatGPT 与开源模型,二者之间有哪些优劣权衡?谁的部署成本更低? 太长不看版:对于日均请求在 1000 次左右的低频使用场景,ChatGPT 的实现成本低于部署在 AWS 上的开源大模型。但面对每天数以…...
腾讯云与中电金信发布联合核心方案
5月11日,以“聚力革新,行稳致远”为主题的 “腾讯金融云国产化战略峰会”在北京举办,来自金融业、科技侧、研究机构的专家学者同聚一堂,共同探讨银行核心下移方法论以及国产化转型实践等话题。会议期间,中电金信副总经…...
老胡的周刊(第090期)
老胡的信息周刊[1],记录这周我看到的有价值的信息,主要针对计算机领域,内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。 🎯 项目 privateGPT[2] 为保证数据私密性,…...
2023-数仓常见问题以及解决方案
01 数据仓库现状 小 A 公司创建时间比较短,才刚过完两周岁生日没多久;业务增长速度快,数据迅速增加,同时取数需求激增与数据应用场景对数据质量、响应速度、数据时效性与稳定要求越来越高;但技术能力滞后业务增长&…...
没关系,前端还死不了
前言 网络上的任何事情都可以在《乌合之众》书中找到答案。大众言论没有理性,全是极端,要么封神,要么踩死。不少人喷前端,说前端已死?前端内卷?前端一个月800包吃住? 对此我想说,“…...
OpenSSL-基于IP或域名生成自签名证书脚本
个人名片: 对人间的热爱与歌颂,可抵岁月冗长🌞 Github👨🏻💻:念舒_C.ying CSDN主页✏️:念舒_C.ying 个人博客🌏 :念舒_C.ying 一、安装 需要安装并配置Op…...
如何在C#中创建和使用自定义异常
C#是一种强类型语言,可以捕获和处理各种异常,从而帮助我们发现程序中出现的错误。在程序开发过程中,如果需要找到特定的错误情况并处理,这时就需要创建自定义异常。下面介绍一下如何在C#中创建和使用自定义异常。 1、什么是异常&…...
通过systemctl管理服务
文章目录 通过systemctl管理服务通过systemctl管理单一服务(service unit)使用案例服务启动/关闭/查看的练习关于systemctl命令启动/停止服务后面的后缀名是否加? 通过systemctl查看系统上所有的服务使用案例 通过systemctl管理不同的操作环境(target unit)使用案例…...
面经|小红书经营分析师
感觉面试官还挺严肃的,并且猎头说因为工作经验不够是外包岗位。 但是没想到最后败在了SQL上,很久没刷题了 平时工作中还是需要想下给公司整体带来的收益结果是什么,实际工作中不一定会用到,但是要有这个思路,面试的时候…...
abpvnext后台工作者使用quartz扩展的一些思路和使用细节记录--(未完待续)
需求背景描述: 我有一个温湿度数据采集的物联网系统,每个租户都需要定时执行若干种任务, 不同的租户, 他定时执行的间隔不一样 ,比如 A租户,数据保存间隔60秒,数据是否超限的轮询间隔是是600…...
提升应届生职场竞争力:有效策略和关键推动因素
应届生进入职场是一个关键的阶段,他们需要通过有效的方法和策略来提高自己的竞争力,以适应职场的挑战并取得成功。以下是一些可以帮助应届生提升竞争力的方法和策略,以及对其职场发展起到关键推动和支撑作用的方面。 学习和继续教育ÿ…...
PBDB Data Service:List of fossil collections(化石采集记录列表)
List of fossil collections(化石采集记录列表) 描述用法参数以下参数可用于按各种条件查询集合。以下参数可用于筛选所选内容以下参数还可用于根据分类筛选结果列表以下参数可用于生成数据存档您可以使用以下参数选择要检索的额外信息,以及要…...
centos安装SNB服务
Samba 是一种开源软件,它提供了一种让 Linux 和 Unix 系统与 Windows 操作系统相互通信的标准协议。Samba 允许 Linux 和 Unix 系统作为文件服务器和打印服务器,提供 Windows 客户端所需的服务。 具体来说,Samba 通过实现 SMB/CIFS 协议来实现…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...
【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...
算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...
