package com.hdl.hdllinphonesdk.core.service; 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.Build; import android.os.Handler; import android.os.IBinder; import android.os.SystemClock; import com.hdl.hdllinphonesdk.HDLLinphoneKit; 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.core.linphone.LinphoneUtils; 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 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() { return sInstance.mCore; } @Override public IBinder onBind(Intent intent) { return null; } public static void addPhoneCallback(PhoneCallback phoneCallback) { sPhoneCallback = phoneCallback; } public static void removePhoneCallback() { if (sPhoneCallback != null) { sPhoneCallback = null; } } public static void addRegistrationCallback(RegistrationCallback registrationCallback) { sRegistrationCallback = registrationCallback; } public static void removeRegistrationCallback() { if (sRegistrationCallback != null) { sRegistrationCallback = null; } } public void removeAllCallback() { removePhoneCallback(); removeRegistrationCallback(); } @Override public void onCreate() { super.onCreate(); 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 our Service is already running, no need to continue if (sInstance != null) { return START_STICKY; } // 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( new Runnable() { @Override public void run() { if (mCore != null) { mCore.iterate(); } } }); } }; mTimer = new Timer("Linphone scheduler"); mTimer.schedule(lTask, 0, 20); return START_STICKY; } @Override public void onDestroy() { removeAllCallback(); mCore.removeListener(mCoreListener); mTimer.cancel(); 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); 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() { String basePath = getFilesDir().getAbsolutePath(); Factory.instance().setLogCollectionPath(basePath); Factory.instance().enableLogCollection(LogCollectionState.Enabled); Factory.instance().setDebugMode(false, getString(R.string.app_name)); // Dump some useful information about the device we're running on Log.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(); } private void initCoreListener() { mCoreListener = new CoreListenerStub() { @Override public void onCallStateChanged(Core core, Call linphoneCall, Call.State state, String message) { Log.e(START_LINPHONE_LOGS, "callState: " + state.toString()); 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(); } 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(); Log.i(START_LINPHONE_LOGS, "registrationState: " + state); if (sRegistrationCallback != null) { if (state.equals(RegistrationState.None.toString())) { sRegistrationCallback.registrationNone(); } else if (state.equals(RegistrationState.Progress.toString())) { sRegistrationCallback.registrationProgress(); } else if (state.equals(RegistrationState.Ok.toString())) { sRegistrationCallback.registrationOk(); } else if (state.equals(RegistrationState.Cleared.toString())) { sRegistrationCallback.registrationCleared(); } else if (state.equals(RegistrationState.Failed.toString())) { sRegistrationCallback.registrationFailed(); } } } }; } 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()) { Log.e(userCerts + " can't be created."); } } setUserAgent(); mCore.setUserCertificatesPath(userCerts); // mCore.setNativeRingingEnabled(true); // // mCore.setRemoteRingbackTone(mRingSoundFile); // mCore.setTone(ToneID.CallWaiting, mRingSoundFile); mCore.setRing(mRingSoundFile); // mCore.setPlayFile(mPauseSoundFile); // mCore.enableVideoCapture(false);//禁用手机摄像头视频采集 mCore.setNetworkReachable(true); //回声消除 // boolean isEchoCancellation = (boolean) SPUtils.get(mServiceContext, "echo_cancellation", true); //回声消除 mCore.enableEchoCancellation(true); mCore.enableEchoLimiter(true); //自适应码率控制 // boolean isAdaptiveRateControl = (boolean) SPUtils.get(mServiceContext, "adaptive_rate_control", true); mCore.enableAdaptiveRateControl(true); //audio 码率设置 mCore.getConfig().setInt("audio", "codec_bitrate_limit", 36); VideoDefinition preferredVideoDefinition = Factory.instance().createVideoDefinitionFromName("qvga"); mCore.setPreferredVideoDefinition(preferredVideoDefinition); mCore.setUploadBandwidth(1536); mCore.setDownloadBandwidth(1536); mCore.getConfig().setBool("app", "open_h264_download_enabled", true); VideoActivationPolicy vap = mCore.getVideoActivationPolicy(); vap.setAutomaticallyInitiate(true); mCore.setVideoActivationPolicy(vap); // 设置编码格式 setCodecMime(); } private void setCodecMime() { PayloadType[] ptList = mCore.getAudioPayloadTypes(); for (PayloadType pt : ptList) { org.linphone.mediastream.Log.d("payloadaudio", pt.getMimeType()); if (pt.getMimeType().equals("PCMA") && pt.getClockRate() == 8000) { pt.enable(true); } else { pt.enable(false); } } mCore.setAudioPayloadTypes(ptList); PayloadType[] ptVideoList = mCore.getVideoPayloadTypes(); for (PayloadType pt : ptVideoList) { pt.enable(true); } mCore.setVideoPayloadTypes(ptVideoList); } public void enableDeviceRingtone(boolean use) { if (use) { mCore.setRing(null); } else { mCore.setRing(mRingSoundFile); } } 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"); Log.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) { Log.i( "[Service] Linphone version is ", info.versionName + " (" + info.versionCode + ")"); } else { Log.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, basePath + "/oldphone_mono.wav"); copyIfNotExist(R.raw.ringback, basePath + "/ringback.wav"); mPauseSoundFile = basePath + "/toy_mono.wav"; copyIfNotExist( R.raw.toy_mono, basePath + "/toy_mono.wav"); // LinphoneUtils.copyIfNotExist(mServiceContext, R.raw.ringback, mRingBackSoundFile); // LinphoneUtils.copyIfNotExist(mServiceContext, R.raw.toy_mono, mPauseSoundFile); // LinphoneUtils.copyIfNotExist(mServiceContext, R.raw.linphonerc_default, mLinphoneConfigFile); // LinphoneUtils.copyIfNotExist(mServiceContext, R.raw.linphonerc_factory, new File(mLinphoneFactoryConfigFile).getName()); // LinphoneUtils.copyIfNotExist(mServiceContext, R.raw.lpconfig, mLPConfigXsd); // LinphoneUtils.copyIfNotExist(mServiceContext, R.raw.rootca, mLinphoneRootCaFile); } catch (IOException ioe) { Log.e(ioe); } } private void copyIfNotExist(int ressourceId, String target) throws IOException { File lFileToCopy = new File(target); if (!lFileToCopy.exists()) { copyFromPackage(ressourceId, lFileToCopy.getName()); } } 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; } }