Compare commits

...
Sign in to create a new pull request.

4 commits

Author SHA1 Message Date
21055f24f5 [android] Add a VerifyError workaround / test case
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2022-05-30 21:13:43 +03:00
01a3e0b456 [android] Catch exceptions in getDirSizeRecursively()
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2022-05-30 12:15:18 +03:00
6278efaf44 [android] Make internal storage always visible
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2022-05-30 12:15:18 +03:00
8947380197 [android] Improve reliability of map files detection
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2022-05-30 12:15:18 +03:00
4 changed files with 61 additions and 63 deletions

View file

@ -1036,6 +1036,12 @@ Java_com_mapswithme_maps_Framework_nativeGetSettingsDir(JNIEnv * env, jclass)
return jni::ToJavaString(env, GetPlatform().SettingsDir().c_str());
}
JNIEXPORT jstring JNICALL
Java_com_mapswithme_maps_Framework_nativeGetDataFileExt(JNIEnv * env, jclass)
{
return jni::ToJavaString(env, DATA_FILE_EXTENSION);
}
JNIEXPORT jobjectArray JNICALL
Java_com_mapswithme_maps_Framework_nativeGetMovableFilesExts(JNIEnv * env, jclass)
{

View file

@ -212,6 +212,8 @@ public class Framework
public static native void nativeDeactivatePopup();
public static native String nativeGetDataFileExt();
public static native String[] nativeGetMovableFilesExts();
public static native String[] nativeGetBookmarksFilesExts();

View file

@ -32,6 +32,7 @@ public class StoragePathManager
{
static final String TAG = StoragePathManager.class.getName();
private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.STORAGE);
private static final String DATA_FILE_EXT = Framework.nativeGetDataFileExt();
private static final String[] MOVABLE_EXTS = Framework.nativeGetMovableFilesExts();
static final FilenameFilter MOVABLE_FILES_FILTER = (dir, filename) -> {
for (String ext : MOVABLE_EXTS)
@ -52,7 +53,7 @@ public class StoragePathManager
public final List<StorageItem> mStorages = new ArrayList<>();
public int mCurrentStorageIndex = -1;
private StorageItem mEmulatedStorage = null;
private StorageItem mInternalStorage = null;
public StoragePathManager(@NonNull Context context)
{
@ -118,8 +119,6 @@ public class StoragePathManager
/**
* Adds a storage into the list if it passes sanity checks.
* Internal storage is omitted if it backs an emulated one (unless internal is the current one),
* hence internal should be fed to this method last.
*/
private void addStorageOption(File dir, boolean isInternal, String configPath)
{
@ -147,20 +146,27 @@ public class StoragePathManager
(isInternal ? "internal" : "external") + ", " +
freeSize + " available out of " + totalSize + " bytes";
// Check if internal and emulated are the same physical device.
// Allow for some divergence in freeSize because there could have been file operations inbetween free space checks.
if (isInternal && mEmulatedStorage != null && mEmulatedStorage.mTotalSize == totalSize
&& mEmulatedStorage.mFreeSize > freeSize - 1024 * 1024
&& mEmulatedStorage.mFreeSize < freeSize + 1024 * 1024)
/**
Removal of a second condition ("&& mEmulatedStorage.mFreeSize < 0") or a whole "if" block
leads to a VerifyError on Android 5 reproducible in android emulator using Nexus S API 21
with an Android 5.0 x86_64 AOSP (w/o Google APIs) image (also on a Honor 4C with Android 5.1.1).
Log:
I/art: Verification error in void com.mapswithme.maps.settings.StoragePathManager.addStorageOption(java.io.File, boolean, java.lang.String)
I/art: couldn't find method android.os.storage.StorageManager.getStorageVolume (Ljava/io/File;)Landroid/os/storage/StorageVolume;
I/art: void com.mapswithme.maps.settings.StoragePathManager.addStorageOption(java.io.File, boolean, java.lang.String) failed to verify: Set register to unknown type ConflictragePathManager.addStorageOption(java.io.File, boolean, java.lang.String): [0x109]
I/art:
E/art: Verification failed on class com.mapswithme.maps.settings.StoragePathManager in /data/app/app.organicmaps.beta-2/base.apk because: Verifier rejected class com.mapswithme.maps.settings.StoragePathManager due to bad method void com.mapswithme.maps.settings.StoragePathManager.addStorageOption(java.io.File, boolean, java.lang.String)
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: app.organicmaps.beta, PID: 7188
java.lang.VerifyError: Verifier rejected class com.mapswithme.maps.settings.StoragePathManager due to bad method void com.mapswithme.maps.settings.StoragePathManager.addStorageOption(java.io.File, boolean, java.lang.String) (declaration of 'com.mapswithme.maps.settings.StoragePathManager' appears in /data/app/app.organicmaps.beta-2/base.apk)
*/
if (
mInternalStorage != null
&& mInternalStorage.mFreeSize < 0
)
{
final String emulated = ", backs emulated (" + mEmulatedStorage.mPath + ")";
// Allow duplicating internal storage if its the current one (for migration purposes).
if (!isCurrent)
{
LOGGER.i(TAG, "Duplicate" + emulated + ": " + commentedPath);
return;
}
commentedPath += emulated;
commentedPath += " *VerifyError test case*";
}
boolean isEmulated = false;
@ -245,8 +251,8 @@ public class StoragePathManager
mStorages.add(storage);
if (isCurrent)
mCurrentStorageIndex = mStorages.size() - 1;
if (isEmulated)
mEmulatedStorage = storage;
if (isInternal)
mInternalStorage = storage;
LOGGER.i(TAG, "Accepted " + commentedPath);
}
catch (SecurityException | IOException ex)
@ -267,7 +273,7 @@ public class StoragePathManager
LOGGER.i(TAG, "Begin scanning storages");
mStorages.clear();
mCurrentStorageIndex = -1;
mEmulatedStorage = null;
mInternalStorage = null;
// External storages (SD cards and other).
for (File externalDir : mContext.getExternalFilesDirs(null))
@ -291,57 +297,34 @@ public class StoragePathManager
}
/**
* Determine whether the storage contains map files
* by checking for non-empty directories with version-like names (e.g. "220415").
* Determine whether the storage contains map files.
*/
private static boolean containsMapData(String storagePath)
{
File path = new File(storagePath);
File[] candidates = path.listFiles((pathname) -> {
if (!pathname.isDirectory())
return false;
try
{
String name = pathname.getName();
if (name.length() != 6)
return false;
int version = Integer.valueOf(name);
return (version > 120000 && version <= 999999);
}
catch (NumberFormatException ignored)
{
}
return false;
});
return (candidates != null && candidates.length > 0 &&
candidates[0].list().length > 0);
return StorageUtils.getDirSizeRecursively(new File(storagePath), (dir, filename) -> filename.endsWith(DATA_FILE_EXT)) > 0;
}
/**
* Get storage with the most free space.
* Get a writable non-internal storage with the most free space.
* Returns an internal storage if no other options are suitable.
*/
public StorageItem getBiggestStorage()
public StorageItem getDefaultStorage()
{
StorageItem res = null;
for (StorageItem storage : mStorages)
{
if (res == null || res.mFreeSize < storage.mFreeSize)
{
if ((res == null || res.mFreeSize < storage.mFreeSize)
&& storage.mIsReadonly == false && !storage.equals(mInternalStorage))
res = storage;
}
}
return res;
return res != null ? res : mInternalStorage;
}
/**
* Returns an available storage with existing maps files.
* Checks the currently configured storage first,
* then scans other storages. If no maps files found
* defaults to the storage with the most free space.
* Checks the currently configured storage first, then scans other storages.
* If no map files found uses getDefaultStorage().
*/
public static String findMapsStorage(@NonNull Application application)
{
@ -382,8 +365,8 @@ public class StoragePathManager
}
}
path = mgr.getBiggestStorage().mPath;
LOGGER.i(TAG, "Defaulting to a storage with the most free space: " + path);
path = mgr.getDefaultStorage().mPath;
LOGGER.i(TAG, "Using default storage " + path);
return path;
}

View file

@ -252,17 +252,24 @@ public class StorageUtils
public static long getDirSizeRecursively(File file, FilenameFilter fileFilter)
{
if (file.isDirectory())
try
{
long dirSize = 0;
for (File child : file.listFiles())
dirSize += getDirSizeRecursively(child, fileFilter);
if (file.isDirectory())
{
long dirSize = 0;
for (File child : file.listFiles())
dirSize += getDirSizeRecursively(child, fileFilter);
return dirSize;
return dirSize;
}
if (fileFilter.accept(file.getParentFile(), file.getName()))
return file.length();
}
catch (Exception e)
{
LOGGER.e(TAG, "Can't calculate file or directory size", e);
}
if (fileFilter.accept(file.getParentFile(), file.getName()))
return file.length();
return 0;
}