Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1304843
  • 博文数量: 478
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 4833
  • 用 户 组: 普通用户
  • 注册时间: 2014-06-28 11:12
文章分类

全部博文(478)

文章存档

2019年(1)

2018年(27)

2017年(21)

2016年(171)

2015年(258)

我的朋友

分类: Android平台

2016-04-07 20:45:18

refer:http://blog.csdn.net/airk000/article/details/9177077

refer:http://blog.csdn.net/leerobin83/article/details/7162751

目录(?)[-]

  1. public void reboot String reason
  2. --------------------------------上层空间--------------------------------
    1. frameworks/base/core/java/android/os/PowerManagerjava
    2. frameworks/base/core/java/android/os/IPowerManageraidl
    3. frameworks/base/services/java/com/androidserver/PowerManagerServicejava
    4. frameworks/base/services/java/com/androidserver/pm/ShutdownThreadjava
    5. frameworks/base/services/java/com/androidserver/PowerManagerServicejava
    6. frameworks/base/services/jni/com_android_server_PowerManagerServicecpp
    7. system/core/lib/cutils/android_rebootc
    8. bionic/libc/unistdre/bootc
  3. --------------------------------KERNEL域--------------------------------
    1. __reboot通过syscall来到内核
    2. kernelsysc
    3. 最终实现
  4. 关机怎么用(最终的总结:本质的调用)
  5. adb 测试重启命令(adb reboot recovery,charger,kpoc,bootloader,etc)
  6. 资料:

    https://developer.android.com/intl/zh-CN/reference/android/os/PowerManager.html
















--------------------------------Introduction--------------------------

         1. 在PowerManager的API文档中,给出了一个关机/重启接口:

        public void reboot (String reason)

         2. 对于这个接口的描述很简单,就是几句话。

                    接口的作用就是重启设备,而且,就算重启成功了也没有返回值。

                    需要包含REBOOT权限,也就是android.permission.REBOOT

                    唯一参数reason代表需要的特定重启模式,比如recovery,当然也可以为null。

--------------------------------上层空间--------------------------------

1.frameworks/base/core/java/android/os/PowerManager.java

  1. /**  
  2.  * Reboot the device.  Will not return if the reboot is 
  3.  * successful.  Requires the {@link android.Manifest.permission#REBOOT} 
  4.  * permission. 
  5.  * 
  6.  * @param reason code to pass to the kernel (e.g., "recovery") to 
  7.  *               request special boot modes, or null. 
  8.  */  
  9. public void reboot(String reason)  
  10. {     
  11.     try {  
  12.         mService.reboot(reason);  
  13.     } catch (RemoteException e) {  
  14.     }     
  15. }   

mService为IPowerManager Binder接口服务。
  1. /** 
  2.  * {@hide} 
  3.  */  
  4. public PowerManager(IPowerManager service, Handler handler)  
  5. {  
  6.     mService = service;  
  7.     mHandler = handler;  
  8. }  


2.frameworks/base/core/java/android/os/IPowerManager.aidl

  1. interface IPowerManager  
  2. {  
  3. ...  
  4. void reboot(String reason);  
  5. ...  
  6. }  

3.frameworks/base/services/java/com/android/server/PowerManagerService.java

  1. /**   
  2.  * Reboot the device immediately, passing 'reason' (may be null) 
  3.  * to the underlying __reboot system call.  Should not return. 
  4.  */  
  5. public void reboot(String reason)  
  6. {      
  7.     mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);  
  8.   
  9.     if (mHandler == null || !ActivityManagerNative.isSystemReady()) {  
  10.         throw new IllegalStateException("Too early to call reboot()");  
  11.     }      
  12.   
  13.     final String finalReason = reason;  
  14.     Runnable runnable = new Runnable() {  
  15.         public void run() {  
  16.             synchronized (this) {  
  17.                 ShutdownThread.reboot(getUiContext(), finalReason, false);  
  18.             }      
  19.   
  20.         }      
  21.     };     
  22.     // ShutdownThread must run on a looper capable of displaying the UI.  
  23.     mHandler.post(runnable);  
  24.   
  25.     // PowerManager.reboot() is documented not to return so just wait for the inevitable.  
  26.     synchronized (runnable) {  
  27.         while (true) {  
  28.             try {  
  29.                 runnable.wait();  
  30.             } catch (InterruptedException e) {   
  31.             }      
  32.         }      
  33.     }      
  34. }  

