1>fdisk_shell.sh文件需在linux下用vim生成 不然會出不少意想不到的問題, 若在win下生成該文件在導入linux需用busybox dos2unix fdisk_shell.sh命令轉化爲linux文件。 2>總體思路:在應用層調用fdisk_shell.sh腳本實現格式化功能。 3>實踐中發現,應用層經過system方法調用的. /fdisk_shell.sh語句只能執行fdisk_shell.sh腳本中的簡單語句 像echo命令,可是system/bin下的其餘命令及BUSYBOX命令是沒法執行的;這行命令的執行須要ROOT權限,其實 system方法是能夠實現ROOT身份的轉換,但只能執行具體的命令,或是運行腳本的命令system(". /fdisk_shell.sh"); 可是要想運行腳本中的busybox命令是不成功的,只有另闢蹊徑; 最終思路是在init.rc中開啓一個守護進程rootexec,實現腳本的執行工做,應用層經過進程間通訊,實現腳本的執行。 應用層經過JNI調用startRootExec()方法,startRootExec()方法經過bind機制使rootexec實現執行腳本的任務。 也能夠用非正式的方法shocket,這樣能夠避開JNI層的調用; 4>重要代碼分析: \root\cmds\rootexec\RootExecService.cpp: if (-1 == status) { ALOGD("system error!"); return 0; } else { ALOGD("exit status value = [0x%x]\n", status); if (WIFEXITED(status)) { if (0 == WEXITSTATUS(status)) { ALOGD("run shell script successfully.\n"); return 1; } else { ALOGD("run shell script fail, script exit code: %d\n", WEXITSTATUS(status)); return 0; } } else { ALOGD("exit status = [%d]\n", WEXITSTATUS(status)); return 0; } } 此段代碼是檢測system方法執行腳本完成狀況,弱弱的講講system方法的返回值的問題: system函數對返回值的處理,涉及3個階段: 階段1:建立子進程等準備工做。若是失敗,返回-1。 階段2:調用/bin/sh拉起shell腳本,若是拉起失敗或者shell未正常執行結束(參見備註1), 緣由值被寫入到status的低8~15比特位中。system的man中只說明瞭會寫了127這個值,但實測發現還會寫126等值。 階段3:若是shell腳本正常執行結束,將shell返回值填到status的低8~15比特位中。 備註1: 只要可以調用到/bin/sh,而且執行shell過程當中沒有被其餘信號異常中斷,都算正常結束。 好比:無論shell腳本中返回什麼緣由值,是0仍是非0,都算正常執行結束。即便shell腳本不存在或沒有執行權限, 也都算正常執行結束。 若是shell腳本執行過程當中被強制kill掉等狀況則算異常結束。 如何判斷階段2中,shell腳本是否正常執行結束呢?系統提供了宏:WIFEXITED(status)。若是WIFEXITED(status)爲真, 則說明正常結束。 如何取得階段3中的shell返回值?你能夠直接經過右移8bit來實現, 但安全的作法是使用系統提供的宏:WEXITSTATUS(status)。 因爲咱們通常在shell腳本中會經過返回值判斷本腳本是否正常執行,若是成功返回0,失敗返回正數。 因此綜上,判斷一個system函數調用shell腳本是否正常結束的方法應該是以下3個條件同時成立: (1)-1 != status (2)WIFEXITED(status)爲真 (3)0 == WEXITSTATUS(status) 注意: 根據以上分析,當shell腳本不存在、沒有執行權限等場景下時,以上前2個條件仍會成立, 此時WEXITSTATUS(status)爲127,126等數值。 因此,咱們在shell腳本中不能將127,126等數值定義爲返回值,不然沒法區分中是shell的返回值, 仍是調用shell腳本異常的緣由值。shell腳本中的返回值最好多1開始遞增。 腳本執行成功返回0,失敗1; \myTest\jni\fdisk.cpp: static jstring run(JNIEnv *env,jobject obj,jstring comd,jint num) { char* tmpstr1 = "successed"; char* tmpstr2 = "failed"; jstring jstr1 = env->NewStringUTF(tmpstr1); jstring jstr2 = env->NewStringUTF(tmpstr2); ALOGE("fdiskShelljni.so"); if(startRootExec(2,2)) { ALOGE("startRootExec success!\n"); return jstr1; }else{ ALOGE("startRootExec failed!\n"); return jstr2; } } 此段代碼爲jni的主體,做用很簡單,執行startRootExec()方法,成功返回successed,失敗返回failed; 改文件中還實現了其餘方法,應用層調用JNI庫所需。 \myTest\src\com\fdisk\FdiskActivity.java: 改文件爲測試程序。格式化一個500G的硬盤須要很長時間,這麼,漫長的等待,花兒會不會謝呢?只有在另外的線程完成, 主線程只負責啓動thread,在thread中實現FLAG狀態的改變, 腳本執行成功JNI會返回successed,此時將FLAG置1,失敗返回failed,將FLAG置0,; 主線程根據值得變化判斷格式化是否成功。開始將FLAG置2,處理很好,讀者本身琢磨; fdisk_shell.sh: 這個腳本所要完成的工做就是判斷目前硬盤(/dev/block/sda)裏的分區狀況,而後去刪除分區,再從新建立一個sda1的 分區。該腳本花了我很長時間,但功能很完善,除了能夠刪除主分區還能刪除擴展分區,固然這個工做能夠在RootExecService.cpp中用刪除 表頭的方法實現,我認爲這樣更好; /system/fdisk/1.0保存的是整個硬盤的分區狀況,共有幾個分區; /system/fdisk/1.1保存的是整個硬盤的主分區狀況,共有幾個主分區; /system/fdisk/1.2保存的是整個硬盤的擴展分區狀況,共有幾個擴展分區; 這3個文件保存的信息是給後面命令提供參數依據; 若是你很熟悉FDISK命令的話,就會明白爲何在刪除最後一個分區的時候要另外echo? 也會明白要umount vfat格式自動掛載的8_*目錄的緣由,不然是mount不上的; 致使mount命令失敗的緣由大體爲二:1,格式化未成功;2,以前已經mount。 因此該腳本爲了保險用力的umount. 這腳本花的精力不少,讀者如果以爲有更好的方法能夠聯繫:424758702(qq) 5>整個代碼的編譯: myTest文件要放在android4.2/package/app/下,jni庫的生成和測試apk的生成只要在myTest文件夾下mm便可。 生成的so庫會出現android/out/target/product/win_k70/system/lib/下,改庫須要的librootexec.so,若是你徹底按root目錄 下的Read文檔作了就會在android/out/target/product/win_k70/system/lib/下生成;librootexec.so這個庫和 android/out/target/product/win_k70/system/bin/下的rootexec二進制文件我是自動打包進固件裏的, 會出如今板子的system/lib和system/bin裏的(若是你徹底按文檔作了); 個人郵件wangjian1937@live.cn 文檔很粗糙,願君自琢磨。 源碼不提供,思路很重要(保密,公司要求我不得不服)。