Skip to content

Commit 16410d5

Browse files
committed
Start a foreground service to ba able to get audio on Android 11 bg
1 parent c3ade44 commit 16410d5

File tree

4 files changed

+56
-5
lines changed

4 files changed

+56
-5
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,13 @@ const options = {
4747
cancelButton: 'Cancel',
4848
okButton: 'ok',
4949
imageName: 'phone_account_icon',
50-
additionalPermissions: [PermissionsAndroid.PERMISSIONS.example]
50+
additionalPermissions: [PermissionsAndroid.PERMISSIONS.example],
51+
// Required to get audio in background when using Android 11
52+
foregroundService: {
53+
channelId: 'com.company.my',
54+
channelName: 'Foreground service for my app',
55+
notificationTitle: 'My app is running on background',
56+
},
5157
}
5258
};
5359

@@ -805,6 +811,12 @@ Since iOS 13, you'll have to report the incoming calls that wakes up your applic
805811
}
806812
```
807813

814+
## Android 11
815+
816+
Since Android 11, your application [requires to start a foregroundService](https://developer.android.com/about/versions/11/privacy/foreground-services) in order to access the microphone in background.
817+
818+
You have to set the `foregroundService` key in the [`setup()`](#setup) method and add a `foregroundServiceType` in the [`AndroidManifest` file](docs/android-installation.md#android-common-step-installation).
819+
808820
## Debug
809821

810822
### Android

android/src/main/java/io/wazo/callkeep/RNCallKeepModule.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ public void setup(ReadableMap options) {
128128
this.registerEvents();
129129
VoiceConnectionService.setAvailable(true);
130130
}
131+
132+
VoiceConnectionService.setSettings(options);
131133
}
132134

133135
@ReactMethod

android/src/main/java/io/wazo/callkeep/VoiceConnectionService.java

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
package io.wazo.callkeep;
1919

2020
import android.annotation.TargetApi;
21+
import android.app.ActivityManager;
22+
import android.app.ActivityManager.RunningTaskInfo;
23+
import android.app.Notification;
24+
import android.app.NotificationChannel;
25+
import android.app.NotificationManager;
2126
import android.content.Intent;
2227
import android.content.Context;
2328
import android.content.ComponentName;
@@ -27,6 +32,7 @@
2732
import android.os.Handler;
2833
import android.speech.tts.Voice;
2934
import androidx.annotation.Nullable;
35+
import android.support.v4.app.NotificationCompat;
3036
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
3137
import android.telecom.CallAudioState;
3238
import android.telecom.Connection;
@@ -37,10 +43,8 @@
3743
import android.telecom.TelecomManager;
3844
import android.util.Log;
3945

40-
import android.app.ActivityManager;
41-
import android.app.ActivityManager.RunningTaskInfo;
42-
4346
import com.facebook.react.HeadlessJsTaskService;
47+
import com.facebook.react.bridge.ReadableMap;
4448

4549
import java.util.ArrayList;
4650
import java.util.HashMap;
@@ -51,6 +55,7 @@
5155
import java.util.UUID;
5256
import java.util.stream.Collectors;
5357

58+
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
5459
import static io.wazo.callkeep.Constants.ACTION_AUDIO_SESSION;
5560
import static io.wazo.callkeep.Constants.ACTION_ONGOING_CALL;
5661
import static io.wazo.callkeep.Constants.ACTION_CHECK_REACHABILITY;
@@ -70,6 +75,7 @@ public class VoiceConnectionService extends ConnectionService {
7075
private static String notReachableCallUuid;
7176
private static ConnectionRequest currentConnectionRequest;
7277
private static PhoneAccountHandle phoneAccountHandle;
78+
private static ReadableMap _settings;
7379
private static String TAG = "RNCK:VoiceConnectionService";
7480
public static Map<String, VoiceConnection> currentConnections = new HashMap<>();
7581
public static Boolean hasOutgoingCall = false;
@@ -105,6 +111,10 @@ public static void setAvailable(Boolean value) {
105111
isAvailable = value;
106112
}
107113

114+
public static void setSettings(ReadableMap settings) {
115+
_settings = settings;
116+
}
117+
108118
public static void setCanMakeMultipleCalls(Boolean allow) {
109119
VoiceConnectionService.canMakeMultipleCalls = allow;
110120
}
@@ -133,6 +143,8 @@ public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManage
133143
incomingCallConnection.setRinging();
134144
incomingCallConnection.setInitialized();
135145

146+
startForegroundService();
147+
136148
return incomingCallConnection;
137149
}
138150

@@ -185,6 +197,8 @@ private Connection makeOutgoingCall(ConnectionRequest request, String uuid, Bool
185197
outgoingCallConnection.setAudioModeIsVoip(true);
186198
outgoingCallConnection.setCallerDisplayName(displayName, TelecomManager.PRESENTATION_ALLOWED);
187199

200+
startForegroundService();
201+
188202
// ‍️Weirdly on some Samsung phones (A50, S9...) using `setInitialized` will not display the native UI ...
189203
// when making a call from the native Phone application. The call will still be displayed correctly without it.
190204
if (!Build.MANUFACTURER.equalsIgnoreCase("Samsung")) {
@@ -201,6 +215,28 @@ private Connection makeOutgoingCall(ConnectionRequest request, String uuid, Bool
201215
return outgoingCallConnection;
202216
}
203217

218+
private void startForegroundService() {
219+
if (_settings == null || !_settings.hasKey("foregroundService")) {
220+
return;
221+
}
222+
ReadableMap foregroundSettings = _settings.getMap("foregroundService");
223+
String NOTIFICATION_CHANNEL_ID = foregroundSettings.getString("channelId");
224+
String channelName = foregroundSettings.getString("channelName");
225+
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
226+
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
227+
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
228+
assert manager != null;
229+
manager.createNotificationChannel(chan);
230+
231+
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
232+
Notification notification = notificationBuilder.setOngoing(true)
233+
.setContentTitle(foregroundSettings.getString("notificationTitle"))
234+
.setPriority(NotificationManager.IMPORTANCE_MIN)
235+
.setCategory(Notification.CATEGORY_SERVICE)
236+
.build();
237+
startForeground(FOREGROUND_SERVICE_TYPE_MICROPHONE, notification);
238+
}
239+
204240
private void wakeUpApplication(String uuid, String number, String displayName) {
205241
Intent headlessIntent = new Intent(
206242
this.getApplicationContext(),

docs/android-installation.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ public class MainActivity extends ReactActivity {
7171
// ...
7272
<service android:name="io.wazo.callkeep.VoiceConnectionService"
7373
android:label="Wazo"
74-
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
74+
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
75+
android:foregroundServiceType="camera|microphone">>
7576
<intent-filter>
7677
<action android:name="android.telecom.ConnectionService" />
7778
</intent-filter>

0 commit comments

Comments
 (0)