4.frameworks/base/services/java/com/android/server/pm/ShutdownThread.java

  1. /** 
  2.  * Request a clean shutdown, waiting for subsystems to clean up their 
  3.  * state etc.  Must be called from a Looper thread in which its UI 
  4.  * is shown. 
  5.  * 
  6.  * @param context Context used to display the shutdown progress dialog. 
  7.  * @param reason code to pass to the kernel (e.g. "recovery"), or null. 
  8.  * @param confirm true if user confirmation is needed before shutting down. 
  9.  */  
  10. public static void reboot(final Context context, String reason, boolean confirm) {  
  11.     mReboot = true;  
  12.     mRebootSafeMode = false;  
  13.     mRebootReason = reason;  
  14.     shutdownInner(context, confirm);  
  15. }  
       如上所叙述,这里说明是需要重启,且不是安全模式,重启参数为传递下来的reason,shutdownInner的confirm参数是用来设置是否有确认提示框的,通过reboot接口调用重启是没有的,为false。



        重启的实现在run()中,因为ShutdownThread是Thread的扩展,所以run会自动运行,如下。

 /**
     * Makes sure we handle the shutdown gracefully.
     * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
     */
    public void run() {
        checkShutdownFlow();
        while (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
            stMgr.saveStates(mContext);
            stMgr.enterShutdown(mContext);
            running();
        }
        if (mShutdownFlow != IPO_SHUTDOWN_FLOW) {
            stMgr.enterShutdown(mContext);
            running();
        }
    }




public void running() {

            if(sPreShutdownApi != null){
                try {
                    sPreShutdownApi.onPowerOff();
                } catch (RemoteException e) {
                    Log.e(TAG, "onPowerOff exception" + e.getMessage());
                }
            }else{
                Log.w(TAG, "sPreShutdownApi is null");
            }

            command = SystemProperties.get("sys.ipo.pwrdncap");

            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();
                }
            };
  1.     /* 
  2.      * Write a system property in case the system_server reboots before we 
  3.      * get to the actual hardware restart. If that happens, we'll retry at 
  4.      * the beginning of the SystemServer startup. 
  5.      */   
  6.     {     
  7.         String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");  
  8.         SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);  
  9.     }  
  10.   
  11.     /* 
  12.      * If we are rebooting into safe mode, write a system property 
  13.      * indicating so. 
  14.      */   
  15.     if (mRebootSafeMode) {  
  16.         SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");  
  17.     }  
  18.     ...  
  19.     rebootOrShutdown(mReboot, mRebootReason);  
  20. }   

         如上所叙述,在重启前会将重启原因写入sys.shutdown.requested,如果没有则为空,如果是安全模式还会将persist.sys.safemode置1,之后会进行一些关机前的预处理,关闭ActivityManager以及MountService,最终调用rebootOrShutdown进行关机操作。


======>>>>>>android2中的rebootOrShutdown

  1.     /** 
  2.      * Do not call this directly. Use {@link #reboot(Context, String, boolean)} 
  3.      * or {@link #shutdown(Context, boolean)} instead. 
  4.      * 
  5.      * @param reboot true to reboot or false to shutdown 
  6.      * @param reason reason for reboot 
  7.      */  
  8.     public static void rebootOrShutdown(boolean reboot, String reason) {  
  9.         if (reboot) {  
  10.             Log.i(TAG, "Rebooting, reason: " + reason);   
  11.             try {  
  12.                 PowerManagerService.lowLevelReboot(reason);  ///重启Reboot
  13.             } catch (Exception e) {  
  14.                 Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);  
  15.             }   
  16.         } else if (SHUTDOWN_VIBRATE_MS > 0) {  ///////关机的时候需要震动,就是这里了 
  17.                                                                                //////SHUTDOWN_VIBRATE_MS,默认的定义是500ms。
  18.             // vibrate before shutting down  
  19.             Vibrator vibrator = new SystemVibrator();  
  20.             try {  
  21.                 vibrator.vibrate(SHUTDOWN_VIBRATE_MS);  
  22.             } catch (Exception e) {  
  23.                 // Failure to vibrate shouldn't interrupt shutdown.  Just log it.  
  24.                 Log.w(TAG, "Failed to vibrate during shutdown.", e);  
  25.             }     
  26.                   
  27.             // vibrator is asynchronous so we need to wait to avoid shutting down too soon.  
  28.             try {  
  29.                 Thread.sleep(SHUTDOWN_VIBRATE_MS);  
  30.             } catch (InterruptedException unused) {  
  31.             }     
  32.         }     
  33.               
  34.         // Shutdown power  
  35.         Log.i(TAG, "Performing low-level shutdown...");  
  36.         PowerManagerService.lowLevelShutdown();////直接关机Shutdown
  37.     }  
  38. }  
    如上所叙述,

            如果重启,则调用PowerManagerService的lowLevelReboot函数,参数就是传递下来的reason,稍后分析。    

            如果不是重启,即mReboot=false,那就是需要关机了,在shutdown函数中就能够知道。如下所示:

  1. /** 
  2.  * Request a clean shutdown, waiting for subsystems to clean up their 
  3.  * state etc.  Must be called from a Looper thread in which its UI 
  4.  * is shown. 
  5.  * 
  6.  * @param context Context used to display the shutdown progress dialog. 
  7.  * @param confirm true if user confirmation is needed before shutting down. 
  8.  */  
  9. public static void shutdown(final Context context, boolean confirm) {  
  10.     mReboot = false;  
  11.     mRebootSafeMode = false;  
  12.     shutdownInner(context, confirm);  
  13. }  

        如上所示,会有reboot 以及 shutdown的操作。

        但是在代码逻辑上看,无论如何,最后都会调用一下lowLevelShutdown函数,也就是关机。

         逻辑上,这里可能是个问题,但是实际中,如果重启操作能够调用成功的话,整个系统都重启了,后边的代码当然不可能执行到了。

