1. 程式人生 > >【Android休眠】之休眠鎖的獲取和釋放

【Android休眠】之休眠鎖的獲取和釋放

Linux 3.10
Android 4.4

http://blog.csdn.net/u013686019/article/details/53691895

一、PowerManagerService

引起休眠動作(進入休眠前執行一些必要的操作)的事件有兩個:

  • PowerKey事件,通過JNI呼叫PowerManagerService中的goToSleepFromNative()方法
  • Timeout,指【設定->顯示->休眠】中設定的Timeout數值

Android休眠在PowerManagerService中的流程如下圖:



圖示:最終都會呼叫到updatePowerStateLocked()方法,在更新一些標誌的狀態、傳送休眠通知後,呼叫updateSuspendBlockerLocked()執行休眠鎖的釋放動作

二、PowerManagerService中Timeout處理流程

/**
 * PowerManagerService設定了很多的標誌位,用來標識某個事件的狀態是否發生改變,比如:
 * DIRTY_SETTINGS,一旦系統設定發生變化,DIRTY_SETTINGS位就會被設定,
 * 處理函式檢測到DIRTY_SETTINGS被置位,就進行相應的動作
 * dirty:包含了所有發生變化的標誌
 */
private void updateUserActivitySummaryLocked(long now, int dirty) {
	// Update the status of the user activity timeout timer.
	if ((dirty & (DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {
		// 1、訊息佇列中含有尚未處理的MSG_USER_ACTIVITY_TIMEOUT,就移除,避免重複進入休眠操作
		mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);

		long nextTimeout = 0;
		// 2、mWakefulness != WAKEFULNESS_ASLEEP:當前醒著
		if (mWakefulness != WAKEFULNESS_ASLEEP) {
			// 3、獲取Timeout的值,比如30s
			final int screenOffTimeout = getScreenOffTimeoutLocked();
			// 螢幕在熄滅前,會先變暗一段時間,這段時間叫DimDuration,計算方式:
			// SCREEN_DIM_DURATION = 7s,MAXIMUM_SCREEN_DIM_RATIO = 0.2
			// Math.min(SCREEN_DIM_DURATION, (int)(screenOffTimeout * MAXIMUM_SCREEN_DIM_RATIO))
			// 4、獲取DimDuration的值,30s x 0.2 = 6s
			final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);

			mUserActivitySummary = 0;
			// 5、mLastUserActivityTime >= mLastWakeTime: 使用者最後使用機器的時間在上次喚醒時間之後
			if (mLastUserActivityTime >= mLastWakeTime) {
				// nextTimeout:此處指到螢幕Dim的時間間隔
				// 6、nextTimeout的時間:BASE + 30 - 6 = BASE + 24
				nextTimeout = mLastUserActivityTime
						+ screenOffTimeout - screenDimDuration;
				if (now < nextTimeout) {
					// now在螢幕Dim之前,說明螢幕亮著,設定flag
					mUserActivitySummary |= USER_ACTIVITY_SCREEN_BRIGHT;
				} else {
					// extTimeout:此處指到螢幕熄滅的時間間隔
					//7、nextTimeout的時間:BASE + 30 = BASE + 30
					nextTimeout = mLastUserActivityTime + screenOffTimeout;
					// 8、now處於螢幕Dim之後、螢幕熄滅之前設定DIM flag
					if (now < nextTimeout) {
						mUserActivitySummary |= USER_ACTIVITY_SCREEN_DIM;
					}
				}
			}
			if (mUserActivitySummary == 0
					&& mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {
				nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
				if (now < nextTimeout
						&& mDisplayPowerRequest.screenState
								!= DisplayPowerRequest.SCREEN_STATE_OFF) {
					mUserActivitySummary = mDisplayPowerRequest.screenState
							== DisplayPowerRequest.SCREEN_STATE_BRIGHT ?
							USER_ACTIVITY_SCREEN_BRIGHT : USER_ACTIVITY_SCREEN_DIM;
				}
			}
			// mUserActivitySummary發生了改變
			if (mUserActivitySummary != 0) {
				Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
				Slog.i(TAG, "updateUserActivitySummaryLocked, send MSG_USER_ACTIVITY_TIMEOUT");
				msg.setAsynchronous(true);
				mHandler.sendMessageAtTime(msg, nextTimeout);
			}
		} else {
			mUserActivitySummary = 0;
		}
	}
}

MSG_USER_ACTIVITY_TIMEOUT事件處理:

private final class PowerManagerHandler extends Handler {
	@Override
	public void handleMessage(Message msg) {
		switch (msg.what) {
		case MSG_USER_ACTIVITY_TIMEOUT:
			handleUserActivityTimeout();
			break;
	}
}

/**
 * Called when a user activity timeout has occurred.
 * Simply indicates that something about user activity has changed so that the new
 * state can be recomputed when the power state is updated.
 */
private void handleUserActivityTimeout() { // runs on handler thread
	mDirty |= DIRTY_USER_ACTIVITY;
	updatePowerStateLocked();
}


三、PowerManagerService中休眠鎖的獲取/釋放

這部分程式碼清晰,直接看下:

private void updatePowerStateLocked() {
	if (!mSystemReady || mDirty == 0) {
		return;
	}
	// Phase 0: Basic state updates.

	// Phase 1: Update wakefulness.
	
	// Phase 2: Update dreams and display power state.
	
	// Phase 3: Send notifications, if needed.
	
	// Phase 4: Update suspend blocker.
	// Because we might release the last suspend blocker here, we need to make sure
	// we finished everything else first!
	updateSuspendBlockerLocked();
}

/**
 * Updates the suspend blocker that keeps the CPU alive.
 */
private void updateSuspendBlockerLocked() {
	final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);
	final boolean needDisplaySuspendBlocker = needDisplaySuspendBlocker();

	// First acquire suspend blockers if needed.
	if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
		mWakeLockSuspendBlocker.acquire();
		mHoldingWakeLockSuspendBlocker = true;
	}
	if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) {
		mDisplaySuspendBlocker.acquire();
		mHoldingDisplaySuspendBlocker = true;
	}

	// Then release suspend blockers if needed.
	if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
		mWakeLockSuspendBlocker.release();
		mHoldingWakeLockSuspendBlocker = false;
	}
	if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) {
		mDisplaySuspendBlocker.release();
		mHoldingDisplaySuspendBlocker = false;
	}
}

