# 設備恢復
## 類關系

## 理解
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");
}
~~~
- 前言
- (1)-windows下cts配置
- (2)-cts調試環境的搭建
- (3)-基礎庫tradefederation配置
- (4)-任務的添加
- (5)-9大組件配置
- (6)-任務的執行
- (7)-任務執行的調度室
- (8)-IBuildProvider
- (9)-IDeviceRecovery
- (10)-TestDeviceOptions
- (11)-ICommandOptions
- (12)-ITargetPreparer
- (13)-任務執行過程
- (14)-任務執行過程
- (15)-任務執行完
- (16)-logcat信息收集系統
- (17)-fastboot狀態監聽器
- (18)-設備恢復
- (19)-設備狀態的分類以及恢復模式的分類
- (20)-cts自身log系統
- (21)-測試結果收集系統
- (22)-自動檢測設備
- (23)-設備分類
- (24)-case的組織