Recently I have been creating a new Android app to make/receive VoIP calls using Linphone lib. The first version of the app worked almost ok, I updated the lib and decided to do some code arrange, and for some reason, the app does not longer receive calls.
All the VoIP functionality is inside an android service:
package com.test.voice.linphone;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
import android.util.Log;
import com.test.voice.voice.R;
import org.linphone.core.AVPFMode;
import org.linphone.core.Address;
import org.linphone.core.AuthInfo;
import org.linphone.core.AuthMethod;
import org.linphone.core.Call;
import org.linphone.core.CallLog;
import org.linphone.core.CallStats;
import org.linphone.core.ChatMessage;
import org.linphone.core.ChatRoom;
import org.linphone.core.ConfiguringState;
import org.linphone.core.Content;
import org.linphone.core.Core;
import org.linphone.core.CoreException;
import org.linphone.core.CoreListener;
import org.linphone.core.EcCalibratorStatus;
import org.linphone.core.Event;
import org.linphone.core.Factory;
import org.linphone.core.Friend;
import org.linphone.core.FriendList;
import org.linphone.core.GlobalState;
import org.linphone.core.InfoMessage;
import org.linphone.core.PayloadType;
import org.linphone.core.PresenceModel;
import org.linphone.core.ProxyConfig;
import org.linphone.core.PublishState;
import org.linphone.core.Reason;
import org.linphone.core.RegistrationState;
import org.linphone.core.SubscriptionState;
import org.linphone.core.Transports;
import org.linphone.core.VersionUpdateCheckResult;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Timer;
import java.util.TimerTask;
public class LinphoneService extends Service {
// *************************************** CONSTANTS *************************************** //
private static final String TAG = "linphone_service";
private static final String WAKE_LOG_TAG = ":voicewakelock";
private static final String LIN_TAG = "libcore";
private static final String USER_AGENT = "VoiceAgent";
private static final long TIME_ITERATE = 200;
// ****************************************** VARS ***************************************** //
private final IBinder mBinder = new LocalBinder();
private PowerManager.WakeLock mWakeLock;
private Core mCore;
private String mLpConfig = null;
private String mConfigFactoryFile = null;
public String mLinphoneConfigFile = null;
private String mRootCaFile = null;
private String mRingSoundFile = null;
private String mRingBackSoundFile = null;
private String mPauseSoundFile = null;
private AuthInfo mAuthInfo;
private ProxyConfig mProxyConfig;
private Timer mIterateTimer;
private LinphoneCallback mCallback;
private RegistrationState mRegistrationState = RegistrationState.None;
// ************************************* INNER CLASSES ************************************* //
public class LocalBinder extends Binder {
public LinphoneService getService() {
return LinphoneService.this;
}
}
// *************************************** LIFECYCLE *************************************** //
@Override
public void onCreate() {
super.onCreate();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOG_TAG);
mWakeLock.acquire();
String basePath = getFilesDir().getAbsolutePath();
mLpConfig = basePath + "/lpconfig.xsd";
mConfigFactoryFile = basePath + "/linphonerc";
mLinphoneConfigFile = basePath + "/.linphonerc";
mRootCaFile = basePath + "/rootca.pem";
mRingSoundFile = basePath + "/oldphone_mono.wav";
mRingBackSoundFile = basePath + "/ringback.wav";
mPauseSoundFile = basePath + "/toy_mono.wav";
}
@Override
public void onDestroy() {
super.onDestroy();
mWakeLock.release();
cancelIterateTimer();
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// ************************************* PUBLIC METHODS ************************************ //
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
/**
* Set a {@link LinphoneCallback} to receive event callbacks.
*
* @param callback {@link LinphoneCallback} to set.
*/
public void setCallback(LinphoneCallback callback) {
mCallback = callback;
}
/**
* Initiate Linphone library, set configuration.
*
* @throws Exception
*/
public void initLibrary() throws Exception {
copyAssetsFromPackage();
Factory.instance().setDebugMode(true, LIN_TAG);
mCore = Factory.instance().createCore(
mLinphoneConfigFile, mConfigFactoryFile, this);
mCore.addListener(getCoreListener());
mCore.enableIpv6(false);
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
mCore.setUserAgent(USER_AGENT, packageInfo.versionName);
mCore.setRemoteRingbackTone(mRingSoundFile);
mCore.setRing(mRingSoundFile);
mCore.setRootCa(mRootCaFile);
mCore.setPlayFile(mPauseSoundFile);
mCore.setNetworkReachable(true);
mCore.enableEchoCancellation(true);
mCore.enableEchoLimiter(true);
mCore.enableAdaptiveRateControl(true);
mCore.clearAllAuthInfo();
mCore.clearProxyConfig();
enablePayloads();
startIterateTimer();
}
/**
* Login agains the VoIP server.
*
* @param name Username.
* @param password Password.
* @param host Server.
* @param tcpPort Specify the TCP port to use, only TCP available.
* @throws CoreException
*/
public void login(String name, String password, String host, int tcpPort)
throws CoreException {
String identity = "sip:" + name + "@" + host;
String proxy = "sip:" + host;
Address proxyAddress = Factory.instance().createAddress(proxy);
Address identityAddress = Factory.instance().createAddress(identity);
if (proxyAddress == null || identityAddress == null) {
throw new CoreException("Proxy or Identity address is null.");
}
mProxyConfig = mCore.createProxyConfig();
mProxyConfig.setIdentityAddress(identityAddress);
mProxyConfig.setServerAddr(proxyAddress.asStringUriOnly());
mProxyConfig.setAvpfMode(AVPFMode.Disabled);
mProxyConfig.setAvpfRrInterval(0);
mProxyConfig.enableQualityReporting(false);
mProxyConfig.setQualityReportingCollector(null);
mProxyConfig.setQualityReportingInterval(0);
// mProxyConfig.setRoute(proxyAddress.asStringUriOnly());
mProxyConfig.enableRegister(true);
mAuthInfo = Factory.instance().createAuthInfo(
name, null, password, null, null, host);
Transports transports = mCore.getTransports();
transports.setUdpPort(-1);
transports.setTlsPort(-1);
transports.setTcpPort(tcpPort);
mCore.setTransports(transports);
mCore.addProxyConfig(mProxyConfig);
mCore.addAuthInfo(mAuthInfo);
mCore.setDefaultProxyConfig(mProxyConfig);
}
/**
* Disconnect from remote VoIP server.
*/
public void logout() {
if (mProxyConfig != null) {
mProxyConfig.edit();
mProxyConfig.enableRegister(false);
mProxyConfig.done();
}
}
/**
* Accept incoming call.
*/
public void acceptCall() {
mCore.acceptCall(mCore.getCurrentCall());
}
/**
* Decline current call.
*/
public void declineCall() {
mCore.declineCall(mCore.getCurrentCall(), Reason.Declined);
}
/**
* Hang up the current call.
*/
public void hangUp() {
Call currentCall = mCore.getCurrentCall();
if (currentCall != null) {
mCore.terminateCall(currentCall);
} else if (mCore.isInConference()) {
mCore.terminateConference();
} else {
mCore.terminateAllCalls();
}
}
// ************************************ PRIVATE METHODS ************************************ //
/**
* Copy resource files.
*
* @throws IOException If an I/O error occurrs.
*/
private void copyAssetsFromPackage() throws IOException {
copyIfNotExist(this, R.raw.oldphone_mono, mRingSoundFile);
copyIfNotExist(this, R.raw.ringback, mRingBackSoundFile);
copyIfNotExist(this, R.raw.toy_mono, mPauseSoundFile);
copyIfNotExist(this, R.raw.linphonerc_default, mLinphoneConfigFile);
copyIfNotExist(this, R.raw.linphonerc_factory, (
new File(this.mConfigFactoryFile)).getName());
copyIfNotExist(this, R.raw.lpconfig, mLpConfig);
copyIfNotExist(this, R.raw.rootca, mRootCaFile);
}
private void copyIfNotExist(Context context, int resourceId, String target) throws IOException {
File fileToCopy = new File(target);
if (!fileToCopy.exists()) {
copyFromPackage(context, resourceId, fileToCopy.getName());
}
}
private void copyFromPackage(Context context, int resourceId, String target) throws
IOException {
FileOutputStream outputStream = context.openFileOutput(target, 0);
InputStream inputStream = context.getResources().openRawResource(resourceId);
byte[] buff = new byte[8048];
int readByte;
while ((readByte = inputStream.read(buff)) != -1) {
outputStream.write(buff, 0, readByte);
}
outputStream.flush();
outputStream.close();
inputStream.close();
}
/**
* Get the {@link CoreListener}.
*
* @return Instance of {@link CoreListener}.
*/
private CoreListener getCoreListener() {
return new CoreListener() {
@Override
public void onGlobalStateChanged(Core core, GlobalState globalState, String s) {
Log.d(TAG, "Core listener - Global State Changed: " + s);
mCallback.onGlobalStateChanged(globalState);
}
@Override
public void onRegistrationStateChanged(Core core, ProxyConfig proxyConfig,
RegistrationState registrationState,
String state) {
Log.d(TAG, "Core listener - On Registration State Changed: " +
state);
if (registrationState != mRegistrationState) {
mCallback.onRegistrationStateChanged(registrationState);
}
mRegistrationState = registrationState;
}
@Override
public void onCallStateChanged(Core core, Call call, Call.State state, String s) {
Log.d(TAG, "Core listener - On Call State Changed");
mCallback.onCallStateChanged(call, state);
}
@Override
public void onNotifyPresenceReceived(Core core, Friend friend) {
Log.d(TAG, "Core listener - On Notify Presence Received");
}
@Override
public void onNotifyPresenceReceivedForUriOrTel(Core core, Friend friend, String s,
PresenceModel presenceModel) {
Log.d(TAG, "Core listener - On Notify Presence Received For Uri Or Tel");
}
@Override
public void onNewSubscriptionRequested(Core core, Friend friend, String s) {
Log.d(TAG, "Core listener - On New Subscription Requested");
}
@Override
public void onAuthenticationRequested(Core core, AuthInfo authInfo, AuthMethod
authMethod) {
Log.d(TAG, "Core listener - On Authentication Requested");
}
@Override
public void onCallLogUpdated(Core core, CallLog callLog) {
Log.d(TAG, "Core listener - On Call Log Updated");
}
@Override
public void onMessageReceived(Core core, ChatRoom chatRoom, ChatMessage chatMessage) {
Log.d(TAG, "Core listener - On Message Received");
}
@Override
public void onMessageReceivedUnableDecrypt(Core core, ChatRoom chatRoom, ChatMessage
chatMessage) {
Log.d(TAG, "Core listener - On Message Received Unable Decrypt");
}
@Override
public void onIsComposingReceived(Core core, ChatRoom chatRoom) {
Log.d(TAG, "Core listener - On Is Composing Received");
}
@Override
public void onDtmfReceived(Core core, Call call, int i) {
Log.d(TAG, "Core listener - On Dtmf Received");
}
@Override
public void onReferReceived(Core core, String s) {
Log.d(TAG, "Core listener - On Refer Received");
}
@Override
public void onCallEncryptionChanged(Core core, Call call, boolean b, String s) {
Log.d(TAG, "Core listener - On Call Encrypted Changed");
}
@Override
public void onTransferStateChanged(Core core, Call call, Call.State state) {
Log.d(TAG, "Core listener - On Transfer State Changed");
}
@Override
public void onBuddyInfoUpdated(Core core, Friend friend) {
Log.d(TAG, "Core listener - On Buddy Info Updated");
}
@Override
public void onCallStatsUpdated(Core core, Call call, CallStats callStats) {
Log.d(TAG, "Core listener - On Call Stats Updated");
}
@Override
public void onInfoReceived(Core core, Call call, InfoMessage infoMessage) {
Log.d(TAG, "Core listener - On Info Received");
}
@Override
public void onSubscriptionStateChanged(Core core, Event event, SubscriptionState
subscriptionState) {
Log.d(TAG, "Core listener - On Subscription State Changed");
}
@Override
public void onNotifyReceived(Core core, Event event, String s, Content content) {
Log.d(TAG, "Core listener - On Notify Received");
}
@Override
public void onSubscribeReceived(Core core, Event event, String s, Content content) {
Log.d(TAG, "Core listener - On Subscribe Received");
}
@Override
public void onPublishStateChanged(Core core, Event event, PublishState publishState) {
Log.d(TAG, "Core listener - On Publish State Changed");
}
@Override
public void onConfiguringStatus(Core core, ConfiguringState configuringState, String
s) {
Log.d(TAG, "Core listener - On Configuring Status");
}
@Override
public void onNetworkReachable(Core core, boolean b) {
Log.d(TAG, "Core listener - On Network Reachable");
}
@Override
public void onLogCollectionUploadStateChanged(Core core, Core
.LogCollectionUploadState logCollectionUploadState, String s) {
Log.d(TAG, "Core listener - On Log Collection Upload StateChanged");
}
@Override
public void onLogCollectionUploadProgressIndication(Core core, int i, int i1) {
Log.d(TAG, "Core listener - On Log Collection Upload ProgressIndication");
}
@Override
public void onFriendListCreated(Core core, FriendList friendList) {
Log.d(TAG, "Core listener - On Friend List Created");
}
@Override
public void onFriendListRemoved(Core core, FriendList friendList) {
Log.d(TAG, "Core listener - On Friend List Removed");
}
@Override
public void onCallCreated(Core core, Call call) {
Log.d(TAG, "Core listener - On Call Created");
}
@Override
public void onVersionUpdateCheckResultReceived(Core core, VersionUpdateCheckResult
versionUpdateCheckResult, String s, String s1) {
Log.d(TAG, "Core listener - On Version Update Check ResultReceived");
}
@Override
public void onChatRoomStateChanged(Core core, ChatRoom chatRoom, ChatRoom.State state) {
Log.d(TAG, "Core listener - On Chat Room State Changed");
}
@Override
public void onQrcodeFound(Core core, String s) {
Log.d(TAG, "Core listener - On QR Code Found");
}
@Override
public void onEcCalibrationResult(Core core, EcCalibratorStatus ecCalibratorStatus,
int i) {
Log.d(TAG, "Core listener - On EC Calibration Result");
}
@Override
public void onEcCalibrationAudioInit(Core core) {
Log.d(TAG, "Core listener - On EC Calibration Audio Init");
}
@Override
public void onEcCalibrationAudioUninit(Core core) {
Log.d(TAG, "Core listener - On EC Calibration Audio Uninit");
}
};
}
private void startIterateTimer() {
cancelIterateTimer();
mIterateTimer = new Timer();
mIterateTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (mCore != null) mCore.iterate();
}
}, 0, TIME_ITERATE);
}
/**
* Remove iterate timer.
*/
private void cancelIterateTimer() {
if (mIterateTimer != null) mIterateTimer.cancel();
mIterateTimer = null;
}
private void enablePayloads() {
PayloadType[] audioPayloads = mCore.getAudioPayloadTypes();
for (int i = 0; i < audioPayloads.length; ++i) {
PayloadType payloadType = audioPayloads[i];
payloadType.enable(true);
}
mCore.setAudioPayloadTypes(audioPayloads);
}
}
All the configurations and audio files are ok since they were working in the first implementation, but I guess I am doing something wrong on this class.
Any help?
Finally I solved the issue, I was missing Core.start():
/**
* Initiate Linphone library, set configuration.
*
* @throws Exception
*/
public void initLibrary() throws Exception {
copyAssetsFromPackage();
Factory.instance().setDebugMode(false, LIN_TAG);
mCore = Factory.instance().createCore(
mLinphoneConfigFile, mConfigFactoryFile, this);
mCore.addListener(getCoreListener());
mCore.start();
mCore.enableIpv6(false);
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
mCore.setUserAgent(USER_AGENT, packageInfo.versionName);
mCore.setRemoteRingbackTone(mRingSoundFile);
mCore.setRing(mRingSoundFile);
mCore.setRootCa(mRootCaFile);
mCore.setPlayFile(mPauseSoundFile);
mCore.setNetworkReachable(true);
mCore.enableEchoCancellation(true);
mCore.enableEchoLimiter(true);
mCore.enableAdaptiveRateControl(true);
mCore.clearAllAuthInfo();
mCore.clearProxyConfig();
enablePayloads();
startIterateTimer();
}