Function call ignored on Android API 29 but works on older versions

I have a function that reads text messages from a specific contact and displays them in a list. This worked perfectly on older Android versions but stopped working on API 29.

The loadTextMessages(getContext()) method gets completely ignored when running on API 29. I can even set a debug breakpoint right where it should execute, but it never gets hit. However, the same code runs fine on API 22.

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_messages, container, false);
    
    if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_SMS) 
            != PackageManager.PERMISSION_GRANTED) {
        // Permission not granted
    } else {
        // This function call is being skipped on API 29
        loadTextMessages(getContext());
    }
    
    return rootView;
}

@WithPermissions(permissions = {Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_SMS})
@TargetApi(Build.VERSION_CODES.M)
public void loadTextMessages(Context ctx) {
    HashSet<String> contactSet = new HashSet<>();
    contactSet.add(selectedContact);
    long conversationId = Telephony.Threads.getOrCreateThreadId(ctx, contactSet);
    Uri conversationUri = ContentUris.withAppendedId(Telephony.Threads.CONTENT_URI, conversationId);
    
    String[] columns = new String[] {Telephony.MmsSms.TYPE_DISCRIMINATOR_COLUMN, BaseColumns._ID,
            Telephony.Sms.Conversations.THREAD_ID, Telephony.Sms.ADDRESS, Telephony.Sms.BODY,
            "sort_index", Telephony.Sms.DATE_SENT, Telephony.Sms.DATE, Telephony.Sms.READ,
            Telephony.Sms.TYPE, Telephony.Sms.STATUS};
    
    Cursor cursor = ctx.getContentResolver().query(conversationUri, columns, null, null, "normalized_date desc");
    DatabaseUtils.dumpCursor(cursor);
    
    ArrayList<String> messageList = new ArrayList<String>();
    int messageCount = cursor.getCount();
    
    if(cursor.moveToFirst()) {
        for(int j = 0; j < messageCount; j++) {
            String messageText = cursor.getString(cursor.getColumnIndex(Telephony.Sms.BODY));
            String timestamp = cursor.getString(cursor.getColumnIndex(Telephony.Sms.DATE));
            
            Long timeValue = Long.parseLong(timestamp);
            Date messageDate = new Date(timeValue);
            SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss dd-MM-yyyy");
            String formattedDate = formatter.format(messageDate);
            
            cursor.moveToNext();
            messageAdapter.add("Text: " + messageText + "\n" + "Time: " + formattedDate);
        }
    }
}

What could cause this behavior change between Android versions?

Sounds like a scoped storage nightmare tbh. API 29 changed how apps access SMS data, so your loadTextMessages method probably fails internally even though checkSelfPermission shows granted. Try temporarily adding android:targetSdkVersion="28" to see if that fixes it. If it does, you’ll know it’s definitely the API 29 restrictions causing the problem.

Hit the same issue migrating to API 29. It’s the scoped storage changes - even with permissions passing, Android 10 added stricter SMS provider controls that make methods fail silently. I fixed it temporarily by adding android:requestLegacyExternalStorage="true" to the application tag in AndroidManifest.xml. But you’ll need to update to the newer SMS retrieval methods for a real fix. Also check that your app is the default SMS app or has explicit user consent - the permission model got way more restrictive. Your ContentResolver query is probably returning an empty cursor because of these security changes, which is why your breakpoint never hits the data processing code.

Android 29 broke a lot of SMS stuff, which probably explains what you’re seeing. The getOrCreateThreadId method got deprecated and doesn’t work reliably anymore. Don’t use Telephony.Threads.getOrCreateThreadId - query the SMS content provider directly with content://sms/conversations instead. Also check if you’ve got the right permissions. Google locked down SMS access hard in API 29, and some methods now need your app to be the default SMS handler. Your permission check might be failing silently too - add some logging right after it to make sure you’re actually hitting the else block. One more thing: ContentResolver queries can return null now because of stricter security policies. Add null checks before you call any cursor methods or you’ll crash.