Android系统开机时,在启动SurfaceFlinger服务过程中通过Android属性系统方式来启动bootanim进程,实现开机动画显示过程;当系统关机时,又是如何启动关机动画的呢?Android系统的整个关机流程又是怎样的呢?本文就针对这两个问题透过
​​源码​​
来给出具体的分析。我们知道,当长按电源键,系统会弹出关机提示对话框









当点击选择关机时,系统就会完成整个关机流程。接下来就通过源码来介绍Android关机流程的完整实现过程。当长按电源键时,按键消息被分发到PhoneWindowManager的interceptKeyBeforeQueueing函数中处理:







[java]



public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {



...



switch (keyCode) {



...



case KeyEvent.KEYCODE_POWER: {



result &= ~ACTION_PASS_TO_USER;



if (down) {



if (isScreenOn && !mPowerKeyTriggered



&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {



mPowerKeyTriggered = true;



mPowerKeyTime = event.getDownTime();



interceptScreenshotChord();//抓屏



}



ITelephony telephonyService = getTelephonyService();



boolean hungUp = false;



if (telephonyService != null) {



try {



if (telephonyService.isRinging()) {



//当来电时按下电源键,启动静音



telephonyService.silenceRinger();



} else if ((mIncallPowerBehavior



& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0



&& telephonyService.isOffhook()) {



// Otherwise, if "Power button ends call" is enabled,



// the Power button will hang up any current active call.



hungUp = telephonyService.endCall();



}



} catch (RemoteException ex) {



Log.w(TAG, "ITelephony threw RemoteException", ex);



}



}



interceptPowerKeyDown(!isScreenOn || hungUp



|| mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);



} else {



...



}



break;



}



...



}



return result;



}



电源键和音量键的组合可以实现特定功能,比如按下电源键和音量向下键,可实现抓屏,interceptKeyBeforeQueueing函数首先根据条件处理电源键按下的特定任务,然后调用interceptPowerKeyDown做进一步处理



[java]



private void interceptPowerKeyDown(boolean handled) {



mPowerKeyHandled = handled;



if (!handled) {



//隔500ms处理电源按键事件



mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());



}



}



这里的mHandler是在初始化PhoneWindowManager对象时创建的



[java]



public void init(Context context, IWindowManager windowManager,WindowManagerFuncs windowManagerFuncs,



LocalPowerManager powerManager) {



...



mHandler = new PolicyHandler();



...



}



将一个Runnable对象mPowerLongPress发送到PolicyHandler中进行处理



[java]



private final Runnable mPowerLongPress = new Runnable() {



public void run() {



// The context isn't read



if (mLongPressOnPowerBehavior < 0) {



mLongPressOnPowerBehavior = mContext.getResources().getInteger(



com.android.internal.R.integer.config_longPressOnPowerBehavior);



}



switch (mLongPressOnPowerBehavior) {



case LONG_PRESS_POWER_NOTHING:



break;



case LONG_PRESS_POWER_GLOBAL_ACTIONS:



mPowerKeyHandled = true;



performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);



sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);



showGlobalActionsDialog();



break;



case LONG_PRESS_POWER_SHUT_OFF:



mPowerKeyHandled = true;



performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);



sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);



mWindowManagerFuncs.shutdown();



break;



}



}



};



在处理电源长按事件时,根据mLongPressOnPowerBehavior完成不同的处理过程,mLongPressOnPowerBehavior的值是通过配置文件来设置的,在frameworks/base/core/res/values/config.xml中有以下一段配置:







通过读取配置文件取得config_longPressOnPowerBehavior配置的值为1,因此将显示关机对话框







[java]



case LONG_PRESS_POWER_GLOBAL_ACTIONS:



mPowerKeyHandled = true;



performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);



//向ActivityManagerService请求关闭所有窗口



sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);



//显示关机对话框



showGlobalActionsDialog();



break;



关机对话框显示:



[java]



void showGlobalActionsDialog() {



//创建GlobalActions对象



if (mGlobalActions == null) {



mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);



}



final boolean keyguardShowing = keyguardIsShowingTq();



//显示对话框



mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());



if (keyguardShowing) {



// since it took two seconds of long press to bring this up,



// poke the wake lock so they have some time to see the dialog.



mKeyguardMediator.pokeWakelock();



}



}



通过GlobalActions的showDialog函数来显示对话框



[java]



