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'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 25
|
compileSdkVersion 26
|
||||||
buildToolsVersion "25.0.2"
|
buildToolsVersion "25.0.2"
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.google.android.sambadocumentsprovider"
|
applicationId "com.google.android.sambadocumentsprovider"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 25
|
targetSdkVersion 26
|
||||||
versionCode 102
|
versionCode 102
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
|
|
@ -527,6 +527,46 @@ Java_com_google_android_sambadocumentsprovider_nativefacade_NativeSambaFacade_op
|
||||||
return fd;
|
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(
|
jlong Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_read(
|
||||||
JNIEnv *env,
|
JNIEnv *env,
|
||||||
jobject instance,
|
jobject instance,
|
||||||
|
|
|
@ -68,6 +68,14 @@ JNIEXPORT jlong JNICALL
|
||||||
Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_read(
|
Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_read(
|
||||||
JNIEnv *env, jobject instance, jlong pointer, jint fd, jobject buffer, jint maxlen);
|
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
|
JNIEXPORT jlong JNICALL
|
||||||
Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_write(
|
Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_write(
|
||||||
JNIEnv *env, jobject instance, jlong pointer, jint fd, jobject buffer, jint length);
|
JNIEnv *env, jobject instance, jlong pointer, jint fd, jobject buffer, jint length);
|
||||||
|
|
|
@ -160,6 +160,19 @@ SambaClient::ReadDir(
|
||||||
return 0;
|
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
|
int
|
||||||
SambaClient::Stat(const char *url, struct stat * const st) {
|
SambaClient::Stat(const char *url, struct stat * const st) {
|
||||||
LOGD(TAG, "Getting stat for %s.", url);
|
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;
|
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
|
ssize_t
|
||||||
SambaClient::ReadFile(const int fd, void *buffer, const size_t maxlen) {
|
SambaClient::ReadFile(const int fd, void *buffer, const size_t maxlen) {
|
||||||
LOGV(TAG, "Reading max %lu bytes from file with fd %x", maxlen, fd);
|
LOGV(TAG, "Reading max %lu bytes from file with fd %x", maxlen, fd);
|
||||||
|
|
|
@ -18,10 +18,11 @@
|
||||||
#ifndef SAMBADOCUMENTSPROVIDER_SAMBAPROVIDER_H
|
#ifndef SAMBADOCUMENTSPROVIDER_SAMBAPROVIDER_H
|
||||||
#define SAMBADOCUMENTSPROVIDER_SAMBAPROVIDER_H
|
#define SAMBADOCUMENTSPROVIDER_SAMBAPROVIDER_H
|
||||||
|
|
||||||
|
#include "samba_includes/libsmbclient.h"
|
||||||
#include "base/Callback.h"
|
#include "base/Callback.h"
|
||||||
#include "jni_helper/JniHelper.h"
|
#include "jni_helper/JniHelper.h"
|
||||||
#include "samba_includes/libsmbclient.h"
|
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace SambaClient {
|
namespace SambaClient {
|
||||||
|
@ -38,6 +39,8 @@ class SambaClient {
|
||||||
|
|
||||||
int Stat(const char *url, struct stat *st);
|
int Stat(const char *url, struct stat *st);
|
||||||
|
|
||||||
|
int Fstat(const int fd, struct stat * const st);
|
||||||
|
|
||||||
int CreateFile(const char *url);
|
int CreateFile(const char *url);
|
||||||
|
|
||||||
int Mkdir(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);
|
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);
|
int CloseFile(const int fd);
|
||||||
private:
|
private:
|
||||||
::SMBCCTX *sambaContext = NULL;
|
::SMBCCTX *sambaContext = NULL;
|
||||||
|
|
|
@ -24,17 +24,18 @@ import android.net.ConnectivityManager.NetworkCallback;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
import android.net.NetworkRequest;
|
import android.net.NetworkRequest;
|
||||||
|
|
||||||
import com.google.android.sambadocumentsprovider.SambaConfiguration.OnConfigurationChangedListener;
|
import com.google.android.sambadocumentsprovider.SambaConfiguration.OnConfigurationChangedListener;
|
||||||
import com.google.android.sambadocumentsprovider.cache.DocumentCache;
|
import com.google.android.sambadocumentsprovider.cache.DocumentCache;
|
||||||
import com.google.android.sambadocumentsprovider.nativefacade.CredentialCache;
|
import com.google.android.sambadocumentsprovider.nativefacade.CredentialCache;
|
||||||
import com.google.android.sambadocumentsprovider.nativefacade.SambaMessageLooper;
|
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 {
|
public class SambaProviderApplication extends Application {
|
||||||
|
|
||||||
private final DocumentCache mCache = new DocumentCache();
|
private final DocumentCache mCache = new DocumentCache();
|
||||||
private final TaskManager mTaskManager = new TaskManager();
|
private final TaskManager mTaskManager = new TaskManager();
|
||||||
private final SmbClient mSambaClient;
|
private final SmbFacade mSambaClient;
|
||||||
private final CredentialCache mCredentialCache;
|
private final CredentialCache mCredentialCache;
|
||||||
|
|
||||||
private SambaConfiguration mSambaConf;
|
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;
|
return getApplication(context).mSambaClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
|
|
||||||
package com.google.android.sambadocumentsprovider.nativefacade;
|
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.ErrnoException;
|
||||||
import android.system.StructStat;
|
import android.system.StructStat;
|
||||||
import com.google.android.sambadocumentsprovider.BuildConfig;
|
import com.google.android.sambadocumentsprovider.BuildConfig;
|
||||||
|
|
|
@ -18,17 +18,25 @@
|
||||||
package com.google.android.sambadocumentsprovider.nativefacade;
|
package com.google.android.sambadocumentsprovider.nativefacade;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.os.storage.StorageManager;
|
||||||
import android.support.annotation.IntDef;
|
import android.support.annotation.IntDef;
|
||||||
import android.system.StructStat;
|
import android.system.StructStat;
|
||||||
|
|
||||||
import com.google.android.sambadocumentsprovider.base.DirectoryEntry;
|
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.io.IOException;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.List;
|
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 })
|
@IntDef({ RESET, READ_DIR, STAT, MKDIR, RENAME, UNLINK, RMDIR, OPEN_FILE })
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@ -134,14 +142,37 @@ class SambaFacadeClient extends BaseClient implements SmbClient {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SmbFile openFile(String uri, String mode) throws IOException {
|
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()) {
|
try (final MessageValues<SambaFile> messageValues = MessageValues.obtain()) {
|
||||||
final Message msg = obtainMessage(OPEN_FILE, messageValues, uri);
|
enqueue(obtainMessageForOpenFile(uri, mode, messageValues));
|
||||||
msg.peekData().putString(MODE, mode);
|
return messageValues.getObj();
|
||||||
enqueue(msg);
|
|
||||||
return new SambaFileClient(mHandler.getLooper(), 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 static class SambaServiceHandler extends BaseHandler {
|
||||||
|
|
||||||
private final SmbClient mClientImpl;
|
private final SmbClient mClientImpl;
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
package com.google.android.sambadocumentsprovider.nativefacade;
|
package com.google.android.sambadocumentsprovider.nativefacade;
|
||||||
|
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
|
import android.system.StructStat;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
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
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
try {
|
try {
|
||||||
|
@ -63,5 +83,10 @@ class SambaFile implements SmbFile {
|
||||||
private native int write(long handler, int fd, ByteBuffer buffer, int length)
|
private native int write(long handler, int fd, ByteBuffer buffer, int length)
|
||||||
throws ErrnoException;
|
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;
|
private native void close(long handler, int fd) throws ErrnoException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,12 @@
|
||||||
|
|
||||||
package com.google.android.sambadocumentsprovider.nativefacade;
|
package com.google.android.sambadocumentsprovider.nativefacade;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.support.annotation.IntDef;
|
import android.support.annotation.IntDef;
|
||||||
|
import android.system.StructStat;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
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 READ = 1;
|
||||||
private static final int WRITE = 2;
|
private static final int WRITE = 2;
|
||||||
private static final int CLOSE = 3;
|
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) {
|
SambaFileClient(Looper looper, SmbFile smbFileImpl) {
|
||||||
mHandler = new SambaFileHandler(looper, 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
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
try (final MessageValues messageValues = MessageValues.obtain()) {
|
try (final MessageValues messageValues = MessageValues.obtain()) {
|
||||||
|
@ -81,22 +112,28 @@ class SambaFileClient extends BaseClient implements SmbFile {
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void processMessage(Message msg) {
|
public void processMessage(Message msg) {
|
||||||
final MessageValues<ByteBuffer> messageValues = (MessageValues<ByteBuffer>) msg.obj;
|
final MessageValues messageValues = (MessageValues) msg.obj;
|
||||||
try {
|
try {
|
||||||
final ByteBuffer buffer = messageValues.getObj();
|
|
||||||
|
|
||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
case READ:
|
case READ:
|
||||||
messageValues.setInt(mSmbFileImpl.read(buffer));
|
final ByteBuffer readBuffer = (ByteBuffer) messageValues.getObj();
|
||||||
|
messageValues.setInt(mSmbFileImpl.read(readBuffer));
|
||||||
break;
|
break;
|
||||||
case WRITE: {
|
case WRITE: {
|
||||||
|
final ByteBuffer writeBuffer = (ByteBuffer) messageValues.getObj();
|
||||||
final int length = msg.arg1;
|
final int length = msg.arg1;
|
||||||
messageValues.setInt(mSmbFileImpl.write(buffer, length));
|
messageValues.setInt(mSmbFileImpl.write(writeBuffer, length));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CLOSE:
|
case CLOSE:
|
||||||
mSmbFileImpl.close();
|
mSmbFileImpl.close();
|
||||||
break;
|
break;
|
||||||
|
case SEEK:
|
||||||
|
long offset = mSmbFileImpl.seek(msg.peekData().getLong(OFFSET));
|
||||||
|
msg.peekData().putLong(OFFSET, offset);
|
||||||
|
case FSTAT:
|
||||||
|
messageValues.setObj(mSmbFileImpl.fstat());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedOperationException("Unknown operation " + msg.what);
|
throw new UnsupportedOperationException("Unknown operation " + msg.what);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package com.google.android.sambadocumentsprovider.nativefacade;
|
package com.google.android.sambadocumentsprovider.nativefacade;
|
||||||
|
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
public class SambaMessageLooper {
|
public class SambaMessageLooper {
|
||||||
|
@ -41,7 +42,7 @@ public class SambaMessageLooper {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SmbClient getClient() {
|
public SmbFacade getClient() {
|
||||||
return mServiceClient;
|
return mServiceClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +56,7 @@ public class SambaMessageLooper {
|
||||||
mLatch.await();
|
mLatch.await();
|
||||||
|
|
||||||
mCredentialCacheClient = new CredentialCacheClient(mLooper, mCredentialCacheImpl);
|
mCredentialCacheClient = new CredentialCacheClient(mLooper, mCredentialCacheImpl);
|
||||||
|
|
||||||
mServiceClient = new SambaFacadeClient(mLooper, mClientImpl);
|
mServiceClient = new SambaFacadeClient(mLooper, mClientImpl);
|
||||||
} catch(InterruptedException e) {
|
} catch(InterruptedException e) {
|
||||||
// Should never happen
|
// 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;
|
package com.google.android.sambadocumentsprovider.nativefacade;
|
||||||
|
|
||||||
|
import android.system.StructStat;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
@ -25,5 +27,7 @@ public interface SmbFile extends Closeable {
|
||||||
|
|
||||||
int read(ByteBuffer buffer) throws IOException;
|
int read(ByteBuffer buffer) throws IOException;
|
||||||
int write(ByteBuffer buffer, int length) 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 android.support.v4.util.Pools;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
class ByteBufferPool {
|
public class ByteBufferPool {
|
||||||
|
|
||||||
private static final int BUFFER_CAPACITY = 128 * 1024;
|
private static final int BUFFER_CAPACITY = 128 * 1024;
|
||||||
private final Pools.Pool<ByteBuffer> mBufferPool = new Pools.SynchronizedPool<>(16);
|
private final Pools.Pool<ByteBuffer> mBufferPool = new Pools.SynchronizedPool<>(16);
|
||||||
|
|
||||||
ByteBuffer obtainBuffer() {
|
public ByteBuffer obtainBuffer() {
|
||||||
ByteBuffer buffer = mBufferPool.acquire();
|
ByteBuffer buffer = mBufferPool.acquire();
|
||||||
|
|
||||||
if (buffer == null) {
|
if (buffer == null) {
|
||||||
|
@ -35,7 +35,7 @@ class ByteBufferPool {
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void recycleBuffer(ByteBuffer buffer) {
|
public void recycleBuffer(ByteBuffer buffer) {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
mBufferPool.release(buffer);
|
mBufferPool.release(buffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,11 @@ import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.MatrixCursor;
|
import android.database.MatrixCursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.CancellationSignal;
|
import android.os.CancellationSignal;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.os.storage.StorageManager;
|
||||||
import android.provider.DocumentsContract;
|
import android.provider.DocumentsContract;
|
||||||
import android.provider.DocumentsContract.Document;
|
import android.provider.DocumentsContract.Document;
|
||||||
import android.provider.DocumentsContract.Root;
|
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.LoadDocumentTask;
|
||||||
import com.google.android.sambadocumentsprovider.document.LoadStatTask;
|
import com.google.android.sambadocumentsprovider.document.LoadStatTask;
|
||||||
import com.google.android.sambadocumentsprovider.nativefacade.SmbClient;
|
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.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -131,10 +136,11 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
private ShareManager mShareManager;
|
private ShareManager mShareManager;
|
||||||
private SmbClient mClient;
|
private SmbFacade mClient;
|
||||||
private ByteBufferPool mBufferPool;
|
private ByteBufferPool mBufferPool;
|
||||||
private DocumentCache mCache;
|
private DocumentCache mCache;
|
||||||
private TaskManager mTaskManager;
|
private TaskManager mTaskManager;
|
||||||
|
private StorageManager mStorageManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
|
@ -145,6 +151,7 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
||||||
mBufferPool = new ByteBufferPool();
|
mBufferPool = new ByteBufferPool();
|
||||||
mShareManager = SambaProviderApplication.getServerManager(context);
|
mShareManager = SambaProviderApplication.getServerManager(context);
|
||||||
mShareManager.addListener(mShareChangeListener);
|
mShareManager.addListener(mShareChangeListener);
|
||||||
|
mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
|
||||||
|
|
||||||
return mClient != null;
|
return mClient != null;
|
||||||
}
|
}
|
||||||
|
@ -547,12 +554,23 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
||||||
public ParcelFileDescriptor openDocument(String documentId, String mode,
|
public ParcelFileDescriptor openDocument(String documentId, String mode,
|
||||||
CancellationSignal cancellationSignal) throws FileNotFoundException {
|
CancellationSignal cancellationSignal) throws FileNotFoundException {
|
||||||
Log.d(TAG, "Opening document " + documentId + " with mode " + mode);
|
Log.d(TAG, "Opening document " + documentId + " with mode " + mode);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!"r".equals(mode) && !"w".equals(mode)) {
|
if (!"r".equals(mode) && !"w".equals(mode)) {
|
||||||
throw new UnsupportedOperationException("Mode " + mode + " is not supported");
|
throw new UnsupportedOperationException("Mode " + mode + " is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
final String uri = toUriString(documentId);
|
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();
|
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "r": {
|
case "r": {
|
||||||
|
@ -560,7 +578,7 @@ public class SambaDocumentsProvider extends DocumentsProvider {
|
||||||
uri, mClient, pipe[1], mBufferPool, cancellationSignal);
|
uri, mClient, pipe[1], mBufferPool, cancellationSignal);
|
||||||
mTaskManager.runIoTask(task);
|
mTaskManager.runIoTask(task);
|
||||||
}
|
}
|
||||||
return pipe[0];
|
return pipe[0];
|
||||||
case "w": {
|
case "w": {
|
||||||
final WriteFileTask task = new WriteFileTask(uri, mClient, pipe[0], mBufferPool,
|
final WriteFileTask task = new WriteFileTask(uri, mClient, pipe[0], mBufferPool,
|
||||||
cancellationSignal, mWriteFinishedCallback);
|
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()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
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