CM android的CMUpdater分析(二)

简介: 版权声明:您好,转载请留下本人博客的地址,谢谢 https://blog.csdn.net/hongbochen1223/article/details/51159435 至于为何要在这里讲解android系统源码中的系统更新,我已经在上一篇《 CM android的CMUpdater分析(一)》中介绍了。
版权声明:您好,转载请留下本人博客的地址,谢谢 https://blog.csdn.net/hongbochen1223/article/details/51159435

至于为何要在这里讲解android系统源码中的系统更新,我已经在上一篇《 CM android的CMUpdater分析(一)》中介绍了。在上一篇中,主要讲解了在eclipse中如何搭建系统应用的开发环境,现在我们就使用eclipse来分析CMUpdater源码。该系统更新是CM修改原生android的基础上实现的。通过分析android系统的应用源码,可以学到一些很好的思想和编程方法学。好了,废话少说,现在就开始我们的学习之旅。

首先,在开始介绍之前,我先把之前根据CMUpdater源码分析来的框图放到上来,大家先看框图,把系统更新的整体流程大体了解一下。

这里写图片描述

通过这个框图,看上去挺复杂,其实是挺简单的,就是Service检查更新,后台下载更新,包括全安装包或者是增量更新包,下载完成之后,安装更新,重启,安装完成。

现在我们从代码开始,进行分析。

首先,我们先来看一个app项目中,所有的activity和service,就是要查看AndroidManifest.xml文件。通过分析该代码,我们看到,在CMUpdater中的入口Activity是UpdatesSettings,同时在
AndroidManifest.xml文件中,还静态注册了四个receiver,其中包括:

  • UpdateCheckReceiver : 在该receiver中注册了两个action,其中包括BOOT_COMPLETED和CONNECTIVITY_CHANGE,该receiver用于在系统启动后或者是网络连接发生变化的时候,进行检查系统更新。
  • DownloadReceiver : 该receiver也注册了两个action,包括DOWNLOAD_COMPLETE和START_DOWNLOAD,当下载完成和开始下载广播发出之后,进行相应的操作。
  • NotificationClickReceiver,该广播接收器注册了一个DOWNLOAD_NOTIFICATION_CLICKED广播,当接收到下载提醒框被点击的时候,进行更新操作。
  • CheckFinishReceiver : 该广播接收器用于接收UPDATE_CHECK_FINISHED广播,当检查更新完成之后,刷新列表。

同时,在该xml文件中,也注册了几个Service:

  • CMDashClockExtension : 该服务用于检查时间,用户可以选择某个时间点,进行检查更新。
  • UpdateCheckService : 该服务的操作就是用于检查更新,检查更新完成之后,发出检查更新完成的广播。
  • DownloadService : 顾名思义,下载服务,也就是说该服务是用来下载系统更新的。
  • DownloadCompleteIntentService : 下载完成服务,当下载完成之后,发出下载完成广播。

好了,现在我们先进入入口Activity来看一下。

进入到UpdateSettings.java文件,可以看到,该类中注册了一个Receiver,该接收器的实现为:


    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            //如果Action为ACTION_DOWNLOAD_STARTED,即为下载开始
            if (DownloadReceiver.ACTION_DOWNLOAD_STARTED.equals(action)) {
                mDownloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
                mUpdateHandler.post(mUpdateProgress);  //更新ProgressBar
                //如果Action为ACTION_CHECK_FINISHED,即为检查更新完成
            } else if (UpdateCheckService.ACTION_CHECK_FINISHED.equals(action)) {
                if (mProgressDialog != null) {
                    mProgressDialog.dismiss();
                    mProgressDialog = null;

                    //获取系统更新的条数
                    int count = intent.getIntExtra(UpdateCheckService.EXTRA_NEW_UPDATE_COUNT, -1);
                    if (count == 0) {  // 如果为0,提醒用户未找到
                        Toast.makeText(UpdatesSettings.this, R.string.no_updates_found,
                                Toast.LENGTH_SHORT).show();
                    } else if (count < 0) {  //如果小于0,则表示检查更新失败
                        Toast.makeText(UpdatesSettings.this, R.string.update_check_failed,
                                Toast.LENGTH_LONG).show();
                    }
                }
                //如果其他都不是,表示更新完成,并且有更新,则刷新UI
                updateLayout();
            }
        }
    };

