Back to project page neoupdate-android-sdk.
The source code is released under:
GNU General Public License
If you think the Android project neoupdate-android-sdk listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package in.neoandroid.neoupdate; //from ww w. j a va 2 s . c o m /* neoUpdate Android SDK: neoUpdate.java Copyright (C) 2013-2014 Neophyte Technologies LLP & Respective Contributors See contributors.txt for complete list of contributors. 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 <http://www.gnu.org/licenses/>. */ import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.security.DigestInputStream; import java.security.KeyFactory; import java.security.MessageDigest; import java.security.PublicKey; import java.security.Signature; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.concurrent.locks.ReentrantLock; import java.util.zip.GZIPInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicNameValuePair; import org.json.JSONArray; import org.json.JSONObject; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.Environment; import android.provider.Settings; import android.util.Base64; import android.util.Log; import android.webkit.MimeTypeMap; import static android.provider.BaseColumns._ID; public class neoUpdate extends AsyncTask<Void, Float, String> { private final double neoUpdateVersion = 1.0f; private final static boolean enableDebug = true; private final static String TAG = "[neoUpdate]"; private final String serverUrl = "https://neoupdate-in.appspot.com"; private String baseUrl; private int nConnections; /**< if 0 -> assumes local filesystem */ private ArrayList<NewAsset> filesToDownload; private String tmpDir; private Boolean stopped; private String appToken; private String appSecret; private int totalFilesToDownload; private String deviceID; private String serialNo; private String macAddress; private Context context; private PackageInfo packageInfo; private NewAsset apkUpdatePath; private neoUpdateDB db; private ReentrantLock lock; private Boolean fromOfflineStorage = false; private Boolean fromNPKStorage = false; // File list private final String metafile = "/neoupdate.json"; /** * file:// implies local filesystem * @param baseUrl * @param tmpDir * @param nSimultaneousConnections */ public neoUpdate(Context c, String baseUrl, String tmpDir, String appToken, String appSecret, int nSimultaneousConnections) { this.baseUrl = baseUrl; this.tmpDir = tmpDir; this.appToken = appToken; this.appSecret = appSecret; nConnections = nSimultaneousConnections; context = c; if(nConnections <= 0) nConnections = 1; // Check for local filesystem if(baseUrl.startsWith("file:///") || appToken == null || appSecret == null) { nConnections = 0; this.baseUrl = baseUrl.replace("file:///", "/"); fromOfflineStorage = true; if(baseUrl.endsWith(".npk")) fromNPKStorage = true; } else { this.baseUrl = serverUrl+baseUrl; if(this.baseUrl.endsWith("/")) this.baseUrl = this.baseUrl.substring(0, this.baseUrl.length()-1); lock = new ReentrantLock(); } filesToDownload = new ArrayList<NewAsset>(); deviceID = Settings.Secure.getString(c.getContentResolver(), Settings.Secure.ANDROID_ID); serialNo = neoUpdate.getSerialNo(); macAddress = getWifiMac(c); if(deviceID == null) deviceID = ""; if(serialNo == null) serialNo = ""; if(macAddress == null) macAddress = ""; try { packageInfo = c.getPackageManager().getPackageInfo(c.getPackageName(), 0); } catch(Exception e) { } db = new neoUpdateDB(c); stopped = false; totalFilesToDownload = 0; if(enableDebug) { Log.d(TAG, "DeviceID: "+deviceID); Log.d(TAG, "serialNo: "+serialNo); Log.d(TAG, "MAC Address: "+macAddress); Log.d(TAG, "SDCard: "+Environment.getExternalStorageDirectory().getAbsolutePath()); Log.d(TAG, "Data: "+Environment.getDataDirectory().getAbsolutePath()); } } private String getMetaFromNPK() { try { GZIPInputStream npkFile = new GZIPInputStream(new FileInputStream(baseUrl)); //FileInputStream npkFile = new FileInputStream(baseUrl); TarArchiveInputStream input = new TarArchiveInputStream(npkFile); TarArchiveEntry ae; while((ae = input.getNextTarEntry())!=null) { if(ae.isDirectory()) Log.e("[neoUpdate]", "Dir: "+ae.getName()); else Log.e("[neoUpdate]", "File: "+ae.getName()); if(ae.getName().equalsIgnoreCase("neoupdate.json")) { byte buff[] = new byte[(int) ae.getSize()]; input.read(buff); input.close(); return new String(buff); } } input.close(); } catch(Exception e) { e.printStackTrace(); } return null; } private void startUpdateApk(Uri installerUri) { MimeTypeMap myMime = MimeTypeMap.getSingleton(); String mimeType = myMime.getMimeTypeFromExtension("apk"); Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setDataAndType(installerUri, mimeType);//"application/vnd.android.package-archive"); context.startActivity(intent); } public static String getDeviceID(Context c) { return Settings.Secure.getString(c.getContentResolver(), Settings.Secure.ANDROID_ID); } public static String getWifiMac(Context c) { try { WifiManager wifiMgr = (WifiManager) c.getSystemService(Context.WIFI_SERVICE); return wifiMgr.getConnectionInfo().getMacAddress(); } catch(Exception e) { } return null; } private boolean checkSignature(String jsonContent, String sign) { Log.d(TAG, "JSON: "+jsonContent); if( sign == null) return false; final String publicKeyStr = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq+6EG/fAE+zIdh5Wzqnf"+ "Fo4nCf7t7eJcKyvk1lqX1MdkIi/fUs8HQ4aQ4jWLCO4M1Gkz1FQiXOnheGLV5MXY"+ "c9GyaglsofvpA/pU5d16FybX2pCevbTzcm39eU+XlwQWOr8gh23tYD8G6uMX6sIJ"+ "W+1k1FWdud9errMVm0YUScI+J4AV5xzN0IQ29h9IeNp6oFqZ2ByWog6OBMTUDFIW"+ "q8oRvH0OuPv3zFR5rKwsbTYb5Da8lhUht04dLBA860Y4zeUu98huvS9jQPu2N4ns"+ "Hf425FfDJ/wae+7eLdQo7uFb+Wvc+PO9U39e6vXQfa8ZkUoXHD0XZN4jsFcKYuJw"+ "OwIDAQAB"; try { byte keyBytes[] = Base64.decode(publicKeyStr.getBytes(), Base64.NO_WRAP); X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(keyBytes); KeyFactory kf = KeyFactory.getInstance("RSA"); PublicKey publicKey = kf.generatePublic(publicSpec); Signature signer = Signature.getInstance("SHA1withRSA"); signer.initVerify(publicKey); signer.update(jsonContent.getBytes(), 0, jsonContent.length()); return signer.verify(Base64.decode(sign, Base64.NO_WRAP)); } catch (Exception e) { } return false; } /* -- Works only with Android 2.3+ */ public static String getSerialNo() { //return android.os.SystemProperties.get("ro.serialno", "unknown"); try { Class<?> c = Class.forName("android.os.SystemProperties"); Method get = c.getMethod("get", String.class, String.class ); return (String) (get.invoke(c, "ro.serialno", "")); } catch(Exception e) { } return ""; } /** * A Simpler HTTPConnection helper. * Used currently. */ private static HttpURLConnection getHTTPConnection(String url) throws MalformedURLException, IOException { HttpURLConnection c; c = (HttpURLConnection) new URL(url).openConnection(); c.setUseCaches(false); c.connect(); return c; } public HttpResponse HttpWithPostData(String api, long fromBytes) { String url = baseUrl+api; try { url = baseUrl+URLEncoder.encode(api, "UTF-8"); } catch(Exception e) { if(enableDebug) e.printStackTrace(); url = baseUrl+api; } // Create a new HttpClient and Post Header HttpClient httpclient = new DefaultHttpClient(); HttpPost httppost = new HttpPost(url); Log.d(TAG, "HTTP Fetch: "+url+" with Resume: "+fromBytes); if(appToken == null || appSecret == null || appToken.length() == 0 || appSecret.length() == 0) return null; if(deviceID.length() == 0 && serialNo.length() == 0 && macAddress.length() == 0) return null; try { // For resuming downloads if(fromBytes > 0) httppost.addHeader(new BasicHeader("Range","bytes="+fromBytes+"-")); // Add post data ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2); nameValuePairs.add(new BasicNameValuePair("APP_TOKEN", appToken)); nameValuePairs.add(new BasicNameValuePair("APP_SECRET", appSecret)); if(deviceID != null) nameValuePairs.add(new BasicNameValuePair("DEVICE_ID", deviceID)); if(serialNo != null) nameValuePairs.add(new BasicNameValuePair("DEVICE_SERIAL", serialNo)); if(macAddress != null) nameValuePairs.add(new BasicNameValuePair("DEVICE_MAC", macAddress)); httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs)); // Execute HTTP Post Request return httpclient.execute(httppost); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } private static String inputStreamToString(InputStream is) { String line = ""; StringBuilder total = new StringBuilder(); BufferedReader rd = new BufferedReader(new InputStreamReader(is)); try { while ((line = rd.readLine()) != null) total.append("\n"+line); } catch(Exception e) { e.printStackTrace(); }; return total.toString(); } public int totalFilesToDownload() { return totalFilesToDownload; } public int filesToDownload() { return filesToDownload.size(); } /** * Stops the update procedure. * @return Success/failure */ public Boolean stopUpdate() { stopped = true; return true; } public static Boolean isDevicePresent(Context c, String device) { neoUpdateDB db = new neoUpdateDB(c); return db.isDevicePresent(device); } private InputStream getLocalFile(String path) throws FileNotFoundException { FileInputStream file = new FileInputStream(path); return file; } private JSONObject downloadMetafile() { // TODO: Check for resuming download (?) - Not necessary for metafiles ? try { String str = null; if(nConnections == 0 || fromOfflineStorage) { if(fromNPKStorage) str = getMetaFromNPK(); else str = inputStreamToString(getLocalFile(baseUrl+metafile)); } else { HttpResponse res = HttpWithPostData(metafile, 0); if(res != null) str = inputStreamToString(res.getEntity().getContent()); } if(str == null) return null; String sign = str.substring(str.lastIndexOf('\n')).trim(); str = str.substring(0, str.lastIndexOf('\n')).trim(); if(!checkSignature(str, sign)) { Log.e(TAG, "Signature Verification failed!"); return null; } return new JSONObject(str); } catch (Exception e) { e.printStackTrace(); } return null; } private Boolean parseMetafile(JSONObject metafile) { double version; boolean forceVersion; boolean allowed = false; try { version = metafile.getDouble("version"); forceVersion = metafile.getBoolean("forceVersion"); JSONObject appDetails = metafile.getJSONObject("app"); JSONArray assets = metafile.getJSONArray("assets"); JSONArray devices = metafile.getJSONArray("allowedDevices"); int nAssets = assets.length(); String packageName = appDetails.getString("packageName"); int versionCode = appDetails.getInt("versionCode"); String apkPath = appDetails.getString("APK"); boolean offlineSupport = appDetails.getBoolean("offlineSupport"); if(enableDebug) { Log.d(TAG,"Version: "+version+":"+neoUpdateVersion); Log.d(TAG,"Package Name: "+packageName+":"+packageInfo.packageName); Log.d(TAG,"APK Path: "+apkPath); } // Check if it is being updated using offline storage if(!offlineSupport && fromOfflineStorage) { Log.e(TAG, "Updating from offline storage is disabled for this app?"); return false; } db.clearDevicesList(); for(int i=0;i<devices.length();i++) { String device = devices.getString(i); if(device.length() > 0 && deviceID.compareToIgnoreCase(device) == 0) allowed = true; db.insertDevice(device); if(enableDebug) Log.d(TAG,"Device Allowed: "+device); } // DeviceID or signature error if(!allowed) return false; apkUpdatePath = null; if(version > neoUpdateVersion && forceVersion) { Log.e(TAG,"neoUpdate seems to be of older version! Required: "+version+" Current: "+neoUpdateVersion); return false; } if(packageInfo.packageName.compareTo(packageName) != 0) { Log.e(TAG, "PackageNames don't seem to match - url for some other app? Provided: "+packageName); return false; } if(packageInfo.versionCode < versionCode) { // APK Update Required - Lets first do that apkUpdatePath = new NewAsset(); apkUpdatePath.path = apkPath; apkUpdatePath.md5 = appDetails.getString("md5"); return true; } // Parse the assets for(int i=0;i<nAssets;i++) { JSONObject obj = assets.getJSONObject(i); NewAsset asset = new NewAsset(); asset.path = obj.getString("path"); asset.md5 = obj.getString("md5"); // Ignore already downloaded files if(db.updateAndGetStatus(asset.path, asset.md5) == neoUpdateDB.UPDATE_STATUS.UPDATE_COMPLETE) continue; filesToDownload.add(asset); if(enableDebug) { Log.d(TAG, "Enqueued: "+asset.path+" With MD5: "+asset.md5); } } totalFilesToDownload = filesToDownload.size(); } catch(Exception e) { if(enableDebug) e.printStackTrace(); return false; } return true; } private String mapPath(String path) { String ret = path; if(path.startsWith("/sdcard/")) path.replaceFirst("/sdcard/",Environment.getExternalStorageDirectory().getAbsolutePath()+"/"); if(path.startsWith("/data/")) path.replaceFirst("/data/",Environment.getDataDirectory().getAbsolutePath()+"/"); return ret; } private void createSubDirectories(String path) { try { File newDir = new File(path.substring(0, path.lastIndexOf('/'))); newDir.mkdirs(); } catch(Exception e) { if(enableDebug) e.printStackTrace(); } } private boolean downloadFile(NewAsset asset, String toPath) { return downloadFile(asset, toPath, null, null); } private boolean downloadFile(NewAsset asset, String toPath, TarArchiveInputStream tin, TarArchiveEntry ae ) { if(enableDebug) Log.d(TAG,"Start download: "+asset.path + ":NPK: "+(tin!=null)); boolean resume = (db.updateAndGetStatus(asset.path, asset.md5) == neoUpdateDB.UPDATE_STATUS.UPDATE_RESUME); String newPath; if(toPath != null) newPath = toPath; else newPath = mapPath(asset.path); createSubDirectories(newPath); File newFile = new File(newPath); long fromBytes = 0; if(resume) fromBytes = newFile.length(); try { FileOutputStream os = new FileOutputStream(newFile, resume); db.setMd5(asset.path, asset.md5); if(tin != null && ae != null) { // Via NPK final int BUFF_SIZE = (8*1024); // Buffer size of 8KB byte[] buffer = new byte[BUFF_SIZE]; int n = 0; long size = ae.getSize(); if(resume && fromBytes > 0 && fromBytes < size) { tin.skip(fromBytes); size -= fromBytes; } while(size > 0) { n = BUFF_SIZE; if( n > size) n = (int)size; n = tin.read(buffer, 0, n); if(n < 0) break; if(n > 0) os.write(buffer, 0, n); } } else if(nConnections <= 0) { // Via Local File System FileInputStream is = new FileInputStream(baseUrl+asset.path); is.getChannel().transferTo(fromBytes, is.getChannel().size()-fromBytes, os.getChannel()); is.close(); } else { // Via Internet HttpResponse resp = HttpWithPostData(asset.path, fromBytes); resp.getEntity().writeTo(os); } db.setDownloaded(asset.path, true); os.close(); } catch(Exception e) { if(enableDebug) e.printStackTrace(); return false; } return true; } private String processFromLocalStorage() { while(filesToDownload.size() > 0 && !stopped) { NewAsset asset = filesToDownload.remove(0); if(!downloadFile(asset, null)) { Log.e(TAG,"File download failed!"); return "Unable to find the required file: "+asset.path; } publishProgress((float)(totalFilesToDownload-filesToDownload.size())/(float)totalFilesToDownload); } return "Success"; } private NewAsset findAndGetAsset(String path) { for(NewAsset asset:filesToDownload) { if(asset.path.equalsIgnoreCase(path)) { filesToDownload.remove(asset); return asset; } } return null; } private String processFromNPK() { try { GZIPInputStream npkFile = new GZIPInputStream(new FileInputStream(baseUrl)); //FileInputStream npkFile = new FileInputStream(baseUrl); TarArchiveInputStream input = new TarArchiveInputStream(npkFile); TarArchiveEntry ae; while((ae = input.getNextTarEntry())!=null && filesToDownload.size() > 0 && !stopped) { if(ae.isDirectory()) { Log.e("[neoUpdate]", "Dir: "+ae.getName()); } else { Log.e("[neoUpdate]", "File: "+ae.getName()); String filename = ae.getName(); NewAsset asset = findAndGetAsset(filename); if(asset != null) { downloadFile(asset, null, input, ae); publishProgress((float)(totalFilesToDownload-filesToDownload.size())/(float)totalFilesToDownload); } } } input.close(); } catch(Exception e) { e.printStackTrace(); return "Unknown Error: Update Failed!"; } return "Success"; } private boolean downloadAPKFromNPK() { try { String apkName = apkUpdatePath.path.replace("/", ""); GZIPInputStream npkFile = new GZIPInputStream(new FileInputStream(baseUrl)); //FileInputStream npkFile = new FileInputStream(baseUrl); TarArchiveInputStream input = new TarArchiveInputStream(npkFile); TarArchiveEntry ae; while((ae = input.getNextTarEntry())!=null) { if(!ae.isDirectory() && ae.getName().equalsIgnoreCase(apkName)) { String apkPath = tmpDir+apkUpdatePath.path; boolean status = downloadFile(apkUpdatePath, apkPath, input, ae); input.close(); return status; } } input.close(); } catch(Exception e) { e.printStackTrace(); } return false; } @Override protected String doInBackground(Void... params) { JSONObject metafile = downloadMetafile(); if(metafile == null) return "Could not download meta data. Please check the connection"; if(!parseMetafile(metafile)) return "Failed to authenticate the device or Failed to parse metadata."; if(apkUpdatePath != null) { String apkPath = ""; if(fromNPKStorage) { apkPath = tmpDir+apkUpdatePath.path.replace("/", ""); if(!downloadAPKFromNPK()) return "Unable to save APK!"; } else if(nConnections <= 0) { apkPath = baseUrl+apkUpdatePath.path; } else { apkPath = tmpDir+apkUpdatePath.path.substring(apkUpdatePath.path.lastIndexOf('/')); if(!downloadFile(apkUpdatePath, apkPath)) { return "Unable to download APK!"; } } startUpdateApk(Uri.parse("file:///"+apkPath)); return "APK Updated. Please re-run update after restarting the app to continue update"; } if(nConnections > 0) { long waitTime = 1000/nConnections; if(waitTime <= 0) waitTime = 1; Thread threads[] = new Thread[nConnections]; int i; for(i=0;i<nConnections;i++) { threads[i] = new Thread(new DownloadRunnable()); threads[i].start(); } while(!stopped) { if(totalFilesToDownload > 0) { float nComplete = (totalFilesToDownload-filesToDownload.size()-nConnections); if(nComplete >= 0) publishProgress(nComplete/(float)totalFilesToDownload); } boolean inprogress = false; for(i=0;i<nConnections;i++) { try { threads[i].join(waitTime); } catch(Exception e) { } inprogress = inprogress || threads[i].isAlive(); } if(!inprogress) break; if(stopped && filesToDownload.size() > 0) { lock.lock(); filesToDownload.clear(); lock.unlock(); } } } else { if(fromNPKStorage) return processFromNPK(); return processFromLocalStorage(); } return "Success"; } private static class neoUpdateDB extends SQLiteOpenHelper { private final static String DB_NAME = "neoUpdateDB"; private final static int DB_VERSION = 1; private final static String TABLE_NAME = "neoUpdate"; private final static String COL_PATH = "PATH"; private final static String COL_MD5 = "MD5"; private final static String COL_DOWNLOADED = "DOWNLOADED"; private final static String DEVICES_TABLE = "neoDevices"; private final static String COL_DEVICES = "DEVICES"; private SQLiteDatabase neoDB; public static enum UPDATE_STATUS { UPDATE_REQUIRED, UPDATE_RESUME, UPDATE_COMPLETE }; public neoUpdateDB(Context context) { super(context, DB_NAME, null, DB_VERSION); neoDB = getWritableDatabase(); } public void insertDevice(String device) { ContentValues cv = new ContentValues(); cv.put(COL_DEVICES, device); neoDB.insert(DEVICES_TABLE, null, cv); } public Boolean isDevicePresent(String device) { Cursor c = neoDB.query(DEVICES_TABLE, new String[] {COL_DEVICES}, COL_DEVICES+"=?", new String[]{device}, null, null, null, "1"); if(c != null && c.getCount() > 0) { c.close(); return true; } return false; } /** * Call this after completion of the download (Or to pause and resume the download later) * @param path Path of the file * @param status True to set it as complete / False to resume later */ public void setDownloaded(String path, boolean status) { ContentValues cv = new ContentValues(); cv.put(COL_DOWNLOADED, status); neoDB.update(TABLE_NAME, cv, COL_PATH+"=?", new String[] {path}); } /** * Call this to set the Md5, just after creating an empty file. * Note: Make sure not to call this before creating an empty file, * otherwise it might cause the subsequent call to return UPDATE_RESUME wrongly. * * @param path Path of the file * @param md5 MD5 sum of the file */ public void setMd5(String path, String md5) { ContentValues cv = new ContentValues(); cv.put(COL_MD5, md5); neoDB.update(TABLE_NAME, cv, COL_PATH+"=?", new String[] {path}); } private String getMd5Sum(File file) { try { byte[] buffer = new byte[8192]; FileInputStream iStream = new FileInputStream(file); MessageDigest md = MessageDigest.getInstance("MD5"); DigestInputStream dis = new DigestInputStream(iStream, md); while(dis.read(buffer) != -1) { } byte[] data = md.digest(); StringBuffer md5 = new StringBuffer(); for (int i=0;i<data.length;i++) { String hex = Integer.toHexString(0xFF & data[i]); if(hex.length() == 1) md5.append("0"); md5.append(hex); } dis.close(); return new String(md5); } catch(Exception e) { if(enableDebug) e.printStackTrace(); } return ""; } /** * * @param path * @param md5 * @return */ public UPDATE_STATUS updateAndGetStatus(String path, String md5) { boolean latestFileAvailable = false; Cursor c = neoDB.query(TABLE_NAME, new String[] {COL_MD5, COL_DOWNLOADED}, COL_PATH+"=?", new String[]{path}, null, null, null, "1"); int iMd5 = c.getColumnIndex(COL_MD5); int iDownloaded = c.getColumnIndex(COL_DOWNLOADED); File file = new File(path); if(c != null && c.getCount() > 0) { c.moveToFirst(); boolean downloaded = (c.getInt(iDownloaded) > 0); boolean md5Match = (md5.compareToIgnoreCase(c.getString(iMd5)) == 0); c.close(); if(!md5Match || !file.exists()) { if(downloaded) setDownloaded(path, false); return UPDATE_STATUS.UPDATE_REQUIRED; } else { if(downloaded) return UPDATE_STATUS.UPDATE_COMPLETE; return UPDATE_STATUS.UPDATE_RESUME; } } else if(file.exists()) { // File exists check its MD5 if(getMd5Sum(file).equalsIgnoreCase(md5)) latestFileAvailable = true; } // New path - insert this ContentValues cv = new ContentValues(); cv.put(COL_PATH, path); if(latestFileAvailable) { // Latest file is available - but it is not present in the db cv.put(COL_MD5, md5); cv.put(COL_DOWNLOADED, true); } else { cv.put(COL_MD5, ""); // Make sure not to input correct md5 - otherwise it might cause // the subsequent call to return UPDATE_RESUME wrongly cv.put(COL_DOWNLOADED, false); } neoDB.insert(TABLE_NAME, null, cv); return latestFileAvailable ? UPDATE_STATUS.UPDATE_COMPLETE : UPDATE_STATUS.UPDATE_REQUIRED; } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE "+TABLE_NAME+" ("+_ID+ " INTEGER PRIMARY KEY AUTOINCREMENT, "+COL_PATH+" TEXT NOT NULL, "+ COL_MD5+" TEXT NOT NULL, "+COL_DOWNLOADED+" INTEGER);"); db.execSQL("CREATE TABLE "+DEVICES_TABLE+" ("+_ID+ " INTEGER PRIMARY KEY AUTOINCREMENT, "+COL_DEVICES+" TEXT NOT NULL);"); } public void clearDevicesList() { neoDB.execSQL("DELETE FROM "+DEVICES_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME); db.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME); onCreate(db); } @Override public void finalize() { neoDB.close(); } } private static class NewAsset { public String path; public String md5; } private class DownloadRunnable implements Runnable { @Override public void run() { while(true) { NewAsset asset = null; lock.lock(); try { if(filesToDownload.size() > 0) asset = filesToDownload.remove(0); } catch(Exception e) { if(enableDebug) e.printStackTrace(); } lock.unlock(); if(asset != null) downloadFile(asset, null); else return; } } } }