blob: 9ca8b401ba0c9c813d059344032c4adec47914e8 [file] [log] [blame]
Brett Chabot33513a52011-11-09 18:20:04 -08001/*
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 */
Brett Chabot9c27c902013-09-27 11:00:56 -070016package com.android.tradefed.command.remote;
Brett Chabot33513a52011-11-09 18:20:04 -080017
Brett Chabot33513a52011-11-09 18:20:04 -080018import com.android.ddmlib.Log.LogLevel;
Brett Chabot9c27c902013-09-27 11:00:56 -070019import com.android.tradefed.command.ICommandScheduler;
20import com.android.tradefed.command.remote.RemoteOperation.RemoteException;
Brett Chabot33513a52011-11-09 18:20:04 -080021import com.android.tradefed.device.IDeviceManager;
22import com.android.tradefed.device.IDeviceManager.FreeDeviceState;
23import com.android.tradefed.device.ITestDevice;
24import com.android.tradefed.log.LogUtil.CLog;
25import com.android.tradefed.util.ArrayUtil;
26
27import java.io.BufferedReader;
28import java.io.IOException;
29import java.io.InputStreamReader;
30import java.io.PrintWriter;
31import java.net.ServerSocket;
32import java.net.Socket;
Brett Chabot33513a52011-11-09 18:20:04 -080033
34/**
Brett Chabot9c27c902013-09-27 11:00:56 -070035 * Class that receives {@link RemoteOperation}s via a socket.
Brett Chabot33513a52011-11-09 18:20:04 -080036 * <p/>
37 * Currently accepts only one remote connection at one time, and processes incoming commands
38 * serially.
39 * <p/>
40 * Usage:
41 * <pre>
42 * RemoteManager r = new RemoteManager(deviceMgr, scheduler);
43 * r.start();
44 * int port = r.getPort();
45 * ... inform client of port to use. Shuts down when instructed by client or on #cancel()
46 * </pre>
47 */
48public class RemoteManager extends Thread {
49
Brett Chabot33513a52011-11-09 18:20:04 -080050 private ServerSocket mServerSocket = null;
51 private boolean mCancel = false;
52 private final IDeviceManager mDeviceManager;
53 private final ICommandScheduler mScheduler;
Brett Chabot33513a52011-11-09 18:20:04 -080054
55 /**
56 * Creates a {@link RemoteManager}.
57 *
58 * @param manager the {@link IDeviceManager} to use to allocate and free devices.
59 * @param scheduler the {@link ICommandScheduler} to use to schedule commands.
60 */
61 public RemoteManager(IDeviceManager manager, ICommandScheduler scheduler) {
62 mDeviceManager = manager;
63 mScheduler = scheduler;
64 }
65
66 /**
67 * The main thread body of the remote manager.
68 * <p/>
69 * Creates a server socket, and waits for client connections.
70 */
71 @Override
72 public void run() {
73 synchronized (this) {
74 try {
75 mServerSocket = new ServerSocket(0);
76 } catch (IOException e) {
77 CLog.e("Failed to open server socket: %s", e);
78 return;
79 } finally {
80 // notify any listeners that the socket has been created
81 notifyAll();
82 }
83 }
84 try {
85 processClientConnections(mServerSocket);
86 } finally {
87 freeAllDevices();
88 closeSocket(mServerSocket);
89 }
90 }
91
92 /**
93 * Gets the socket port the remote manager is listening on, blocking for a short time if
94 * necessary.
95 * <p/>
96 * {@link #start()} should be called before this method.
97 * @return
98 */
99 public synchronized int getPort() {
100 if (mServerSocket == null) {
101 try {
102 wait(10*1000);
103 } catch (InterruptedException e) {
104 // ignore
105 }
106 }
107 if (mServerSocket == null) {
108 return -1;
109 }
110 return mServerSocket.getLocalPort();
111 }
112
113 private void processClientConnections(ServerSocket serverSocket) {
114 while (!mCancel) {
115 Socket clientSocket = null;
116 BufferedReader in = null;
117 PrintWriter out = null;
118 try {
119 clientSocket = serverSocket.accept();
120 in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
121 out = new PrintWriter(clientSocket.getOutputStream(), true);
Brett Chabot9c27c902013-09-27 11:00:56 -0700122 processClientOperations(in, out);
Brett Chabot33513a52011-11-09 18:20:04 -0800123 } catch (IOException e) {
124 CLog.e("Failed to accept connection: %s", e);
125 } finally {
126 closeReader(in);
127 closeWriter(out);
128 closeSocket(clientSocket);
129 }
130 }
131 }
132
Brett Chabot9c27c902013-09-27 11:00:56 -0700133 private void processClientOperations(BufferedReader in, PrintWriter out) throws IOException {
Brett Chabot33513a52011-11-09 18:20:04 -0800134 String line = null;
135 while ((line = in.readLine()) != null && !mCancel) {
136 boolean result = false;
Brett Chabot9c27c902013-09-27 11:00:56 -0700137 RemoteOperation rc;
138 try {
139 rc = RemoteOperation.createRemoteOpFromString(line);
140 switch (rc.getType()) {
141 case ADD_COMMAND:
142 result = processAdd((AddCommandOp)rc);
143 break;
144 case CLOSE:
145 result = processClose((CloseOp)rc);
146 break;
147 case ALLOCATE_DEVICE:
148 result = processAllocate((AllocateDeviceOp)rc);
149 break;
150 case FREE_DEVICE:
151 result = processFree((FreeDeviceOp)rc);
152 break;
153 }
154 } catch (RemoteException e) {
155 CLog.e("Failed to handle remote command", e);
Brett Chabot33513a52011-11-09 18:20:04 -0800156 }
157 sendAck(result, out);
158 }
159 }
160
Brett Chabot9c27c902013-09-27 11:00:56 -0700161 private boolean processAllocate(AllocateDeviceOp c) {
162 ITestDevice allocatedDevice = mDeviceManager.forceAllocateDevice(c.mDeviceSerial);
Brett Chabot33513a52011-11-09 18:20:04 -0800163 if (allocatedDevice != null) {
Brett Chabot9c27c902013-09-27 11:00:56 -0700164 CLog.logAndDisplay(LogLevel.INFO,
165 "Allocating device %s that is still in use by remote tradefed",
166 c.mDeviceSerial);
167 DeviceTracker.getInstance().allocateDevice(allocatedDevice);
Brett Chabot33513a52011-11-09 18:20:04 -0800168 return true;
Brett Chabot33513a52011-11-09 18:20:04 -0800169 }
Brett Chabot9c27c902013-09-27 11:00:56 -0700170 CLog.e("Failed to allocate device %s", c.mDeviceSerial);
171 return false;
Brett Chabot33513a52011-11-09 18:20:04 -0800172 }
173
Brett Chabot9c27c902013-09-27 11:00:56 -0700174 private boolean processFree(FreeDeviceOp c) {
175 if (FreeDeviceOp.ALL_DEVICES.equals(c.mDeviceSerial)) {
Brett Chabot33513a52011-11-09 18:20:04 -0800176 freeAllDevices();
177 return true;
178 } else {
Brett Chabot9c27c902013-09-27 11:00:56 -0700179 ITestDevice d = DeviceTracker.getInstance().freeDevice(c.mDeviceSerial);
Brett Chabot33513a52011-11-09 18:20:04 -0800180 if (d != null) {
Brett Chabot9c27c902013-09-27 11:00:56 -0700181 CLog.logAndDisplay(LogLevel.INFO,
182 "Freeing device %s no longer in use by remote tradefed",
183 c.mDeviceSerial);
Brett Chabot33513a52011-11-09 18:20:04 -0800184 mDeviceManager.freeDevice(d, FreeDeviceState.AVAILABLE);
185 return true;
186 } else {
Brett Chabot9c27c902013-09-27 11:00:56 -0700187 CLog.w("Could not find device to free %s", c.mDeviceSerial);
Brett Chabot33513a52011-11-09 18:20:04 -0800188 }
189 }
190 return false;
191 }
192
Brett Chabot9c27c902013-09-27 11:00:56 -0700193 boolean processAdd(AddCommandOp c) {
194 CLog.logAndDisplay(LogLevel.INFO, "Adding command '%s'", ArrayUtil.join(" ",
195 c.mCommandArgs));
196 return mScheduler.addCommand(c.mCommandArgs, c.mTotalTime);
197 }
198
199 private boolean processClose(CloseOp rc) {
200 cancel();
201 return true;
Brett Chabot33513a52011-11-09 18:20:04 -0800202 }
203
204 private void freeAllDevices() {
Brett Chabot9c27c902013-09-27 11:00:56 -0700205 for (ITestDevice d : DeviceTracker.getInstance().freeAll()) {
206 CLog.logAndDisplay(LogLevel.INFO,
207 "Freeing device %s no longer in use by remote tradefed",
208 d.getSerialNumber());
Brett Chabot33513a52011-11-09 18:20:04 -0800209 mDeviceManager.freeDevice(d, FreeDeviceState.AVAILABLE);
210 }
Brett Chabot33513a52011-11-09 18:20:04 -0800211 }
212
213 private void sendAck(boolean result, PrintWriter out) {
214 out.println(result);
215 }
216
217 /**
218 * Cancel the remote manager.
219 */
220 public synchronized void cancel() {
221 if (!mCancel) {
222 mCancel = true;
Brett Chabot9c27c902013-09-27 11:00:56 -0700223 CLog.logAndDisplay(LogLevel.INFO, "Closing remote manager");
Brett Chabot33513a52011-11-09 18:20:04 -0800224 }
225 }
226
227 private void closeSocket(ServerSocket serverSocket) {
228 if (serverSocket != null) {
229 try {
230 serverSocket.close();
231 } catch (IOException e) {
232 // ignore
233 }
234 }
235 }
236
237 private void closeSocket(Socket clientSocket) {
238 if (clientSocket != null) {
239 try {
240 clientSocket.close();
241 } catch (IOException e) {
242 // ignore
243 e.printStackTrace();
244 }
245 }
246 }
247
248 private void closeReader(BufferedReader in) {
249 if (in != null) {
250 try {
251 in.close();
252 } catch (IOException e) {
253 // ignore
254 }
255 }
256 }
257
258 private void closeWriter(PrintWriter out) {
259 if (out != null) {
260 out.close();
261 }
262 }
263
264 /**
265 * @return <code>true</code> if a cancel has been requested
266 */
267 public boolean isCanceled() {
268 return mCancel;
269 }
270}