======>>>>>>android4.4中的rebootOrShutdown

{ android 4.4中的rebootOrShutdown---解析

public static void rebootOrShutdown(boolean reboot, String reason) {
        if (reboot) {
            Log.i(TAG, "Rebooting, reason: " + reason);
            if ( (reason != null) && reason.equals("recovery") ) {
                delayForPlayAnimation();
            }
            try {
                PowerManagerService.lowLevelReboot(reason);
            } catch (Exception e) {
                Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
            }
        } else if (SHUTDOWN_VIBRATE_MS > 0) {
            // vibrate before shutting down
            Vibrator vibrator = new SystemVibrator();
            try {
                vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
            } 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) {
            }
        }

        delayForPlayAnimation();
        // Shutdown power
        // power off auto test, don't modify
        Log.i(TAG, "Performing low-level shutdown...");
        //PowerManagerService.lowLevelShutdown();////已经注释掉了
        //add your func: HDMI off
        //add for MFR
        try {
            if (ImHDMI == null)
                ImHDMI=MediatekClassFactory.createInstance(IHDMINative.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        ImHDMI.hdmiPowerEnable(false);

        //unmout data/cache partitions while performing shutdown

        SystemProperties.set("ctl.start", "shutdown");////直接关机shutdown操作

        /* sleep for a long time, prevent start another service */
        try {
            Thread.currentThread().sleep(Integer.MAX_VALUE);
        } catch ( Exception e) {
            Log.e(TAG, "Shutdown rebootOrShutdown Thread.currentThread().sleep exception!");
        }
    }


}

          目光转回PowerManagerService 中进行的lowLevelReboot()调用。。。。

4.frameworks/base/services/java/com/android/server/PowerManagerService.java

  1. /**   
  2.  * Low-level function to reboot the device. 
  3.  * 
  4.  * @param reason code to pass to the kernel (e.g. "recovery"), or null. 
  5.  * @throws IOException if reboot fails for some reason (eg, lack of 
  6.  *         permission) 
  7.  */  
  8. public static void lowLevelReboot(String reason) throws IOException {  
  9.     nativeReboot(reason);  
  10. }    
  11.   
  12. /**   
  13.  * Low-level function turn the device off immediately, without trying 
  14.  * to be clean.  Most people should use 
  15.  * {@link com.android.server.pm.internal.app.ShutdownThread} for a clean shutdown. 
  16.  */  
  17. public static void lowLevelShutdown() {  
  18.     nativeShutdown();  
  19. }   

很熟悉的字样native,是JNI调用了:

  1. private static native void nativeShutdown();  
  2. private static native void nativeReboot(String reason) throws IOException; 


        ===>>>>>android 4.4 中的lowLevelShutdown  以及  lowLevelReboot

public static void lowLevelShutdown() {
        SystemProperties.set("sys.powerctl", "shutdown");////直接关机
    }

    /**
     * Low-level function to reboot the device. On success, this function
     * doesn't return. If more than 5 seconds passes from the time,
     * a reboot is requested, this method returns.
     *
     * @param reason code to pass to the kernel (e.g. "recovery"), or null.
     */
    public static void lowLevelReboot(String reason) {
        if (reason == null) {
            reason = "";
        }
        SystemProperties.set("sys.powerctl", "reboot," + reason);///重启
        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

【注解:】

        关机流程中最后是通过修改Android属性进行关机操作(SystemProperties.java通过JNI调用访问系统属性)。

        当然我们也可以通过adb命令修改Android系统属性执行关机操作,例如adb shell setpro sys.powerctl shutdown。

         这里我们简单介绍下修改Android属性关机的原理或流程。

native_set()--->SystemProperties_set()

这是SystemProperties.java类中设置系统函数的方法。



5.frameworks/base/services/jni/com_android_server_PowerManagerService.cpp

  1. static JNINativeMethod gPowerManagerServiceMethods[] = {   
  2.     /* name, signature, funcPtr */  
  3.     ...  
  4.     { "nativeShutdown""()V",  
  5.             (void*) nativeShutdown },  
  6.     { "nativeReboot""(Ljava/lang/String;)V",  
  7.             (void*) nativeReboot },  
  8.     ...  
  9. };  

这两个好哥俩的实现也是在一起的:
  1. static void nativeShutdown(JNIEnv *env, jobject clazz) {  
  2.     android_reboot(ANDROID_RB_POWEROFF, 0, 0);  ////直接关机
  3. }  
  4.   
  5. static void nativeReboot(JNIEnv *env, jobject clazz, jstring reason) {  
  6.     if (reason == NULL) {  
  7.         android_reboot(ANDROID_RB_RESTART, 0, 0);  //////无reboot reason的重启
  8.     } else {  
  9.         const char *chars = env->GetStringUTFChars(reason, NULL);  
  10.         android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars); //////有reboot reason的重启
  11.         env->ReleaseStringUTFChars(reason, chars);  // In case it fails.  
  12.     }  
  13.     jniThrowIOException(env, errno);  
  14. }  

可以看到无论是关机还是重启,都是调用android_reboot来实现的,只是参数不一样而已。


6.system/core/libcutils/android_reboot.c

      int android_reboot(int cmd, int flags, char *arg)
{
    int ret;

    sync();
    remount_ro();

    switch (cmd) {
        case ANDROID_RB_RESTART://普通重启
            ret = reboot(RB_AUTOBOOT);
            break;

        case ANDROID_RB_POWEROFF://///直接关机
            ret = reboot(RB_POWER_OFF);
            break;

        case ANDROID_RB_RESTART2:
            ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,///有参数的重启直接走这里
                           LINUX_REBOOT_CMD_RESTART2, arg);
            break;

        default:
            ret = -1;
    }

    return ret;
}

        以reboot recovery为例,arg即为recovery,所在在第5步的时候会传入ANDROID_RB_RESTART2。

        接下来才是对参数的解析处理:

