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