1. 程式人生 > >Android開發:針對系統檔案目錄 /system目錄下進行的檔案操作

Android開發:針對系統檔案目錄 /system目錄下進行的檔案操作

Android的/system目錄是安卓的系統目錄,裡面存放的都是系統檔案,主要有以下資料夾:

更加具體的檔案結構,有興趣的讀者可以安裝一個RE檔案管理器去檢視,在這裡我們只關注/system目錄下檔案的操作問題。 

  • /system/app/ : 一些系統APP
  • /system/bin/ : Linux自帶元件
  • /system/build.prop :系統的屬性資訊
  • /system/fonts/ : 存放目錄root後下載的TTF格式字型替換原字型,達到修改系統字型效果
  • /system/framework/ : 系統核心檔案、框架層
  • /system/lib : 存放幾乎所有共享庫的.so檔案
  • /system/media :系統提示音、系統鈴聲
  • /system/media/audio : Android預設鈴聲(alarms:鬧鈴提示;notification:簡訊或提示音;ringtones:來電鈴聲;ui:介面音效)
  • /system/usr/ :使用者配置檔案,包括鍵盤佈局、共享、時區檔案等

有時候我們在做一些開發工作的時候,業務上需要修改/system目錄下的相關檔案,於是當你興致勃勃著手去修改檔案時,結果發現困難重重,主要的困難是我們發現/system目錄下的檔案 只允許只讀許可權,不可讀寫,也就意味著我們根本無法下手去修改,當然重要的系統檔案怎麼可能允許外部隨意更改!

到底有什麼辦法可以修改/system目錄下的檔案呢?答案只有一個,修改/system目錄的檔案操作許可權,把只讀改成可讀寫。我們很快就想到使用 adb命令,用root身份登入,賦予/system 777許可權,修改它的檔案操作許可權改為可讀寫,這樣我們就可以愉快的進行檔案操作了。例如下面的adb命令:

1、  adb shell ,進入shell介面

2、mount -o remount rw /system/

3、chmod 777 /system

關於adb 修改/system檔案許可權,網上的例子有很多,這裡不再進行細緻的講解。

當然,我們今天的主題絕不會是將這種氾濫的東西,下面開始具體的問題探討:

我們該如何在程式碼中,去操作/system目錄下的檔案?

問題一:該在哪裡執行adb命令來修改檔案操作許可權?

就像是之前提到過的RE檔案管理器,給予它root許可權,你會發現它可以隨意的修改系統目錄下的檔案,包括建立,新增,刪除,移動等等。使用者在使用RE檔案管理器的時候,只是給它了Root許可權,並不是說又親自去執行了adb命令修改掉了/system的檔案操作許可權,再者也不可能,我們開發人員可以很輕易的執行adb命令,但是一個普通使用者來說這是不現實的,所以最終執行adb命令的還是在程式碼中。 

問題二:

修改過檔案操作許可權之後,你認為終於可以作業系統檔案了吧,錯,還是不能。為什麼?

