Skip to content

Conversation

@Jignesh-dimagi
Copy link
Contributor

Product Description

There are no visible changes

Technical Summary

https://dimagi.atlassian.net/browse/CCCT-1991

As it's not clear what is causing FCM token fetch fail, it has been decided to add more network logs to understand this issue. This PR will not solve the said ticket but will give more info about the network state, which will be used further to take the action accordingly.

QA Plan

As no specific QA required for testing this ticket.

Labels and Review

  • Do we need to enhance the manual QA test coverage ? If yes, the "QA Note" label is set correctly
  • Does the PR introduce any major changes worth communicating ? If yes, the "Release Note" label is set and a "Release Note" is specified in PR description.
  • Risk label is set correctly
  • The set of people pinged as reviewers is appropriate for the level of risk of the change

@coderabbitai
Copy link

coderabbitai bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

The PR introduces a new public API getNetworkType(Context) to ConnectivityStatus that returns the device's current network type as a human-readable string. The implementation handles version-specific Android networking APIs: for Android N and above, it uses NetworkCapabilities with transport types (WIFI, ETHERNET, CELLULAR); for earlier versions, it uses NetworkInfo constants. For cellular networks, it resolves the network subtype (2G/3G/4G/5G) via TelephonyManager and includes permission checks for READ_PHONE_STATE. FirebaseMessagingUtil is updated to include network type information when logging FCM token retrieval errors.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant ConnectivityStatus
    participant ActivityCompat
    participant ConnectivityManager
    participant NetworkCapabilities
    participant TelephonyManager
    
    Caller->>ConnectivityStatus: getNetworkType(context)
    
    Note over ConnectivityStatus: Check Android Version
    
    alt Android N (API 24+)
        ConnectivityStatus->>ConnectivityManager: getActiveNetwork()
        ConnectivityManager-->>ConnectivityStatus: network
        
        alt Network exists
            ConnectivityStatus->>ConnectivityManager: getNetworkCapabilities(network)
            ConnectivityManager-->>ConnectivityStatus: capabilities
            
            Note over ConnectivityStatus: Check Transport Type
            
            alt TRANSPORT_WIFI
                ConnectivityStatus-->>Caller: "wifi"
            else TRANSPORT_ETHERNET
                ConnectivityStatus-->>Caller: "ethernet"
            else TRANSPORT_CELLULAR
                rect rgb(200, 220, 255)
                    Note over ConnectivityStatus: Check READ_PHONE_STATE Permission
                    ConnectivityStatus->>ActivityCompat: checkSelfPermission()
                    ActivityCompat-->>ConnectivityStatus: permission result
                    
                    alt Permission Granted
                        ConnectivityStatus->>TelephonyManager: getNetworkType()
                        TelephonyManager-->>ConnectivityStatus: subtype constant
                        ConnectivityStatus->>ConnectivityStatus: mapSubtypeToString(subtype)
                        ConnectivityStatus-->>Caller: "2g"/"3g"/"4g"/"5g"
                    else Permission Denied
                        ConnectivityStatus-->>Caller: "unknown_network"
                    end
                end
            else Other
                ConnectivityStatus-->>Caller: "unknown_network"
            end
        else No network
            ConnectivityStatus-->>Caller: "no_network"
        end
    else Below Android N
        rect rgb(240, 220, 200)
            Note over ConnectivityStatus: Legacy NetworkInfo API
            ConnectivityStatus->>ConnectivityManager: getActiveNetworkInfo()
            ConnectivityManager-->>ConnectivityStatus: networkInfo
            
            alt NetworkInfo type = TYPE_WIFI
                ConnectivityStatus-->>Caller: "wifi"
            else TYPE_ETHERNET
                ConnectivityStatus-->>Caller: "ethernet"
            else TYPE_MOBILE
                ConnectivityStatus->>TelephonyManager: getNetworkType()
                TelephonyManager-->>ConnectivityStatus: subtype
                ConnectivityStatus->>ConnectivityStatus: mapSubtypeToString(subtype)
                ConnectivityStatus-->>Caller: "2g"/"3g"/"4g"/"5g"
            else Other or None
                ConnectivityStatus-->>Caller: "unknown_network"/"no_network"
            end
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

skip-integration-tests

