blob: d9ed4ab5dc4a6a39b93af94067d42e6d0edeaacf [file] [log] [blame]
package com.android.mtp;
import android.content.ContentResolver;
import android.content.res.Resources;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.os.Process;
import android.provider.DocumentsContract;
import android.util.Log;
import java.io.IOException;
final class RootScanner {
/**
* Polling interval in milliseconds used for first SHORT_POLLING_TIMES because it is more
* likely to add new root just after the device is added.
*/
private final static long SHORT_POLLING_INTERVAL = 2000;
/**
* Polling interval in milliseconds for low priority polling, when changes are not expected.
*/
private final static long LONG_POLLING_INTERVAL = 30 * 1000;
/**
* @see #SHORT_POLLING_INTERVAL
*/
private final static long SHORT_POLLING_TIMES = 10;
final ContentResolver mResolver;
final Resources mResources;
final MtpManager mManager;
final MtpDatabase mDatabase;
boolean mClosed = false;
int mPollingCount;
Thread mBackgroundThread;
RootScanner(
ContentResolver resolver,
Resources resources,
MtpManager manager,
MtpDatabase database) {
mResolver = resolver;
mResources = resources;
mManager = manager;
mDatabase = database;
}
void notifyChange() {
final Uri uri =
DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY);
mResolver.notifyChange(uri, null, false);
}
/**
* Starts to check new changes right away.
* If the background thread has already gone, it restarts another background thread.
*/
synchronized void scanNow() {
if (mClosed) {
return;
}
mPollingCount = 0;
if (mBackgroundThread == null) {
mBackgroundThread = new BackgroundLoaderThread();
mBackgroundThread.start();
} else {
notify();
}
}
void close() throws InterruptedException {
Thread thread;
synchronized (this) {
mClosed = true;
thread = mBackgroundThread;
if (mBackgroundThread == null) {
return;
}
notify();
}
thread.join();
}
private final class BackgroundLoaderThread extends Thread {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
synchronized (RootScanner.this) {
while (!mClosed) {
final int[] deviceIds = mManager.getOpenedDeviceIds();
if (deviceIds.length == 0) {
break;
}
boolean changed = false;
for (int deviceId : deviceIds) {
mDatabase.startAddingRootDocuments(deviceId);
try {
changed = mDatabase.putRootDocuments(
deviceId, mResources, mManager.getRoots(deviceId)) || changed;
} catch (IOException|SQLiteException exp) {
// The error may happen on the device. We would like to continue getting
// roots for other devices.
Log.e(MtpDocumentsProvider.TAG, exp.getMessage());
continue;
} finally {
changed = mDatabase.stopAddingRootDocuments(deviceId) || changed;
}
}
if (changed) {
notifyChange();
}
mPollingCount++;
try {
// Use SHORT_POLLING_PERIOD for the first SHORT_POLLING_TIMES because it is
// more likely to add new root just after the device is added.
// TODO: Use short interval only for a device that is just added.
RootScanner.this.wait(
mPollingCount > SHORT_POLLING_TIMES ?
LONG_POLLING_INTERVAL : SHORT_POLLING_INTERVAL);
} catch (InterruptedException exception) {
break;
}
}
mBackgroundThread = null;
}
}
}
}