Android关机流程源码分析

 

上一篇文章 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系统调用关闭系统。到此关机操作就基本完成了。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值