|
@@ -0,0 +1,510 @@
|
|
|
+package com.wdkl.app.ncs.siptext.service;
|
|
|
+
|
|
|
+import android.app.Notification;
|
|
|
+import android.app.NotificationChannel;
|
|
|
+import android.app.NotificationManager;
|
|
|
+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.Environment;
|
|
|
+import android.os.Handler;
|
|
|
+import android.os.IBinder;
|
|
|
+import android.os.RemoteCallbackList;
|
|
|
+import android.os.RemoteException;
|
|
|
+import android.text.TextUtils;
|
|
|
+import android.util.Log;
|
|
|
+import android.widget.Toast;
|
|
|
+
|
|
|
+import com.wdkl.app.ncs.siptext.IMyAidlInterface;
|
|
|
+import com.wdkl.app.ncs.siptext.IPersonChangeListener;
|
|
|
+import com.wdkl.app.ncs.siptext.R;
|
|
|
+import com.wdkl.app.ncs.siptext.Util.Base64Util;
|
|
|
+import com.wdkl.app.ncs.siptext.Util.NetHelper;
|
|
|
+import com.wdkl.app.ncs.siptext.Util.Util;
|
|
|
+import com.wdkl.app.ncs.siptext.activity.CallActivity;
|
|
|
+
|
|
|
+import androidx.annotation.Nullable;
|
|
|
+
|
|
|
+import org.linphone.core.AccountCreator;
|
|
|
+import org.linphone.core.Address;
|
|
|
+import org.linphone.core.Call;
|
|
|
+import org.linphone.core.CallParams;
|
|
|
+import org.linphone.core.Core;
|
|
|
+import org.linphone.core.CoreListenerStub;
|
|
|
+import org.linphone.core.Factory;
|
|
|
+import org.linphone.core.LogCollectionState;
|
|
|
+import org.linphone.core.ProxyConfig;
|
|
|
+import org.linphone.core.TransportType;
|
|
|
+import org.linphone.core.Transports;
|
|
|
+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 WdklSipService extends Service {
|
|
|
+ private static final String START_SIPPHONE_LOGS = " ==== Device information dump ====";
|
|
|
+
|
|
|
+ private static final String PREFER_PAYLOAD = "PUMU";
|
|
|
+ //单例化服务,以便全局调用
|
|
|
+ private static WdklSipService sInstance;
|
|
|
+
|
|
|
+ private NotificationManager notificationManager = null;
|
|
|
+ private String notificationId = "channelId0";
|
|
|
+ private String notificationName = "sip_service";
|
|
|
+
|
|
|
+ private Handler mHandler;
|
|
|
+ private Timer mTimer;
|
|
|
+
|
|
|
+ private Core mCore;
|
|
|
+ private CoreListenerStub mCoreListener;
|
|
|
+
|
|
|
+ private String state=null;
|
|
|
+
|
|
|
+ private String username="";
|
|
|
+
|
|
|
+ private String Adderss="";
|
|
|
+
|
|
|
+ public static boolean isReady() {
|
|
|
+ return sInstance != null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static WdklSipService getInstance() {
|
|
|
+ return sInstance;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Core getCore() {
|
|
|
+ return sInstance.mCore;
|
|
|
+ }
|
|
|
+
|
|
|
+ private RemoteCallbackList<IPersonChangeListener> mListenerList = new RemoteCallbackList<>();
|
|
|
+
|
|
|
+
|
|
|
+ @Nullable
|
|
|
+ @Override
|
|
|
+ public IBinder onBind(Intent intent) {
|
|
|
+// 绑定的包名
|
|
|
+// int check = checkCallingOrSelfPermission("com.example.aidlclient.ipc.aidl.ACCESS_PERSON_SERVICE");
|
|
|
+// if (check == PackageManager.PERMISSION_DENIED) {
|
|
|
+// Log.d(">>>>>>>>>>>>>>", "没有权限");
|
|
|
+// return null;
|
|
|
+// }
|
|
|
+
|
|
|
+// Log.d(">>>>>>>>>>>>>>", "有权限");
|
|
|
+ return mBinder;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 包名验证
|
|
|
+ private boolean verifyPackage(int uid) {
|
|
|
+ String packageName = null;
|
|
|
+ String[] packages = getPackageManager().getPackagesForUid(uid);
|
|
|
+ if (packages != null && packages.length > 0) {
|
|
|
+ packageName = packages[0];
|
|
|
+ }
|
|
|
+ if (!"com.example.aidlclient".equals(packageName)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onCreate() {
|
|
|
+ super.onCreate();
|
|
|
+ notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
+ NotificationChannel channel = new NotificationChannel(
|
|
|
+ notificationId,
|
|
|
+ notificationName,
|
|
|
+ NotificationManager.IMPORTANCE_HIGH
|
|
|
+ );
|
|
|
+ channel.enableVibration(true);
|
|
|
+ channel.enableLights(true);
|
|
|
+ channel.setBypassDnd(true);
|
|
|
+ channel.setShowBadge(true);
|
|
|
+ channel.setSound(null, null);
|
|
|
+ notificationManager.createNotificationChannel(channel);
|
|
|
+ }
|
|
|
+
|
|
|
+ //首次调用必须使用 Factory相关方法
|
|
|
+ //这里开户调试日志及设置路径
|
|
|
+ String basePath = getFilesDir().getAbsolutePath();
|
|
|
+ Factory.instance().setLogCollectionPath(basePath);
|
|
|
+ Factory.instance().enableLogCollection(LogCollectionState.Enabled);
|
|
|
+ Factory.instance().setDebugMode(false, getString(R.string.app_name));
|
|
|
+
|
|
|
+ //收集一些设备信息
|
|
|
+ Log.i(">>>>>>>>>>>>>>", START_SIPPHONE_LOGS);
|
|
|
+ dumpDeviceInformation();
|
|
|
+ dumpInstalledLinphoneInformation();
|
|
|
+
|
|
|
+ mHandler = new Handler();
|
|
|
+ //主监听器,根据事件调用界面
|
|
|
+ mCoreListener = new CoreListenerStub() {
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onCallStateChanged(Core core, Call call, Call.State state, String message) {
|
|
|
+ Log.i("sipCall>>>>>>>>>>>>>>","state " + state.name() + " ,message " + message +
|
|
|
+ ", remote address "+call.getRemoteAddressAsString() +
|
|
|
+ ", remote user " + call.getRemoteAddress().getUsername() +
|
|
|
+ ", remoe contact " + call.getRemoteContact()
|
|
|
+ );
|
|
|
+
|
|
|
+ if (call.getRemoteParams()!=null){
|
|
|
+ String shuju= call.getRemoteParams().getCustomHeader("X-extraData");
|
|
|
+ username = Base64Util.decodeBase64(shuju);
|
|
|
+ Log.d("sipCall", ">>>>>>>>>>>> call shuju:"+shuju);
|
|
|
+ }
|
|
|
+ onDataChange(state.name());
|
|
|
+ if (state == Call.State.IncomingReceived) {
|
|
|
+
|
|
|
+ } else if (state == Call.State.Connected) {
|
|
|
+ //录音
|
|
|
+ call.startRecording();
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ try {
|
|
|
+ //复制一些源资源
|
|
|
+ //默认配置只能在首次时安装一次
|
|
|
+ copyIfNotExist(R.raw.linphonerc_default, basePath + "/.wdkl_sip_rc");
|
|
|
+ //用户配置,每次复制
|
|
|
+ copyFromPackage(R.raw.linphonerc_factory, "wdkl_sip_rc");
|
|
|
+ } catch (IOException ioe) {
|
|
|
+ Log.e("sipCall>>>>>>>>>>>>>>",ioe.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ //创建SIP核心并加载监听器
|
|
|
+ mCore = Factory.instance()
|
|
|
+ .createCore(basePath + "/.wdkl_sip_rc", basePath + "/wdkl_sip_rc", this);
|
|
|
+ mCore.addListener(mCoreListener);
|
|
|
+ String time = Util.getSipTime(this);
|
|
|
+ mCore.setRing(time);
|
|
|
+ //SIP核心配置完成
|
|
|
+ configureCore();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int onStartCommand(Intent intent, int flags, int startId) {
|
|
|
+ super.onStartCommand(intent, flags, startId);
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
+ startForeground(11, getNotification()); //开前台服务
|
|
|
+ }
|
|
|
+ super.onStartCommand(intent, flags, startId);
|
|
|
+ //如果服务已经在运行,则返回
|
|
|
+ if (sInstance != null) {
|
|
|
+ return START_STICKY;
|
|
|
+ }
|
|
|
+
|
|
|
+ //一旦服务启动,则一直保持
|
|
|
+ sInstance = this;
|
|
|
+
|
|
|
+ //SIP核心在创建和配置完成后,开启
|
|
|
+ mCore.start();
|
|
|
+ //必须定时运行 SIP核心 iterate()
|
|
|
+ TimerTask lTask =
|
|
|
+ new TimerTask() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ mHandler.post(
|
|
|
+ new Runnable() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ if (mCore != null) {
|
|
|
+ mCore.iterate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ };
|
|
|
+ mTimer = new Timer("wdkl sip scheduler");
|
|
|
+ mTimer.schedule(lTask, 0, 20);
|
|
|
+
|
|
|
+ return START_STICKY;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onDestroy() {
|
|
|
+ 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;
|
|
|
+
|
|
|
+ super.onDestroy();
|
|
|
+ }
|
|
|
+
|
|
|
+ private Notification getNotification() {
|
|
|
+ Notification.Builder builder = new Notification.Builder(this)
|
|
|
+ .setSmallIcon(R.mipmap.ic_launcher)
|
|
|
+ .setContentTitle("sip service")
|
|
|
+ .setContentText("running...")
|
|
|
+ .setOnlyAlertOnce(true);
|
|
|
+
|
|
|
+ //设置Notification的ChannelID,否则不能正常显示
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
+ builder.setChannelId(notificationId);
|
|
|
+ }
|
|
|
+
|
|
|
+ return builder.build();
|
|
|
+ }
|
|
|
+
|
|
|
+ @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);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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("sipCall>>>>>>>>>>>>>>",userCerts + " can't be created.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ mCore.setUserCertificatesPath(userCerts);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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("sipCall>>>>>>>>>>>>>>",sb.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ private void dumpInstalledLinphoneInformation() {
|
|
|
+ PackageInfo info = null;
|
|
|
+ try {
|
|
|
+ info = getPackageManager().getPackageInfo(getPackageName(), 0);
|
|
|
+ } catch (PackageManager.NameNotFoundException nnfe) {
|
|
|
+ Log.e("sipCall>>>>>>>>>>>>>>",nnfe.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (info != null) {
|
|
|
+ Log.i(
|
|
|
+ "sipCall>>>>>>>>>>>>>>",
|
|
|
+ info.versionName + " (" + info.versionCode + ")");
|
|
|
+ } else {
|
|
|
+ Log.i("sipCall>>>>>>>>>>>>>>","[Service] sipphone version is unknown");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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();
|
|
|
+ }
|
|
|
+ private void onDataChange( String type ) {
|
|
|
+ try {
|
|
|
+ int count = mListenerList.beginBroadcast();
|
|
|
+ for (int i = 0; i < count; i++) {
|
|
|
+ try {
|
|
|
+ mListenerList.getBroadcastItem(i).onPersonDataChanged(type);
|
|
|
+ } catch (RemoteException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ mListenerList.finishBroadcast();
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置sip账户名称
|
|
|
+ * */
|
|
|
+ @Override
|
|
|
+ public void setSipAccount(String username,String url) throws RemoteException {
|
|
|
+ Log.d("sipCall", ">>>>>>>>>>>> call state: 设置sip账户名称 " +username );
|
|
|
+ //无服务器时端口必须,固定5060,否则拨打IP时,也需加上端口号。如:sip:192.168.1.100:6666
|
|
|
+ Transports transports = Factory.instance().createTransports();
|
|
|
+ transports.setUdpPort(5060);
|
|
|
+ transports.setTcpPort(5060);
|
|
|
+ mCore.setTransports(transports);
|
|
|
+ //设置显示名称
|
|
|
+ AccountCreator accountCreator = mCore.createAccountCreator(null);
|
|
|
+ accountCreator.setUsername(username); //此处就当设置为房间床位号
|
|
|
+ if (url==null || url==""){
|
|
|
+ accountCreator.setDomain("");
|
|
|
+ }else {
|
|
|
+ accountCreator.setDomain(NetHelper.getInstance().getLocalIP());
|
|
|
+ }
|
|
|
+ accountCreator.setTransport(TransportType.Udp);
|
|
|
+ ProxyConfig cfg = accountCreator.createProxyConfig();
|
|
|
+ mCore.setDefaultProxyConfig(cfg);
|
|
|
+ mCore.setIncTimeout(60);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置sip呼叫振铃的时间秒
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void setBellsTime(int time) throws RemoteException {
|
|
|
+ Util.setSipTime(getBaseContext(),String.valueOf(time));
|
|
|
+ mCore.setRing(String.valueOf(time));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 呼叫
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void setCalling(String username, String info) throws RemoteException {
|
|
|
+ Log.d("sipCall", ">>>>>>>>>>>> call state: 呼叫" +username );
|
|
|
+// Address addressToCall = mCore.interpretUrl(username+"@"+NetHelper.getInstance().getLocalIP());
|
|
|
+ Address addressToCall = mCore.interpretUrl(username);
|
|
|
+ CallParams params = mCore.createCallParams(null);
|
|
|
+ Adderss = Util.getSipAddress(getBaseContext());
|
|
|
+ //录音文件路径
|
|
|
+ if (Adderss.equals("")){
|
|
|
+ //录音文件路径
|
|
|
+ params.setRecordFile(Environment.getExternalStorageDirectory().getPath()+"/Download/"+ Util.getTimeFilename() +".wav");
|
|
|
+ }else {
|
|
|
+ params.setRecordFile(Adderss);
|
|
|
+ }
|
|
|
+ if(!TextUtils.isEmpty(info)){
|
|
|
+ params.addCustomHeader("X-extraData",Base64Util.encodeBase64(info));
|
|
|
+ }
|
|
|
+ if (addressToCall != null) {
|
|
|
+ mCore.inviteAddressWithParams(addressToCall, params);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 来电
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public String getCalling() throws RemoteException {
|
|
|
+ Log.d("sipCall", ">>>>>>>>>>>> call state: 来电" );
|
|
|
+ return username;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getSIP() throws RemoteException {
|
|
|
+ String myname = mCore.getIdentity();
|
|
|
+ return myname;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 接听
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void setAcceptWithParams() throws RemoteException {
|
|
|
+ Log.d("sipCall", ">>>>>>>>>>>> call state: 接听" );
|
|
|
+ if (mCore != null && mCore.getCallsNb() > 0) {
|
|
|
+ Call call = mCore.getCurrentCall();
|
|
|
+ if (call == null) {
|
|
|
+ call = mCore.getCalls()[0];
|
|
|
+ }
|
|
|
+ CallParams params = mCore.createCallParams(call);
|
|
|
+ params.setVideoEnabled(false);
|
|
|
+ Adderss = Util.getSipAddress(getBaseContext());
|
|
|
+ //录音文件路径
|
|
|
+ if (Adderss.equals("")){
|
|
|
+ //录音文件路径
|
|
|
+ params.setRecordFile(Environment.getExternalStorageDirectory().getPath()+"/Download/"+ Util.getTimeFilename() +".wav");
|
|
|
+ }else {
|
|
|
+ params.setRecordFile(Adderss);
|
|
|
+ }
|
|
|
+ call.acceptWithParams(params);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 挂断
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void setCallTerminate() throws RemoteException {
|
|
|
+ Log.d("sipCall", ">>>>>>>>>>>> call state: 挂断" );
|
|
|
+ if (mCore.getCallsNb() > 0) {
|
|
|
+ Call call = mCore.getCurrentCall();
|
|
|
+ if (call == null) {
|
|
|
+ call = mCore.getCalls()[0];
|
|
|
+ }
|
|
|
+ call.stopRecording();
|
|
|
+ call.terminate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置录音文件地址
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void setCallAddress(String address) throws RemoteException {
|
|
|
+ Util.setSipTimeAddress(getBaseContext(),String.valueOf(address));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置关闭录音
|
|
|
+ *
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void setMicEnabled(boolean isMicEnabled) throws RemoteException {
|
|
|
+ if (isMicEnabled){
|
|
|
+ mCore.setMicEnabled(true);
|
|
|
+ }else {
|
|
|
+ mCore.setMicEnabled(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * sip状态
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void registerListener(IPersonChangeListener listener) throws RemoteException {
|
|
|
+ mListenerList.register(listener);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void unregisterListener(IPersonChangeListener listener) throws RemoteException {
|
|
|
+ mListenerList.unregister(listener);
|
|
|
+ }
|
|
|
+
|
|
|
+ };
|
|
|
+}
|