From 0c5030a704219507e149b2f7d932e09e169ebe58 Mon Sep 17 00:00:00 2001 From: Garfield Tan Date: Thu, 6 Jul 2017 16:12:29 -0700 Subject: [PATCH] Separate directory loading into steps to avoid blocking samba thread. --- .idea/misc.xml | 2 +- app/build.gradle | 6 +- app/src/main/cpp/jni_helper/JniHelper.cc | 108 +++++++----------- app/src/main/cpp/jni_helper/JniHelper.h | 14 ++- app/src/main/cpp/samba_client/SambaClient.cc | 43 ++++--- app/src/main/cpp/samba_client/SambaClient.h | 6 +- .../document/DocumentMetadata.java | 9 +- .../nativefacade/MessageValues.java | 11 ++ .../nativefacade/NativeSambaFacade.java | 6 +- .../nativefacade/SambaDir.java | 58 ++++++++++ .../nativefacade/SambaDirClient.java | 93 +++++++++++++++ .../nativefacade/SambaFacadeClient.java | 6 +- .../nativefacade/SmbClient.java | 2 +- .../nativefacade/SmbDir.java | 29 +++++ build.gradle | 3 + 15 files changed, 295 insertions(+), 101 deletions(-) create mode 100644 app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SambaDir.java create mode 100644 app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SambaDirClient.java create mode 100644 app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SmbDir.java diff --git a/.idea/misc.xml b/.idea/misc.xml index 5d19981..fbb6828 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -37,7 +37,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index eb39a9a..e75b033 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 26 - buildToolsVersion "25.0.2" + buildToolsVersion "26.0.0" defaultConfig { applicationId "com.google.android.sambadocumentsprovider" minSdkVersion 21 @@ -45,7 +45,7 @@ dependencies { androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) - compile 'com.android.support:appcompat-v7:25.1.0' - compile 'com.android.support:design:25.1.0' + compile 'com.android.support:appcompat-v7:25.4.0' + compile 'com.android.support:design:25.4.0' testCompile 'junit:junit:4.12' } diff --git a/app/src/main/cpp/jni_helper/JniHelper.cc b/app/src/main/cpp/jni_helper/JniHelper.cc index 0932478..2c92748 100644 --- a/app/src/main/cpp/jni_helper/JniHelper.cc +++ b/app/src/main/cpp/jni_helper/JniHelper.cc @@ -97,40 +97,6 @@ create_directory_entry(JNIEnv* env, const struct smbc_dirent &ent) { return entry; } -static jobject create_array_list(JNIEnv* env) { - static const jclass arrayListClass = - classCache_.get(env, "java/util/ArrayList"); - static const jmethodID arrayListConstructor = - env->GetMethodID(arrayListClass, "", "()V"); - - return env->NewObject(arrayListClass, arrayListConstructor); -} - -static void -add_object_to_array_list(JNIEnv *env, jobject arrayList, jobject obj) { - static const jclass arrayListClass = - classCache_.get(env, "java/util/ArrayList"); - static const jmethodID addMethod = - env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); - - env->CallBooleanMethod(arrayList, addMethod, obj); -} - -static int add_dir_entry_to_array_list( - JniContext context, struct smbc_dirent* dirent) { - jobject entry = create_directory_entry(context.env, *dirent); - if (entry == NULL) { - return -1; - } - add_object_to_array_list(context.env, context.instance, entry); - if (context.env->ExceptionCheck()) { - return -1; - } - // We're done with this entry, remove local ref to avoid leak. - context.env->DeleteLocalRef(entry); - return 0; -} - static void throw_new_file_not_found_exception(JNIEnv *env, const char *fmt, ...) { char message[256]; @@ -184,36 +150,20 @@ throw_new_auth_failed_exception(JNIEnv* env) { env->Throw(authFailedException); } -jobject -Java_com_google_android_sambadocumentsprovider_nativefacade_NativeSambaFacade_readDir( +jint +Java_com_google_android_sambadocumentsprovider_nativefacade_NativeSambaFacade_openDir( JNIEnv *env, jobject instance, jlong pointer, jstring uri_) { const char *uri = env->GetStringUTFChars(uri_, 0); if (uri == NULL) { - return NULL; + return -1; } - jobject arrayList = create_array_list(env); - if (arrayList == NULL) { - // Java exception happened. - env->ReleaseStringUTFChars(uri_, uri); - return NULL; - } - - JniContext context(env, arrayList); SambaClient::SambaClient *client = reinterpret_cast(pointer); - int result = client->ReadDir( - uri, - JniCallback( - context, add_dir_entry_to_array_list)); + int fd = client->OpenDir(uri); - if (env->ExceptionCheck()) { - // Java exception happened. - goto bail; - } - - if (result < 0) { - int err = -result; + if (fd < 0) { + int err = -fd; switch (err) { case ENODEV: case ENOENT: @@ -226,14 +176,13 @@ Java_com_google_android_sambadocumentsprovider_nativefacade_NativeSambaFacade_re throw_new_auth_failed_exception(env); break; default: - throw_new_errno_exception(env, "readDir", err); + throw_new_errno_exception(env, "openDir", err); } } - bail: env->ReleaseStringUTFChars(uri_, uri); - return arrayList; + return fd; } static jobject create_structstat(JNIEnv *env, const struct stat &st) { @@ -527,21 +476,52 @@ Java_com_google_android_sambadocumentsprovider_nativefacade_NativeSambaFacade_op return fd; } +jobject Java_com_google_android_sambadocumentsprovider_nativefacade_SambaDir_readDir( + JNIEnv *env, jobject instance, jlong pointer, jint dh) { + SambaClient::SambaClient *client = + reinterpret_cast(pointer); + + const struct smbc_dirent* dirent; + + int result = client->ReadDir(dh, &dirent); + if (result < 0) { + throw_new_errno_exception(env, "readDir", -result); + return NULL; + } + + if (dirent == NULL) { + // This is a normal case, indicating that we finished reading this directory. + return NULL; + } + + return create_directory_entry(env, *dirent); +} + +void Java_com_google_android_sambadocumentsprovider_nativefacade_SambaDir_close( + JNIEnv *env, jobject instance, jlong pointer, jint dh) { + SambaClient::SambaClient *client = + reinterpret_cast(pointer); + + int result = client->CloseDir(dh); + if (result < 0) { + throw_new_errno_exception(env, "close", -result); + } +} + jobject Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_fstat( JNIEnv *env, jobject instance, jlong pointer, jint fd) { SambaClient::SambaClient *client = - reinterpret_cast(pointer); - - jobject stat = NULL; + reinterpret_cast(pointer); struct stat st; int result = client->Fstat(fd, &st); if (result < 0) { throw_new_errno_exception(env, "stat", -result); + return NULL; } return create_structstat(env, st); @@ -555,7 +535,7 @@ jlong Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_seek jlong offset, jint whence) { SambaClient::SambaClient *client = - reinterpret_cast(pointer); + reinterpret_cast(pointer); jlong result = client->SeekFile(fd, offset, whence); if (result < 0) { @@ -565,8 +545,6 @@ jlong Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_seek return result; } - - jlong Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_read( JNIEnv *env, jobject instance, diff --git a/app/src/main/cpp/jni_helper/JniHelper.h b/app/src/main/cpp/jni_helper/JniHelper.h index 54d62e7..3da71da 100644 --- a/app/src/main/cpp/jni_helper/JniHelper.h +++ b/app/src/main/cpp/jni_helper/JniHelper.h @@ -32,8 +32,8 @@ JNIEXPORT void JNICALL Java_com_google_android_sambadocumentsprovider_nativefacade_NativeSambaFacade_nativeDestroy( JNIEnv *env, jobject instance, jlong pointer); -JNIEXPORT jobject JNICALL - Java_com_google_android_sambadocumentsprovider_nativefacade_NativeSambaFacade_readDir( +JNIEXPORT jint JNICALL + Java_com_google_android_sambadocumentsprovider_nativefacade_NativeSambaFacade_openDir( JNIEnv *env, jobject instance, jlong pointer, jstring uri_); JNIEXPORT jobject JNICALL @@ -64,6 +64,14 @@ JNIEXPORT jint JNICALL Java_com_google_android_sambadocumentsprovider_nativefacade_NativeSambaFacade_openFile( JNIEnv *env, jobject instance, jlong pointer, jstring uri_, jstring mode_); +JNIEXPORT jobject JNICALL + Java_com_google_android_sambadocumentsprovider_nativefacade_SambaDir_readDir( + JNIEnv *env, jobject instance, jlong pointer, jint fd); + +JNIEXPORT void JNICALL + Java_com_google_android_sambadocumentsprovider_nativefacade_SambaDir_close( + JNIEnv *env, jobject instance, jlong pointer, jint fd); + JNIEXPORT jlong JNICALL Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_read( JNIEnv *env, jobject instance, jlong pointer, jint fd, jobject buffer, jint maxlen); @@ -74,7 +82,7 @@ JNIEXPORT jlong JNICALL JNIEXPORT jobject JNICALL Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_fstat( - JNIEnv *env, jobject instance, jlong pointer, jint fd); + JNIEnv *env, jobject instance, jlong pointer, jint fd); JNIEXPORT jlong JNICALL Java_com_google_android_sambadocumentsprovider_nativefacade_SambaFile_write( diff --git a/app/src/main/cpp/samba_client/SambaClient.cc b/app/src/main/cpp/samba_client/SambaClient.cc index 9b7cbc3..f548e2b 100644 --- a/app/src/main/cpp/samba_client/SambaClient.cc +++ b/app/src/main/cpp/samba_client/SambaClient.cc @@ -130,31 +130,40 @@ static const char* getTypeName(unsigned int smbc_type) { } int -SambaClient::ReadDir( - const char *url, const Callback &entryHandler) { - LOGD(TAG, "Reading dir at %s.", url); - const int dir = smbc_opendir(url); - if (dir < 0) { +SambaClient::OpenDir(const char *url) { + LOGD(TAG, "Opening dir at %s.", url); + const int fd = smbc_opendir(url); + if (fd < 0) { int err = errno; LOGE(TAG, "Failed to open dir at %s. Errno: %x", url, err); return -err; } - struct smbc_dirent *dirent = NULL; - while ((dirent = smbc_readdir(dir)) != NULL) { - LOGV(TAG, "Found entry name: %s, comment: %s, type: %s.", - dirent->name, dirent->comment, getTypeName(dirent->smbc_type)); - if (entryHandler(dirent) < 0) { - // Java exceptions. - smbc_closedir(dir); - return -1; - } - } + return fd; +} + +int +SambaClient::ReadDir(const int dh, const struct smbc_dirent ** dirent) { + LOGD(TAG, "Reading dir for %x.", dh); + *dirent = smbc_readdir(dh); + if (*dirent == NULL) { + LOGV(TAG, "Finished reading dir ent for %x.", dh); + } else { + LOGV(TAG, "Found entry name: %s, comment: %s, type: %s.", + (*dirent)->name, (*dirent)->comment, getTypeName((*dirent)->smbc_type)); + } + return 0; +} + +int +SambaClient::CloseDir(const int dh) { + LOGD(TAG, "Close dir for %x.", dh); + const int ret = smbc_closedir(dh); - const int ret = smbc_closedir(dir); if (ret) { int err = errno; - LOGW(TAG, "Failed to close dir %d at %s. Errno: %x.", dir, url, err); + LOGW(TAG, "Failed to close dir with dh %x. Errno: %x.", dh, err); + return -err; } return 0; diff --git a/app/src/main/cpp/samba_client/SambaClient.h b/app/src/main/cpp/samba_client/SambaClient.h index f5419cb..ea15c44 100644 --- a/app/src/main/cpp/samba_client/SambaClient.h +++ b/app/src/main/cpp/samba_client/SambaClient.h @@ -35,7 +35,11 @@ class SambaClient { bool Init(const bool debug, const CredentialCache *credentialCache); - int ReadDir(const char *url, const Callback &entryHandler); + int OpenDir(const char *url); + + int ReadDir(const int dh, const struct smbc_dirent** dirent); + + int CloseDir(const int dh); int Stat(const char *url, struct stat *st); diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/document/DocumentMetadata.java b/app/src/main/java/com/google/android/sambadocumentsprovider/document/DocumentMetadata.java index d46f5fa..81c6f39 100644 --- a/app/src/main/java/com/google/android/sambadocumentsprovider/document/DocumentMetadata.java +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/document/DocumentMetadata.java @@ -27,6 +27,7 @@ import android.util.Log; import android.webkit.MimeTypeMap; import com.google.android.sambadocumentsprovider.base.DirectoryEntry; import com.google.android.sambadocumentsprovider.nativefacade.SmbClient; +import com.google.android.sambadocumentsprovider.nativefacade.SmbDir; import java.io.IOException; import java.util.HashMap; import java.util.List; @@ -203,11 +204,11 @@ public class DocumentMetadata { } public void loadChildren(SmbClient client) throws IOException { - try { - List entries = client.readDir(mUri.toString()); + try (final SmbDir dir = client.openDir(mUri.toString())) { - Map children = new HashMap<>(entries.size()); - for (DirectoryEntry entry : entries) { + Map children = new HashMap<>(); + DirectoryEntry entry; + while ((entry = dir.readDir()) != null) { Uri childUri = DocumentMetadata.buildChildUri(mUri, entry); if (childUri != null) { children.put(childUri, new DocumentMetadata(childUri, entry)); diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/MessageValues.java b/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/MessageValues.java index 4b2a865..71870db 100644 --- a/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/MessageValues.java +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/MessageValues.java @@ -34,6 +34,7 @@ class MessageValues implements AutoCloseable { private volatile T mObj; private volatile int mInt; + private volatile long mLong; private volatile IOException mException; private volatile RuntimeException mRuntimeException; @@ -62,6 +63,15 @@ class MessageValues implements AutoCloseable { return mInt; } + long getLong() throws IOException { + checkException(); + return mLong; + } + + void setLong(long value) { + mLong = value; + } + void setInt(int value) { mInt = value; } @@ -87,6 +97,7 @@ class MessageValues implements AutoCloseable { public void close() { mObj = null; mInt = 0; + mLong = 0L; mException = null; mRuntimeException = null; POOL.release(this); diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/NativeSambaFacade.java b/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/NativeSambaFacade.java index 45f151f..ed60545 100644 --- a/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/NativeSambaFacade.java +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/NativeSambaFacade.java @@ -58,10 +58,10 @@ class NativeSambaFacade implements SmbClient { } @Override - public List readDir(String uri) throws IOException { + public SmbDir openDir(String uri) throws IOException { try { checkNativeHandler(); - return readDir(mNativeHandler, uri); + return new SambaDir(mNativeHandler, openDir(mNativeHandler, uri)); } catch (ErrnoException e) { throw new IOException("Failed to read directory " + uri, e); } @@ -147,7 +147,7 @@ class NativeSambaFacade implements SmbClient { private native void nativeDestroy(long handler); - private native List readDir(long handler, String uri) throws ErrnoException; + private native int openDir(long handler, String uri) throws ErrnoException; private native StructStat stat(long handler, String uri) throws ErrnoException; diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SambaDir.java b/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SambaDir.java new file mode 100644 index 0000000..1aa7fa6 --- /dev/null +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SambaDir.java @@ -0,0 +1,58 @@ +/* + * 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 . + */ + +package com.google.android.sambadocumentsprovider.nativefacade; + +import android.support.annotation.Nullable; +import android.system.ErrnoException; +import com.google.android.sambadocumentsprovider.base.DirectoryEntry; +import java.io.IOException; + +class SambaDir implements SmbDir { + + private final long mNativeHandler; + private int mNativeDh; + + SambaDir(long nativeHandler, int nativeFd) { + mNativeHandler = nativeHandler; + mNativeDh = nativeFd; + } + + @Override + public DirectoryEntry readDir() throws IOException { + try { + return readDir(mNativeHandler, mNativeDh); + } catch (ErrnoException e) { + throw new IOException(e); + } + } + + + @Override + public void close() throws IOException { + try { + int dh = mNativeDh; + mNativeDh = -1; + close(mNativeHandler, dh); + } catch (ErrnoException e) { + throw new IOException(e); + } + } + + private native @Nullable DirectoryEntry readDir(long handler, int fd) throws ErrnoException; + private native void close(long handler, int fd) throws ErrnoException; +} diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SambaDirClient.java b/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SambaDirClient.java new file mode 100644 index 0000000..849e001 --- /dev/null +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SambaDirClient.java @@ -0,0 +1,93 @@ +/* + * 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 . + */ + +package com.google.android.sambadocumentsprovider.nativefacade; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.support.annotation.IntDef; +import android.support.annotation.Nullable; +import com.google.android.sambadocumentsprovider.base.DirectoryEntry; +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +class SambaDirClient extends BaseClient implements SmbDir { + + @IntDef({ READ_DIR, CLOSE }) + @Retention(RetentionPolicy.SOURCE) + @interface Operation {} + private static final int READ_DIR = 0; + private static final int CLOSE = READ_DIR + 1; + + SambaDirClient(Looper looper, SmbDir smbDirImpl) { + mHandler = new SambaDirHandler(looper, smbDirImpl); + } + + @Nullable + @Override + public DirectoryEntry readDir() throws IOException { + try (MessageValues messageValues = MessageValues.obtain()) { + final Message msg = mHandler.obtainMessage(READ_DIR, messageValues); + enqueue(msg); + return messageValues.getObj(); + } + } + + @Override + public void close() throws IOException { + try (MessageValues messageValues = MessageValues.obtain()) { + final Message msg = mHandler.obtainMessage(CLOSE, messageValues); + enqueue(msg); + messageValues.checkException(); + } + } + + private static class SambaDirHandler extends BaseHandler { + + private final SmbDir mSmbDirImpl; + + private SambaDirHandler(Looper looper, SmbDir smbDirImpl) { + super(looper); + + mSmbDirImpl = smbDirImpl; + } + + @Override + @SuppressWarnings("unchecked") + public void processMessage(Message msg) { + final MessageValues messageValues = (MessageValues) msg.obj; + try { + switch (msg.what) { + case READ_DIR: + messageValues.setObj(mSmbDirImpl.readDir()); + break; + case CLOSE: + mSmbDirImpl.close(); + break; + default: + throw new UnsupportedOperationException("Unknown operation " + msg.what); + } + } catch (RuntimeException e) { + messageValues.setRuntimeException(e); + } catch (IOException e) { + messageValues.setException(e); + } + } + } +} diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SambaFacadeClient.java b/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SambaFacadeClient.java index 6ef7e4d..2cb142b 100644 --- a/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SambaFacadeClient.java +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SambaFacadeClient.java @@ -77,8 +77,8 @@ class SambaFacadeClient extends BaseClient implements SmbFacade { } @Override - public List readDir(String uri) throws IOException { - try (final MessageValues> messageValues = MessageValues.obtain()) { + public SmbDir openDir(String uri) throws IOException { + try (final MessageValues messageValues = MessageValues.obtain()) { final Message msg = obtainMessage(READ_DIR, messageValues, uri); enqueue(msg); return messageValues.getObj(); @@ -195,7 +195,7 @@ class SambaFacadeClient extends BaseClient implements SmbFacade { mClientImpl.reset(); break; case READ_DIR: - messageValues.setObj(mClientImpl.readDir(uri)); + messageValues.setObj(mClientImpl.openDir(uri)); break; case STAT: messageValues.setObj(mClientImpl.stat(uri)); diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SmbClient.java b/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SmbClient.java index efa04fb..d3118d3 100644 --- a/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SmbClient.java +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SmbClient.java @@ -26,7 +26,7 @@ public interface SmbClient { void reset(); - List readDir(String uri) throws IOException; + SmbDir openDir(String uri) throws IOException; StructStat stat(String uri) throws IOException; diff --git a/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SmbDir.java b/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SmbDir.java new file mode 100644 index 0000000..7901776 --- /dev/null +++ b/app/src/main/java/com/google/android/sambadocumentsprovider/nativefacade/SmbDir.java @@ -0,0 +1,29 @@ +/* + * 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 . + */ + +package com.google.android.sambadocumentsprovider.nativefacade; + +import android.support.annotation.Nullable; +import com.google.android.sambadocumentsprovider.base.DirectoryEntry; +import java.io.Closeable; +import java.io.IOException; + +public interface SmbDir extends Closeable { + + @Nullable DirectoryEntry readDir() throws IOException; + +} diff --git a/build.gradle b/build.gradle index ee9a47a..b63c96e 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,9 @@ buildscript { allprojects { repositories { jcenter() + maven { + url "https://maven.google.com" + } } }