Android Oreo静态广播接收器注册失效,如何实现后台通话记录功能?
Hey there, let's tackle this Android Oreo broadcast receiver issue you're facing—totally get why this is frustrating, since the old static broadcast approach doesn't work anymore, and dynamic registration dies when your app process is killed. Here are the most reliable solutions and workarounds specifically for call logging (incoming/outgoing calls) on Oreo and above:
First off, not all static broadcasts are blocked in Oreo! For call-related events, two key system broadcasts are still permitted for static registration:
android.intent.action.PHONE_STATE(triggers on incoming call state changes: ringing, off-hook, idle)android.intent.action.NEW_OUTGOING_CALL(triggers when an outgoing call starts)
If you tried this before and it didn't work, make sure you've got all the pieces right:
- Declare required permissions in your manifest:
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" /> <!-- For Android 10+, add READ_CALL_LOG if you need call details --> <uses-permission android:name="android.permission.READ_CALL_LOG" /> - Register the receiver correctly in the manifest, with the exact intent filters:
<receiver android:name=".CallLogReceiver" android:exported="true"> <intent-filter> <action android:name="android.intent.action.PHONE_STATE" /> <action android:name="android.intent.action.NEW_OUTGOING_CALL" /> </intent-filter> </receiver> - Note: On Android 6.0+, you need to request
READ_PHONE_STATEandREAD_CALL_LOGdynamically at runtime, since they're dangerous permissions.
If static broadcasts still don't cut it (or you need more granular call data), AccessibilityService is a solid option. It's a system-level service that can monitor device-wide events, including call state changes—and it keeps running even if your app's main process is killed.
Here's the gist of how to set it up:
- Create a class extending
AccessibilityService:public class CallAccessibilityService extends AccessibilityService { @Override public void onAccessibilityEvent(AccessibilityEvent event) { if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { // Check if the window belongs to the system phone app String packageName = event.getPackageName().toString(); if (packageName.equals("com.android.phone") || packageName.equals("com.google.android.dialer")) { // Extract call state or details here TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); int state = tm.getCallState(); switch(state) { case TelephonyManager.CALL_STATE_RINGING: // Handle incoming call break; case TelephonyManager.CALL_STATE_OFFHOOK: // Handle active call (incoming or outgoing) break; case TelephonyManager.CALL_STATE_IDLE: // Handle call ended break; } } } } @Override public void onInterrupt() {} } - Register it in your manifest with the required meta-data:
<service android:name=".CallAccessibilityService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service> - Create the
accessibility_service_config.xmlinres/xml/to define what events you want to listen to:<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeWindowStateChanged" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagDefault" android:canRetrieveWindowContent="true" android:description="@string/accessibility_service_description" /> - Important: Users have to manually enable your app's accessibility service in Settings > Accessibility—you'll need to guide them through this in your app, since it's a system-level permission.
If you don't want to rely on accessibility permissions, using a foreground service paired with telephony listeners is another way to keep your call monitoring alive in the background.
For Android 12+ (API 31):
Use TelephonyManager.registerTelephonyCallback() which replaces the older PhoneStateListener:
- Create a foreground service, then register the callback inside it:
public class CallMonitoringService extends ForegroundService { private TelephonyManager telephonyManager; private TelephonyCallback telephonyCallback; @Override public void onCreate() { super.onCreate(); // Start foreground service with a notification startForeground(1, createNotification()); telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); telephonyCallback = new TelephonyCallback() { @Override public void onCallStateChanged(int state) { super.onCallStateChanged(state); // Handle call state changes here (same as before) } }; // Register the callback (needs READ_PHONE_STATE permission) telephonyManager.registerTelephonyCallback(getMainExecutor(), telephonyCallback); } @Override public void onDestroy() { super.onDestroy(); if (telephonyCallback != null) { telephonyManager.unregisterTelephonyCallback(telephonyCallback); } } }
For Android 11 and below:
Use PhoneStateListener instead:
public class CallMonitoringService extends ForegroundService { private TelephonyManager telephonyManager; private PhoneStateListener phoneStateListener; @Override public void onCreate() { super.onCreate(); startForeground(1, createNotification()); telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); phoneStateListener = new PhoneStateListener() { @Override public void onCallStateChanged(int state, String phoneNumber) { super.onCallStateChanged(state, phoneNumber); // Handle call state and number here } }; telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); } @Override public void onDestroy() { super.onDestroy(); telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE); } }
- Note: Foreground services require a persistent notification (you can make it low-priority to be less intrusive), and you'll need the
FOREGROUND_SERVICEpermission (plusREAD_PHONE_STATE/READ_CALL_LOG). While this is more reliable than dynamic registration alone, the service can still be killed by the system under extreme resource pressure—you can add logic inonTaskRemoved()to restart the service if needed.
If your app needs more control over calls (like showing custom UI during calls), InCallService is the official system service for call management. It can listen to call lifecycle events even in the background, provided your app is granted the necessary permissions and is set as a default call handler (or at least has call access).
This is more complex, but it's the most robust option if you're building a full-featured call-related app. You'll need to:
- Declare the
BIND_INCALL_SERVICEpermission in your manifest - Register the service with the
android.telecom.InCallServiceintent filter - Handle callbacks like
onCallAdded()andonCallRemoved()to track incoming/outgoing calls
- Permissions are critical: Always test permission flows, especially on Android 10+ where call log access has stricter restrictions.
- Battery optimization: Some devices have aggressive battery savers that can kill foreground services—you might need to guide users to exclude your app from battery optimization.
内容的提问来源于stack exchange,提问作者Jayanth

