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 can use my solution below posted as an answer.
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;
Here is what I came up with, based on @Dave Nottage's gist, to retrieve Android IPv4 addresses and subnet masks:
unit AndroidGetInetAddress;
interface
{$IFDEF Android}
uses
System.SysUtils, {System.Types, System.UITypes, System.Classes, System.Variants,}
IdStack{, IdBaseComponent, IdComponent, IdUDPBase, IdUDPClient, IdGlobal};
procedure GetLocalAddressList(const AAddresses: TIdStackLocalAddressList);
{$ENDIF}
implementation
{$IFDEF Android}
uses
Androidapi.JNI.Java.Net, Androidapi.JNI.JavaTypes, Androidapi.Helpers, Androidapi.JNIBridge;
{ Dumb conversion of number of bits to 4-byte SubnetMask string}
function SubnetMask(ACode: string): string;
begin
var NCode := ACode.ToInteger;
if NCode < 25 then
Result := '255.255.255.' + (%11111111 shr NCode).ToString
else if NCode < 33 then
Result := '255.255.' + (%11111111 shr NCode-24).ToString + '.0'
else if NCode < 41 then
Result := '255.' + (%11111111 shr NCode-32).ToString + '.0.0'
else if NCode < 49 then
Result := (%11111111 shr NCode-40).ToString + '.0.0.0'
else
Result := '0.0.0.0.';
end;
procedure GetLocalAddressList(const AAddresses: TIdStackLocalAddressList);
begin
AAddresses.Clear;
var LInterfaces := TJNetworkInterface.JavaClass.getNetworkInterfaces;
while LInterfaces.hasMoreElements do
begin
var LInterface := TJNetworkInterface.Wrap(TAndroidHelper.JObjectToID(LInterfaces.nextElement));
////// method getInetAddresses evidently does not return a subnet mask
// var LAddresses := LInterface.getInetAddresses;
// while LAddresses.hasMoreElements do
// begin
// var LAddress := TJInetAddress.Wrap(TAndroidHelper.JObjectToID(LAddresses.nextElement));
// WriteLog('DEBUG', 'getAddress: ' + JStringToString(LAddress.ToString));
// if LAddress.isLoopbackAddress then
// Continue;
// // Dave Nottage: Hack until I can find out how to check properly
// WriteLog('DEBUG', 'LName: ' + JStringToString(LAddress.getClass.getName));
// WriteLog('DEBUG', 'LHostAddress: ' + JStringToString(LAddress.getHostAddress));
// end;
////// method getInterfaceAddresses does return IP address, mask, and '[null]' or broadcast IP
var LInterfaceAddresses := LInterface.getInterfaceAddresses;
for var i := 0 to LInterfaceAddresses.size-1 do
begin
var LHostAddress := JStringToString(LInterfaceAddresses.get(i).toString);
// WriteLog('DEBUG', 'Address ' + i.ToString + ': ' + LHostAddress);
if not LHostAddress.Contains('[null]') then
// Have IPv4Inet address with subnet mask and broadcast address
begin
var Address := LHostAddress.Split(['/','[',']',' '], TStringSplitOptions.ExcludeEmpty);
TIdStackLocalAddressIPv4.Create(AAddresses, Address[0], SubnetMask(Address[1]))
end;
end;
end;
end;
{$ENDIF}
end.
"Use at your own risk" :)