1 /* 2 * Copyright (C) 2011 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.command.remote; 17 18 import com.android.ddmlib.Log; 19 20 import org.json.JSONException; 21 22 import java.io.BufferedReader; 23 import java.io.IOException; 24 import java.io.InputStreamReader; 25 import java.io.PrintWriter; 26 import java.net.InetAddress; 27 import java.net.Socket; 28 import java.net.UnknownHostException; 29 import java.util.List; 30 31 /** 32 * Class for sending remote commands to another TF process. 33 * <p/> 34 * Currently uses JSON-encoded data sent via sockets. 35 */ 36 public class RemoteClient implements IRemoteClient { 37 38 // choose an arbitrary default port that according to the interweb is not used by another 39 // popular program 40 public static final int DEFAULT_PORT = 30103; 41 42 private static final String TAG = RemoteClient.class.getSimpleName(); 43 private final Socket mSocket; 44 private final PrintWriter mWriter; 45 private final BufferedReader mReader; 46 47 /** 48 * Initialize the {@RemoteClient}, and instruct it to connect to the given port 49 * on localhost. 50 * 51 * @param port the tcp/ip port number 52 * @throws IOException 53 * @throws UnknownHostException 54 */ 55 private RemoteClient(int port) throws UnknownHostException, IOException { 56 this(InetAddress.getLocalHost().getHostName(), port); 57 } 58 59 /** 60 * Initialize the {@RemoteClient}, and instruct it to connect to the given hostname and port. 61 * 62 * @param hostName to connect to 63 * @param port the tcp/ip port number 64 * @throws IOException 65 * @throws UnknownHostException 66 */ 67 private RemoteClient(String hostName, int port) throws UnknownHostException, IOException { 68 mSocket = new Socket(hostName, port); 69 mWriter = new PrintWriter(mSocket.getOutputStream(), true); 70 mReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream())); 71 } 72 73 /** 74 * Send the given operation to the remote TF. 75 * 76 * @param op the {@link RemoteOperation} to send 77 * @throws RemoteException if failed to perform operation 78 */ 79 private synchronized <T> T sendOperation(RemoteOperation<T> op) throws RemoteException { 80 try { 81 Log.d(TAG, String.format("Sending remote op %s", op.getType())); 82 mWriter.println(op.pack()); 83 String response = mReader.readLine(); 84 if (response == null) { 85 throw new RemoteException("no response from remote manager"); 86 } 87 return op.unpackResponseFromString(response); 88 } catch (IOException e) { 89 throw new RemoteException(e.getMessage(), e); 90 } catch (JSONException e) { 91 throw new RemoteException(e.getMessage(), e); 92 } 93 } 94 95 /** 96 * Helper method to create a {@link RemoteClient} connected to given port 97 * 98 * @param port the tcp/ip port 99 * @return the {@link RemoteClient} 100 * @throws RemoteException if failed to connect 101 */ 102 public static IRemoteClient connect(int port) throws RemoteException { 103 try { 104 return new RemoteClient(port); 105 } catch (IOException e) { 106 throw new RemoteException(e); 107 } 108 } 109 110 /** 111 * Helper method to create a {@link RemoteClient} connected to given host and port 112 * 113 * @param hostname the host name 114 * @param port the tcp/ip port 115 * @return the {@link RemoteClient} 116 * @throws RemoteException if failed to connect 117 */ 118 public static IRemoteClient connect(String hostname, int port) throws RemoteException { 119 try { 120 return new RemoteClient(hostname, port); 121 } catch (IOException e) { 122 throw new RemoteException(e); 123 } 124 } 125 126 /** 127 * Helper method to create a {@link RemoteClient} connected to default port 128 * 129 * @return the {@link RemoteClient} 130 * @throws RemoteException if failed to connect 131 */ 132 public static IRemoteClient connect() throws RemoteException { 133 return connect(DEFAULT_PORT); 134 } 135 136 /** 137 * {@inheritDoc} 138 */ 139 @Override 140 public void sendAllocateDevice(String serial) throws RemoteException { 141 sendOperation(new AllocateDeviceOp(serial)); 142 } 143 144 /** 145 * {@inheritDoc} 146 */ 147 @Override 148 public void sendFreeDevice(String serial) throws RemoteException { 149 sendOperation(new FreeDeviceOp(serial)); 150 } 151 152 /** 153 * {@inheritDoc} 154 */ 155 @Override 156 public void sendAddCommand(long totalTime, String... commandArgs) throws RemoteException { 157 sendOperation(new AddCommandOp(totalTime, commandArgs)); 158 } 159 160 /** 161 * {@inheritDoc} 162 */ 163 @Override 164 public void sendAddCommandFile(String commandFile, List<String> extraArgs) 165 throws RemoteException { 166 sendOperation(new AddCommandFileOp(commandFile, extraArgs)); 167 } 168 169 /** 170 * {@inheritDoc} 171 */ 172 @Deprecated 173 @Override 174 public void sendClose() throws RemoteException { 175 sendOperation(new CloseOp()); 176 } 177 178 /** 179 * {@inheritDoc} 180 */ 181 @Override 182 public void sendStartHandover(int port) throws RemoteException { 183 sendOperation(new StartHandoverOp(port)); 184 } 185 186 /** 187 * {@inheritDoc} 188 */ 189 @Override 190 public void sendHandoverInitComplete() throws RemoteException { 191 sendOperation(new HandoverInitCompleteOp()); 192 } 193 194 /** 195 * {@inheritDoc} 196 */ 197 @Override 198 public void sendHandoverComplete() throws RemoteException { 199 sendOperation(new HandoverCompleteOp()); 200 } 201 202 /** 203 * {@inheritDoc} 204 */ 205 @Override 206 public List<DeviceDescriptor> sendListDevices() throws RemoteException { 207 return sendOperation(new ListDevicesOp()); 208 } 209 210 /** 211 * {@inheritDoc} 212 */ 213 @Override 214 public void sendExecCommand(String serial, String[] commandArgs) throws RemoteException { 215 sendOperation(new ExecCommandOp(serial, commandArgs)); 216 } 217 218 /** 219 * {@inheritDoc} 220 */ 221 @Override 222 public void sendGetLastCommandResult(String serial, ICommandResultHandler handler) 223 throws RemoteException { 224 CommandResult r = sendOperation(new GetLastCommandResultOp(serial)); 225 switch (r.getStatus()) { 226 case EXECUTING: 227 handler.stillRunning(); 228 break; 229 case INVOCATION_ERROR: 230 handler.failure(r.getInvocationErrorDetails(), r.getFreeDeviceState(), 231 r.getRunMetrics()); 232 break; 233 case INVOCATION_SUCCESS: 234 handler.success(r.getRunMetrics()); 235 break; 236 case NO_ACTIVE_COMMAND: 237 handler.noActiveCommand(); 238 break; 239 case NOT_ALLOCATED: 240 handler.notAllocated(); 241 break; 242 default: 243 throw new RemoteException("unrecognized status " + r.getStatus().name()); 244 } 245 } 246 247 /** 248 * {@inheritDoc} 249 */ 250 @Override 251 public synchronized void close() { 252 if (mSocket != null) { 253 try { 254 mSocket.close(); 255 } catch (IOException e) { 256 Log.w(TAG, String.format("exception closing socket: %s", e.toString())); 257 } 258 } 259 if (mWriter != null) { 260 mWriter.close(); 261 } 262 } 263 } 264