1)普通重启 ANDROID_RB_RESTART, reason = RB_AUTOBOOT;

2)关机 ANDROID_RB_POWEROFF, 无需reason,直接调用reboot进行关机;

3)带参数的特殊重启 ANDROID_RB_RESTART2, reason 将为默认值 -1

      注解1:在有的android版本里,这里又会出现一个#ifdef RECOVERY_PRE_COMMAND_CLEAR_REASON,如果定义了它,则无论上层传下来的参数是什么样的,最终都只是普通重启而已。定义它的方式是在BoardConfig.mk中加入TARGET_RECOVERY_PRE_COMMAND_CLEAR_REASON := true,应该有厂商会喜欢这么做的,毕竟除了普通重启,都可能带给用户一定的风险。最后会对reason进行一个检测,那么通过上边的分析,其实只有带参数的特殊重启才会为-1,而不等于-1的情况中有普通重启和关机,而关机已经自行解决了……所以,不等于-1的情况到了这里也只有普通重启了。最终这里就是区分普通重启与特殊重启的地方了。

      注解2: 这里再插入一个问题,其他的几个cmd都是什么值呢?答案在bionic/libc/include/sys/reboot.h中:

  1. #define RB_AUTOBOOT     LINUX_REBOOT_CMD_RESTART  
  2. #define RB_HALT_SYSTEM  LINUX_REBOOT_CMD_HALT  
  3. #define RB_ENABLE_CAD   LINUX_REBOOT_CMD_CAD_ON  
  4. #define RB_DISABLE_CAD  LINUX_REBOOT_CMD_CAD_OFF  
  5. #define RB_POWER_OFF    LINUX_REBOOT_CMD_POWER_OFF  

      而,LINUX_REBOOT_XXXX之类的在bionic/libc/kernel/common/linux/reboot.h中:
  1. #define LINUX_REBOOT_MAGIC1 0xfee1dead  
  2. #define LINUX_REBOOT_MAGIC2 672274793  
  3. /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */  
  4. #define LINUX_REBOOT_MAGIC2A 85072278  
  5. #define LINUX_REBOOT_MAGIC2B 369367448  
  6. #define LINUX_REBOOT_MAGIC2C 537993216  
  7. #define LINUX_REBOOT_CMD_RESTART 0x01234567  
  8. /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */  
  9. #define LINUX_REBOOT_CMD_HALT 0xCDEF0123  
  10. #define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF  
  11. #define LINUX_REBOOT_CMD_CAD_OFF 0x00000000  
  12. #define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC  
  13. /* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */  
  14. #define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4  
  15. #define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2  
  16. #define LINUX_REBOOT_CMD_KEXEC 0x45584543  
       至于为什么他们是这样奇怪的值这个问题,我只能说他们是magic number,魔法嘛,本来就是正常人不能够理解的,所以~~~放过他们吧,只要知道他们没有是-1的就OK啦。


       先来看reboot函数,按照往常的经验,reboot最终一定会调用到__reboot的。


7.bionic/libc/unistd/reboot.c   ///A:\v387-debug\my-ap\bionic\libc\bionic\reboot.c

  1. int reboot (int  mode)   
  2. {  
  3.     return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL );  
  4. }  

Bingo!果然是这样,如此说来reboot(reason) -> reboot(RB_AUTOBOOT) -> __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART, NULL ),要是直接这样写多好~~~免得绕这一层了。

--------------------------------KERNEL域--------------------------------

8.__reboot通过syscall来到内核

这里用一些篇幅简要介绍syscall,以后遇到类似的东西更好追踪一些。

