Cts框架解析(18)-设备恢复

设备恢复


类关系



理解


4个分类中,AbortRecovery和StubDeviceRecovery2个类都是直接继承方法,直接做报错处理。但是报错的信息体现了他们的不同点,一个是放弃恢复,一个是不能恢复。还有客观世界中的区别:好比在说,我能上清华,只是我不上而已。


那么就剩下另外2个类。ReconnectingRecovery和WaitDeviceRecovery。一个是重连,一个实等待设备恢复。2者有什么区别,去具体看方法里的定义吧。


代码


AbortRecovery:放弃恢复。就是我不恢复


private static class AbortRecovery implements IDeviceRecovery {

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void recoverDevice(IDeviceStateMonitor monitor, boolean recoverUntilOnline) throws DeviceNotAvailableException {
			throw new DeviceNotAvailableException("aborted test session");
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void recoverDeviceBootloader(IDeviceStateMonitor monitor) throws DeviceNotAvailableException {
			throw new DeviceNotAvailableException("aborted test session");
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void recoverDeviceRecovery(IDeviceStateMonitor monitor) throws DeviceNotAvailableException {
			throw new DeviceNotAvailableException("aborted test session");
		}
	}

StubDeviceRecovery:为虚拟设备定义的,意思是:我不能恢复。


public class StubDeviceRecovery implements IDeviceRecovery {

    /**
     * {@inheritDoc}
     */
    @Override
    public void recoverDevice(IDeviceStateMonitor monitor, boolean recoverUntilOnline)
            throws DeviceNotAvailableException {
        throw new DeviceNotAvailableException("device recovery not implemented");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void recoverDeviceBootloader(IDeviceStateMonitor monitor)
            throws DeviceNotAvailableException {
        throw new DeviceNotAvailableException("device recovery not implemented");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void recoverDeviceRecovery(IDeviceStateMonitor monitor)
            throws DeviceNotAvailableException {
        throw new DeviceNotAvailableException("device recovery not implemented");
    }
}


ReconnectingRecovery:重新连接设备。用于wifi连接设备的时候,如果短线了,调用adb connect的命令来连接设备。


public class ReconnectingRecovery implements IDeviceRecovery {

    private static final int ADB_TIMEOUT = 2 * 60 * 1000;
    private static final int CONNECTION_ATTEMPTS = 5;

    /**
     * {@inheritDoc}
     */
    @Override
    public void recoverDevice(IDeviceStateMonitor monitor, boolean recoverUntilOnline)
            throws DeviceNotAvailableException {
        String serial = monitor.getSerialNumber();

        // disconnect - many versions of adb client have stale TCP connection
        // status
        getRunUtil().runTimedCmd(ADB_TIMEOUT, "adb", "disconnect", serial);

        // try to reconnect
        int attempt = 1;
        do {
            CLog.i("Trying to reconnect with device " + serial + " / attempt " + attempt);
            getRunUtil().runTimedCmd(ADB_TIMEOUT, "adb", "connect", serial);
        } while (monitor.waitForDeviceOnline() == null && ++attempt <= CONNECTION_ATTEMPTS);

        String errMsg = "Could not recover device " + serial + " after " + --attempt + " attempts";

        // occasionally device is erroneously reported as online - double check
        // that we can shell into device
        if (!monitor.waitForDeviceShell(10 * 1000)) {
            throw new DeviceUnresponsiveException(errMsg);
        }

        if (!recoverUntilOnline) {
            if (monitor.waitForDeviceAvailable() == null) {
                throw new DeviceUnresponsiveException(errMsg);
            }
        }

        CLog.v("Successfully reconnected with device " + serial);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void recoverDeviceBootloader(IDeviceStateMonitor monitor)
            throws DeviceNotAvailableException {
        throw new java.lang.UnsupportedOperationException(
                "This implementation can't recover a device in bootloader mode.");
    }

    /**
     * {@inheritDoc}
     * <p>
     * This implementation assumes devices in recovery mode can't be talked to
     * at all, so it will try to recover a device and leave it in fully booted
     * mode.
     */
    @Override
    public void recoverDeviceRecovery(IDeviceStateMonitor monitor)
            throws DeviceNotAvailableException {
        recoverDevice(monitor, false);
    }

    /**
     * Get the {@link RunUtil} instance to use.
     * <p/>
     * Exposed for unit testing.
     */
    IRunUtil getRunUtil() {
        return RunUtil.getDefault();
    }
}


WaitDeviceRecovery,这才是真正要去理解的类。一般的测试主要是用的该类来恢复设备。


/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.tradefed.device;

import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.Log;
import com.android.ddmlib.TimeoutException;
import com.android.tradefed.config.Option;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.util.IRunUtil;
import com.android.tradefed.util.RunUtil;

import java.io.IOException;

/**
 * A simple implementation of a {@link IDeviceRecovery} that waits for device to be online and
 * respond to simple commands.
 */
public class WaitDeviceRecovery implements IDeviceRecovery {

    private static final String LOG_TAG = "WaitDeviceRecovery";

    /** the time in ms to wait before beginning recovery attempts */
    protected static final long INITIAL_PAUSE_TIME = 5 * 1000;

    /**
     * The number of attempts to check if device is in bootloader.
     * <p/>
     * Exposed for unit testing
     */
    public static final int BOOTLOADER_POLL_ATTEMPTS = 3;

    // TODO: add a separate configurable timeout per operation
    @Option(name="device-wait-time",
            description="maximum time in ms to wait for a single device recovery command.")
    protected long mWaitTime = 4 * 60 * 1000;

    @Option(name="bootloader-wait-time",
            description="maximum time in ms to wait for device to be in fastboot.")
    protected long mBootloaderWaitTime = 30 * 1000;

    @Option(name="shell-wait-time",
            description="maximum time in ms to wait for device shell to be responsive.")
    protected long mShellWaitTime = 30 * 1000;

    @Option(name = "disable-unresponsive-reboot",
            description = "If this is set, we will not attempt to reboot an unresponsive device" +
            "that is in userspace.  Note that this will have no effect if the device is in " +
            "fastboot or is expected to be in fastboot.")
    protected boolean mDisableUnresponsiveReboot = false;

    /**
     * Get the {@link RunUtil} instance to use.
     * <p/>
     * Exposed for unit testing.
     */
    protected IRunUtil getRunUtil() {
        return RunUtil.getDefault();
    }

    /**
     * Sets the maximum time in ms to wait for a single device recovery command.
     */
    void setWaitTime(long waitTime) {
        mWaitTime = waitTime;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void recoverDevice(IDeviceStateMonitor monitor, boolean recoverUntilOnline)
            throws DeviceNotAvailableException {
        // device may have just gone offline
        // sleep a small amount to give ddms state a chance to settle
        // TODO - see if there is better way to handle this
        Log.i(LOG_TAG, String.format("Pausing for %d for %s to recover",
                INITIAL_PAUSE_TIME, monitor.getSerialNumber()));
        getRunUtil().sleep(INITIAL_PAUSE_TIME);

        // ensure bootloader state is updated,最新获取过fastboot devices信息
        monitor.waitForDeviceBootloaderStateUpdate();

        if (monitor.getDeviceState().equals(TestDeviceState.FASTBOOT)) {
            Log.i(LOG_TAG, String.format(
                    "Found device %s in fastboot but expected online. Rebooting...",
                    monitor.getSerialNumber()));
            // TODO: retry if failed
            getRunUtil().runTimedCmd(20*1000, "fastboot", "-s", monitor.getSerialNumber(),
                    "reboot");
        }

        // wait for device online
        IDevice device = monitor.waitForDeviceOnline();
        if (device == null) {
            handleDeviceNotAvailable(monitor, recoverUntilOnline);
            return;
        }
        // occasionally device is erroneously reported as online - double check that we can shell
        // into device
        if (!monitor.waitForDeviceShell(mShellWaitTime)) {
            // treat this as a not available device
            handleDeviceNotAvailable(monitor, recoverUntilOnline);
            return;
        }

        if (!recoverUntilOnline) {
            if (monitor.waitForDeviceAvailable(mWaitTime) == null) {
                // device is online but not responsive
                handleDeviceUnresponsive(device, monitor);
            }
        }
    }

    /**
     * Handle situation where device is online but unresponsive.
     * @param monitor
     * @throws DeviceNotAvailableException
     */
    protected void handleDeviceUnresponsive(IDevice device, IDeviceStateMonitor monitor)
            throws DeviceNotAvailableException {
        if (!mDisableUnresponsiveReboot) {
            rebootDevice(device);
        }
        IDevice newdevice = monitor.waitForDeviceOnline();
        if (newdevice == null) {
            handleDeviceNotAvailable(monitor, false);
            return;
        }
        if (monitor.waitForDeviceAvailable(mWaitTime) == null) {
            throw new DeviceUnresponsiveException(String.format(
                    "Device %s is online but unresponsive", monitor.getSerialNumber()));
        }
    }

    /**
     * Handle situation where device is not available.
     *
     * @param monitor the {@link IDeviceStateMonitor}
     * @param recoverTillOnline if true this method should return if device is online, and not
     * check for responsiveness
     * @throws DeviceNotAvailableException
     */
    protected void handleDeviceNotAvailable(IDeviceStateMonitor monitor, boolean recoverTillOnline)
            throws DeviceNotAvailableException {
        throw new DeviceNotAvailableException(String.format("Could not find device %s",
                monitor.getSerialNumber()));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void recoverDeviceBootloader(final IDeviceStateMonitor monitor)
            throws DeviceNotAvailableException {
        // device may have just gone offline
        // wait a small amount to give device state a chance to settle
        // TODO - see if there is better way to handle this
        Log.i(LOG_TAG, String.format("Pausing for %d for %s to recover",
                INITIAL_PAUSE_TIME, monitor.getSerialNumber()));
        getRunUtil().sleep(INITIAL_PAUSE_TIME);

        // poll and wait for device to return to valid state
        long pollTime = mBootloaderWaitTime / BOOTLOADER_POLL_ATTEMPTS;
        for (int i=0; i < BOOTLOADER_POLL_ATTEMPTS; i++) {
            if (monitor.waitForDeviceBootloader(pollTime)) {
                handleDeviceBootloaderUnresponsive(monitor);
                // passed above check, abort
                return;
            } else if (monitor.getDeviceState() == TestDeviceState.ONLINE) {
                handleDeviceOnlineExpectedBootloader(monitor);
                return;
            }
        }
        handleDeviceBootloaderNotAvailable(monitor);
    }

    /**
     * Handle condition where device is online, but should be in bootloader state.
     * <p/>
     * If this method
     * @param monitor
     * @throws DeviceNotAvailableException
     */
    protected void handleDeviceOnlineExpectedBootloader(final IDeviceStateMonitor monitor)
            throws DeviceNotAvailableException {
        Log.i(LOG_TAG, String.format("Found device %s online but expected fastboot.",
            monitor.getSerialNumber()));
        // call waitForDeviceOnline to get handle to IDevice
        IDevice device = monitor.waitForDeviceOnline();
        if (device == null) {
            handleDeviceBootloaderNotAvailable(monitor);
            return;
        }
        rebootDeviceIntoBootloader(device);
        if (!monitor.waitForDeviceBootloader(mBootloaderWaitTime)) {
            throw new DeviceNotAvailableException(String.format(
                    "Device %s not in bootloader after reboot", monitor.getSerialNumber()));
        }
    }

    /**
     * @param monitor
     * @throws DeviceNotAvailableException
     */
    protected void handleDeviceBootloaderUnresponsive(IDeviceStateMonitor monitor)
            throws DeviceNotAvailableException {
        CLog.i("Found device %s in fastboot but potentially unresponsive.",
                monitor.getSerialNumber());
        // TODO: retry reboot
        getRunUtil().runTimedCmd(20*1000, "fastboot", "-s", monitor.getSerialNumber(),
                "reboot-bootloader");
        // wait for device to reboot
        monitor.waitForDeviceNotAvailable(20*1000);
        if (!monitor.waitForDeviceBootloader(mBootloaderWaitTime)) {
            throw new DeviceNotAvailableException(String.format(
                    "Device %s not in bootloader after reboot", monitor.getSerialNumber()));
        }
    }

    /**
     * Reboot device into bootloader.
     *
     * @param device the {@link IDevice} to reboot.
     */
    protected void rebootDeviceIntoBootloader(IDevice device) {
        try {
            device.reboot("bootloader");
        } catch (IOException e) {
            Log.w(LOG_TAG, String.format("failed to reboot %s: %s", device.getSerialNumber(),
                    e.getMessage()));
        } catch (TimeoutException e) {
            Log.w(LOG_TAG, String.format("failed to reboot %s: timeout", device.getSerialNumber()));
        } catch (AdbCommandRejectedException e) {
            Log.w(LOG_TAG, String.format("failed to reboot %s: %s", device.getSerialNumber(),
                    e.getMessage()));
        }
    }

    /**
     * Reboot device into bootloader.
     *
     * @param device the {@link IDevice} to reboot.
     */
    protected void rebootDevice(IDevice device) {
        try {
            device.reboot(null);
        } catch (IOException e) {
            Log.w(LOG_TAG, String.format("failed to reboot %s: %s", device.getSerialNumber(),
                    e.getMessage()));
        } catch (TimeoutException e) {
            Log.w(LOG_TAG, String.format("failed to reboot %s: timeout", device.getSerialNumber()));
        } catch (AdbCommandRejectedException e) {
            Log.w(LOG_TAG, String.format("failed to reboot %s: %s", device.getSerialNumber(),
                    e.getMessage()));
        }
    }

    /**
     * Handle situation where device is not available when expected to be in bootloader.
     *
     * @param monitor the {@link IDeviceStateMonitor}
     * @throws DeviceNotAvailableException
     */
    protected void handleDeviceBootloaderNotAvailable(final IDeviceStateMonitor monitor)
            throws DeviceNotAvailableException {
        throw new DeviceNotAvailableException(String.format(
                "Could not find device %s in bootloader", monitor.getSerialNumber()));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void recoverDeviceRecovery(IDeviceStateMonitor monitor)
            throws DeviceNotAvailableException {
        throw new DeviceNotAvailableException("device recovery not implemented");
    }
}

重点


现在着重来理解WaitDeviceRecovery中的方法。


recoverDevice方法


/**
     * {@inheritDoc}
     */
    @Override
    public void recoverDevice(IDeviceStateMonitor monitor, boolean recoverUntilOnline)
            throws DeviceNotAvailableException {
        // device may have just gone offline
        // sleep a small amount to give ddms state a chance to settle
        // TODO - see if there is better way to handle this
        Log.i(LOG_TAG, String.format("Pausing for %d for %s to recover",
                INITIAL_PAUSE_TIME, monitor.getSerialNumber()));
        getRunUtil().sleep(INITIAL_PAUSE_TIME);

        // ensure bootloader state is updated,最新获取过fastboot devices信息
        monitor.waitForDeviceBootloaderStateUpdate();

        if (monitor.getDeviceState().equals(TestDeviceState.FASTBOOT)) {
            Log.i(LOG_TAG, String.format(
                    "Found device %s in fastboot but expected online. Rebooting...",
                    monitor.getSerialNumber()));
            // TODO: retry if failed
            getRunUtil().runTimedCmd(20*1000, "fastboot", "-s", monitor.getSerialNumber(),
                    "reboot");
        }

        // wait for device online
        IDevice device = monitor.waitForDeviceOnline();
        if (device == null) {
            handleDeviceNotAvailable(monitor, recoverUntilOnline);
            return;
        }
        // occasionally device is erroneously reported as online - double check that we can shell
        // into device
        if (!monitor.waitForDeviceShell(mShellWaitTime)) {
            // treat this as a not available device
            handleDeviceNotAvailable(monitor, recoverUntilOnline);
            return;
        }

        if (!recoverUntilOnline) {
            if (monitor.waitForDeviceAvailable(mWaitTime) == null) {
                // device is online but not responsive
                handleDeviceUnresponsive(device, monitor);
            }
        }
    }

1.首先等待5秒钟,是为了给adb足够的时间先自己处理一下设备断线问题。(adb有自动恢复功能)。

2.我们要等待最新一次的fastboot监听器的执行结果,这个在上一篇文章讲过fastboot监听器是做什么用的。

3.判断目前设备断线的原因是否是因为进入到了fastboot模式下。如果是的话,执行fastboot reboot操作重启设备。

4.等待设备恢复到在线状态(具体怎么等待的,我会在DeviceMonitor中讲解)。

5.如果等待过后,设备没有恢复到在线状态,则调用handleDeviceNotAvailable方法,直接抛出错误。

6.如果设备正常恢复到online状态。再次通过adb命令来检测其正常性,双重验证。如果验证不通过也需要抛出错误。

7.如果第二次验证通过,是否需要等待设备处于有效状态,有效状态和在线状态的区别在于:在线不等于就可以用于测试。可以用于测试的测试需要经过很多中检测。这个也会在以后讲解。

8.如果做了等待设备直到处于有效状态的操作后,设备没有反馈正确的结果,说明设备没有处于有效状态,这个时候我们就需要调用handleDeviceUnresponsive()


protected void handleDeviceUnresponsive(IDevice device, IDeviceStateMonitor monitor)
            throws DeviceNotAvailableException {
        if (!mDisableUnresponsiveReboot) {
            rebootDevice(device);
        }
        IDevice newdevice = monitor.waitForDeviceOnline();
        if (newdevice == null) {
            handleDeviceNotAvailable(monitor, false);
            return;
        }
        if (monitor.waitForDeviceAvailable(mWaitTime) == null) {
            throw new DeviceUnresponsiveException(String.format(
                    "Device %s is online but unresponsive", monitor.getSerialNumber()));
        }
    }


9.在上面的方法中,会先判断是否可以重启,如果可以的话,先重启一下设备。

10.然后重复一遍4~7的步骤

11.如果还没成功,就直接报错了。


上面的步骤中在执行设备恢复到设备处于在线状态的的过程中,执行了2次,称为retry的动作。再试一次,可以使成功的概率大一点。


recoverDeviceBootloader方法


@Override
    public void recoverDeviceBootloader(final IDeviceStateMonitor monitor)
            throws DeviceNotAvailableException {
        // device may have just gone offline
        // wait a small amount to give device state a chance to settle
        // TODO - see if there is better way to handle this
        Log.i(LOG_TAG, String.format("Pausing for %d for %s to recover",
                INITIAL_PAUSE_TIME, monitor.getSerialNumber()));
        getRunUtil().sleep(INITIAL_PAUSE_TIME);

        // poll and wait for device to return to valid state
        long pollTime = mBootloaderWaitTime / BOOTLOADER_POLL_ATTEMPTS;
        for (int i=0; i < BOOTLOADER_POLL_ATTEMPTS; i++) {
            if (monitor.waitForDeviceBootloader(pollTime)) {
                handleDeviceBootloaderUnresponsive(monitor);
                // passed above check, abort
                return;
            } else if (monitor.getDeviceState() == TestDeviceState.ONLINE) {
                handleDeviceOnlineExpectedBootloader(monitor);
                return;
            }
        }
        handleDeviceBootloaderNotAvailable(monitor);
    }


1.第一步和上面的意义,等待一段时间,让adb先自己处理。

2.计算重复执行的次数,用总时间/执行的次数,等待每次执行所用的时间。

3.执行等待设备处于BootLoader模式,也就是fastboot模式。

4.如果成功进入fastboot模式,则直接返回,如果设备处于online模式,则调用handleDeviceOnlineExpectedBootloader方法


protected void handleDeviceOnlineExpectedBootloader(final IDeviceStateMonitor monitor)
            throws DeviceNotAvailableException {
        Log.i(LOG_TAG, String.format("Found device %s online but expected fastboot.",
            monitor.getSerialNumber()));
        // call waitForDeviceOnline to get handle to IDevice
        IDevice device = monitor.waitForDeviceOnline();
        if (device == null) {
            handleDeviceBootloaderNotAvailable(monitor);
            return;
        }
        rebootDeviceIntoBootloader(device);
        if (!monitor.waitForDeviceBootloader(mBootloaderWaitTime)) {
            throw new DeviceNotAvailableException(String.format(
                    "Device %s not in bootloader after reboot", monitor.getSerialNumber()));
        }
    }

5.进入该方法后,首先通过直接monitor.waitForDeviceOnline()来得到该设备,因为该设备就已经处于online,这里调用只是为了得到IDevice.

6.如果这个时候得到的设备为null,直接抛出错误。

7.然后重启设备,进入fastboot模式。

8.然后再次等待设备处于fastboot模式,如果成功,方法结束,回到recoverDeviceBootloader中,然后recoverDeviceBootloader也结束,返回。如果失败,直接抛出异常。


9.重复3~8的过程,如果循环结束后,还无法进入fastboot模式,直接抛出异常。


recoverDeviceRecovery方法


因为不可能执行卡刷,所以该方法直接抛出异常。


public void recoverDeviceRecovery(IDeviceStateMonitor monitor)
            throws DeviceNotAvailableException {
        throw new DeviceNotAvailableException("device recovery not implemented");
    }



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值