[Enhancement] Cursor not closed
Summary
The method getAllCars() opens a Cursor via SQLiteDatabase#rawQuery and never closes it. This results in a resource leak of the underlying cursor/window and associated file descriptors.
- File:
Android-Debug-Database.git/sample-app/src/main/java/com/sample/database/CarDBHelper.java - Class:
CarDBHelper - Method:
public ArrayList<String> getAllCars(),public int count() - Lines: 109-122, 124-133
Impact
- Stability: Unclosed cursors leak native resources. Accumulating these leaks may triggers a
CursorWindowAllocationException, causing the app to crash. - Memory: Each leaked cursor holds onto a
CursorWindow(typically 2MB buffer)
Code Analysis
Current implementation:
public ArrayList<String> getAllCars() {
ArrayList<String> arrayList = new ArrayList<>();
SQLiteDatabase db = this.getReadableDatabase();
Cursor res = db.rawQuery("select * from cars", null);
res.moveToFirst();
while (!res.isAfterLast()) {
arrayList.add(res.getString(res.getColumnIndex(CARS_COLUMN_NAME)));
res.moveToNext();
}
return arrayList; // Cursor `res` is never closed
}
public int count() {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.rawQuery("select * from cars", null);
if (cursor != null && cursor.getCount() > 0) {
cursor.moveToFirst();
return cursor.getInt(0);
} else {
return 0;
}
}Issues:
- The
Cursorresis never closed on any path. moveToFirst()is called without checking the return value, which can cause incorrect iteration if the result set is empty.- Uses
select *when onlynameis needed, increasing memory usage and column-index fragility.
Suggested Fix
Using try-with-resources (API 19+) to ensure cursors are automatically closed is recommended.
- method
getAllCars():
public ArrayList<String> getAllCars() {
ArrayList<String> list = new ArrayList<>();
SQLiteDatabase db = getReadableDatabase();
try (Cursor c = db.rawQuery("select name from cars", null)) {
while (!c.isAfterLast()) {
list.add(res.getString(res.getColumnIndex(CARS_COLUMN_NAME)));
c.moveToNext();
}
}
return list;
}- method
count():
public int count() {
SQLiteDatabase db = getReadableDatabase();
// Fix: Use try-with-resources or try-finally
try (Cursor c = db.rawQuery("select count(*) from cars", null)) {
if (c.moveToFirst()) {
return c.getInt(0);
}
return 0;
}
}Additional Affected Locations
The same resource leak pattern (unclosed Cursor from rawQuery) exists in the following locations.
Module: sample-app
(src/main/java/com/sample/database/)
ContactDBHelper.java:getAllCotacts()(Lines ~111-124),count()(Lines ~126-135)ExtTestDBHelper.java:count()(Lines ~57-66)
Module: sample-app-encrypt
(src/main/java/com/sample/encrypt/database/)
CarDBHelper.java:getAllCars()(Lines ~109-122)ContactDBHelper.java:getAllCotacts()(Lines ~111-124),count()(Lines ~126-135)ExtTestDBHelper.java:count()(Lines ~57-66)
Context & Acknowledgement: This issue was identified during our academic research on Java resource management. We have manually reviewed this finding. Thank you for maintaining this open-source project! We hope this report helps.
Source: amitshekhariitbhu/Android-Debug-Database