This guide explains how Android apps can detect incoming and outgoing calls without requesting the microphone or call log permissions, using the PhoneStateListener and TelephonyCallback APIs. You’ll learn the exact steps to implement call state detection while respecting user privacy, and what to do when the system restricts access.
What You’ll Need
- Android Studio (current stable version)
- An Android device or emulator running Android 6.0 (API 23) or higher
- Basic knowledge of Kotlin or Java and Android app structure
- Understanding of Android permissions and runtime permission requests
Step-by-Step
- Declare the phone state permission in your manifest. Add
<uses-permission android:name="android.permission.READ_PHONE_STATE" />to yourAndroidManifest.xml. This permission is required to listen to phone state changes. It does not grant access to call logs or audio. - Check if your app targets Android 12 (API 31) or higher. If yes, you must also declare
<uses-permission android:name="android.permission.PHONE_STATE" />? Actually, that permission is not needed. The correct additional permission for call detection without call logs isREAD_PHONE_STATEonly. For Android 12+, you need to declareandroid.permission.READ_PHONE_STATEand also ensure you do not requestREAD_CALL_LOGunless you truly need call history. - Request the permission at runtime. On Android 6.0+ (API 23+), you must request
READ_PHONE_STATEfrom the user. UseActivityCompat.requestPermissions()with a request code. The user can grant or deny this permission. If denied, you cannot detect calls. - Create a phone state listener. For Android 11 (API 30) and below, use
PhoneStateListenerwithLISTEN_CALL_STATE. For Android 12+ (API 31+), useTelephonyCallbackand its inner classCallStateListener. The system will call your callback when the call state changes. - Register your listener with the TelephonyManager. Get an instance of
TelephonyManagerviagetSystemService(Context.TELEPHONY_SERVICE). Then calltelephonyManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE)(for old API) ortelephonyManager.registerTelephonyCallback(executor, callback)(for new API). - Handle the call state changes. In your callback, you will receive the state as one of
TelephonyManager.CALL_STATE_IDLE,CALL_STATE_RINGING, orCALL_STATE_OFFHOOK. You can also get the incoming phone number via theincomingNumberparameter, but note that on Android 12+ this is only available if your app has theREAD_CALL_LOGpermission. For privacy, you can ignore the number and just track the state. - Unregister the listener when done. To avoid memory leaks, call
telephonyManager.listen(listener, PhoneStateListener.LISTEN_NONE)ortelephonyManager.unregisterTelephonyCallback(callback)when your app no longer needs to detect calls (e.g., inonDestroy()of your activity or service). - Test your implementation. Run your app on a device or emulator. Make a test call to the device (or use the emulator’s phone app) and verify that your callback logs the state changes. Remember that the emulator may not simulate all call states perfectly.
Common Mistakes
- Forgetting to request the permission at runtime. Declaring it in the manifest is not enough for Android 6.0+. You must prompt the user.
- Using the wrong API for your target SDK. If your app targets Android 12+ but you use
PhoneStateListener, it will still work but is deprecated. More importantly, on Android 12+ you must useTelephonyCallbackto receive call state without theREAD_CALL_LOGpermission. - Trying to read the phone number without permission. On Android 12+, the
incomingNumberfield is redacted unless your app hasREAD_CALL_LOG. Do not rely on it. - Not unregistering the listener. This can cause memory leaks and your app may keep receiving updates even when not needed.
- Assuming the permission is granted automatically. Some OEMs may have stricter permission policies. Always check if you have permission before registering.
Tips
- Use a foreground service if you need to detect calls while the app is not in the foreground. The system may kill background apps, so a service with a notification is more reliable.
- Consider using
ACTION_PHONE_STATE_CHANGEDbroadcast receiver as an alternative, but note that this broadcast is not delivered to manifest-declared receivers on Android 8.0+ unless the app is in the foreground. For background detection, a foreground service is the recommended approach. - Always explain to the user why you need the
READ_PHONE_STATEpermission. This improves trust and acceptance rates. - Test on both a real device and an emulator, as emulator call simulation can be limited.
- If you only need to know if a call is ongoing (e.g., to pause music), you can also use
TelecomManagerto checkisInCall(), but that requires theREAD_PHONE_STATEpermission as well.
Troubleshooting
- No call state changes are received. First, ensure that the permission is granted. Go to Settings > Apps > Your App > Permissions and verify. If it is denied, request it again. Also check that you have registered the listener correctly and that your app is running (not killed).
- On Android 12+, the callback is not called. Make sure you are using
TelephonyCallbackand notPhoneStateListener. Also, ensure you are passing a validExecutor(e.g.,ContextCompat.getMainExecutor(context)). - The app crashes when registering the listener. This can happen if you do not have the permission. Wrap the registration in a try-catch and check for
SecurityException. - The incoming number is always null. As mentioned, on Android 12+ you need
READ_CALL_LOGto see the number. If you do not need it, ignore it. - The listener works, but only when the app is in the foreground. This is expected on modern Android. If you need background detection, use a foreground service and register the listener from that service.
Frequently Asked Questions
Q: Can I detect calls without any permission?
A: No, you need READ_PHONE_STATE to access call state. It is a normal permission, so the user is prompted to grant it.
Q: Does this method allow me to record calls?
A: No, it only gives you the call state (idle, ringing, off-hook). Recording audio requires the microphone permission and additional APIs, and is subject to legal restrictions.
Q: Can I get the caller’s phone number without call log permission?
A: On Android 11 and earlier, you may get the number with READ_PHONE_STATE. On Android 12+, the number is redacted unless you have READ_CALL_LOG. So, for privacy, you should not rely on it.
Q: Will this work on all Android devices?
A: In general, yes, but some OEMs (like Samsung, Xiaomi) may have additional restrictions or battery optimization that can interfere. Always test on your target devices.
Q: Is there a way to detect calls without any user prompt?
A: No, the user must grant the permission. You cannot bypass the permission dialog.
Q: Can I use this to automatically reject calls?
A: No, rejecting a call requires the MODIFY_PHONE_STATE permission, which is a system permission not available to regular apps. You can only observe the state.
Related
General steps for current mainstream versions; menus can differ by device and version. See the disclaimer or report an error.
More in this bed
- Gmail Gemini Privacy: Data Access & Limit Settings
- Fix Outlook Inbox Corruption with DISM in Windows
- How to Set Up Apple Pay on iPhone
- How to Choose a Refurbished iPhone With Years of Support
- Voice Dictation in Google Docs: How to Use It
- Turn Off Message Notifications on Mac