Search code examples
androidc++sipc++builder

Instantiate Android SipManager in c++ builder


I'm trying to instantiate the SipManager (developer.android.com) in Embarcadero with c++ builder but I'm not able to find the right way. For this, I'm using Delphi Interface instances.

In Java it is like this:

public static SipManager manager;
manager = SipManager.newInstance(context);
//Context is the application context for creating the manager object.

For the correct compilation of the code that will appear next, it is necessary to include the following:

#include <System.Classes.hpp>
#if defined (_PLAT_ANDROID)
    #include <Androidapi.Helpers.HPP>
    #include <Androidapi.JNI.Net.HPP>
    #include <Androidapi.JNI.os.HPP>
    #include <Androidapi.JNI.Support.HPP>
    #include <Androidapi.JNI.Media.HPP>
#endif

In addition, the "Use Sip" and "Internet" permissions must also be added to the manifest.

I tried to do it in many ways, based on the fact that I already knew how to install the WifiManager:

#if defined (_PLAT_ANDROID)
    _di_JObject obj;
    _diJWifiManager wm;
    obj = SharedActivityContext()->getSystemService(TJContext::JavaClass->WIFI_SERVICE);
    wm = TJWifiManager::Wrap(((_di_ILocalObject)obj)->GetObjectID());
    wm->setWifiEnabled(true);
#endif

For my application I have done the following:

  1. This compiles but when running the application it says that the method init() is not found.

    #if defined (_PLAT_ANDROID)
        _di_JObject obj;
        _di_JSipManager sipm;
        obj = TJSipManager::JavaClass->init();
        sipm = TJSipManager::Wrap(((_di_ILocalObject)obj)->GetObjectID());
    #endif
    
  2. This compiles too, but when running the application, this one has an exception kind of "Abort".

    #if defined (_PLAT_ANDROID)
        _di_JSipManager sipm;
        sipm = TJSipManager::Create();
    #endif
    
  3. This compiles too, but when running the application, this one has an exception kind of "Access Violation".

    #if defined (_PLAT_ANDROID)
        _di_JSipManager sipm;
        sipm = TJSipManager::JavaClass;
    #endif
    
  4. This compiles too, but it has an exception kind of "Abort" when I try to open the profile.

    #if defined (_PLAT_ANDROID)
        _di_JSipManager sipm;
        _di_JContext context;
        _di_JSipProfileBuilder sippb;
        _di_JSipProfile sipp;
        context = SharedActivityContext()->getApplicationContext();
        TJSipManager *sipn = new TJSipManager();
        sipm = sipn->Wrap(((_di_ILocalObject)context) >GetObjectID());
        _di_JString uri;
        uri = StringToJString("sip:[email protected]:5060");
        sippb = TJSipProfile_Builder::JavaClass->init(uri);
        sipp = sippb->build();
        sipm->open(sipp);
    #endif
    

If I do only the part of the SipProfile, there is no problem and the profile is built.

#if defined (_PLAT_ANDROID)
    _di_JSipProfileBuilder sippb;
    _di_JSipProfile sipp;
    _di_JString uri;
    uri = StringToJString("sip:[email protected]:5060");
    sippb = TJSipProfile_Builder::JavaClass->init(uri);
    sipp = sippb->build();
#endif

And in Java it is like this:

public static SipProfile me;
SipProfile.Builder builder = new SipProfile.Builder("uri");
me = builder.build();

I would like to know how to instantiate the SipManager for c++ builder. In Java, it has been seen that it uses the method "newInstance(context)" for it, but when I try to instantiate it in C++ builder it doesn't appear any similar method that belongs to this class.

  1. Can I instantiate in C++ builder?

  2. What's the right way to do it?

  3. If not possible, is there another library to make a Sip application in Embarcadero with c++ builder?


In another forum, Remy replied:

"It SHOULD be something as simple as this in C++Builder:

#if defined (_PLAT_ANDROID)
    _di_JSipManager manager = TJSipManager::JavaClass->newInstance(SharedActivityContext());
    // use manager as needed...
#endif

But in actuality, this doesn't work (at least in Seattle, don't know about later versions) because the static newInstance() method is MISSING from the JSipManagerClass interface in Androidapi.JNI.Net.hpp!

You could try using Java2OP to re-import the SipManager class, but I don't know if that will end up suffering from the same problem or not."


But I have the last release of Tokio and I still have the same problem. I will try to re-import the SipManager.