private final class SuspendBlockerImpl implements SuspendBlocker {
	private final String mName;
	private int mReferenceCount;

	public SuspendBlockerImpl(String name) {
		mName = name;
	}
	
	@Override
	public void acquire() {
		synchronized (this) {
			mReferenceCount += 1;
			if (mReferenceCount == 1) {
				nativeAcquireSuspendBlocker(mName);
			}
		}
	}

	@Override
	public void release() {
		synchronized (this) {
			mReferenceCount -= 1;
			if (mReferenceCount == 0) {

				nativeReleaseSuspendBlocker(mName);
			}
		}
	}
}

休眠鎖的獲取和釋放,最終通過JNI方式讀寫/sys/power/wake_lock、/sys/power/wake_unlock:
// 1、JNI介面
com_android_server_power_PowerManagerService.cpp (frameworks\base\services\jni)
static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass clazz, jstring nameStr) {
    ScopedUtfChars name(env, nameStr);
    acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str());
}

// 2、定義要操作的檔案
power.c (hardware\libhardware_legacy\power)
const char * const NEW_PATHS[] = {
    "/sys/power/wake_lock",
    "/sys/power/wake_unlock",
};

// 3、初始化裝置節點
static inline void initialize_fds(void)
{
    if (g_initialized == 0) {
        if(open_file_descriptors(NEW_PATHS) < 0)
            open_file_descriptors(OLD_PATHS);
        g_initialized = 1;
    }
}

static int open_file_descriptors(const char * const paths[])
{
    int i;
    for (i=0; i<OUR_FD_COUNT; i++) {
        int fd = open(paths[i], O_RDWR);
        if (fd < 0) {
            fprintf(stderr, "fatal error opening \"%s\"\n", paths[i]);
            g_error = errno;
            return -1;
        }
        g_fds[i] = fd;
    }

    g_error = 0;
    return 0;
}

// 4、id即為鎖的名字,之後就是讀寫裝置
int acquire_wake_lock(int lock, const char* id)
{
    initialize_fds();

    if (g_error) return g_error;

    int fd;

    if (lock == PARTIAL_WAKE_LOCK) {
        fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];
    }
    else {
        return EINVAL;
    }

    return write(fd, id, strlen(id));
}