public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {



mKeyguardShowing = keyguardShowing;



mDeviceProvisioned = isDeviceProvisioned;



if (mDialog != null) {



mDialog.dismiss();



mDialog = null;



// Show delayed, so that the dismiss of the previous dialog completes



mHandler.sendEmptyMessage(MESSAGE_SHOW);



} else {



handleShow();//关机对话框显示



}



}







[java]



private void handleShow() {



//创建对话框



mDialog = createDialog();



prepareDialog();//设置对话框属性



mDialog.show();//显示对话框



mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);



}



createDialog()函数用于创建一个即将显示的关机对话框,就是前面图片显示的对话框。



[java]



private AlertDialog createDialog() {



//=========================创建对话框显示的列表项视图=====================================



//每一个列表项被被抽象为Action对象,



// Simple toggle style if there's no vibrator, otherwise use a tri-state



if (!mHasVibrator) {



mSilentModeAction = new SilentModeToggleAction();



} else {



mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);



}



//创建飞行模式切换ToggleAction对象



mAirplaneModeOn = new ToggleAction(



R.drawable.ic_lock_airplane_mode,



R.drawable.ic_lock_airplane_mode_off,



R.string.global_actions_toggle_airplane_mode,



R.string.global_actions_airplane_mode_on_status,



R.string.global_actions_airplane_mode_off_status) {



void onToggle(boolean on) {



if (mHasTelephony && Boolean.parseBoolean(SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {



mIsWaitingForEcmExit = true;



// Launch ECM exit dialog



Intent ecmDialogIntent =new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);



ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);



mContext.startActivity(ecmDialogIntent);



} else {



changeAirplaneModeSystemSetting(on);



mHandler.removeMessages(EVENT_SERVICE_CHANGE_WAIT_TIMEOUT);



mHandler.sendEmptyMessageDelayed(EVENT_SERVICE_CHANGE_WAIT_TIMEOUT,DELAY_AIRPLANE_SET_TIME);



}



}



@Override



protected void changeStateFromPress(boolean buttonOn) {



if (!mHasTelephony) return;



// In ECM mode airplane state cannot be changed



if (!(Boolean.parseBoolean(SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {



mState = buttonOn ? State.TurningOn : State.TurningOff;



mAirplaneState = mState;



}



}



public boolean showDuringKeyguard() {



return true;



}



public boolean showBeforeProvisioning() {



return false;



}



};



//更新当前飞行模式状态



onAirplaneModeChanged();



onRadioBusyStateChanged();



mItems = new ArrayList<Action>();



//向mItems列表中依次添加关机选择,飞行模式切换,静音模式选择



// first: power off



mItems.add(



//创建关机SinglePressAction



new SinglePressAction(com.android.internal.R.drawable.ic_lock_power_off,



R.string.global_action_power_off) {



public void onPress() {



// shutdown by making sure radio and power are handled accordingly.



mWindowManagerFuncs.shutdown();



}



public boolean onLongPress() {



mWindowManagerFuncs.rebootSafeMode();



return true;



}



public boolean showDuringKeyguard() {



return true;



}



public boolean showBeforeProvisioning() {



return true;



}



});



// next: airplane mode



mItems.add(mAirplaneModeOn);



// last: silent mode



if (SHOW_SILENT_TOGGLE) {



mItems.add(mSilentModeAction);



}



//获取系统中所有用户信息



List<UserInfo> users = mContext.getPackageManager().getUsers();



if (users.size() > 1) {//对于多用户Android系统,在显示的对话框下面添加用户切换选项



UserInfo currentUser;



try {



currentUser = ActivityManagerNative.getDefault().getCurrentUser();



} catch (RemoteException re) {



currentUser = null;



}



for (final UserInfo user : users) {



boolean isCurrentUser = currentUser == null



? user.id == 0 : (currentUser.id == user.id);



SinglePressAction switchToUser = new SinglePressAction(



com.android.internal.R.drawable.ic_menu_cc,



(user.name != null ? user.name : "Primary")



+ (isCurrentUser ? " \u2714" : "")) {



public void onPress() {



try {



ActivityManagerNative.getDefault().switchUser(user.id);



getWindowManager().lockNow();



} catch (RemoteException re) {



Log.e(TAG, "Couldn't switch user " + re);



}



}







public boolean showDuringKeyguard() {



return true;



}







public boolean showBeforeProvisioning() {



return false;



}



};



mItems.add(switchToUser);



}



}



mAdapter = new MyAdapter();//创建适配器,保存了所有数据,这里用MyAdapter保存列表项视图



//=========================创建对话框=========================================



final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);



ab.setAdapter(mAdapter, this).setInverseBackgroundForced(true);



final AlertDialog dialog = ab.create();



dialog.getListView().setItemsCanFocus(true);



dialog.getListView().setLongClickable(true);



dialog.getListView().setOnItemLongClickListener(



new AdapterView.OnItemLongClickListener() {



@Override



public boolean onItemLongClick(AdapterView<?> parent, View view, int position,



long id) {



return mAdapter.getItem(position).onLongPress();//视图和数据相关联



}



});



dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);



dialog.setOnDismissListener(this);



return dialog;



}



