blob: 19c3c14d67013f6bd1f80e5fbe00a62bffb847fc [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ddmlib;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ddmlib.AdbHelper.AdbResponse;
import com.android.ddmlib.FileListingService.FileEntry;
import com.android.ddmlib.SyncException.SyncError;
import com.android.ddmlib.utils.ArrayHelper;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Date;
/**
* Sync service class to push/pull to/from devices/emulators, through the debug bridge.
* <p/>
* To get a {@link SyncService} object, use {@link Device#getSyncService()}.
*/
public class SyncService {
private static final byte[] ID_OKAY = { 'O', 'K', 'A', 'Y' };
private static final byte[] ID_FAIL = { 'F', 'A', 'I', 'L' };
private static final byte[] ID_STAT = { 'S', 'T', 'A', 'T' };
private static final byte[] ID_RECV = { 'R', 'E', 'C', 'V' };
private static final byte[] ID_DATA = { 'D', 'A', 'T', 'A' };
private static final byte[] ID_DONE = { 'D', 'O', 'N', 'E' };
private static final byte[] ID_SEND = { 'S', 'E', 'N', 'D' };
// private final static byte[] ID_LIST = { 'L', 'I', 'S', 'T' };
// private final static byte[] ID_DENT = { 'D', 'E', 'N', 'T' };
private static final NullSyncProgressMonitor sNullSyncProgressMonitor =
new NullSyncProgressMonitor();
private static final int S_ISOCK = 0xC000; // type: symbolic link
private static final int S_IFLNK = 0xA000; // type: symbolic link
private static final int S_IFREG = 0x8000; // type: regular file
private static final int S_IFBLK = 0x6000; // type: block device
private static final int S_IFDIR = 0x4000; // type: directory
private static final int S_IFCHR = 0x2000; // type: character device
private static final int S_IFIFO = 0x1000; // type: fifo
/*
private final static int S_ISUID = 0x0800; // set-uid bit
private final static int S_ISGID = 0x0400; // set-gid bit
private final static int S_ISVTX = 0x0200; // sticky bit
private final static int S_IRWXU = 0x01C0; // user permissions
private final static int S_IRUSR = 0x0100; // user: read
private final static int S_IWUSR = 0x0080; // user: write
private final static int S_IXUSR = 0x0040; // user: execute
private final static int S_IRWXG = 0x0038; // group permissions
private final static int S_IRGRP = 0x0020; // group: read
private final static int S_IWGRP = 0x0010; // group: write
private final static int S_IXGRP = 0x0008; // group: execute
private final static int S_IRWXO = 0x0007; // other permissions
private final static int S_IROTH = 0x0004; // other: read
private final static int S_IWOTH = 0x0002; // other: write
private final static int S_IXOTH = 0x0001; // other: execute
*/
private static final int SYNC_DATA_MAX = 64*1024;
private static final int REMOTE_PATH_MAX_LENGTH = 1024;
/**
* Classes which implement this interface provide methods that deal
* with displaying transfer progress.
*/
public interface ISyncProgressMonitor {
/**
* Sent when the transfer starts
* @param totalWork the total amount of work.
*/
void start(int totalWork);
/**
* Sent when the transfer is finished or interrupted.
*/
void stop();
/**
* Sent to query for possible cancellation.
* @return true if the transfer should be stopped.
*/
boolean isCanceled();
/**
* Sent when a sub task is started.
* @param name the name of the sub task.
*/
void startSubTask(String name);
/**
* Sent when some progress have been made.
* @param work the amount of work done.
*/
void advance(int work);
}
public static class FileStat {
private final int myMode;
private final int mySize;
private final Date myLastModified;
public FileStat(int mode, int size, int lastModifiedSecs) {
myMode = mode;
mySize = size;
myLastModified = new Date((long)(lastModifiedSecs) * 1000);
}
public int getMode() {
return myMode;
}
public int getSize() {
return mySize;
}
public Date getLastModified() {
return myLastModified;
}
}
/**
* A Sync progress monitor that does nothing
*/
private static class NullSyncProgressMonitor implements ISyncProgressMonitor {
@Override
public void advance(int work) {
}
@Override
public boolean isCanceled() {
return false;
}
@Override
public void start(int totalWork) {
}
@Override
public void startSubTask(String name) {
}
@Override
public void stop() {
}
}
private InetSocketAddress mAddress;
private Device mDevice;
private SocketChannel mChannel;
/**
* Buffer used to send data. Allocated when needed and reused afterward.
*/
private byte[] mBuffer;
/**
* Creates a Sync service object.
* @param address The address to connect to
* @param device the {@link Device} that the service connects to.
*/
SyncService(InetSocketAddress address, Device device) {
mAddress = address;
mDevice = device;
}
/**
* Opens the sync connection. This must be called before any calls to push[File] / pull[File].
* @return true if the connection opened, false if adb refuse the connection. This can happen
* if the {@link Device} is invalid.
* @throws TimeoutException in case of timeout on the connection.
* @throws AdbCommandRejectedException if adb rejects the command
* @throws IOException If the connection to adb failed.
*/
boolean openSync() throws TimeoutException, AdbCommandRejectedException, IOException {
try {
mChannel = SocketChannel.open(mAddress);
mChannel.configureBlocking(false);
// target a specific device
AdbHelper.setDevice(mChannel, mDevice);
byte[] request = AdbHelper.formAdbRequest("sync:"); //$NON-NLS-1$
AdbHelper.write(mChannel, request, -1, DdmPreferences.getTimeOut());
AdbResponse resp = AdbHelper.readAdbResponse(mChannel, false /* readDiagString */);
if (!resp.okay) {
Log.w("ddms", "Got unhappy response from ADB sync req: " + resp.message);
mChannel.close();
mChannel = null;
return false;
}
} catch (TimeoutException e) {
if (mChannel != null) {
try {
mChannel.close();
} catch (IOException e2) {
// we want to throw the original exception, so we ignore this one.
}
mChannel = null;
}
throw e;
} catch (IOException e) {
if (mChannel != null) {
try {
mChannel.close();
} catch (IOException e2) {
// we want to throw the original exception, so we ignore this one.
}
mChannel = null;
}
throw e;
}
return true;
}
/**
* Closes the connection.
*/
public void close() {
if (mChannel != null) {
try {
mChannel.close();
} catch (IOException e) {
// nothing to be done really...
}
mChannel = null;
}
}
/**
* Returns a sync progress monitor that does nothing. This allows background tasks that don't
* want/need to display ui, to pass a valid {@link ISyncProgressMonitor}.
* <p/>This object can be reused multiple times and can be used by concurrent threads.
*/
public static ISyncProgressMonitor getNullProgressMonitor() {
return sNullSyncProgressMonitor;
}
/**
* Pulls file(s) or folder(s).
* @param entries the remote item(s) to pull
* @param localPath The local destination. If the entries count is > 1 or
* if the unique entry is a folder, this should be a folder.
* @param monitor The progress monitor. Cannot be null.
* @throws SyncException
* @throws IOException
* @throws TimeoutException
*
* @see FileListingService.FileEntry
* @see #getNullProgressMonitor()
*/
public void pull(FileEntry[] entries, String localPath, ISyncProgressMonitor monitor)
throws SyncException, IOException, TimeoutException {
// first we check the destination is a directory and exists
File f = new File(localPath);
if (!f.exists()) {
throw new SyncException(SyncError.NO_DIR_TARGET);
}
if (!f.isDirectory()) {
throw new SyncException(SyncError.TARGET_IS_FILE);
}
// get a FileListingService object
FileListingService fls = new FileListingService(mDevice);
// compute the number of file to move
int total = getTotalRemoteFileSize(entries, fls);
// start the monitor
monitor.start(total);
doPull(entries, localPath, fls, monitor);
monitor.stop();
}
/**
* Pulls a single file.
* @param remote the remote file
* @param localFilename The local destination.
* @param monitor The progress monitor. Cannot be null.
*
* @throws IOException in case of an IO exception.
* @throws TimeoutException in case of a timeout reading responses from the device.
* @throws SyncException in case of a sync exception.
*
* @see FileListingService.FileEntry
* @see #getNullProgressMonitor()
*/
public void pullFile(FileEntry remote, String localFilename, ISyncProgressMonitor monitor)
throws IOException, SyncException, TimeoutException {
int total = remote.getSizeValue();
monitor.start(total);
doPullFile(remote.getFullPath(), localFilename, monitor);
monitor.stop();
}
/**
* Pulls a single file.
* <p/>Because this method just deals with a String for the remote file instead of a
* {@link FileEntry}, the size of the file being pulled is unknown and the
* {@link ISyncProgressMonitor} will not properly show the progress
* @param remoteFilepath the full path to the remote file
* @param localFilename The local destination.
* @param monitor The progress monitor. Cannot be null.
*
* @throws IOException in case of an IO exception.
* @throws TimeoutException in case of a timeout reading responses from the device.
* @throws SyncException in case of a sync exception.
*
* @see #getNullProgressMonitor()
*/
public void pullFile(String remoteFilepath, String localFilename,
ISyncProgressMonitor monitor) throws TimeoutException, IOException, SyncException {
FileStat fileStat = statFile(remoteFilepath);
if (fileStat == null) {
// attempts to download anyway
} else if (fileStat.getMode() == 0) {
throw new SyncException(SyncError.NO_REMOTE_OBJECT);
}
monitor.start(0);
//TODO: use the {@link FileListingService} to get the file size.
doPullFile(remoteFilepath, localFilename, monitor);
monitor.stop();
}
/**
* Push several files.
* @param local An array of loca files to push
* @param remote the remote {@link FileEntry} representing a directory.
* @param monitor The progress monitor. Cannot be null.
* @throws SyncException if file could not be pushed
* @throws IOException in case of I/O error on the connection.
* @throws TimeoutException in case of a timeout reading responses from the device.
*/
public void push(String[] local, FileEntry remote, ISyncProgressMonitor monitor)
throws SyncException, IOException, TimeoutException {
if (!remote.isDirectory()) {
throw new SyncException(SyncError.REMOTE_IS_FILE);
}
// make a list of File from the list of String
ArrayList<File> files = new ArrayList<File>();
for (String path : local) {
files.add(new File(path));
}
// get the total count of the bytes to transfer
File[] fileArray = files.toArray(new File[files.size()]);
int total = getTotalLocalFileSize(fileArray);
monitor.start(total);
doPush(fileArray, remote.getFullPath(), monitor);
monitor.stop();
}
/**
* Push a single file.
* @param local the local filepath.
* @param remote The remote filepath.
* @param monitor The progress monitor. Cannot be null.
*
* @throws SyncException if file could not be pushed
* @throws IOException in case of I/O error on the connection.
* @throws TimeoutException in case of a timeout reading responses from the device.
*/
public void pushFile(String local, String remote, ISyncProgressMonitor monitor)
throws SyncException, IOException, TimeoutException {
File f = new File(local);
if (!f.exists()) {
throw new SyncException(SyncError.NO_LOCAL_FILE);
}
if (f.isDirectory()) {
throw new SyncException(SyncError.LOCAL_IS_DIRECTORY);
}
monitor.start((int)f.length());
doPushFile(local, remote, monitor);
monitor.stop();
}
/**
* compute the recursive file size of all the files in the list. Folder
* have a weight of 1.
* @param entries
* @param fls
* @return
*/
private int getTotalRemoteFileSize(FileEntry[] entries, FileListingService fls) {
int count = 0;
for (FileEntry e : entries) {
int type = e.getType();
if (type == FileListingService.TYPE_DIRECTORY) {
// get the children
FileEntry[] children = fls.getChildren(e, false, null);
count += getTotalRemoteFileSize(children, fls) + 1;
} else if (type == FileListingService.TYPE_FILE) {
count += e.getSizeValue();
}
}
return count;
}
/**
* compute the recursive file size of all the files in the list. Folder
* have a weight of 1.
* This does not check for circular links.
* @param files
* @return
*/
private int getTotalLocalFileSize(File[] files) {
int count = 0;
for (File f : files) {
if (f.exists()) {
if (f.isDirectory()) {
return getTotalLocalFileSize(f.listFiles()) + 1;
} else if (f.isFile()) {
count += f.length();
}
}
}
return count;
}
/**
* Pulls multiple files/folders recursively.
* @param entries The list of entry to pull
* @param localPath the localpath to a directory
* @param fileListingService a FileListingService object to browse through remote directories.
* @param monitor the progress monitor. Must be started already.
*
* @throws SyncException if file could not be pushed
* @throws IOException in case of I/O error on the connection.
* @throws TimeoutException in case of a timeout reading responses from the device.
*/
private void doPull(FileEntry[] entries, String localPath,
FileListingService fileListingService,
ISyncProgressMonitor monitor) throws SyncException, IOException, TimeoutException {
for (FileEntry e : entries) {
// check if we're cancelled
if (monitor.isCanceled()) {
throw new SyncException(SyncError.CANCELED);
}
// get type (we only pull directory and files for now)
int type = e.getType();
if (type == FileListingService.TYPE_DIRECTORY) {
monitor.startSubTask(e.getFullPath());
String dest = localPath + File.separator + e.getName();
// make the directory
File d = new File(dest);
d.mkdir();
// then recursively call the content. Since we did a ls command
// to get the number of files, we can use the cache
FileEntry[] children = fileListingService.getChildren(e, true, null);
doPull(children, dest, fileListingService, monitor);
monitor.advance(1);
} else if (type == FileListingService.TYPE_FILE) {
monitor.startSubTask(e.getFullPath());
String dest = localPath + File.separator + e.getName();
doPullFile(e.getFullPath(), dest, monitor);
}
}
}
/**
* Pulls a remote file
* @param remotePath the remote file (length max is 1024)
* @param localPath the local destination
* @param monitor the monitor. The monitor must be started already.
* @throws SyncException if file could not be pushed
* @throws IOException in case of I/O error on the connection.
* @throws TimeoutException in case of a timeout reading responses from the device.
*/
private void doPullFile(String remotePath, String localPath,
ISyncProgressMonitor monitor) throws IOException, SyncException, TimeoutException {
byte[] msg = null;
byte[] pullResult = new byte[8];
final int timeOut = DdmPreferences.getTimeOut();
try {
byte[] remotePathContent = remotePath.getBytes(AdbHelper.DEFAULT_ENCODING);
if (remotePathContent.length > REMOTE_PATH_MAX_LENGTH) {
throw new SyncException(SyncError.REMOTE_PATH_LENGTH);
}
// create the full request message
msg = createFileReq(ID_RECV, remotePathContent);
// and send it.
AdbHelper.write(mChannel, msg, -1, timeOut);
// read the result, in a byte array containing 2 ints
// (id, size)
AdbHelper.read(mChannel, pullResult, -1, timeOut);
// check we have the proper data back
if (!checkResult(pullResult, ID_DATA) &&
!checkResult(pullResult, ID_DONE)) {
throw new SyncException(SyncError.TRANSFER_PROTOCOL_ERROR,
readErrorMessage(pullResult, timeOut));
}
} catch (UnsupportedEncodingException e) {
throw new SyncException(SyncError.REMOTE_PATH_ENCODING, e);
}
// access the destination file
File f = new File(localPath);
// create the stream to write in the file. We use a new try/catch block to differentiate
// between file and network io exceptions.
FileOutputStream fos = null;
try {
fos = new FileOutputStream(f);
// the buffer to read the data
byte[] data = new byte[SYNC_DATA_MAX];
// loop to get data until we're done.
while (true) {
// check if we're cancelled
if (monitor.isCanceled()) {
throw new SyncException(SyncError.CANCELED);
}
// if we're done, we stop the loop
if (checkResult(pullResult, ID_DONE)) {
break;
}
if (!checkResult(pullResult, ID_DATA)) {
// hmm there's an error
throw new SyncException(SyncError.TRANSFER_PROTOCOL_ERROR,
readErrorMessage(pullResult, timeOut));
}
int length = ArrayHelper.swap32bitFromArray(pullResult, 4);
if (length > SYNC_DATA_MAX) {
// buffer overrun!
// error and exit
throw new SyncException(SyncError.BUFFER_OVERRUN);
}
// now read the length we received
AdbHelper.read(mChannel, data, length, timeOut);
// get the header for the next packet.
AdbHelper.read(mChannel, pullResult, -1, timeOut);
// write the content in the file
fos.write(data, 0, length);
monitor.advance(length);
}
fos.flush();
} catch (IOException e) {
Log.e("ddms", String.format("Failed to open local file %s for writing, Reason: %s",
f.getAbsolutePath(), e.toString()));
throw new SyncException(SyncError.FILE_WRITE_ERROR);
} finally {
if (fos != null) {
fos.close();
}
}
}
/**
* Push multiple files
* @param fileArray
* @param remotePath
* @param monitor
*
* @throws SyncException if file could not be pushed
* @throws IOException in case of I/O error on the connection.
* @throws TimeoutException in case of a timeout reading responses from the device.
*/
private void doPush(File[] fileArray, String remotePath, ISyncProgressMonitor monitor)
throws SyncException, IOException, TimeoutException {
for (File f : fileArray) {
// check if we're canceled
if (monitor.isCanceled()) {
throw new SyncException(SyncError.CANCELED);
}
if (f.exists()) {
if (f.isDirectory()) {
// append the name of the directory to the remote path
String dest = remotePath + "/" + f.getName(); // $NON-NLS-1S
monitor.startSubTask(dest);
doPush(f.listFiles(), dest, monitor);
monitor.advance(1);
} else if (f.isFile()) {
// append the name of the file to the remote path
String remoteFile = remotePath + "/" + f.getName(); // $NON-NLS-1S
monitor.startSubTask(remoteFile);
doPushFile(f.getAbsolutePath(), remoteFile, monitor);
}
}
}
}
/**
* Push a single file
* @param localPath the local file to push
* @param remotePath the remote file (length max is 1024)
* @param monitor the monitor. The monitor must be started already.
*
* @throws SyncException if file could not be pushed
* @throws IOException in case of I/O error on the connection.
* @throws TimeoutException in case of a timeout reading responses from the device.
*/
private void doPushFile(String localPath, String remotePath,
ISyncProgressMonitor monitor) throws SyncException, IOException, TimeoutException {
FileInputStream fis = null;
byte[] msg;
final int timeOut = DdmPreferences.getTimeOut();
File f = new File(localPath);
try {
byte[] remotePathContent = remotePath.getBytes(AdbHelper.DEFAULT_ENCODING);
if (remotePathContent.length > REMOTE_PATH_MAX_LENGTH) {
throw new SyncException(SyncError.REMOTE_PATH_LENGTH);
}
// create the stream to read the file
fis = new FileInputStream(f);
// create the header for the action
msg = createSendFileReq(ID_SEND, remotePathContent, 0644);
// and send it. We use a custom try/catch block to make the difference between
// file and network IO exceptions.
AdbHelper.write(mChannel, msg, -1, timeOut);
System.arraycopy(ID_DATA, 0, getBuffer(), 0, ID_DATA.length);
// look while there is something to read
while (true) {
// check if we're canceled
if (monitor.isCanceled()) {
throw new SyncException(SyncError.CANCELED);
}
// read up to SYNC_DATA_MAX
int readCount = fis.read(getBuffer(), 8, SYNC_DATA_MAX);
if (readCount == -1) {
// we reached the end of the file
break;
}
// now send the data to the device
// first write the amount read
ArrayHelper.swap32bitsToArray(readCount, getBuffer(), 4);
// now write it
AdbHelper.write(mChannel, getBuffer(), readCount+8, timeOut);
// and advance the monitor
monitor.advance(readCount);
}
} catch (UnsupportedEncodingException e) {
throw new SyncException(SyncError.REMOTE_PATH_ENCODING, e);
} finally {
// close the local file
if (fis != null) {
fis.close();
}
}
// create the DONE message
long time = f.lastModified() / 1000;
msg = createReq(ID_DONE, (int)time);
// and send it.
AdbHelper.write(mChannel, msg, -1, timeOut);
// read the result, in a byte array containing 2 ints
// (id, size)
byte[] result = new byte[8];
AdbHelper.read(mChannel, result, -1 /* full length */, timeOut);
if (!checkResult(result, ID_OKAY)) {
throw new SyncException(SyncError.TRANSFER_PROTOCOL_ERROR,
readErrorMessage(result, timeOut));
}
}
/**
* Reads an error message from the opened {@link #mChannel}.
* @param result the current adb result. Must contain both FAIL and the length of the message.
* @param timeOut
* @return
* @throws TimeoutException in case of a timeout reading responses from the device.
* @throws IOException
*/
private String readErrorMessage(byte[] result, final int timeOut) throws TimeoutException,
IOException {
if (checkResult(result, ID_FAIL)) {
int len = ArrayHelper.swap32bitFromArray(result, 4);
if (len > 0) {
AdbHelper.read(mChannel, getBuffer(), len, timeOut);
String message = new String(getBuffer(), 0, len);
Log.e("ddms", "transfer error: " + message);
return message;
}
}
return null;
}
/**
* Returns the stat info of the remote file.
* @param path the remote file
* @return an FileStat containing the mode, size and last modified info if all went well or null
* otherwise
* @throws IOException
* @throws TimeoutException in case of a timeout reading responses from the device.
*/
@Nullable
public FileStat statFile(@NonNull String path) throws TimeoutException, IOException {
// create the stat request message.
byte[] msg = createFileReq(ID_STAT, path);
AdbHelper.write(mChannel, msg, -1 /* full length */, DdmPreferences.getTimeOut());
// read the result, in a byte array containing 4 ints
// (id, mode, size, time)
byte[] statResult = new byte[16];
AdbHelper.read(mChannel, statResult, -1 /* full length */, DdmPreferences.getTimeOut());
// check we have the proper data back
if (!checkResult(statResult, ID_STAT)) {
return null;
}
final int mode = ArrayHelper.swap32bitFromArray(statResult, 4);
final int size = ArrayHelper.swap32bitFromArray(statResult, 8);
final int lastModifiedSecs = ArrayHelper.swap32bitFromArray(statResult, 12);
return new FileStat(mode, size, lastModifiedSecs);
}
/**
* Create a command with a code and an int values
* @param command
* @param value
* @return
*/
private static byte[] createReq(byte[] command, int value) {
byte[] array = new byte[8];
System.arraycopy(command, 0, array, 0, 4);
ArrayHelper.swap32bitsToArray(value, array, 4);
return array;
}
/**
* Creates the data array for a stat request.
* @param command the 4 byte command (ID_STAT, ID_RECV, ...)
* @param path The path of the remote file on which to execute the command
* @return the byte[] to send to the device through adb
*/
private static byte[] createFileReq(byte[] command, String path) {
byte[] pathContent = null;
try {
pathContent = path.getBytes(AdbHelper.DEFAULT_ENCODING);
} catch (UnsupportedEncodingException e) {
return null;
}
return createFileReq(command, pathContent);
}
/**
* Creates the data array for a file request. This creates an array with a 4 byte command + the
* remote file name.
* @param command the 4 byte command (ID_STAT, ID_RECV, ...).
* @param path The path, as a byte array, of the remote file on which to
* execute the command.
* @return the byte[] to send to the device through adb
*/
private static byte[] createFileReq(byte[] command, byte[] path) {
byte[] array = new byte[8 + path.length];
System.arraycopy(command, 0, array, 0, 4);
ArrayHelper.swap32bitsToArray(path.length, array, 4);
System.arraycopy(path, 0, array, 8, path.length);
return array;
}
private static byte[] createSendFileReq(byte[] command, byte[] path, int mode) {
// make the mode into a string
String modeStr = "," + (mode & 0777); // $NON-NLS-1S
byte[] modeContent = null;
try {
modeContent = modeStr.getBytes(AdbHelper.DEFAULT_ENCODING);
} catch (UnsupportedEncodingException e) {
return null;
}
byte[] array = new byte[8 + path.length + modeContent.length];
System.arraycopy(command, 0, array, 0, 4);
ArrayHelper.swap32bitsToArray(path.length + modeContent.length, array, 4);
System.arraycopy(path, 0, array, 8, path.length);
System.arraycopy(modeContent, 0, array, 8 + path.length, modeContent.length);
return array;
}
/**
* Checks the result array starts with the provided code
* @param result The result array to check
* @param code The 4 byte code.
* @return true if the code matches.
*/
private static boolean checkResult(byte[] result, byte[] code) {
return !(result[0] != code[0] ||
result[1] != code[1] ||
result[2] != code[2] ||
result[3] != code[3]);
}
private static int getFileType(int mode) {
if ((mode & S_ISOCK) == S_ISOCK) {
return FileListingService.TYPE_SOCKET;
}
if ((mode & S_IFLNK) == S_IFLNK) {
return FileListingService.TYPE_LINK;
}
if ((mode & S_IFREG) == S_IFREG) {
return FileListingService.TYPE_FILE;
}
if ((mode & S_IFBLK) == S_IFBLK) {
return FileListingService.TYPE_BLOCK;
}
if ((mode & S_IFDIR) == S_IFDIR) {
return FileListingService.TYPE_DIRECTORY;
}
if ((mode & S_IFCHR) == S_IFCHR) {
return FileListingService.TYPE_CHARACTER;
}
if ((mode & S_IFIFO) == S_IFIFO) {
return FileListingService.TYPE_FIFO;
}
return FileListingService.TYPE_OTHER;
}
/**
* Retrieve the buffer, allocating if necessary
* @return
*/
private byte[] getBuffer() {
if (mBuffer == null) {
// create the buffer used to read.
// we read max SYNC_DATA_MAX, but we need 2 4 bytes at the beginning.
mBuffer = new byte[SYNC_DATA_MAX + 8];
}
return mBuffer;
}
}