Suggested reviewers

  • OrangeAndGreen
  • shubham1g5
  • conroy-ricketts
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding network logs when FCM token fetch fails, which is the primary focus of the PR.
Description check ✅ Passed The description covers the essential sections (Product Description, Technical Summary with ticket link, and Safety Assurance elements). However, it is incomplete—missing Automated test coverage details, Feature Flag clarification, and specific safety story documentation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jignesh/ccct-1991

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @app/src/org/commcare/utils/ConnectivityStatus.java:
- Around line 63-86: getNetworkTypeFromAndroid24 may NPE because
connectivityManager from context.getSystemService(ConnectivityManager.class) can
be null; add a null check right after obtaining connectivityManager in that
method (ConnectivityManager connectivityManager = ...), and if it's null return
NO_NETWORK (same convention used elsewhere), so the method only proceeds to call
getActiveNetwork() and getNetworkCapabilities() when connectivityManager is
non-null.
- Around line 88-104: In getNetworkTypeFromBelowAndroid24, add a null check for
the ConnectivityManager (cm) immediately after obtaining it and return
NO_NETWORK (or the same sentinel used in getNetworkTypeFromAndroid24) if cm is
null to avoid an NPE; i.e., after "ConnectivityManager cm =
(ConnectivityManager) context.getSystemService(...)" check "if (cm == null)
return NO_NETWORK;" before calling cm.getActiveNetworkInfo(), keeping existing
logic for activeNetwork, WIFI, TYPE_MOBILE, and READ_PHONE_STATE_UNKNOWN_NETWORK
intact.
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36a1cee and 4bdd0c3.

📒 Files selected for processing (2)
  • app/src/org/commcare/utils/ConnectivityStatus.java
  • app/src/org/commcare/utils/FirebaseMessagingUtil.java
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: shubham1g5
Repo: dimagi/commcare-android PR: 0
File: :0-0
Timestamp: 2025-05-08T11:08:18.530Z
Learning: PR #3048 "Phase 4 Connect PR" introduces a substantial feature called "Connect" to the CommCare Android app, which includes messaging, job management, delivery tracking, payment processing, authentication flows, and learning modules. It follows a modern architecture using Navigation Components with three navigation graphs, segregated business logic in Manager classes, and proper database persistence.
🧬 Code graph analysis (1)
app/src/org/commcare/utils/FirebaseMessagingUtil.java (1)
app/src/org/commcare/utils/ConnectivityStatus.java (1)
  • ConnectivityStatus (24-122)
🔇 Additional comments (4)
app/src/org/commcare/utils/FirebaseMessagingUtil.java (1)

103-119: LGTM! Network status logging for FCM diagnostics looks good.

The addition of network type information to the error logs will help diagnose intermittent FCM token fetch failures. The implementation correctly covers both the empty-token and task-failure cases.

app/src/org/commcare/utils/ConnectivityStatus.java (3)

28-36: LGTM! Well-defined constants for network types.

The constants provide clear, descriptive labels for network state logging. The READ_PHONE_STATE_UNKNOWN_NETWORK distinction is useful for diagnosing permission-related issues separately from actual network detection failures.


44-53: Good defensive null check added.

The null check for conManager before calling getActiveNetworkInfo() prevents a potential NPE and maintains consistent logging behavior.


106-121: LGTM! Comprehensive network type mapping.

The switch expression correctly maps all common TelephonyManager network type constants to their respective generations. The default case appropriately handles any unknown or future network types.

Comment on lines 63 to 86
@RequiresApi(Build.VERSION_CODES.N)
private static String getNetworkTypeFromAndroid24(Context context){
ConnectivityManager connectivityManager = context.getSystemService(ConnectivityManager.class);
Network currentNetwork = connectivityManager.getActiveNetwork();
if(currentNetwork!=null) {
NetworkCapabilities networkCapabilities = connectivityManager.getNetworkCapabilities(currentNetwork);
if (networkCapabilities != null) {
if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return WIFI;
}else if(networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)){
return ETHERNET;
}else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
return getNetworkType(tm.getDataNetworkType());
}else{
return READ_PHONE_STATE_UNKNOWN_NETWORK;
}
}
return UNKNOWN_NETWORK;
}
}
return NO_NETWORK;
}
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

Add null check for ConnectivityManager to prevent potential NPE.

getSystemService() can return null in edge cases (e.g., restricted contexts). The method isNetworkAvailable already handles this scenario with a null check, but this method does not.

🐛 Proposed fix
 @RequiresApi(Build.VERSION_CODES.N)
 private static String getNetworkTypeFromAndroid24(Context context){
     ConnectivityManager connectivityManager = context.getSystemService(ConnectivityManager.class);
+    if (connectivityManager == null) {
+        return UNKNOWN_NETWORK;
+    }
     Network currentNetwork = connectivityManager.getActiveNetwork();
     if(currentNetwork!=null) {
🤖 Prompt for AI Agents
In @app/src/org/commcare/utils/ConnectivityStatus.java around lines 63 - 86,
getNetworkTypeFromAndroid24 may NPE because connectivityManager from
context.getSystemService(ConnectivityManager.class) can be null; add a null
check right after obtaining connectivityManager in that method
(ConnectivityManager connectivityManager = ...), and if it's null return
NO_NETWORK (same convention used elsewhere), so the method only proceeds to call
getActiveNetwork() and getNetworkCapabilities() when connectivityManager is
non-null.

Comment on lines 88 to 104
private static String getNetworkTypeFromBelowAndroid24(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (activeNetwork == null || !activeNetwork.isConnected()) {
return NO_NETWORK;
}else if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
return WIFI;
} else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
TelephonyManager teleMan = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
return getNetworkType(teleMan.getNetworkType());
}else{
return READ_PHONE_STATE_UNKNOWN_NETWORK;
}
}
return UNKNOWN_NETWORK;
}
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

