編輯:關於Android編程
下面是簡單的流程圖,從java到kernel層。
ShutdownThread.java文件
stop playing music,因為後面可能要playing shutdown music.
代碼如下:
private static void beginShutdownSequence(Context context) { .... //acquire audio focus to make the other apps to stop playing muisc mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mAudioManager.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
show system dialog to indicate phone is shutting down,如果沒有關機動畫的話,要show一個關機提示出來。
代碼如下:
if (!checkAnimationFileExist()) { // 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.mCpuWakeLock = sInstance.mPowerManager.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, TAG + -cpu);//這個只是鎖住cpu不進入休眠,但screen是off的,需full鎖來保證screen常亮 sInstance.mCpuWakeLock.setReferenceCounted(false); sInstance.mCpuWakeLock.acquire();
make sure the screen stays on,再抓一個full鎖,防止屏幕半暗
代碼如下:
sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock( PowerManager.FULL_WAKE_LOCK, TAG + -screen);//保持srceen常亮 sInstance.mScreenWakeLock.setReferenceCounted(false); sInstance.mScreenWakeLock.acquire();
sending shutdown broadcast,發出廣播,通知各app該保存數據趕緊的,我要關機了
代碼如下:
Intent intent = new Intent(Intent.ACTION_SHUTDOWN); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendOrderedBroadcastAsUser(intent,//發廣播 UserHandle.ALL, null, br, mHandler, 0, null, null);
注意:android L 與KK在關閉UsageStatsService上有所區別
代碼如下:
[ActivityManagerService.java]
final IActivityManager am = ActivityManagerNative.asInterface(ServiceManager.checkService(activity)); if (am != null) { try { am.shutdown(MAX_BROADCAST_TIME); } catch (RemoteException e) { } }
代碼如下:
[PackageManagerService.java]
final PackageManagerService pm = (PackageManagerService) ServiceManager.getService(package); if (pm != null) { pm.shutdown(); }
代碼如下:
private static void showShutdownAnimation() { /* * When boot completed, service.bootanim.exit property is set to 1. * Bootanimation checks this property to stop showing the boot animation. * Since we use the same code for shutdown animation, we * need to reset this property to 0. If this is not set to 0 then shutdown * will stop and exit after displaying the first frame of the animation */ SystemProperties.set(service.bootanim.exit, 0); SystemProperties.set(ctl.start, bootanim);//也是用bootanim進程,跟開關動畫一樣的方式。 }
代碼如下:
shutdownRadios(MAX_RADIO_WAIT_TIME);
shutdown 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); } 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) { } } }
代碼如下:
public static void rebootOrShutdown(boolean reboot, String reason) { deviceRebootOrShutdown(reboot, 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, VIBRATION_ATTRIBUTES); } 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();//關機 }
lowLevelShutdown()與lowLevelReboot()都在PowerManagerService.java實現,其實都只是設置一個屬性:SystemProperties.set(sys.powerctl, xxx);
正是這個動作觸發關機流程往下走,這涉及到init進程的4大功能,請參考我的另一篇文章Android的init進程
sys.powerctl屬性觸發開關在init.rc定義
on property:sys.powerctl=* powerctl ${sys.powerctl}
從下面的表可知,powerctl對應的操作是do_powerctl
[system/core/init/keywords.h]
KEYWORD(powerctl, COMMAND, 1, do_powerctl)
代碼如下:
[system/core/init/builtins.c]
int do_powerctl(int nargs, char **args) { .... return android_reboot(cmd, 0, reboot_target); }
[system/core/libcutils/android_reboot.c]
int android_reboot(int cmd, int flags UNUSED, char *arg) { int ret; sync(); remount_ro(); switch (cmd) { case ANDROID_RB_RESTART: ret = reboot(RB_AUTOBOOT); break; case ANDROID_RB_POWEROFF: ret = reboot(RB_POWER_OFF); break; case ANDROID_RB_RESTART2: ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, arg); break; default: ret = -1; } return ret; }
sync() 回寫block設備的內容,這是阻塞型操作。
remount_ro() 把block設備remount成ro,這裡有個關鍵LOG:SysRq : Emergency Remount R/O,這是在logkit所能看到的最後一句LOG,因為remount成ro了,後面的LOG要通過last kmsg技術導出來。
reboot()或者syscall(__NR_reboot....,這點與android KK不同,這邊直接用syscall功能,KK則通過匯編。
後面syscall(__NR_reboot...知道,直接調用了linux的__NR_reboot系統調用,這個系統調用會跑哪裡?後面會講。
reboot()這個函數實現如下:
[bionic/libc/bionic/reboot.cpp]
int reboot(int mode) { return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL); }
[bionic/libc/arch-arm/syscalls/__reboot.S]
ENTRY(__reboot) mov ip, r7 ldr r7, =__NR_reboot//也跑到__NR_reboot系統調用 swi #0 mov r7, ip cmn r0, #(MAX_ERRNO + 1) bxls lr neg r0, r0 b __set_errno_internal END(__reboot)
__NR_reboot對應的內核入口在哪裡?
如下:
[bionic/libc/kernel/uapi/asm-generic/unistd.h]
#define __NR_reboot 142
注:bionic/libc/kernel/uapi/asm-generic/unistd.h與kernel/include/uapi/asm-generic/unistd.h是對應的,方便以後代碼追蹤
[kernel/include/uapi/asm-generic/unistd.h]
#define __NR_reboot 142 __SYSCALL(__NR_reboot, sys_reboot)
__NR_reboot 映射到 sys_reboot
grep 下sys_reboot 找不到,其實在這裡
用SYSCALL_DEFINE定義
[kernel/kernel/sys.c]
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg) { struct pid_namespace *pid_ns = task_active_pid_ns(current); char buffer[256]; int ret = 0; /* We only trust the superuser with rebooting the system. */ if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT)) return -EPERM; /* For safety, we require magic arguments. */ if (magic1 != LINUX_REBOOT_MAGIC1 || (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A && magic2 != LINUX_REBOOT_MAGIC2B && magic2 != LINUX_REBOOT_MAGIC2C)) return -EINVAL; /* * If pid namespaces are enabled and the current task is in a child * pid_namespace, the command is handled by reboot_pid_ns() which will * call do_exit(). */ ret = reboot_pid_ns(pid_ns, cmd); if (ret) return ret; /* Instead of trying to make the power_off code look like * halt when pm_power_off is not set do it the easy way. */ if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off) cmd = LINUX_REBOOT_CMD_HALT; mutex_lock(&reboot_mutex); switch (cmd) { case LINUX_REBOOT_CMD_RESTART: kernel_restart(NULL); break; case LINUX_REBOOT_CMD_CAD_ON: C_A_D = 1; break; case LINUX_REBOOT_CMD_CAD_OFF: C_A_D = 0; break; case LINUX_REBOOT_CMD_HALT: kernel_halt(); do_exit(0); panic(cannot halt); case LINUX_REBOOT_CMD_POWER_OFF: kernel_power_off(); do_exit(0); break; case LINUX_REBOOT_CMD_RESTART2: if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) { ret = -EFAULT; break; } buffer[sizeof(buffer) - 1] = ''; kernel_restart(buffer); break; #ifdef CONFIG_KEXEC case LINUX_REBOOT_CMD_KEXEC: ret = kernel_kexec(); break; #endif #ifdef CONFIG_HIBERNATION case LINUX_REBOOT_CMD_SW_SUSPEND: ret = hibernate(); break; #endif default: ret = -EINVAL; break; } mutex_unlock(&reboot_mutex); return ret; }
有很多分支,我們只關心kernel_power_off()和kernel_restart()兩函數就行
如下:
void kernel_power_off(void) { kernel_shutdown_prepare(SYSTEM_POWER_OFF);//關閉外設 if (pm_power_off_prepare) pm_power_off_prepare(); migrate_to_reboot_cpu(); syscore_shutdown();//關閉syscore printk(KERN_EMERG Power down. );//關鍵打印 kmsg_dump(KMSG_DUMP_POWEROFF); machine_power_off(); }
void kernel_restart(char *cmd) { kernel_restart_prepare(cmd);//關閉外設 migrate_to_reboot_cpu(); syscore_shutdown();//關閉syscore if (!cmd) printk(KERN_EMERG Restarting system. );//關鍵打印 else printk(KERN_EMERG Restarting system with command '%s'. , cmd); kmsg_dump(KMSG_DUMP_RESTART); machine_restart(cmd); }
static void kernel_shutdown_prepare(enum system_states state) { blocking_notifier_call_chain(&reboot_notifier_list, (state == SYSTEM_HALT)?SYS_HALT:SYS_POWER_OFF, NULL); system_state = state; usermodehelper_disable(); device_shutdown(); }
void kernel_restart_prepare(char *cmd) { blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); system_state = SYSTEM_RESTART; usermodehelper_disable(); device_shutdown(); }
machine_power_off() machine_resestart()函數實現
void machine_power_off(void) { preempt_disable(); smp_send_stop(); if (pm_power_off) pm_power_off();//關機 }
void machine_restart(char *cmd) { preempt_disable(); smp_send_stop(); /* Flush the console to make sure all the relevant messages make it * out to the console drivers */ arm_machine_flush_console(); arm_pm_restart(reboot_mode, cmd);//重啟 /* Give a grace period for failure to restart of 1s */ mdelay(1000); /* Whoops - the platform was unable to reboot. Tell the user! */ printk(Reboot failed -- System halted ); local_irq_disable(); while (1); }
賦值如下:
[kernel/drivers/power/reset/msm-poweroff.c]
pm_power_off = do_msm_poweroff; arm_pm_restart = do_msm_restart;
do_msm_poweroff()與do_msm_restart()實現如下:
static void do_msm_poweroff(void) { .... pr_notice(Powering off the SoC );//關鍵打印 #ifdef CONFIG_MSM_DLOAD_MODE set_dload_mode(0);//關機,所以dloadmode是0 #endif qpnp_pon_system_pwr_off(PON_POWER_OFF_SHUTDOWN);//配置PMIC,是關機 ..... /* MSM initiated power off, lower ps_hold */ __raw_writel(0, msm_ps_hold);//拉 PS_HOLD,執行關機動作。 mdelay(10000); pr_err(Powering off has failed ); return; }
static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd) ... pr_notice(Going down for restart now );//關鍵打印 msm_restart_prepare(cmd);//重啟准備前動作 #ifdef CONFIG_MSM_DLOAD_MODE /* * Trigger a watchdog bite here and if this fails, * device will take the usual restart path. */ if (WDOG_BITE_ON_PANIC && in_panic) msm_trigger_wdog_bite(); #endif .... halt_spmi_pmic_arbiter(); __raw_writel(0, msm_ps_hold);//拉PS_HOLD重啟 mdelay(10000); }
static void msm_restart_prepare(const char *cmd) { #ifdef CONFIG_MSM_DLOAD_MODE /* Write download mode flags if we're panic'ing * Write download mode flags if restart_mode says so * Kill download mode if master-kill switch is set */ set_dload_mode(download_mode && (in_panic || restart_mode == RESTART_DLOAD));//設置dload #endif /* Hard reset the PMIC unless memory contents must be maintained. */ if (get_dload_mode() || (cmd != NULL && cmd[0] != '')) qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET);//設置PIMC為熱重啟 else qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET);//設置PIMC為硬重啟 if (cmd != NULL) { if (!strncmp(cmd, bootloader, 10)) { __raw_writel(0x77665500, restart_reason);//寫一些東東到IMEM,用於bootloader,recovery等 } else if (!strncmp(cmd, recovery, 8)) { __raw_writel(0x77665502, restart_reason); } else if (!strcmp(cmd, rtc)) { __raw_writel(0x77665503, restart_reason); } else if (!strncmp(cmd, oem-, 4)) { unsigned long code; int ret; ret = kstrtoul(cmd + 4, 16, &code); if (!ret) __raw_writel(0x6f656d00 | (code & 0xff), restart_reason); } else if (!strncmp(cmd, edl, 3)) { enable_emergency_dload_mode(); } else { __raw_writel(0x77665501, restart_reason); } } ..... }
do_msm_poweroff()與do_msm_restart()都設置了dload,PMIC,唯一不同的是do_msm_restart()裡多了一個__raw_writel的動作,即reason寫入IMEM,目的在於重啟進入sbl1時判斷應該進入那種模式,如我們開發用的bootloader模式,恢復出廠設置的recovery模式等。
ListView我們一直都在用,只不過當Adapter中的內容比較多的時候我們有時候沒辦法去設置一些組件,舉個例子:可以看到京東的故事裡面的這樣一個布局,這個布局可以說是
微信中刪除的聊天記錄如何找回?相信大家都有過誤刪了重要的微信聊天記錄而在那裡拼命懊悔的經歷,那都是因為沒有找到一個很好的解決方法。其實在科技如此發達的今天,
最近在玩3g體育門戶客戶端的時候,看到這樣個效果: 輕觸賽事圖標,會有一個圖標變大浮出的效果.,蠻有意思的.於是就把仿照它做了一個. 這個是原圖:
一、程序運行效果圖: 二、代碼實現 1、main.xml 2、MainActivity 1