Solution

  • Let's go through your offerings and see why they don't work:

    1) you are calling the parameterless Java constructor, surfaced as init() but this class is not meant to be constructed through a regular constructor. One presumes that constructor is private, but regardless, the documentation says you create an instance through newInstance().

    2) invoking the import class Create() class method is normally much the same as calling init(), but done through a different avenue. Given init is not available it fails, just with a different symptom here.

    3) You have declared a variable of the Delphi JSipManager instance methods interface type, but then assigned it the JavaClass property, which gives the Delphi JSipManagerClass class methods interface type. Those 2 are not the same. An error is to be expected.

    4) The template class TJSipManager is a helper to gain access to the class methods interface (through the JavaClass property) or to instance methods via an instance created by invoking a constructor (maybe via Create() or JavaClass->init(). You shouldn't construct an instance of this template class - it serves no valid purpose. Proceeding to cast (via Wrap()) the context object into a SipManager smacks of desperation - they are wholly disparate types.

    OK, now let's see why what should work doesn't work.

    The reason the newInstance() method (and others) are commented out of the JSipManagerClass class methods interface in the original Delphi unit, Androidapi.JNI.Net.pas, is that they all have a parameter or a return type such as JContext or JIntent that is defined and exposed from a unit that already uses Androidapi.JNI.Net.pas, namely the unit Androidapi.JNI.GraphicsContentViewText.pas.

    Java2OP has left the methods there because they exist, but commented them out to avoid a circular unit reference error.

    What will work is to redefine the SipManager JNI Bridge types in a new Delphi unit, add that new unit to your C++ project, include its generated header and proceed from there.

    To make the unit you can right-click on the project in the Project Manager and choose Add New, Unit - Delphi. If you are using C++ Builder only (with no Delphi support) just create the unit in a text editor. It's called Androidapi.JNI.SIP.pas and contains the import of just one type: SipManager - to avoid fighting with ambiguity I've called the base import interface JSipManager2:

    unit Androidapi.JNI.SIP;
    
    interface
    
    uses
      Androidapi.JNIBridge,
      Androidapi.JNI.Net,
      Androidapi.JNI.GraphicsContentViewText,
      Androidapi.JNI.JavaTypes,
      Androidapi.JNI.Os;
    
    type
    // ===== Forward declarations =====
    
      JSipManager2 = interface;//android.net.sip.SipManager
    
      JSipManager2Class = interface(JObjectClass)
        ['{62C12348-C7E4-4B5E-AE75-715EAAFD4465}']
        {class} function _GetEXTRA_CALL_ID: JString; cdecl;
        {class} function _GetEXTRA_OFFER_SD: JString; cdecl;
        {class} function _GetINCOMING_CALL_RESULT_CODE: Integer; cdecl;
        {class} function getCallId(incomingCallIntent: JIntent): JString; cdecl;
        {class} function getOfferSessionDescription(incomingCallIntent: JIntent): JString; cdecl;
        {class} function isApiSupported(context: JContext): Boolean; cdecl;
        {class} function isIncomingCallIntent(intent: JIntent): Boolean; cdecl;
        {class} function isSipWifiOnly(context: JContext): Boolean; cdecl;
        {class} function isVoipSupported(context: JContext): Boolean; cdecl;
        {class} function newInstance(context: JContext): JSipManager2; cdecl;
        {class} property EXTRA_CALL_ID: JString read _GetEXTRA_CALL_ID;
        {class} property EXTRA_OFFER_SD: JString read _GetEXTRA_OFFER_SD;
        {class} property INCOMING_CALL_RESULT_CODE: Integer read _GetINCOMING_CALL_RESULT_CODE;
      end;
    
      [JavaSignature('android/net/sip/SipManager')]
      JSipManager2 = interface(JObject)
        ['{EDE899E1-4774-41FB-BC53-03BB69565231}']
        procedure close(localProfileUri: JString); cdecl;
        function createSipSession(localProfile: JSipProfile; listener: JSipSession_Listener): JSipSession; cdecl;
        //function getSessionFor(incomingCallIntent: JIntent): JSipSession; cdecl;
        function isOpened(localProfileUri: JString): Boolean; cdecl;
        function isRegistered(localProfileUri: JString): Boolean; cdecl;
        function makeAudioCall(localProfile: JSipProfile; peerProfile: JSipProfile; listener: JSipAudioCall_Listener; timeout: Integer): JSipAudioCall; cdecl; overload;
        function makeAudioCall(localProfileUri: JString; peerProfileUri: JString; listener: JSipAudioCall_Listener; timeout: Integer): JSipAudioCall; cdecl; overload;
        procedure open(localProfile: JSipProfile); cdecl; overload;
        //procedure open(localProfile: JSipProfile; incomingCallPendingIntent: JPendingIntent; listener: JSipRegistrationListener); cdecl; overload;
        procedure register(localProfile: JSipProfile; expiryTime: Integer; listener: JSipRegistrationListener); cdecl;
        procedure setRegistrationListener(localProfileUri: JString; listener: JSipRegistrationListener); cdecl;
        //function takeAudioCall(incomingCallIntent: JIntent; listener: JSipAudioCall_Listener): JSipAudioCall; cdecl;
        procedure unregister(localProfile: JSipProfile; listener: JSipRegistrationListener); cdecl;
      end;
      TJSipManager2 = class(TJavaGenericImport<JSipManager2Class, JSipManager2>) end;
    
    implementation
    
    procedure RegisterTypes;
    begin
      TRegTypes.RegisterType('Androidapi.JNI.SIP.JSipManager2', TypeInfo(Androidapi.JNI.SIP.JSipManager2));
    end;
    
    initialization
      RegisterTypes;
    end.
    

    Then use it in some C++ code like this:

    ...
    // Cleanse build of lots of errors about __cdecl...
    #define __cdecl
    #include "Androidapi.JNI.SIP.hpp"
    #include <Androidapi.Helpers.hpp>
    ...
        _di_JSipManager2 manager = TJSipManager2::JavaClass->newInstance(TAndroidHelper::Context);
        // use manager as needed...
    

    This seems to compile and execute OK, but I have no SIP service so cannot get further than that.

    As a parting comment, the global SharedActivityContext() function is deprecated - the TAndroidHelper class now contains all that sort of thing as static methods/properties such as TAndroidHelper::Context.