編輯:關於Android編程
最近因為公司項目需求,研究了下android自動關機。網上找到了很多種方法,把可行性方法都整合在一起了
一. 發送廣播方式
Broadcast是Android的四大基本組件之一,也就是我們常說的廣播。Android系統本身就包含了許多廣播,時時刻刻在監聽著系統中注冊的每一個廣播並隨時准備響應操作。其中,就有關於關機或重啟的廣播:Intent.ACTION_REQUEST_SHUTDOWN和Intent.ACTION_REBOOT,通過發送這兩個廣播,Android就能自動接收廣播,並響應關機或重啟的操作。ACTION_REQUEST和ACTION_REBOOT是Intent.java是聲明的兩個字符串常量
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下面。具體實現方法如下
//廣播方式關機重啟
caseR.id.shutdown_btn1:
Log.v(TAG,"broadcast->shutdown");
Intent intent = newIntent(Intent.ACTION_REQUEST_SHUTDOWN);
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
//其中false換成true,會彈出是否關機的確認窗口
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;
需要注意的幾點是:
第一,如前面所說,需要將APP提升至系統權限,具體做法是在AndroidMenifest.xml中添加如下代碼
android:sharedUserId="android.uid.system"
第二,同時需要添加關機權限
第三,在Eclipse中,代碼中的Intent.ACTION_REQUEST_SHUTDOWN 及 Intent.EXTRA_KEY_CONFIRM 在Eclipse IDE中報錯,還是和前面說的一樣,這兩個屬性不對上層開放,如果把項目放在源碼中進行編譯,是可以編譯通過的。
第四,由於需要在源碼中編譯項目,所以需要為項目編寫mk文件,在項目根目錄下添加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:= PowerActionDemo LOCAL_CERTIFICATE:= platform include$(BUILD_PACKAGE)
最後,將編譯生成的apk文件,通過adb push到機器上就可以驗證功能了。
二. 通過init.rc啟動系統服務來運行sh文件
Android啟動文件系統後調用的會調用第一個應用程序是/init,此文件一個很重要的內容就是解析了init.rc和init.xxx.rc,然後執行解析出來的任務。而init.rc,可以在系統的初始化過程中進行一些簡單的初始化操作。利用這一點,可以編寫簡單的關機或重啟的sh腳本文件,通過系統init解析,執行相應的關機或重啟操作。
1.首先,編寫關機和重啟的sh腳本。比如,新建
重啟腳本 system_reboot.sh,內容如下:
#!/system/bin/sh
reboot
關機腳本system_shutdown.sh
#!/system/bin/sh
reboot -p
注意:此處關機命令並不是shutdown,而是reboot -p
2. 編寫Android.mk編譯腳本,目的是在源碼編譯的時候,將這兩個sh文件一起編譯到/system/bin目錄下
LOCAL_PATH :=$(call my-dir) include$(CLEAR_VARS) LOCAL_PREBUILT_EXECUTABLES:= system_shutdown.sh system_reboot.sh LOCAL_MODULE_TAGS:= optional include$(BUILD_MULTI_PREBUILT)
3. init.rc添加關機和重啟的服務,打開init.rc文件,在最後面添加如下內容:
servicesystem_shutdown /system/bin/system_shutdown.sh oneshot disabled servicesystem_reboot /system/bin/system_reboot.sh oneshot disabled
oneshot選項表示該服務只啟動一次,而如果沒有oneshot選項,這個可執行程序會一直存在--如果可執行程序被殺死,則會重新啟動。
disabled 表示禁用服務,此服務開機時不會自動啟動,但是可以在應用程序中手動啟動它。
4.新建一個目錄,比如poweraction, 將以上的Android.mk , system_shutdown.sh, system_reboot.sh放在這個目錄下,然後將poweraction這個目錄拷貝到Android系統中,比如device路徑下面。然後,編譯Android源碼,源碼編譯完成後, 查看生成的out/.../system/bin下面是不是包含system_shutdown.sh, system_reboot.sh兩個sh文件,如果有,則說明編譯成功。
5.最後,啟動系統服務,進行關機或重啟。
//啟動系統服務進行關機或重啟
caseR.id.shutdown_btn2:
Log.v(TAG,"system service->shutdown");
SystemProperties.set("ctl.start","system_shutdwon");
break;
case R.id.reboot_btn2:
Log.v(TAG,"system service->reboot");
SystemProperties.set("ctl.start","system_reboot");
break;
三.Runtime調用Linux-shell
我們知道,Runtime這個Java類是可以用來調用並執行shell命令的,而Android虛擬機是支持Linux-shell語言的,基於這一點,可以利用Runtime來執行 關機或重啟的shell命令,這一點和上面介紹的方式二原理上大致相同。功能代碼如下:
//Runtime執行linux-shell
caseR.id.shutdown_btn3:
try{
Log.v(TAG,"root Runtime->shutdown");
//Processproc =Runtime.getRuntime().exec(newString[]{"su","-c","shutdown"}); //關機
Processproc =Runtime.getRuntime().exec(newString[]{"su","-c","reboot -p"}); //關機
proc.waitFor();
}catch(Exceptione){
e.printStackTrace();
}
break;
case R.id.reboot_btn3:
try {
Log.v(TAG,"root Runtime->reboot");
Processproc =Runtime.getRuntime().exec(newString[]{"su","-c","reboot "}); //關機
proc.waitFor();
}catch(Exception ex){
ex.printStackTrace();
}
break;
使用該方法需要注意的是,普通用戶是沒有權限執行reboot和shutdown的,自然而然也無法實現關機或重啟。使用的Android設備必須已經root過,上面的代碼加上su命令其實也就是為了獲取管理員權限。另外一點,需要注意的是,該方法能夠奏效的前提是,你的android系統system/bin 目錄下存在reboot和shutdown文件(其實跟上面的原理一樣,也是調用bin目錄下的文件),聽說大部分設備存在reboot和shutdown這兩個文件,可使用的Android系統偏偏沒有shutdown文件,所以,無法直接使用
Runtime.getRuntime().exec(newString[]{"su","-c","shutdown"})
只能執行下面命令來進行關機(好神奇的p參數)
Runtime.getRuntime().exec(newString[]{"su","-c","reboot -p"});
四 .PowerManager reboot以及反射調用PowerManagerService shutdown
1. PowerManager提供了reboot等接口,因此,利用PowerManager實現重啟,就比較簡單。
PowerManagerpManager=(PowerManager) getSystemService(Context.POWER_SERVICE); //重啟到fastboot模式
pManager.reboot("");
2. PowerManager類並沒有提供關機的shutdown接口,而是通過IBinder這種Android中特有的通信模式,與PowerManagerService 類進行通信。PowerManagerService是PowerManager 類中定義的接口的具體實現,並進一步調用Power 類來與下一層進行通信. 在PowerManagerService實現了shutdown接口,power服務實現了關機功能
PowerManager的實現通過IPowerManager來調用Power服務的接口。 IPowerManager是AIDL文件自動生成的類,便於遠程通信。IPowerManage.aidl文件目錄
framework/base/core/java/android/os/IPowerManage.aidl
IPowerManager實現了shutdown接口,所以,如果我們能夠獲得Power服務的IBinder,通過反射調用shutdown方法就能實現關機功能。
需要注意的是,ServiceManager管理著系統的服務程序,它保存著所有服務的IBinder,通過服務名就能獲取到這個服務的IBinder。
但ServiceManager這個類也是HIDE的,也需要反射進行調用。兩次,通過兩次反射調用,就能調用power服務實現的關機功能。
try {
//獲得ServiceManager類
Class> ServiceManager =Class
.forName("android.os.ServiceManager");
//獲得ServiceManager的getService方法
Method getService =ServiceManager.getMethod("getService", java.lang.String.class);
//調用getService獲取RemoteService
Object oRemoteService =getService.invoke(null,Context.POWER_SERVICE);
//獲得IPowerManager.Stub類
Class> cStub = Class
.forName("android.os.IPowerManager$Stub");
//獲得asInterface方法
Method asInterface =cStub.getMethod("asInterface", android.os.IBinder.class);
//調用asInterface方法獲取IPowerManager對象
Object oIPowerManager =asInterface.invoke(null, oRemoteService);
//獲得shutdown()方法
Method shutdown =oIPowerManager.getClass().getMethod("shutdown",boolean.class,boolean.class);
//調用shutdown()方法
shutdown.invoke(oIPowerManager,false,true);
} catch (Exception e) {
Log.e(TAG, e.toString(), e);
}
五,設置自動關機的alarm
1) 設置自動關機的alarm:
AlarmManager am = (AlarmManager) context .getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent( "com.android.settings.action.REQUEST_POWER_OFF"); PendingIntent pendingIntent =PendingIntent.getBroadcast(context, 0, intent,PendingIntent.FLAG_CANCEL_CURRENT); am = (AlarmManager) context .getSystemService(Context.ALARM_SERVICE); am.set(AlarmManager.RTC_WAKEUP,time, pendingIntent);
2)自動關機掉的是./frameworks/base/services/java/com/android/server/ShutdownActivity.java:
Intent newIntent = newIntent(Intent.ACTION_REQUEST_SHUTDOWN); newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(newIntent);
Intent.ACTION_REQUEST_SHUTDOWN是Intent裡面一個隱藏的action。
六,需要手機有root權限,如果手機有root權限,運行該程序時,會提示該應用正在申請獲取root權限,選擇運行即可實現關機。
private voidshutdown() {
try {
Process process = Runtime.getRuntime().exec("su");
DataOutputStream out = new DataOutputStream(
process.getOutputStream());
out.writeBytes("reboot -p\n");
out.writeBytes("exit\n");
out.flush();
}catch (IOException e) {
e.printStackTrace();
}
}
七. power服務實現了關機功能
framework/base/services/java/com/android/server/power/PowerManagerService.java
/**
* Shuts down the device.
*
* @param confirm If true, shows a shutdownconfirmation dialog.
* @param wait If true, this call waits for the shutdownto complete and does not return.
*/
@Override // Binder call
public void shutdown(boolean confirm, boolean wait) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT,null);
final long ident =Binder.clearCallingIdentity();
try {
shutdownOrRebootInternal(true, confirm, null, wait);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
八. PowerManager提供了reboot等接口,沒有提供shutdown接口。
若是重啟,實現就很簡單:
PowerManager pm =(PowerManager)this.getSystemService(Context.POWER_SERVICE);
pm.reboot();
但是shutdown沒有實現,PowerManager的實現通過IPowerManager來調用Power服務的接口。
IPowerManager是AIDL文件自動生成的類,便於遠程通信。IPowerManage.aidl文件目錄framework/base/core/java/android/os/IPowerManage.aidl
九. IPowerManager實現了shutdown接口,這裡只要獲得Power服務的IBinder,通過反射調用shutdown方法就能實現關機功能。
ServiceManager管理著系統的服務程序,它保存著所有服務的IBinder,通過服務名就能獲取到這個服務的IBinder。
而ServiceManager這個類也是HIDE的,也需要反射進行調用。
代碼實現:
try {
//獲得ServiceManager類
ClassServiceManager= Class
.forName("android.os.ServiceManager");
//獲得ServiceManager的getService方法
Method getService = ServiceManager.getMethod("getService",java.lang.String.class);
//調用getService獲取RemoteService
Object oRemoteService =getService.invoke(null,Context.POWER_SERVICE);
//獲得IPowerManager.Stub類
ClasscStub = Class
.forName("android.os.IPowerManager$Stub");
//獲得asInterface方法
Method asInterface =cStub.getMethod("asInterface", android.os.IBinder.class);
//調用asInterface方法獲取IPowerManager對象
Object oIPowerManager =asInterface.invoke(null, oRemoteService);
//獲得shutdown()方法
Method shutdown =oIPowerManager.getClass().getMethod("shutdown",boolean.class,boolean.class);
//調用shutdown()方法
shutdown.invoke(oIPowerManager,false,true);
} catch (Exception e) {
Log.e(TAG, e.toString(), e);
}
十
pad中自帶有重啟命令(reboot),本以為也應該有關機命令啊,可是找來找去也沒有諸如shutdown、poweroff等命令。
然後想到busybox中有一個poweroff命令,故又去編譯了一個busybox推進去,執行了busybox poweroff之後看了下返回碼
是0(即echo $?),然後,然後什麼也沒發生……
網上流行的方法是在Java端發送要求關機的Intent,如下:
1. Intentintent=newIntent(Intent.ACTION_REQUEST_SHUTDOWN); 2. intent.putExtra(Intent.EXTRA_KEY_CONFIRM,false); 3. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 4. mContext.startActivity(intent);
需要加上相關的權限:android:sharedUserId="android.uid.system"
1. staticvoidandroid_os_Power_shutdown(JNIEnv*env,jobjectclazz)
2. {
3. sync();
4. #ifdefHAVE_ANDROID_OS
5. reboot(RB_POWER_OFF);
6. #endif
7. }
看到有個reboot函數,然後再追蹤這個reboot(RB_POWER_OFF)函數,看到其最終會調用__reboot這個函數 (匯編實現的),我們只要給它傳遞“合適”的參數就行了。 代碼實現: 1、在Android源碼目錄中的packages/apps/下創建一個目錄,例如:Hello。 2、編寫Android.mk文件:LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= shutdown.c LOCAL_MODULE:= shutdown LOCAL_PRELINK_MODULE:= false LOCAL_SHARED_LIBRARIES:= libutils include $(BUILD_EXECUTABLE) 3、編寫shutdown.c源文件,如下:
1. #include
2. #include
3. #include
4. #include
5.
6. #ifndefLOG_TAG
7. #defineLOG_TAG"dxyh"
8. #endif
9.
10. intmain(intargc,char**argv)
11. {
12. intretval;
13.
14. if((retval=__reboot(LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,
15. RB_POWER_OFF,NULL))<0){
16. LOGE("Trytoshutdownthemachinefailed!");
17. exit(EXIT_FAILURE);
18. }
19. return0;
20. }
4、mm
5、將生成的shutdown可執行程序推入到pad中的/system/bin下即可。 6、然後就可以和其他命令一樣運行了,例如adb shell shutdown。
十一 . 非 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 thefolloing include to make our test apk. include $(callall-makefiles-under,$(LOCAL_PATH)) 具體含義可自行查閱資料。注意:LOCAL_CERTIFICATE := platform 3. 編寫補充 AndroidMenifest.xml package="mark.zhang" android:versionCode="1" android:versionName="1.0" > android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:sharedUserId="android.uid.system" > android:label="@string/app_name" android:name=".RebootActivity" >
注意:android:sharedUserId="android.uid.system"
4. 編寫邏輯代碼 RebootActivity.java
package mark.zhang;
importandroid.app.Activity;
importandroid.content.Intent;
importandroid.os.Bundle;
importandroid.view.View;
public classRebootActivity 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://www.linuxidc.com/Linux/2012-05/59704.htm)
如果編譯成功,在 /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 是主要代碼。
[Android測試] AS+Appium+Java+Win 自動化測試之八:使用PageObject模式和重封裝
一、 What? 什麼是PageObject?簡稱PO,這是一個設計模式,其實設計模式就是代碼的架構,一個整體的框架。例如mvc 就是模型-視圖-控制的一個代碼架構,mv
Android Ubuntu平台下ADB驅動的安裝
1. 確保設備已經連接正常 首先需要取得root權限,這個沒啥說的。然後用lsusb命令列一下所有USB設備,如下圖所示: 這裡可以比較清楚的看到有一個設
微信換手機號了怎麼辦 換手機號微信怎麼辦
如今我們大部分人都在玩微信,都用手機綁定了微信號,手機的功能太強大了,如果手機丟了,或者要換手機號碼怎麼辦?沒關系啦,騰訊公司也會想到這個問題,下面我來為大
Android判斷用戶的網絡類型實例講解(2/3/4G、wifi)
很多時候需要先判斷當前用戶的網絡,才會繼續之後的一些處理邏輯。但網絡類型獲取這一塊,我用我自己的的手機調試時遇到一些問題,這裡記錄一下。一加手機一代,移動4G 網絡,得到
我的Android進階之旅------)解決錯誤:You need to use a Theme.AppCompat theme (or descendant) with this activity.
#1、錯誤描述今天,想實現Activity不顯示標題欄的效果,在項目的A