第七步中的__reboot在arm架构的实现是这样的(bionic/libc/arch-arm/syscalls/__reboot.S)

/////home/tonylau/tony-workspace/v387-debug/my-ap/bionic/libc/arch-arm/syscalls/__reboot.S

  1. ENTRY(__reboot)  
  2.     .save   {r4, r7}   
  3.     stmfd   sp!, {r4, r7}  
  4.     ldr     r7, =__NR_reboot  
  5.     swi     #0    
  6.     ldmfd   sp!, {r4, r7}  
  7.     movs    r0, r0  
  8.     bxpl    lr    
  9.     b       __set_syscall_errno  
  10. END(__reboot)  

      可以看出来,这里将__reboot的实现映射到了__NR_reboot, 而在bionic/libc/sys/linux-syscalls.h((/home/tonylau/tony-workspace/v387-debug/my-ap/prebuilts/ndk/8/platforms/android-4/arch-arm/usr/include/sys/linux-syscalls.h))能够找到:
  1. #define __NR_reboot                       (__NR_SYSCALL_BASE + 88)  

        其被指定了一个固定的偏移量,在被调用的时候就是通过这个偏移量去内核中寻找对应的入口的,由此可见,内核中一定有着相同的定义,否则将不能成功调用。内核中对syscall偏移量的定义在内核源码中的arch/arm/include/asm/unistd.h,相关信息完全一致。

        已经找到了内核中的对应映射,那么下一步就要去找寻真正的实现函数了,在include/asm-generic/unistd.h(/home/tonylau/tony-workspace/v387-debug/my-ap/prebuilts/ndk/9/platforms/android-4/arch-arm/usr/include/asm/unistd.h)中可以找到内核对__NR_reboot的syscall函数映射,即

  1. /* kernel/sys.c */  
  2. #define __NR_setpriority 140  
  3. __SYSCALL(__NR_setpriority, sys_setpriority)  
  4. #define __NR_getpriority 141  
  5. __SYSCALL(__NR_getpriority, sys_getpriority)  
  6. #define __NR_reboot 142  
  7. __SYSCALL(__NR_reboot, sys_reboot)  

同时,能够发现如此温馨的一幕,内核已经指引我们下一步该去哪里寻找sys_reboot,即kernel/sys.c。


9.kernel/sys.c

       在进入这个文件前,我们先去include/linux/syscalls.h中查看一下sys_reboot的定义:

  1. asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd,  
  2.                 void __user *arg);  

与__reboot的调用参数一致。

          进入sys.c文件后,并没有找到名为sys_reboot的函数,而通过仔细查找,发现一个很有趣的函数,其定义为SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg),对比__reboot的参数,能够符合。究竟是不是这个函数?

         同样在include/linux/syscalls.h文件中,能够找到这样几个定义:

  1. #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)  
  2. #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)  
  3. #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)  
  4. #define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)  
  5. #define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)  
  6. #define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)  
  7. ...  
  8.   
  9. #define SYSCALL_DEFINEx(x, sname, ...)              \  
  10.     __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)  
  11. ...  
  12.   
  13. #define __SYSCALL_DEFINEx(x, name, ...)                 \  
  14.     asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))  

