package org.microg.networklocation.data;
import android.util.Log;
import org.microg.networklocation.MainService;
import org.microg.networklocation.database.LocationDatabase;
import org.microg.networklocation.source.LocationSource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;
public class LocationRetriever {
private static final String TAG = "nlp.LocationRetriever";
private static final long WAIT_BETWEEN = 1000 * 10; //every 10 seconds
private final Thread loopThread = new Thread(new Runnable() {
@Override
public void run() {
retrieveLoop();
}
});
private LocationDatabase locationDatabase;
private List<LocationSource<CellSpec>> cellLocationSources = new ArrayList<LocationSource<CellSpec>>();
private List<LocationSource<WifiSpec>> wifiLocationSources = new ArrayList<LocationSource<WifiSpec>>();
private Deque<CellSpec> cellStack = new LinkedBlockingDeque<CellSpec>();
private Deque<WifiSpec> wifiStack = new LinkedBlockingDeque<WifiSpec>();
private long lastRetrieve = 0;
public LocationRetriever(LocationDatabase locationDatabase) {
this.locationDatabase = locationDatabase;
}
public List<LocationSource<CellSpec>> getCellLocationSources() {
return cellLocationSources;
}
public void setCellLocationSources(List<LocationSource<CellSpec>> cellLocationSources) {
this.cellLocationSources = new ArrayList<LocationSource<CellSpec>>(cellLocationSources);
}
public List<LocationSource<WifiSpec>> getWifiLocationSources() {
return wifiLocationSources;
}
public void setWifiLocationSources(List<LocationSource<WifiSpec>> wifiLocationSources) {
this.wifiLocationSources = new ArrayList<LocationSource<WifiSpec>>(wifiLocationSources);
}
public void queueLocationRetrieval(CellSpec cellSpec) {
if (!cellStack.contains(cellSpec)) {
cellStack.push(cellSpec);
if (MainService.DEBUG) {
Log.d(TAG, "queued " + cellSpec + " for retrieval");
}
}
synchronized (loopThread) {
loopThread.notifyAll();
}
}
public void queueLocationRetrieval(WifiSpec wifiSpec) {
if (!wifiStack.contains(wifiSpec)) {
wifiStack.push(wifiSpec);
if (MainService.DEBUG) {
Log.d(TAG, "queued " + wifiSpec + " for retrieval");
}
}
synchronized (loopThread) {
loopThread.notifyAll();
}
}
public <T extends PropSpec> void queueLocationRetrieval(T spec) {
if (spec instanceof CellSpec) {
CellSpec cellSpec = (CellSpec) spec;
queueLocationRetrieval(cellSpec);
} else if (spec instanceof WifiSpec) {
WifiSpec wifiSpec = (WifiSpec) spec;
queueLocationRetrieval(wifiSpec);
} else {
throw new IllegalArgumentException("spec must be Cell or Wifi spec");
}
}
private void retrieveLocations(int threadCount) {
if (cellStack.isEmpty() && wifiStack.isEmpty()) {
return;
}
if (MainService.DEBUG) {
Log.d(TAG, "retrieving with " + threadCount + " threads");
}
Collection<Thread> threads = new ArrayList<Thread>();
while (threadCount-- > 0) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
retrieveLocations(cellStack, 10, cellLocationSources);
}
});
threads.add(t);
t.start();
t = new Thread(new Runnable() {
@Override
public void run() {
retrieveLocations(wifiStack, 10, wifiLocationSources);
}
});
threads.add(t);
t.start();
}
try {
for (Thread thread : threads) {
thread.join();
}
} catch (InterruptedException e) {
Log.w(TAG, e);
}
}
private <T extends PropSpec> void retrieveLocations(Deque<T> stack, int num,
List<? extends LocationSource<T>> locationSources) {
if (!stack.isEmpty()) {
Collection<T> specs = new ArrayList<T>();
while (!stack.isEmpty() && (num-- > 0)) {
T pop = stack.pop();
if (locationDatabase.get(pop) == null) {
specs.add(pop);
} else {
++num;
}
}
retrieveLocations(locationSources, specs);
}
}
private <T extends PropSpec> void retrieveLocations(List<? extends LocationSource<T>> locationSources,
Collection<T> todo) {
for (LocationSource<T> locationSource : locationSources) {
if (locationSource.isSourceAvailable()) {
if (MainService.DEBUG) {
Log.d(TAG, "Retrieving " + todo.size() + " locations from " + locationSource);
}
try {
for (LocationSpec<T> locationSpec : locationSource.retrieveLocation(todo)) {
locationDatabase.put(locationSpec);
todo.remove(locationSpec.getSource());
}
if (todo.isEmpty()) {
break;
}
} catch (Throwable t) {
Log.d(TAG, locationSource.getName() + " caused problem!", t);
}
} else if (MainService.DEBUG) {
Log.d(TAG, locationSource.getName() + " is currently not available");
}
}
for (T spec : todo) {
locationDatabase.put(new LocationSpec<T>(spec));
}
todo.clear();
}
private void retrieveLoop() {
while (!Thread.interrupted()) {
long time = System.currentTimeMillis();
if (lastRetrieve < (time - WAIT_BETWEEN)) {
retrieveLocations(1);
lastRetrieve = time;
}
synchronized (loopThread) {
try {
if (cellStack.isEmpty() && wifiStack.isEmpty()) {
loopThread.wait();
} else {
loopThread.wait((lastRetrieve + WAIT_BETWEEN) - time);
}
} catch (InterruptedException e) {
Log.w(TAG, e);
return;
}
}
}
}
public void start() {
loopThread.start();
}
public void stop() {
loopThread.interrupt();
}
}