Add null check for ConnectivityManager for consistency.

Same issue as getNetworkTypeFromAndroid24 - add a null check for the ConnectivityManager to prevent potential NPE.

🐛 Proposed fix
 private static String getNetworkTypeFromBelowAndroid24(Context context) {
     ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+    if (cm == null) {
+        return UNKNOWN_NETWORK;
+    }
     NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
     if (activeNetwork == null || !activeNetwork.isConnected()) {
📝 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
private static String getNetworkTypeFromBelowAndroid24(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (activeNetwork == null || !activeNetwork.isConnected()) {
return NO_NETWORK;
}else if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
return WIFI;
} else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
TelephonyManager teleMan = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
return getNetworkType(teleMan.getNetworkType());
}else{
return READ_PHONE_STATE_UNKNOWN_NETWORK;
}
}
return UNKNOWN_NETWORK;
}
private static String getNetworkTypeFromBelowAndroid24(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm == null) {
return UNKNOWN_NETWORK;
}
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (activeNetwork == null || !activeNetwork.isConnected()) {
return NO_NETWORK;
}else if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
return WIFI;
} else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
TelephonyManager teleMan = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
return getNetworkType(teleMan.getNetworkType());
}else{
return READ_PHONE_STATE_UNKNOWN_NETWORK;
}
}
return UNKNOWN_NETWORK;
}
🤖 Prompt for AI Agents
In @app/src/org/commcare/utils/ConnectivityStatus.java around lines 88 - 104, In
getNetworkTypeFromBelowAndroid24, add a null check for the ConnectivityManager
(cm) immediately after obtaining it and return NO_NETWORK (or the same sentinel
used in getNetworkTypeFromAndroid24) if cm is null to avoid an NPE; i.e., after
"ConnectivityManager cm = (ConnectivityManager) context.getSystemService(...)"
check "if (cm == null) return NO_NETWORK;" before calling
cm.getActiveNetworkInfo(), keeping existing logic for activeNetwork, WIFI,
TYPE_MOBILE, and READ_PHONE_STATE_UNKNOWN_NETWORK intact.

Copy link
Contributor

@conroy-ricketts conroy-ricketts left a comment

Choose a reason for hiding this comment

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

This will be great to have for additional context from these errors!

shubham1g5
shubham1g5 previously approved these changes Jan 8, 2026
conroy-ricketts
conroy-ricketts previously approved these changes Jan 9, 2026
return UNKNOWN_NETWORK;
}

private static @networkType String getNetworkType(int telephonyManagerNetworkType){
Copy link
Contributor

Choose a reason for hiding this comment

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

@networkType was a nice addition here 👍


@StringDef({WIFI,ETHERNET,TWO_G,THREE_G,FOUR_G,FIVE_G,UNKNOWN_NETWORK,READ_PHONE_STATE_UNKNOWN_NETWORK,NO_NETWORK})
@Retention(RetentionPolicy.SOURCE)
public @interface networkType {}
Copy link
Contributor

Choose a reason for hiding this comment

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

annotations should start with capital letter - NetworkType

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done here

return (netInfo != null && netInfo.isConnected());
}

public static String getNetworkType(Context context){
Copy link
Contributor

Choose a reason for hiding this comment

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

all the getNetworkType methods should now use the @NetworkType

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done here

OrangeAndGreen
OrangeAndGreen previously approved these changes Jan 12, 2026
Copy link
Contributor

@OrangeAndGreen OrangeAndGreen left a comment

Choose a reason for hiding this comment

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

Looks good to me pending Shubham's comments.

@Jignesh-dimagi
Copy link
Contributor Author

@damagatchi reset this please

@Jignesh-dimagi
Copy link
Contributor Author

@damagatchi retest this please

@Jignesh-dimagi Jignesh-dimagi merged commit 5bca821 into master Jan 19, 2026
7 checks passed
@Jignesh-dimagi Jignesh-dimagi deleted the jignesh/ccct-1991 branch January 19, 2026 04:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-integration-tests Skip android tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants