package com.hdl.hdllinphonesdk.core.service; import static com.hdl.hdllinphonesdk.HDLLinphoneKit.HDLLinphoneKitNAME; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; 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 com.google.gson.Gson; import com.hdl.hdllinphonesdk.R; 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.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; 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); if (android.os.Build.VERSION.SDK_INT >= 31) { mKeepAlivePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_IMMUTABLE); } else { mKeepAlivePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_ONE_SHOT); } ((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(); // 全视通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()); if ("VP8".equals(pt.getMimeType())){ pt.enable(false); }else { 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); } } /** * 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); // } } }