android L 关机流程分析

frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java

ShutdownThread.java文件

stop playing music,因为后面可能要playing shutdown music.

代码如下:

1
2
3
4
5
6
private static void beginShutdownSequence(Context context) {
  ....
       
....

}

?

show system dialog to indicate phone is shutting down,如果没有关机动画的话,要show一个关机提示出来。

代码如下

  synchronized (mEnableAnimatingSync) {
            if(!mEnableAnimating) {
                //sInstance.mPowerManager.setBacklightBrightness(PowerManager.BRIGHTNESS_DIM);
            } else {
                if (mShutOffAnimation) {
                    Log.e(TAG, "mIBootAnim.isCustBootAnim() is true");
                    bootanimCust();
                } else {
                    pd = new ProgressDialog(context);
                    pd.setTitle(context.getText(com.android.internal.R.string.power_off));
                   pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
                    pd.setIndeterminate(true);
                    pd.setCancelable(false);
                    pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
                    /* To fix video+UI+blur flick issue */
                    pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
                    pd.show();
                }
                sInstance.mHandler.postDelayed(mDelayDim, screenTurnOffTime); 
            }
        }
 

?
   Hold the wakelock,make sure we never fall asleep again,抓锁防止机器关机过程中休眠

 代码如下:

       sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
       PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");//这个只是锁住cpu不进入休眠,但screen是off的,需full锁来保证screen常亮
       sInstance.mCpuWakeLock.setReferenceCounted(false);
       sInstance.mCpuWakeLock.acquire();

make sure the screen stays on,再抓一个full锁,防止屏幕半暗

 代码如下:

       sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
       PowerManager.FULL_WAKE_LOCK, TAG + "-screen");//保持srceen常亮
       sInstance.mScreenWakeLock.setReferenceCounted(false);

       sInstance.mScreenWakeLock.acquire();

    sending shutdown broadcast,发出广播,通知各app该保存数据赶紧的,我要关机了

代码如下:

     /// M: 2012-05-20 ALPS00286063 @{
        mContext.sendBroadcast(new Intent("android.intent.action.ACTION_PRE_SHUTDOWN"));
        /// @} 2012-05-20
        Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
        intent.putExtra("_mode", mShutdownFlow);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        mContext.sendOrderedBroadcastAsUser(intent,
            UserHandle.ALL, null, br, mHandler, 0, null, null);

?
   shutdown activity manager,关闭activity manager,即关闭AppOpsService,UsageStatsService,BatteryStatsService


注意:android L 与KK在关闭UsageStatsService上有所区别

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

[ActivityManagerService.java]

final IActivityManager am =

      ActivityManagerNative.asInterface(ServiceManager.checkService(activity));
        
if(am != null) {
            
try{
      am.shutdown(MAX_BROADCAST_TIME);
            
}catch(RemoteException e) {

   }
 }

   shutdown package manager,保存app使用时间到 disk里,这是android L新增的功能。

代码如下:

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

[PackageManagerService.java]

finalPackageManagerService pm =
(PackageManagerService) ServiceManager.getService( package );

if(pm != null) {
    pm.shutdown();
}

?
   shutdown radio[NFC,BT,MODEM],注意这里关闭modem这块与andorid KK的不一样

代码如下:

shutdownRadios(MAX_RADIO_WAIT_TIME);

shutdown MountService,特别这里会导致关机失败。

