10eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com/* 20eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com * Copyright (C) 2010 The Android Open Source Project 30eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com * 40eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com * Licensed under the Apache License, Version 2.0 (the "License"); 50eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com * you may not use this file except in compliance with the License. 60eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com * You may obtain a copy of the License at 70eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com * 80eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com * http://www.apache.org/licenses/LICENSE-2.0 90eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com * 100eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com * Unless required by applicable law or agreed to in writing, software 110eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com * distributed under the License is distributed on an "AS IS" BASIS, 120eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com * See the License for the specific language governing permissions and 140eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com * limitations under the License. 150eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com */ 160eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com 170eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.compackage vogar.android; 180eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com 197029503206e4c89fe167c3389c1062d89cf35c52Paul Duffinimport com.google.common.annotations.VisibleForTesting; 200eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.comimport com.google.common.collect.ImmutableList; 210eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.comimport java.io.File; 220eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.comimport java.io.FileNotFoundException; 230eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.comimport java.util.List; 247029503206e4c89fe167c3389c1062d89cf35c52Paul Duffinimport java.util.concurrent.TimeoutException; 250eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.comimport java.util.regex.Matcher; 260eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.comimport java.util.regex.Pattern; 277029503206e4c89fe167c3389c1062d89cf35c52Paul Duffinimport vogar.Log; 287029503206e4c89fe167c3389c1062d89cf35c52Paul Duffinimport vogar.Md5Cache; 290eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.comimport vogar.Target; 300eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.comimport vogar.commands.Command; 310eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com 320eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.compublic final class AdbTarget extends Target { 330eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com 34411cb1fb67241125203629229600ecbd821eb9c7Paul Duffin private static final ImmutableList<String> TARGET_PROCESS_PREFIX = 35411cb1fb67241125203629229600ecbd821eb9c7Paul Duffin ImmutableList.of("adb", "shell"); 36411cb1fb67241125203629229600ecbd821eb9c7Paul Duffin 377029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin private final Log log; 387029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin 397029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin private final DeviceFilesystem deviceFilesystem; 407029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin 417029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin private final Md5Cache pushCache; 427029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin 437029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin @VisibleForTesting 447029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin public AdbTarget(Log log, DeviceFilesystem deviceFilesystem, DeviceFileCache deviceFileCache) { 457029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin this.log = log; 467029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin this.deviceFilesystem = deviceFilesystem; 477029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin this.pushCache = 487029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin deviceFileCache == null ? null : new Md5Cache(log, "pushed", deviceFileCache); 490eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com } 500eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com 517029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin public static File defaultDeviceDir() { 52a3baab9f2576bcdebd5f16647bcb3f0f80621e58Neil Fuller return new File("/data/local/tmp/vogar"); 530eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com } 540eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com 55411cb1fb67241125203629229600ecbd821eb9c7Paul Duffin @Override protected ImmutableList<String> targetProcessPrefix() { 56411cb1fb67241125203629229600ecbd821eb9c7Paul Duffin return TARGET_PROCESS_PREFIX; 570eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com } 580eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com 59ab130e4323645a4ce682eb2b871b6b22ca2e5d3aNicolas Geoffray @Override public void await(File directory) { 607029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin waitForDevice(); 617029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin ensureDirectory(directory); 627029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin remount(); 637029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } 647029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin 657029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin private void waitForDevice() { 667029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin new Command.Builder(log) 677029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin .args("adb", "wait-for-device") 687029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin .permitNonZeroExitStatus(true) 697029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin .execute(); 707029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } 717029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin 727029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin /** 737029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin * Make sure the directory exists. 747029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin */ 757029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin private void ensureDirectory(File directory) { 767029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin String pathArgument = directory.getPath() + "/"; 777029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin if (pathArgument.equals("/sdcard/")) { 787029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin // /sdcard is a mount point. If it exists but is empty we do 797029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin // not want to use it. So we wait until it is not empty. 807029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin waitForNonEmptyDirectory(pathArgument, 5 * 60); 817029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } else { 827029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin Command command = new Command.Builder(log) 837029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin .args("adb", "shell", "ls", pathArgument) 847029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin .permitNonZeroExitStatus(true) 857029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin .build(); 867029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin List<String> output = command.execute(); 877029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin // TODO: We should avoid checking for the error message, and instead have 887029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin // the Command class understand a non-zero exit code from an adb shell command. 897029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin if (!output.isEmpty() 907029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin && output.get(0).equals(pathArgument + ": No such file or directory")) { 917029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin throw new RuntimeException("'" + pathArgument + "' does not exist on device"); 927029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } 937029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin // Otherwise the directory exists. 947029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } 957029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } 967029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin 977029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin private void remount() { 987029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin new Command(log, "adb", "remount").execute(); 997029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } 1007029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin 1017029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin private void waitForNonEmptyDirectory(String pathArgument, int timeoutSeconds) { 1027029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin final int millisPerSecond = 1000; 1037029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin final long start = System.currentTimeMillis(); 1047029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin final long deadline = start + (millisPerSecond * timeoutSeconds); 1057029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin 1067029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin while (true) { 1077029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin final int remainingSeconds = 1087029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin (int) ((deadline - System.currentTimeMillis()) / millisPerSecond); 1097029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin Command command = new Command.Builder(log) 1107029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin .args("adb", "shell", "ls", pathArgument) 1117029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin .permitNonZeroExitStatus(true) 1127029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin .build(); 1137029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin List<String> output; 1147029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin try { 1157029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin output = command.executeWithTimeout(remainingSeconds); 1167029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } catch (TimeoutException e) { 1177029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin throw new RuntimeException("Timed out after " + timeoutSeconds 1187029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin + " seconds waiting for " + pathArgument, e); 1197029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } 1207029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin try { 1217029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin Thread.sleep(millisPerSecond); 1227029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } catch (InterruptedException e) { 1237029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin throw new RuntimeException(e); 1247029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } 1257029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin 1267029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin // We just want any output. 1277029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin if (!output.isEmpty()) { 1287029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin return; 1297029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } 1307029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin 1317029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin log.warn("Waiting on " + pathArgument + " to be mounted "); 1327029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } 1330eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com } 1340eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com 1350eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com @Override public List<File> ls(File directory) throws FileNotFoundException { 1367029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin return deviceFilesystem.ls(directory); 1370eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com } 1380eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com 1390eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com @Override public String getDeviceUserName() { 1400eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com // The default environment doesn't include $USER, so dalvikvm doesn't set "user.name". 1417029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin // DeviceRuntime uses this to set "user.name" manually with -D. 1427029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin String line = new Command(log, "adb", "shell", "id").execute().get(0); 143cc4bfe8e88d686c6fc28624c2b6a7a7e434beb77Spencer Low // TODO: use 'id -un' when we don't need to support anything older than M 144cc4bfe8e88d686c6fc28624c2b6a7a7e434beb77Spencer Low Matcher m = Pattern.compile("^uid=\\d+\\((\\S+)\\) gid=\\d+\\(\\S+\\).*").matcher(line); 1450eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com return m.matches() ? m.group(1) : "root"; 1460eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com } 1470eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com 1480eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com @Override public void rm(File file) { 1497029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin new Command.Builder(log).args("adb", "shell", "rm", "-r", file.getPath()) 1507029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin // Note: When all supported versions of Android correctly return the exit code 1517029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin // from adb we can rely on the exit code to detect failure. Until then: no. 1527029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin .permitNonZeroExitStatus(true) 1537029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin .execute(); 1540eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com } 1550eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com 1560eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com @Override public void mkdirs(File file) { 1577029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin deviceFilesystem.mkdirs(file); 1580eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com } 1590eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com 1600eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com @Override public void forwardTcp(int port) { 1617029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin new Command(log, "adb", "forward", "tcp:" + port, "tcp:" + port).execute(); 1620eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com } 1630eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com 1640eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com @Override public void push(File local, File remote) { 1657029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin Command fallback = new Command(log, "adb", "push", local.getPath(), remote.getPath()); 1667029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin deviceFilesystem.mkdirs(remote.getParentFile()); 1677029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin 1687029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin // don't yet cache directories (only used by jtreg tests) 1697029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin if (pushCache != null && local.isFile()) { 1707029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin String key = pushCache.makeKey(local); 1717029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin boolean cacheHit = pushCache.getFromCache(remote, key); 1727029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin if (cacheHit) { 1737029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin log.verbose("device cache hit for " + local); 1747029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin return; 1757029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } 1767029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin fallback.execute(); 1777029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin pushCache.insert(key, remote); 1787029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } else { 1797029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin fallback.execute(); 1807029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin } 1810eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com } 1820eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com 1830eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com @Override public void pull(File remote, File local) { 1847029503206e4c89fe167c3389c1062d89cf35c52Paul Duffin new Command(log, "adb", "pull", remote.getPath(), local.getPath()).execute(); 1850eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com } 1860eb0936e90bf11463c8ee937ca996d7bd654a098jessewilson@google.com} 187