当用户点击item(MENU_REFRESH)之后,手动进行更新检查,即为checkForUpdates()


    private void checkForUpdates() {
        if (mProgressDialog != null) {
            return;
        }

        // 未连接网络,提示用户并返回
        if (!Utils.isOnline(this)) {
            Toast.makeText(this, R.string.data_connection_required, Toast.LENGTH_SHORT).show();
            return;
        }

        //弹出进度条,用户提示用户正在检查更新
        mProgressDialog = new ProgressDialog(this);
        mProgressDialog.setTitle(R.string.checking_for_updates);
        mProgressDialog.setMessage(getString(R.string.checking_for_updates));
        mProgressDialog.setIndeterminate(true);
        mProgressDialog.setCancelable(true);
        mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                //当用户点击取消后,提醒服务程序,取消检查更新操作
                Intent cancelIntent = new Intent(UpdatesSettings.this, UpdateCheckService.class);
                cancelIntent.setAction(UpdateCheckService.ACTION_CANCEL_CHECK);
                startService(cancelIntent);
                mProgressDialog = null;
            }
        });

        //开启服务程序进行检查更新
        Intent checkIntent = new Intent(UpdatesSettings.this, UpdateCheckService.class);
        checkIntent.setAction(UpdateCheckService.ACTION_CHECK);
        startService(checkIntent);

        mProgressDialog.show();
    }

现在我们进入检查更新服务程序,即UpdateCheckService。在服务程序的启动方法onStartCommand()检查用户的操作是取消检查还是检查更新,如果是取消检查,就将位于请求列表中的请求删除掉。


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //取消检查更新
        if (TextUtils.equals(intent.getAction(), ACTION_CANCEL_CHECK)) {
            ((UpdateApplication) getApplicationContext()).getQueue().cancelAll(TAG);
            return START_NOT_STICKY;
        }

        return super.onStartCommand(intent, flags, startId);
    }

接着,服务程序调用getAvailableUpdates()来检查可用更新,我们来看一下这个方法:

   private void getAvailableUpdates() {
        // Get the type of update we should check for
        int updateType = Utils.getUpdateType();

        // Get the actual ROM Update Server URL
        URI updateServerUri = getServerURI();

Log.e(TAG, updateServerUri.toString());

        UpdatesJsonObjectRequest request;

        try {
            request = new UpdatesJsonObjectRequest(updateServerUri.toASCIIString(),
                    Utils.getUserAgentString(this), buildUpdateRequest(updateType), this, this);
            // Set the tag for the request, reuse logging tag
            request.setTag(TAG);
        } catch (JSONException e) {
            Log.e(TAG, "Could not build request", e);
            return;
        }

        ((UpdateApplication) getApplicationContext()).getQueue().add(request);
    }

这个代码很好理解,就是封装一下请求信息,封装为请求对象,将该请求对象添加到请求队列中,等待从服务器中获取信息。请求对象的封装是通过buildUpdateRequest()方法来实现的。

接着,服务程序通过回调函数onResponse()来获取相应的服务器反馈信息。


    @Override
    public void onResponse(JSONObject jsonObject) {
        int updateType = Utils.getUpdateType();

        LinkedList<UpdateInfo> lastUpdates = State.loadState(this);
        LinkedList<UpdateInfo> updates = parseJSON(jsonObject.toString(), updateType);

        int newUpdates = 0, realUpdates = 0;
        for (UpdateInfo ui : updates) {
            if (!lastUpdates.contains(ui)) {
                newUpdates++;
            }
            if (ui.isNewerThanInstalled()) {
                realUpdates++;
            }
        }

        Intent intent = new Intent(ACTION_CHECK_FINISHED);
        intent.putExtra(EXTRA_UPDATE_COUNT, updates.size());
        intent.putExtra(EXTRA_REAL_UPDATE_COUNT, realUpdates);
        intent.putExtra(EXTRA_NEW_UPDATE_COUNT, newUpdates);

        recordAvailableUpdates(updates, intent);
        State.saveState(this, updates);
    }

