zoukankan      html  css  js  c++  java
  • 【Android休眠】之休眠锁的获取和释放

    受不了xxxx恶心人的行为,遂搬迁至博客园。
    始发:2016-12-16 13:11:41
    
    版本信息:
    Linux:3.10
    Android:4.4
    

    一、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;
    		}
    	}
    }
    

    SG_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 (frameworksaseservicesjni)
    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 (hardwarelibhardware_legacypower)
    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"
    ", 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));
    }
    
  • 相关阅读:
    通过数据库查看EBS的登录地址
    重启redis报错:Waiting for Redis to shutdown
    Tomcat8启动报there was insufficient free space available after evicting expired cache entries
    EBS安装过程报错,oracle.apps.fnd.txk.config.ProcessStateException: FileSys OS COMMAND Failed : Exit=2 See log for details.
    CentOS系统将UTC时间修改为CST时间
    Tomcat启动时报错,Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext
    LICEcap 录制Gif动画
    Java 8 Optional In Depth
    IntelliJ IDEA推荐插件
    Java 8 – Convert Map to LIST
  • 原文地址:https://www.cnblogs.com/rockyching2009/p/13283723.html
Copyright © 2011-2022 走看看