因為Android整個開發處於應用層,在這一層根本觸及不到系統底層的東西,應用層只能操作 /data/data/應用安裝包 下的檔案和 sd卡檔案, 其它檔案沒有許可權,所以在應用層你還是無法去修改系統底層檔案,不信你可以去嘗試一下,你會發現儘管你已經通過adb命令修改了/system目錄的檔案操作許可權,但是還是會報IO錯誤:open failed: EROFS (Read-only file syste。

這下就很尷尬了!明明許可權都已經修改了還是行不通,這就是因為你所處的開發層面是應用層,根本無法觸及到系統底層的檔案。那怎麼辦?

首先我們要明確的是,在應用層一定是可以對/system系統底層檔案做出操作的,要不然RE檔案管理器是怎麼實現的?既然RE檔案管理器可以實現對底層檔案的操作,那麼一定有辦法可以,只不過不是尋常做法,只能採用取巧的特殊方式。

解決思路:

既然應用層沒有許可權去作業系統底層檔案,那麼我們就需要找到一位可以作業系統底層檔案的傢伙,然後在應用層釋出相應的命令去指揮它修改相應的系統底層檔案即可,那麼這個傢伙是誰呢?很快,我們就鎖定了關鍵人物:adb命令。

應用層在它可以觸及的檔案操作許可權範圍內,操作一個檔案;操作完成後,我們命令adb把操作完成的檔案給“搬”到系統底層檔案中,這樣達到在應用層修改系統底層檔案的目的!

下面就開始讓我們按照這個思路進行嘗試吧:

嘗試:在/system目錄內新建一個檔案,檔名為“test.txt”,檔案內容:testing,,,,,

博主的程式碼:

//建立檔案
File file=new File(getExternalCacheDir(),"test.txt");
         
       try { 
            //判斷檔案是否存在
            if (file.exists()){
                file.delete();
            }
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            //開始向檔案中寫內容
            FileWriter fileWriter=new FileWriter(file);
            fileWriter.write("testing,,,,");
            fileWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

這一段程式碼相信大家都不陌生,很普通的檔案輸入輸出流操作。

首先聲明瞭檔案的儲存路徑和檔名,然後建立檔案,接著就開始向檔案中寫入內容。

這裡有幾點需要大家注意,其中這個檔案儲存的路徑為應用關聯快取目錄,呼叫getExternalCacheDir()方法獲得該目錄,是SD卡中專門用於存放當前應用快取資料的位置。

下面我們就需要一個具體的adb命令的翻譯者和執行者。我們平常在做開發的時候,執行adb命令都是在命令提示符中,而在這裡,我們需要在程式碼中執行adb命令,畢竟普通使用者他們沒有辦法去執行adb命令。

自定義的adb命令執行者程式碼如下:

package com.example.xposedtest;

import android.util.Log;

import java.io.DataOutputStream;

/**
 * Created by 王將 on 2018/7/23.
 */

public class RootCmd {
    
    //翻譯並執行相應的adb命令
    public static boolean exusecmd(String command) {
        Process process = null;
        DataOutputStream os = null;
        try {
            process = Runtime.getRuntime().exec("su");
            os = new DataOutputStream(process.getOutputStream());
            os.writeBytes(command + "\n");
            os.writeBytes("exit\n");
            os.flush();
            Log.e("updateFile", "======000==writeSuccess======");
            process.waitFor();
        } catch (Exception e) {
            Log.e("updateFile", "======111=writeError======" + e.toString());
            return false;
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
                if (process != null) {
                    process.destroy();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    //移動檔案
    public static void moveFileToSystem(String filePath, String sysFilePath) {
        exusecmd("mount -o rw,remount /system");
        exusecmd("chmod 777 /system");
        exusecmd("cp  " + filePath + " " + sysFilePath);
    }

  
}

程式碼中exusecmd()負責去翻譯並執行傳進來的adb命令,這裡的adb命令以String字串的形式進行解析執行。關於具體在程式程式碼中執行adb命令的相關知識,這裡不再做細緻的講解,有興趣的讀者可以自行搜尋相關的資料文件。

我們具體看下面的moveFileToSystem()方法。首先有兩個String型引數,分別是filePath和sysFilePath,其中filePath指的是你想要操作的檔案的所處路徑,sysFilePath指的是你想要操作的系統檔案路徑。往下看它的執行邏輯,是不是很熟悉?沒錯,這裡先執行了兩句adb命令,第一句是修改/system為可讀寫許可權,第二句是賦予/system 777許可權,然後執行的檔案拷貝命令,把處於應用層的檔案拷貝到系統底層檔案路徑。

然後我們在主活動中執行moveFileToSystem()方法:

 RootCmd.moveFileToSystem(file.getAbsolutePath(),"/system");

把我們檔案的路徑和系統檔案路徑傳進去,就這樣我們成功的在/system目錄下新建了一個檔案!

去看一下執行效果,開啟RE檔案管理器:

 成功建立!

我們接下來繼續下面一個問題:既然能夠建立檔案,同樣也需要可以刪除檔案,怎麼刪除系統檔案呢?

同理,我們還是使用adb命令去執行刪除。

程式碼如下:

 public static void deleteFileToSystem(String filePath){
        exusecmd("mount -o rw,remount /system");
        exusecmd("rm "+filePath);
    }

注意:這裡的deleteFileToSystem靜態方法,同樣是位於我們自定義的RootCmd操作類中。

我們從程式碼可以看到:我們傳遞了一個String型別的引數,它代表的是你想刪除的檔案的路徑,方法體中,同樣首先執行了一條修改/system檔案操作許可權的命令,改為可讀寫,然後下面就是一條檔案刪除命令,刪除的檔案路徑為我們傳遞進來的引數。

接著我們在主活動中呼叫這個方法,程式碼如下:

RootCmd.deleteFileToSystem("/system/test.txt");

這裡我們刪除的是剛才我們建立的測試檔案:test.txt。

程式執行完成後,我們開啟RE檔案管理器去看一下:

 test.txt檔案被成功刪除!

這裡我們關於系統檔案/system目錄的檔案操作已經講解完畢,其中關鍵的有兩點:

1.應用層負責建立檔案,2.adb命令負責檔案的操作。

接下來你或許還有一個問題,如果是修改系統檔案/system目錄下某個檔案的內容,該怎麼去修改??

例如,/system目錄下的build.prop檔案,這個檔案裡面存放的都是系統裝置的相關資訊,比如裝置序列號,手機型號等等。下面業務要求我們去修改build.prop檔案裡的相關資訊,具體修改思路如下:

首先在應用層我們建立一個檔名也是build.prop的檔案,檔案裡面的內容和/system目錄下的build.prop檔案內容大致相同,只是其中我們需要修改的地方資訊不同。例如,這裡我們修改build.prop檔案中手機型號資訊,原本資訊為“huawei”,在我們應用層建立的build.prop檔案中,手機型號資訊改為“oppo R11”。

下面使用adb命令操作,首先刪除/system目錄下的build.prop檔案,接著把我們建立的新的build.prop檔案複製到/system目錄下,替換原先build.prop檔案,以此達到修改build.prop檔案的目的。

這裡不再進行演示,具體操作請讀者自己嘗試。

還有相應的檔案移動,檔案複製等等一系列的操作 大家可以自行探索,主要是adb命令的使用。

本文到此結束,如引用本文,請標明出處。