Android Framework层Power键关机流程(二,关机流程)
二,关机流程
从前一篇博文我们知道,当用户长按Power键时会弹出(关机、重启,飞行模式等选项)对话框,我们点击关机,则会弹出关机确认对话框。那么从选项对话框到关机确认对话框又是一个什么流程呢。下面我们在简单分析一下:
showGlobalActionsDialog()-->showDialog()-->handleShow()-->createDialog()-->onPress()-->shutdown()
PhoneWindowManager.java
void showGlobalActionsDialog() {
......
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
......
}
GlobalActions.java
public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
......
handleShow();
......
}
private void handleShow() {
......
mDialog = createDialog();
......
}
private GlobalActionsDialog createDialog(){
......
mItems = new ArrayList<Action>();
// first: power off
mItems.add(
new SinglePressAction(
com.android.internal.R.drawable.uirom_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(true);
}
public boolean onLongPress() {
mWindowManagerFuncs.rebootSafeMode(true);
return true;
}
public boolean showDuringKeyguard() {
return true;
}
public boolean showBeforeProvisioning() {
return true;
}
});
......
}
上述代码中的mWindowManagerFuncs实际上是WindowManagerService的对象,该对象有PhoneWindowManager的init的方法传入GlobalActions的构造函数中,并在上述代码中进行调用。下面这一行代码是调用的关键代码。
mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
下面是弹出“关机确认对话框”的堆栈:
01-16 18:08:21.497 D/bill ( 720): java.lang.Throwable
01-16 18:08:21.497 D/bill ( 720): at com.android.server.power.ShutdownThread.shutdown(ShutdownThread.java:175)
01-16 18:08:21.497 D/bill ( 720): at com.android.server.wm.WindowManagerService.shutdown(WindowManagerService.java:5783)01-16 18:08:21.497 D/bill ( 720):at com.android.internal.policy.impl.GlobalActions$2.onPress(GlobalActions.java:352)//WindowManagerService实现了接口WindowsManagerFuncs
01-16 18:08:21.497 D/bill ( 720): at com.android.internal.policy.impl.GlobalActions.onClick(GlobalActions.java:581)
01-16 18:08:21.497 D/bill ( 720): at com.android.internal.app.AlertController$AlertParams$3.onItemClick(AlertController.java:952)
01-16 18:08:21.497 D/bill ( 720): at android.widget.AdapterView.performItemClick(AdapterView.java:299)
01-16 18:08:21.497 D/bill ( 720): at android.widget.AbsListView.performItemClick(AbsListView.java:1152)
01-16 18:08:21.497 D/bill ( 720): at android.widget.AbsListView$PerformClick.run(AbsListView.java:3014)
01-16 18:08:21.497 D/bill ( 720): at android.widget.AbsListView$3.run(AbsListView.java:3865)
01-16 18:08:21.497 D/bill ( 720): at android.os.Handler.handleCallback(Handler.java:808)
01-16 18:08:21.497 D/bill ( 720): at android.os.Handler.dispatchMessage(Handler.java:103)
01-16 18:08:21.497 D/bill ( 720): at android.os.Looper.loop(Looper.java:193)
01-16 18:08:21.497 D/bill ( 720): at android.os.HandlerThread.run(HandlerThread.java:61)
从这里(shutdown())我们正式进入关机流程的关键。
shutdown()<ShutdownThread.java> --->shutdownInner() --->beginShutdownSequence()--->run()--->rebootOrShutdown()--->lowLevelShutdown()<PowerManagerService.java>--->
源码来自:https://github.com/android/platform_frameworks_base/blob/master/services/java/com/android/server/power/ShutdownThread.java
public static void shutdown(final Context context, boolean confirm) {
mReboot = false;
mRebootSafeMode = false;
shutdownInner(context, confirm);
}
注!
参数2:confir;关机操作前是否需要用户进行确认
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);
//longPressBehavior的值标示当前长按Power操作意向(关机、重启。。。)
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);
if (sConfirmDialog != null) {
sConfirmDialog.dismiss();
}
sConfirmDialog = 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 = sConfirmDialog;
sConfirmDialog.setOnDismissListener(closer);
sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
sConfirmDialog.show();
} else {
beginShutdownSequence(context);
}
}
注:上述代码中,如果需要用户确认关机操作,则会弹出对话框,在对话框的确认按钮被触发时,调用beginShutdownSequence()方法继续关机流程。如果无需用户确认,则直接调用beginShutdownSequence()进入下一个关机流程节点。
在beginShutdownSequence()有些手机厂商常常会在这里添加一些定制功能,例如在对话框中添加“下次快速开机”,定制关机动画等等。随后会根据不同平台进行讲解。下面这张图是Android原生系统的关机画面(对应下面加粗显示的代码):
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();
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() {
};
sInstance.start();⑤
}
注解!
①上述红色代码中的作用主要是为了防止手机进入休眠状态,从代码中我们看到,此时通过PowerManager的newWakeLock方法生成了PowerManager.WakeLock对象。newWakeLock()是PowerManager中最为常用的方法,该对象是一种锁机制,通过该对象可以控制设备的电源状态。在生成WakeLock实例时通过第一个参数的传入只开控制获取不同的WakeLock,主要是不同的lock对CPU,屏幕,键盘灯有不同的影响。如下:
- PARTIAL_WAKE_LOCK:保持CPU 运转,屏幕和键盘灯有可能是关闭的。
- SCREEN_DIM_WAKE_LOCK:保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯
- SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯
- FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度
②Wake Lock 是一种锁的机制,只要有人拿着这个锁,系统九五案发进入休眠,可以被用户动态程序和内核获得,这个锁可以使有超时的或者是没有超时的,超时的锁会在时间过去以后自动解锁。如果没有锁了,或者超时了,内核就会启动休眠的那套机制来进入休眠。PowerManager.WakeLock有加锁和解锁的两种状态,加锁的方式有两种,一种是永久的锁住,这样的锁除非是显示的放开,否则是不会解锁的,所以这种锁用起来要非常小心,第二种锁是超时锁,这种锁会在锁住一段时间后自动解锁。
sInstance.mCpuWakeLock.setReferenceCounted(false);是设置锁的方式为永久的锁住。
③sInstance.mCpuWakeLock.acquire(); 加锁
④上述蓝色代码的作用是为了保证用户体验,保持屏幕、键盘的亮度
⑤接着启动关机线程,进入关机流程的下一个节点。
/**
* Makes sure we handle the shutdown gracefully.
* Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
*/
public void run() {
BroadcastReceiver br = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
actionDone();//这里用于接受关机广播,actionDone()方法主要是防止应用程序取消关机操作。
}
};
/*
* Write a system property in case the system_server reboots before we
* get to the actual hardware restart. If that happens, we'll retry at
* the beginning of the SystemServer startup.
*/
{
String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
}
/*
* If we are rebooting into safe mode, write a system property
* indicating so.
*/
if (mRebootSafeMode) {
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
}
Log.i(TAG, "Sending shutdown broadcast...");
// First send the high-level shut down broadcast.
mActionDone = false;
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcastAsUser(intent,
UserHandle.ALL, null, br, mHandler, 0, null, null);//发送关机广播
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) {
}
}
}
Log.i(TAG, "Shutting down activity manager...");
final IActivityManager am =
ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
if (am != null) {
try {
am.shutdown(MAX_BROADCAST_TIME);//关闭ActivityManagerService
} catch (RemoteException e) {
}
}
Log.i(TAG, "Shutting down package manager...");
final PackageManagerService pm = (PackageManagerService)
ServiceManager.getService("package");
if (pm != null) {
pm.shutdown();//关闭PackageManagerService服务
}
// 关闭Radios
shutdownRadios(MAX_RADIO_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");
// Set initial variables and time out time.
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);//关闭MountService
} 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) {
}
}
}
rebootOrShutdown(mReboot, mRebootReason);
}
最后调用rebootOrShutdown()
public static void rebootOrShutdown(boolean reboot, String reason) {
if (reboot) {
Log.i(TAG, "Rebooting, reason: " + reason);
PowerManagerService.lowLevelReboot(reason);
Log.e(TAG, "Reboot failed, will attempt shutdown instead");
} else if (SHUTDOWN_VIBRATE_MS > 0) {
// vibrate before shutting down
Vibrator vibrator = new SystemVibrator();
try {
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) {
}
}
// Shutdown power
Log.i(TAG, "Performing low-level shutdown...");
PowerManagerService.lowLevelShutdown();//关闭电源
}
}
/**
* Low-level function turn the device off immediately, without trying
* to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
*/
public static void lowLevelShutdown() {
SystemProperties.set("sys.powerctl", "shutdown");//这里通过修改Android属性进行关机
}
注:上述代码中,红色加粗部分为关机关键代码,我也可以通过adb 命令进行修改Android系统的属性进行关机,具体命令如下
adb shell setprop sys.powerctl shutdown
Android Framework层Power键关机流程(二,关机流程)的更多相关文章
- Android Framework层Power键关机流程(一,Power长按键操作处理)
一:Android处理Power按键长按操作 在Framework层中,Android4.x对Power键(KeyEvent.KEYCODE_POWER)的操作,我们从PhoneWindowManag ...
- 怎样从C++代码直接訪问android framework层的WifiService
说究竟,Java层的service就是就C++层的binder的封装.所以从原理上来讲通过C++代码直接訪问android framework层的service是全然可能的,这篇文章以訪问WifiSe ...
- Android 手动按power键上锁,没有锁屏提示音,无法恢复【单机必现】
測试步骤 [測试版本号]T0606 [模块版本号] NAVI锁屏:5.0.0.ck [測试步骤] 1.手动按power键上锁, [測试结果] 没有锁屏提示音,无法恢复[单机必现] [预期结果] 有提示 ...
- Android framework层实现实现wifi无缝切换AP
http://www.linuxidc.com/Linux/2013-12/93476.htm Android市场上有一款叫Wifijumper的软件,实现相同ssid的多个AP之间根据wifi信号的 ...
- Android 7.0 Power 按键处理流程
Android 7.0 Power 按键处理流程 Power按键的处理逻辑由PhoneWindowManager来完成,本文只关注PhoneWindowManager中与Power键相关的内容,其他 ...
- Android下添加新的自定义键值和按键处理流程【转】
本文转载自: Android下添加新的自定义键值和按键处理流程 说出来不怕大家笑话,我写这篇博客的原因在于前几天去一个小公司面试Android系统工程师,然后在面试的时候对方的技术总监问了我一 ...
- 关于一条定制长按Power键弹出Dialog的需求
如题,需要定制长按Power键弹出的Dialog,UI上的大致效果是:全屏,中间下拉按钮“Swipe Down To Power Off”下拉关机,底部左右两侧“Reboot”,“Cancel”按钮, ...
- 如何调试Android Framework?
Linus有一句名言广为人知:Read the fucking source code. 但其实,要深入理解某个软件.框架或者系统的工作原理,仅仅「看」代码是远远不够的.就拿Android Frame ...
- Android按键事件传递流程(二)
5 应用层如何从Framework层接收按键事件 由3.2和4.5.4节可知,当InputDispatcher通过服务端管道向socket文件描述符发送消息后,epoll机制监听到了I/O事件, ...
随机推荐
- python(二)数据类型
一.整数 创建方法 i = 10 i = int(10) i = int("10",base=2) #“”中是几进制的表示,base是选择要表示的进制,如base=2,用2进制的 ...
- CentOS的网络配置(终端环境)
在虚拟机中安装了Server版的CentOS,用于做Hadoop相关的实验.Server版的CentOS默认是没有桌面环境的,eth0的网络默认也没有开启,需要我们手动启动网络. 关键配置说明 网络和 ...
- Winform开发框架之介绍
winform开发框架,尽量减少程序员在界面中的代码量和工作量,Model自动生成,界面以及控件自动生成,简单的逻辑自动生成.自动生成的界面已经实现简单逻辑增删改查功能. 其他开发框架都已经或多火烧实 ...
- 教你解决Sublime Text中文乱码问题
教你解决Sublime Text中文乱码问题[转载自成长的企鹅] Sublime Text 2是一个非常不错的源代码及文本编辑器,但是不支持GB2312和GBK编码在很多情况下会非常麻烦. ...
- jQuery核心之DOM操作的常用方法
参考jQuery官网API文档 1..attr() 获取 : ); 3.选择器与常用方法: ) .html( "new text for the third h3!" ) .en ...
- 启动tomcat后struts框架报异常严重: Exception starting filter struts2 Unable to load configuration.
启动tomcat后struts框架报异常严重: Exception starting filter struts2 Unable to load configuration. 出现此异常是因为,str ...
- 使用window.print实现网页打印
Window.print()方法用于在浏览器中打印当前窗口的内容,如果想要打印当前窗口中指定部分的内容的话需要其他的一些特殊的处理; Demo: <html> <head> & ...
- VIM 代码折叠
VIM 代码折叠 VIM代码折叠方式可以用"foldmethod"选项来设置,如: set foldmethod=indent 有6种方式来折叠代码 1. manual //手工定 ...
- 能源项目xml文件 -- springMVC-servlet.xml -- default-lazy-init
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w ...
- Windows C++ 子目录数量
CFileFind OneFile; BOOL bWorking = FALSE; ; ) != "\\") { strPath += "\\*.*"; } b ...