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