(9)-IDeviceRecovery
最后更新于:2022-04-01 06:54:40
当设备处于offline状态时,cts框架就要调用IDeviceRecovery接口类去做相应的恢复工作。
# 接口
~~~
/*
* 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;
/**
* Interface for recovering a device that has gone offline.
*/
public interface IDeviceRecovery {
/**
* Attempt to recover the given device that can no longer be communicated with.
* <p/>
* Method should block and only return when device is in requested state.
*
* @param monitor the {@link IDeviceStateMonitor} to use.
* @param recoverUntilOnline if true, method should return as soon as device is online on adb.
* If false, method should block until device is fully available for testing (ie
* {@link IDeviceStateMonitor#waitForDeviceAvailable()} succeeds.
* @throws DeviceNotAvailableException if device could not be recovered
*/
public void recoverDevice(IDeviceStateMonitor monitor, boolean recoverUntilOnline)
throws DeviceNotAvailableException;
/**
* Attempt to recover the given unresponsive device in recovery mode.
*
* @param monitor the {@link IDeviceStateMonitor} to use.
* @throws DeviceNotAvailableException if device could not be recovered
*/
public void recoverDeviceRecovery(IDeviceStateMonitor monitor)
throws DeviceNotAvailableException;
/**
* Attempt to recover the given unresponsive device in bootloader mode.
*
* @param monitor the {@link IDeviceStateMonitor} to use.
* @throws DeviceNotAvailableException if device could not be recovered
*/
public void recoverDeviceBootloader(IDeviceStateMonitor monitor)
throws DeviceNotAvailableException;
}
~~~
该接口中要三个方法:
recoverDevice:连接不再通信的设备
recoverDeviceRecovery:当手机处于[Recovery](http://www.anqu.com/lab_74/21078/)工程模式下,已经发送信息,但是没有反应,就需要调用该方法再次重连。
recoverDeviceBootloader:恢复没有反馈的设备,与上面不同的是当前设备处于[BootLoader](http://mobile.zol.com.cn/242/2424698_all.html)模式下。
# 实现类
cts模式的实现类为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
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");
}
}
~~~
这个类的方法中具体讲了如何实现重连机制,有兴趣的可以详细了解。我不太感兴趣,就一笔带过了。