android 系統重啟與關機:java 程式碼實現

yangxi_001發表於2013-11-28

在應用層如何通過程式碼實現 android 系統的重啟,分享給大家。

這篇部落格是在 android 系統開發的基礎之上進行實踐的,所以如果你是純粹的 app 開發,可能要讓你失望了。


該程式碼在真機上測試成功,在模擬器上面測試失敗。


在 linux 下面,重啟 pc,非 root 使用者需要執行 sudo reboot,所以在 android 下重啟機器也需要一定的許可權。


應用程式介面:




1. 新建一個 android 工程 reboot




2. 編寫 Android.mk 


LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_PACKAGE_NAME := reboot
LOCAL_CERTIFICATE := platform

include $(BUILD_PACKAGE)

# Use the folloing include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))


具體含義可自行查閱資料。注意:LOCAL_CERTIFICATE := platform


3. 編寫補充 AndroidMenifest.xml


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="mark.zhang"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="7" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:sharedUserId="android.uid.system" >


        <activity
            android:label="@string/app_name"
            android:name=".RebootActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>


    </application>

</manifest>


注意:android:sharedUserId="android.uid.system"


4. 編寫邏輯程式碼 RebootActivity.java


package mark.zhang;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class RebootActivity extends Activity {
    // 是否顯示關機確認的對話方塊
    // false 不顯示確認關機的對話方塊,直接關機
    // true 顯示確認關機的對話方塊,讓使用者選擇是否確認關機
    public static final boolean showShutdownDialog = false;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    /**
     * 傳送廣播.
     * 
     * @param view
     */
    public void onReboot(View view) {
        Intent reboot = new Intent(Intent.ACTION_REBOOT);
        reboot.putExtra("nowait", 1);
        reboot.putExtra("interval", 1);
        reboot.putExtra("window", 0);
        sendBroadcast(reboot);
    }

    /**
     * 啟動 Activity.
     * 
     * @param view
     */
    public void onShutdown(View view) {
        public void onShutdown(View view) {
        Intent shutdown = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
        shutdown.putExtra(Intent.EXTRA_KEY_CONFIRM, showShutdownDialog);
        shutdown.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(shutdown);
    }
    }

}


注意:


Intent.ACTION_REQUEST_SHUTDOWN

Intent.EXTRA_KEY_CONFIRM


會報錯,這是因為該屬性是  {@hide} 的。

不要擔心放到系統編譯就可以了,注意下面的步驟即可。


5. 將整個工程放到 android 原始碼裡面編譯


a. 手動複製 reboot 到 src/packages/app 下面


b. mm -j4 開始編譯(可以參考 http://blog.csdn.net/androidbluetooth/article/details/7058717


如果編譯成功,在 /out/target/product/generic/system/app 目錄下面,會多一個 reboot.apk 檔案。


6. 安裝 apk


該 apk 必須放到 system/app 下面,即 adb push reboot.apk /system/app



思考:


關機或者重啟,一個是傳送廣播,一個是啟動 Activity。

通過查詢相應的 action 或者 string 資源(如關機關鍵字)就可以找到對應的類。


我們知道,framwork 的 mk 檔案、資原始檔等都在:

/frameworks/base/core/res、frameworks/base/core/res/res


比如關機的那個 Activity 就是 ShutDownActiviy,其又呼叫 ShutdownThread.shutdown。


重啟的相關的類在 WatchDog.java(定義了相關的量), 其內部類 RebootRequestReceiver 是主要程式碼。


繼續追蹤你就會了解整個流程了。



貼上一篇關於許可權的文章:

http://www.5bay.cn/android%E7%AC%94%E8%AE%B0/androidquanxianzhishareduseridheqianming.html


最近在做個東西,巧合碰到了sharedUserId 的問題,所以收集了一些資料,存存檔備份。

安裝在裝置中的每一個apk檔案,Android 給每個 APK 程式分配一個單獨的使用者空間,

其 manifest 中的 userid 就是對應一個 Linux 使用者都會被分配到一個屬於自己的統一的 Linux 使用者 ID,

並且為它建立一個沙箱,以防止影響其他應用程式(或者其他應用程式影響它)。

使用者 ID 在應用程式安裝到裝置中時被分配,並且在這個裝置中保持它的永久性。

通過 Shared User id 擁有同一個User id的多個APK可以配置成執行在同一個程式中.

所以預設就是可以互相訪問任意資料. 也可以配置成執行成不同的程式,

同時可以訪問其他APK的資料目錄下的資料庫和檔案.就像訪問本程式的資料一樣.


對於一個 APK 來說,如果要使用某個共享 UID 的話,必須做三步:


1、在 Manifest 節點中增加 android:sharedUserId 屬性。


2、在 Android.mk 中增加 LOCAL_CERTIFICATE 的定義。


如果增加了上面的屬性但沒有定義與之對應的 LOCAL_CERTIFICATE 的話,APK是安裝不上去的。

提示錯誤是:

Package com.test.MyTest has no signatures that match those in shared user android.uid.system; ignoring!

也就是說,僅有相同簽名和相同 sharedUserID 標籤的兩個應用程式簽名都會被分配相同的使用者ID。

例如所有和 media/download 相關的 APK 都使用 android.media 作為 sharedUserId 的話,

那麼它們必須有相同的簽名 media。


3、把 APK 的原始碼放到 packages/apps/ 目錄下,用 mm 進行編譯。


舉例說明一下。


系統中所有使用android.uid.system作為共享UID的APK,都會首先在manifest節點中增加

android:sharedUserId=”android.uid.system”,

然後在 Android.mk 中增加 LOCAL_CERTIFICATE := platform。可以參見Settings等。


系統中所有使用 android.uid.shared 作為共享 UID 的 APK,都會在 manifest 節點中增加

android:sharedUserId=”android.uid.shared”,

然後在 Android.mk 中增加 LOCAL_CERTIFICATE := shared。可以參見Launcher等


系統中所有使用 android.media 作為共享 UID 的 APK,都會在 manifest 節點中增加

android:sharedUserId=”android.media”,

然後在 Android.mk 中增加 LOCAL_CERTIFICATE := media。可以參見Gallery等。


另外,應用建立的任何檔案都會被賦予應用的使用者標識,並且正常情況下不能被其他包訪問。

當通過 getSharedPreferences(String,int)、openFileOutput(String、int)

或者 openOrCreate Database(String、int、SQLiteDatabase.CursorFactory)

建立一個新檔案時,開發者可以同時或分別使用 

MODE_WORLD_READABLE 和MODE_WORLD_RITEABLE 標誌允許其他包讀/寫此檔案。

當設定了這些標誌後,這個檔案仍然屬於自己的應用程式,但是它的全域性讀/寫和讀/寫許可權已經設定,

所以其他任何應用程式可以看到它。


關於簽名:


build/target/product/security 目錄中有四組預設簽名供 Android.mk 在編譯APK使用:

1、testkey:  普通 APK,預設情況下使用。


2、platform:該 APK 完成一些系統的核心功能。經過對系統中存在的資料夾的訪問測試,

                        這種方式編譯出來的 APK 所在程式的 UID 為 system。


3、shared:   該APK需要和home/contacts程式共享資料。


4、media:該APK是media/download系統中的一環。


應用程式的 Android.mk 中有一個 LOCAL_CERTIFICATE 欄位,

由它指定用哪個 key 簽名,未指定的預設用 testkey.

相關文章