Начать поступление звонка из фоновой службы XMPP smack webRTC - PullRequest
0 голосов
/ 03 октября 2019

Я создаю приложение для видеовызовов для сотрудников нашей команды.

Так что после некоторого времени, проведенного в переполнении стека, я сделал фоновый сервис самостоятельно запущенным и активным для большей части приложения.

Я держал свой телефон подключенным к Android Studio довольно долго, как 3 часа, и после этого, когда я попытался позвонить пользователю. В Android Studio я мог видеть, что служба XMPP пыталась запустить действие, но не смогла, потому что приложение больше не было открыто.

Logs when i tried to call

При точно13:08 Я попытался позвонить, и служба попыталась инициировать класс активности вызова. оно сломалось.

Я прилагаю код моей службы XMPP, Broadcast Receiver, Manifest и пример того, как можно запустить службу Activity Call.

Служба XMPP

package com.medimetry.partner_app;

import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import com.medimetry.partner_app.utils.Class_SharedPreferences;
import com.medimetry.partner_app.webRTC.ServerPingWithAlarmManager;
import com.medimetry.partner_app.webRTC.XmppRTCClient;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jivesoftware.smackx.iqregister.AccountManager;
import org.jivesoftware.smackx.ping.PingFailedListener;
import org.jivesoftware.smackx.ping.PingManager;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;

import static com.medimetry.partner_app.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED;
import static com.medimetry.partner_app.CallActivity.EXTRA_ROOMID;

public class XMPPService extends Service {
    private Intent intent;
    private Class_SharedPreferences sharedPreferences;
    private XmppAsyncConnection xmppAsyncConnection;
    public XMPPTCPConnection conn1;
    private static XMPPService self;
    private XMPPEventsListener eventsListener;

    public static Boolean connected = false;
    public static String TAG = "xmppservice";

    DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder dBuilder = null;
    private String uid;
    private boolean shouldSendPresence = false;

    public static XMPPService instance() {
        return self;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        this.intent = intent;
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        self = this;

        sharedPreferences = new Class_SharedPreferences(this);

        if (sharedPreferences.getWpUserId().equals("0")) return;

        this.xmppAsyncConnection = new XmppAsyncConnection();
        this.xmppAsyncConnection.execute();

        XmppRTCClient.instance(null, sharedPreferences.getWpUserId()).xmppServiceInitiated();

        if (dBuilder == null) {
            try {
                dBuilder = dbFactory.newDocumentBuilder();

            } catch (ParserConfigurationException e) {
                e.printStackTrace();
            }
        }
    }

    public void setEventsListener(XMPPEventsListener eventsListener) {
        this.eventsListener = eventsListener;
    }

    public void setConnected(boolean isConnected) {
        connected = isConnected;

        Log.i(TAG, "IsConnected status change: " + (isConnected ? "true" : "false"));
    }

    public void sendPresence() {
        if ( ! connected) return;

        new android.os.Handler().postDelayed(
                new Runnable() {
                    public void run() {

                        Log.i(TAG, "Sending presence from xmpp");
                    }
                },
                30000); // 30 seconds
    }

    class XmppAsyncConnection extends AsyncTask {