这里需要介绍对话框列表创建的方法,代码实现比较灵魂,值得学习。首先将显示对话框列表中的每一项用应该Action来描述,Action是一个接口,定义了每一个列表项的动作方法











各个列表项定义不同类型的Action,比如关机选项使用SinglePressAction来描述,目前Android系统定义了几种类型的Action,对应关机对话框中的不同选项。











将创建的列表选项添加到动态数组mItems中,并且为该对话框定义一个适配器MyAdapter,在单击对话框列表项时,调用适配器中对应的项来响应单击事件。Android适配器的使用实现了MVC
​​编程​​模式,将数据和视图分开。通常我们将数据保存在一个数组中,通过适配器和视图控件关联显示,只不过这里的数据比较特殊,它是用来描述列表项显示的内容。







[java]



public boolean onItemLongClick(AdapterView<?> parent, View view, int position,long id) {



return mAdapter.getItem(position).onLongPress();



}







public void onClick(DialogInterface dialog, int which) {



if (!(mAdapter.getItem(which) instanceof SilentModeTriStateAction)) {



dialog.dismiss();



}



mAdapter.getItem(which).onPress();



}



对于关机选项,其单击和长按事件处理过程如下:



[java]



public void onPress() {



// shutdown by making sure radio and power are handled accordingly.



mWindowManagerFuncs.shutdown();



}







public boolean onLongPress() {



mWindowManagerFuncs.rebootSafeMode();



return true;



}



对关机的处理都是调用mWindowManagerFuncs来完成的,mWindowManagerFuncs的类型为WindowManagerFuncs,在调用PhoneWindowManager的init函数时通过参数传进来。mWindowManagerFuncs对象在那里构造呢?WindowManagerService定义了一个WindowManagerPolicy类型变量:



[java]



final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();



通过策略管理类PolicyManager对象的makeNewWindowManager函数创建一个窗口策略管理对象



[java]



public static WindowManagerPolicy makeNewWindowManager() {



return sPolicy.makeNewWindowManager();



}



该函数通过Binder通信方式请求服务端Policy来完成对象的创建过程



Policy.java







[java]



public WindowManagerPolicy makeNewWindowManager() {



return new PhoneWindowManager();



}



在WindowManagerService的构造过程中,会创建一个PolicyThread类来初始化窗口管理策略WindowManagerPolicy



[java]



private WindowManagerService(Context context, PowerManagerService pm,



boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {



...



PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);



thr.start();



synchronized (thr) {



while (!thr.mRunning) {



try {



thr.wait();



} catch (InterruptedException e) {



}



}



}



...



}



在PolicyThread线程中执行初始化函数



[java] class PolicyThread extends Thread {



@Override



public void run() {



Looper.prepare();



...



mPolicy.init(mContext, mService, mService, mPM);



synchronized (this) {



mRunning = true;



notifyAll();



}



...



Looper.loop();



}



}



mPolicy的类型定义为WindowManagerPolicy类型,而WindowManagerPolicy是一个接口类型,PhoneWindowManager实现了该接口,为mPolicy创建的真正对象是PhoneWindowManager对象,因此PolicyThread线程将调用PhoneWindowManager的init函数



[java]



public void init(Context context, IWindowManager windowManager,



WindowManagerFuncs windowManagerFuncs,LocalPowerManager powerManager) {



...



mWindowManagerFuncs = windowManagerFuncs;



...



}



传进来的参数windowManager和windowManagerFuncs都是WindowManagerService对象,因为WindowManagerService继承于IWindowManager.Stub类同时实现了WindowManagerPolicy.WindowManagerFuncs接口,这下就很清晰地知道GlobalActions类中的mWindowManagerFuncs变量其实是WindowManagerService对象,因此关机的单击及长按事件由WindowManagerService实现:



[java]



public void shutdown() {



ShutdownThread.shutdown(mContext, true);



}







public void rebootSafeMode() {



ShutdownThread.rebootSafeMode(mContext, true);



}



WindowManagerService也不真正实现关机操作,而是转交个ShutdownThread来完成。对于关机处理过程:



[java]



public static void shutdown(final Context context, boolean confirm) {



mReboot = false;



mRebootSafeMode = false;



shutdownInner(context, confirm);



}



该函数实现非常简单,只是设置一些标志位,然后将关机任务又转交给shutdownInner来处理,这里的参数confirm用于标识是否需要弹出关机确认对话框。











[java]



static void shutdownInner(final Context context, boolean confirm) {



// ensure that only one thread is trying to power down.



// any additional calls are just returned



synchronized (sIsStartedGuard) {



if (sIsStarted) {



Log.d(TAG, "Request to shutdown already running, returning.");



return;



}



}



final int longPressBehavior = context.getResources().getInteger(



com.android.internal.R.integer.config_longPressOnPowerBehavior);



final int resourceId = mRebootSafeMode



? com.android.internal.R.string.reboot_safemode_confirm



: (longPressBehavior == 2



? com.android.internal.R.string.shutdown_confirm_question



: com.android.internal.R.string.shutdown_confirm);



Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);



//显示关机确认对话框



if (confirm) {



final CloseDialogReceiver closer = new CloseDialogReceiver(context);



final AlertDialog dialog = new AlertDialog.Builder(context)



.setTitle(mRebootSafeMode



? com.android.internal.R.string.reboot_safemode_title



: com.android.internal.R.string.power_off)



.setMessage(resourceId)



.setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {



public void onClick(DialogInterface dialog, int which) {



beginShutdownSequence(context);



}



})



.setNegativeButton(com.android.internal.R.string.no, null)



.create();



closer.dialog = dialog;



dialog.setOnDismissListener(closer);



dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);



dialog.show();



} else {//不显示关机确认对话框,直接进入关机流程



beginShutdownSequence(context);



}



}



调用beginShutdownSequence函数进入关机流程。



[java]



private static void beginShutdownSequence(Context context) {



synchronized (sIsStartedGuard) {



if (sIsStarted) {



Log.d(TAG, "Shutdown sequence already running, returning.");



return;



}



sIsStarted = true;



}



// throw up an indeterminate system dialog to indicate radio is



// shutting down.



ProgressDialog pd = new ProgressDialog(context);



pd.setTitle(context.getText(com.android.internal.R.string.power_off));



pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));



pd.setIndeterminate(true);



pd.setCancelable(false);



pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);



//pd.show();



shutdownTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_TIME;



//执行关机动画



String[] bootcmd = {"bootanimation", "shutdown"} ;



try {



Log.i(TAG, "exec the bootanimation ");



SystemProperties.set("service.bootanim.exit", "0");



Runtime.getRuntime().exec(bootcmd);



} catch (Exception e){



Log.e(TAG,"bootanimation command exe err!");



}



//初始化关机线程ShutdownThread



sInstance.mContext = context;



sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);



// make sure we never fall asleep again



sInstance.mCpuWakeLock = null;



try {



sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");



sInstance.mCpuWakeLock.setReferenceCounted(false);



sInstance.mCpuWakeLock.acquire();



} catch (SecurityException e) {



Log.w(TAG, "No permission to acquire wake lock", e);



sInstance.mCpuWakeLock = null;



}



// also make sure the screen stays on for better user experience



sInstance.mScreenWakeLock = null;



if (sInstance.mPowerManager.isScreenOn()) {



try {



sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(



PowerManager.FULL_WAKE_LOCK, TAG + "-screen");



sInstance.mScreenWakeLock.setReferenceCounted(false);



sInstance.mScreenWakeLock.acquire();



} catch (SecurityException e) {



Log.w(TAG, "No permission to acquire wake lock", e);



sInstance.mScreenWakeLock = null;



}



}



// start the thread that initiates shutdown



sInstance.mHandler = new Handler() {



};



//启动关机线程ShutdownThread



sInstance.start();



}



这个函数执行两个任务,1)通过
​​虚拟机​​​Runtime启动关机动画进程bootanimation,用于显示关机动画,开关机动画在 ​​Android​​ 开关机动画显示源码分析进行了详细的分析介绍。2)启动关机线程ShutdownThread来完成关机操作



[java]



