2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.ddmlib;
19 import com.android.ddmlib.AdbHelper.AdbResponse;
20 import com.android.ddmlib.FileListingService.FileEntry;
21 import com.android.ddmlib.utils.ArrayHelper;
24 import java.io.FileInputStream;
25 import java.io.FileNotFoundException;
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;
34 * Sync service class to push/pull to/from devices/emulators, through the debug bridge.
36 * To get a {@link SyncService} object, use {@link Device#getSyncService()}.
38 public final class SyncService {
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' };
50 private final static NullSyncProgresMonitor sNullSyncProgressMonitor =
51 new NullSyncProgresMonitor();
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
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
78 private final static int SYNC_DATA_MAX = 64*1024;
79 private final static int REMOTE_PATH_MAX_LENGTH = 1024;
81 /** Result code for transfer success. */
82 public static final int RESULT_OK = 0;
83 /** Result code for canceled transfer */
84 public static final int RESULT_CANCELED = 1;
85 /** Result code for unknown error */
86 public static final int RESULT_UNKNOWN_ERROR = 2;
87 /** Result code for network connection error */
88 public static final int RESULT_CONNECTION_ERROR = 3;
89 /** Result code for unknown remote object during a pull */
90 public static final int RESULT_NO_REMOTE_OBJECT = 4;
91 /** Result code when attempting to pull multiple files into a file */
92 public static final int RESULT_TARGET_IS_FILE = 5;
93 /** Result code when attempting to pull multiple into a directory that does not exist. */
94 public static final int RESULT_NO_DIR_TARGET = 6;
95 /** Result code for wrong encoding on the remote path. */
96 public static final int RESULT_REMOTE_PATH_ENCODING = 7;
97 /** Result code for remote path that is too long. */
98 public static final int RESULT_REMOTE_PATH_LENGTH = 8;
99 /** Result code for error while writing local file. */
100 public static final int RESULT_FILE_WRITE_ERROR = 9;
101 /** Result code for error while reading local file. */
102 public static final int RESULT_FILE_READ_ERROR = 10;
103 /** Result code for attempting to push a file that does not exist. */
104 public static final int RESULT_NO_LOCAL_FILE = 11;
105 /** Result code for attempting to push a directory. */
106 public static final int RESULT_LOCAL_IS_DIRECTORY = 12;
107 /** Result code for when the target path of a multi file push is a file. */
108 public static final int RESULT_REMOTE_IS_FILE = 13;
109 /** Result code for receiving too much data from the remove device at once */
110 public static final int RESULT_BUFFER_OVERRUN = 14;
113 * A file transfer result.
115 * This contains a code, and an optional string
117 public static class SyncResult {
119 private String mMessage;
120 SyncResult(int code, String message) {
125 SyncResult(int code, Exception e) {
126 this(code, e.getMessage());
129 SyncResult(int code) {
130 this(code, errorCodeToString(code));
133 public int getCode() {
137 public String getMessage() {
143 * Classes which implement this interface provide methods that deal
144 * with displaying transfer progress.
146 public interface ISyncProgressMonitor {
148 * Sent when the transfer starts
149 * @param totalWork the total amount of work.
151 public void start(int totalWork);
153 * Sent when the transfer is finished or interrupted.
157 * Sent to query for possible cancellation.
158 * @return true if the transfer should be stopped.
160 public boolean isCanceled();
162 * Sent when a sub task is started.
163 * @param name the name of the sub task.
165 public void startSubTask(String name);
167 * Sent when some progress have been made.
168 * @param work the amount of work done.
170 public void advance(int work);
174 * A Sync progress monitor that does nothing
176 private static class NullSyncProgresMonitor implements ISyncProgressMonitor {
177 public void advance(int work) {
179 public boolean isCanceled() {
183 public void start(int totalWork) {
185 public void startSubTask(String name) {
191 private InetSocketAddress mAddress;
192 private Device mDevice;
193 private SocketChannel mChannel;
196 * Buffer used to send data. Allocated when needed and reused afterward.
198 private byte[] mBuffer;
201 * Creates a Sync service object.
202 * @param address The address to connect to
203 * @param device the {@link Device} that the service connects to.
205 SyncService(InetSocketAddress address, Device device) {
211 * Opens the sync connection. This must be called before any calls to push[File] / pull[File].
212 * @return true if the connection opened, false if adb refuse the connection. This can happen
213 * if the {@link Device} is invalid.
214 * @throws IOException If the connection to adb failed.
216 boolean openSync() throws IOException {
218 mChannel = SocketChannel.open(mAddress);
219 mChannel.configureBlocking(false);
221 // target a specific device
222 AdbHelper.setDevice(mChannel, mDevice);
224 byte[] request = AdbHelper.formAdbRequest("sync:"); // $NON-NLS-1$
225 AdbHelper.write(mChannel, request, -1, DdmPreferences.getTimeOut());
227 AdbResponse resp = AdbHelper.readAdbResponse(mChannel, false /* readDiagString */);
229 if (!resp.ioSuccess || !resp.okay) {
231 "Got timeout or unhappy response from ADB sync req: "
237 } catch (IOException e) {
238 if (mChannel != null) {
241 } catch (IOException e2) {
242 // we want to throw the original exception, so we ignore this one.
254 * Closes the connection.
256 public void close() {
257 if (mChannel != null) {
260 } catch (IOException e) {
261 // nothing to be done really...
268 * Returns a sync progress monitor that does nothing. This allows background tasks that don't
269 * want/need to display ui, to pass a valid {@link ISyncProgressMonitor}.
270 * <p/>This object can be reused multiple times and can be used by concurrent threads.
272 public static ISyncProgressMonitor getNullProgressMonitor() {
273 return sNullSyncProgressMonitor;
277 * Converts an error code into a non-localized string
278 * @param code the error code;
280 private static String errorCodeToString(int code) {
284 case RESULT_CANCELED:
285 return "Tranfert canceled by the user.";
286 case RESULT_UNKNOWN_ERROR:
287 return "Unknown Error.";
288 case RESULT_CONNECTION_ERROR:
289 return "Adb Connection Error.";
290 case RESULT_NO_REMOTE_OBJECT:
291 return "Remote object doesn't exist!";
292 case RESULT_TARGET_IS_FILE:
293 return "Target object is a file.";
294 case RESULT_NO_DIR_TARGET:
295 return "Target directory doesn't exist.";
296 case RESULT_REMOTE_PATH_ENCODING:
297 return "Remote Path encoding is not supported.";
298 case RESULT_REMOTE_PATH_LENGTH:
299 return "Remove path is too long.";
300 case RESULT_FILE_WRITE_ERROR:
301 return "Writing local file failed!";
302 case RESULT_FILE_READ_ERROR:
303 return "Reading local file failed!";
304 case RESULT_NO_LOCAL_FILE:
305 return "Local file doesn't exist.";
306 case RESULT_LOCAL_IS_DIRECTORY:
307 return "Local path is a directory.";
308 case RESULT_REMOTE_IS_FILE:
309 return "Remote path is a file.";
310 case RESULT_BUFFER_OVERRUN:
311 return "Receiving too much data.";
314 throw new RuntimeException();
318 * Pulls file(s) or folder(s).
319 * @param entries the remote item(s) to pull
320 * @param localPath The local destination. If the entries count is > 1 or
321 * if the unique entry is a folder, this should be a folder.
322 * @param monitor The progress monitor. Cannot be null.
323 * @return a {@link SyncResult} object with a code and an optional message.
325 * @see FileListingService.FileEntry
326 * @see #getNullProgressMonitor()
328 public SyncResult pull(FileEntry[] entries, String localPath, ISyncProgressMonitor monitor) {
330 // first we check the destination is a directory and exists
331 File f = new File(localPath);
332 if (f.exists() == false) {
333 return new SyncResult(RESULT_NO_DIR_TARGET);
335 if (f.isDirectory() == false) {
336 return new SyncResult(RESULT_TARGET_IS_FILE);
339 // get a FileListingService object
340 FileListingService fls = new FileListingService(mDevice);
342 // compute the number of file to move
343 int total = getTotalRemoteFileSize(entries, fls);
346 monitor.start(total);
348 SyncResult result = doPull(entries, localPath, fls, monitor);
356 * Pulls a single file.
357 * @param remote the remote file
358 * @param localFilename The local destination.
359 * @param monitor The progress monitor. Cannot be null.
360 * @return a {@link SyncResult} object with a code and an optional message.
362 * @see FileListingService.FileEntry
363 * @see #getNullProgressMonitor()
365 public SyncResult pullFile(FileEntry remote, String localFilename,
366 ISyncProgressMonitor monitor) {
367 int total = remote.getSizeValue();
368 monitor.start(total);
370 SyncResult result = doPullFile(remote.getFullPath(), localFilename, monitor);
377 * Pulls a single file.
378 * <p/>Because this method just deals with a String for the remote file instead of a
379 * {@link FileEntry}, the size of the file being pulled is unknown and the
380 * {@link ISyncProgressMonitor} will not properly show the progress
381 * @param remoteFilepath the full path to the remote file
382 * @param localFilename The local destination.
383 * @param monitor The progress monitor. Cannot be null.
384 * @return a {@link SyncResult} object with a code and an optional message.
386 * @see #getNullProgressMonitor()
388 public SyncResult pullFile(String remoteFilepath, String localFilename,
389 ISyncProgressMonitor monitor) {
391 //TODO: use the {@link FileListingService} to get the file size.
393 SyncResult result = doPullFile(remoteFilepath, localFilename, monitor);
400 * Push several files.
401 * @param local An array of loca files to push
402 * @param remote the remote {@link FileEntry} representing a directory.
403 * @param monitor The progress monitor. Cannot be null.
404 * @return a {@link SyncResult} object with a code and an optional message.
406 public SyncResult push(String[] local, FileEntry remote, ISyncProgressMonitor monitor) {
407 if (remote.isDirectory() == false) {
408 return new SyncResult(RESULT_REMOTE_IS_FILE);
411 // make a list of File from the list of String
412 ArrayList<File> files = new ArrayList<File>();
413 for (String path : local) {
414 files.add(new File(path));
417 // get the total count of the bytes to transfer
418 File[] fileArray = files.toArray(new File[files.size()]);
419 int total = getTotalLocalFileSize(fileArray);
421 monitor.start(total);
423 SyncResult result = doPush(fileArray, remote.getFullPath(), monitor);
431 * Push a single file.
432 * @param local the local filepath.
433 * @param remote The remote filepath.
434 * @param monitor The progress monitor. Cannot be null.
435 * @return a {@link SyncResult} object with a code and an optional message.
437 public SyncResult pushFile(String local, String remote, ISyncProgressMonitor monitor) {
438 File f = new File(local);
439 if (f.exists() == false) {
440 return new SyncResult(RESULT_NO_LOCAL_FILE);
443 if (f.isDirectory()) {
444 return new SyncResult(RESULT_LOCAL_IS_DIRECTORY);
447 monitor.start((int)f.length());
449 SyncResult result = doPushFile(local, remote, monitor);
457 * compute the recursive file size of all the files in the list. Folder
458 * have a weight of 1.
463 private int getTotalRemoteFileSize(FileEntry[] entries, FileListingService fls) {
465 for (FileEntry e : entries) {
466 int type = e.getType();
467 if (type == FileListingService.TYPE_DIRECTORY) {
469 FileEntry[] children = fls.getChildren(e, false, null);
470 count += getTotalRemoteFileSize(children, fls) + 1;
471 } else if (type == FileListingService.TYPE_FILE) {
472 count += e.getSizeValue();
480 * compute the recursive file size of all the files in the list. Folder
481 * have a weight of 1.
482 * This does not check for circular links.
486 private int getTotalLocalFileSize(File[] files) {
489 for (File f : files) {
491 if (f.isDirectory()) {
492 return getTotalLocalFileSize(f.listFiles()) + 1;
493 } else if (f.isFile()) {
503 * Pulls multiple files/folders recursively.
504 * @param entries The list of entry to pull
505 * @param localPath the localpath to a directory
506 * @param fileListingService a FileListingService object to browse through remote directories.
507 * @param monitor the progress monitor. Must be started already.
508 * @return a {@link SyncResult} object with a code and an optional message.
510 private SyncResult doPull(FileEntry[] entries, String localPath,
511 FileListingService fileListingService,
512 ISyncProgressMonitor monitor) {
514 for (FileEntry e : entries) {
515 // check if we're cancelled
516 if (monitor.isCanceled() == true) {
517 return new SyncResult(RESULT_CANCELED);
520 // get type (we only pull directory and files for now)
521 int type = e.getType();
522 if (type == FileListingService.TYPE_DIRECTORY) {
523 monitor.startSubTask(e.getFullPath());
524 String dest = localPath + File.separator + e.getName();
526 // make the directory
527 File d = new File(dest);
530 // then recursively call the content. Since we did a ls command
531 // to get the number of files, we can use the cache
532 FileEntry[] children = fileListingService.getChildren(e, true, null);
533 SyncResult result = doPull(children, dest, fileListingService, monitor);
534 if (result.mCode != RESULT_OK) {
538 } else if (type == FileListingService.TYPE_FILE) {
539 monitor.startSubTask(e.getFullPath());
540 String dest = localPath + File.separator + e.getName();
541 SyncResult result = doPullFile(e.getFullPath(), dest, monitor);
542 if (result.mCode != RESULT_OK) {
548 return new SyncResult(RESULT_OK);
552 * Pulls a remote file
553 * @param remotePath the remote file (length max is 1024)
554 * @param localPath the local destination
555 * @param monitor the monitor. The monitor must be started already.
556 * @return a {@link SyncResult} object with a code and an optional message.
558 private SyncResult doPullFile(String remotePath, String localPath,
559 ISyncProgressMonitor monitor) {
561 byte[] pullResult = new byte[8];
563 final int timeOut = DdmPreferences.getTimeOut();
566 byte[] remotePathContent = remotePath.getBytes(AdbHelper.DEFAULT_ENCODING);
568 if (remotePathContent.length > REMOTE_PATH_MAX_LENGTH) {
569 return new SyncResult(RESULT_REMOTE_PATH_LENGTH);
572 // create the full request message
573 msg = createFileReq(ID_RECV, remotePathContent);
576 AdbHelper.write(mChannel, msg, -1, timeOut);
578 // read the result, in a byte array containing 2 ints
580 AdbHelper.read(mChannel, pullResult, -1, timeOut);
582 // check we have the proper data back
583 if (checkResult(pullResult, ID_DATA) == false &&
584 checkResult(pullResult, ID_DONE) == false) {
585 return new SyncResult(RESULT_CONNECTION_ERROR);
587 } catch (UnsupportedEncodingException e) {
588 return new SyncResult(RESULT_REMOTE_PATH_ENCODING, e);
589 } catch (IOException e) {
590 return new SyncResult(RESULT_CONNECTION_ERROR, e);
593 // access the destination file
594 File f = new File(localPath);
596 // create the stream to write in the file. We use a new try/catch block to differentiate
597 // between file and network io exceptions.
598 FileOutputStream fos = null;
600 fos = new FileOutputStream(f);
601 } catch (FileNotFoundException e) {
602 return new SyncResult(RESULT_FILE_WRITE_ERROR, e);
605 // the buffer to read the data
606 byte[] data = new byte[SYNC_DATA_MAX];
608 // loop to get data until we're done.
610 // check if we're cancelled
611 if (monitor.isCanceled() == true) {
612 return new SyncResult(RESULT_CANCELED);
615 // if we're done, we stop the loop
616 if (checkResult(pullResult, ID_DONE)) {
619 if (checkResult(pullResult, ID_DATA) == false) {
620 // hmm there's an error
621 return new SyncResult(RESULT_CONNECTION_ERROR);
623 int length = ArrayHelper.swap32bitFromArray(pullResult, 4);
624 if (length > SYNC_DATA_MAX) {
627 return new SyncResult(RESULT_BUFFER_OVERRUN);
631 // now read the length we received
632 AdbHelper.read(mChannel, data, length, timeOut);
634 // get the header for the next packet.
635 AdbHelper.read(mChannel, pullResult, -1, timeOut);
636 } catch (IOException e) {
637 return new SyncResult(RESULT_CONNECTION_ERROR, e);
640 // write the content in the file
642 fos.write(data, 0, length);
643 } catch (IOException e) {
644 return new SyncResult(RESULT_FILE_WRITE_ERROR, e);
647 monitor.advance(length);
652 } catch (IOException e) {
653 return new SyncResult(RESULT_FILE_WRITE_ERROR, e);
655 return new SyncResult(RESULT_OK);
660 * Push multiple files
664 * @return a {@link SyncResult} object with a code and an optional message.
666 private SyncResult doPush(File[] fileArray, String remotePath, ISyncProgressMonitor monitor) {
667 for (File f : fileArray) {
668 // check if we're canceled
669 if (monitor.isCanceled() == true) {
670 return new SyncResult(RESULT_CANCELED);
673 if (f.isDirectory()) {
674 // append the name of the directory to the remote path
675 String dest = remotePath + "/" + f.getName(); // $NON-NLS-1S
676 monitor.startSubTask(dest);
677 SyncResult result = doPush(f.listFiles(), dest, monitor);
679 if (result.mCode != RESULT_OK) {
684 } else if (f.isFile()) {
685 // append the name of the file to the remote path
686 String remoteFile = remotePath + "/" + f.getName(); // $NON-NLS-1S
687 monitor.startSubTask(remoteFile);
688 SyncResult result = doPushFile(f.getAbsolutePath(), remoteFile, monitor);
689 if (result.mCode != RESULT_OK) {
696 return new SyncResult(RESULT_OK);
701 * @param localPath the local file to push
702 * @param remotePath the remote file (length max is 1024)
703 * @param monitor the monitor. The monitor must be started already.
704 * @return a {@link SyncResult} object with a code and an optional message.
706 private SyncResult doPushFile(String localPath, String remotePath,
707 ISyncProgressMonitor monitor) {
708 FileInputStream fis = null;
711 final int timeOut = DdmPreferences.getTimeOut();
714 byte[] remotePathContent = remotePath.getBytes(AdbHelper.DEFAULT_ENCODING);
716 if (remotePathContent.length > REMOTE_PATH_MAX_LENGTH) {
717 return new SyncResult(RESULT_REMOTE_PATH_LENGTH);
720 File f = new File(localPath);
722 // this shouldn't happen but still...
723 if (f.exists() == false) {
724 return new SyncResult(RESULT_NO_LOCAL_FILE);
727 // create the stream to read the file
728 fis = new FileInputStream(f);
730 // create the header for the action
731 msg = createSendFileReq(ID_SEND, remotePathContent, 0644);
732 } catch (UnsupportedEncodingException e) {
733 return new SyncResult(RESULT_REMOTE_PATH_ENCODING, e);
734 } catch (FileNotFoundException e) {
735 return new SyncResult(RESULT_FILE_READ_ERROR, e);
738 // and send it. We use a custom try/catch block to make the difference between
739 // file and network IO exceptions.
741 AdbHelper.write(mChannel, msg, -1, timeOut);
742 } catch (IOException e) {
743 return new SyncResult(RESULT_CONNECTION_ERROR, e);
746 // create the buffer used to read.
747 // we read max SYNC_DATA_MAX, but we need 2 4 bytes at the beginning.
748 if (mBuffer == null) {
749 mBuffer = new byte[SYNC_DATA_MAX + 8];
751 System.arraycopy(ID_DATA, 0, mBuffer, 0, ID_DATA.length);
753 // look while there is something to read
755 // check if we're canceled
756 if (monitor.isCanceled() == true) {
757 return new SyncResult(RESULT_CANCELED);
760 // read up to SYNC_DATA_MAX
763 readCount = fis.read(mBuffer, 8, SYNC_DATA_MAX);
764 } catch (IOException e) {
765 return new SyncResult(RESULT_FILE_READ_ERROR, e);
768 if (readCount == -1) {
769 // we reached the end of the file
773 // now send the data to the device
774 // first write the amount read
775 ArrayHelper.swap32bitsToArray(readCount, mBuffer, 4);
779 AdbHelper.write(mChannel, mBuffer, readCount+8, timeOut);
780 } catch (IOException e) {
781 return new SyncResult(RESULT_CONNECTION_ERROR, e);
784 // and advance the monitor
785 monitor.advance(readCount);
787 // close the local file
790 } catch (IOException e) {
791 return new SyncResult(RESULT_FILE_READ_ERROR, e);
795 // create the DONE message
796 long time = System.currentTimeMillis() / 1000;
797 msg = createReq(ID_DONE, (int)time);
800 AdbHelper.write(mChannel, msg, -1, timeOut);
802 // read the result, in a byte array containing 2 ints
804 byte[] result = new byte[8];
805 AdbHelper.read(mChannel, result, -1 /* full length */, timeOut);
807 if (checkResult(result, ID_OKAY) == false) {
808 if (checkResult(result, ID_FAIL)) {
809 // read some error message...
810 int len = ArrayHelper.swap32bitFromArray(result, 4);
812 AdbHelper.read(mChannel, mBuffer, len, timeOut);
814 // output the result?
815 String message = new String(mBuffer, 0, len);
816 Log.e("ddms", "transfer error: " + message);
817 return new SyncResult(RESULT_UNKNOWN_ERROR, message);
820 return new SyncResult(RESULT_UNKNOWN_ERROR);
822 } catch (IOException e) {
823 return new SyncResult(RESULT_CONNECTION_ERROR, e);
826 return new SyncResult(RESULT_OK);
830 * Returns the mode of the remote file.
831 * @param path the remote file
832 * @return and Integer containing the mode if all went well or null
835 private Integer readMode(String path) {
837 // create the stat request message.
838 byte[] msg = createFileReq(ID_STAT, path);
840 AdbHelper.write(mChannel, msg, -1 /* full length */, DdmPreferences.getTimeOut());
842 // read the result, in a byte array containing 4 ints
843 // (id, mode, size, time)
844 byte[] statResult = new byte[16];
845 AdbHelper.read(mChannel, statResult, -1 /* full length */, DdmPreferences.getTimeOut());
847 // check we have the proper data back
848 if (checkResult(statResult, ID_STAT) == false) {
852 // we return the mode (2nd int in the array)
853 return ArrayHelper.swap32bitFromArray(statResult, 4);
854 } catch (IOException e) {
860 * Create a command with a code and an int values
865 private static byte[] createReq(byte[] command, int value) {
866 byte[] array = new byte[8];
868 System.arraycopy(command, 0, array, 0, 4);
869 ArrayHelper.swap32bitsToArray(value, array, 4);
875 * Creates the data array for a stat request.
876 * @param command the 4 byte command (ID_STAT, ID_RECV, ...)
877 * @param path The path of the remote file on which to execute the command
878 * @return the byte[] to send to the device through adb
880 private static byte[] createFileReq(byte[] command, String path) {
881 byte[] pathContent = null;
883 pathContent = path.getBytes(AdbHelper.DEFAULT_ENCODING);
884 } catch (UnsupportedEncodingException e) {
888 return createFileReq(command, pathContent);
892 * Creates the data array for a file request. This creates an array with a 4 byte command + the
894 * @param command the 4 byte command (ID_STAT, ID_RECV, ...).
895 * @param path The path, as a byte array, of the remote file on which to
896 * execute the command.
897 * @return the byte[] to send to the device through adb
899 private static byte[] createFileReq(byte[] command, byte[] path) {
900 byte[] array = new byte[8 + path.length];
902 System.arraycopy(command, 0, array, 0, 4);
903 ArrayHelper.swap32bitsToArray(path.length, array, 4);
904 System.arraycopy(path, 0, array, 8, path.length);
909 private static byte[] createSendFileReq(byte[] command, byte[] path, int mode) {
910 // make the mode into a string
911 String modeStr = "," + (mode & 0777); // $NON-NLS-1S
912 byte[] modeContent = null;
914 modeContent = modeStr.getBytes(AdbHelper.DEFAULT_ENCODING);
915 } catch (UnsupportedEncodingException e) {
919 byte[] array = new byte[8 + path.length + modeContent.length];
921 System.arraycopy(command, 0, array, 0, 4);
922 ArrayHelper.swap32bitsToArray(path.length + modeContent.length, array, 4);
923 System.arraycopy(path, 0, array, 8, path.length);
924 System.arraycopy(modeContent, 0, array, 8 + path.length, modeContent.length);
932 * Checks the result array starts with the provided code
933 * @param result The result array to check
934 * @param code The 4 byte code.
935 * @return true if the code matches.
937 private static boolean checkResult(byte[] result, byte[] code) {
938 if (result[0] != code[0] ||
939 result[1] != code[1] ||
940 result[2] != code[2] ||
941 result[3] != code[3]) {
949 private static int getFileType(int mode) {
950 if ((mode & S_ISOCK) == S_ISOCK) {
951 return FileListingService.TYPE_SOCKET;
954 if ((mode & S_IFLNK) == S_IFLNK) {
955 return FileListingService.TYPE_LINK;
958 if ((mode & S_IFREG) == S_IFREG) {
959 return FileListingService.TYPE_FILE;
962 if ((mode & S_IFBLK) == S_IFBLK) {
963 return FileListingService.TYPE_BLOCK;
966 if ((mode & S_IFDIR) == S_IFDIR) {
967 return FileListingService.TYPE_DIRECTORY;
970 if ((mode & S_IFCHR) == S_IFCHR) {
971 return FileListingService.TYPE_CHARACTER;
974 if ((mode & S_IFIFO) == S_IFIFO) {
975 return FileListingService.TYPE_FIFO;
978 return FileListingService.TYPE_OTHER;