1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.tradefed.device; 17 18 import com.android.ddmlib.Log.LogLevel; 19 import com.android.tradefed.log.LogUtil.CLog; 20 import com.android.tradefed.util.CommandResult; 21 import com.android.tradefed.util.CommandStatus; 22 import com.android.tradefed.util.IRunUtil; 23 24 import java.util.HashMap; 25 import java.util.HashSet; 26 import java.util.LinkedHashMap; 27 import java.util.Map; 28 import java.util.Set; 29 import java.util.regex.Matcher; 30 import java.util.regex.Pattern; 31 32 /** 33 * A helper class for fastboot operations. 34 */ 35 public class FastbootHelper { 36 37 /** max wait time in ms for fastboot devices command to complete */ 38 private static final long FASTBOOT_CMD_TIMEOUT = 1 * 60 * 1000; 39 40 private IRunUtil mRunUtil; 41 private String mFastbootPath = "fastboot"; 42 43 /** 44 * Constructor. 45 * 46 * @param runUtil a {@link IRunUtil}. 47 */ FastbootHelper(final IRunUtil runUtil, final String fastbootPath)48 public FastbootHelper(final IRunUtil runUtil, final String fastbootPath) { 49 if (runUtil == null) { 50 throw new IllegalArgumentException("runUtil cannot be null"); 51 } 52 if (fastbootPath == null || fastbootPath.isEmpty()) { 53 throw new IllegalArgumentException("fastboot cannot be null or empty"); 54 } 55 mRunUtil = runUtil; 56 mFastbootPath = fastbootPath; 57 } 58 59 /** 60 * Determine if fastboot is available for use. 61 */ isFastbootAvailable()62 public boolean isFastbootAvailable() { 63 // Run "fastboot help" to check the existence and the version of fastboot 64 // (Old versions do not support "help" command). 65 CommandResult fastbootResult = mRunUtil.runTimedCmdSilently(15000, mFastbootPath, "help"); 66 if (CommandStatus.SUCCESS.equals(fastbootResult.getStatus())) { 67 return true; 68 } 69 if (fastbootResult.getStderr() != null && 70 fastbootResult.getStderr().indexOf("usage: fastboot") >= 0) { 71 CLog.logAndDisplay(LogLevel.WARN, 72 "You are running an older version of fastboot, please update it."); 73 return true; 74 } 75 CLog.d("fastboot not available. stdout: %s, stderr: %s", 76 fastbootResult.getStdout(), fastbootResult.getStderr()); 77 return false; 78 } 79 80 /** 81 * Returns a set of device serials in fastboot mode or an empty set if no fastboot devices. 82 * 83 * @return a set of device serials. 84 */ getDevices()85 public Set<String> getDevices() { 86 CommandResult fastbootResult = 87 mRunUtil.runTimedCmdSilently(FASTBOOT_CMD_TIMEOUT, mFastbootPath, "devices"); 88 if (fastbootResult.getStatus().equals(CommandStatus.SUCCESS)) { 89 CLog.v("fastboot devices returned\n %s", 90 fastbootResult.getStdout()); 91 return parseDevices(fastbootResult.getStdout(), false); 92 } else { 93 CLog.w("'fastboot devices' failed. Result: %s, stderr: %s", fastbootResult.getStatus(), 94 fastbootResult.getStderr()); 95 } 96 return new HashSet<String>(); 97 } 98 99 /** 100 * Returns a map of device serials and whether they are in fastbootd mode or not. 101 * 102 * @return a Map of serial in bootloader or fastbootd, the boolean is true if in fastbootd 103 */ getBootloaderAndFastbootdDevices()104 public Map<String, Boolean> getBootloaderAndFastbootdDevices() { 105 CommandResult fastbootResult = 106 mRunUtil.runTimedCmdSilently(FASTBOOT_CMD_TIMEOUT, mFastbootPath, "devices"); 107 if (fastbootResult.getStatus().equals(CommandStatus.SUCCESS)) { 108 CLog.v("fastboot devices returned\n %s", fastbootResult.getStdout()); 109 Set<String> fastboot = parseDevices(fastbootResult.getStdout(), false); 110 Set<String> fastbootd = parseDevices(fastbootResult.getStdout(), true); 111 HashMap<String, Boolean> devices = new LinkedHashMap<>(); 112 for (String f : fastboot) { 113 devices.put(f, false); 114 } 115 for (String f : fastbootd) { 116 devices.put(f, true); 117 } 118 return devices; 119 } else { 120 CLog.w( 121 "'fastboot devices' failed. Result: %s, stderr: %s", 122 fastbootResult.getStatus(), fastbootResult.getStderr()); 123 } 124 return new HashMap<String, Boolean>(); 125 } 126 127 /** 128 * Parses the output of "fastboot devices" command. Exposed for unit testing. 129 * 130 * @param fastbootOutput the output of fastboot command. 131 * @param fastbootd whether or not we parse fastbootd or fastboot output 132 * @return a set of device serials. 133 */ parseDevices(String fastbootOutput, boolean fastbootd)134 Set<String> parseDevices(String fastbootOutput, boolean fastbootd) { 135 Set<String> serials = new HashSet<String>(); 136 Pattern fastbootPattern = null; 137 if (fastbootd) { 138 fastbootPattern = Pattern.compile("([\\w\\d-]+)\\s+fastbootd\\s*"); 139 } else { 140 fastbootPattern = Pattern.compile("([\\w\\d-]+)\\s+fastboot\\s*"); 141 } 142 Matcher fastbootMatcher = fastbootPattern.matcher(fastbootOutput); 143 while (fastbootMatcher.find()) { 144 serials.add(fastbootMatcher.group(1)); 145 } 146 return serials; 147 } 148 149 /** 150 * Executes a fastboot command on a device and return the output. 151 * 152 * @param serial a device serial. 153 * @param command a fastboot command to run. 154 * @return the output of the fastboot command. null if the command failed. 155 */ executeCommand(String serial, String command)156 public String executeCommand(String serial, String command) { 157 final CommandResult fastbootResult = mRunUtil.runTimedCmd(FASTBOOT_CMD_TIMEOUT, 158 mFastbootPath, "-s", serial, command); 159 if (fastbootResult.getStatus() != CommandStatus.SUCCESS) { 160 CLog.w("'fastboot -s %s %s' failed. Result: %s, stderr: %s", serial, command, 161 fastbootResult.getStatus(), fastbootResult.getStderr()); 162 return null; 163 } 164 return fastbootResult.getStdout(); 165 } 166 167 /** Returns whether or not a device is in Fastbootd instead of Bootloader. */ isFastbootd(String serial)168 public boolean isFastbootd(String serial) { 169 final CommandResult fastbootResult = 170 mRunUtil.runTimedCmd( 171 FASTBOOT_CMD_TIMEOUT, 172 mFastbootPath, 173 "-s", 174 serial, 175 "getvar", 176 "is-userspace"); 177 if (fastbootResult.getStderr().contains("is-userspace: yes")) { 178 return true; 179 } 180 return false; 181 } 182 } 183