Implement seekability for file descriptors (#6)
* Implement seekability for file descriptors
This commit is contained in:
parent
ef48c1647e
commit
228ca3f9d7
17 changed files with 377 additions and 23 deletions
|
@ -1,12 +1,12 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
compileSdkVersion 26
|
||||
buildToolsVersion "25.0.2"
|
||||
defaultConfig {
|
||||
applicationId "com.google.android.sambadocumentsprovider"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
targetSdkVersion 26
|
||||
versionCode 102
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
|
|
|
@ -527,6 +527,46 @@ Java_com_google_android_sambadocumentsprovider_nativefacade_NativeSambaFacade_op
|
|||
return fd;
|
||||
}
|
||||
|
||||
jobject Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_fstat(
|
||||
JNIEnv *env,
|
||||
jobject instance,
|
||||
jlong pointer,
|
||||
jint fd) {
|
||||
SambaClient::SambaClient *client =
|
||||
reinterpret_cast<SambaClient::SambaClient*>(pointer);
|
||||
|
||||
jobject stat = NULL;
|
||||
|
||||
struct stat st;
|
||||
|
||||
int result = client->Fstat(fd, &st);
|
||||
if (result < 0) {
|
||||
throw_new_errno_exception(env, "stat", -result);
|
||||
}
|
||||
|
||||
return create_structstat(env, st);
|
||||
}
|
||||
|
||||
jlong Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_seek(
|
||||
JNIEnv *env,
|
||||
jobject instance,
|
||||
jlong pointer,
|
||||
jint fd,
|
||||
jlong offset,
|
||||
jint whence) {
|
||||
SambaClient::SambaClient *client =
|
||||
reinterpret_cast<SambaClient::SambaClient*>(pointer);
|
||||
|
||||
jlong result = client->SeekFile(fd, offset, whence);
|
||||
if (result < 0) {
|
||||
throw_new_errno_exception(env, "seek", static_cast<int>(-result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
jlong Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_read(
|
||||
JNIEnv *env,
|
||||
jobject instance,
|
||||
|
|
|
@ -68,6 +68,14 @@ JNIEXPORT jlong JNICALL
|
|||
Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_read(
|
||||
JNIEnv *env, jobject instance, jlong pointer, jint fd, jobject buffer, jint maxlen);
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_seek(
|
||||
JNIEnv *env, jobject instance, jlong pointer, jint fd, jlong offset, jint whence);
|
||||
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_fstat(
|
||||
JNIEnv *env, jobject instance, jlong pointer, jint fd);
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_write(
|
||||
JNIEnv *env, jobject instance, jlong pointer, jint fd, jobject buffer, jint length);
|
||||
|
|
|
@ -160,6 +160,19 @@ SambaClient::ReadDir(
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
SambaClient::Fstat(const int fd, struct stat * const st) {
|
||||
LOGD(TAG, "Getting stat for %x.", fd);
|
||||
int result = smbc_fstat(fd, st);
|
||||
if (result < 0) {
|
||||
int err = errno;
|
||||
LOGE(TAG, "Failed to obtain stat for %x. Errno: %x.", fd, err);
|
||||
return -err;
|
||||
}
|
||||
LOGV(TAG, "Got stat for %x.", fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
SambaClient::Stat(const char *url, struct stat * const st) {
|
||||
LOGD(TAG, "Getting stat for %s.", url);
|
||||
|
@ -251,6 +264,20 @@ int SambaClient::OpenFile(const char *url, const int flag, const mode_t mode) {
|
|||
return fd;
|
||||
}
|
||||
|
||||
off_t
|
||||
SambaClient::SeekFile(const int fd, const off_t offset, const int whence) {
|
||||
LOGV(TAG, "Set offset to %x for file with fd %x", offset, fd);
|
||||
|
||||
off_t result = smbc_lseek(fd, offset, whence);
|
||||
if (result < 0) {
|
||||
int err = errno;
|
||||
LOGE(TAG, "Failed to seek in file %x. Errno: %x", fd, err);
|
||||
return -err;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
SambaClient::ReadFile(const int fd, void *buffer, const size_t maxlen) {
|
||||
LOGV(TAG, "Reading max %lu bytes from file with fd %x", maxlen, fd);
|
||||
|
|
|
@ -18,10 +18,11 @@
|
|||
#ifndef SAMBADOCUMENTSPROVIDER_SAMBAPROVIDER_H
|
||||
#define SAMBADOCUMENTSPROVIDER_SAMBAPROVIDER_H
|
||||
|
||||
#include "samba_includes/libsmbclient.h"
|
||||
#include "base/Callback.h"
|
||||
#include "jni_helper/JniHelper.h"
|
||||
#include "samba_includes/libsmbclient.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <vector>
|
||||
|
||||
namespace SambaClient {
|
||||
|
@ -38,6 +39,8 @@ class SambaClient {
|
|||
|
||||
int Stat(const char *url, struct stat *st);
|
||||
|
||||
int Fstat(const int fd, struct stat * const st);
|
||||
|
||||
int CreateFile(const char *url);
|
||||
|
||||
int Mkdir(const char *url);
|
||||
|
@ -54,6 +57,8 @@ class SambaClient {
|
|||
|
||||
ssize_t WriteFile(const int fd, void *buffer, const size_t length);
|
||||
|
||||
off_t SeekFile(const int fd, const off_t offset, const int whence);
|
||||
|
||||
int CloseFile(const int fd);
|
||||
private:
|
||||
::SMBCCTX *sambaContext = NULL;
|
||||
|
|
|
@ -24,17 +24,18 @@ import android.net.ConnectivityManager.NetworkCallback;
|
|||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkRequest;
|
||||
|
||||
import com.google.android.sambadocumentsprovider.SambaConfiguration.OnConfigurationChangedListener;
|
||||
import com.google.android.sambadocumentsprovider.cache.DocumentCache;
|
||||
import com.google.android.sambadocumentsprovider.nativefacade.CredentialCache;
|
||||
import com.google.android.sambadocumentsprovider.nativefacade.SambaMessageLooper;
|
||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbClient;
|
||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbFacade;
|
||||
|
||||
public class SambaProviderApplication extends Application {
|
||||
|
||||
private final DocumentCache mCache = new DocumentCache();
|
||||
private final TaskManager mTaskManager = new TaskManager();
|
||||
private final SmbClient mSambaClient;
|
||||
private final SmbFacade mSambaClient;
|
||||
private final CredentialCache mCredentialCache;
|
||||
|
||||
private SambaConfiguration mSambaConf;
|
||||
|
@ -94,7 +95,7 @@ public class SambaProviderApplication extends Application {
|
|||
}
|
||||
}
|
||||
|
||||
public static SmbClient getSambaClient(Context context) {
|
||||
public static SmbFacade getSambaClient(Context context) {
|
||||
return getApplication(context).mSambaClient;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
|
||||
package com.google.android.sambadocumentsprovider.nativefacade;
|
||||
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.ProxyFileDescriptorCallback;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.StructStat;
|
||||
import com.google.android.sambadocumentsprovider.BuildConfig;
|
||||
|
|
|
@ -18,17 +18,25 @@
|
|||
package com.google.android.sambadocumentsprovider.nativefacade;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.system.StructStat;
|
||||
|
||||
import com.google.android.sambadocumentsprovider.base.DirectoryEntry;
|
||||
import com.google.android.sambadocumentsprovider.provider.ByteBufferPool;
|
||||
import com.google.android.sambadocumentsprovider.provider.SambaProxyFileCallback;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
class SambaFacadeClient extends BaseClient implements SmbClient {
|
||||
class SambaFacadeClient extends BaseClient implements SmbFacade {
|
||||
|
||||
@IntDef({ RESET, READ_DIR, STAT, MKDIR, RENAME, UNLINK, RMDIR, OPEN_FILE })
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
|
@ -134,14 +142,37 @@ class SambaFacadeClient extends BaseClient implements SmbClient {
|
|||
|
||||
@Override
|
||||
public SmbFile openFile(String uri, String mode) throws IOException {
|
||||
return new SambaFileClient(mHandler.getLooper(), openFileRaw(uri, mode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openProxyFile(
|
||||
String uri,
|
||||
String mode,
|
||||
StorageManager storageManager,
|
||||
ByteBufferPool bufferPool,
|
||||
CancellationSignal signal) throws IOException {
|
||||
SambaFile file = openFileRaw(uri, mode);
|
||||
return storageManager.openProxyFileDescriptor(
|
||||
ParcelFileDescriptor.parseMode(mode),
|
||||
new SambaProxyFileCallback(file, bufferPool, signal),
|
||||
mHandler);
|
||||
}
|
||||
|
||||
private SambaFile openFileRaw(String uri, String mode) throws IOException {
|
||||
try (final MessageValues<SambaFile> messageValues = MessageValues.obtain()) {
|
||||
final Message msg = obtainMessage(OPEN_FILE, messageValues, uri);
|
||||
msg.peekData().putString(MODE, mode);
|
||||
enqueue(msg);
|
||||
return new SambaFileClient(mHandler.getLooper(), messageValues.getObj());
|
||||
enqueue(obtainMessageForOpenFile(uri, mode, messageValues));
|
||||
return messageValues.getObj();
|
||||
}
|
||||
}
|
||||
|
||||
private Message obtainMessageForOpenFile(String uri, String mode,
|
||||
MessageValues<SambaFile> messageValues) {
|
||||
final Message msg = obtainMessage(OPEN_FILE, messageValues, uri);
|
||||
msg.peekData().putString(MODE, mode);
|
||||
return msg;
|
||||
}
|
||||
|
||||
private static class SambaServiceHandler extends BaseHandler {
|
||||
|
||||
private final SmbClient mClientImpl;
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
package com.google.android.sambadocumentsprovider.nativefacade;
|
||||
|
||||
import android.system.ErrnoException;
|
||||
import android.system.StructStat;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
@ -46,6 +49,23 @@ class SambaFile implements SmbFile {
|
|||
}
|
||||
}
|
||||
|
||||
public long seek(long offset) throws IOException {
|
||||
try {
|
||||
return seek(mNativeHandler, mNativeFd, offset, 0);
|
||||
} catch (ErrnoException e) {
|
||||
throw new IOException("Failed to move to offset in file. Fd: " + mNativeFd, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructStat fstat() throws IOException {
|
||||
try {
|
||||
return fstat(mNativeHandler, mNativeFd);
|
||||
} catch (ErrnoException e) {
|
||||
throw new IOException("Failed to get stat of " + mNativeFd, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
|
@ -63,5 +83,10 @@ class SambaFile implements SmbFile {
|
|||
private native int write(long handler, int fd, ByteBuffer buffer, int length)
|
||||
throws ErrnoException;
|
||||
|
||||
private native long seek(long handler, int fd, long offset, int whence)
|
||||
throws ErrnoException;
|
||||
|
||||
private native StructStat fstat(long handler, int fd) throws ErrnoException;
|
||||
|
||||
private native void close(long handler, int fd) throws ErrnoException;
|
||||
}
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
|
||||
package com.google.android.sambadocumentsprovider.nativefacade;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.system.StructStat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
@ -33,6 +36,10 @@ class SambaFileClient extends BaseClient implements SmbFile {
|
|||
private static final int READ = 1;
|
||||
private static final int WRITE = 2;
|
||||
private static final int CLOSE = 3;
|
||||
private static final int SEEK = 4;
|
||||
private static final int FSTAT = 5;
|
||||
|
||||
private static final String OFFSET = "offset";
|
||||
|
||||
SambaFileClient(Looper looper, SmbFile smbFileImpl) {
|
||||
mHandler = new SambaFileHandler(looper, smbFileImpl);
|
||||
|
@ -59,6 +66,30 @@ class SambaFileClient extends BaseClient implements SmbFile {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long seek(long offset) throws IOException {
|
||||
try (final MessageValues messageValues = MessageValues.obtain()) {
|
||||
final Message msg = mHandler.obtainMessage(SEEK, messageValues);
|
||||
|
||||
Bundle data = new Bundle();
|
||||
data.putLong(OFFSET, offset);
|
||||
|
||||
msg.setData(data);
|
||||
|
||||
enqueue(msg);
|
||||
return msg.peekData().getLong(OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructStat fstat() throws IOException {
|
||||
try (final MessageValues<StructStat> messageValues = MessageValues.obtain()) {
|
||||
final Message msg = mHandler.obtainMessage(FSTAT, messageValues);
|
||||
enqueue(msg);
|
||||
return messageValues.getObj();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try (final MessageValues messageValues = MessageValues.obtain()) {
|
||||
|
@ -81,22 +112,28 @@ class SambaFileClient extends BaseClient implements SmbFile {
|
|||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void processMessage(Message msg) {
|
||||
final MessageValues<ByteBuffer> messageValues = (MessageValues<ByteBuffer>) msg.obj;
|
||||
final MessageValues messageValues = (MessageValues) msg.obj;
|
||||
try {
|
||||
final ByteBuffer buffer = messageValues.getObj();
|
||||
|
||||
switch (msg.what) {
|
||||
case READ:
|
||||
messageValues.setInt(mSmbFileImpl.read(buffer));
|
||||
final ByteBuffer readBuffer = (ByteBuffer) messageValues.getObj();
|
||||
messageValues.setInt(mSmbFileImpl.read(readBuffer));
|
||||
break;
|
||||
case WRITE: {
|
||||
final ByteBuffer writeBuffer = (ByteBuffer) messageValues.getObj();
|
||||
final int length = msg.arg1;
|
||||
messageValues.setInt(mSmbFileImpl.write(buffer, length));
|
||||
messageValues.setInt(mSmbFileImpl.write(writeBuffer, length));
|
||||
break;
|
||||
}
|
||||
case CLOSE:
|
||||
mSmbFileImpl.close();
|
||||
break;
|
||||
case SEEK:
|
||||
long offset = mSmbFileImpl.seek(msg.peekData().getLong(OFFSET));
|
||||
msg.peekData().putLong(OFFSET, offset);
|
||||
case FSTAT:
|
||||
messageValues.setObj(mSmbFileImpl.fstat());
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown operation " + msg.what);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package com.google.android.sambadocumentsprovider.nativefacade;
|
||||
|
||||
import android.os.Looper;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class SambaMessageLooper {
|
||||
|
@ -41,7 +42,7 @@ public class SambaMessageLooper {
|
|||
init();
|
||||
}
|
||||
|
||||
public SmbClient getClient() {
|
||||
public SmbFacade getClient() {
|
||||
return mServiceClient;
|
||||
}
|
||||
|
||||
|
@ -55,6 +56,7 @@ public class SambaMessageLooper {
|
|||
mLatch.await();
|
||||
|
||||
mCredentialCacheClient = new CredentialCacheClient(mLooper, mCredentialCacheImpl);
|
||||
|
||||
mServiceClient = new SambaFacadeClient(mLooper, mClientImpl);
|
||||
} catch(InterruptedException e) {
|
||||
// Should never happen
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.google.android.sambadocumentsprovider.nativefacade;
|
||||
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.storage.StorageManager;
|
||||
|
||||
import com.google.android.sambadocumentsprovider.provider.ByteBufferPool;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface SmbFacade extends SmbClient {
|
||||
ParcelFileDescriptor openProxyFile(
|
||||
String uri,
|
||||
String mode,
|
||||
StorageManager storageManager,
|
||||
ByteBufferPool bufferPool,
|
||||
CancellationSignal signal) throws IOException;
|
||||
}
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
package com.google.android.sambadocumentsprovider.nativefacade;
|
||||
|
||||
import android.system.StructStat;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -25,5 +27,7 @@ public interface SmbFile extends Closeable {
|
|||
|
||||
int read(ByteBuffer buffer) throws IOException;
|
||||
int write(ByteBuffer buffer, int length) throws IOException;
|
||||
long seek(long offset) throws IOException;
|
||||
StructStat fstat() throws IOException;
|
||||
|
||||
}
|
||||
|
|
|
@ -20,12 +20,12 @@ package com.google.android.sambadocumentsprovider.provider;
|
|||
import android.support.v4.util.Pools;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
class ByteBufferPool {
|
||||
public class ByteBufferPool {
|
||||
|
||||
private static final int BUFFER_CAPACITY = 128 * 1024;
|
||||
private final Pools.Pool<ByteBuffer> mBufferPool = new Pools.SynchronizedPool<>(16);
|
||||
|
||||
ByteBuffer obtainBuffer() {
|
||||
public ByteBuffer obtainBuffer() {
|
||||
ByteBuffer buffer = mBufferPool.acquire();
|
||||
|
||||
if (buffer == null) {
|
||||
|
@ -35,7 +35,7 @@ class ByteBufferPool {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
void recycleBuffer(ByteBuffer buffer) {
|
||||
public void recycleBuffer(ByteBuffer buffer) {
|
||||
buffer.clear();
|
||||
mBufferPool.release(buffer);
|
||||
}
|
||||
|
|
|
@ -27,9 +27,11 @@ import android.content.Context;
|
|||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.DocumentsContract.Document;
|
||||
import android.provider.DocumentsContract.Root;
|
||||
|
@ -53,6 +55,9 @@ import com.google.android.sambadocumentsprovider.base.OnTaskFinishedCallback;
|
|||
import com.google.android.sambadocumentsprovider.document.LoadDocumentTask;
|
||||
import com.google.android.sambadocumentsprovider.document.LoadStatTask;
|
||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbClient;
|
||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbFacade;
|
||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbFile;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
@ -131,10 +136,11 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
|||
};
|
||||
|
||||
private ShareManager mShareManager;
|
||||
private SmbClient mClient;
|
||||
private SmbFacade mClient;
|
||||
private ByteBufferPool mBufferPool;
|
||||
private DocumentCache mCache;
|
||||
private TaskManager mTaskManager;
|
||||
private StorageManager mStorageManager;
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
|
@ -145,6 +151,7 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
|||
mBufferPool = new ByteBufferPool();
|
||||
mShareManager = SambaProviderApplication.getServerManager(context);
|
||||
mShareManager.addListener(mShareChangeListener);
|
||||
mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
|
||||
|
||||
return mClient != null;
|
||||
}
|
||||
|
@ -547,12 +554,23 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
|||
public ParcelFileDescriptor openDocument(String documentId, String mode,
|
||||
CancellationSignal cancellationSignal) throws FileNotFoundException {
|
||||
Log.d(TAG, "Opening document " + documentId + " with mode " + mode);
|
||||
|
||||
try {
|
||||
if (!"r".equals(mode) && !"w".equals(mode)) {
|
||||
throw new UnsupportedOperationException("Mode " + mode + " is not supported");
|
||||
}
|
||||
|
||||
final String uri = toUriString(documentId);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
return mClient.openProxyFile(
|
||||
uri,
|
||||
mode,
|
||||
mStorageManager,
|
||||
mBufferPool,
|
||||
cancellationSignal);
|
||||
}
|
||||
|
||||
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
|
||||
switch (mode) {
|
||||
case "r": {
|
||||
|
@ -560,7 +578,7 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
|||
uri, mClient, pipe[1], mBufferPool, cancellationSignal);
|
||||
mTaskManager.runIoTask(task);
|
||||
}
|
||||
return pipe[0];
|
||||
return pipe[0];
|
||||
case "w": {
|
||||
final WriteFileTask task = new WriteFileTask(uri, mClient, pipe[0], mBufferPool,
|
||||
cancellationSignal, mWriteFinishedCallback);
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.google.android.sambadocumentsprovider.provider;
|
||||
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.ProxyFileDescriptorCallback;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.StructStat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbFile;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class SambaProxyFileCallback extends ProxyFileDescriptorCallback {
|
||||
private static final String TAG = "SambaProxyFileCallback";
|
||||
|
||||
private final SmbFile mFile;
|
||||
private final ByteBufferPool mBufferPool;
|
||||
private final ByteBuffer mBuffer;
|
||||
private final CancellationSignal mSignal;
|
||||
|
||||
public SambaProxyFileCallback(SmbFile file, ByteBufferPool bufferPool, CancellationSignal signal) {
|
||||
mFile = file;
|
||||
mBufferPool = bufferPool;
|
||||
mBuffer = mBufferPool.obtainBuffer();
|
||||
mSignal = signal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long onGetSize() throws ErrnoException {
|
||||
StructStat stat = null;
|
||||
try {
|
||||
stat = mFile.fstat();
|
||||
return stat.st_size;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to get size for file");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onRead(long offset, int size, byte[] data) throws ErrnoException {
|
||||
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
|
||||
mFile.seek(offset);
|
||||
|
||||
int readSize;
|
||||
int total = 0;
|
||||
while ((mSignal == null || !mSignal.isCanceled())
|
||||
&& (readSize = mFile.read(mBuffer)) > 0) {
|
||||
mBuffer.get(data, total, Math.min(size - total, readSize));
|
||||
mBuffer.clear();
|
||||
total += Math.min(size - total, readSize);
|
||||
if (total >= size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.min(size, total);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
|
||||
int written = 0;
|
||||
|
||||
try {
|
||||
mFile.seek(offset);
|
||||
|
||||
while ((mSignal == null || !mSignal.isCanceled())
|
||||
&& written < size) {
|
||||
int willWrite = Math.min(size - written, mBuffer.capacity());
|
||||
mBuffer.put(data, written, willWrite);
|
||||
int res = mFile.write(mBuffer, willWrite);
|
||||
written += res;
|
||||
mBuffer.clear();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to write file.", e);
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRelease() {
|
||||
mBufferPool.recycleBuffer(mBuffer);
|
||||
|
||||
try {
|
||||
mFile.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to close file");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ buildscript {
|
|||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.3.1'
|
||||
classpath 'com.android.tools.build:gradle:2.3.3'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue