• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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.ddmlib;
18 
19 import com.android.ddmlib.AdbHelper.AdbResponse;
20 import com.android.ddmlib.FileListingService.FileEntry;
21 import com.android.ddmlib.SyncException.SyncError;
22 import com.android.ddmlib.utils.ArrayHelper;
23 
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.UnsupportedEncodingException;
29 import java.net.InetSocketAddress;
30 import java.nio.channels.SocketChannel;
31 import java.util.ArrayList;
32 
33 /**
34  * Sync service class to push/pull to/from devices/emulators, through the debug bridge.
35  * <p/>
36  * To get a {@link SyncService} object, use {@link Device#getSyncService()}.
37  */
38 public final class SyncService {
39 
40     private final static byte[] ID_OKAY = { 'O', 'K', 'A', 'Y' };
41     private final static byte[] ID_FAIL = { 'F', 'A', 'I', 'L' };
42     private final static byte[] ID_STAT = { 'S', 'T', 'A', 'T' };
43     private final static byte[] ID_RECV = { 'R', 'E', 'C', 'V' };
44     private final static byte[] ID_DATA = { 'D', 'A', 'T', 'A' };
45     private final static byte[] ID_DONE = { 'D', 'O', 'N', 'E' };
46     private final static byte[] ID_SEND = { 'S', 'E', 'N', 'D' };
47 //    private final static byte[] ID_LIST = { 'L', 'I', 'S', 'T' };
48 //    private final static byte[] ID_DENT = { 'D', 'E', 'N', 'T' };
49 
50     private final static NullSyncProgresMonitor sNullSyncProgressMonitor =
51             new NullSyncProgresMonitor();
52 
53     private final static int S_ISOCK = 0xC000; // type: symbolic link
54     private final static int S_IFLNK = 0xA000; // type: symbolic link
55     private final static int S_IFREG = 0x8000; // type: regular file
56     private final static int S_IFBLK = 0x6000; // type: block device
57     private final static int S_IFDIR = 0x4000; // type: directory
58     private final static int S_IFCHR = 0x2000; // type: character device
59     private final static int S_IFIFO = 0x1000; // type: fifo
60 /*
61     private final static int S_ISUID = 0x0800; // set-uid bit
62     private final static int S_ISGID = 0x0400; // set-gid bit
63     private final static int S_ISVTX = 0x0200; // sticky bit
64     private final static int S_IRWXU = 0x01C0; // user permissions
65     private final static int S_IRUSR = 0x0100; // user: read
66     private final static int S_IWUSR = 0x0080; // user: write
67     private final static int S_IXUSR = 0x0040; // user: execute
68     private final static int S_IRWXG = 0x0038; // group permissions
69     private final static int S_IRGRP = 0x0020; // group: read
70     private final static int S_IWGRP = 0x0010; // group: write
71     private final static int S_IXGRP = 0x0008; // group: execute
72     private final static int S_IRWXO = 0x0007; // other permissions
73     private final static int S_IROTH = 0x0004; // other: read
74     private final static int S_IWOTH = 0x0002; // other: write
75     private final static int S_IXOTH = 0x0001; // other: execute
76 */
77 
78     private final static int SYNC_DATA_MAX = 64*1024;
79     private final static int REMOTE_PATH_MAX_LENGTH = 1024;
80 
81     /**
82      * Classes which implement this interface provide methods that deal
83      * with displaying transfer progress.
84      */
85     public interface ISyncProgressMonitor {
86         /**
87          * Sent when the transfer starts
88          * @param totalWork the total amount of work.
89          */
start(int totalWork)90         public void start(int totalWork);
91         /**
92          * Sent when the transfer is finished or interrupted.
93          */
stop()94         public void stop();
95         /**
96          * Sent to query for possible cancellation.
97          * @return true if the transfer should be stopped.
98          */
isCanceled()99         public boolean isCanceled();
100         /**
101          * Sent when a sub task is started.
102          * @param name the name of the sub task.
103          */
startSubTask(String name)104         public void startSubTask(String name);
105         /**
106          * Sent when some progress have been made.
107          * @param work the amount of work done.
108          */
advance(int work)109         public void advance(int work);
110     }
111 
112     /**
113      * A Sync progress monitor that does nothing
114      */
115     private static class NullSyncProgresMonitor implements ISyncProgressMonitor {
advance(int work)116         public void advance(int work) {
117         }
isCanceled()118         public boolean isCanceled() {
119             return false;
120         }
121 
start(int totalWork)122         public void start(int totalWork) {
123         }
startSubTask(String name)124         public void startSubTask(String name) {
125         }
stop()126         public void stop() {
127         }
128     }
129 
130     private InetSocketAddress mAddress;
131     private Device mDevice;
132     private SocketChannel mChannel;
133 
134     /**
135      * Buffer used to send data. Allocated when needed and reused afterward.
136      */
137     private byte[] mBuffer;
138 
139     /**
140      * Creates a Sync service object.
141      * @param address The address to connect to
142      * @param device the {@link Device} that the service connects to.
143      */
SyncService(InetSocketAddress address, Device device)144     SyncService(InetSocketAddress address, Device device) {
145         mAddress = address;
146         mDevice = device;
147     }
148 
149     /**
150      * Opens the sync connection. This must be called before any calls to push[File] / pull[File].
151      * @return true if the connection opened, false if adb refuse the connection. This can happen
152      * if the {@link Device} is invalid.
153      * @throws TimeoutException in case of timeout on the connection.
154      * @throws AdbCommandRejectedException if adb rejects the command
155      * @throws IOException If the connection to adb failed.
156      */
openSync()157     boolean openSync() throws TimeoutException, AdbCommandRejectedException, IOException {
158         try {
159             mChannel = SocketChannel.open(mAddress);
160             mChannel.configureBlocking(false);
161 
162             // target a specific device
163             AdbHelper.setDevice(mChannel, mDevice);
164 
165             byte[] request = AdbHelper.formAdbRequest("sync:"); //$NON-NLS-1$
166             AdbHelper.write(mChannel, request, -1, DdmPreferences.getTimeOut());
167 
168             AdbResponse resp = AdbHelper.readAdbResponse(mChannel, false /* readDiagString */);
169 
170             if (resp.okay == false) {
171                 Log.w("ddms", "Got unhappy response from ADB sync req: " + resp.message);
172                 mChannel.close();
173                 mChannel = null;
174                 return false;
175             }
176         } catch (TimeoutException e) {
177             if (mChannel != null) {
178                 try {
179                     mChannel.close();
180                 } catch (IOException e2) {
181                     // we want to throw the original exception, so we ignore this one.
182                 }
183                 mChannel = null;
184             }
185 
186             throw e;
187         } catch (IOException e) {
188             if (mChannel != null) {
189                 try {
190                     mChannel.close();
191                 } catch (IOException e2) {
192                     // we want to throw the original exception, so we ignore this one.
193                 }
194                 mChannel = null;
195             }
196 
197             throw e;
198         }
199 
200         return true;
201     }
202 
203     /**
204      * Closes the connection.
205      */
close()206     public void close() {
207         if (mChannel != null) {
208             try {
209                 mChannel.close();
210             } catch (IOException e) {
211                 // nothing to be done really...
212             }
213             mChannel = null;
214         }
215     }
216 
217     /**
218      * Returns a sync progress monitor that does nothing. This allows background tasks that don't
219      * want/need to display ui, to pass a valid {@link ISyncProgressMonitor}.
220      * <p/>This object can be reused multiple times and can be used by concurrent threads.
221      */
getNullProgressMonitor()222     public static ISyncProgressMonitor getNullProgressMonitor() {
223         return sNullSyncProgressMonitor;
224     }
225 
226     /**
227      * Pulls file(s) or folder(s).
228      * @param entries the remote item(s) to pull
229      * @param localPath The local destination. If the entries count is > 1 or
230      *      if the unique entry is a folder, this should be a folder.
231      * @param monitor The progress monitor. Cannot be null.
232      * @throws SyncException
233      * @throws IOException
234      * @throws TimeoutException
235      *
236      * @see FileListingService.FileEntry
237      * @see #getNullProgressMonitor()
238      */
pull(FileEntry[] entries, String localPath, ISyncProgressMonitor monitor)239     public void pull(FileEntry[] entries, String localPath, ISyncProgressMonitor monitor)
240             throws SyncException, IOException, TimeoutException {
241 
242         // first we check the destination is a directory and exists
243         File f = new File(localPath);
244         if (f.exists() == false) {
245             throw new SyncException(SyncError.NO_DIR_TARGET);
246         }
247         if (f.isDirectory() == false) {
248             throw new SyncException(SyncError.TARGET_IS_FILE);
249         }
250 
251         // get a FileListingService object
252         FileListingService fls = new FileListingService(mDevice);
253 
254         // compute the number of file to move
255         int total = getTotalRemoteFileSize(entries, fls);
256 
257         // start the monitor
258         monitor.start(total);
259 
260         doPull(entries, localPath, fls, monitor);
261 
262         monitor.stop();
263     }
264 
265     /**
266      * Pulls a single file.
267      * @param remote the remote file
268      * @param localFilename The local destination.
269      * @param monitor The progress monitor. Cannot be null.
270      *
271      * @throws IOException in case of an IO exception.
272      * @throws TimeoutException in case of a timeout reading responses from the device.
273      * @throws SyncException in case of a sync exception.
274      *
275      * @see FileListingService.FileEntry
276      * @see #getNullProgressMonitor()
277      */
pullFile(FileEntry remote, String localFilename, ISyncProgressMonitor monitor)278     public void pullFile(FileEntry remote, String localFilename, ISyncProgressMonitor monitor)
279             throws IOException, SyncException, TimeoutException {
280         int total = remote.getSizeValue();
281         monitor.start(total);
282 
283         doPullFile(remote.getFullPath(), localFilename, monitor);
284 
285         monitor.stop();
286     }
287 
288     /**
289      * Pulls a single file.
290      * <p/>Because this method just deals with a String for the remote file instead of a
291      * {@link FileEntry}, the size of the file being pulled is unknown and the
292      * {@link ISyncProgressMonitor} will not properly show the progress
293      * @param remoteFilepath the full path to the remote file
294      * @param localFilename The local destination.
295      * @param monitor The progress monitor. Cannot be null.
296      *
297      * @throws IOException in case of an IO exception.
298      * @throws TimeoutException in case of a timeout reading responses from the device.
299      * @throws SyncException in case of a sync exception.
300      *
301      * @see #getNullProgressMonitor()
302      */
pullFile(String remoteFilepath, String localFilename, ISyncProgressMonitor monitor)303     public void pullFile(String remoteFilepath, String localFilename,
304             ISyncProgressMonitor monitor) throws TimeoutException, IOException, SyncException {
305         Integer mode = readMode(remoteFilepath);
306         if (mode == null) {
307             // attempts to download anyway
308         } else if (mode == 0) {
309             throw new SyncException(SyncError.NO_REMOTE_OBJECT);
310         }
311 
312         monitor.start(0);
313         //TODO: use the {@link FileListingService} to get the file size.
314 
315         doPullFile(remoteFilepath, localFilename, monitor);
316 
317         monitor.stop();
318     }
319 
320     /**
321      * Push several files.
322      * @param local An array of loca files to push
323      * @param remote the remote {@link FileEntry} representing a directory.
324      * @param monitor The progress monitor. Cannot be null.
325      * @throws SyncException if file could not be pushed
326      * @throws IOException in case of I/O error on the connection.
327      * @throws TimeoutException in case of a timeout reading responses from the device.
328      */
push(String[] local, FileEntry remote, ISyncProgressMonitor monitor)329     public void push(String[] local, FileEntry remote, ISyncProgressMonitor monitor)
330             throws SyncException, IOException, TimeoutException {
331         if (remote.isDirectory() == false) {
332             throw new SyncException(SyncError.REMOTE_IS_FILE);
333         }
334 
335         // make a list of File from the list of String
336         ArrayList<File> files = new ArrayList<File>();
337         for (String path : local) {
338             files.add(new File(path));
339         }
340 
341         // get the total count of the bytes to transfer
342         File[] fileArray = files.toArray(new File[files.size()]);
343         int total = getTotalLocalFileSize(fileArray);
344 
345         monitor.start(total);
346 
347         doPush(fileArray, remote.getFullPath(), monitor);
348 
349         monitor.stop();
350     }
351 
352     /**
353      * Push a single file.
354      * @param local the local filepath.
355      * @param remote The remote filepath.
356      * @param monitor The progress monitor. Cannot be null.
357      *
358      * @throws SyncException if file could not be pushed
359      * @throws IOException in case of I/O error on the connection.
360      * @throws TimeoutException in case of a timeout reading responses from the device.
361      */
pushFile(String local, String remote, ISyncProgressMonitor monitor)362     public void pushFile(String local, String remote, ISyncProgressMonitor monitor)
363             throws SyncException, IOException, TimeoutException {
364         File f = new File(local);
365         if (f.exists() == false) {
366             throw new SyncException(SyncError.NO_LOCAL_FILE);
367         }
368 
369         if (f.isDirectory()) {
370             throw new SyncException(SyncError.LOCAL_IS_DIRECTORY);
371         }
372 
373         monitor.start((int)f.length());
374 
375         doPushFile(local, remote, monitor);
376 
377         monitor.stop();
378     }
379 
380     /**
381      * compute the recursive file size of all the files in the list. Folder
382      * have a weight of 1.
383      * @param entries
384      * @param fls
385      * @return
386      */
getTotalRemoteFileSize(FileEntry[] entries, FileListingService fls)387     private int getTotalRemoteFileSize(FileEntry[] entries, FileListingService fls) {
388         int count = 0;
389         for (FileEntry e : entries) {
390             int type = e.getType();
391             if (type == FileListingService.TYPE_DIRECTORY) {
392                 // get the children
393                 FileEntry[] children = fls.getChildren(e, false, null);
394                 count += getTotalRemoteFileSize(children, fls) + 1;
395             } else if (type == FileListingService.TYPE_FILE) {
396                 count += e.getSizeValue();
397             }
398         }
399 
400         return count;
401     }
402 
403     /**
404      * compute the recursive file size of all the files in the list. Folder
405      * have a weight of 1.
406      * This does not check for circular links.
407      * @param files
408      * @return
409      */
getTotalLocalFileSize(File[] files)410     private int getTotalLocalFileSize(File[] files) {
411         int count = 0;
412 
413         for (File f : files) {
414             if (f.exists()) {
415                 if (f.isDirectory()) {
416                     return getTotalLocalFileSize(f.listFiles()) + 1;
417                 } else if (f.isFile()) {
418                     count += f.length();
419                 }
420             }
421         }
422 
423         return count;
424     }
425 
426     /**
427      * Pulls multiple files/folders recursively.
428      * @param entries The list of entry to pull
429      * @param localPath the localpath to a directory
430      * @param fileListingService a FileListingService object to browse through remote directories.
431      * @param monitor the progress monitor. Must be started already.
432      *
433      * @throws SyncException if file could not be pushed
434      * @throws IOException in case of I/O error on the connection.
435      * @throws TimeoutException in case of a timeout reading responses from the device.
436      */
doPull(FileEntry[] entries, String localPath, FileListingService fileListingService, ISyncProgressMonitor monitor)437     private void doPull(FileEntry[] entries, String localPath,
438             FileListingService fileListingService,
439             ISyncProgressMonitor monitor) throws SyncException, IOException, TimeoutException {
440 
441         for (FileEntry e : entries) {
442             // check if we're cancelled
443             if (monitor.isCanceled() == true) {
444                 throw new SyncException(SyncError.CANCELED);
445             }
446 
447             // get type (we only pull directory and files for now)
448             int type = e.getType();
449             if (type == FileListingService.TYPE_DIRECTORY) {
450                 monitor.startSubTask(e.getFullPath());
451                 String dest = localPath + File.separator + e.getName();
452 
453                 // make the directory
454                 File d = new File(dest);
455                 d.mkdir();
456 
457                 // then recursively call the content. Since we did a ls command
458                 // to get the number of files, we can use the cache
459                 FileEntry[] children = fileListingService.getChildren(e, true, null);
460                 doPull(children, dest, fileListingService, monitor);
461                 monitor.advance(1);
462             } else if (type == FileListingService.TYPE_FILE) {
463                 monitor.startSubTask(e.getFullPath());
464                 String dest = localPath + File.separator + e.getName();
465                 doPullFile(e.getFullPath(), dest, monitor);
466             }
467         }
468     }
469 
470     /**
471      * Pulls a remote file
472      * @param remotePath the remote file (length max is 1024)
473      * @param localPath the local destination
474      * @param monitor the monitor. The monitor must be started already.
475      * @throws SyncException if file could not be pushed
476      * @throws IOException in case of I/O error on the connection.
477      * @throws TimeoutException in case of a timeout reading responses from the device.
478      */
doPullFile(String remotePath, String localPath, ISyncProgressMonitor monitor)479     private void doPullFile(String remotePath, String localPath,
480             ISyncProgressMonitor monitor) throws IOException, SyncException, TimeoutException {
481         byte[] msg = null;
482         byte[] pullResult = new byte[8];
483 
484         final int timeOut = DdmPreferences.getTimeOut();
485 
486         try {
487             byte[] remotePathContent = remotePath.getBytes(AdbHelper.DEFAULT_ENCODING);
488 
489             if (remotePathContent.length > REMOTE_PATH_MAX_LENGTH) {
490                 throw new SyncException(SyncError.REMOTE_PATH_LENGTH);
491             }
492 
493             // create the full request message
494             msg = createFileReq(ID_RECV, remotePathContent);
495 
496             // and send it.
497             AdbHelper.write(mChannel, msg, -1, timeOut);
498 
499             // read the result, in a byte array containing 2 ints
500             // (id, size)
501             AdbHelper.read(mChannel, pullResult, -1, timeOut);
502 
503             // check we have the proper data back
504             if (checkResult(pullResult, ID_DATA) == false &&
505                     checkResult(pullResult, ID_DONE) == false) {
506                 throw new SyncException(SyncError.TRANSFER_PROTOCOL_ERROR,
507                         readErrorMessage(pullResult, timeOut));
508             }
509         } catch (UnsupportedEncodingException e) {
510             throw new SyncException(SyncError.REMOTE_PATH_ENCODING, e);
511         }
512 
513         // access the destination file
514         File f = new File(localPath);
515 
516         // create the stream to write in the file. We use a new try/catch block to differentiate
517         // between file and network io exceptions.
518         FileOutputStream fos = null;
519         try {
520             fos = new FileOutputStream(f);
521         } catch (IOException e) {
522             Log.e("ddms", String.format("Failed to open local file %s for writing, Reason: %s",
523                     f.getAbsolutePath(), e.toString()));
524             throw new SyncException(SyncError.FILE_WRITE_ERROR);
525         }
526 
527         // the buffer to read the data
528         byte[] data = new byte[SYNC_DATA_MAX];
529 
530         // loop to get data until we're done.
531         while (true) {
532             // check if we're cancelled
533             if (monitor.isCanceled() == true) {
534                 throw new SyncException(SyncError.CANCELED);
535             }
536 
537             // if we're done, we stop the loop
538             if (checkResult(pullResult, ID_DONE)) {
539                 break;
540             }
541             if (checkResult(pullResult, ID_DATA) == false) {
542                 // hmm there's an error
543                 throw new SyncException(SyncError.TRANSFER_PROTOCOL_ERROR,
544                         readErrorMessage(pullResult, timeOut));
545             }
546             int length = ArrayHelper.swap32bitFromArray(pullResult, 4);
547             if (length > SYNC_DATA_MAX) {
548                 // buffer overrun!
549                 // error and exit
550                 throw new SyncException(SyncError.BUFFER_OVERRUN);
551             }
552 
553             // now read the length we received
554             AdbHelper.read(mChannel, data, length, timeOut);
555 
556             // get the header for the next packet.
557             AdbHelper.read(mChannel, pullResult, -1, timeOut);
558 
559             // write the content in the file
560             fos.write(data, 0, length);
561 
562             monitor.advance(length);
563         }
564 
565         fos.flush();
566     }
567 
568 
569     /**
570      * Push multiple files
571      * @param fileArray
572      * @param remotePath
573      * @param monitor
574      *
575      * @throws SyncException if file could not be pushed
576      * @throws IOException in case of I/O error on the connection.
577      * @throws TimeoutException in case of a timeout reading responses from the device.
578      */
doPush(File[] fileArray, String remotePath, ISyncProgressMonitor monitor)579     private void doPush(File[] fileArray, String remotePath, ISyncProgressMonitor monitor)
580             throws SyncException, IOException, TimeoutException {
581         for (File f : fileArray) {
582             // check if we're canceled
583             if (monitor.isCanceled() == true) {
584                 throw new SyncException(SyncError.CANCELED);
585             }
586             if (f.exists()) {
587                 if (f.isDirectory()) {
588                     // append the name of the directory to the remote path
589                     String dest = remotePath + "/" + f.getName(); // $NON-NLS-1S
590                     monitor.startSubTask(dest);
591                     doPush(f.listFiles(), dest, monitor);
592 
593                     monitor.advance(1);
594                 } else if (f.isFile()) {
595                     // append the name of the file to the remote path
596                     String remoteFile = remotePath + "/" + f.getName(); // $NON-NLS-1S
597                     monitor.startSubTask(remoteFile);
598                     doPushFile(f.getAbsolutePath(), remoteFile, monitor);
599                 }
600             }
601         }
602     }
603 
604     /**
605      * Push a single file
606      * @param localPath the local file to push
607      * @param remotePath the remote file (length max is 1024)
608      * @param monitor the monitor. The monitor must be started already.
609      *
610      * @throws SyncException if file could not be pushed
611      * @throws IOException in case of I/O error on the connection.
612      * @throws TimeoutException in case of a timeout reading responses from the device.
613      */
doPushFile(String localPath, String remotePath, ISyncProgressMonitor monitor)614     private void doPushFile(String localPath, String remotePath,
615             ISyncProgressMonitor monitor) throws SyncException, IOException, TimeoutException {
616         FileInputStream fis = null;
617         byte[] msg;
618 
619         final int timeOut = DdmPreferences.getTimeOut();
620 
621         try {
622             byte[] remotePathContent = remotePath.getBytes(AdbHelper.DEFAULT_ENCODING);
623 
624             if (remotePathContent.length > REMOTE_PATH_MAX_LENGTH) {
625                 throw new SyncException(SyncError.REMOTE_PATH_LENGTH);
626             }
627 
628             File f = new File(localPath);
629 
630             // create the stream to read the file
631             fis = new FileInputStream(f);
632 
633             // create the header for the action
634             msg = createSendFileReq(ID_SEND, remotePathContent, 0644);
635         } catch (UnsupportedEncodingException e) {
636             throw new SyncException(SyncError.REMOTE_PATH_ENCODING, e);
637         }
638 
639         // and send it. We use a custom try/catch block to make the difference between
640         // file and network IO exceptions.
641         AdbHelper.write(mChannel, msg, -1, timeOut);
642 
643         // create the buffer used to read.
644         // we read max SYNC_DATA_MAX, but we need 2 4 bytes at the beginning.
645         if (mBuffer == null) {
646             mBuffer = new byte[SYNC_DATA_MAX + 8];
647         }
648         System.arraycopy(ID_DATA, 0, mBuffer, 0, ID_DATA.length);
649 
650         // look while there is something to read
651         while (true) {
652             // check if we're canceled
653             if (monitor.isCanceled() == true) {
654                 throw new SyncException(SyncError.CANCELED);
655             }
656 
657             // read up to SYNC_DATA_MAX
658             int readCount = fis.read(mBuffer, 8, SYNC_DATA_MAX);
659 
660             if (readCount == -1) {
661                 // we reached the end of the file
662                 break;
663             }
664 
665             // now send the data to the device
666             // first write the amount read
667             ArrayHelper.swap32bitsToArray(readCount, mBuffer, 4);
668 
669             // now write it
670             AdbHelper.write(mChannel, mBuffer, readCount+8, timeOut);
671 
672             // and advance the monitor
673             monitor.advance(readCount);
674         }
675         // close the local file
676         fis.close();
677 
678         // create the DONE message
679         long time = System.currentTimeMillis() / 1000;
680         msg = createReq(ID_DONE, (int)time);
681 
682         // and send it.
683         AdbHelper.write(mChannel, msg, -1, timeOut);
684 
685         // read the result, in a byte array containing 2 ints
686         // (id, size)
687         byte[] result = new byte[8];
688         AdbHelper.read(mChannel, result, -1 /* full length */, timeOut);
689 
690         if (checkResult(result, ID_OKAY) == false) {
691             throw new SyncException(SyncError.TRANSFER_PROTOCOL_ERROR,
692                     readErrorMessage(result, timeOut));
693         }
694     }
695 
696     /**
697      * Reads an error message from the opened {@link #mChannel}.
698      * @param result the current adb result. Must contain both FAIL and the length of the message.
699      * @param timeOut
700      * @return
701      * @throws TimeoutException in case of a timeout reading responses from the device.
702      * @throws IOException
703      */
readErrorMessage(byte[] result, final int timeOut)704     private String readErrorMessage(byte[] result, final int timeOut) throws TimeoutException,
705             IOException {
706         if (checkResult(result, ID_FAIL)) {
707             int len = ArrayHelper.swap32bitFromArray(result, 4);
708 
709             if (len > 0) {
710                 AdbHelper.read(mChannel, mBuffer, len, timeOut);
711 
712                 String message = new String(mBuffer, 0, len);
713                 Log.e("ddms", "transfer error: " + message);
714 
715                 return message;
716             }
717         }
718 
719         return null;
720     }
721 
722     /**
723      * Returns the mode of the remote file.
724      * @param path the remote file
725      * @return an Integer containing the mode if all went well or null
726      *      otherwise
727      * @throws IOException
728      * @throws TimeoutException in case of a timeout reading responses from the device.
729      */
readMode(String path)730     private Integer readMode(String path) throws TimeoutException, IOException {
731         // create the stat request message.
732         byte[] msg = createFileReq(ID_STAT, path);
733 
734         AdbHelper.write(mChannel, msg, -1 /* full length */, DdmPreferences.getTimeOut());
735 
736         // read the result, in a byte array containing 4 ints
737         // (id, mode, size, time)
738         byte[] statResult = new byte[16];
739         AdbHelper.read(mChannel, statResult, -1 /* full length */, DdmPreferences.getTimeOut());
740 
741         // check we have the proper data back
742         if (checkResult(statResult, ID_STAT) == false) {
743             return null;
744         }
745 
746         // we return the mode (2nd int in the array)
747         return ArrayHelper.swap32bitFromArray(statResult, 4);
748     }
749 
750     /**
751      * Create a command with a code and an int values
752      * @param command
753      * @param value
754      * @return
755      */
createReq(byte[] command, int value)756     private static byte[] createReq(byte[] command, int value) {
757         byte[] array = new byte[8];
758 
759         System.arraycopy(command, 0, array, 0, 4);
760         ArrayHelper.swap32bitsToArray(value, array, 4);
761 
762         return array;
763     }
764 
765     /**
766      * Creates the data array for a stat request.
767      * @param command the 4 byte command (ID_STAT, ID_RECV, ...)
768      * @param path The path of the remote file on which to execute the command
769      * @return the byte[] to send to the device through adb
770      */
createFileReq(byte[] command, String path)771     private static byte[] createFileReq(byte[] command, String path) {
772         byte[] pathContent = null;
773         try {
774             pathContent = path.getBytes(AdbHelper.DEFAULT_ENCODING);
775         } catch (UnsupportedEncodingException e) {
776             return null;
777         }
778 
779         return createFileReq(command, pathContent);
780     }
781 
782     /**
783      * Creates the data array for a file request. This creates an array with a 4 byte command + the
784      * remote file name.
785      * @param command the 4 byte command (ID_STAT, ID_RECV, ...).
786      * @param path The path, as a byte array, of the remote file on which to
787      *      execute the command.
788      * @return the byte[] to send to the device through adb
789      */
createFileReq(byte[] command, byte[] path)790     private static byte[] createFileReq(byte[] command, byte[] path) {
791         byte[] array = new byte[8 + path.length];
792 
793         System.arraycopy(command, 0, array, 0, 4);
794         ArrayHelper.swap32bitsToArray(path.length, array, 4);
795         System.arraycopy(path, 0, array, 8, path.length);
796 
797         return array;
798     }
799 
createSendFileReq(byte[] command, byte[] path, int mode)800     private static byte[] createSendFileReq(byte[] command, byte[] path, int mode) {
801         // make the mode into a string
802         String modeStr = "," + (mode & 0777); // $NON-NLS-1S
803         byte[] modeContent = null;
804         try {
805             modeContent = modeStr.getBytes(AdbHelper.DEFAULT_ENCODING);
806         } catch (UnsupportedEncodingException e) {
807             return null;
808         }
809 
810         byte[] array = new byte[8 + path.length + modeContent.length];
811 
812         System.arraycopy(command, 0, array, 0, 4);
813         ArrayHelper.swap32bitsToArray(path.length + modeContent.length, array, 4);
814         System.arraycopy(path, 0, array, 8, path.length);
815         System.arraycopy(modeContent, 0, array, 8 + path.length, modeContent.length);
816 
817         return array;
818 
819 
820     }
821 
822     /**
823      * Checks the result array starts with the provided code
824      * @param result The result array to check
825      * @param code The 4 byte code.
826      * @return true if the code matches.
827      */
checkResult(byte[] result, byte[] code)828     private static boolean checkResult(byte[] result, byte[] code) {
829         if (result[0] != code[0] ||
830                 result[1] != code[1] ||
831                 result[2] != code[2] ||
832                 result[3] != code[3]) {
833             return false;
834         }
835 
836         return true;
837 
838     }
839 
getFileType(int mode)840     private static int getFileType(int mode) {
841         if ((mode & S_ISOCK) == S_ISOCK) {
842             return FileListingService.TYPE_SOCKET;
843         }
844 
845         if ((mode & S_IFLNK) == S_IFLNK) {
846             return FileListingService.TYPE_LINK;
847         }
848 
849         if ((mode & S_IFREG) == S_IFREG) {
850             return FileListingService.TYPE_FILE;
851         }
852 
853         if ((mode & S_IFBLK) == S_IFBLK) {
854             return FileListingService.TYPE_BLOCK;
855         }
856 
857         if ((mode & S_IFDIR) == S_IFDIR) {
858             return FileListingService.TYPE_DIRECTORY;
859         }
860 
861         if ((mode & S_IFCHR) == S_IFCHR) {
862             return FileListingService.TYPE_CHARACTER;
863         }
864 
865         if ((mode & S_IFIFO) == S_IFIFO) {
866             return FileListingService.TYPE_FIFO;
867         }
868 
869         return FileListingService.TYPE_OTHER;
870     }
871 }
872