package com.hdl.hdllinphonesdk.core.service;
|
|
import android.app.AlarmManager;
|
import android.app.Notification;
|
import android.app.NotificationChannel;
|
import android.app.NotificationManager;
|
import android.app.PendingIntent;
|
import android.app.Service;
|
import android.content.ComponentName;
|
import android.content.Context;
|
import android.content.Intent;
|
import android.content.ServiceConnection;
|
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageManager;
|
import android.os.Binder;
|
import android.os.Build;
|
import android.os.Handler;
|
import android.os.IBinder;
|
import android.os.SystemClock;
|
import android.support.v4.app.NotificationCompat;
|
import android.telecom.ConnectionService;
|
|
|
import com.google.gson.Gson;
|
import com.hdl.hdllinphonesdk.R;
|
import com.hdl.hdllinphonesdk.activity.HDLLinphoneIntercomActivity;
|
import com.hdl.hdllinphonesdk.activity.HDLLinphoneReverseCallActivity;
|
import com.hdl.hdllinphonesdk.core.callback.PhoneCallback;
|
import com.hdl.hdllinphonesdk.core.callback.RegistrationCallback;
|
import com.hdl.hdllinphonesdk.core.linphone.KeepAliveHandler;
|
import com.hdl.hdllinphonesdk.utils.HDLLog;
|
|
import org.linphone.core.Call;
|
import org.linphone.core.Core;
|
import org.linphone.core.CoreListenerStub;
|
import org.linphone.core.Factory;
|
import org.linphone.core.LogCollectionState;
|
import org.linphone.core.PayloadType;
|
import org.linphone.core.ProxyConfig;
|
import org.linphone.core.RegistrationState;
|
import org.linphone.core.ToneID;
|
import org.linphone.core.VideoActivationPolicy;
|
import org.linphone.core.VideoDefinition;
|
import org.linphone.core.tools.Log;
|
import org.linphone.mediastream.Version;
|
|
import java.io.File;
|
import java.io.FileOutputStream;
|
import java.io.IOException;
|
import java.io.InputStream;
|
import java.util.Timer;
|
import java.util.TimerTask;
|
|
import static com.hdl.hdllinphonesdk.HDLLinphoneKit.HDLLinphoneKitNAME;
|
|
public class HDLLinphoneService extends Service {
|
private static final String START_LINPHONE_LOGS = " ==== HDLLinphoneService ====";
|
// Keep a static reference to the Service so we can access it from anywhere in the app
|
private volatile static HDLLinphoneService sInstance;
|
|
private PendingIntent mKeepAlivePendingIntent;
|
private static PhoneCallback sPhoneCallback;//通话状态回调
|
private static RegistrationCallback sRegistrationCallback;//账号注册登录状态回调
|
private String mRingSoundFile, mPauseSoundFile = null;
|
|
private Handler mHandler;
|
private Timer mTimer;
|
|
private Core mCore;
|
private CoreListenerStub mCoreListener;
|
private Call.State currentCallState;//当前通话状态
|
|
public static boolean isReady() {
|
return sInstance != null;
|
}
|
|
public static HDLLinphoneService getInstance() {
|
return sInstance;
|
}
|
|
public static Boolean getCoreIsNotNull() {
|
return sInstance.mCore != null;
|
}
|
|
public static Core getCore() {
|
if (sInstance == null) {
|
return null;
|
}
|
return sInstance.mCore;
|
}
|
|
public class LinphoneBinder extends Binder {
|
|
public HDLLinphoneService getService() {
|
return HDLLinphoneService.this;
|
}
|
}
|
|
private final IBinder binder = new LinphoneBinder();
|
|
@Override
|
public IBinder onBind(Intent intent) {
|
return binder;
|
}
|
|
/**
|
* 添加通话状态回调
|
*
|
* @param phoneCallback
|
*/
|
public static void addPhoneCallback(PhoneCallback phoneCallback) {
|
sPhoneCallback = phoneCallback;
|
}
|
|
public static void removePhoneCallback() {
|
if (sPhoneCallback != null) {
|
sPhoneCallback = null;
|
}
|
}
|
|
/**
|
* 添加注册状态回调
|
*
|
* @param registrationCallback
|
*/
|
public static void addRegistrationCallback(RegistrationCallback registrationCallback) {
|
sRegistrationCallback = registrationCallback;
|
}
|
|
public static void removeRegistrationCallback() {
|
if (sRegistrationCallback != null) {
|
sRegistrationCallback = null;
|
}
|
}
|
|
/**
|
* 移除所以Callback
|
*/
|
public void removeAllCallback() {
|
removePhoneCallback();
|
removeRegistrationCallback();
|
}
|
|
@Override
|
public void onCreate() {
|
super.onCreate();
|
hdlStartForeground();
|
initLinphone();
|
Intent intent = new Intent(this, KeepAliveHandler.class);
|
mKeepAlivePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
((AlarmManager) this.getSystemService(Context.ALARM_SERVICE)).setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
SystemClock.elapsedRealtime() + 60000, 60000, mKeepAlivePendingIntent);
|
}
|
|
@Override
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
super.onStartCommand(intent, flags, startId);
|
/* if (sInstance != null) {
|
return START_STICKY;
|
}*/
|
return START_STICKY;
|
}
|
|
|
public void initCore() {
|
|
// If our Service is already running, no need to continue
|
|
|
// Our Service has been started, we can keep our reference on it
|
// From now one the Launcher will be able to call onServiceReady()
|
sInstance = this;
|
|
// Core must be started after being created and configured
|
mCore.start();
|
mCore.enableKeepAlive(true);
|
// We also MUST call the iterate() method of the Core on a regular basis
|
TimerTask lTask =
|
new TimerTask() {
|
@Override
|
public void run() {
|
mHandler.post(() -> {
|
if (mCore != null) {
|
mCore.iterate();
|
}
|
});
|
}
|
};
|
mTimer = new Timer("Linphone scheduler");
|
mTimer.schedule(lTask, 0, 20);
|
|
}
|
|
@Override
|
public void onDestroy() {
|
removeAllCallback();
|
logout();
|
|
if (mTimer != null) {
|
mTimer.cancel();
|
}
|
|
if (mCore != null) {
|
mCore.removeListener(mCoreListener);
|
mCore.stop();
|
// A stopped Core can be started again
|
// To ensure resources are freed, we must ensure it will be garbage collected
|
mCore = null;
|
}
|
// Don't forget to free the singleton as well
|
sInstance = null;
|
|
((AlarmManager) this.getSystemService(Context.ALARM_SERVICE)).cancel(mKeepAlivePendingIntent);
|
|
hdlStopForeground();
|
super.onDestroy();
|
}
|
|
@Override
|
public void onTaskRemoved(Intent rootIntent) {
|
// For this sample we will kill the Service at the same time we kill the app
|
stopSelf();
|
|
super.onTaskRemoved(rootIntent);
|
}
|
|
/**
|
* 初始化Linphone
|
*/
|
private void initLinphone() {
|
currentCallState = Call.State.Idle;
|
String basePath = getFilesDir().getAbsolutePath();
|
Factory.instance().setLogCollectionPath(basePath);
|
Factory.instance().enableLogCollection(LogCollectionState.Enabled);
|
Factory.instance().setDebugMode(true, "HDLLinphone");
|
// Dump some useful information about the device we're running on
|
// HDLLog.I(START_LINPHONE_LOGS);
|
// dumpDeviceInformation();
|
// dumpInstalledLinphoneInformation();
|
mHandler = new Handler();
|
// This will be our main Core listener, it will change activities depending on events
|
initCoreListener();
|
copyAssetsFromPackage(basePath);
|
// Create the Core and add our listener
|
mCore = Factory.instance().createCore(basePath + "/.linphonerc",
|
basePath + "/linphonerc", this);
|
mCore.addListener(mCoreListener);
|
// Core is ready to be configured
|
configureCore();
|
}
|
|
/**
|
* 初始化CoreListener监听
|
*/
|
private void initCoreListener() {
|
mCoreListener = new CoreListenerStub() {
|
@Override
|
public void onCallStateChanged(Core core, Call linphoneCall, Call.State state, String message) {
|
HDLLog.e(START_LINPHONE_LOGS, "callState: " + state.toString());
|
HDLLog.e(START_LINPHONE_LOGS, "state.toInt()=" + state.toInt() + "==============message=========: " + message);
|
currentCallState = state;
|
if (sPhoneCallback != null) {
|
if (state == Call.State.IncomingReceived) {
|
sPhoneCallback.incomingCall(linphoneCall);
|
} else if (state == Call.State.OutgoingInit) {
|
sPhoneCallback.outgoingInit();
|
} else if (state == Call.State.Connected) {
|
sPhoneCallback.callConnected();
|
} else if (state == Call.State.Error) {
|
sPhoneCallback.error(message);
|
} else if (state == Call.State.End) {
|
sPhoneCallback.callEnd();
|
} else if (state == Call.State.Released) {
|
sPhoneCallback.callReleased();
|
}
|
}
|
}
|
|
@Override
|
public void onRegistrationStateChanged(Core core, ProxyConfig proxyConfig, RegistrationState registrationState, String message) {
|
String state = registrationState.toString();
|
HDLLog.i(START_LINPHONE_LOGS, "registrationState: " + state + ",message:" + message);
|
|
HDLLog.i(START_LINPHONE_LOGS, "Registration info=" + new Gson().toJson(proxyConfig));
|
if (state.equals(RegistrationState.None.toString())) {
|
if (sRegistrationCallback != null) {
|
sRegistrationCallback.registrationNone();
|
}
|
} else if (state.equals(RegistrationState.Progress.toString())) {
|
if (sRegistrationCallback != null) {
|
sRegistrationCallback.registrationProgress();
|
}
|
} else if (state.equals(RegistrationState.Ok.toString())) {
|
if (sRegistrationCallback != null) {
|
sRegistrationCallback.registrationOk();
|
}
|
} else if (state.equals(RegistrationState.Cleared.toString())) {
|
removeProxyConfig(core, proxyConfig);
|
if (sRegistrationCallback != null) {
|
sRegistrationCallback.registrationCleared();
|
}
|
} else if (state.equals(RegistrationState.Failed.toString())) {
|
if (sRegistrationCallback != null) {
|
sRegistrationCallback.registrationFailed();
|
}
|
}
|
}
|
};
|
}
|
|
/**
|
* 收到注销成功后移除当前代理
|
*
|
* @param core
|
* @param proxyConfig
|
*/
|
void removeProxyConfig(Core core, ProxyConfig proxyConfig) {
|
try {
|
HDLLog.i(START_LINPHONE_LOGS, "registrationState: 注销成功移除账号:" + proxyConfig.getIdentityAddress().getUsername());
|
//收到注销成功后移除当前代理
|
if (core != null) {
|
core.removeProxyConfig(proxyConfig);
|
}
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
}
|
|
/**
|
* mCore启动默认参数配置
|
*/
|
private void configureCore() {
|
// We will create a directory for user signed certificates if needed
|
String basePath = getFilesDir().getAbsolutePath();
|
String userCerts = basePath + "/user-certs";
|
File f = new File(userCerts);
|
if (!f.exists()) {
|
if (!f.mkdir()) {
|
HDLLog.E(userCerts + " can't be created.");
|
}
|
}
|
|
//1.设置存储用户x509证书的目录的路径
|
//mCore.setUserCertificatesPath(userCerts);
|
mCore.setUserCertificatesPath(basePath);
|
//2.设置UserAgent
|
setUserAgent();
|
//3.设置播放铃声
|
mCore.setNativeRingingEnabled(true);
|
mCore.setRemoteRingbackTone(mRingSoundFile);
|
mCore.setTone(ToneID.CallWaiting, mRingSoundFile);
|
mCore.setRing(mRingSoundFile);
|
mCore.setPlayFile(mPauseSoundFile);
|
//4.禁用手机摄像头视频采集
|
// mCore.enableVideoCapture(false);//禁用手机摄像头视频采集
|
// mCore.setNetworkReachable(true);
|
//5.回声消除
|
mCore.enableEchoCancellation(true);
|
// mCore.enableEchoLimiter(false);
|
mCore.enableMic(true);//启用麦克风
|
//6.自适应码率控制
|
mCore.enableAdaptiveRateControl(true);
|
//7.audio 码率设置
|
mCore.getConfig().setInt("audio", "codec_bitrate_limit", 36);
|
//8.视频相关设置
|
mCore.setVideoPreset("custom");//视频预设为custom
|
mCore.setPreferredFramerate(5);//FPS优先设置为5
|
VideoDefinition preferredVideoDefinition = Factory.instance().createVideoDefinitionFromName("qvga");
|
mCore.setPreferredVideoDefinition(preferredVideoDefinition);//视频尺寸偏好默认qvga
|
//9.设置带宽限制,带宽单位为kbits/s, 0表示无限
|
// mCore.setUploadBandwidth(1536);
|
// mCore.setDownloadBandwidth(1536);
|
//10.设置视频的默认策略
|
VideoActivationPolicy vap = mCore.getVideoActivationPolicy();
|
vap.setAutomaticallyInitiate(true);
|
mCore.setVideoActivationPolicy(vap);
|
//11.设置编码格式
|
setCodecMime();
|
|
// mCore.setRing(mRingSoundFile);
|
|
|
// 全视通sip设置ca证书
|
//mCore.setRootCa(basePath + "/rootca.pem");
|
}
|
|
/**
|
* 设置编码格式
|
*/
|
private void setCodecMime() {
|
//1.启用PCMA PCMU 8000Hz
|
PayloadType[] ptList = mCore.getAudioPayloadTypes();
|
for (PayloadType pt : ptList) {
|
// HDLLog.i("payloadaudio", pt.getMimeType());
|
if ((pt.getMimeType().equals("PCMA") || pt.getMimeType().equals("PCMU")) && pt.getClockRate() == 8000) {
|
pt.enable(true);
|
} else {
|
pt.enable(false);
|
}
|
}
|
mCore.setAudioPayloadTypes(ptList);
|
//2.设置VP8、H264、H265 等启用
|
PayloadType[] ptVideoList = mCore.getVideoPayloadTypes();
|
for (PayloadType pt : ptVideoList) {
|
// HDLLog.i("VideoPayload", pt.getMimeType());
|
pt.enable(true);
|
}
|
mCore.setVideoPayloadTypes(ptVideoList);
|
}
|
|
/**
|
* 设置是否启用铃声,目前发现设置无效待分析原因
|
*
|
* @param use
|
*/
|
public void enableDeviceRingtone(boolean use) {
|
if (use) {
|
mCore.setRing(null);
|
} else {
|
mCore.setRing(mRingSoundFile);
|
}
|
}
|
|
/**
|
* 设置UserAgent
|
*/
|
private void setUserAgent() {
|
try {
|
String versionName = this.getPackageManager().getPackageInfo(this.getPackageName(),
|
0).versionName;
|
if (versionName == null) {
|
versionName = String.valueOf(this.getPackageManager().getPackageInfo(this.getPackageName(), 0).versionCode);
|
}
|
mCore.setUserAgent(HDLLinphoneKitNAME, versionName);
|
} catch (PackageManager.NameNotFoundException e) {
|
e.printStackTrace();
|
}
|
}
|
|
/**
|
* 打印相关信息
|
*/
|
private void dumpDeviceInformation() {
|
StringBuilder sb = new StringBuilder();
|
sb.append("DEVICE=").append(Build.DEVICE).append("\n");
|
sb.append("MODEL=").append(Build.MODEL).append("\n");
|
sb.append("MANUFACTURER=").append(Build.MANUFACTURER).append("\n");
|
sb.append("SDK=").append(Build.VERSION.SDK_INT).append("\n");
|
sb.append("Supported ABIs=");
|
for (String abi : Version.getCpuAbis()) {
|
sb.append(abi).append(", ");
|
}
|
sb.append("\n");
|
HDLLog.I(sb.toString());
|
}
|
|
/**
|
* 打印版本号信息
|
*/
|
private void dumpInstalledLinphoneInformation() {
|
PackageInfo info = null;
|
try {
|
info = getPackageManager().getPackageInfo(getPackageName(), 0);
|
} catch (PackageManager.NameNotFoundException nnfe) {
|
Log.e(nnfe);
|
}
|
|
if (info != null) {
|
HDLLog.i(
|
"[Service] Linphone version is ",
|
info.versionName + " (" + info.versionCode + ")");
|
} else {
|
HDLLog.I("[Service] Linphone version is unknown");
|
}
|
}
|
|
/**
|
* copyAssetsFromPackage
|
*/
|
private void copyAssetsFromPackage(String basePath) {
|
try {
|
// Let's copy some RAW resources to the device
|
// The default config file must only be installed once (the first time)
|
copyIfNotExist(R.raw.linphonerc_default, basePath + "/.linphonerc");
|
// The factory config is used to override any other setting, let's copy it each time
|
copyFromPackage(R.raw.linphonerc_factory, "linphonerc");
|
mRingSoundFile = basePath + "/oldphone_mono.wav";
|
copyIfNotExist(R.raw.oldphone_mono, mRingSoundFile);
|
// copyIfNotExist(R.raw.ringback, basePath + "/ringback.wav");
|
// mPauseSoundFile = basePath + "/toy_mono.wav";
|
// copyIfNotExist(R.raw.toy_mono, basePath + "/toy_mono.wav");
|
|
// String mRootca = basePath + "/rootca.pem"; // 2022-05-23 对接全视通可视对讲添加的证书
|
// copyIfNotExist(R.raw.freeview_rootca, mRootca);
|
|
String mLPConfigXsd = basePath + "/lpconfig.xsd";
|
copyIfNotExist(R.raw.lpconfig, mLPConfigXsd);
|
|
} catch (IOException ioe) {
|
Log.e(ioe);
|
}
|
|
}
|
|
public void pddd (){
|
// RingtoneManager.GetDefaultUri
|
}
|
|
/**
|
* copyIfNotExist
|
*
|
* @param ressourceId
|
* @param target
|
* @throws IOException
|
*/
|
private void copyIfNotExist(int ressourceId, String target) throws IOException {
|
File lFileToCopy = new File(target);
|
if (!lFileToCopy.exists()) {
|
copyFromPackage(ressourceId, lFileToCopy.getName());
|
}
|
}
|
|
/**
|
* copyFromPackage
|
*
|
* @param ressourceId
|
* @param target
|
* @throws IOException
|
*/
|
private void copyFromPackage(int ressourceId, String target) throws IOException {
|
FileOutputStream lOutputStream = openFileOutput(target, 0);
|
InputStream lInputStream = getResources().openRawResource(ressourceId);
|
int readByte;
|
byte[] buff = new byte[8048];
|
while ((readByte = lInputStream.read(buff)) != -1) {
|
lOutputStream.write(buff, 0, readByte);
|
}
|
lOutputStream.flush();
|
lOutputStream.close();
|
lInputStream.close();
|
}
|
|
/**
|
* 获取当前通话状态
|
*/
|
public Call.State getCurrentCallState() {
|
return currentCallState;
|
}
|
|
/**
|
* 注销所有账号
|
*/
|
void logout() {
|
try {
|
if (mCore != null) {
|
ProxyConfig[] configs = mCore.getProxyConfigList();
|
for (ProxyConfig config : configs) {
|
if (config != null) {
|
config.edit();
|
config.enableRegister(false);
|
config.done();
|
}
|
}
|
HDLLog.E("注销所有账号");
|
// core.clearAllAuthInfo();
|
// core.clearProxyConfig();
|
}
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
}
|
|
//通知ID
|
public static final int NOTIFICATION_ID = 2;
|
|
/**
|
* 解决android8.0以上无法启动服务的问题
|
*/
|
void hdlStartForeground() {
|
|
}
|
|
/**
|
* stopForeground
|
*/
|
void hdlStopForeground() {
|
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
// stopForeground(true);
|
// }
|
}
|
}
|