package io.evercam.androidapp.tasks;
import android.os.AsyncTask;
import android.util.Log;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import io.evercam.Vendor;
import io.evercam.androidapp.ScanActivity;
import io.evercam.androidapp.dto.AppData;
import io.evercam.androidapp.feedback.ScanFeedbackItem;
import io.evercam.androidapp.utils.Commons;
import io.evercam.androidapp.utils.NetInfo;
import io.evercam.network.Constants;
import io.evercam.network.EvercamDiscover;
import io.evercam.network.discovery.DiscoveredCamera;
import io.evercam.network.discovery.GatewayDevice;
import io.evercam.network.discovery.IpScan;
import io.evercam.network.discovery.MacAddress;
import io.evercam.network.discovery.NatMapEntry;
import io.evercam.network.discovery.NetworkInfo;
import io.evercam.network.discovery.PortScan;
import io.evercam.network.discovery.ScanRange;
import io.evercam.network.discovery.ScanResult;
import io.evercam.network.discovery.UpnpDevice;
import io.evercam.network.discovery.UpnpDiscovery;
import io.evercam.network.discovery.UpnpResult;
import io.evercam.network.onvif.OnvifDiscovery;
import io.evercam.network.query.EvercamQuery;
public class ScanForCameraTask extends AsyncTask<Void, DiscoveredCamera, ArrayList<DiscoveredCamera>>
{
private final String TAG = "ScanForCameraTask";
private WeakReference<ScanActivity> scanActivityReference;
private NetInfo netInfo;
private Date startTime;
public ExecutorService pool;
public static ArrayList<DiscoveredCamera> cameraList;
public ArrayList<UpnpDevice> upnpDeviceList;
private boolean upnpDone = false;
private boolean natDone = false;
private boolean onvifDone = false;
//Check if single IP scan and port scan is completed or not by comparing the start and end count
private int singleIpStartedCount = 0;
private int singleIpEndedCount = 0;
private String externalIp = "";
private float scanPercentage = 0;
private int totalDevices = 255;
//ONVIF,SSDP and NAT discovery take 9 percents each. The rest is allocated to IP scan
private final int PER__DISCOVERY_METHOD_PERCENT = 9;
public ScanForCameraTask(ScanActivity scanActivity)
{
this.scanActivityReference = new WeakReference<>(scanActivity);
netInfo = new NetInfo(scanActivity);
pool = Executors.newFixedThreadPool(EvercamDiscover.DEFAULT_FIXED_POOL);
cameraList = new ArrayList<>();
upnpDeviceList = new ArrayList<>();
}
@Override
protected void onPreExecute()
{
getScanActivity().onScanningStarted();
}
@Override
protected ArrayList<DiscoveredCamera> doInBackground(Void... params)
{
startTime = new Date();
try
{
final ScanRange scanRange = new ScanRange(netInfo.getGatewayIp(), netInfo.getNetmaskIp());
totalDevices = scanRange.size();
externalIp = NetworkInfo.getExternalIP();
if(!pool.isShutdown() && ! isCancelled())
{
pool.execute(new OnvifRunnable());
pool.execute(new UpnpRunnable());
pool.execute(new NatRunnable(netInfo.getGatewayIp()));
}
IpScan ipScan = new IpScan(new ScanResult(){
@Override
public void onActiveIp(String ip)
{
if(!pool.isShutdown() && !isCancelled())
{
pool.execute(new IpScanRunnable(ip));
}
}
@Override
public void onIpScanned(String ip)
{
scanPercentage += getPerDevicePercent();
updatePercentageOnActivity(scanPercentage);
}
});
ipScan.scanAll(scanRange);
}
catch(Exception e)
{
Log.e(TAG, e.getLocalizedMessage());
e.printStackTrace();
}
int loopCount = 0;
while(!onvifDone && !upnpDone || ! natDone || singleIpStartedCount != singleIpEndedCount)
{
loopCount++;
if(loopCount > 20) break; //Wait for maximum 10 secs
if(isCancelled()) break;
try
{
Thread.sleep(500);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
return cameraList;
}
@Override
protected void onProgressUpdate(DiscoveredCamera... discoveredCameras)
{
if(getScanActivity() != null)
{
getScanActivity().addNewCameraToResultList(discoveredCameras[0]);
}
}
@Override
protected void onPostExecute(ArrayList<DiscoveredCamera> cameraList)
{
if(getScanActivity() != null)
{
getScanActivity().showScanResults(cameraList);
getScanActivity().onScanningFinished();
}
pool.shutdown();
Float scanningTime = Commons.calculateTimeDifferenceFrom(startTime);
Log.d(TAG, "Scanning time: " + scanningTime);
String username = "";
if(AppData.defaultUser != null)
{
username = AppData.defaultUser.getUsername();
}
new ScanFeedbackItem(getScanActivity(), username, scanningTime, cameraList).sendToKeenIo();
}
private float getPerDevicePercent()
{
return (float)(100 - PER__DISCOVERY_METHOD_PERCENT*3)/totalDevices;
}
private ScanActivity getScanActivity()
{
return scanActivityReference.get();
}
private void updatePercentageOnActivity(Float percentage)
{
if(getScanActivity() != null)
{
if(percentage != null)
{
if(!isCancelled() && getStatus() != Status.FINISHED)
{
getScanActivity().updateScanPercentage(percentage);
}
}
else
{
getScanActivity().updateScanPercentage(percentage);
}
}
}
private class OnvifRunnable implements Runnable
{
@Override
public void run()
{
new OnvifDiscovery(){
@Override
public void onActiveOnvifDevice(DiscoveredCamera discoveredCamera)
{
discoveredCamera.setExternalIp(externalIp);
publishProgress(discoveredCamera);
}
}.probe();
scanPercentage += PER__DISCOVERY_METHOD_PERCENT;
updatePercentageOnActivity(scanPercentage);
onvifDone = true;
}
}
private class IpScanRunnable implements Runnable
{
private String ip;
public IpScanRunnable(String ip)
{
this.ip = ip;
singleIpStartedCount++;
}
@Override
public void run()
{
try
{
String macAddress = MacAddress.getByIpAndroid(ip);
if (!macAddress.equals(Constants.EMPTY_MAC))
{
Vendor vendor = EvercamQuery.getCameraVendorByMac(macAddress);
if (vendor != null)
{
String vendorId = vendor.getId();
if (!vendorId.isEmpty())
{
// Then fill details discovered from IP scan
DiscoveredCamera camera = new DiscoveredCamera(ip);
camera.setMAC(macAddress);
camera.setVendor(vendorId);
camera.setExternalIp(externalIp);
// Start port scan
PortScan portScan = new PortScan(null);
portScan.start(ip);
ArrayList<Integer> activePortList = portScan.getActivePorts();
if(activePortList.size() > 0)
{
// Add active ports to camera object
for (Integer port : activePortList)
{
camera = PortScan.mergePort(camera, port);
}
//Iterate UPnP device list and publish the UPnP details if matches
if(upnpDeviceList.size() > 0)
{
for(UpnpDevice upnpDevice : upnpDeviceList)
{
String ipFromUpnp = upnpDevice.getIp();
if (ipFromUpnp != null && !ipFromUpnp.isEmpty())
{
if(ipFromUpnp.equals(camera.getIP()))
{
EvercamDiscover.mergeSingleUpnpDeviceToCamera(upnpDevice, camera);
break;
}
}
}
}
publishProgress(camera);
}
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
singleIpEndedCount ++;
}
}
private class UpnpRunnable implements Runnable
{
@Override
public void run()
{
try
{
UpnpDiscovery upnpDiscovery = new UpnpDiscovery(new UpnpResult(){
@Override
public void onUpnpDeviceFound(UpnpDevice upnpDevice)
{
Log.d(TAG, "UPnP device found: " + upnpDevice.toString());
upnpDeviceList.add(upnpDevice);
// If IP address matches
String ipFromUpnp = upnpDevice.getIp();
if (ipFromUpnp != null && !ipFromUpnp.isEmpty())
{
for(DiscoveredCamera discoveredCamera : cameraList)
{
if(discoveredCamera.getIP().equals(upnpDevice.getIp()))
{
DiscoveredCamera publishCamera = new DiscoveredCamera(discoveredCamera.getIP());
EvercamDiscover.mergeSingleUpnpDeviceToCamera(upnpDevice, publishCamera);
publishProgress(publishCamera);
break;
}
}
}
}
});
upnpDiscovery.discoverAll();
}
catch (Exception e)
{
e.printStackTrace();
}
upnpDone = true;
scanPercentage += PER__DISCOVERY_METHOD_PERCENT;
updatePercentageOnActivity(scanPercentage);
}
}
private class NatRunnable implements Runnable
{
private String routerIp;
public NatRunnable(String routerIp)
{
this.routerIp = routerIp;
}
@Override
public void run()
{
try
{
GatewayDevice gatewayDevice = new GatewayDevice(routerIp);
ArrayList<NatMapEntry> mapEntries = gatewayDevice.getNatTableArray(); //NAT Table
if(mapEntries.size() > 0)
{
for(NatMapEntry mapEntry : mapEntries)
{
String natIp = mapEntry.getIpAddress();
for(DiscoveredCamera discoveredCamera : cameraList)
{
if(discoveredCamera.getIP().equals(natIp))
{
DiscoveredCamera publishCamera = EvercamDiscover.mergeNatEntryToCameraIfMatches(discoveredCamera, mapEntry);
publishProgress(publishCamera);
break; //break the inner loop
}
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
natDone = true;
scanPercentage += PER__DISCOVERY_METHOD_PERCENT;
updatePercentageOnActivity(scanPercentage);
}
}
}