        @Override
        protected Object doInBackground(Object[] objects) {

            uid = sharedPreferences.getWpUserId();
            // uid = "100111";

            try {
                XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
                        .setUsernameAndPassword(uid + "@" + XmppRTCClient.EJABBERD_SERVICE_NAME, uid)
                        .setHost(XmppRTCClient.EJABBERD_TURN_SERVER_HOST)
                        .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
                        .setServiceName(XmppRTCClient.EJABBERD_SERVICE_NAME)
                        .setDebuggerEnabled(true) // to view what's happening in detail
                        .setConnectTimeout(1000000)
                        .setCompressionEnabled(false)
                        .setDebuggerEnabled(true)
                        .setSendPresence(true)
                        .build();

                SASLAuthentication.blacklistSASLMechanism("SCRAM-SHA-1");
                SASLAuthentication.blacklistSASLMechanism("DIGEST-MD5");
                SASLAuthentication.unBlacklistSASLMechanism("PLAIN");

                conn1 = new XMPPTCPConnection(config);

                conn1.addConnectionListener(new ConnectionListener() {
                    @Override
                    public void connected(XMPPConnection connection) {
                        Log.e("Connected",""+connection.getHost());
                        sendPresence();
                        setConnected(true);
                    }

                    @Override
                    public void authenticated(XMPPConnection connection, boolean resumed) {
                        Log.e("Authenticated",""+connection.getHost());

                        if(connection.isAuthenticated()) {

                            ServerPingWithAlarmManager.getInstanceFor(connection).setEnabled(true);

                            PingManager pingManager = PingManager.getInstanceFor(connection);
                            pingManager.setPingInterval(10);

                            try {
                                pingManager.pingMyServer();
                                pingManager.pingMyServer(true, 10);
                                pingManager.pingServerIfNecessary();
                                pingManager.registerPingFailedListener(new PingFailedListener() {
                                    @Override
                                    public void pingFailed() {
                                        Log.i(TAG, "pingFailed");

                                        conn1.disconnect();
                                        try {
                                            conn1.connect();
                                        } catch (SmackException | IOException | XMPPException e) {
                                            e.printStackTrace();
                                        }
                                    }
                                });

                            } catch (SmackException.NotConnectedException e) {
                                Log.e(TAG, e.getMessage());
                            }
                        }
                    }

                    @Override
                    public void connectionClosed() {
                        Log.e("Connection Closed","Connection Closed");
                        setConnected(false);

                    }

                    @Override
                    public void connectionClosedOnError(Exception e) {
                        Log.e("Connection Close Error",""+e);

                        // createConnection();
                        setConnected(false);
                    }

                    @Override
                    public void reconnectionSuccessful() {
                        Log.e("Reconnection Success","Success");

                        setConnected(true);
                    }

                    @Override
                    public void reconnectingIn(int seconds) {
                        setConnected(false);
                    }

                    @Override
                    public void reconnectionFailed(Exception e) {
                        Log.e("Reconnection Failed","" + e);

                        setConnected(false);
                        tryRegisteringUser(conn1, uid);
                    }
                });

                ReconnectionManager.getInstanceFor(conn1).enableAutomaticReconnection();
                ReconnectionManager.setEnabledPerDefault(true);

                conn1.connect();
                if( conn1.isConnected() ) {
                    Log.w("xmpp", "conn done");

                    setConnected(false);
                }

                conn1.login();

                if(conn1.isAuthenticated()) {
                    Log.w("xmpp", "Auth done");
                }


                conn1.addAsyncStanzaListener(new StanzaListener() {

                    @Override
                    public void processPacket(Stanza packet) {
                        Log.w("xmpp", "received packet, " + packet);

                        try {
                            Document doc = dBuilder.parse(new InputSource(new StringReader(packet.toXML().toString())));
                            Element element = doc.getDocumentElement();

                            String stanzaIdentifier = element.getTagName() + "-" + element.getAttribute("type");
                            if (stanzaIdentifier.equals("message-groupchat")) { // initial call request, get the app on top
                                if (CallActivity.active) {
                                    CallActivity.getInstance().bringToFront();
                                } else {
                                    Intent webViewAppIntent = new Intent(getApplicationContext(), CallActivity.class);
                                    Uri uri = Uri.parse(getString(R.string.base_url));
                                    webViewAppIntent.setData(uri);
                                    webViewAppIntent.putExtra(EXTRA_ROOMID, sharedPreferences.getWpUserId());
                                    webViewAppIntent.putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true);
                                    webViewAppIntent.putExtra("IncommingCall", true);

                                    webViewAppIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

                                    startActivity(webViewAppIntent);
                                }
                            }

                            if (eventsListener == null) {
                                Log.e("xmpp", "Event listener is null");
                            } else {
                                eventsListener.processStanza(doc, packet);
                            }

                        } catch (SAXException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }, new AndFilter());

                eventsListener.setConnection(conn1);

            } catch (Exception e) {
                e.printStackTrace();
            }

            return null;
        }
    }

    private void tryRegisteringUser(XMPPTCPConnection conn1, String uid) {
        try {
            if (conn1.isConnected())
            {
                AccountManager accountManager = AccountManager.getInstance(conn1);

                Map<String, String> map = new HashMap<>();
                map.put("username", uid);
                map.put("name", uid);
                map.put("password", uid);
                map.put("email", "abc@gmail.com");
                map.put("creationDate", "" + System.currentTimeMillis() / 1000L);

                accountManager.createAccount(uid, uid, map);

                conn1.connect();
                conn1.login();

            } else {
                conn1.connect();
            }
        } catch ( Exception e) {
            Log.e(TAG, "Something went wrong while registering user: " + e);
        }
    }
}

Приемник сетевого детектора

package com.medimetry.partner_app;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.CountDownTimer;
import android.util.Log;
import android.view.View;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;

import java.io.IOException;

public class NetworkDetectorReceiver extends BroadcastReceiver {

    private static final String TAG = "NetworkDetector";

    Context mContext;
    private boolean isNetworkConnected;

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(TAG, "Wokring");

        mContext = context;

