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.Map.Entry; 29 import java.util.Set; 30 import java.util.regex.Matcher; 31 import java.util.regex.Pattern; 32 33 /** 34 * A helper class for fastboot operations. 35 */ 36 public class FastbootHelper { 37 38 /** max wait time in ms for fastboot devices command to complete */ 39 private static final long FASTBOOT_CMD_TIMEOUT = 1 * 60 * 1000; 40 41 private IRunUtil mRunUtil; 42 private String mFastbootPath = "fastboot"; 43 44 /** 45 * Constructor. 46 * 47 * @param runUtil a {@link IRunUtil}. 48 */ FastbootHelper(final IRunUtil runUtil, final String fastbootPath)49 public FastbootHelper(final IRunUtil runUtil, final String fastbootPath) { 50 if (runUtil == null) { 51 throw new IllegalArgumentException("runUtil cannot be null"); 52 } 53 if (fastbootPath == null || fastbootPath.isEmpty()) { 54 throw new IllegalArgumentException("fastboot cannot be null or empty"); 55 } 56 mRunUtil = runUtil; 57 mFastbootPath = fastbootPath; 58 } 59 60 /** 61 * Determine if fastboot is available for use. 62 */ isFastbootAvailable()63 public boolean isFastbootAvailable() { 64 // Run "fastboot help" to check the existence and the version of fastboot 65 // (Old versions do not support "help" command). 66 CommandResult fastbootResult = mRunUtil.runTimedCmdSilently(15000, mFastbootPath, "help"); 67 if (CommandStatus.SUCCESS.equals(fastbootResult.getStatus())) { 68 return true; 69 } 70 if (fastbootResult.getStderr() != null && 71 fastbootResult.getStderr().indexOf("usage: fastboot") >= 0) { 72 CLog.logAndDisplay(LogLevel.WARN, 73 "You are running an older version of fastboot, please update it."); 74 return true; 75 } 76 CLog.d("fastboot not available. stdout: %s, stderr: %s", 77 fastbootResult.getStdout(), fastbootResult.getStderr()); 78 return false; 79 } 80 81 /** 82 * Returns a set of device serials in fastboot mode or an empty set if no fastboot devices. 83 * 84 * @return a set of device serials. 85 */ getDevices()86 public Set<String> getDevices() { 87 CommandResult fastbootResult = 88 mRunUtil.runTimedCmdSilently(FASTBOOT_CMD_TIMEOUT, mFastbootPath, "devices"); 89 if (fastbootResult.getStatus().equals(CommandStatus.SUCCESS)) { 90 CLog.v("fastboot devices returned\n %s", 91 fastbootResult.getStdout()); 92 return parseDevices(fastbootResult.getStdout(), false); 93 } else { 94 CLog.w("'fastboot devices' failed. Result: %s, stderr: %s", fastbootResult.getStatus(), 95 fastbootResult.getStderr()); 96 } 97 return new HashSet<String>(); 98 } 99 100 /** 101 * Returns a map of device serials and whether they are in fastbootd mode or not. 102 * 103 * @return a Map of serial in bootloader or fastbootd, the boolean is true if in fastbootd 104 */ getBootloaderAndFastbootdDevices()105 public Map<String, Boolean> getBootloaderAndFastbootdDevices() { 106 CommandResult fastbootResult = 107 mRunUtil.runTimedCmdSilently(FASTBOOT_CMD_TIMEOUT, mFastbootPath, "devices"); 108 if (fastbootResult.getStatus().equals(CommandStatus.SUCCESS)) { 109 CLog.v("fastboot devices returned\n %s", fastbootResult.getStdout()); 110 Set<String> fastboot = parseDevices(fastbootResult.getStdout(), false); 111 Set<String> fastbootd = parseDevices(fastbootResult.getStdout(), true); 112 HashMap<String, Boolean> devices = new LinkedHashMap<>(); 113 for (String f : fastboot) { 114 devices.put(f, false); 115 } 116 for (String f : fastbootd) { 117 devices.put(f, true); 118 } 119 return devices; 120 } else { 121 CLog.w( 122 "'fastboot devices' failed. Result: %s, stderr: %s", 123 fastbootResult.getStatus(), fastbootResult.getStderr()); 124 } 125 return new HashMap<String, Boolean>(); 126 } 127 128 /** 129 * Returns a map of device serials and whether they are in fastbootd mode or not. 130 * 131 * @param serials a map of devices serial number and fastboot mode serial number. 132 * @return a Map of serial in bootloader or fastbootd, the boolean is true if in fastbootd 133 */ getBootloaderAndFastbootdTcpDevices(Map<String, String> serials)134 public Map<String, Boolean> getBootloaderAndFastbootdTcpDevices(Map<String, String> serials) { 135 HashMap<String, Boolean> devices = new LinkedHashMap<>(); 136 long TIMEOUT = 1500; 137 138 for (Entry<String, String> entry : serials.entrySet()) { 139 CLog.v("Run 'fastboot -s %s getvar is-userspace' command", entry.getValue()); 140 CommandResult fastbootResult = 141 mRunUtil.runTimedCmdSilently( 142 TIMEOUT, 143 mFastbootPath, 144 "-s", 145 entry.getValue(), 146 "getvar", 147 "is-userspace"); 148 if (fastbootResult.getStatus().equals(CommandStatus.SUCCESS)) { 149 if (fastbootResult.getStderr().contains("yes")) { 150 devices.put(entry.getKey(), true); 151 } else { 152 devices.put(entry.getKey(), false); 153 } 154 } 155 } 156 157 return devices; 158 } 159 160 /** 161 * Parses the output of "fastboot devices" command. Exposed for unit testing. 162 * 163 * @param fastbootOutput the output of fastboot command. 164 * @param fastbootd whether or not we parse fastbootd or fastboot output 165 * @return a set of device serials. 166 */ parseDevices(String fastbootOutput, boolean fastbootd)167 Set<String> parseDevices(String fastbootOutput, boolean fastbootd) { 168 Set<String> serials = new HashSet<String>(); 169 Pattern fastbootPattern = null; 170 if (fastbootd) { 171 fastbootPattern = Pattern.compile("([\\w\\d-]+)\\s+fastbootd\\s*"); 172 } else { 173 fastbootPattern = Pattern.compile("([\\w\\d-]+)\\s+fastboot\\s*"); 174 } 175 Matcher fastbootMatcher = fastbootPattern.matcher(fastbootOutput); 176 while (fastbootMatcher.find()) { 177 serials.add(fastbootMatcher.group(1)); 178 } 179 return serials; 180 } 181 182 /** 183 * Executes a fastboot command on a device and return the output. 184 * 185 * @param serial a device serial. 186 * @param command a fastboot command to run. 187 * @return the output of the fastboot command. null if the command failed. 188 */ executeCommand(String serial, String command)189 public String executeCommand(String serial, String command) { 190 final CommandResult fastbootResult = mRunUtil.runTimedCmd(FASTBOOT_CMD_TIMEOUT, 191 mFastbootPath, "-s", serial, command); 192 if (fastbootResult.getStatus() != CommandStatus.SUCCESS) { 193 CLog.w("'fastboot -s %s %s' failed. Result: %s, stderr: %s", serial, command, 194 fastbootResult.getStatus(), fastbootResult.getStderr()); 195 return null; 196 } 197 return fastbootResult.getStdout(); 198 } 199 200 /** Returns whether or not a device is in Fastbootd instead of Bootloader. */ isFastbootd(String serial)201 public boolean isFastbootd(String serial) { 202 final CommandResult fastbootResult = 203 mRunUtil.runTimedCmd( 204 FASTBOOT_CMD_TIMEOUT, 205 mFastbootPath, 206 "-s", 207 serial, 208 "getvar", 209 "is-userspace"); 210 if (fastbootResult.getStderr().contains("is-userspace: yes")) { 211 return true; 212 } 213 return false; 214 } 215 } 216