Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5bb211f
Automated hotfix version bump
avazirna Jan 15, 2026
4e129b7
Fix user info retrieval for Crashlytics session
avazirna Dec 29, 2025
d7dda5d
log stale locations as non-fatals, remove deprecated API
shubham1g5 Jan 13, 2026
c8b16f9
Use an app setting to discard stale locations
shubham1g5 Jan 15, 2026
536d3cc
better method name and constant for default val
shubham1g5 Jan 15, 2026
c3839c4
log a different non-fatal on saving a stale location, removes locatio…
shubham1g5 Jan 15, 2026
81e52ef
dry code better
shubham1g5 Jan 15, 2026
680dd7c
Add perf monitoring trace for media files encryption time
avazirna Jan 14, 2026
47b2830
Add perf monitoring trace for large files encryption time
avazirna Jan 14, 2026
fe50e49
Add perf monitoring trace for XML files encryption time
avazirna Jan 14, 2026
01def49
Merge pull request #3494 from dimagi/hotfix-fix-user-info-missing-in-…
avazirna Jan 16, 2026
4f0b903
fix missing return
shubham1g5 Jan 16, 2026
dd74a99
Merge pull request #3495 from dimagi/copyLocationImprovements
shubham1g5 Jan 18, 2026
bf7e580
Refactor
avazirna Jan 18, 2026
9aa8df9
Refactor
avazirna Jan 19, 2026
78a496f
Merge pull request #3496 from dimagi/hotfix_add-file-cryptographic-op…
avazirna Jan 19, 2026
e4110bc
Make trace nullable when stopping tracing
avazirna Jan 19, 2026
eeb83ab
Refactor
avazirna Jan 19, 2026
184ee06
Merge pull request #3499 from dimagi/make-trace-null-safe-when-stoppi…
avazirna Jan 19, 2026
2729474
Merge branch 'master' into commcare_2.61
avazirna Jan 20, 2026
5473e68
Merge branch 'master' into commcare_2.61
avazirna Jan 21, 2026
6fe9114
Merge branch 'master' into commcare_2.61
avazirna Jan 23, 2026
ec194ee
Fix merge conflicts
avazirna Jan 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion app/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
android:versionCode="106"
android:versionName="2.62">


<uses-permission android:name="android.permission.NFC"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
Expand Down
25 changes: 21 additions & 4 deletions app/src/org/commcare/google/services/analytics/CCPerfMonitoring.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.commcare.google.services.analytics

import com.google.firebase.perf.FirebasePerformance
import com.google.firebase.perf.metrics.Trace
import org.apache.commons.io.FilenameUtils
import org.commcare.android.logging.ReportingUtils
import org.javarosa.core.services.Logger