整合后等价于:
  1. #define SYSCALL_DEFINE4(name, ...) \  
  2.     asmlinkage long sys##_name(__SC_DECL##4(__VA_ARGS__))  

        这样就不难看出,SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)就是sys_reboot,也就是上层调用的__reboot的最终实现。函数实现如下:
  1. /* 
  2.  * Reboot system call: for obvious reasons only root may call it, 
  3.  * and even root needs to set up some magic numbers in the registers 
  4.  * so that some mistake won't make this reboot the whole machine. 
  5.  * You can also set the meaning of the ctrl-alt-del-key here. 
  6.  * 
  7.  * reboot doesn't sync: do that yourself before calling this. 
  8.  */  
  9. SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,  
  10.         void __user *, arg)  
  11. {  
  12.     char buffer[256];  
  13.     int ret = 0;  
  14.   
  15.     /* We only trust the superuser with rebooting the system. */  
  16.     if (!capable(CAP_SYS_BOOT))  ///首先会检测权限问题,只有超级用户才可以执行重启系统的操作
  17.         return -EPERM;        /////否则将返回权限错误。对应的权限列表在include/linux/capability.h中,重启操作为22
  18.   
  19.     /* For safety, we require "magic" arguments. */  //随后对magic number进行了校验
  20.     if (magic1 != LINUX_REBOOT_MAGIC1 ||       ////如果数据传输过程中没有发生错误的话,
  21.                                                            ////这里也当然不会有问题,所以只是一个安全性校验,基本不会发生错误。
  22.         (magic2 != LINUX_REBOOT_MAGIC2 &&  
  23.                     magic2 != LINUX_REBOOT_MAGIC2A &&  
  24.             magic2 != LINUX_REBOOT_MAGIC2B &&  
  25.                     magic2 != LINUX_REBOOT_MAGIC2C))  
  26.         return -EINVAL;  
  27.   
  28.     /* Instead of trying to make the power_off code look like 
  29.      * halt when pm_power_off is not set do it the easy way. 
  30.      */  
  31.     if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)  
  32.         cmd = LINUX_REBOOT_CMD_HALT;  ///之后有一个很有趣的检查,如果用户要求关机,而pm_power_off为空的话,就把用户的关机命令转换为挂起:
  33.   
  34.     mutex_lock(&reboot_mutex);  
  35.     switch (cmd) {  
  36.     case LINUX_REBOOT_CMD_RESTART:  
  37.         kernel_restart(NULL);  ////重启方式1---无命令
  38.         break;  
  39.   
  40.     case LINUX_REBOOT_CMD_CAD_ON:  
  41.         C_A_D = 1;  
  42.         break;  
  43.   
  44.     case LINUX_REBOOT_CMD_CAD_OFF:  
  45.         C_A_D = 0;  
  46.         break;  
  47.   
  48.     case LINUX_REBOOT_CMD_HALT:  
  49.         kernel_halt();  
  50.         do_exit(0);  
  51.         panic("cannot halt");  
  52.   
  53.     case LINUX_REBOOT_CMD_POWER_OFF:  
  54.         kernel_power_off(); ////直接
  55.         do_exit(0);  
  56.         break;  
  57.   
  58.     case LINUX_REBOOT_CMD_RESTART2:  
  59.         if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {  
  60.             ret = -EFAULT;  
  61.             break;  
  62.         }  
  63.         buffer[sizeof(buffer) - 1] = '\0';  
  64.   
  65.         kernel_restart(buffer);   ////重启方式2---有命令
  66.         break;  
  67.   
  68. #ifdef CONFIG_KEXEC  
  69.     case LINUX_REBOOT_CMD_KEXEC:  
  70.         ret = kernel_kexec();  
  71.         break;  
  72. #endif  
  73.   
  74. #ifdef CONFIG_HIBERNATION  
  75.     case LINUX_REBOOT_CMD_SW_SUSPEND:  
  76.         ret = hibernate();  
  77.         break;  
  78. #endif  
  79.   
  80.     default:  
  81.         ret = -EINVAL;  
  82.         break;  
  83.     }  
  84.     mutex_unlock(&reboot_mutex);  
  85.     return ret;  
  86. }  




     注解1:pm_power_off挂起的操作,

      在arch/arm/kernel/process.c中可以找到它的定义:

  1. /* 
  2.  * Function pointers to optional machine specific functions 
  3.  */  
  4. void (*pm_power_off)(void);  
  5. EXPORT_SYMBOL(pm_power_off);  

好的,只是一个函数指针,而且做了全局操作,整个kernel都可以调用它。以高通msm7x30为例,在arch/arm/mach-msm/pm2.c中对这个函数指针进行了赋值:
  1. pm_power_off = msm_pm_power_off;  

msm_pm_power_off的具体实现就不再跟踪了,各家的都不一样,跟下去没有太大意义。现在只要知道,我分析的这个kernel是给了这个函数指针赋值的,所以不为空,关机命令将正常执行。

      注解2:接下来就是这个函数的正题了,对用户命令进行解析操作,同时这个过程是用reboot_mutex互斥锁来进行保护的,以保证同一时间只可能有一个解析过程,避免冲突。

下边贴出所有关机重启相关的命令定义:

  1. /* 
  2.  * Commands accepted by the _reboot() system call. 
  3.  *       
  4.  * RESTART     Restart system using default command and mode. 
  5.  * HALT        Stop OS and give system control to ROM monitor, if any. 
  6.  * CAD_ON      Ctrl-Alt-Del sequence causes RESTART command. 
  7.  * CAD_OFF     Ctrl-Alt-Del sequence sends SIGINT to init task. 
  8.  * POWER_OFF   Stop OS and remove all power from system, if possible. 
  9.  * RESTART2    Restart system using given command string. 
  10.  * SW_SUSPEND  Suspend system using software suspend if compiled in. 
  11.  * KEXEC       Restart system using a previously loaded Linux kernel 
  12.  */       
  13.           
  14. #define LINUX_REBOOT_CMD_RESTART    0x01234567  
  15. #define LINUX_REBOOT_CMD_HALT       0xCDEF0123  
  16. #define LINUX_REBOOT_CMD_CAD_ON     0x89ABCDEF  
  17. #define LINUX_REBOOT_CMD_CAD_OFF    0x00000000   
  18. #define LINUX_REBOOT_CMD_POWER_OFF  0x4321FEDC  
  19. #define LINUX_REBOOT_CMD_RESTART2   0xA1B2C3D4  
  20. #define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2  
  21. #define LINUX_REBOOT_CMD_KEXEC      0x45584543  

       注释中的说明很详细了,比较陌生的就是关于CAD,其实就是用来想用Ctrl+Alt+Del操作的;然后SW_SYSPEND是软件休眠;KEXEC就太高端了,属于内核的一个补丁,用来利用老内核重启,详细资料:http://www.ibm.com/developerworks/cn/linux/l-kexec/?ca=dwcn-newsletter-linux

        以上这些只有前六个命令被Android系统所使用,为什么这么说,可以去看bionic/libc/include/sys/reboot.h,上边已经贴出了。LINUX_REBOOT_CMD_HALT虽有定义,但是也没有发现Android系统中哪里有调用,有高手找到的话,希望能够告知一下。

        最终的最终,能够用到的就只有三个:
