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