zoukankan      html  css  js  c++  java
  • Android恢复出厂设置流程分析【Android源码解析十】

          最近看恢复出厂的一个问题,以前也查过这方面的流程,所以这里整理一些AP+framework层的流程;

          在setting-->备份与重置--->恢复出厂设置--->重置手机--->清除全部内容--->手机关机--->开机--->进行恢复出厂的操作--->开机流程;

     

          Step 1:前面找settings中的布局我就省略了,这部分相对简单一些,直接到清除全部内容这个按钮的操作,

        对应的java类是setting中的MasterClearConfirm.java这个类,

    private Button.OnClickListener mFinalClickListener = new Button.OnClickListener() {
    
            public void onClick(View v) {
                if (Utils.isMonkeyRunning()) {
                    return;
                }
    
                if (mEraseSdCard) {
                    Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET);
                    intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME);
                    getActivity().startService(intent);
                } else {
                    getActivity().sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
                    // Intent handling is asynchronous -- assume it will happen soon.
                }
            }
        };

    通过上述的代码,可以看出,实际上点击清除全部内容的时候,如果前面勾选上格式哈SD卡,就会执行mEraseSdCard为true里面的逻辑,如果没有勾选,就执行mEraseSdCard=false的逻辑,其实就是发送一个广播,

    “android.intent.action.MASTER_CLEAR”

           Step 2:这个广播接受的地方,参见AndroidManifest.xml中的代码,如下:

    <receiver android:name="com.android.server.MasterClearReceiver"
                android:permission="android.permission.MASTER_CLEAR"
                android:priority="100" >
                <intent-filter>
                    <!-- For Checkin, Settings, etc.: action=MASTER_CLEAR -->
                    <action android:name="android.intent.action.MASTER_CLEAR" />
    
                    <!-- MCS always uses REMOTE_INTENT: category=MASTER_CLEAR -->
                    <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                    <category android:name="android.intent.category.MASTER_CLEAR" />
                </intent-filter>
            </receiver>

    找这个MasterClearReceiver.java这个receiver,下面来看看这个onReceiver()里面做了什么操作:

    public void onReceive(final Context context, final Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
                if (!"google.com".equals(intent.getStringExtra("from"))) {
                    Slog.w(TAG, "Ignoring master clear request -- not from trusted server.");
                    return;
                }
            }
    
            Slog.w(TAG, "!!! FACTORY RESET !!!");
            // The reboot call is blocking, so we need to do it on another thread.
            Thread thr = new Thread("Reboot") {
                @Override
                public void run() {
                    try {
                        RecoverySystem.rebootWipeUserData(context);
                        Log.wtf(TAG, "Still running after master clear?!");
                    } catch (IOException e) {
                        Slog.e(TAG, "Can't perform master clear/factory reset", e);
                    }
                }
            };
            thr.start();
        }

    这个里面主要的操作是:RecoverySystem.rebootWipeUserData(context);准备做重启的动作,告诉手机要清除userData的数据;

          Step 3:接着来看看RecoverySystem.rebootWipeUserData()这个方法做了哪些操作:

    public static void rebootWipeUserData(Context context) throws IOException {
            final ConditionVariable condition = new ConditionVariable();
    
            Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
            context.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
                    android.Manifest.permission.MASTER_CLEAR,
                    new BroadcastReceiver() {
                        @Override
                        public void onReceive(Context context, Intent intent) {
                            condition.open();
                        }
                    }, null, 0, null, null);
    
            // Block until the ordered broadcast has completed.
            condition.block();
    
            bootCommand(context, "--wipe_data
    --locale=" + Locale.getDefault().toString());
        }

    这个里面的广播可以先忽略不计,重点来看看bootCommand()这个方法,注意这个参数“--wipe_data --locale=”

    private static void bootCommand(Context context, String arg) throws IOException {
            RECOVERY_DIR.mkdirs();  // In case we need it
            COMMAND_FILE.delete();  // In case it's not writable
            LOG_FILE.delete();
    
            FileWriter command = new FileWriter(COMMAND_FILE);
            try {
                command.write(arg);
                command.write("
    ");
            } finally {
                command.close();
            }
    
            // Having written the command file, go ahead and reboot
            PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
            pm.reboot("recovery");
    
            throw new IOException("Reboot failed (no permissions?)");
        }

    这个方法的操作大致是“写节点/cache/recovery/command”,把传递过来的字符串写进去;然后调用PowerManager进行重启操作,reboot();

     

        Step 4:接着我们来看看PowerManager的reboot方法做了哪些操作:

      public void reboot(String reason) {
            try {
                mService.reboot(false, reason, true);
            } catch (RemoteException e) {
            }
        }

    这个调用到了PowerManagerService.java这个类的reboot方法中了:

    @Override // Binder call
        public void reboot(boolean confirm, String reason, boolean wait) {
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
    
            final long ident = Binder.clearCallingIdentity();
            try {
                shutdownOrRebootInternal(false, confirm, reason, wait);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

    重点来看看shutdownOrRebootInternal()这个方法,

    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) {
                        }
                    }
                }
            }
        }

    由于传递过来的shutdown为false,所以执行ShutdownThread.reboot(mContext, reason, confirm);reason:recevory

    下面调用到ShutdownThread

        Step 5:这个追踪ShutdownThread.reboot()这个方法,这就有点像破案电影,一点一点查找罪犯的难点;

    来窥视一下这个类:

     public static void reboot(final Context context, String reason, boolean confirm) {
            mReboot = true;
            mRebootSafeMode = false;
            mRebootReason = reason;
            Log.d(TAG, "reboot");
            shutdownInner(context, confirm);
        }

    这个里面做的操作就是给这个变量mRebootReason复制“recevory”,重点调用shutdownInner()这个方法;

    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;
                }
            }
    
            Log.d(TAG, "Notifying thread to start radio shutdown");
            bConfirmForAnimation = confirm;
            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);
                if (sConfirmDialog != null) {
                    sConfirmDialog.dismiss();
                }
                if (sConfirmDialog == null) {
                    Log.d(TAG, "PowerOff dialog doesn't exist. Create it first");
                    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);
                                if (sConfirmDialog != null) {
                                sConfirmDialog = null;
                                }
                                }
                                })
                    .setNegativeButton(com.android.internal.R.string.no, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                            synchronized (sIsStartedGuard) {
                            sIsStarted = false;
                            }
                            if (sConfirmDialog != null) {
                            sConfirmDialog = null;
                            }
                            }
                            })
                    .create();
                    sConfirmDialog.setCancelable(false);//blocking back key
                    sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
                    /*if (!context.getResources().getBoolean(
                      com.android.internal.R.bool.config_sf_slowBlur)) {
                      sConfirmDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
                      }*/
                    /* To fix video+UI+blur flick issue */
                    sConfirmDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
                }
    
                closer.dialog = sConfirmDialog;
                sConfirmDialog.setOnDismissListener(closer);
    
                if (!sConfirmDialog.isShowing()) {
                    sConfirmDialog.show();
                }
            } else {
                beginShutdownSequence(context);
            }
        }

    看beginShutdownSequence()这个方法吧,重点调用到这个方法里面去了,来瞅瞅这个方法:

    private static void beginShutdownSequence(Context context) {
            synchronized (sIsStartedGuard) {
                if (sIsStarted) {
                    Log.e(TAG, "ShutdownThread is already running, returning.");		
                    return;
                }
                sIsStarted = true;
            }
    
            // start the thread that initiates shutdown
            sInstance.mContext = context;
            sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
            sInstance.mHandler = new Handler() {
            };    
    
            bPlayaudio = true;
            if (!bConfirmForAnimation) {
                if (!sInstance.mPowerManager.isScreenOn()) {
                    bPlayaudio = false;
                }
            }
    
            // throw up an indeterminate system dialog to indicate radio is
            // shutting down.
            beginAnimationTime = 0;
            boolean mShutOffAnimation = false;
    
            try {
                if (mIBootAnim == null) {
                    mIBootAnim = MediatekClassFactory.createInstance(IBootAnimExt.class);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            int screenTurnOffTime = mIBootAnim.getScreenTurnOffTime();
            mShutOffAnimation = mIBootAnim.isCustBootAnim();
            Log.e(TAG, "mIBootAnim get screenTurnOffTime : " + screenTurnOffTime);
    
            String cust = SystemProperties.get("ro.operator.optr");
    
            if (cust != null) {
                if (cust.equals("CUST")) {
                    mShutOffAnimation = true;
                }
            }
    
            synchronized (mEnableAnimatingSync) {
    
                if(!mEnableAnimating) {
    //                sInstance.mPowerManager.setBacklightBrightness(PowerManager.BRIGHTNESS_DIM);
                } else {
                    if (mShutOffAnimation) {
                        Log.e(TAG, "mIBootAnim.isCustBootAnim() is true");
                        bootanimCust();
                    } else {
                        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);
                        /* To fix video+UI+blur flick issue */
                        pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
                        pd.show();
                    }
                    sInstance.mHandler.postDelayed(mDelayDim, screenTurnOffTime ); 
                }
            }
    
            // make sure we never fall asleep again
            sInstance.mCpuWakeLock = null;
            try {
                sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
                        。。。 。。。
    }


    这段代码有句话会影响关机动画播放不完

    “sInstance.mHandler.postDelayed(mDelayDim, screenTurnOffTime ); ”

     

    解决办法

        (1)“可以把这个screenTurnOffTime时间乘以2,这个时间看log是5000毫秒,就是5秒,乘以2就是10秒,大概就能播放完全关机动画了。”

        (2)把这句话注释掉,但是有可能会引起问题,导致恢复出厂设置的时候没有进行恢复出厂的操作。目前正在追踪此问题;

     

    这段代码中还有影响关机动画是否走客制化的关机动画,如果ro.operator.optr这个属性配置的是CUST,则会走客制化的关机动画,否则走系统默认的关机动画;

    String cust = SystemProperties.get("ro.operator.optr");
    
    
            if (cust != null) {
                if (cust.equals("CUST")) {
                    mShutOffAnimation = true;
                }
            }


    然后重点看 sInstance.start();这个方法,就走到了run()方法里满了;

     

        Step 6: 来看看ShutDownThread.java这个类的run()方法;

    public void run() {
            checkShutdownFlow();
            while (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
                stMgr.saveStates(mContext);
                stMgr.enterShutdown(mContext);
                running();
            } 
            if (mShutdownFlow != IPO_SHUTDOWN_FLOW) {
                stMgr.enterShutdown(mContext);
                running();
            }
        }

    重点看running()这个方法:

    下面这个方法比较长,来分析一下:

     public void running() {
            if(sPreShutdownApi != null){
                try {
                    sPreShutdownApi.onPowerOff();
                } catch (RemoteException e) {
                    Log.e(TAG, "onPowerOff exception" + e.getMessage());
                }
            }else{
                Log.w(TAG, "sPreShutdownApi is null");
            }
    
            command = SystemProperties.get("sys.ipo.pwrdncap");
    
            BroadcastReceiver br = new BroadcastReceiver() {
                @Override public void onReceive(Context context, Intent intent) {
                    // We don't allow apps to cancel this, so ignore the result.
                    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;
            /// M: 2012-05-20 ALPS00286063 @{
            mContext.sendBroadcast(new Intent("android.intent.action.ACTION_PRE_SHUTDOWN"));
            /// @} 2012-05-20
            mContext.sendOrderedBroadcastAsUser((new Intent()).setAction(Intent.ACTION_SHUTDOWN).putExtra("_mode", mShutdownFlow),
                    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 ACTION_SHUTDOWN timed out");
                        if (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
                            Log.d(TAG, "change shutdown flow from ipo to normal: ACTION_SHUTDOWN timeout");
                            mShutdownFlow = NORMAL_SHUTDOWN_FLOW;
                        }
                        break;
                    }
                    try {
                        mActionDoneSync.wait(delay);
                    } catch (InterruptedException e) {
                    }
                }
            }
    
            // Also send ACTION_SHUTDOWN_IPO in IPO shut down flow
            if (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
                mActionDone = false;
                mContext.sendOrderedBroadcast(new Intent("android.intent.action.ACTION_SHUTDOWN_IPO"), null,
                        br, mHandler, 0, null, null);
                final long endTimeIPO = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
                synchronized (mActionDoneSync) {
                    while (!mActionDone) {
                        long delay = endTimeIPO - SystemClock.elapsedRealtime();
                        if (delay <= 0) {
                            Log.w(TAG, "Shutdown broadcast ACTION_SHUTDOWN_IPO timed out");
                            if (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
                                Log.d(TAG, "change shutdown flow from ipo to normal: ACTION_SHUTDOWN_IPO timeout");
                                mShutdownFlow = NORMAL_SHUTDOWN_FLOW;
                            }
                            break;
                        }
                        try {
                            mActionDoneSync.wait(delay);
                        } catch (InterruptedException e) {
                        }
                    }
                }
            }
    
            if (mShutdownFlow != IPO_SHUTDOWN_FLOW) {
                // power off auto test, don't modify
                Log.i(TAG, "Shutting down activity manager...");
    
                final IActivityManager am =
                    ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
                if (am != null) {
                    try {
                        am.shutdown(MAX_BROADCAST_TIME);
                    } catch (RemoteException e) {
                    }
                }
            }
    
            // power off auto test, don't modify
            // Shutdown radios.
            Log.i(TAG, "Shutting down radios...");
            shutdownRadios(MAX_RADIO_WAIT_TIME);
    
            // power off auto test, don't modify
            Log.i(TAG, "Shutting down MountService...");
            if ( (mShutdownFlow == IPO_SHUTDOWN_FLOW) && (command.equals("1")||command.equals("3")) ) {
                Log.i(TAG, "bypass MountService!");
            } else {
                // 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");
                        if (statusCode < 0) {
                            mShutdownFlow = NORMAL_SHUTDOWN_FLOW; 
                        }
                        actionDone();
                    }
                };
    
                
    
                // 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");
                            if (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
                                Log.d(TAG, "change shutdown flow from ipo to normal: MountService");
                                mShutdownFlow = NORMAL_SHUTDOWN_FLOW;
                            }
                            break;
                        }
                        try {
                            mActionDoneSync.wait(delay);
                        } catch (InterruptedException e) {
                        }
                    }
                }
            }
    
            // power off auto test, don't modify
            //mountSerivce ���
            Log.i(TAG, "MountService shut done...");
            // [MTK] fix shutdown animation timing issue
            //==================================================================
            try {
                SystemProperties.set("service.shutanim.running","1");
                Log.i(TAG, "set service.shutanim.running to 1");
    
            } catch (Exception ex) {
                Log.e(TAG, "Failed to set 'service.shutanim.running' = 1).");
            }
            //==================================================================
    
            if (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
                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
                // power off auto test, don't modify
                Log.i(TAG, "Performing ipo low-level shutdown...");
    
                delayForPlayAnimation();
    
                if (sInstance.mScreenWakeLock != null && sInstance.mScreenWakeLock.isHeld()) {
                    sInstance.mScreenWakeLock.release();
                    sInstance.mScreenWakeLock = null;
                }
    
                sInstance.mHandler.removeCallbacks(mDelayDim); 
                stMgr.shutdown(mContext);
                stMgr.finishShutdown(mContext);
    
                //To void previous UI flick caused by shutdown animation stopping before BKL turning off         
                if (pd != null) {
                    pd.dismiss();
                    pd = null;
                } else if (beginAnimationTime > 0) {
                    try {
                        SystemProperties.set("service.bootanim.exit","1");
                        Log.i(TAG, "set 'service.bootanim.exit' = 1).");
                    } catch (Exception ex) {
                        Log.e(TAG, "Failed to set 'service.bootanim.exit' = 1).");
                    }  
                    //SystemProperties.set("ctl.stop","bootanim");
                }
    
                synchronized (sIsStartedGuard) {
                    sIsStarted = false;
                }
    
                sInstance.mPowerManager.setBacklightBrightnessOff(false); 
                sInstance.mCpuWakeLock.acquire(2000); 
    
                synchronized (mShutdownThreadSync) {
                    try {
                        mShutdownThreadSync.wait();
                    } catch (InterruptedException e) {
                    }
                }
            } else {
                rebootOrShutdown(mReboot, mRebootReason);
            }
        }

    这个方法做了一些列的操作,会关闭一些操作,如:

    1.  shutdownRadios(MAX_RADIO_WAIT_TIME);
    2. mount.shutdown(observer);
    3. stMgr.shutdown(mContext);

    重点看  rebootOrShutdown(mReboot, mRebootReason);这个方法;准备重启的方法;

     

       Step 7:来看看rebootOrShutdown()这个方法:

    public static void rebootOrShutdown(boolean reboot, String reason) {
            if (reboot) {
                Log.i(TAG, "Rebooting, reason: " + reason);
                if ( (reason != null) && reason.equals("recovery") ) {
                    delayForPlayAnimation();
                }
                try {
                    PowerManagerService.lowLevelReboot(reason);
                } catch (Exception e) {
                    Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
                }
            } 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) {
                }
            }
    
            delayForPlayAnimation();
            // Shutdown power
            // power off auto test, don't modify
            Log.i(TAG, "Performing low-level shutdown...");
            //PowerManagerService.lowLevelShutdown();
            //add your func: HDMI off
            //add for MFR
            try {
                if (ImHDMI == null)
                    ImHDMI=MediatekClassFactory.createInstance(IHDMINative.class);
            } catch (Exception e) {
                e.printStackTrace();		    
            }
            ImHDMI.hdmiPowerEnable(false);
            try {
                if (mTvOut == null)
                    mTvOut =MediatekClassFactory.createInstance(ITVOUTNative.class);
            } catch (Exception e) {
                e.printStackTrace();		    
            }
    
            mTvOut.tvoutPowerEnable(false);
            //add your func: HDMI off
            //unmout data/cache partitions while performing shutdown
    
            SystemProperties.set("ctl.start", "shutdown");
    
            /* sleep for a long time, prevent start another service */
            try {
                Thread.currentThread().sleep(Integer.MAX_VALUE);
            } catch ( Exception e) {
                Log.e(TAG, "Shutdown rebootOrShutdown Thread.currentThread().sleep exception!");  
            }
        }
    

    关机震动也在这个方法里面;这个方法重点看PowerManagerService.lowLevelReboot(reason);

      Log.i(TAG, "Rebooting, reason: " + reason);这句log也很重要,可以有助于我们分析代码;

     

        Step 8:下面来看看PowerManagerServices.java这个类的lowLevelReboot()这个方法:

    public static void lowLevelReboot(String reason) throws IOException {
            nativeReboot(reason);
        }

    这个方法调用到了native里面,后面的操作我就不分析了。。。

     

    大致流程是:

       关机,然后开机,底层判断节点后进入恢复出厂模式,recevory.img释放完全后,进入开机的流程。。。

    以后有进展再补充这部分的流程,整个过程大致就是这个样子了,里面的细节有好多没有分析,大家可以自行研究。。。,抛砖引玉的目的达到了。





     




     



     


     

  • 相关阅读:
    Atitit fms Strait (海峡) lst 数据列表目录1. 4大洋 12. 著名的海大约40个,总共约55个海 13. 海区列表 23.1. 、波利尼西亚(Polynesia,
    Atitit trave islands list 旅游资源列表岛屿目录1. 东南亚著名的旅游岛屿 21.1. Cjkv 日韩 冲绳 琉球 济州岛 北海道 21.2. 中国 涠洲岛 南澳
    Atitit Major island groups and archipelagos 主要的岛群和群岛目录资料目录1. 岛群 波利尼西亚(Polynesia, 美拉尼西亚(Melanesia,
    Atitit glb 3tie city lst 三线城市列表 数据目录1. 全球范围内约90个城市 三线 12. 世界性三线城市全球共
    Atitit glb 1tie 2tie city lst 一二线城市列表数据约50个一线城市Alpha ++ 阿尔法++,,London 伦敦,,New York 纽约,,Alpha +
    Attit 现代编程语言重要特性目录第一章 类型系统 基本三大类型 2第一节 字符串 数字 bool 2第二节 推断局部变量 2第三节 动态类型 2第二章 可读性与开发效率 简单性 2
    Atitit 未来数据库新特性展望目录1. 统一的翻页 21.1. 2 Easy Top-N
    使用Chrome DevTools(console ande elements panel)进行xpath/css/js定位
    chrome -console妙用之定位xpath/js/css
    表达式树之构建Lambda表达式
  • 原文地址:https://www.cnblogs.com/pangblog/p/3339606.html
Copyright © 2011-2022 走看看