①设置界面进入系统更新的设置界面为设置模块下DeviceInfoSettings.java类,所对应preference为Device_info_settings.xml中如下:
android:title="@string/system_update_settings_list_item_title"
android:summary="@string/system_update_settings_list_item_summary">
②我们在DeviceInfoSettings.java中对该preference进行监听,当用户触发点击事件时发出广播,XXX应用会接收该广播并启动XXX做出下一步处理。如下所示:
else if (preference.getKey().equals(KEY_DMSW_UPDATE)) {
// /M: for DMSW to broadcast @{
Intent i = new Intent();
i.setAction("com.mediatek.DMSWUPDATE");
getActivity().sendBroadcast(i);
// /@}
}
2.一般来说,由于Android原生态的系统更新体验一般,我们手机中的系统更新应用都是厂商定制的,也就是说我们点击系统更新时一般进入的都是厂商定制的系统更新应用,这里我们先不赘述,随后的博文中会针对原生中的系统更新应用进行一个分析。
①这里我们跳过,只在厂商定制的应用中选择本地升级,然后进入设置警告界面SystemUpgradeChooserActivity.java(升级过程中,请不要移除电池,SIM卡和存储卡。。。。。。),然后我们通过点击确定并进入升级提醒界面(升级将在两到五分钟完成。。。。。。),部分代码如下:
case R.id.okButton:
//是否挂在SDCard
if (!hasSDCard()) {
showToast(R.string.no_sd_card);
return;
}
//更新包是否存在
if (file == null || !file.exists()) {
showToast(R.string.no_update_file);
return;
}
//进入升级提醒界面
Intent intent = new Intent(SystemUpgradeChooserActivity.this,UpdateToStart.class);
startActivity(intent);
break;
②此时我们通过intent进入到升级提醒界面UpdateToStart.java,这里我们点击确定开始安装更新包,详细代码如下:
buttonOkUp.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
//是否挂在SDCard
if (!hasSDCard()) {
showToast(R.string.no_sd_card);
return;
}
//更新文件是否存在
File file = new File(Environment.getExternalStorageDirectory() + "/dload/update.zip");
if (file == null || !file.exists()) {
showToast(R.string.no_update_file);
return;
}
//安装更新包,这是上层系统新的关键
RecoverySystem.installPackage(getApplicationContext(), file);
}
catch (Exception e) {
//
}
}
});
3.上面我们了解了,通过调用RecoverySystem.installPackage()方法尽心安装更新包。具体方法如下:
/**
*为了安装更新包将会重启设备,此外要调用该方法需要在清单文件中增加REBOOT权限
* 参数 context 上下文
* 参数 packageFile 封装更新包路径等信息的file对象.
*/
public static void installPackage(Context context, File packageFile)
throws IOException {
String filename = packageFile.getCanonicalPath();
Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
//参数拼接,该参数最终会写入BCB,也就是说设备重启进入Recovery模式后,Recovery.cpp将会解析该参数,并且根据参数做出操作,它将会被传入到bootcommand(context,arg)函数中去。
String arg = "--update_package=" + filename +
"\n--locale=" + Locale.getDefault().toString();
bootCommand(context, arg);
}
4.下面是对bootCommand进行分析。
private static void bootCommand(Context context, String arg) throws IOException {
RECOVERY_DIR.mkdirs();
// 创建/cache/recovery目录
COMMAND_FILE.delete();
// 删除command文件,进行初始化
LOG_FILE.delete();
//删除log文件,进行初始化
Log.d(TAG,"Preapre to write command: " + arg +"\n");
WriteByFdSync(COMMAND_FILE,arg);
//将参数写入到command文件中
Log.d(TAG,"Success to write command: " + arg +"\n");
Log.d(TAG,"Current build type is: " + Build.TYPE +"\n");
ReadInAndPrint(COMMAND_FILE);
//读取command文件,并打印log
// Having written the command file, go ahead and reboot
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
//从系统服务中获得PowerManager实例
pm.
reboot("recovery");
//重启
Log.d(TAG,"!!! pm.reboot failed !!!\n");
throw new IOException("Reboot failed (no permissions?)");
}
下面的方法调用流程为PowerManager:reboot("recovery")-->PowerManagerService:reboot(reason)
-->PowerManagerService:shtudownOrRebootInternal(false,confirm,reason,wait)
-->PowerManagerService:shutdownOrRebootInternal(...)
-->ShutdownThread:reboot(mContext, reason, confirm);
-->ShutdownThread:run():running()......
@Override // Binder call
public void
reboot(boolean confirm, String reason, boolean wait) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
Slog.i(TAG, "reboot call pid: " + Binder.getCallingPid() + " uid: " + Binder.getCallingUid());
final long ident = Binder.clearCallingIdentity();
try {
shutdownOrRebootInternal(false, confirm, reason, wait);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void
shutdownOrRebootInternal(final boolean shutdown, final boolean confirm,
final String reason, boolean wait) {
if (mHandler == null || !mSystemReady) {
throw new IllegalStateException("Too early to call shutdown() or reboot()");
}
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
if (shutdown) {
ShutdownThread.shutdown(mContext, confirm);
} else {
ShutdownThread.reboot(mContext, reason, confirm);
}
}
}
};
// ShutdownThread must run on a looper capable of displaying the UI.
Message msg = Message.obtain(mHandler, runnable);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
// PowerManager.reboot() is documented not to return so just wait for the inevitable.
if (wait) {
synchronized (runnable) {
while (true) {
try {
runnable.wait();
} catch (InterruptedException e) {
}
}
}
}
}
public static void
reboot(final Context context, String reason, boolean confirm) {
mReboot = true;
mRebootSafeMode = false;
mRebootReason = reason;
Log.d(TAG, "reboot");
if (mSpew) {
StackTraceElement[] stack = new Throwable().getStackTrace();
for (StackTraceElement element : stack)
{
Log.d(TAG, " |----" + element.toString());
}
}
shutdownInner(context, confirm);
}
这里在run方法下的,running方法中将重启原因写入系统属性文件中去。
。。。。。。
{
String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
}
。。。。。。
最后,重启设备。