        ConnectivityManager connectivityManager
                = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetworkInfo = connectivityManager != null ? connectivityManager.getActiveNetworkInfo() : null;
        isNetworkConnected = activeNetworkInfo != null && activeNetworkInfo.isConnected();
        Log.i(TAG, "Is network connected: " + isNetworkConnected);

        new CountDownTimer(400, 1000) {
            @Override
            public void onTick(long millisUntilFinished) {

            }

            @Override
            public void onFinish() {

                if (WebViewClass.getInstance() != null) {
                    if (!isNetworkConnected) {
                        WebViewClass.getInstance().offlineRibbon.setVisibility(View.VISIBLE);
                    } else {
                        WebViewClass.getInstance().offlineRibbon.setVisibility(View.GONE);
                        if (WebViewClass.getInstance().errorOnStartup) {
                            WebViewClass.getInstance().errorOnStartup = false;
                            WebViewClass.getInstance().initFunction();
                        }

                    }
                }
                Log.e("Is Connected", "" + isNetworkConnected);
            }
        }.start();

        // check if XMPP is connected.
        Log.w(TAG, "checking if XMPP is connected: " + XMPPService.connected);

        Intent serviceIntent = new Intent(mContext, XMPPService.class);

        // if xmpp is connected, return
        if (XMPPService.connected || !isNetworkConnected) return;

        if ((XMPPService.instance() != null) && (XMPPService.instance().conn1 != null)) {
            Log.i(TAG, "XMPP conn1 is actually Connected? " + XMPPService.instance().conn1.isConnected());
        }

        if ((XMPPService.instance() != null) && (XMPPService.instance().conn1 != null) && !XMPPService.instance().conn1.isConnected()) {
            try {
                XMPPService.instance().conn1.connect();

                return;
            } catch (SmackException | IOException | XMPPException e) {
                e.printStackTrace();
            }
        }

        mContext.stopService(serviceIntent);
        mContext.startService(serviceIntent);
    }
}

Файл манифеста для получения широковещания

<receiver android:name=".NetworkDetectorReceiver">
            <intent-filter>
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
                <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
                <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.PROVIDER_CHANGED"/>
                <action android:name="android.intent.action.SCREEN_ON"/>
                <action android:name="android.intent.action.USER_UNLOCKED"/>
            </intent-filter>
        </receiver>

<service
                android:name=".XMPPService"
                android:enabled="true"
                android:stopWithTask="false" />

и как запускается операция вызова

в службе XMPP

                            if (CallActivity.active) {
                                CallActivity.getInstance().bringToFront();
                            } else {
                                Intent incommingCallViewIntent = new Intent(getApplicationContext(), CallActivity.class);
                                Uri uri = Uri.parse(getString(R.string.base_url));
                                incommingCallViewIntent.setData(uri);
                                incommingCallViewIntent.putExtra(EXTRA_ROOMID, sharedPreferences.getWpUserId());
                                incommingCallViewIntent.putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true);
                                incommingCallViewIntent.putExtra("IncommingCall", true);

                                incommingCallViewIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

                                startActivity(incommingCallViewIntent);
                            }

Итак, мои вопросы: 1. Верны ли мои приемники вещания, я хочу, чтобы эта служба работала каждый раз. Я переусердствовал что-нибудь или недооценил. Пожалуйста, дайте мне знать, я не уверен, как получить Wi-Fi подключить / отключить или мобильную сеть вкл / выкл действие. 2. через 2-3 часа, когда мой телефон находится в спящем режиме, при звонке я получаю эту ошибку

10-03 13:08:16.385 20956-21199/com.medimetry.partner_app E/AndroidRuntime: FATAL EXCEPTION: Smack Packet Reader (1)
    Process: com.medimetry.partner_app, PID: 20956
    java.lang.InternalError: Thread starting during runtime shutdown
        at java.lang.Thread.nativeCreate(Native Method)
        at java.lang.Thread.start(Thread.java:733)
        at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:975)
        at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1382)
        at org.jivesoftware.smack.util.BoundedThreadPoolExecutor.executeBlocking(BoundedThreadPoolExecutor.java:44)
        at org.jivesoftware.smack.AbstractXMPPConnection.processPacket(AbstractXMPPConnection.java:984)
        at org.jivesoftware.smack.AbstractXMPPConnection.parseAndProcessStanza(AbstractXMPPConnection.java:968)
        at org.jivesoftware.smack.tcp.XMPPTCPConnection.access$500(XMPPTCPConnection.java:140)
        at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets(XMPPTCPConnection.java:997)
        at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$300(XMPPTCPConnection.java:952)
        at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run(XMPPTCPConnection.java:967)
        at java.lang.Thread.run(Thread.java:764)

, как также упоминается на скриншоте.

Что будет лучшим для начала активностипо запросу. Мы нацелены на надежность, такую ​​как Google Duo.

...