在该回调函数中,将更新信息放到更新列表中,同时通过调用recordAvailableUpdates()发送actionACTION_CHECK_FINISHED的广播给UpdateSettings中。接着UpdateSettings调用updateLayout()更新UI,同时调用refreshPreferences()接着调用回调函数onStartDownload()来通知DownloadReceiver进行下载。

我们先来看一下recordAvailableUpdates()函数的实现:


    private void recordAvailableUpdates(LinkedList<UpdateInfo> availableUpdates,
            Intent finishedIntent) {

        if (availableUpdates == null) {
            sendBroadcast(finishedIntent);
            return;
        }

        //保存上次更新时间,然后确保启动检查更新完成是正确的
        Date d = new Date();
        PreferenceManager.getDefaultSharedPreferences(UpdateCheckService.this).edit()
                .putLong(Constants.LAST_UPDATE_CHECK_PREF, d.getTime())
                .putBoolean(Constants.BOOT_CHECK_COMPLETED, true)
                .apply();

        int realUpdateCount = finishedIntent.getIntExtra(EXTRA_REAL_UPDATE_COUNT, 0);
        UpdateApplication app = (UpdateApplication) getApplicationContext();

        // Write to log
        Log.i(TAG, "The update check successfully completed at " + d + " and found "
                + availableUpdates.size() + " updates ("
                + realUpdateCount + " newer than installed)");

        //如果程序被关掉了,则在提醒框中显示更新信息
        if (realUpdateCount != 0 && !app.isMainActivityActive()) {
            // There are updates available
            // The notification should launch the main app
            Intent i = new Intent(this, UpdatesSettings.class);
            i.putExtra(UpdatesSettings.EXTRA_UPDATE_LIST_UPDATED, true);
            PendingIntent contentIntent = PendingIntent.getActivity(this, 0, i,
                    PendingIntent.FLAG_ONE_SHOT);

            Resources res = getResources();
            String text = res.getQuantityString(R.plurals.not_new_updates_found_body,
                    realUpdateCount, realUpdateCount);

            // Get the notification ready
            Notification.Builder builder = new Notification.Builder(this)
                    .setSmallIcon(R.drawable.cm_updater)
                    .setWhen(System.currentTimeMillis())
                    .setTicker(res.getString(R.string.not_new_updates_found_ticker))
                    .setContentTitle(res.getString(R.string.not_new_updates_found_title))
                    .setContentText(text)
                    .setContentIntent(contentIntent)
                    .setAutoCancel(true);

            LinkedList<UpdateInfo> realUpdates = new LinkedList<UpdateInfo>();
            for (UpdateInfo ui : availableUpdates) {
                if (ui.isNewerThanInstalled()) {
                    realUpdates.add(ui);
                }
            }

            Collections.sort(realUpdates, new Comparator<UpdateInfo>() {
                @Override
                public int compare(UpdateInfo lhs, UpdateInfo rhs) {
                    /* sort by date descending */
                    long lhsDate = lhs.getDate();
                    long rhsDate = rhs.getDate();
                    if (lhsDate == rhsDate) {
                        return 0;
                    }
                    return lhsDate < rhsDate ? 1 : -1;
                }
            });

            Notification.InboxStyle inbox = new Notification.InboxStyle(builder)
                    .setBigContentTitle(text);
            int added = 0, count = realUpdates.size();

            for (UpdateInfo ui : realUpdates) {
                if (added < EXPANDED_NOTIF_UPDATE_COUNT) {
                    inbox.addLine(ui.getName());
                    added++;
                }
            }
            if (added != count) {
                inbox.setSummaryText(res.getQuantityString(R.plurals.not_additional_count,
                        count - added, count - added));
            }
            builder.setStyle(inbox);
            builder.setNumber(availableUpdates.size());

            //如果更新条数为1 ,则直接发送开始下载更新的广播
            if (count == 1) {
                i = new Intent(this, DownloadReceiver.class);
                i.setAction(DownloadReceiver.ACTION_START_DOWNLOAD);
                i.putExtra(DownloadReceiver.EXTRA_UPDATE_INFO, (Parcelable) realUpdates.getFirst());
                PendingIntent downloadIntent = PendingIntent.getBroadcast(this, 0, i,
                        PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);

                builder.addAction(R.drawable.ic_tab_download,
                        res.getString(R.string.not_action_download), downloadIntent);
            }

            // Trigger the notification
            NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            nm.notify(R.string.not_new_updates_found_title, builder.build());
        }

        //发送广播,提醒UpdateSettings类
        sendBroadcast(finishedIntent);
    }

