Expose smb.conf to external storage. (#21)
* Consolidate ShareManager into the unified init process. * Remove misc.xml * Expose smb.conf to external storage. Some clean-up on initialization code. Fix a bug that doesn't restart SMB client after configuration is updated. * Use the right TAG. * Guard a log behind debug flag.
This commit is contained in:
parent
6b1a97b626
commit
b16d629dc5
2 changed files with 86 additions and 41 deletions
|
@ -45,42 +45,75 @@ class SambaConfiguration implements Iterable<Map.Entry<String, String>> {
|
||||||
private static final String CONF_KEY_VALUE_SEPARATOR = " = ";
|
private static final String CONF_KEY_VALUE_SEPARATOR = " = ";
|
||||||
|
|
||||||
private final File mHomeFolder;
|
private final File mHomeFolder;
|
||||||
|
private final File mShareFolder;
|
||||||
private final Map<String, String> mConfigurations = new HashMap<>();
|
private final Map<String, String> mConfigurations = new HashMap<>();
|
||||||
|
|
||||||
SambaConfiguration(File homeFolder) {
|
SambaConfiguration(File homeFolder, @Nullable File shareFolder) {
|
||||||
mHomeFolder = homeFolder;
|
mHomeFolder = homeFolder;
|
||||||
|
if (shareFolder != null && (shareFolder.isDirectory() || shareFolder.mkdirs())) {
|
||||||
|
mShareFolder = shareFolder;
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Failed to create share folder. Only default value is supported.");
|
||||||
|
|
||||||
|
// Use home folder as the share folder to avoid null checks everywhere.
|
||||||
|
mShareFolder = mHomeFolder;
|
||||||
|
}
|
||||||
|
|
||||||
setHomeEnv(homeFolder.getAbsolutePath());
|
setHomeEnv(homeFolder.getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flushAsDefault() {
|
void flushDefault(OnConfigurationChangedListener listener) {
|
||||||
|
// lmhosts are not used in SambaDocumentsProvider and prioritize bcast because sometimes in home
|
||||||
|
// settings DNS will resolve unknown domain name to a specific IP for advertisement.
|
||||||
|
//
|
||||||
|
// lmhosts -- lmhosts file if existed side by side to smb.conf
|
||||||
|
// wins -- Windows Internet Name Service
|
||||||
|
// hosts -- hosts file and DNS resolution
|
||||||
|
// bcast -- NetBIOS broadcast
|
||||||
|
addConfiguration("name resolve order", "wins bcast hosts");
|
||||||
|
|
||||||
|
// Urge from users to disable SMB1 by default.
|
||||||
|
addConfiguration("client min protocol", "SMB2");
|
||||||
|
addConfiguration("client max protocol", "SMB3");
|
||||||
|
|
||||||
File smbFile = getSmbFile(mHomeFolder);
|
File smbFile = getSmbFile(mHomeFolder);
|
||||||
if (!smbFile.exists()) {
|
if (!smbFile.exists()) {
|
||||||
flush(null);
|
flush(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized SambaConfiguration addConfiguration(String key, String value) {
|
synchronized SambaConfiguration addConfiguration(String key, String value) {
|
||||||
mConfigurations.put(key, value);
|
mConfigurations.put(key, value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized SambaConfiguration removeConfiguration(String key) {
|
synchronized SambaConfiguration removeConfiguration(String key) {
|
||||||
mConfigurations.remove(key);
|
mConfigurations.remove(key);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void load(OnConfigurationChangedListener listener) {
|
boolean syncFromExternal(OnConfigurationChangedListener listener) {
|
||||||
new LoadTask(listener).execute();
|
final File smbFile = getSmbFile(mHomeFolder);
|
||||||
|
final File extSmbFile = getExtSmbFile(mShareFolder);
|
||||||
|
|
||||||
|
if (extSmbFile.isFile() && extSmbFile.lastModified() > smbFile.lastModified()) {
|
||||||
|
if (BuildConfig.DEBUG) Log.d(TAG, "Syncing " + SMB_CONF_FILE +
|
||||||
|
" from external source to internal source.");
|
||||||
|
new SyncTask(listener).execute(extSmbFile);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flush(@Nullable OnConfigurationChangedListener listener) {
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flush(OnConfigurationChangedListener listener) {
|
||||||
new FlushTask(listener).execute();
|
new FlushTask(listener).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void read() throws IOException {
|
private synchronized void read(File smbFile) throws IOException {
|
||||||
mConfigurations.clear();
|
mConfigurations.clear();
|
||||||
try (BufferedReader reader = new BufferedReader(new FileReader(getSmbFile(mHomeFolder)))) {
|
try (BufferedReader reader = new BufferedReader(new FileReader(smbFile))) {
|
||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
String[] conf = line.split(CONF_KEY_VALUE_SEPARATOR);
|
String[] conf = line.split(CONF_KEY_VALUE_SEPARATOR);
|
||||||
|
@ -113,6 +146,10 @@ class SambaConfiguration implements Iterable<Map.Entry<String, String>> {
|
||||||
return new File(smbFolder, SMB_CONF_FILE);
|
return new File(smbFolder, SMB_CONF_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static File getExtSmbFile(File shareFolder) {
|
||||||
|
return new File(shareFolder, SMB_CONF_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
private void setHomeEnv(String absoluteFolder) {
|
private void setHomeEnv(String absoluteFolder) {
|
||||||
try {
|
try {
|
||||||
setEnv(HOME_VAR, absoluteFolder);
|
setEnv(HOME_VAR, absoluteFolder);
|
||||||
|
@ -128,16 +165,18 @@ class SambaConfiguration implements Iterable<Map.Entry<String, String>> {
|
||||||
return mConfigurations.entrySet().iterator();
|
return mConfigurations.entrySet().iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LoadTask extends BiResultTask<Void, Void, Void> {
|
private class SyncTask extends BiResultTask<File, Void, Void> {
|
||||||
|
|
||||||
private final OnConfigurationChangedListener mListener;
|
private final OnConfigurationChangedListener mListener;
|
||||||
|
|
||||||
private LoadTask(OnConfigurationChangedListener listener) {
|
private SyncTask(OnConfigurationChangedListener listener) {
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void run(Void... params) throws IOException {
|
public Void run(File... params) throws IOException {
|
||||||
read();
|
read(params[0]);
|
||||||
|
write();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,9 +187,9 @@ class SambaConfiguration implements Iterable<Map.Entry<String, String>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FlushTask extends BiResultTask<Void, Void, Void> {
|
private class FlushTask extends BiResultTask<Void, Void, Void> {
|
||||||
private final @Nullable OnConfigurationChangedListener mListener;
|
private final OnConfigurationChangedListener mListener;
|
||||||
|
|
||||||
private FlushTask(@Nullable OnConfigurationChangedListener listener) {
|
private FlushTask(OnConfigurationChangedListener listener) {
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,11 +201,9 @@ class SambaConfiguration implements Iterable<Map.Entry<String, String>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSucceeded(Void result) {
|
public void onSucceeded(Void result) {
|
||||||
if (mListener != null) {
|
|
||||||
mListener.onConfigurationChanged();
|
mListener.onConfigurationChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
interface OnConfigurationChangedListener {
|
interface OnConfigurationChangedListener {
|
||||||
void onConfigurationChanged();
|
void onConfigurationChanged();
|
||||||
|
|
|
@ -25,21 +25,22 @@ import android.net.Network;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
import android.net.NetworkRequest;
|
import android.net.NetworkRequest;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
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.SmbFacade;
|
import com.google.android.sambadocumentsprovider.nativefacade.SmbFacade;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
public class SambaProviderApplication extends Application {
|
public class SambaProviderApplication extends Application {
|
||||||
|
|
||||||
|
private static final String TAG = "SambaProviderApplication";
|
||||||
|
|
||||||
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 SmbFacade mSambaClient;
|
private SmbFacade mSambaClient;
|
||||||
private CredentialCache mCredentialCache;
|
|
||||||
|
|
||||||
private SambaConfiguration mSambaConf;
|
|
||||||
private ShareManager mShareManager;
|
private ShareManager mShareManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -49,10 +50,6 @@ public class SambaProviderApplication extends Application {
|
||||||
init(this);
|
init(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void init(Context context) {
|
|
||||||
((SambaProviderApplication) context.getApplicationContext()).initialize(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initialize(Context context) {
|
private void initialize(Context context) {
|
||||||
if (mSambaClient != null) {
|
if (mSambaClient != null) {
|
||||||
// Already initialized.
|
// Already initialized.
|
||||||
|
@ -62,30 +59,38 @@ public class SambaProviderApplication extends Application {
|
||||||
initializeSambaConf(context);
|
initializeSambaConf(context);
|
||||||
|
|
||||||
final SambaMessageLooper looper = new SambaMessageLooper();
|
final SambaMessageLooper looper = new SambaMessageLooper();
|
||||||
mCredentialCache = looper.getCredentialCache();
|
CredentialCache credentialCache = looper.getCredentialCache();
|
||||||
mSambaClient = looper.getClient();
|
mSambaClient = looper.getClient();
|
||||||
|
|
||||||
mShareManager = new ShareManager(context, mCredentialCache);
|
mShareManager = new ShareManager(context, credentialCache);
|
||||||
|
|
||||||
registerNetworkCallback(context);
|
registerNetworkCallback(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeSambaConf(Context context) {
|
private void initializeSambaConf(Context context) {
|
||||||
mSambaConf = new SambaConfiguration(context.getDir("home", MODE_PRIVATE));
|
final File home = context.getDir("home", MODE_PRIVATE);
|
||||||
|
final File share = context.getExternalFilesDir(null);
|
||||||
|
final SambaConfiguration sambaConf = new SambaConfiguration(home, share);
|
||||||
|
|
||||||
// lmhosts are not used in SambaDocumentsProvider and prioritize bcast because sometimes in home
|
final OnConfigurationChangedListener listener = new OnConfigurationChangedListener() {
|
||||||
// settings DNS will resolve unknown domain name to a specific IP for advertisement.
|
@Override
|
||||||
//
|
public void onConfigurationChanged() {
|
||||||
// lmhosts -- lmhosts file if existed side by side to smb.conf
|
if (mSambaClient != null) {
|
||||||
// wins -- Windows Internet Name Service
|
mSambaClient.reset();
|
||||||
// hosts -- hosts file and DNS resolution
|
}
|
||||||
// bcast -- NetBIOS broadcast
|
}
|
||||||
mSambaConf.addConfiguration("name resolve order", "wins bcast hosts");
|
};
|
||||||
|
|
||||||
// Urge from users to disable SMB1 by default.
|
// Sync from external folder. The reason why we don't use external folder directly as HOME is
|
||||||
mSambaConf.addConfiguration("client min protocol", "SMB2");
|
// because there are cases where external storage is not ready, and we don't have an external
|
||||||
mSambaConf.addConfiguration("client max protocol", "SMB3");
|
// folder at all.
|
||||||
mSambaConf.flushAsDefault();
|
if (sambaConf.syncFromExternal(listener)) {
|
||||||
|
if (BuildConfig.DEBUG) Log.d(TAG, "Syncing smb.conf from external folder. No need to try "
|
||||||
|
+ "flushing default config.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sambaConf.flushDefault(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerNetworkCallback(Context context) {
|
private void registerNetworkCallback(Context context) {
|
||||||
|
@ -102,7 +107,10 @@ public class SambaProviderApplication extends Application {
|
||||||
mSambaClient.reset();
|
mSambaClient.reset();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void init(Context context) {
|
||||||
|
getApplication(context).initialize(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ShareManager getServerManager(Context context) {
|
public static ShareManager getServerManager(Context context) {
|
||||||
|
|
Loading…
Reference in a new issue