Android 進程常駐(4)----native保活5.0以上方案推演過程以及代碼詳述

這是一個輕量級的庫,配置幾行代碼,就能夠實如今android上實現進程常駐,也就是在系統強殺下,以及360獲取root權限下,clean master獲取root權限下都沒法殺死進程java

支持系統2.3到6.0linux

支持大部分設備,包括三星,華爲,oppo,nexus,魅族等等android

能夠簡單對開機廣播進行保護git


github地址:github

https://github.com/Marswin/MarsDaemonwindows

原理分析:api

Android 進程常駐(0)----MarsDaemon使用說明
session

Android 進程常駐(1)----開篇
多線程

Android 進程常駐(2)----細數利用android系統機制的保活手段
測試

Android 進程常駐(3)----native保活5.0如下方案推演過程以及代碼詳述

Android 進程常駐(4)----native保活5.0以上方案推演過程以及代碼詳述

Android 進程常駐(5)----開機廣播的簡單守護以及總結



正文:



上一篇咱們經過父子進程間創建雙管道,來監聽進程死掉,通過測試,無耗電問題,無內存消耗問題,能夠在設置中force close下成功拉起,也能夠在獲取到root權限的360/cleanmaster下成功存活。


但是放到5.0+的系統就不能用了,爲何呢?咱們來看源碼4.4系統和5.0系統在系統force close的時候都作了什麼修改。


4.4.3的ActivityManagerService

實如今這裏



而後5.0的AMS



實現




能夠看出來5.0的源碼中系統強殺的時候會連同同group中的全部進程也一塊兒幹掉,使用上一篇的策略經過打印log咱們也能夠看出,在父進程被殺死的時候,子進像是被凍結了同樣,作不了任何事情,拿不到監聽事件。那麼咱們該怎麼辦呢?

開始本身頭腦風暴,想出如下幾種解決策略:


一、放棄java進程,使用兩個純native的binary進程,相互保活,確保native進程常駐,而後按時拉起須要常駐的java進程。

二、使用setsid將子進程的session改變,是否可讓系統不凍結子進程。

三、子進程建立子進程,而後自殺,重複1000次,也就是父進程和本身子進程的子進程的子進程重複一千遍的進程之間互保,是否可讓系統不凍結子進程


通過代碼嘗試,結果你能夠猜到,沒錯,然並卵。這幾種方案都沒法阻擋系統的force close,就更不用說360/cm with root了。


因而換一個思路,再也不糾結於父子進程,而是兩個普通進程之間是否能互保


鑑於以前pipe管道的使用,首先想到的是fifo管道,這是linux下能夠創建在兩個沒有關係的進程間的雙向管道。一樣兩個進程間在c層創建fifo管道,而後阻塞讀取。可問題在於,這個fifo與pipe是有本質區別的,fifo是經過一個文件作通訊的,對方進程掛掉以後,本身這邊既不會報錯也不會返回,而是一直阻塞,直到管道里面有數據。因此,然並卵。


那麼,當進程死的時候,本身往管道里面寫數據,另外一邊阻塞讀到數據不就能夠得知這邊死掉了嗎?當時確實有這樣一瞬間的好笑想法,可是這要首先要本身知道本身的死亡啊。


這個好笑的想法沒有什麼意義,可是他啓發了我,當一個進程死掉的時候,他和另外一個進程之間的什麼會發生變化呢?


順着這個思路,我想到了文件鎖。不論windows仍是linux都會有一套文件的進程同步機制,爲了各個進程間文件的同步問題,一個進程能夠給一個文件上鎖,操做完了以後再解鎖,其餘進程若是擔憂同步問題,會首先檢查一下文件是否有鎖,若是有鎖,表明別人正在操做,就會延遲對文件的操做。那麼當一個進程掛掉,他給文件加的鎖也天然會消失。


因而按照這個思路,進程a給文件1加鎖,而後阻塞讀取文件2的鎖,進程b給文件2加鎖,而後阻塞讀取文件1的鎖,若是對方進程掛掉,他所在進程所持有的文件鎖會當即釋放,那麼另外一個進程就能夠讀取到被釋放文件鎖的文件,監聽到對方掛掉。


實現這個方案,首先使用的java代碼,java裏是有一套文件鎖的api的,可是寫完以後編譯時報錯。我就不寫demo截圖了,錯誤的信息是死鎖exception,兩個進程不能互相持有對方的鎖。java的編譯器可真多事兒,因而我想能不能用三個進程,1拿2的鎖,2拿3的鎖,3拿1的鎖,仍是不行,deadlock exception.


懷着忐忑的心情我用c來實現嘗試,慶幸的是c中沒有問題。原理通了,剩下的就是把代碼寫健壯,最難搞的問題就是同步問題,ab兩個進程,若是b進程還沒給文件加鎖,a就開始讀鎖,那麼就會誤覺得b進程掛掉了。需求:
一、須要讓a進程先把文件1鎖了,而後不去讀文件2,等b進程作一樣的操做以後,兩邊再同時讀取對方的鎖。
二、須要考慮第程序從新進入的初始化狀態與單個進程等待的狀態衝突問題
三、不能耗時!!時間很重要,後面會說到


這一塊確實想了一夜纔想出來合適的解決方案,首先確定不能用管道、信號這些進程間通訊,連memset都不要考慮,由於耗時,java層的機制就更不用考慮了!那麼就只能用一些標示位手段,好比一個文件在了就表明一個進程在了,一個文件沒了,就表明一個進程鎖好了。
因而最後的同步方案是:

一、4個文件,a進程文件a1,a2,b進程b1,b2


二、a進程加鎖文件a1,b進程同理


三、a進程建立a2文件,而後輪詢查看b2文件是否存在(這裏能夠輪詢,由於時間很短),不存在表明b進程還沒建立,b進程同理


四、a進程輪詢到b2文件存在了,表明b進程已經建立並可能在對b1文件加鎖,此時刪除文件b2,表明a進程已經加鎖完畢,容許b進程讀取a進程的鎖,b進程同理


五、a進程監聽文件a2,若是a2被刪除,表明b進程進行到了步驟4已經對b1加鎖完成,能夠開始讀取b1文件的鎖(不能直接監聽a2文件刪除,也就是不能跳過34步,這也是最難想的一部分,若是那樣可能此時b進程還沒建立,和b進程建立完成並加鎖完成的狀態是同樣的,就會讓進程a誤覺得進程b加鎖完成),b進程同理




看代碼
/MarsDaemon/LibMarsdaemon/jni/daemon_api21.c


其中a和b兩個進程都會執行這塊代碼,對於a進程來講,indicator_self_path對應a1,indicator_daemon_path對應b1,observer_self_path對應a2,observer_daemon_path對應b2,b進程同理

首先本身加鎖,而後加入同步模塊notify_and_waitfor


以上代碼就是上述同步邏輯

同步完成後,兩個進程同時監聽對方的鎖,一方掛掉另外一方馬上能夠監聽到。這套方案在5.0+上能夠作到互相監聽對方死亡狀態,由於都是阻塞方法,因此無耗電效率問題。

這裏要說一下,也許你看完源碼你會問爲何步在5.0如下采用這種方案,由於這種方案是須要掛兩個進程的,雖然與父子進程相比,在linux下都是兩個進程兩個pid,可是在android系統的設置裏面,父子進程中的native進程是android系通通計不到的,因此不會列出來,不會讓用戶以爲你爲啥啓這麼多進程。因此從用戶體驗角度,5.0如下仍是採用上一篇博文采用的方案。



ok,監聽成功,對方死亡就把對方拉起來,而後自殺,從新初始化。把方案替換進來以後發現5.0是ok的,360/cm with root也是ok的,可是在5.1又失效了。爲何?


打印log咱們發現,5.1上監聽死亡狀態是ok的,可是卻沒法將對方拉起來。


好了,咱們要開始第二個重點了,那就是如何啓動對方。


原先咱們的方案是用一個鬧鐘啓動對方,可是在5.1上,鬧鐘在force close以後,鬧鐘一樣會被廢掉,以前的拉起方案行不通了,那麼開始想如何將對方的進程拉起來,想了好久也沒有相出什麼別的方法,發送intent彷佛根本不執行,可是此時這種情形只能繼續研究發的intent爲何沒有執行。


經過打印log,咱們才發現,force close的時候,系統殺應用對應進程的時候,速度很是快,在a進程監聽到b進程被殺的時候,系統的死亡鐮刀已經伸向了a進程,大概只有幾十毫秒的時間!而發送一個intent咱們知道他是在本身進程中使用系統ActivityManagerService的一個代理類,經過一個binder將intent傳給系統,雖然大部分操做是在ams中,可是咱們這邊執行時間竟然也在百毫秒級的,難怪intent發不出去。


因此時間很重要!!!咱們要跟系統搶時間



那麼如今的問題是怎樣才能把intent發出去,查看源碼,咱們能夠看出intent的發送其實是ActivityManagerNative這個類,他持有一個binder,用來與系統的ActivityManagerService通訊,而後咱們發送intent的時候會初始化一個Parcel,經過binder transcate過去。



時間都消耗在了pacel的建立上。

那麼咱們可不能夠把這些耗時操做放在進程開始的時候就完成,等到監聽到進程掛掉,直接調用。

可是那個binder咱們無法直接拿到,這裏須要用到反射。

看代碼:


代碼com.marswin89.marsdaemon.strategy.DaemonStrategy22.java



 拿到ActivityManagerNative實例而後拿binder,也就是他的一個成員變量mRemote




  而後把Pacel初始化出來,這裏註釋掉的是我一行一行試驗的,爲了節省時間,在能達到目的的前提下,時間越少越好,因此參數能少傳就少傳。




而後在檢測到對方進程死掉的時候,直接調用transcate方法。




ok,5.1上也能夠實現雙向守護。

可是很遺憾,6.0上又跪了。

而後通過排查,發現經過以上方式用binder transcate的方法沒法啓動一個service。那該怎麼辦呢?抱着試試看的內心嘗試了broadcastreceiver,果真廣播是能夠的。一樣的原理用ActivityManagerNative中的binder transcate一個pacel來啓動一個broadcast拉起進程。

也是仿照系統源碼來作的廣播的Pacel

代碼com.marswin89.marsdaemon.strategy.DaemonStrategy22.java



ok,6.0也搞定。

在系統force close時成功拉起對方,可是在360\clean master with root的強殺下仍然有必定的概率會跪。經過log能夠看出來,在一鍵清理的時候是有成功拉起的,可是360\cm均會重複殺,被拉起來的新進程還沒初始化完成,就又被殺掉了。



再次,時間很重要!!!咱們要跟360\cm搶時間


這裏咱們在進程剛建立的時候使用多線程,將文件的同步加鎖監聽與啓動另外一個進程同時進行,歷來能節約100毫秒左右的時間



大功告成!


從5.0到6.0,在force close和360\cm with root的一鍵清理下均可以實現互相守護了。

相關文章
相關標籤/搜索