updateLayout()函数中会更新UI,用户可以点击下载按钮进行更新下载。

当下载开始之后,会广播给`DownloadReceiver’接收器。


     @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (ACTION_START_DOWNLOAD.equals(action)) {
            UpdateInfo ui = (UpdateInfo) intent.getParcelableExtra(EXTRA_UPDATE_INFO);
            handleStartDownload(context, ui);
        } else if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
            long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
            handleDownloadComplete(context, id);
        } else if (ACTION_INSTALL_UPDATE.equals(action)) {
            StatusBarManager sb = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
            sb.collapsePanels();
            String fileName = intent.getStringExtra(EXTRA_FILENAME);
            try {
                Utils.triggerUpdate(context, fileName);
            } catch (IOException e) {
                Log.e(TAG, "Unable to reboot into recovery mode", e);
                Toast.makeText(context, R.string.apply_unable_to_reboot_toast,
                            Toast.LENGTH_SHORT).show();
                Utils.cancelNotification(context);
            }
        }
    }

在该接收器中,如果action为ACTION_START_DOWNLOAD,则进行handleStartDownload()操作。


    private void handleStartDownload(Context context, UpdateInfo ui) {
        DownloadService.start(context, ui);
    }

启动DownloadService开始操作。


    public static void start(Context context, UpdateInfo ui) {
        Intent intent = new Intent(context, DownloadService.class);
        intent.putExtra(EXTRA_UPDATE_INFO, (Parcelable) ui);
        context.startService(intent);
    }

静态方法,启动DownloadService服务。


    @Override
    protected void onHandleIntent(Intent intent) {
        mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        mInfo = intent.getParcelableExtra(EXTRA_UPDATE_INFO);

        if (mInfo == null) {
            Log.e(TAG, "Intent UpdateInfo extras were null");
            return;
        }

        try {
            getIncremental();
        } catch (IOException e) {
            downloadFullZip();
        }
    }

尝试下载增量更新包,如果增量更新包报出异常,则下载全更新包。


    private void downloadFullZip() {
        Log.v(TAG, "Downloading full zip");

        // Build the name of the file to download, adding .partial at the end.  It will get
        // stripped off when the download completes
        String fullFilePath = "file://" + getUpdateDirectory().getAbsolutePath() +
                "/" + mInfo.getFileName() + ".partial";

        long downloadId = enqueueDownload(mInfo.getDownloadUrl(), fullFilePath);

        // Store in shared preferences
        mPrefs.edit()
                .putLong(Constants.DOWNLOAD_ID, downloadId)
                .putString(Constants.DOWNLOAD_MD5, mInfo.getMD5Sum())
                .apply();

        Utils.cancelNotification(this);

        Intent intent = new Intent(DownloadReceiver.ACTION_DOWNLOAD_STARTED);
        intent.putExtra(DownloadManager.EXTRA_DOWNLOAD_ID, downloadId);
        sendBroadcast(intent);
    }

调用enqueueDownload()函数来获取该下载在下载队列中的id:


    private long enqueueDownload(String downloadUrl, String localFilePath) {
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUrl));
        String userAgent = Utils.getUserAgentString(this);
        if (userAgent != null) {
            request.addRequestHeader("User-Agent", userAgent);
        }
        request.setTitle(getString(R.string.app_name));
        request.setDestinationUri(Uri.parse(localFilePath));
        request.setAllowedOverRoaming(false);
        request.setVisibleInDownloadsUi(false);

        // TODO: this could/should be made configurable
        request.setAllowedOverMetered(true);

        final DownloadManager dm = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
        return dm.enqueue(request);
    }

该操作相当于封装请求服务器的信息,将该信息放到DownloadManager中,获取id。

接着,向DownloadReceiver发送ACTION_DOWNLOAD_STARTED广播。

当下载完成之后,会向DownloadReceiver发送广播ACTION_DOWNLOAD_COMPLET,当接收器接到该广播之后,则调用handleDownloadComplete(context, id)函数来处理下载完成的安装包。


    private void handleDownloadComplete(Context context, long id) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        long enqueued = prefs.getLong(Constants.DOWNLOAD_ID, -1);
        if (enqueued < 0 || id < 0 || id != enqueued) {
            return;
        }

        String downloadedMD5 = prefs.getString(Constants.DOWNLOAD_MD5, "");
        String incrementalFor = prefs.getString(Constants.DOWNLOAD_INCREMENTAL_FOR, null);

        // Send off to DownloadCompleteIntentService
        Intent intent = new Intent(context, DownloadCompleteIntentService.class);
        intent.putExtra(Constants.DOWNLOAD_ID, id);
        intent.putExtra(Constants.DOWNLOAD_MD5, downloadedMD5);
        intent.putExtra(Constants.DOWNLOAD_INCREMENTAL_FOR, incrementalFor);
        context.startService(intent);

        // Clear the shared prefs
        prefs.edit()
                .remove(Constants.DOWNLOAD_MD5)
                .remove(Constants.DOWNLOAD_ID)
                .remove(Constants.DOWNLOAD_INCREMENTAL_FOR)
                .apply();
    }

通过md5检查完整性,接着启动DownloadCompleteIntentService服务来安装更新。


    @Override
    protected void onHandleIntent(Intent intent) {
        if (!intent.hasExtra(Constants.DOWNLOAD_ID) ||
                !intent.hasExtra(Constants.DOWNLOAD_MD5)) {
            return;
        }

        long id = intent.getLongExtra(Constants.DOWNLOAD_ID, -1);
        String downloadedMD5 = intent.getStringExtra(Constants.DOWNLOAD_MD5);
        String incrementalFor = intent.getStringExtra(Constants.DOWNLOAD_INCREMENTAL_FOR);

        Intent updateIntent = new Intent(this, UpdatesSettings.class);
        updateIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);

        int status = fetchDownloadStatus(id);
        if (status == DownloadManager.STATUS_SUCCESSFUL) {
            // Get the full path name of the downloaded file and the MD5

            // Strip off the .partial at the end to get the completed file
            String partialFileFullPath = fetchDownloadPartialPath(id);

            if (partialFileFullPath == null) {
                displayErrorResult(updateIntent, R.string.unable_to_download_file);
            }

            String completedFileFullPath = partialFileFullPath.replace(".partial", "");

            File partialFile = new File(partialFileFullPath);
            File updateFile = new File(completedFileFullPath);
            partialFile.renameTo(updateFile);

            // Start the MD5 check of the downloaded file
            if (MD5.checkMD5(downloadedMD5, updateFile)) {
                // We passed. Bring the main app to the foreground and trigger download completed
                updateIntent.putExtra(UpdatesSettings.EXTRA_FINISHED_DOWNLOAD_ID, id);
                updateIntent.putExtra(UpdatesSettings.EXTRA_FINISHED_DOWNLOAD_PATH,
                        completedFileFullPath);
                updateIntent.putExtra(UpdatesSettings.EXTRA_FINISHED_DOWNLOAD_INCREMENTAL_FOR,
                        incrementalFor);
                displaySuccessResult(updateIntent, updateFile);
            } else {
                // We failed. Clear the file and reset everything
                mDm.remove(id);

                if (updateFile.exists()) {
                    updateFile.delete();
                }
                displayErrorResult(updateIntent, R.string.md5_verification_failed);
            }
        } else if (status == DownloadManager.STATUS_FAILED) {
            // The download failed, reset
            mDm.remove(id);
            displayErrorResult(updateIntent, R.string.unable_to_download_file);
        }
    }

该服务通过封装Intent来返回UpdateSettings中的onNewIntent()函数来进行处理。


    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        // Check if we need to refresh the screen to show new updates
        if (intent.getBooleanExtra(EXTRA_UPDATE_LIST_UPDATED, false)) {
            updateLayout();
        }

        //检查下载完成
        checkForDownloadCompleted(intent);
    }

我们来看一下checkForDownloadCompleted()函数的实现:


    private void checkForDownloadCompleted(Intent intent) {
        if (intent == null) {
            return;
        }

        long downloadId = intent.getLongExtra(EXTRA_FINISHED_DOWNLOAD_ID, -1);
        if (downloadId < 0) {
            return;
        }

        String fullPathName = intent.getStringExtra(EXTRA_FINISHED_DOWNLOAD_PATH);
        if (fullPathName == null) {
            return;
        }

        String fileName = new File(fullPathName).getName();

        // If this is an incremental, find matching target and mark it as downloaded.
        String incrementalFor = intent.getStringExtra(EXTRA_FINISHED_DOWNLOAD_INCREMENTAL_FOR);
        if (incrementalFor != null) {
            UpdatePreference pref = (UpdatePreference) mUpdatesList.findPreference(incrementalFor);
            if (pref != null) {
                pref.setStyle(UpdatePreference.STYLE_DOWNLOADED);
                pref.getUpdateInfo().setFileName(fileName);
                //调用更新安装
                onStartUpdate(pref);
            }
        } else {
            // Find the matching preference so we can retrieve the UpdateInfo
            UpdatePreference pref = (UpdatePreference) mUpdatesList.findPreference(fileName);
            if (pref != null) {
                pref.setStyle(UpdatePreference.STYLE_DOWNLOADED);
                onStartUpdate(pref);
            }
        }

        resetDownloadState();
    }

该函数通过调用onStartUpdate(pref)来安装更新:


    @Override
    public void onStartUpdate(UpdatePreference pref) {
        final UpdateInfo updateInfo = pref.getUpdateInfo();

        // Prevent the dialog from being triggered more than once
        if (mStartUpdateVisible) {
            return;
        }

        mStartUpdateVisible = true;

        // Get the message body right
        String dialogBody = getString(R.string.apply_update_dialog_text, updateInfo.getName());

        // Display the dialog
        new AlertDialog.Builder(this)
                .setTitle(R.string.apply_update_dialog_title)
                .setMessage(dialogBody)
                .setPositiveButton(R.string.dialog_update, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        try {
                            //提醒用户是否安装,用户点击确定之后,triggerUpdate()
                            Utils.triggerUpdate(UpdatesSettings.this, updateInfo.getFileName());
                        } catch (IOException e) {
                            Log.e(TAG, "Unable to reboot into recovery mode", e);
                            Toast.makeText(UpdatesSettings.this, R.string.apply_unable_to_reboot_toast,
                                    Toast.LENGTH_SHORT).show();
                        }
                    }
                })
                .setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        mStartUpdateVisible = false;
                    }
                })
                .show();
    }

提醒用户是否安装,用户点击安装之后,激发triggerUpdate()函数进行安装:


    public static void triggerUpdate(Context context, String updateFileName) throws IOException {
        /*
         * Should perform the following steps.
         * 1.- mkdir -p /cache/recovery
         * 2.- echo 'boot-recovery' > /cache/recovery/command
         * 3.- if(mBackup) echo '--nandroid'  >> /cache/recovery/command
         * 4.- echo '--update_package=SDCARD:update.zip' >> /cache/recovery/command
         * 5.- reboot recovery
         */

        // Set the 'boot recovery' command
        Process p = Runtime.getRuntime().exec("sh");
        OutputStream os = p.getOutputStream();
        os.write("mkdir -p /cache/recovery/\n".getBytes());
        os.write("echo 'boot-recovery' >/cache/recovery/command\n".getBytes());

        // See if backups are enabled and add the nandroid flag
        /* TODO: add this back once we have a way of doing backups that is not recovery specific
           if (mPrefs.getBoolean(Constants.BACKUP_PREF, true)) {
           os.write("echo '--nandroid'  >> /cache/recovery/command\n".getBytes());
           }
           */

        // Add the update folder/file name
        // Emulated external storage moved to user-specific paths in 4.2
        String userPath = Environment.isExternalStorageEmulated() ? ("/" + UserHandle.myUserId()) : "";

        String cmd = "echo '--update_package=" + getStorageMountpoint(context) + userPath
            + "/" + Constants.UPDATES_FOLDER + "/" + updateFileName
            + "' >> /cache/recovery/command\n";
        os.write(cmd.getBytes());
        os.flush();

        // Trigger the reboot
        PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        powerManager.reboot("recovery");
    }

/*
* Should perform the following steps.
* 1.- mkdir -p /cache/recovery
* 2.- echo ‘boot-recovery’ > /cache/recovery/command
* 3.- if(mBackup) echo ‘–nandroid’ >> /cache/recovery/command
* 4.- echo ‘–update_package=SDCARD:update.zip’ >> /cache/recovery/command
* 5.- reboot recovery
*/

在注释中,说明了必须运行下面步骤:

  1. mkdir -p /cache/recovery //创建文件夹
  2. echo ‘boot-recovery’ > /cache/recovery/command
  3. if(mBackup) echo ‘–nandroid’ >> /cache/recovery/command
  4. echo ‘–update_package=SDCARD:update.zip’ >> /cache/recovery/command
  5. reboot recovery

这五个命令运行完成之后,便会重启安装系统更新了。

目录
相关文章
|
5天前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
2月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统无疑是主角。它们各自拥有独特的特点和优势,为开发者提供了不同的开发环境和工具。本文将深入浅出地探讨安卓和iOS开发环境的主要差异,包括开发工具、编程语言、用户界面设计、性能优化以及市场覆盖等方面,旨在帮助初学者更好地理解两大平台的开发特点,并为他们选择合适的开发路径提供参考。通过比较分析,我们将揭示不同环境下的开发实践,以及如何根据项目需求和目标受众来选择最合适的开发平台。
45 2
|
13天前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
39 15
Android 系统缓存扫描与清理方法分析
|
28天前
|
存储 Linux Android开发
Android底层:通熟易懂分析binder:1.binder准备工作
本文详细介绍了Android Binder机制的准备工作,包括打开Binder驱动、内存映射(mmap)、启动Binder主线程等内容。通过分析系统调用和进程与驱动层的通信,解释了Binder如何实现进程间通信。文章还探讨了Binder主线程的启动流程及其在进程通信中的作用,最后总结了Binder准备工作的调用时机和重要性。
Android底层:通熟易懂分析binder:1.binder准备工作
|
2月前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
124 3
|
27天前
|
开发工具 Android开发 Swift
安卓与iOS开发环境的差异性分析
【10月更文挑战第8天】 本文旨在探讨Android和iOS两大移动操作系统在开发环境上的不同,包括开发语言、工具、平台特性等方面。通过对这些差异性的分析,帮助开发者更好地理解两大平台,以便在项目开发中做出更合适的技术选择。
|
2月前
|
安全 Linux Android开发
探索安卓与iOS的安全性差异:技术深度分析
本文深入探讨了安卓(Android)和iOS两个主流操作系统平台在安全性方面的不同之处。通过比较它们在架构设计、系统更新机制、应用程序生态和隐私保护策略等方面的差异,揭示了每个平台独特的安全优势及潜在风险。此外,文章还讨论了用户在使用这些设备时可以采取的一些最佳实践,以增强个人数据的安全。
|
3月前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
【8月更文挑战第20天】在移动应用开发的广阔天地中,Android和iOS两大平台各自占据着重要的位置。本文将深入探讨这两种操作系统的开发环境,从编程语言到开发工具,从用户界面设计到性能优化,以及市场趋势对开发者选择的影响。我们旨在为读者提供一个全面的比较视角,帮助理解不同平台的优势与挑战,并为那些站在选择十字路口的开发者提供有价值的参考信息。
|
2月前
|
IDE 开发工具 Android开发
安卓与iOS开发环境对比分析
本文将探讨安卓和iOS这两大移动操作系统在开发环境上的差异,从工具、语言、框架到生态系统等多个角度进行比较。我们将深入了解各自的优势和劣势,并尝试为开发者提供一些实用的建议,以帮助他们根据自己的需求选择最适合的开发平台。
44 1
|
3月前
|
开发框架 Android开发 Swift
安卓与iOS应用开发对比分析
【8月更文挑战第20天】在移动应用开发的广阔天地中,安卓和iOS两大平台各占半壁江山。本文将深入探讨这两大操作系统在开发环境、编程语言、用户界面设计、性能优化及市场分布等方面的差异和特点。通过比较分析,旨在为开发者提供一个宏观的视角,帮助他们根据项目需求和目标受众选择最合适的开发平台。同时,文章还将讨论跨平台开发框架的利与弊,以及它们如何影响着移动应用的开发趋势。