Android 电源管理分析

一 简述

手机不同于PC,手机使用的是可移动电源,由于电源的电量有限,因此如何做到既让“马儿跑又要马儿不吃草”,电源管理系统尤为重要。
本文主要从上层应用入手,介绍安卓系统如何进行电源系统的管理和优化,提高手机的待机能力。
先看一下手机的几个耗电大户,分别是:
1)显示屏
2)AP的cpu和modem的cpu
3)其他的硬件外设
所以,电源优化一般是根据上述几个耗电环节进行优化。
1)显示屏一般技术有:自动熄屏、自动亮度调节、黑白显示等
2)CPU占用上 多核系统的hotplug、动态调频DVFS、PELT或者EAS调度策略、进程冻结技术等,在无交互时cpu自动休眠等
3)硬件外设 在SOC的设计中,可能为每个外设单元设置单独的电源子系统,在空闲状态时,可以自动休眠,设备不运行节省电源的消耗。

二 应用开发中牵扯到的电源管理部分

1) 使屏幕保持开启状态
在Activity 中使用 FLAG_KEEP_SCREEN_ON

    public class MainActivity extends Activity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
      }
    }
    

在应用的布局 XML 文件中,使用 android:keepScreenOn 属性:


        ...
    

2) CPU占用
如果需要使 CPU 保持运行状态,需要 WAKE_LOCK来保持CPU一直处于唤醒状态


持有wakeLock可以避免CPU进入休眠状态

    PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
    WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
            "MyApp::MyWakelockTag");
    wakeLock.acquire();
  1. 使用可使设备保持唤醒状态的广播接收器
    WakefulBroadcastReceiver 是一种特殊类型的广播接收器,可以为应用或者service保持WAKE_LOCK,使用方法如下:

最后用completeWakefulIntent(intent);完成WAKE_LOCK的释放

public class MyWakefulReceiver extends WakefulBroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {

            // Start the service, keeping the device awake while the service is
            // launching. This is the Intent to deliver to the service.
            Intent service = new Intent(context, MyIntentService.class);
            startWakefulService(context, service);
        }
    }
  public class MyIntentService extends IntentService {
        public static final int NOTIFICATION_ID = 1;
        private NotificationManager notificationManager;
        NotificationCompat.Builder builder;
        public MyIntentService() {
            super("MyIntentService");
        }
        @Override
        protected void onHandleIntent(Intent intent) {
            Bundle extras = intent.getExtras();
            // Do the work that requires your app to keep the CPU running.
            // ...
            // Release the wake lock provided by the WakefulBroadcastReceiver.
            MyWakefulReceiver.completeWakefulIntent(intent);
        }
    }

三 PM在FW部分

在frameworks/base/services/core/java/com/android/server/power/目录下,PowerManagerService.java和ThermalManagerService.java
PMS主要调用底层HAL的两处

hardware/interfaces/power/ 
system/hardware/interfaces/suspend/

hardware/interfaces/power/ 主要是power总的控制开关,可以从相关aidl文件可以看出

 interface IPower {

oneway void setMode(in Mode type, in boolean enabled);
 boolean isModeSupported(in Mode type);
oneway void setBoost(in Boost type, in int durationMs);
boolean isBoostSupported(in Boost type);

}

而system/hardware/interfaces/suspend/主要来控制“挂起”相关,以及提供WAKE_LOCK相关控制,其HAL层主要是通过与内核文件的交互来完成相关控制

/sys/power/wake_lock
/sys/power/wake_unlock
/sys/power/wakeup_count
/sys/power/state

主要通过往/sys/power/state写"mem"后系统进入休眠

四 PM内核部分

Android的电源管理系统主要实现在内核的如下目录

kernel/power
drivers/base/power
arch/xxx/mach-xxx/

在我们往/sys/power/state写mem时进入如下函数

kernel/power/main.c
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
               const char *buf, size_t n)
{
    suspend_state_t state;
    int error;

    error = pm_autosleep_lock();
    if (error)
        return error;

    if (pm_autosleep_state() > PM_SUSPEND_ON) {
        error = -EBUSY;
        goto out;
    }

    state = decode_state(buf, n);
    if (state < PM_SUSPEND_MAX) {
        if (state == PM_SUSPEND_MEM)
            state = mem_sleep_current;

        error = pm_suspend(state);
    } else if (state == PM_SUSPEND_MAX) {
        error = hibernate();
    } else {
        error = -EINVAL;
    }

 out:
    pm_autosleep_unlock();
    return error ? error : n;
}

power_attr(state);

通过调用pm_suspend进入suspend state并挂起系统
系统还可以自动挂起,主要实现在kernel/power/autosleep.c中,如果系统不能自动挂起的话,多半是wake_lock没有空导致

static void try_to_suspend(struct work_struct *work)
{
    unsigned int initial_count, final_count;

    if (!pm_get_wakeup_count(&initial_count, true))
        goto out;

    mutex_lock(&autosleep_lock);

    if (!pm_save_wakeup_count(initial_count) ||
        system_state != SYSTEM_RUNNING) {
        mutex_unlock(&autosleep_lock);
        goto out;
    }

    if (autosleep_state == PM_SUSPEND_ON) {
        mutex_unlock(&autosleep_lock);
        return;
    }
    if (autosleep_state >= PM_SUSPEND_MAX)
        hibernate();
    else
        pm_suspend(autosleep_state);

    mutex_unlock(&autosleep_lock);

    if (!pm_get_wakeup_count(&final_count, false))
        goto out;

    /*
     * If the wakeup occured for an unknown reason, wait to prevent the
     * system from trying to suspend and waking up in a tight loop.
     */
    if (final_count == initial_count)
        schedule_timeout_uninterruptible(HZ / 2);

 out:
    queue_up_suspend_work();
}

你可能感兴趣的:(Android 电源管理分析)