public void run() {



BroadcastReceiver br = new BroadcastReceiver() {



@Override public void onReceive(Context context, Intent intent) {



// 用于接收关机广播



actionDone();



}



};



//写属性"sys.shutdown.requested"保存关机原因



{



String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");



SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);



}



//如果是安全模式关机,写属性"persist.sys.safemode"



if (mRebootSafeMode) {



SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");



}



Log.i(TAG, "Sending shutdown broadcast...");



// First send the high-level shut down broadcast.



mActionDone = false;



//发送关机广播



mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null,



br, mHandler, 0, null, null);



//等待10S,前面定义的广播接收器收到关机广播时mActionDone设置为true,同时取消等待



final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;



synchronized (mActionDoneSync) {



while (!mActionDone) {



long delay = endTime - SystemClock.elapsedRealtime();



if (delay <= 0) {



Log.w(TAG, "Shutdown broadcast timed out");



break;



}



try {



mActionDoneSync.wait(delay);



} catch (InterruptedException e) {



}



}



}



//10S时间内关闭ActivityManager服务



final IActivityManager am =ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));



if (am != null) {



try {



am.shutdown(MAX_BROADCAST_TIME);



} catch (RemoteException e) {



}



}



//12s内关闭radios.



shutdownRadios(MAX_RADIO_WAIT_TIME);



//10s内关闭ICCS



shutdownIccs(MAX_ICC_WAIT_TIME);



// Shutdown MountService to ensure media is in a safe state



IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {



public void onShutDownComplete(int statusCode) throws RemoteException {



Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");



actionDone();



}



};



Log.i(TAG, "Shutting down MountService");



//20s内关闭MountService服务



mActionDone = false;



final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;



synchronized (mActionDoneSync) {



try {



final IMountService mount = IMountService.Stub.asInterface(ServiceManager.checkService("mount"));



if (mount != null) {



mount.shutdown(observer);



} else {



Log.w(TAG, "MountService unavailable for shutdown");



}



} catch (Exception e) {



Log.e(TAG, "Exception during MountService shutdown", e);



}



while (!mActionDone) {



long delay = endShutTime - SystemClock.elapsedRealtime();



if (delay <= 0) {



Log.w(TAG, "Shutdown wait timed out");



break;



}



try {



mActionDoneSync.wait(delay);



} catch (InterruptedException e) {



}



}



}



//关机时间定义为5s,这里计算关机超过的时间



long shutdownDelay = shutdownTime - SystemClock.elapsedRealtime();



if (shutdownDelay > 0) {



Log.i(TAG, "Shutdown delay:"+shutdownDelay);



SystemClock.sleep(shutdownDelay);



}



//继续关机



rebootOrShutdown(mReboot, mRebootReason);



}



该函数内主要完成以下一些工作:



(1)发送关机广播ACTION_SHUTDOWN







(2)关闭ActivityManager 服务







(3)关闭无线相关的服务







(4)关闭Iccs







(5)关闭MountService服务







[java]



public static void rebootOrShutdown(boolean reboot, String reason) {



if (reboot) {//是否重启



Log.i(TAG, "Rebooting, reason: " + reason);



try {



PowerManagerService.lowLevelReboot(reason);



} catch (Exception e) {



Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);



}



} else if (SHUTDOWN_VIBRATE_MS > 0) {//震动时间为500ms



// vibrate before shutting down



Vibrator vibrator = new SystemVibrator();



try {



//关机震动500ms



vibrator.vibrate(SHUTDOWN_VIBRATE_MS);



} catch (Exception e) {



// Failure to vibrate shouldn't interrupt shutdown. Just log it.



Log.w(TAG, "Failed to vibrate during shutdown.", e);



}



// vibrator is asynchronous so we need to wait to avoid shutting down too soon.



try {



Thread.sleep(SHUTDOWN_VIBRATE_MS);



} catch (InterruptedException unused) {



}



}



//关闭电源



Log.i(TAG, "Performing low-level shutdown...");



PowerManagerService.lowLevelShutdown();



}



这里是启动关机震动并关闭电源



[java]



public static void lowLevelShutdown() {



nativeShutdown();



}



这里通过JNI调用C++的关机函数



[java]



static void nativeShutdown(JNIEnv *env, jobject clazz) {



delFlag();



android_reboot(ANDROID_RB_POWEROFF, 0, 0);



}



android_reboot函数最终通过Linux
​​系统​​调用关闭系统。到此关机操作就基本完成了。