RESTART

POWER_OFF

RESTART2


10.最终实现

重启调用的是kernel_restart,区别是参数是不是空,关机则调用kernel_power_off(),

注解1:先看关机


/**
 *    kernel_power_off - power_off the system
 *
 *    Shutdown everything and perform a clean system power_off.
 */
void kernel_power_off(void)
{
    kernel_shutdown_prepare(SYSTEM_POWER_OFF);
    if (pm_power_off_prepare)
        pm_power_off_prepare();
    migrate_to_reboot_cpu();
    syscore_shutdown();
    printk(KERN_EMERG "Power down.\n");
    kmsg_dump(KMSG_DUMP_POWEROFF);
    machine_power_off();
}
EXPORT_SYMBOL_GPL(kernel_power_off);






做了一系列准备工作,最终调用machine_power_off():
  1. void machine_power_off(void)  
  2. {     
  3.     machine_shutdown();  
  4.     if (pm_power_off)  
  5.         pm_power_off();  
  6. }  

之前找寻的pm_power_off在这里就有用处了,是关机的最后一步操作。关机完成,之后看下重启操作:
  1. /** 
  2.  *  kernel_restart - reboot the system 
  3.  *  @cmd: pointer to buffer containing command to execute for restart 
  4.  *      or %NULL 
  5.  * 
  6.  *  Shutdown everything and perform a clean reboot. 
  7.  *  This is not safe to call in interrupt context. 
  8.  */  
  9. void kernel_restart(char *cmd)  
  10. {  
  11.     kernel_restart_prepare(cmd);  
  12.     if (!cmd)  
  13.         printk(KERN_EMERG "Restarting system.\n");  
  14.     else  
  15.         printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);  
  16.     kmsg_dump(KMSG_DUMP_RESTART);  
  17.     machine_restart(cmd);  
  18. }  
  19. EXPORT_SYMBOL_GPL(kernel_restart);  

     注解2:在看重启。同样的套路,也是会进行一些准备工作,之后调用machine_restart(cmd), 如果是普通重启,那么中个cmd就为NULL,如果是特殊重启,那么这个cmd就是一层一层传递下来得那个arg了。

@@kernel/arch/arm/kernel/process.c

void machine_restart(char *cmd)////machine_restart执行每个架构特定的函数
{
    machine_shutdown();
    printk("Reboot:machine restart...\n");
    /* 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\n");
    local_irq_disable();
    while (1);
}


void arm_machine_restart(char mode, const char *cmd)

{
        /* Flush the console to make sure all the relevant messages make it
         * out to the console drivers */
        arm_machine_flush_console();

        /* Disable interrupts first */
        local_irq_disable();
        local_fiq_disable();

        /*
         * Tell the mm system that we are going to reboot -
         * we may need it to insert some 1:1 mappings so that
         * soft boot works.
         */
        setup_mm_for_reboot();

        /* When l1 is disabled and l2 is enabled, the spinlock cannot get the lock,
         * so we need to disable the l2 as well. by Chia-Hao Hsu
         */
        outer_flush_all();
        outer_disable();
        outer_flush_all();

        /* Clean and invalidate caches */
        flush_cache_all();

        /* Turn off caching */
        cpu_proc_fin();

        /* Push out any further dirty data, and ensure cache is empty */
        flush_cache_all();

        /*
         * Now call the architecture specific reboot code.
         */
        arch_reset(mode, cmd);

        /*
         * Whoops - the architecture was unable to reboot.
         * Tell the user!
         */
        mdelay(1000);
        printk("Reboot failed -- System halted\n");
        while (1);
}


@@@@kernel/meditek/kernel/kernel/system.c

void arch_reset(char mode, const char *cmd)

{
    char reboot = 0;
    int res=0;
    struct wd_api*wd_api = NULL;
    
    res = get_wd_api(&wd_api);
    printk("arch_reset: cmd = %s\n", cmd ? : "NULL");


    if (cmd && !strcmp(cmd, "charger")) {//////
        /* do nothing */
    } else if (cmd && !strcmp(cmd, "recovery")) {/////如果传下来的字符串是recovery=>就在RTC寄存器里设置某个特定值,

                                                              当uboot里读取RTC寄存器的时候如果获取了这个特定值,那就可以起recovery这个动作了
        rtc_mark_recovery();
    } else if (cmd && !strcmp(cmd, "bootloader")){//////////
            rtc_mark_fast();    
    } 
            #ifdef MTK_KERNEL_POWER_OFF_CHARGING
           else if (cmd && !strcmp(cmd, "kpoc")){ rtc_mark_kpoc(); }///////
            #endif
    else {
        reboot = 1;
    }


    if(res){
        printk("arch_reset, get wd api error %d\n",res);
    } else {
        wd_api->wd_sw_reset(reboot);
    }
}



