Android关机流程源码分析

来源:互联网 发布:js给value赋值 编辑:程序博客网 时间:2024/05/17 16:55

 

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


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

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做进一步处理
private void interceptPowerKeyDown(boolean handled) {  mPowerKeyHandled = handled;  if (!handled) {    //隔500ms处理电源按键事件    mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());  }}
这里的mHandler是在初始化PhoneWindowManager对象时创建的
public void init(Context context, IWindowManager windowManager,WindowManagerFuncs windowManagerFuncs,    LocalPowerManager powerManager) {  ...  mHandler = new PolicyHandler();  ...}
将一个Runnable对象mPowerLongPress发送到PolicyHandler中进行处理
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,因此将显示关机对话框

case LONG_PRESS_POWER_GLOBAL_ACTIONS:  mPowerKeyHandled = true;  performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);  //向ActivityManagerService请求关闭所有窗口  sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);  //显示关机对话框  showGlobalActionsDialog();  break;
关机对话框显示:
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函数来显示对话框
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();//关机对话框显示  }}
private void handleShow() {  //创建对话框  mDialog = createDialog();  prepareDialog();//设置对话框属性  mDialog.show();//显示对话框  mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);}
createDialog()函数用于创建一个即将显示的关机对话框,就是前面图片显示的对话框。
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编程模式,将数据和视图分开。通常我们将数据保存在一个数组中,通过适配器和视图控件关联显示,只不过这里的数据比较特殊,它是用来描述列表项显示的内容。

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();}
对于关机选项,其单击和长按事件处理过程如下:
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类型变量:
final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();
通过策略管理类PolicyManager对象的makeNewWindowManager函数创建一个窗口策略管理对象
public static WindowManagerPolicy makeNewWindowManager() {  return sPolicy.makeNewWindowManager();}
该函数通过Binder通信方式请求服务端Policy来完成对象的创建过程

Policy.java

public WindowManagerPolicy makeNewWindowManager() {  return new PhoneWindowManager();}
在WindowManagerService的构造过程中,会创建一个PolicyThread类来初始化窗口管理策略WindowManagerPolicy
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线程中执行初始化函数
static 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函数 
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实现:
public void shutdown() {  ShutdownThread.shutdown(mContext, true);}public void rebootSafeMode() {  ShutdownThread.rebootSafeMode(mContext, true);}
WindowManagerService也不真正实现关机操作,而是转交个ShutdownThread来完成。对于关机处理过程:
public static void shutdown(final Context context, boolean confirm) {  mReboot = false;  mRebootSafeMode = false;  shutdownInner(context, confirm);}
该函数实现非常简单,只是设置一些标志位,然后将关机任务又转交给shutdownInner来处理,这里的参数confirm用于标识是否需要弹出关机确认对话框。

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函数进入关机流程。
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来完成关机操作
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服务

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();}
这里是启动关机震动并关闭电源
public static void lowLevelShutdown() {  nativeShutdown();}
这里通过JNI调用C++的关机函数
static void nativeShutdown(JNIEnv *env, jobject clazz) {    delFlag();    android_reboot(ANDROID_RB_POWEROFF, 0, 0);}
android_reboot函数最终通过Linux系统调用关闭系统。到此关机操作就基本完成了。 
0 0
原创粉丝点击