代码如下:

  // Set initial variables and time out time.
            mActionDone = false;
            final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
            synchronized (mActionDoneSync) {
                try {
                    final IMountService mount = IMountService.Stub.asInterface(
                            ServiceManager.checkService("mount"));
                    if (mount != null) {
                        mount.shutdown(observer);
                    } else {
                        Log.w(TAG, "MountService unavailable for shutdown");
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Exception during MountService shutdown", e);
                }
                while (!mActionDone) {
                    long delay = endShutTime - SystemClock.elapsedRealtime();
                    if (delay <= 0) {
                        Log.w(TAG, "Shutdown wait timed out");
                        if (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
                            Log.d(TAG, "change shutdown flow from ipo to normal: MountService");
                            mShutdownFlow = NORMAL_SHUTDOWN_FLOW;
                        }
                        break;
                    }
                    try {
                        mActionDoneSync.wait(delay);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

?
     走完上层关机流程,下面就要执行关机动作了。

  代码如下

/**

 * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
     * or {@link #shutdown(Context, boolean)} instead.
     *
     * @param reboot true to reboot or false to shutdown
     * @param reason reason for reboot
     */
    public static void rebootOrShutdown(boolean reboot, String reason) {
        if (reboot) {
            Log.i(TAG, "Rebooting, reason: " + reason);
            PowerManagerService.lowLevelReboot(reason);
            Log.e(TAG, "Reboot failed, will attempt shutdown instead");
        } else if (SHUTDOWN_VIBRATE_MS > 0) {
            // vibrate before shutting down
            Vibrator vibrator = new SystemVibrator();
            try {
                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
            } catch (Exception e) {
                // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                Log.w(TAG, "Failed to vibrate during shutdown.", e);
            }
            // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
            try {
                Thread.sleep(SHUTDOWN_VIBRATE_MS);
            } catch (InterruptedException unused) {
            }
        }
        // Shutdown power
        // power off auto test, don't modify
        Log.i(TAG, "Performing low-level shutdown...");
        //unmout data/cache partitions while performing shutdown
        PowerManagerService.lowLevelShutdown();
        /* sleep for a long time, prevent start another service */
        try {
            Thread.currentThread().sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            Log.e(TAG, "Shutdown rebootOrShutdown Thread.currentThread().sleep exception!");
        }
    }

?
    从代码上看始终会走到lowLevelShutdown(),但如果是重启就不会,lowLevelReboot()就停止了。

lowLevelShutdown()与lowLevelReboot()都在PowerManagerService.java实现,其实都只是设置一个属性:SystemProperties.set(sys.powerctl, xxx);

正是这个动作触发关机流程往下走,这涉及到init进程的4大功能,请参考我的另一篇文章Android的init进程

sys.powerctl属性触发开关在init.rc定义

 system/core/rootdir/init.rc

on property:sys.powerctl=*
    powerctl ${sys.powerctl}

   我们来解读这句话,on property:sys.powerctl=*表示当属性sys.powerctl设置为任何值是都会跑到这里,触发动作是powerctl  ${sys.powerctl},这个动作的意思是调用powerctl指令,并把sys.powerctl的值传给它。powerctl指令在init进程会执行。

从下面的表可知,powerctl对应的操作是do_powerctl

[system/core/init/keywords.h]

KEYWORD(powerctl,    COMMAND, 1, do_powerctl)

    代码如下:
     [system/core/init/builtins.c]
intdo_powerctl(intnargs,char**args)
{
....
 
    returnandroid_reboot(cmd,0, reboot_target);
}

?
    它调用android_reboot()函数,实现如下:

[system/core/libcutils/android_reboot.c]

inta ndroid_reboot(intcmd,intflags UNUSED, char*arg)
{
    intret;
 
    sync();
    remount_ro();
 
    switch(cmd) {
        caseANDROID_RB_RESTART:
            ret = reboot(RB_AUTOBOOT);
            break;
 
        caseANDROID_RB_POWEROFF:
            ret = reboot(RB_POWER_OFF);
            break;
 
        caseANDROID_RB_RESTART2:
            ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                           LINUX_REBOOT_CMD_RESTART2, arg);
            break;
 
        default:
            ret = -1;
    }
 
    returnret;
}

?
   从这里看出它的主要工作:

sync() 回写block设备的内容,这是阻塞型操作。

remount_ro() 把block设备remount成ro,这里有个关键LOG:SysRq : Emergency Remount R/O,这是在logkit所能看到的最后一句LOG,因为remount成ro了,后面的LOG要通过last kmsg技术导出来。

reboot()或者syscall(__NR_reboot....,这点与android KK不同,这边直接用syscall功能,KK则通过汇编。

后面syscall(__NR_reboot...知道,直接调用了linux的__NR_reboot系统调用,这个系统调用会跑哪里?后面会讲。

reboot()这个函数实现如下:

[bionic/libc/bionic/reboot.cpp]

intreboot(intmode) {
  return__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL);
}

?
    调用了__reboot,它在汇编实现 如下: 

[bionic/libc/arch-arm/syscalls/__reboot.S]

ENTRY(__reboot)
    mov     ip, r7
    ldr     r7, =__NR_reboot//也跑到__NR_reboot系统调用
    swi     #0
    mov     r7, ip
    cmn     r0, #(MAX_ERRNO + 1)
    bxls    lr
    neg     r0, r0
    b       __set_errno_internal
END(__reboot)

     __NR_reboot对应的内核入口在哪里?

如下:

[bionic/libc/kernel/uapi/asm-generic/unistd.h]

 #define __NR_reboot 142

?
    它在内核入口如下:

注:bionic/libc/kernel/uapi/asm-generic/unistd.h与kernel/include/uapi/asm-generic/unistd.h是对应的,方便以后代码追踪

[kernel/include/uapi/asm-generic/unistd.h]

#define __NR_reboot 142
__SYSCALL(__NR_reboot, sys_reboot)

__NR_reboot 映射到 sys_reboot

grep 下sys_reboot 找不到,其实在这里

用SYSCALL_DEFINE定义

[kernel/kernel/sys.c]

SYSCALL_DEFINE4(reboot,int, magic1, int, magic2, unsigned int, cmd,
        void__user *, arg)
{
    struct pid_namespace *pid_ns = task_active_pid_ns(current);
    charbuffer[256];
    intret = 0;
 
    /* We only trust the superuser with rebooting the system. */
    if(!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
        return-EPERM;
 
    /* For safety, we require magic arguments. */
    if(magic1 != LINUX_REBOOT_MAGIC1 ||
        (magic2 != LINUX_REBOOT_MAGIC2 &&
                    magic2 != LINUX_REBOOT_MAGIC2A &&
            magic2 != LINUX_REBOOT_MAGIC2B &&
                    magic2 != LINUX_REBOOT_MAGIC2C))
        return-EINVAL;
 
    /*
     * If pid namespaces are enabled and the current task is in a child
     * pid_namespace, the command is handled by reboot_pid_ns() which will
     * call do_exit().
     */
    ret = reboot_pid_ns(pid_ns, cmd);
    if(ret)
        returnret;
 
    /* Instead of trying to make the power_off code look like
     * halt when pm_power_off is not set do it the easy way.
     */
    if((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
        cmd = LINUX_REBOOT_CMD_HALT;
 
    mutex_lock(&reboot_mutex);
    switch(cmd) {
    caseLINUX_REBOOT_CMD_RESTART:
        kernel_restart(NULL);
        break;
 
    caseLINUX_REBOOT_CMD_CAD_ON:
        C_A_D = 1;
        break;
 
    caseLINUX_REBOOT_CMD_CAD_OFF:
        C_A_D = 0;
        break;
 
    caseLINUX_REBOOT_CMD_HALT:
        kernel_halt();
        do_exit(0);
        panic(cannot halt);
 
    caseLINUX_REBOOT_CMD_POWER_OFF:
        kernel_power_off();
        do_exit(0);
        break;
 
    caseLINUX_REBOOT_CMD_RESTART2:
        if(strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {
            ret = -EFAULT;
            break;
        }
        buffer[sizeof(buffer) - 1] = '';
 
        kernel_restart(buffer);
        break;
 
#ifdef CONFIG_KEXEC
    caseLINUX_REBOOT_CMD_KEXEC:
        ret = kernel_kexec();
        break;
#endif
 
#ifdef CONFIG_HIBERNATION
    caseLINUX_REBOOT_CMD_SW_SUSPEND:
        ret = hibernate();
        break;
#endif
 
    default:
        ret = -EINVAL;
        break;
    }
    mutex_unlock(&reboot_mutex);
    returnret;
}


voidkernel_restart_prepare(char*cmd)
{
    blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);
    system_state = SYSTEM_RESTART;
    usermodehelper_disable();
    device_shutdown();
}

    除了前面不同,都调用了device_shutdown()函数,关闭外设。

 machine_power_off() machine_resestart()函数实现

voidmachine_power_off(void)
{
    preempt_disable();
    smp_send_stop();
 
    if(pm_power_off)
        pm_power_off();//关机
}

 

voidmachine_restart(char*cmd)
{
    preempt_disable();
    smp_send_stop();
 
    /* Flush the console to make sure all the relevant messages make it
     * out to the console drivers */
    arm_machine_flush_console();
 
    arm_pm_restart(reboot_mode, cmd);//重启
 
    /* Give a grace period for failure to restart of 1s */
    mdelay(1000);
 
    /* Whoops - the platform was unable to reboot. Tell the user! */
    printk(Reboot failed -- System halted
);
    local_irq_disable();
    while(1);
}

?

   pm_power_offf() arm_pm_restart()都是一个函数指针

赋值如下:

[kernel/drivers/power/reset/msm-poweroff.c]

 

pm_power_off = do_msm_poweroff;
arm_pm_restart = do_msm_restart;


    高通平台的关机代码与之前有所不同,现在文件msm-poweroff.c以前是restart.c。

do_msm_poweroff()与do_msm_restart()实现如下:

 

staticvoiddo_msm_poweroff(void)
{
....
    pr_notice(Powering off the SoC
);//关键打印
#ifdef CONFIG_MSM_DLOAD_MODE
    set_dload_mode(0);//关机,所以dloadmode是0
#endif
    qpnp_pon_system_pwr_off(PON_POWER_OFF_SHUTDOWN);//配置PMIC,是关机
.....
    /* MSM initiated power off, lower ps_hold */
    __raw_writel(0, msm_ps_hold);//拉 PS_HOLD,执行关机动作。
 
    mdelay(10000);
    pr_err(Powering off has failed
);
    return;
}


staticvoiddo_msm_restart(enumreboot_mode reboot_mode, constchar*cmd)
...
 
    pr_notice(Going down forrestart now
);//关键打印
 
    msm_restart_prepare(cmd);//重启准备前动作
 
#ifdef CONFIG_MSM_DLOAD_MODE
    /*
     * Trigger a watchdog bite here and if this fails,
     * device will take the usual restart path.
     */
 
    if(WDOG_BITE_ON_PANIC && in_panic)
        msm_trigger_wdog_bite();
#endif
 
....
    halt_spmi_pmic_arbiter();
    __raw_writel(0, msm_ps_hold);//拉PS_HOLD重启
 
    mdelay(10000);
}



      msm_restart_prepare()实现

 

staticvoidmsm_restart_prepare(constchar*cmd)
{
#ifdef CONFIG_MSM_DLOAD_MODE
 
    /* Write download mode flags if we're panic'ing
     * Write download mode flags if restart_mode says so
     * Kill download mode if master-kill switch is set
     */
 
    set_dload_mode(download_mode &&
            (in_panic || restart_mode == RESTART_DLOAD));//设置dload
#endif
 
    /* Hard reset the PMIC unless memory contents must be maintained. */
    if(get_dload_mode() || (cmd != NULL && cmd[0] != ''))
        qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET);//设置PIMC为热重启
    else
        qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET);//设置PIMC为硬重启
 
    if(cmd != NULL) {
        if(!strncmp(cmd, bootloader, 10)) {
            __raw_writel(0x77665500, restart_reason);//写一些东东到IMEM,用于bootloader,recovery等
        }elseif(!strncmp(cmd, recovery, 8)) {
            __raw_writel(0x77665502, restart_reason);
        }elseif(!strcmp(cmd, rtc)) {
            __raw_writel(0x77665503, restart_reason);
        }elseif(!strncmp(cmd, oem-, 4)) {
            unsignedlongcode;
            intret;
            ret = kstrtoul(cmd + 4,16, &code);
            if(!ret)
                __raw_writel(0x6f656d00| (code & 0xff),
                         restart_reason);
        }elseif(!strncmp(cmd, edl, 3)) {
            enable_emergency_dload_mode();
        }else{
            __raw_writel(0x77665501, restart_reason);
        }
    }
 
.....
 
}

 do_msm_poweroff()与do_msm_restart()都设置了dload,PMIC,唯一不同的是do_msm_restart()里多了一个__raw_writel的动作,即reason写入IMEM,目的在于重启进入sbl1时判断应该进入那种模式,如我们开发用的bootloader模式,恢复出厂设置的recovery模式等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值