xref: /aosp_15_r20/external/pigweed/pw_transfer/integration_test/JavaClient.java (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2022 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker import com.google.common.collect.ImmutableList;
16*61c4878aSAndroid Build Coastguard Worker import com.google.protobuf.TextFormat;
17*61c4878aSAndroid Build Coastguard Worker import dev.pigweed.pw_hdlc.Decoder;
18*61c4878aSAndroid Build Coastguard Worker import dev.pigweed.pw_hdlc.Encoder;
19*61c4878aSAndroid Build Coastguard Worker import dev.pigweed.pw_hdlc.Frame;
20*61c4878aSAndroid Build Coastguard Worker import dev.pigweed.pw_log.Logger;
21*61c4878aSAndroid Build Coastguard Worker import dev.pigweed.pw_rpc.Channel;
22*61c4878aSAndroid Build Coastguard Worker import dev.pigweed.pw_rpc.ChannelOutputException;
23*61c4878aSAndroid Build Coastguard Worker import dev.pigweed.pw_rpc.Client;
24*61c4878aSAndroid Build Coastguard Worker import dev.pigweed.pw_rpc.Status;
25*61c4878aSAndroid Build Coastguard Worker import dev.pigweed.pw_transfer.ProtocolVersion;
26*61c4878aSAndroid Build Coastguard Worker import dev.pigweed.pw_transfer.TransferClient;
27*61c4878aSAndroid Build Coastguard Worker import dev.pigweed.pw_transfer.TransferError;
28*61c4878aSAndroid Build Coastguard Worker import dev.pigweed.pw_transfer.TransferService;
29*61c4878aSAndroid Build Coastguard Worker import dev.pigweed.pw_transfer.TransferTimeoutSettings;
30*61c4878aSAndroid Build Coastguard Worker import java.io.IOException;
31*61c4878aSAndroid Build Coastguard Worker import java.io.InputStream;
32*61c4878aSAndroid Build Coastguard Worker import java.io.OutputStream;
33*61c4878aSAndroid Build Coastguard Worker import java.net.Socket;
34*61c4878aSAndroid Build Coastguard Worker import java.net.SocketException;
35*61c4878aSAndroid Build Coastguard Worker import java.nio.ByteBuffer;
36*61c4878aSAndroid Build Coastguard Worker import java.nio.charset.StandardCharsets;
37*61c4878aSAndroid Build Coastguard Worker import java.nio.file.Files;
38*61c4878aSAndroid Build Coastguard Worker import java.nio.file.Path;
39*61c4878aSAndroid Build Coastguard Worker import java.nio.file.Paths;
40*61c4878aSAndroid Build Coastguard Worker import java.util.concurrent.ExecutionException;
41*61c4878aSAndroid Build Coastguard Worker import pw.transfer.ConfigProtos;
42*61c4878aSAndroid Build Coastguard Worker import pw.transfer.ConfigProtos.TransferAction;
43*61c4878aSAndroid Build Coastguard Worker 
44*61c4878aSAndroid Build Coastguard Worker public class JavaClient {
45*61c4878aSAndroid Build Coastguard Worker   private static final String SERVICE = "pw.transfer.Transfer";
46*61c4878aSAndroid Build Coastguard Worker   private static final Logger logger = Logger.forClass(Client.class);
47*61c4878aSAndroid Build Coastguard Worker 
48*61c4878aSAndroid Build Coastguard Worker   private static final int CHANNEL_ID = 1;
49*61c4878aSAndroid Build Coastguard Worker   private static final long RPC_HDLC_ADDRESS = 'R';
50*61c4878aSAndroid Build Coastguard Worker   private static final String HOSTNAME = "localhost";
51*61c4878aSAndroid Build Coastguard Worker 
52*61c4878aSAndroid Build Coastguard Worker   // This is the maximum size of the socket send buffers. Ideally, this is set
53*61c4878aSAndroid Build Coastguard Worker   // to the lowest allowed value to minimize buffering between the proxy and
54*61c4878aSAndroid Build Coastguard Worker   // clients so rate limiting causes the client to block and wait for the
55*61c4878aSAndroid Build Coastguard Worker   // integration test proxy to drain rather than allowing OS buffers to backlog
56*61c4878aSAndroid Build Coastguard Worker   // large quantities of data.
57*61c4878aSAndroid Build Coastguard Worker   //
58*61c4878aSAndroid Build Coastguard Worker   // Note that the OS may chose to not strictly follow this requested buffer
59*61c4878aSAndroid Build Coastguard Worker   // size. Still, setting this value to be as small as possible does reduce
60*61c4878aSAndroid Build Coastguard Worker   // bufer sizes significantly enough to better reflect typical inter-device
61*61c4878aSAndroid Build Coastguard Worker   // communication.
62*61c4878aSAndroid Build Coastguard Worker   //
63*61c4878aSAndroid Build Coastguard Worker   // For this to be effective, servers should also configure their sockets to a
64*61c4878aSAndroid Build Coastguard Worker   // smaller receive buffer size.
65*61c4878aSAndroid Build Coastguard Worker   private static final int MAX_SOCKET_SEND_BUFFER_SIZE = 1;
66*61c4878aSAndroid Build Coastguard Worker 
67*61c4878aSAndroid Build Coastguard Worker   private final HdlcRpcChannelOutput channelOutput;
68*61c4878aSAndroid Build Coastguard Worker   private final Client rpcClient;
69*61c4878aSAndroid Build Coastguard Worker   private final HdlcParseThread parseThread;
70*61c4878aSAndroid Build Coastguard Worker 
JavaClient(OutputStream writer, InputStream reader)71*61c4878aSAndroid Build Coastguard Worker   public JavaClient(OutputStream writer, InputStream reader) {
72*61c4878aSAndroid Build Coastguard Worker     this.channelOutput = new HdlcRpcChannelOutput(writer, RPC_HDLC_ADDRESS);
73*61c4878aSAndroid Build Coastguard Worker 
74*61c4878aSAndroid Build Coastguard Worker     this.rpcClient = Client.create(ImmutableList.of(new Channel(CHANNEL_ID, this.channelOutput)),
75*61c4878aSAndroid Build Coastguard Worker         ImmutableList.of(TransferService.get()));
76*61c4878aSAndroid Build Coastguard Worker 
77*61c4878aSAndroid Build Coastguard Worker     this.parseThread = new HdlcParseThread(reader, this.rpcClient);
78*61c4878aSAndroid Build Coastguard Worker   }
79*61c4878aSAndroid Build Coastguard Worker 
startClient()80*61c4878aSAndroid Build Coastguard Worker   void startClient() {
81*61c4878aSAndroid Build Coastguard Worker     parseThread.start();
82*61c4878aSAndroid Build Coastguard Worker   }
83*61c4878aSAndroid Build Coastguard Worker 
getRpcClient()84*61c4878aSAndroid Build Coastguard Worker   Client getRpcClient() {
85*61c4878aSAndroid Build Coastguard Worker     return this.rpcClient;
86*61c4878aSAndroid Build Coastguard Worker   }
87*61c4878aSAndroid Build Coastguard Worker 
88*61c4878aSAndroid Build Coastguard Worker   private class HdlcRpcChannelOutput implements Channel.Output {
89*61c4878aSAndroid Build Coastguard Worker     private final OutputStream writer;
90*61c4878aSAndroid Build Coastguard Worker     private final long address;
91*61c4878aSAndroid Build Coastguard Worker 
HdlcRpcChannelOutput(OutputStream writer, long address)92*61c4878aSAndroid Build Coastguard Worker     public HdlcRpcChannelOutput(OutputStream writer, long address) {
93*61c4878aSAndroid Build Coastguard Worker       this.writer = writer;
94*61c4878aSAndroid Build Coastguard Worker       this.address = address;
95*61c4878aSAndroid Build Coastguard Worker     }
96*61c4878aSAndroid Build Coastguard Worker 
send(byte[] packet)97*61c4878aSAndroid Build Coastguard Worker     public void send(byte[] packet) throws ChannelOutputException {
98*61c4878aSAndroid Build Coastguard Worker       try {
99*61c4878aSAndroid Build Coastguard Worker         Encoder.writeUiFrame(this.address, ByteBuffer.wrap(packet), this.writer);
100*61c4878aSAndroid Build Coastguard Worker       } catch (IOException e) {
101*61c4878aSAndroid Build Coastguard Worker         throw new ChannelOutputException("Failed to write HDLC UI Frame", e);
102*61c4878aSAndroid Build Coastguard Worker       }
103*61c4878aSAndroid Build Coastguard Worker     }
104*61c4878aSAndroid Build Coastguard Worker   }
105*61c4878aSAndroid Build Coastguard Worker 
106*61c4878aSAndroid Build Coastguard Worker   private class HdlcParseThread extends Thread {
107*61c4878aSAndroid Build Coastguard Worker     private final InputStream reader;
108*61c4878aSAndroid Build Coastguard Worker     private final RpcOnComplete frame_handler;
109*61c4878aSAndroid Build Coastguard Worker     private final Decoder decoder;
110*61c4878aSAndroid Build Coastguard Worker 
111*61c4878aSAndroid Build Coastguard Worker     private class RpcOnComplete implements Decoder.OnCompleteFrame {
112*61c4878aSAndroid Build Coastguard Worker       private final Client rpc_client;
113*61c4878aSAndroid Build Coastguard Worker 
RpcOnComplete(Client rpc_client)114*61c4878aSAndroid Build Coastguard Worker       public RpcOnComplete(Client rpc_client) {
115*61c4878aSAndroid Build Coastguard Worker         this.rpc_client = rpc_client;
116*61c4878aSAndroid Build Coastguard Worker       }
117*61c4878aSAndroid Build Coastguard Worker 
onCompleteFrame(Frame frame)118*61c4878aSAndroid Build Coastguard Worker       public void onCompleteFrame(Frame frame) {
119*61c4878aSAndroid Build Coastguard Worker         if (frame.getAddress() == RPC_HDLC_ADDRESS) {
120*61c4878aSAndroid Build Coastguard Worker           this.rpc_client.processPacket(frame.getPayload());
121*61c4878aSAndroid Build Coastguard Worker         }
122*61c4878aSAndroid Build Coastguard Worker       }
123*61c4878aSAndroid Build Coastguard Worker     }
124*61c4878aSAndroid Build Coastguard Worker 
HdlcParseThread(InputStream reader, Client rpc_client)125*61c4878aSAndroid Build Coastguard Worker     public HdlcParseThread(InputStream reader, Client rpc_client) {
126*61c4878aSAndroid Build Coastguard Worker       this.reader = reader;
127*61c4878aSAndroid Build Coastguard Worker       this.frame_handler = new RpcOnComplete(rpc_client);
128*61c4878aSAndroid Build Coastguard Worker       this.decoder = new Decoder(this.frame_handler);
129*61c4878aSAndroid Build Coastguard Worker     }
130*61c4878aSAndroid Build Coastguard Worker 
run()131*61c4878aSAndroid Build Coastguard Worker     public void run() {
132*61c4878aSAndroid Build Coastguard Worker       while (true) {
133*61c4878aSAndroid Build Coastguard Worker         int val = 0;
134*61c4878aSAndroid Build Coastguard Worker         try {
135*61c4878aSAndroid Build Coastguard Worker           val = this.reader.read();
136*61c4878aSAndroid Build Coastguard Worker         } catch (IOException e) {
137*61c4878aSAndroid Build Coastguard Worker           logger.atSevere().log("HDLC parse thread read failed");
138*61c4878aSAndroid Build Coastguard Worker           System.exit(1);
139*61c4878aSAndroid Build Coastguard Worker         }
140*61c4878aSAndroid Build Coastguard Worker         this.decoder.process((byte) val);
141*61c4878aSAndroid Build Coastguard Worker       }
142*61c4878aSAndroid Build Coastguard Worker     }
143*61c4878aSAndroid Build Coastguard Worker   }
144*61c4878aSAndroid Build Coastguard Worker 
ParseConfigFrom(InputStream reader)145*61c4878aSAndroid Build Coastguard Worker   public static ConfigProtos.ClientConfig ParseConfigFrom(InputStream reader) throws IOException {
146*61c4878aSAndroid Build Coastguard Worker     byte[] buffer = new byte[reader.available()];
147*61c4878aSAndroid Build Coastguard Worker     reader.read(buffer);
148*61c4878aSAndroid Build Coastguard Worker     ConfigProtos.ClientConfig.Builder config_builder = ConfigProtos.ClientConfig.newBuilder();
149*61c4878aSAndroid Build Coastguard Worker     TextFormat.merge(new String(buffer, StandardCharsets.UTF_8), config_builder);
150*61c4878aSAndroid Build Coastguard Worker     if (config_builder.getChunkTimeoutMs() == 0) {
151*61c4878aSAndroid Build Coastguard Worker       throw new AssertionError("chunk_timeout_ms may not be 0");
152*61c4878aSAndroid Build Coastguard Worker     }
153*61c4878aSAndroid Build Coastguard Worker     if (config_builder.getInitialChunkTimeoutMs() == 0) {
154*61c4878aSAndroid Build Coastguard Worker       throw new AssertionError("initial_chunk_timeout_ms may not be 0");
155*61c4878aSAndroid Build Coastguard Worker     }
156*61c4878aSAndroid Build Coastguard Worker     if (config_builder.getMaxRetries() == 0) {
157*61c4878aSAndroid Build Coastguard Worker       throw new AssertionError("max_retries may not be 0");
158*61c4878aSAndroid Build Coastguard Worker     }
159*61c4878aSAndroid Build Coastguard Worker     if (config_builder.getMaxLifetimeRetries() == 0) {
160*61c4878aSAndroid Build Coastguard Worker       throw new AssertionError("max_lifetime_retries may not be 0");
161*61c4878aSAndroid Build Coastguard Worker     }
162*61c4878aSAndroid Build Coastguard Worker     return config_builder.build();
163*61c4878aSAndroid Build Coastguard Worker   }
164*61c4878aSAndroid Build Coastguard Worker 
ReadFromServer(int resourceId, Path fileName, TransferClient client, Status expected_status, int initial_offset)165*61c4878aSAndroid Build Coastguard Worker   public static void ReadFromServer(int resourceId,
166*61c4878aSAndroid Build Coastguard Worker       Path fileName,
167*61c4878aSAndroid Build Coastguard Worker       TransferClient client,
168*61c4878aSAndroid Build Coastguard Worker       Status expected_status,
169*61c4878aSAndroid Build Coastguard Worker       int initial_offset) {
170*61c4878aSAndroid Build Coastguard Worker     byte[] data;
171*61c4878aSAndroid Build Coastguard Worker     try {
172*61c4878aSAndroid Build Coastguard Worker       data = client.read(resourceId, initial_offset).get();
173*61c4878aSAndroid Build Coastguard Worker     } catch (ExecutionException e) {
174*61c4878aSAndroid Build Coastguard Worker       if (((TransferError) e.getCause()).status() != expected_status) {
175*61c4878aSAndroid Build Coastguard Worker         throw new AssertionError("Unexpected transfer read failure", e);
176*61c4878aSAndroid Build Coastguard Worker       }
177*61c4878aSAndroid Build Coastguard Worker       // Expected failure occurred, skip trying to write the data knowing that
178*61c4878aSAndroid Build Coastguard Worker       // it is missing.
179*61c4878aSAndroid Build Coastguard Worker       return;
180*61c4878aSAndroid Build Coastguard Worker     } catch (InterruptedException e) {
181*61c4878aSAndroid Build Coastguard Worker       throw new AssertionError("Read from server failed", e);
182*61c4878aSAndroid Build Coastguard Worker     }
183*61c4878aSAndroid Build Coastguard Worker 
184*61c4878aSAndroid Build Coastguard Worker     if (expected_status != Status.OK) {
185*61c4878aSAndroid Build Coastguard Worker       throw new AssertionError("Transfer succeeded unexpectedly");
186*61c4878aSAndroid Build Coastguard Worker     }
187*61c4878aSAndroid Build Coastguard Worker 
188*61c4878aSAndroid Build Coastguard Worker     try {
189*61c4878aSAndroid Build Coastguard Worker       Files.write(fileName, data);
190*61c4878aSAndroid Build Coastguard Worker     } catch (IOException e) {
191*61c4878aSAndroid Build Coastguard Worker       logger.atSevere().log("Failed to write to output file `%s`", fileName);
192*61c4878aSAndroid Build Coastguard Worker       throw new AssertionError("Failed to write output file from server", e);
193*61c4878aSAndroid Build Coastguard Worker     }
194*61c4878aSAndroid Build Coastguard Worker   }
195*61c4878aSAndroid Build Coastguard Worker 
WriteToServer(int resourceId, Path fileName, TransferClient client, Status expected_status, int initial_offset)196*61c4878aSAndroid Build Coastguard Worker   public static void WriteToServer(int resourceId,
197*61c4878aSAndroid Build Coastguard Worker       Path fileName,
198*61c4878aSAndroid Build Coastguard Worker       TransferClient client,
199*61c4878aSAndroid Build Coastguard Worker       Status expected_status,
200*61c4878aSAndroid Build Coastguard Worker       int initial_offset) {
201*61c4878aSAndroid Build Coastguard Worker     if (Files.notExists(fileName)) {
202*61c4878aSAndroid Build Coastguard Worker       logger.atSevere().log("Input file `%s` does not exist", fileName);
203*61c4878aSAndroid Build Coastguard Worker     }
204*61c4878aSAndroid Build Coastguard Worker 
205*61c4878aSAndroid Build Coastguard Worker     byte[] data;
206*61c4878aSAndroid Build Coastguard Worker     try {
207*61c4878aSAndroid Build Coastguard Worker       data = Files.readAllBytes(fileName);
208*61c4878aSAndroid Build Coastguard Worker     } catch (IOException e) {
209*61c4878aSAndroid Build Coastguard Worker       logger.atSevere().log("Failed to read input file `%s`", fileName);
210*61c4878aSAndroid Build Coastguard Worker       throw new AssertionError("Failed to read input file on write to server", e);
211*61c4878aSAndroid Build Coastguard Worker     }
212*61c4878aSAndroid Build Coastguard Worker 
213*61c4878aSAndroid Build Coastguard Worker     try {
214*61c4878aSAndroid Build Coastguard Worker       client.write(resourceId, data, initial_offset).get();
215*61c4878aSAndroid Build Coastguard Worker     } catch (ExecutionException e) {
216*61c4878aSAndroid Build Coastguard Worker       if (((TransferError) e.getCause()).status() != expected_status) {
217*61c4878aSAndroid Build Coastguard Worker         throw new AssertionError("Unexpected transfer write failure", e);
218*61c4878aSAndroid Build Coastguard Worker       }
219*61c4878aSAndroid Build Coastguard Worker       return;
220*61c4878aSAndroid Build Coastguard Worker     } catch (InterruptedException e) {
221*61c4878aSAndroid Build Coastguard Worker       throw new AssertionError("Write to server failed", e);
222*61c4878aSAndroid Build Coastguard Worker     }
223*61c4878aSAndroid Build Coastguard Worker 
224*61c4878aSAndroid Build Coastguard Worker     if (expected_status != Status.OK) {
225*61c4878aSAndroid Build Coastguard Worker       throw new AssertionError("Transfer succeeded unexpectedly");
226*61c4878aSAndroid Build Coastguard Worker     }
227*61c4878aSAndroid Build Coastguard Worker   }
228*61c4878aSAndroid Build Coastguard Worker 
main(String[] args)229*61c4878aSAndroid Build Coastguard Worker   public static void main(String[] args) {
230*61c4878aSAndroid Build Coastguard Worker     if (args.length != 1) {
231*61c4878aSAndroid Build Coastguard Worker       logger.atSevere().log("Usage: PORT");
232*61c4878aSAndroid Build Coastguard Worker       System.exit(1);
233*61c4878aSAndroid Build Coastguard Worker     }
234*61c4878aSAndroid Build Coastguard Worker 
235*61c4878aSAndroid Build Coastguard Worker     // The port is provided directly as a commandline argument.
236*61c4878aSAndroid Build Coastguard Worker     int port = Integer.parseInt(args[0]);
237*61c4878aSAndroid Build Coastguard Worker 
238*61c4878aSAndroid Build Coastguard Worker     ConfigProtos.ClientConfig config;
239*61c4878aSAndroid Build Coastguard Worker     try {
240*61c4878aSAndroid Build Coastguard Worker       config = ParseConfigFrom(System.in);
241*61c4878aSAndroid Build Coastguard Worker     } catch (IOException e) {
242*61c4878aSAndroid Build Coastguard Worker       throw new AssertionError("Failed to parse config file from stdin", e);
243*61c4878aSAndroid Build Coastguard Worker     }
244*61c4878aSAndroid Build Coastguard Worker 
245*61c4878aSAndroid Build Coastguard Worker     Socket socket;
246*61c4878aSAndroid Build Coastguard Worker     try {
247*61c4878aSAndroid Build Coastguard Worker       socket = new Socket(HOSTNAME, port);
248*61c4878aSAndroid Build Coastguard Worker     } catch (IOException e) {
249*61c4878aSAndroid Build Coastguard Worker       logger.atSevere().log("Failed to connect to %s:%d", HOSTNAME, port);
250*61c4878aSAndroid Build Coastguard Worker       throw new AssertionError("Failed to connect to server/proxy port", e);
251*61c4878aSAndroid Build Coastguard Worker     }
252*61c4878aSAndroid Build Coastguard Worker     try {
253*61c4878aSAndroid Build Coastguard Worker       socket.setSendBufferSize(MAX_SOCKET_SEND_BUFFER_SIZE);
254*61c4878aSAndroid Build Coastguard Worker     } catch (SocketException e) {
255*61c4878aSAndroid Build Coastguard Worker       logger.atSevere().log("Invalid socket buffer size %d", MAX_SOCKET_SEND_BUFFER_SIZE);
256*61c4878aSAndroid Build Coastguard Worker       throw new AssertionError("Invalid socket buffer size", e);
257*61c4878aSAndroid Build Coastguard Worker     }
258*61c4878aSAndroid Build Coastguard Worker     InputStream reader;
259*61c4878aSAndroid Build Coastguard Worker     OutputStream writer;
260*61c4878aSAndroid Build Coastguard Worker 
261*61c4878aSAndroid Build Coastguard Worker     try {
262*61c4878aSAndroid Build Coastguard Worker       writer = socket.getOutputStream();
263*61c4878aSAndroid Build Coastguard Worker       reader = socket.getInputStream();
264*61c4878aSAndroid Build Coastguard Worker     } catch (IOException e) {
265*61c4878aSAndroid Build Coastguard Worker       throw new AssertionError("Failed to open socket streams", e);
266*61c4878aSAndroid Build Coastguard Worker     }
267*61c4878aSAndroid Build Coastguard Worker 
268*61c4878aSAndroid Build Coastguard Worker     JavaClient hdlc_rpc_client = new JavaClient(writer, reader);
269*61c4878aSAndroid Build Coastguard Worker 
270*61c4878aSAndroid Build Coastguard Worker     hdlc_rpc_client.startClient();
271*61c4878aSAndroid Build Coastguard Worker 
272*61c4878aSAndroid Build Coastguard Worker     TransferClient client = new TransferClient(
273*61c4878aSAndroid Build Coastguard Worker         hdlc_rpc_client.getRpcClient().method(CHANNEL_ID, TransferService.get().name() + "/Read"),
274*61c4878aSAndroid Build Coastguard Worker         hdlc_rpc_client.getRpcClient().method(CHANNEL_ID, TransferService.get().name() + "/Write"),
275*61c4878aSAndroid Build Coastguard Worker         TransferTimeoutSettings.builder()
276*61c4878aSAndroid Build Coastguard Worker             .setTimeoutMillis(config.getChunkTimeoutMs())
277*61c4878aSAndroid Build Coastguard Worker             .setInitialTimeoutMillis(config.getInitialChunkTimeoutMs())
278*61c4878aSAndroid Build Coastguard Worker             .setMaxRetries(config.getMaxRetries())
279*61c4878aSAndroid Build Coastguard Worker             .setMaxLifetimeRetries(config.getMaxLifetimeRetries())
280*61c4878aSAndroid Build Coastguard Worker             .build());
281*61c4878aSAndroid Build Coastguard Worker 
282*61c4878aSAndroid Build Coastguard Worker     for (ConfigProtos.TransferAction action : config.getTransferActionsList()) {
283*61c4878aSAndroid Build Coastguard Worker       int resourceId = action.getResourceId();
284*61c4878aSAndroid Build Coastguard Worker       Path fileName = Paths.get(action.getFilePath());
285*61c4878aSAndroid Build Coastguard Worker 
286*61c4878aSAndroid Build Coastguard Worker       if (action.getProtocolVersion() != TransferAction.ProtocolVersion.UNKNOWN_VERSION) {
287*61c4878aSAndroid Build Coastguard Worker         client.setProtocolVersion(ProtocolVersion.values()[action.getProtocolVersionValue()]);
288*61c4878aSAndroid Build Coastguard Worker       } else {
289*61c4878aSAndroid Build Coastguard Worker         client.setProtocolVersion(ProtocolVersion.latest());
290*61c4878aSAndroid Build Coastguard Worker       }
291*61c4878aSAndroid Build Coastguard Worker       try {
292*61c4878aSAndroid Build Coastguard Worker         if (action.getTransferType() == ConfigProtos.TransferAction.TransferType.WRITE_TO_SERVER) {
293*61c4878aSAndroid Build Coastguard Worker           WriteToServer(resourceId,
294*61c4878aSAndroid Build Coastguard Worker               fileName,
295*61c4878aSAndroid Build Coastguard Worker               client,
296*61c4878aSAndroid Build Coastguard Worker               Status.fromCode(action.getExpectedStatus().getNumber()),
297*61c4878aSAndroid Build Coastguard Worker               action.getInitialOffset());
298*61c4878aSAndroid Build Coastguard Worker         } else if (action.getTransferType()
299*61c4878aSAndroid Build Coastguard Worker             == ConfigProtos.TransferAction.TransferType.READ_FROM_SERVER) {
300*61c4878aSAndroid Build Coastguard Worker           ReadFromServer(resourceId,
301*61c4878aSAndroid Build Coastguard Worker               fileName,
302*61c4878aSAndroid Build Coastguard Worker               client,
303*61c4878aSAndroid Build Coastguard Worker               Status.fromCode(action.getExpectedStatus().getNumber()),
304*61c4878aSAndroid Build Coastguard Worker               action.getInitialOffset());
305*61c4878aSAndroid Build Coastguard Worker         } else {
306*61c4878aSAndroid Build Coastguard Worker           throw new AssertionError("Unknown transfer action type");
307*61c4878aSAndroid Build Coastguard Worker         }
308*61c4878aSAndroid Build Coastguard Worker       } catch (AssertionError e) {
309*61c4878aSAndroid Build Coastguard Worker         System.exit(1);
310*61c4878aSAndroid Build Coastguard Worker       }
311*61c4878aSAndroid Build Coastguard Worker     }
312*61c4878aSAndroid Build Coastguard Worker 
313*61c4878aSAndroid Build Coastguard Worker     logger.atInfo().log("Transfer completed successfully");
314*61c4878aSAndroid Build Coastguard Worker 
315*61c4878aSAndroid Build Coastguard Worker     System.exit(0);
316*61c4878aSAndroid Build Coastguard Worker   }
317*61c4878aSAndroid Build Coastguard Worker }
318