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