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 17 package com.android.tradefed.util; 18 19 import com.android.tradefed.device.CollectingOutputReceiver; 20 import com.android.tradefed.device.DeviceNotAvailableException; 21 import com.android.tradefed.device.ITestDevice; 22 import com.android.tradefed.log.LogUtil.CLog; 23 24 import java.util.concurrent.Callable; 25 import java.util.concurrent.ExecutionException; 26 import java.util.concurrent.ExecutorService; 27 import java.util.concurrent.Future; 28 import java.util.concurrent.TimeUnit; 29 import java.util.concurrent.TimeoutException; 30 31 /** 32 * Contains utility methods and classes for concurrent device side command execution 33 * <p> 34 * Use {@link ExecutorService} to run commands implemented as {@link ShellCommandCallable}, and use 35 * {@link #joinFuture(String, Future, long)} for synchronization against the {@link Future} as 36 * returned by {@link ExecutorService} for the command execution. 37 */ 38 public class DeviceConcurrentUtil { 39 40 /* private constructor for singleton */ DeviceConcurrentUtil()41 private DeviceConcurrentUtil() {} 42 43 /** 44 * Convenience method to join current thread on the <code>task</code> 45 * <p> 46 * {@link DeviceNotAvailableException} and {@link TimeoutException} occurred during execution 47 * are passed through transparently, others are logged as error but not otherwise handled. 48 * @param taskDesc description of task for logging purpose 49 * @param task {@link Future} representing the task to join 50 * @param timeout timeout for waiting on the task 51 * @return The result of the task with the template type. 52 * @throws DeviceNotAvailableException 53 * @throws TimeoutException 54 */ joinFuture(String taskDesc, Future<T> task, long timeout)55 public static <T> T joinFuture(String taskDesc, Future<T> task, long timeout) 56 throws DeviceNotAvailableException, TimeoutException { 57 try { 58 T ret = task.get(timeout, TimeUnit.MILLISECONDS); 59 return ret; 60 } catch (InterruptedException e) { 61 CLog.e("interrupted while executing " + taskDesc); 62 } catch (ExecutionException e) { 63 Throwable t = e.getCause(); 64 if (t instanceof DeviceNotAvailableException) { 65 // passthru 66 throw (DeviceNotAvailableException)t; 67 } else { 68 CLog.e("%s while executing %s", t.getClass().getSimpleName(), taskDesc); 69 CLog.e(t); 70 } 71 } 72 return null; 73 } 74 75 /** 76 * A {@link Callable} that wraps the details of executing shell command on 77 * an {@link ITestDevice}. 78 * <p> 79 * Must implement {@link #processOutput(String)} to process the command 80 * output and determine return of the <code>Callable</code> 81 * 82 * @param <V> passthru of the {@link Callable} return type, see 83 * {@link Callable} 84 */ 85 public static abstract class ShellCommandCallable<V> implements Callable<V> { 86 private String mCommand; 87 private long mTimeout; 88 private ITestDevice mDevice; 89 ShellCommandCallable()90 public ShellCommandCallable() { 91 // do nothing for default 92 } 93 ShellCommandCallable(ITestDevice device, String command, long timeout)94 public ShellCommandCallable(ITestDevice device, String command, long timeout) { 95 this(); 96 mCommand = command; 97 mTimeout = timeout; 98 mDevice = device; 99 } 100 setCommand(String command)101 public ShellCommandCallable<V> setCommand(String command) { 102 mCommand = command; 103 return this; 104 } 105 setTimeout(long timeout)106 public ShellCommandCallable<V> setTimeout(long timeout) { 107 mTimeout = timeout; 108 return this; 109 } 110 setDevice(ITestDevice device)111 public ShellCommandCallable<V> setDevice(ITestDevice device) { 112 mDevice = device; 113 return this; 114 } 115 116 @Override call()117 public V call() throws Exception { 118 CollectingOutputReceiver receiver = new CollectingOutputReceiver(); 119 mDevice.executeShellCommand(mCommand, receiver, mTimeout, TimeUnit.MILLISECONDS, 1); 120 String output = receiver.getOutput(); 121 CLog.v("raw output for \"%s\"\n%s", mCommand, output); 122 return processOutput(output); 123 } 124 processOutput(String output)125 public abstract V processOutput(String output); 126 } 127 128 }