Expand All @@ -12,6 +13,7 @@ object CCPerfMonitoring {
const val TRACE_APP_SYNC_DURATION = "app_sync_duration"
const val TRACE_CASE_SEARCH_TIME = "case_search_time"
const val TRACE_FORM_LOADING_TIME = "form_loading_time"
const val TRACE_FILE_ENCRYPTION_TIME = "file_encryption_time"
const val TRACE_ENTITY_MAP_READY_TIME = "entity_map_ready_time"
const val TRACE_ENTITY_MAP_LOADED_TIME = "entity_map_loaded_time"

Expand All @@ -24,10 +26,13 @@ object CCPerfMonitoring {
const val ATTR_SYNC_TYPE = "sync_type"
const val ATTR_FORM_NAME = "form_name"
const val ATTR_FORM_XMLNS = "form_xmlns"
const val ATTR_FILE_SIZE_BYTES = "file_size_bytes"
const val ATTR_FILE_TYPE = "file_type"
const val ATTR_MAP_MARKERS = "num_markers"
const val ATTR_MAP_POLYGONS = "num_polygons"
const val ATTR_MAP_GEO_POINTS = "num_geo_points"


fun startTracing(traceName: String): Trace? {
try {
val trace = FirebasePerformance.getInstance().newTrace(traceName)
Expand All @@ -43,12 +48,24 @@ object CCPerfMonitoring {
return null
}

fun stopTracing(trace: Trace, attrs: MutableMap<String, String>?) {
fun stopTracing(trace: Trace?, attrs: MutableMap<String, String>?) {
try {
attrs?.forEach { (key, value) -> trace.putAttribute(key, value) }
trace.stop()
attrs?.forEach { (key, value) -> trace?.putAttribute(key, value) }
trace?.stop()
} catch (exception: Exception) {
Logger.exception("Error stopping perf trace: ${trace.name}", exception)
Logger.exception("Error stopping perf trace: ${trace?.name?: "Unknown trace"}", exception)
}
}

fun stopFileEncryptionTracing(trace: Trace?, fileSizeBytes: Long, fileName: String) {
try {
val attrs: MutableMap<String, String> = HashMap()
attrs[ATTR_FILE_SIZE_BYTES] = fileSizeBytes.toString()
attrs[ATTR_FILE_TYPE] = FilenameUtils.getExtension(fileName)
stopTracing(trace, attrs)
} catch (e: java.lang.Exception) {
Logger.exception("Failed to stop tracing: $TRACE_FILE_ENCRYPTION_TIME", e)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class CommCareProviderLocationController(
private var mProviders = GeoUtils.evaluateProviders(mLocationManager)
private val mReceiver = ProviderChangedReceiver()
private var mLocationRequestStarted = false

private val mLocationListener =
object : LocationListener {
override fun onLocationChanged(location: Location) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import android.content.ContentValues;
import android.database.Cursor;

import com.google.firebase.perf.metrics.Trace;

import org.commcare.CommCareApplication;
import org.commcare.google.services.analytics.CCPerfMonitoring;
import org.commcare.interfaces.AppFilePathBuilder;
import org.commcare.models.encryption.EncryptionIO;
import org.commcare.modern.database.DatabaseHelper;
Expand Down Expand Up @@ -328,6 +331,8 @@ protected byte[] generateKeyAndAdd(ContentValues contentValues) {

private void writeStreamToFile(ByteArrayOutputStream bos, String filename,
byte[] key) throws IOException {
Trace trace = CCPerfMonitoring.INSTANCE.startTracing(CCPerfMonitoring.TRACE_FILE_ENCRYPTION_TIME);

DataOutputStream fileOutputStream = null;
try {
fileOutputStream = getOutputFileStream(filename, key);
Expand All @@ -340,6 +345,7 @@ private void writeStreamToFile(ByteArrayOutputStream bos, String filename,
e.printStackTrace();
}
}
CCPerfMonitoring.INSTANCE.stopFileEncryptionTracing(trace, bos.size(), filename);
}
}

Expand Down
12 changes: 10 additions & 2 deletions app/src/org/commcare/models/encryption/EncryptionIO.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.commcare.models.encryption;

import com.google.firebase.perf.metrics.Trace;

import org.commcare.google.services.analytics.CCPerfMonitoring;
import org.commcare.util.LogTypes;
import org.javarosa.core.io.StreamsUtil;
import org.javarosa.core.services.Logger;
Expand All @@ -10,6 +13,7 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidKeyException;
Expand All @@ -28,13 +32,17 @@
*/
public class EncryptionIO {

public static void encryptFile(String sourceFilePath, String destPath, SecretKeySpec symetricKey) throws FileNotFoundException,
StreamsUtil.InputIOException, StreamsUtil.OutputIOException {
public static void encryptFile(String sourceFilePath, String destPath, SecretKeySpec symetricKey) throws IOException {
Trace trace = CCPerfMonitoring.INSTANCE.startTracing(CCPerfMonitoring.TRACE_FILE_ENCRYPTION_TIME);

OutputStream os;
FileInputStream is;
os = createFileOutputStream(destPath, symetricKey);
is = new FileInputStream(sourceFilePath);
int fileSize = is.available();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use File.length() instead of InputStream.available() for accurate file size.

is.available() returns an estimate of bytes that can be read without blocking, not the actual file size. For accurate file size metrics, use the file's length directly.

Proposed fix
     public static void encryptFile(String sourceFilePath, String destPath, SecretKeySpec symetricKey) throws IOException {
         Trace trace = CCPerfMonitoring.INSTANCE.startTracing(CCPerfMonitoring.TRACE_FILE_ENCRYPTION_TIME);
 
         OutputStream os;
         FileInputStream is;
+        File sourceFile = new File(sourceFilePath);
+        long fileSize = sourceFile.length();
         os = createFileOutputStream(destPath, symetricKey);
         is = new FileInputStream(sourceFilePath);
-        int fileSize = is.available();
         StreamsUtil.writeFromInputToOutputNew(is, os);
 
         CCPerfMonitoring.INSTANCE.stopFileEncryptionTracing(trace, fileSize, sourceFilePath);
     }
🤖 Prompt for AI Agents
In `@app/src/org/commcare/models/encryption/EncryptionIO.java` at line 42, In
EncryptionIO, replace the use of InputStream.available() to determine file size
(the line assigning fileSize from is.available()) with the actual File.length()
on the File used to open that InputStream (e.g., use inputFile.length()),
converting or validating the long-to-int cast if the code expects an int and
guarding against null/zero files; ensure you reference the same File instance
that produced the InputStream and update any related logic that assumes
available() semantics.

StreamsUtil.writeFromInputToOutputNew(is, os);

CCPerfMonitoring.INSTANCE.stopFileEncryptionTracing(trace, fileSize, sourceFilePath);
}
Comment on lines +35 to 46
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Trace not stopped on exception path.

If an exception occurs during the write operation, the trace will never be stopped. Consider wrapping in a try-finally to ensure the trace is always stopped.

Proposed fix
     public static void encryptFile(String sourceFilePath, String destPath, SecretKeySpec symetricKey) throws IOException {
         Trace trace = CCPerfMonitoring.INSTANCE.startTracing(CCPerfMonitoring.TRACE_FILE_ENCRYPTION_TIME);
 
         OutputStream os;
         FileInputStream is;
-        os = createFileOutputStream(destPath, symetricKey);
-        is = new FileInputStream(sourceFilePath);
-        int fileSize = is.available();
-        StreamsUtil.writeFromInputToOutputNew(is, os);
-
-        CCPerfMonitoring.INSTANCE.stopFileEncryptionTracing(trace, fileSize, sourceFilePath);
+        File sourceFile = new File(sourceFilePath);
+        long fileSize = sourceFile.length();
+        try {
+            os = createFileOutputStream(destPath, symetricKey);
+            is = new FileInputStream(sourceFilePath);
+            StreamsUtil.writeFromInputToOutputNew(is, os);
+        } finally {
+            CCPerfMonitoring.INSTANCE.stopFileEncryptionTracing(trace, fileSize, sourceFilePath);
+        }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static void encryptFile(String sourceFilePath, String destPath, SecretKeySpec symetricKey) throws IOException {
Trace trace = CCPerfMonitoring.INSTANCE.startTracing(CCPerfMonitoring.TRACE_FILE_ENCRYPTION_TIME);
OutputStream os;
FileInputStream is;
os = createFileOutputStream(destPath, symetricKey);
is = new FileInputStream(sourceFilePath);
int fileSize = is.available();
StreamsUtil.writeFromInputToOutputNew(is, os);
CCPerfMonitoring.INSTANCE.stopFileEncryptionTracing(trace, fileSize, sourceFilePath);
}
public static void encryptFile(String sourceFilePath, String destPath, SecretKeySpec symetricKey) throws IOException {
Trace trace = CCPerfMonitoring.INSTANCE.startTracing(CCPerfMonitoring.TRACE_FILE_ENCRYPTION_TIME);
OutputStream os;
FileInputStream is;
File sourceFile = new File(sourceFilePath);
long fileSize = sourceFile.length();
try {
os = createFileOutputStream(destPath, symetricKey);
is = new FileInputStream(sourceFilePath);
StreamsUtil.writeFromInputToOutputNew(is, os);
} finally {
CCPerfMonitoring.INSTANCE.stopFileEncryptionTracing(trace, fileSize, sourceFilePath);
}
}
🤖 Prompt for AI Agents
In `@app/src/org/commcare/models/encryption/EncryptionIO.java` around lines 35 -
46, The method EncryptionIO.encryptFile starts a trace (trace) but does not
guarantee CCPerfMonitoring.INSTANCE.stopFileEncryptionTracing(...) is called if
StreamsUtil.writeFromInputToOutputNew throws; wrap the file I/O and write call
in a try-finally where in finally you always call
stopFileEncryptionTracing(trace, fileSize, sourceFilePath) and also ensure both
streams (os from createFileOutputStream(...) and is = new FileInputStream(...))
are closed safely (null-check and close inside finally or use try-with-resources
equivalent) so the trace is stopped and resources are released on exceptions as
well.


public static OutputStream createFileOutputStream(String filename,
Expand Down
8 changes: 8 additions & 0 deletions app/src/org/commcare/tasks/SaveToDiskTask.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package org.commcare.tasks;

import com.google.firebase.perf.metrics.Trace;

import org.commcare.CommCareApplication;
import org.commcare.activities.FormEntryActivity;
import org.commcare.activities.components.ImageCaptureProcessing;
import org.commcare.android.database.app.models.FormDefRecord;
import org.commcare.android.database.user.models.FormRecord;
import org.commcare.android.logging.ForceCloseLogger;
import org.commcare.google.services.analytics.CCPerfMonitoring;
import org.commcare.interfaces.FormSavedListener;
import org.commcare.logging.XPathErrorLogger;
import org.commcare.models.database.SqlStorage;
Expand Down Expand Up @@ -36,6 +39,8 @@

import javax.crypto.spec.SecretKeySpec;

import static org.commcare.utils.FileUtil.XML_EXTENSION;

/**
* @author Carl Hartung (carlhartung@gmail.com)
* @author Yaw Anokwa (yanokwa@gmail.com)
Expand Down Expand Up @@ -257,11 +262,14 @@ private void exportData(boolean markCompleted)
}

private void writeXmlToStream(ByteArrayPayload payload, OutputStream output) throws IOException {
Trace trace = CCPerfMonitoring.INSTANCE.startTracing(CCPerfMonitoring.TRACE_FILE_ENCRYPTION_TIME);

try {
InputStream is = payload.getPayloadStream();
StreamsUtil.writeFromInputToOutput(is, output);
} finally {
output.close();
CCPerfMonitoring.INSTANCE.stopFileEncryptionTracing(trace, payload.getLength(), XML_EXTENSION);
}
}

Expand Down
1 change: 1 addition & 0 deletions app/src/org/commcare/utils/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public class FileUtil {
private static final int WARNING_SIZE = 3000;

private static final String LOG_TOKEN = "cc-file-util";
public static final String XML_EXTENSION = "xml";

private static final String[] EXIF_TAGS = {
ExifInterface.TAG_GPS_LATITUDE,
Expand Down