3

I'm having an issue integrating Vidyo into an existing app. Video chat lanches, connects, and works just fine. The problem I'm running into is after closing the Vidyo activity. The previous activity returns, executes on through the OnResume(), and acts normal. Upon any user input the app restarts without any crash. Here is the only errors in log.

> E: Bluetooth binder is null E: Failed to open libwvm.so: dlopen
> failed: library "libwvm.so" not found E: glUtilsParamSize: unknow
> param 0x000082da E: glUtilsParamSize: unknow param 0x000082da E:
> glUtilsParamSize: unknow param 0x000082da E: glUtilsParamSize: unknow
> param 0x000082da E: Bluetooth binder is null E:
> [ERROR:interface_registry.cc(104)] Failed to locate a binder for
> interface: autofill::mojom::PasswordManagerDriver E:
> validate_display:99 error 3008 (EGL_BAD_DISPLAY)
>        [ 02-26 15:46:22.399 14354:14521 D/         ]    HostConnection::get() New Host Connection established 0x6d386280, tid
> 14521

mainly:

E: validate_display:99 error 3008 (EGL_BAD_DISPLAY)

Here's my basic Vidyo activity

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Vibrator;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ToggleButton;

import com.example.android.multidex.myapplication.R;
import com.vidyo.VidyoClient.Connector.Connector;
import com.vidyo.VidyoClient.Connector.ConnectorPkg;
import com.vidyo.VidyoClient.Device.RemoteCamera;
import com.vidyo.VidyoClient.Endpoint.LogRecord;
import com.vidyo.VidyoClient.Endpoint.Participant;
import com.vidyo.VidyoClient.NetworkInterface;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.Request;
import okhttp3.Response;


public class Vidyo extends Activity implements VI, View.OnClickListener, Connector.IConnect, Connector.IRegisterLogEventListener, Connector.IRegisterNetworkInterfaceEventListener {
    private VidyoConnectorState mVidyoConnectorState = VidyoConnectorState.VidyoConnectorStateDisconnected;
    private boolean mVidyoClientInitialized = false;
    private Connector mVidyoConnector = null;
    private ToggleButton cameraSwitch, privacySwitch, microphoneSwitch;
    private LinearLayout toolbar;
    private FrameLayout videoFrame;
    private String resource = null;
    private ServiceConnection SC;
    private boolean isBound = false;
    private RadioService RS;