@@@@@kernel/meditek/kernel/drivers/rtc/mtk_rtc_common.c

void rtc_mark_recovery(void)
{
    unsigned long flags;

    spin_lock_irqsave(&rtc_lock, flags);
    hal_rtc_mark_mode("recv");
    spin_unlock_irqrestore(&rtc_lock, flags);
}


@@@@kernel/mediatek/platform/mt6582/kernel/drivers/rtc/mtk_rtc_hal.c

void hal_rtc_mark_mode(const char *cmd)
{
    u16 pdn1;

    if (!strcmp(cmd, "recv")) {
        pdn1 = rtc_read(RTC_PDN1) & (~RTC_PDN1_RECOVERY_MASK);
        rtc_write(RTC_PDN1, pdn1 | RTC_PDN1_FAC_RESET);
    }
    else if (!strcmp(cmd, "kpoc")) {
        pdn1 = rtc_read(RTC_PDN1) & (~RTC_PDN1_KPOC);
        rtc_write(RTC_PDN1, pdn1 | RTC_PDN1_KPOC);
    }
    else if (!strcmp(cmd, "fast")) {
        pdn1 = rtc_read(RTC_PDN1) & (~RTC_PDN1_FAST_BOOT);
        rtc_write(RTC_PDN1, pdn1 | RTC_PDN1_FAST_BOOT);
    }
    rtc_write_trigger();
}



--------------------------------adb重启测试--------------------------------


adb 测试重启命令(adb reboot +     recovery,charger,kpoc,bootloader,etc)

1. 测试源码:

@@@@/meditek/kernel/kernel/system.c:

void arch_reset(char mode, const char *cmd)

{
    char reboot = 0;
    int res=0;
    struct wd_api*wd_api = NULL;
    
    res = get_wd_api(&wd_api);
    printk("arch_reset: cmd = %s\n", cmd ? : "NULL");


    if (cmd && !strcmp(cmd, "charger")) {//////充电
        /* do nothing */
    } 

    else if (cmd && !strcmp(cmd, "recovery")) {/////如果传下来的字符串是recovery=>就在RTC寄存器里设置某个特定值,

                                                              当uboot里读取RTC寄存器的时候如果获取了这个特定值,那就可以起recovery这个动作了
        rtc_mark_recovery();
    }

    else if (cmd && !strcmp(cmd, "bootloader")){//////////进入bootloader(fastboot 模式)
            rtc_mark_fast();    
    } 
            

    #ifdef MTK_KERNEL_POWER_OFF_CHARGING
    else if (cmd && !strcmp(cmd, "kpoc")){ rtc_mark_kpoc(); }///////(关机充电模式)
    #endif

    else {
        reboot = 1;
    }


    if(res){
        printk("arch_reset, get wd api error %d\n",res);
    } else {
        wd_api->wd_sw_reset(reboot);
    }
}


2.测试命令实例

tonylau@tonylau-OptiPlex-780:~/tony-workspace/new-v387/my-ap$ adb reboot    bootloader

tonylau@tonylau-OptiPlex-780:~/tony-workspace/new-v387/my-ap$ adb reboot    kpoc

tonylau@tonylau-OptiPlex-780:~/tony-workspace/new-v387/my-ap$ adb reboot    charger

tonylau@tonylau-OptiPlex-780:~/tony-workspace/new-v387/my-ap$ adb reboot    handoff_resolution(自己定义的)

若要实现自己的adb reboot handoff_resolution进入bootloader模式

===>>在1中添加如下代码即可:


//begin tony
 if (cmd && !strcmp(cmd, "handoff_resolution_enable")) {
        rtc_mark_fast();    
    } 
//end tony


--------------------------------关机怎么用(总结)--------------------------------

关机怎么用?----总结

        本文的分析是由Android给出的reboot接口开始的,但是分析来分析去,回头想一想会发现,Android给出的接口reboot就真的只能重启而已,不能进行关机操作,可以在跟踪这个流程的过程中会发现,确实是有存在关机的相关接口的。那么关机该怎么用呢?
frameworks/base/services/java/com/android/serverBatteryService.java
  1. private final void shutdownIfNoPower() {  
  2. // shut down gracefully if our battery is critically low and we are not powered.  
  3. // wait until the system has booted before attempting to display the shutdown dialog.  
  4. if (mBatteryLevel == 0 && !isPowered() && ActivityManagerNative.isSystemReady()) {  
  5.     Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);  
  6.     intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);  
  7.     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  8.     mContext.startActivity(intent);  
  9. }  

这样就可以了,不用多说了吧。
阅读(11993) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~