androiddatabasememoryfile-copyinglow-memory

Android File Copy Confirm for Completeness


In my scenario, I'm using an external read only db zipped in Assets and extracting/copying to internal storage on initial run.

The issue happens when user's device does not have sufficient memory on disk to complete the copy.

I do check for available space prior, however Android OS itself will use virtual memory/page file when low on Ram so # of free MB's is not constant.

I would like to find a way to check if file itself is complete, any way to accomplish this?


Solution

  • Checking that the files are consistent is always done by checking the MD5 hash of the two file if they match each other.

    You can use this class from the CyanogenMod ROM repository on github.This should work pretty well https://github.com/CyanogenMod/android_packages_apps_CMUpdater/blob/cm-10.2/src/com/cyanogenmod/updater/utils/MD5.java

    /*
     * Copyright (C) 2012 The CyanogenMod Project
     *
     * * Licensed under the GNU GPLv2 license
     *
     * The text of the license can be found in the LICENSE file
     * or at https://www.gnu.org/licenses/gpl-2.0.txt
     */
    
    package com.cyanogenmod.updater.utils;
    
    import android.text.TextUtils;
    import android.util.Log;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.math.BigInteger;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    
    public class MD5 {
        private static final String TAG = "MD5";
    
        public static boolean checkMD5(String md5, File updateFile) {
            if (TextUtils.isEmpty(md5) || updateFile == null) {
                Log.e(TAG, "MD5 string empty or updateFile null");
                return false;
            }
    
            String calculatedDigest = calculateMD5(updateFile);
            if (calculatedDigest == null) {
                Log.e(TAG, "calculatedDigest null");
                return false;
            }
    
            Log.v(TAG, "Calculated digest: " + calculatedDigest);
            Log.v(TAG, "Provided digest: " + md5);
    
            return calculatedDigest.equalsIgnoreCase(md5);
        }
    
        public static String calculateMD5(File updateFile) {
            MessageDigest digest;
            try {
                digest = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                Log.e(TAG, "Exception while getting digest", e);
                return null;
            }
    
            InputStream is;
            try {
                is = new FileInputStream(updateFile);
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Exception while getting FileInputStream", e);
                return null;
            }
    
            byte[] buffer = new byte[8192];
            int read;
            try {
                while ((read = is.read(buffer)) > 0) {
                    digest.update(buffer, 0, read);
                }
                byte[] md5sum = digest.digest();
                BigInteger bigInt = new BigInteger(1, md5sum);
                String output = bigInt.toString(16);
                // Fill to 32 chars
                output = String.format("%32s", output).replace(' ', '0');
                return output;
            } catch (IOException e) {
                throw new RuntimeException("Unable to process file for MD5", e);
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    Log.e(TAG, "Exception on closing MD5 input stream", e);
                }
            }
        }
    }
    

    You should precalculate your md5 hash of the file and just check if it matches with the new transfered file.