-PowerManagerService中
站在用戶的角度思考問題,與客戶深入溝通,找到固陽網(wǎng)站設(shè)計(jì)與固陽網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都做網(wǎng)站、網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、申請域名、網(wǎng)絡(luò)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋固陽地區(qū)。
lowLevelReboot()
SystemProperties.set("sys.powerctl", "reboot," + reason); //就傳給Kernel進(jìn)入reboot了
=》
那么接下來,走到哪里去了?
sys.powerctl 的配置在init.rc 當(dāng)中,可以參考
on property:sys.powerctl=*
powerctl ${sys.powerctl}
然后執(zhí)行system/core/init/builtins.c#do_powerctl 函數(shù),call android_reboot 重啟或者關(guān)機(jī)。
為何工廠模式下無法adb reboot ? 正常情況下adb reboot 可以重啟, 其關(guān)鍵就是因?yàn)閙ediatek/config/{platform}factory_init.rc 中沒有定義
on property:sys.powerctl=*
powerctl ${sys.powerctl}
如果要添加此功能,只有在factory_init.rc 中添加這兩行即可
繼續(xù)跟
=》在init.rc中和一個(gè)相關(guān)的on 觸發(fā)器
396 on property:sys.powerctl=*
397 powerctl ${sys.powerctl}
=》會調(diào)用 builtins.c 的,參考間reboot,recovery
int do_powerctl(int nargs, char **args)
=
return android_reboot(cmd, 0, reboot_target);
cmd = ANDROID_RB_RESTART2 //在android_reboot.h中#define ANDROID_RB_RESTART2 0xDEAD0003
reboot_target =第二個(gè)參考recovery
=》
/system/core/libcutils/android_reboot.c 中的執(zhí)行android_reboot();
case ANDROID_RB_RESTART2:
121 ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
122 LINUX_REBOOT_CMD_RESTART2, arg); //arg = recovery
=
這個(gè)在bionic/libc/include/sys/reboot.h中定義的。說明這是一個(gè)標(biāo)準(zhǔn)的系統(tǒng)調(diào)用
extern int __reboot(int, int, int, void *);
具體位于bionic/libc/arch-arm/syscalls/__reboot.S
1 /* autogenerated by gensyscalls.py */
2 #include asm/unistd.h
3 #include linux/err.h
4 #include machine/asm.h
5
6 ENTRY(__reboot)
7 mov ip, r7
8 ldr r7, =__NR_reboot
9 swi #0
10 mov r7, ip
11 cmn r0, #(MAX_ERRNO + 1)
12 bxls lr
13 neg r0, r0
14 b __set_errno
15 END(__reboot)
=》最后會進(jìn)入Kernel
kernel/sys.c
case LINUX_REBOOT_CMD_RESTART2:
kernel_restart(buffer); //buffer = arg= recovery
=machine_restart()
=arch/arm/kernel/process.c
void machine_restart(char *cmd)
=》
void (*arm_pm_restart)(char str, const char *cmd) = arm_machine_restart;
=》
void arm_machine_restart(char mode, const char *cmd)
{
...
aml_write_reg32(P_AO_RTI_STATUS_REG1, reboot_reason); //這一個(gè)標(biāo)志寄存器
...
arch_reset(mode, cmd); // mode = 'h' cmd = "recovery"
}
要強(qiáng)制關(guān)機(jī)的話,建議把車熄火了試試應(yīng)該就可以斷電了,如果不行還是要強(qiáng)制關(guān)機(jī)的,可以打開發(fā)動(dòng)機(jī)的電擊,把線頭拔掉就可以了
個(gè)人建議如果出現(xiàn)問題還是要去4s店找專門的人進(jìn)行處理這樣會更加保險(xiǎn)和有 保障。
升個(gè)級試試吧,是不是某個(gè)優(yōu)化程序優(yōu)化不了,卡住了,找個(gè)比你現(xiàn)在版本高的升級一下,068還可以
建議重新卡刷068全包一次。平行刷機(jī)不會丟失數(shù)據(jù)。如果會線刷的話,線刷更穩(wěn)定。(但是一定要下載正版的,不然會有很多廣告
在PowerManager的API文檔中,給出了一個(gè)關(guān)機(jī)/重啟接口:
public void reboot (String reason)
對于這個(gè)接口的描述很簡單,就是幾句話。
接口的作用就是重啟設(shè)備,而且,就算重啟成功了也沒有返回值。
需要包含REBOOT權(quán)限,也就是android.permission.REBOOT
唯一參數(shù)reason代表需要的特定重啟模式,比如recovery,當(dāng)然也可以為null。
1.frameworks/base/core/java/android/os/PowerManager.java
2.frameworks/base/core/java/android/os/IPowerManager.aidl
3.frameworks/base/services/java/com/android/server/PowerManagerService.java
4.frameworks/base/services/java/com/android/server/pm/ShutdownThread.java
5.frameworks/base/services/jni/com_android_server_PowerManagerService.cpp
---------------------》
6.system/core/libcutils/android_reboot.c
7.bionic/libc/unistd/reboot.c
8.__reboot通過syscall來到內(nèi)核
9.kernel/sys.c
frameworks/base/core/java/android/os/PowerManager.java
mService為IPowerManager Binder接口服務(wù)。
frameworks/base/core/java/android/os/IPowerManager.aidl
frameworks/base/services/java/com/android/server/PowerManagerService.java
frameworks/base/services/java/com/android/server/pm/ShutdownThread.java
這里說明是需要重啟,且不是安全模式,重啟參數(shù)為傳遞下來的reason,shutdownInner的confirm參數(shù)是用來設(shè)置是否有確認(rèn)提示框的,通過reboot接口調(diào)用重啟是沒有的,為false。
重啟的實(shí)現(xiàn)在run()中,因?yàn)镾hutdownThread是Thread的擴(kuò)展,所以run會自動(dòng)運(yùn)行。
frameworks/base/services/java/com/android/server/pm/ShutdownThread.java
在重啟前會將重啟原因?qū)懭雜ys.shutdown.requested,如果沒有則為空,如果是安全模式還會將persist.sys.safemode置1,之后會進(jìn)行一些關(guān)機(jī)前的預(yù)處理,關(guān)閉ActivityManager以及MountService,最終調(diào)用rebootOrShutdown進(jìn)行關(guān)機(jī)操作。
如果確認(rèn)重啟,則調(diào)用PowerManagerService的lowLevelReboot函數(shù),參數(shù)就是傳遞下來的reason,稍后分析。如果不是重啟,即mReboot=false,那就是需要關(guān)機(jī)了,在shutdown函數(shù)中就能夠知道。
frameworks/base/services/java/com/android/server/PowerManagerService.java
frameworks/base/services/jni/com_android_server_PowerManagerService.cpp
可以看到無論是關(guān)機(jī)還是重啟,都是調(diào)用android_reboot來實(shí)現(xiàn)的,只是參數(shù)不一樣而已。
system/core/libcutils/android_reboot.c
以reboot recovery為例,arg即為recovery,所在在第五步的時(shí)候會傳入ANDROID_RB_RESTART2。到了android_reboot函數(shù)中,會看到這樣的定義#ifdef RECOVERY_PRE_COMMAND,即屬于重啟前會執(zhí)行的命令,如果定義了就會執(zhí)行。
下面也是做了一些關(guān)機(jī)重啟前的預(yù)處理工作,sync()作用是將緩存中的信息寫入磁盤,以免程序異常結(jié)束導(dǎo)致文件被損壞,linux系統(tǒng)關(guān)機(jī)前會做幾次這樣的動(dòng)作;而remount_ro()作用是通過調(diào)用emergency_remount()強(qiáng)制將文件系統(tǒng)掛載為只讀,不再允許任何寫入操作,同時(shí)會通過檢查/proc/mounts的設(shè)備狀態(tài)來確認(rèn)是否當(dāng)前的所有寫入工作已經(jīng)完成,這個(gè)檢查過程是阻塞操作。
接下來才是對參數(shù)的解析處理:
1)普通重啟 ANDROID_RB_RESTART, reason = RB_AUTOBOOT;
2)關(guān)機(jī) ANDROID_RB_POWEROFF, 無需reason,直接調(diào)用reboot進(jìn)行關(guān)機(jī);
3)帶參數(shù)的特殊重啟 ANDROID_RB_RESTART2, reason 將為默認(rèn)值 -1
這里又出現(xiàn)一個(gè)#ifdef RECOVERY_PRE_COMMAND_CLEAR_REASON,如果定義了它,則無論上層傳下來的參數(shù)是什么樣的,最終都只是普通重啟而已。定義它的方式是在BoardConfig.mk中加入TARGET_RECOVERY_PRE_COMMAND_CLEAR_REASON := true,應(yīng)該有廠商會喜歡這么做的,畢竟除了普通重啟,都可能帶給用戶一定的風(fēng)險(xiǎn)。
最后會對reason進(jìn)行一個(gè)檢測,那么通過上邊的分析,其實(shí)只有帶參數(shù)的特殊重啟才會為-1,而不等于-1的情況中有普通重啟和關(guān)機(jī),而關(guān)機(jī)已經(jīng)自行解決了……所以,不等于-1的情況到了這里也只有普通重啟了。最終這里就是區(qū)分普通重啟與特殊重啟的地方了。這里再插入一個(gè)問題,其他的幾個(gè)cmd都是什么值呢?答案在bionic/libc/include/sys/reboot.h中:
reboot(reason) - reboot(RB_AUTOBOOT) - __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART, NULL )
__reboot通過syscall來到內(nèi)核bionic/libc/arch-arm/syscalls/__reboot.S
其被指定了一個(gè)固定的偏移量,在被調(diào)用的時(shí)候就是通過這個(gè)偏移量去內(nèi)核中尋找對應(yīng)的入口的,由此可見,內(nèi)核中一定有著相同的定義,否則將不能成功調(diào)用。內(nèi)核中對syscall偏移量的定義在內(nèi)核源碼中的arch/arm/include/asm/unistd.h,相關(guān)信息完全一致。
已經(jīng)找到了內(nèi)核中的對應(yīng)映射,那么下一步就要去找尋真正的實(shí)現(xiàn)函數(shù)了,在include/asm-generic/unistd.h中可以找到內(nèi)核對__NR_reboot的syscall函數(shù)映射,即
同時(shí),能夠發(fā)現(xiàn)如此溫馨的一幕,內(nèi)核已經(jīng)指引我們下一步該去哪里尋找sys_reboot,即kernel/sys.c。
include/linux/syscalls.h
與__reboot的調(diào)用參數(shù)一致。
進(jìn)入sys.c文件后,并沒有找到名為sys_reboot的函數(shù),而通過仔細(xì)查找,發(fā)現(xiàn)一個(gè)很有趣的函數(shù),其定義為SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg),對比__reboot的參數(shù),能夠符合。究竟是不是這個(gè)函數(shù)?
同樣在include/linux/syscalls.h文件中,能夠找到這樣幾個(gè)定義:
而pm_power_off為空的話,就把用戶的關(guān)機(jī)命令轉(zhuǎn)換為掛起:
arch/arm/kernel/process.c
pm_power_off = msm_pm_power_off;
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)
這個(gè)過程是用reboot_mutex互斥鎖來進(jìn)行保護(hù)的,以保證同一時(shí)間只可能有一個(gè)解析過程,避免沖突。
bionic/libc/include/sys/reboot.h 中可以看到android定義的啟動(dòng)方式
RESTART
POWER_OFF
RESTART2
對框架進(jìn)行賦值,qcom 平臺 845上已經(jīng)不是這函數(shù),自己查找
arm_pm_restart = msm_pm_restart;
下面是qcom 實(shí)現(xiàn),每個(gè)平臺不同
可以在跟蹤這個(gè)流程的過程中會發(fā)現(xiàn),確實(shí)是有存在關(guān)機(jī)的相關(guān)接口的。那么關(guān)機(jī)該怎么用呢?
frameworks/base/services/java/com/android/serverBatteryService.java
重啟方式: 最后就是設(shè)定寄存器,Uboot 解析不同寄存器的值進(jìn)入不同的啟動(dòng)模式
recovery 如果傳下來的字符串是recovery那么,就在RTC寄存器里設(shè)置某個(gè)特定值,當(dāng)uboot里讀取RTC寄存器的時(shí)候如果獲取了這個(gè)特定值,那就可以起recovery這個(gè)動(dòng)作了。
Ref:
上面主要講到流程,在實(shí)際開發(fā)中, 主動(dòng)調(diào)用系統(tǒng)開機(jī)關(guān)機(jī)如何做
(Ref: )
一. 發(fā)送系統(tǒng)廣播方式
二. 通過init.rc啟動(dòng)系統(tǒng)服務(wù)來運(yùn)行sh文件
三. Runtime調(diào)用Linux-shell
四 . PowerManager reboot以及反射調(diào)用PowerManagerService shutdown
五.使用ShutdownThread (嘗試不成功,但想法覺得可行)
Intent.java位于源碼/frameworks/base/core/java/android/content/Intent.java下面
腳本方式,實(shí)際都是基于指令的
使用PowerManager 或ShutdownThread 都是基于關(guān)機(jī)流程
系統(tǒng)關(guān)機(jī),可以發(fā)送一個(gè)廣播,如下代碼:
一. 發(fā)送廣播方式
Broadcast是Android的四大基本組件之一,也就是我們常說的廣播。Android系統(tǒng)本身就包含了許多廣播,時(shí)時(shí)刻刻在監(jiān)聽著系統(tǒng)中注冊的每一個(gè)廣播并隨時(shí)準(zhǔn)備響應(yīng)操作。其中,就有關(guān)于關(guān)機(jī)或重啟的廣播:Intent.ACTION_REQUEST_SHUTDOWN和Intent.ACTION_REBOOT,通過發(fā)送這兩個(gè)廣播,Android就能自動(dòng)接收廣播,并響應(yīng)關(guān)機(jī)或
public static final String ACTION_REBOOT =
"android.intent.action.REBOOT";
public static final String ACTION_REQUEST_SHUTDOWN = "android.intent.action.ACTION_REQUEST_SHUTDOWN";
Intent.java位于源碼/frameworks/base/core/java/android/content/Intent.java下面。具體實(shí)現(xiàn)方法如下
//廣播方式關(guān)機(jī)重啟
case R.id.shutdown_btn1:
Log.v(TAG, "broadcast-shutdown");
Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
//其中false換成true,會彈出是否關(guān)機(jī)的確認(rèn)窗口
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
break;
case R.id.reboot_btn1:
Log.v(TAG, "broadcast-reboot");
Intent intent2 = new Intent(Intent.ACTION_REBOOT);
intent2.putExtra("nowait", 1);
intent2.putExtra("interval", 1);
intent2.putExtra("window", 0);
sendBroadcast(intent2);
break;
需要注意的幾點(diǎn)是:
第一,如前面所說,需要將APP提升至系統(tǒng)權(quán)限,具體做法是在AndroidMenifest.xml中添加如下代碼
android:sharedUserId="android.uid.system"
第二,同時(shí)需要添加關(guān)機(jī)權(quán)限
uses-permission android:name="android.permission.SHUTDOWN"/uses-permi