Home > other >  How to make TIdStackLocalAddress work on Android?
How to make TIdStackLocalAddress work on Android?

Time:10-11

Modeled on @Remy Lebeau's very helpful code example in this SO item, I have the below function, which works as expected in Windows 10 to return; e.g.:

  No of Addresses: 4
  IPv4 Addresses:
  IP Address #0: 192.168.56.1 - 255.255.255.0 - 11
  IP Address #1: 192.168.1.7 - 255.255.255.0 - 8
  IP: 192.168.56.1

But when I change the platform to Android 64-bit and run it on my Samsung S21 while WiFi is enabled and connected to my LAN, it returns only the local loopback IP and none of the other values; e.g.:

No of Addresses: 1
IPv4 Addresses:
IP Address #0: 127.0.0.1 -  - 0
IP: 127.0.0.1

I had hoped that this had to do with lack of some permission, but as Remy points out in a comment, the problem is that the Indy10 method is broken for Android and one needs to use Dave Nottage's workaround below. (If you want also to get the NetMask that the Indy method is supposed to return, you'll need further work. If/when I get this doped out, I'll post my solution as an answer here)

function getLocalIP: string;
begin
  Result := '';
  try
    var IPList := TIdStackLocalAddressList.Create;
    try
      TIdStack.IncUsage;
      try
        GStack.GetLocalAddressList(IPList);
      finally
        TIdStack.DecUsage;
      end;

      WriteLog('DEBUG', 'No of Addresses: '   IntToStr(IPList.Count));
      WriteLog('DEBUG', 'IPv4 Addresses:');

      var IPStrings := TStringList.Create;
      try
        for var i in IPList do
        begin
          if TIdStackLocalAddressIPv4(i).IPVersion = Id_IPv4 then
          begin
            IPStrings.Add(TIdStackLocalAddressIPv4(i).IPAddress   ' - '   TIdStackLocalAddressIPv4(i).SubNetMask
                ' - '   TIdStackLocalAddressIPv4(i).InterfaceIndex.ToString);
          end;
        end;

        // show IP Addresses in the log file
        for var i := 0 to IPStrings.Count-1 do
          WriteLog('DEBUG', 'IP Address #'   IntToStr(i)   ': '   IPStrings[i]);
        Result := IPStrings[0].Split([' - '])[0];
        WriteLog('DEBUG', 'IP: '   Result);
      finally
        IPStrings.Free;
      end;
    finally
      IPList.Free;
    end;
  except
    On E: Exception do
    begin
      Result := '';
      WriteLog('ERROR', 'IP Error: '   E.message);
    end;
  end;
end;

CodePudding user response:

As per this gist but repeated here:

interface

uses
  IdStack;

procedure GetLocalAddressList(const AAddresses: TIdStackLocalAddressList);

implementation

uses
  System.SysUtils,
  Androidapi.JNI.Java.Net, Androidapi.JNI.JavaTypes, Androidapi.Helpers,  Androidapi.JNIBridge,
  IdGlobal;

procedure GetLocalAddressList(const AAddresses: TIdStackLocalAddressList);
var
  LInterfaces, LAddresses: JEnumeration;
  LInterface: JNetworkInterface;
  LAddress: JInetAddress;
  LInet4Address: JInet4Address;
  LInet6Address: JInet6Address;
  LName, LHostAddress: string;
begin
  AAddresses.Clear;
  LInterfaces := TJNetworkInterface.JavaClass.getNetworkInterfaces;
  while LInterfaces.hasMoreElements do
  begin
    LInterface := TJNetworkInterface.Wrap(JObjectToID(LInterfaces.nextElement));
    LAddresses := LInterface.getInetAddresses;
    while LAddresses.hasMoreElements do
    begin
      LAddress := TJInetAddress.Wrap(JObjectToID(LAddresses.nextElement));
      if LAddress.isLoopbackAddress then
        Continue;
      // Hack until I can find out how to check properly
      LName := JStringToString(LAddress.getClass.getName);
      LHostAddress := JStringToString(LAddress.getHostAddress);
      // Trim excess stuff
      if LHostAddress.IndexOf('%') > -1 then
        LHostAddress := LHostAddress.Substring(0, LHostAddress.IndexOf('%'));
      if LName.Contains('Inet4Address') then
        TIdStackLocalAddressIPv4.Create(AAddresses, LHostAddress, '')
      else if LName.Contains('Inet6Address') then
        TIdStackLocalAddressIPv6.Create(AAddresses, LHostAddress);
    end;
  end;
end;

Use at your own risk

  • Related