    enum VidyoConnectorState {
        VidyoConnectorStateConnected,
        VidyoConnectorStateDisconnected,
        VidyoConnectorStateDisconnectedUnexpected,
        VidyoConnectorStateFailure;
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.vidyo);
        resource = getIntent().getStringExtra("resource");
        videoFrame = findViewById(R.id.videoFrame);
        cameraSwitch = findViewById(R.id.cameraSwitch);
        privacySwitch = findViewById(R.id.privacySwitch);
        microphoneSwitch = findViewById(R.id.microphoneSwitch);
        toolbar = findViewById(R.id.toolbar);
        ConnectorPkg.setApplicationUIContext(this);
        mVidyoClientInitialized = ConnectorPkg.initialize();
        microphoneSwitch.setOnClickListener(this);
        privacySwitch.setOnClickListener(this);
        cameraSwitch.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
        vibrator.vibrate(10);
        switch (view.getId()) {
            case R.id.microphoneSwitch:
                mVidyoConnector.setMicrophonePrivacy(microphoneSwitch.isChecked());
                break;
            case R.id.privacySwitch:
                mVidyoConnector.setCameraPrivacy(privacySwitch.isChecked());
                break;
            case R.id.cameraSwitch:
                mVidyoConnector.cycleCamera();
                break;
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        SC = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                RadioService.LocalBinder binder = (RadioService.LocalBinder) service;
                RS = binder.getService();
                RS.vidyo_activity_callbacks(Vidyo.this);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
            }
        };
        isBound = bindService(new Intent(this, RadioService.class), SC, BIND_IMPORTANT);
        if (mVidyoConnector != null) {
            mVidyoConnector.setMode(Connector.ConnectorMode.VIDYO_CONNECTORMODE_Foreground);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        ViewTreeObserver viewTreeObserver = videoFrame.getViewTreeObserver();
        if (viewTreeObserver.isAlive()) {
            viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    videoFrame.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    if (mVidyoConnector == null) {
                        if (mVidyoClientInitialized) {
                            try {
                                mVidyoConnector = new Connector(videoFrame, Connector.ConnectorViewStyle.VIDYO_CONNECTORVIEWSTYLE_Default, 50, "info@VidyoClient info@VidyoConnector warning", "", 0);
                                // Set initial position
                                refreshUI();
                                connect();
                            } catch (Exception e) {
                                LOG.e("VidyoConnector Construction failed", e.getMessage());
                            }
                        } else {
                            LOG.e("ERROR: VidyoClientInitialize failed - not constructing VidyoConnector ...");
                        }

                        LOG.i("onResume: mVidyoConnectorConstructed => " + (mVidyoConnector != null ? "success" : "failed"));
                    }
                }
            });
        }
    }

    private void connect(){
        final String data = Jwts.builder()
                .setHeader(RadioService.header)
                .claim("userId", RadioService.user.getUser_id())
                .claim("handle", RadioService.user.getHandle())
                .claim("room", "nineteen")
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 60000))
                .signWith(SignatureAlgorithm.HS256, RadioService.user.getKey())
                .compact();
        Request request = new Request.Builder()
                .url(RadioService.SITE_URL + "server/generate_token.php")
                .post(new FormBody.Builder().add("data", data).build())
                .build();
        RadioService.client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                LOG.e("onFailure", e.getMessage());
            }

            @Override
            public void onResponse(Call call, final Response response) throws IOException {
                if (response.isSuccessful()) {
                    try {
                        String data = response.body().string();
                        LOG.i(RadioService.SITE_URL + "server/generate_token.php", data);
                        final JSONObject object = new JSONObject(data);
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                final boolean status;
                                try {
                                    status = mVidyoConnector.connect(object.getString("host"), object.getString("token"), object.getString("handle"), resource, Vidyo.this);
                                    LOG.i("Connected " + status);
                                } catch (JSONException e) {
                                    e.printStackTrace();
                                }
                            }
                        });


                    } catch (JSONException e) {
                        LOG.e("JSONException", e.getMessage());
                    }
                }
            }
        });
    }

    @Override
    public void onStop() {
        if (RS != null) RS.vidyo_activity_callbacks(null);
        if (isBound) unbindService(SC);
        RS = null;
        if (mVidyoConnector != null) {
            mVidyoConnector.setMode(Connector.ConnectorMode.VIDYO_CONNECTORMODE_Background);
        }
        super.onStop();
    }

    @Override
    public void onDestroy() {
        LOG.i("ONDESTROY");
        // Release device resources
        if (mVidyoConnector != null) {
            mVidyoConnector.disconnect();
            mVidyoConnector.disable();
            mVidyoConnector = null;
        }
        // Uninitialize the VidyoClient library
        ConnectorPkg.uninitialize();
        super.onDestroy();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Refresh the video size after it is painted
        ViewTreeObserver viewTreeObserver = videoFrame.getViewTreeObserver();
        if (viewTreeObserver.isAlive()) {
            viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    videoFrame.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    refreshUI();
                }
            });
        }
    }

    private void refreshUI() {
        mVidyoConnector.showViewAt(videoFrame, 0, 0, videoFrame.getWidth(), videoFrame.getHeight());
    }

    private void connectorStateUpdated(VidyoConnectorState state, final String statusText) {
        LOG.i("connectorStateUpdated, state = " + state.toString());
        mVidyoConnectorState = state;
        // Execute this code on the main thread since it is updating the UI layout

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // Set the status text in the toolbar
                LOG.i("TEXTVIEW SHOULD SHOW", statusText);
                Toaster.toastlow(Vidyo.this, statusText);

                if (mVidyoConnectorState == VidyoConnectorState.VidyoConnectorStateConnected) {
                    //toolbar.setVisibility(View.INVISIBLE);
                } else {
                    //TODO: reconnect
                }
            }
        });
    }

    @Override
    public void onSuccess() {
        connectorStateUpdated(VidyoConnectorState.VidyoConnectorStateConnected, "Connected");
    }

    @Override
    public void onFailure(Connector.ConnectorFailReason reason) {
        LOG.i("onFailure: connection attempt failed, reason = " + reason.toString());
        connectorStateUpdated(VidyoConnectorState.VidyoConnectorStateFailure, "Connection failed");
    }

    @Override
    public void onDisconnected(Connector.ConnectorDisconnectReason reason) {
        if (reason == Connector.ConnectorDisconnectReason.VIDYO_CONNECTORDISCONNECTREASON_Disconnected) {
            LOG.i("onDisconnected: successfully disconnected, reason = " + reason.toString());
            connectorStateUpdated(VidyoConnectorState.VidyoConnectorStateDisconnected, "Disconnected");
        } else {
            LOG.i("onDisconnected: unexpected disconnection, reason = " + reason.toString());
            connectorStateUpdated(VidyoConnectorState.VidyoConnectorStateDisconnectedUnexpected, "Unexpected disconnection");
        }
    }

    @Override
    public void onLog(LogRecord logRecord) {
        LOG.i(logRecord.name + " : " + logRecord.level + " : " + logRecord.functionName + " : " + logRecord.message);
    }

    @Override
    public void onNetworkInterfaceAdded(NetworkInterface vidyoNetworkInterface) {
        LOG.i("onNetworkInterfaceAdded: name=" + vidyoNetworkInterface.getName() + " address=" + vidyoNetworkInterface.getAddress() + " type=" + vidyoNetworkInterface.getType() + " family=" + vidyoNetworkInterface.getFamily());
    }

    @Override
    public void onNetworkInterfaceRemoved(NetworkInterface vidyoNetworkInterface) {
        LOG.i("onNetworkInterfaceRemoved: name=" + vidyoNetworkInterface.getName() + " address=" + vidyoNetworkInterface.getAddress() + " type=" + vidyoNetworkInterface.getType() + " family=" + vidyoNetworkInterface.getFamily());
    }

    @Override
    public void onNetworkInterfaceSelected(NetworkInterface vidyoNetworkInterface, NetworkInterface.NetworkInterfaceTransportType vidyoNetworkInterfaceTransportType) {
        LOG.i("onNetworkInterfaceSelected: name=" + vidyoNetworkInterface.getName() + " address=" + vidyoNetworkInterface.getAddress() + " type=" + vidyoNetworkInterface.getType() + " family=" + vidyoNetworkInterface.getFamily());
    }

    @Override
    public void onNetworkInterfaceStateUpdated(NetworkInterface vidyoNetworkInterface, NetworkInterface.NetworkInterfaceState vidyoNetworkInterfaceState) {
        LOG.i("onNetworkInterfaceStateUpdated: name=" + vidyoNetworkInterface.getName() + " address=" + vidyoNetworkInterface.getAddress() + " type=" + vidyoNetworkInterface.getType() + " family=" + vidyoNetworkInterface.getFamily() + " state=" + vidyoNetworkInterfaceState);
    }


}

It's a multiple activity application and no other activity creates this behavior of restarting the application when returning to the main activity except the Vidyo activity. I'm not seeing anything in particularly different with the Vidyo activity to cause this behavior. Searches for the error point towards a JavaScript error.

So obviously my question is why does the app restart and how to fix it?

Robert Goodrick
  • 190
  • 1
  • 11

2 Answers2

0

I ran into a similar issue. I don't know if the cause is the same, but marking the activity as singleTop in the manifest fixed it for me.

Avezou
  • 91
  • 1
  • 6
0

I also ran into the same issue but I solved it by checking the call off connector.disable() method.

Please focus on disconnect() API. It's asynchronous call which is following to onDisconencted() callback so if you trigger it right before activity exit this will lead to crash because from onDestroy() you are releasing client with disable().

Imagine like you are starting disconnection process (which take some amount of time) and at the same time releasing all the resources/drop connections.

Call to disconnect only from user interaction perspective (via UI button) and once received onDisconnected() -> quit activity here (with finish) what will cause onDestroy() and release client. Same you can handle via onBackPressed().

I hope it helps.

Shivam Yadav
  • 958
  • 11
  • 23