I set an Android:process=":XX" for my particular activity to make it run in a separate process.
However when the new activity/process init, it will call my Application:onCreate() which contains some application level initialization.
I'm thinking of avoiding duplication initialization by checking current process name.
So is there an API available?
Thanks.
Full code is
String currentProcName = "";
int pid = android.os.Process.myPid();
ActivityManager manager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
for (RunningAppProcessInfo processInfo : manager.getRunningAppProcesses())
{
if (processInfo.pid == pid)
{
currentProcName = processInfo.processName;
return;
}
}
Get it from ActivityThread
In API 28+, you can call Application.getProcessName(), which is just a public wrapper around ActivityThread.currentProcessName().
On older platforms, just call ActivityThread.currentProcessName() directly.
Note that prior to API 18, the method was incorrectly called ActivityThread.currentPackageName() but still in fact returned the process name.
Example code
public static String getProcessName() {
if (Build.VERSION.SDK_INT >= 28)
return Application.getProcessName();
// Using the same technique as Application.getProcessName() for older devices
// Using reflection since ActivityThread is an internal API
try {
#SuppressLint("PrivateApi")
Class<?> activityThread = Class.forName("android.app.ActivityThread");
// Before API 18, the method was incorrectly named "currentPackageName", but it still returned the process name
// See https://github.com/aosp-mirror/platform_frameworks_base/commit/b57a50bd16ce25db441da5c1b63d48721bb90687
String methodName = Build.VERSION.SDK_INT >= 18 ? "currentProcessName" : "currentPackageName";
Method getProcessName = activityThread.getDeclaredMethod(methodName);
return (String) getProcessName.invoke(null);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
Compatibility
Tested and working on
Official emulator
16
17
18
19
21
22
23
24
25
26
27
28
Q beta 1
Real devices
Motorola Moto G5 Plus running Android 8.1.0
Samsung Galaxy S5 running Android 6.0.1
Sony Xperia M running stock Android 7.1.1
Sony Xperia M running Sony Android 4.1.2
The ActivityManager solution contains a sneaky bug, particularly if you check your own process name from your Application object. Sometimes, the list returned from getRunningAppProcesses simply doesn't contain your own process, raising a peculiar existential issue.
The way I solve this is
BufferedReader cmdlineReader = null;
try {
cmdlineReader = new BufferedReader(new InputStreamReader(
new FileInputStream(
"/proc/" + android.os.Process.myPid() + "/cmdline"),
"iso-8859-1"));
int c;
StringBuilder processName = new StringBuilder();
while ((c = cmdlineReader.read()) > 0) {
processName.append((char) c);
}
return processName.toString();
} finally {
if (cmdlineReader != null) {
cmdlineReader.close();
}
}
EDIT: Please notice that this solution is much faster than going through the ActivityManager but does not work if the user is running Xposed or similar. In that case you might want to do the ActivityManager solution as a fallback strategy.
This is an update to David Burström's answer. This can be written far more concisely as:
public String get() {
final File cmdline = new File("/proc/" + android.os.Process.myPid() + "/cmdline");
try (BufferedReader reader = new BufferedReader(new FileReader(cmdline))) {
return reader.readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
I have more efficient method, you don't need IPC to ActivityManagerService and poll the Running process, or read the file.You can call this method from your custom Application class;
private String getProcessName(Application app) {
String processName = null;
try {
Field loadedApkField = app.getClass().getField("mLoadedApk");
loadedApkField.setAccessible(true);
Object loadedApk = loadedApkField.get(app);
Field activityThreadField = loadedApk.getClass().getDeclaredField("mActivityThread");
activityThreadField.setAccessible(true);
Object activityThread = activityThreadField.get(loadedApk);
Method getProcessName = activityThread.getClass().getDeclaredMethod("getProcessName", null);
processName = (String) getProcessName.invoke(activityThread, null);
} catch (Exception e) {
e.printStackTrace();
}
return processName;
}
ActivityManagerService is already send the process infor to ActivityThread when process is start.(ActivityThread.main-->attach()-->IActivityManager.attachApplication--IPC-->ActivityManagerService-->ApplicationThread.bindApplication)
ApplicationThread:
public final void bindApplication(String processName,***) {
//***
AppBindData data = new AppBindData();
data.processName = processName;
//**
}
When we called getProcessName, it will finally deliver to AppBindData object.
So we can easily and efficient get current process name;
To wrap up different approaches of getting process name using Kotlin:
Based on the https://stackoverflow.com/a/21389402/3256989 (/proc/pid/cmdline):
fun getProcessName(): String? =
try {
FileInputStream("/proc/${Process.myPid()}/cmdline")
.buffered()
.readBytes()
.filter { it > 0 }
.toByteArray()
.inputStream()
.reader(Charsets.ISO_8859_1)
.use { it.readText() }
} catch (e: Throwable) {
null
}
Based on https://stackoverflow.com/a/55549556/3256989 (from SDK v.28 (Android P)):
fun getProcessName(): String? =
if (VERSION.SDK_INT >= VERSION_CODES.P) Application.getProcessName() else null
Based on https://stackoverflow.com/a/45960344/3256989 (reflection):
fun getProcessName(): String? =
try {
val loadedApkField = application.javaClass.getField("mLoadedApk")
loadedApkField.isAccessible = true
val loadedApk = loadedApkField.get(application)
val activityThreadField = loadedApk.javaClass.getDeclaredField("mActivityThread")
activityThreadField.isAccessible = true
val activityThread = activityThreadField.get(loadedApk)
val getProcessName = activityThread.javaClass.getDeclaredMethod("getProcessName")
getProcessName.invoke(activityThread) as String
} catch (e: Throwable) {
null
}
Based on https://stackoverflow.com/a/19632382/3256989 (ActivityManager):
fun getProcessName(): String? {
val pid = Process.myPid()
val manager = appContext.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
return manager?.runningAppProcesses?.filterNotNull()?.firstOrNull { it.pid == pid }?.processName
}
First, get the current process pid. Second, list all processes of running. Finally, if it has equal pid, it's ok, or it's false.
public static String getProcessName(Context context) {
int pid = android.os.Process.myPid();
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> infos = manager.getRunningAppProcesses();
if (infos != null) {
for (ActivityManager.RunningAppProcessInfo processInfo : infos) {
if (processInfo.pid == pid) {
return processInfo.processName;
}
}
}
return null;
}
Since Android Pie (SDK v28), there is actually an official method for this in the Application class:
public static String getProcessName ()
See the docs
If I've understood your question correctly, you should be able to use ActivityManager, as per this thread.
There is a method in ActivityThread class, You may use reflection to get the current processName. You don't need any loop or tricks. The performance is best compares to other solution. The limitation is you can only get your own process name. It's not a big deal since it covers most usage cases.
val activityThreadClass = XposedHelpers.findClass("android.app.ActivityThread", param.classLoader)
val activityThread = XposedHelpers.callStaticMethod(activityThreadClass, "currentActivityThread")
val processName = XposedHelpers.callStaticMethod(activityThreadClass, "currentProcessName")
The main process's father process should be zygote, this should be the accurate solution
first judge the process's name from /proc/pid/cmdline which should equal to package name
judge the process's father whether Zygote(why do this? because some APP have different processes with same name)
Related
Now with android 10 updated permission and security, we cannot access the user's devices device id and IMEI number but I want some unique id of the device so that we can track the user.
The requirement is we want to have/restrict one login from one phone
Android 10 Restricted developer to Access IMEI number.
You can have a alternate solution by get Software ID. You can use software id as a unique id. Please find below code as i use in Application.
public static String getDeviceId(Context context) {
String deviceId;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
deviceId = Settings.Secure.getString(
context.getContentResolver(),
Settings.Secure.ANDROID_ID);
} else {
final TelephonyManager mTelephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (mTelephony.getDeviceId() != null) {
deviceId = mTelephony.getDeviceId();
} else {
deviceId = Settings.Secure.getString(
context.getContentResolver(),
Settings.Secure.ANDROID_ID);
}
}
return deviceId;
}
Android introduced a new id to identify a device uniquely, which is called Advertisement_Id. You can get this Id from the below code implementation in you Application class onCreate method.
/** Retrieve the Android Advertising Id
*
* The device must be KitKat (4.4)+
* This method must be invoked from a background thread.
*
* */
public static synchronized String getAdId (Context context) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
return null;
}
AdvertisingIdClient.Info idInfo = null;
try {
idInfo = AdvertisingIdClient.getAdvertisingIdInfo(context);
} catch (GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
} catch (GooglePlayServicesRepairableException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
String advertId = null;
try{
advertId = idInfo.getId();
}catch (NullPointerException e){
e.printStackTrace();
}
return advertId;
}
For Kotlin
fun getAdId() {
//Background Task
AsyncTask.execute {
var adInfo: AdvertisingIdClient.Info? = null
try {
adInfo = AdvertisingIdClient.getAdvertisingIdInfo(applicationContext)
if(adInfo!=null){
val id = adInfo!!.getId()
val isLAT = adInfo!!.isLimitAdTrackingEnabled()
PersistData.setStringData(applicationContext, AppConstant.advertId, id)
val advertId = PersistData.getStringData(applicationContext, AppConstant.advertId)
}
} catch (e: IOException) {
// Unrecoverable error connecting to Google Play services (e.g.,
// the old version of the service doesn't support getting AdvertisingId).
} catch (e: GooglePlayServicesAvailabilityException) {
// Encountered a recoverable error connecting to Google Play services.
} catch (e: GooglePlayServicesNotAvailableException) {
// Google Play services is not available entirely.
}
}
}
As Serial number and IMEI number has been deprecated for Android 10 and onwards ,
So we can find the android id for unique identifier with READ_PRIVILEGED_PHONE_STATE.
for More information please follow below link.
https://developer.android.com/training/articles/user-data-ids
getDeviceId() has been deprecated since API level 26.
"READ_PRIVILEGE_PHONE_STATE" is only accessible by The best practices suggest that you should "Avoid using hardware identifiers." for unique identifiers. You can use an instance id from firebase e.g FirebaseInstanceId.getInstance().getId();
public static String getDeviceId(Context context) {
String deviceId;
if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
deviceId = Settings.Secure.getString(
context.getContentResolver(),
Settings.Secure.ANDROID_ID);
} else {
deviceId =FirebaseInstanceId.getInstance().getId();
}
return deviceId;
}
Use FirebaseInstanceId.getInstance().getId(); for Api level above Android 10
This works for me //import android.provider.Settings
val mId = Settings.Secure.getString(this.contentResolver, Settings.Secure.ANDROID_ID)
I'm try send DTMF codes in icoming CALL. For this i'n try use Java reflection:
public void initialize(){
ClassLoader classLoader = Dtmf.class.getClassLoader();
final Class<?> classCallManager = classLoader.loadClass("com.android.internal.telephony.CallManager");
Method methodGetInstance = classCallManager.getDeclaredMethod("getInstance");
objectCallManager = methodGetInstance.invoke(null);
methodGetState = classCallManager.getDeclaredMethod(SEND_DTMF, char.class);
}
public boolean sendDtmf(char ch) {
boolean result = false;
if ( methodGetState != null) {
try {
Object res = methodGetState.invoke(objectCallManager,
new Object[]{Character.valueOf(ch)});
if (res instanceof Boolean) {
result = ((Boolean) res).booleanValue();
}
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
}
return result;
}
Link for source code of class CallManager : Call Manager source code
But i'm always get "false" in method sendDtmf(). In debug, code is go into next:
Object res = methodGetState.invoke(objectCallManager,
new Object[]{Character.valueOf(ch)});
What wrong?
The method is likely throwing an InvocationTargetException if your application isn't signed with the platform certificate as conventional apps cannot execute these methods (and will not be granted the required platform permissions to do so).
In short: the method is returning false because you're catching (and ignoring) the exception.
There's an open issue (#1428) on the Android issue tracker for sending DTMF tones as it presently isn't possible.
I'm trying to build a plugin-System, where DexClassLoader is fetching code from other installed apks containing fragments(my plugins), and showing them in my host. This is working quite nice.
I also like to make the plugins hotswappable, this means I can change the code from a plugin, install it new and the host will notice and will load the new code. This also works, if I'm changing the code for the first time. (Although I thought it shouldn't, it seems I've got a wrong understanding of this code:
try {
requiredClass = Class.forName(fullName);
} catch(ClassNotFoundException e) {
isLoaded = false;
}
)
If i'm trying it a second time with the same plugin, the host shuts down at requiredClass = classLoader.loadClass(fullName); with something like
libc Fatal signal 7 (SIGBUS) at 0x596ed4d6 (code=2), thread 28814
(ctivityapp.host)
Does anybody has a deeper insight in the functionality of DexClassLoader and may tell me, what is happening here? I'm quite stuck at this.
Heres the full code of the method loading the foreign code:
/**
* takes the name of a package as String, and tries to load the code from the corresponding akp using DexclassLaoder.
* Checking if a package is a valid plugin must be done before calling this.
* The Plugin must contain a public class UI that extends Fragment and implements plugin as a starting point for loading
* #param packageName The full name of the package, as String
* #return the plugins object if loaded, null otherwise
*/
private Plugin attachPluginToHost(String packageName) {
try {
Class<?> requiredClass = null;
final ApplicationInfo info = context.getPackageManager().getApplicationInfo(packageName,0);
final String apkPath = info.sourceDir;
final File dexTemp = context.getDir("temp_folder", 0);
final String fullName = packageName + ".UI";
boolean isLoaded = true;
// Check if class loaded
try {
requiredClass = Class.forName(fullName);
} catch(ClassNotFoundException e) {
isLoaded = false;
}
if (!isLoaded) {
final DexClassLoader classLoader = new DexClassLoader(apkPath, dexTemp.getAbsolutePath(), null, context.getApplicationContext().getClassLoader());
requiredClass = classLoader.loadClass(fullName);
}
if (null != requiredClass) {
// Try to cast to required interface to ensure that it's can be cast
final Plugin plugin = Plugin.class.cast(requiredClass.newInstance());
installedPlugins.put(plugin.getName(), plugin);
return plugin;
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
Many thanks in advance!
Not that it really matters (As nobody is actually viewing this), or that I even understand what's going on, but deleting the corresponding file of the plugin in dexTemp.getAbsolutePath() before reloading it solves the problem.
PS: Tumbleweed-Badge, YAY!
I'm trying to use the SecureRandom workaround that Google posted in my android application:
http://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html
This work around involve writing to (and reading from) /dev/urandom. However, it looks like Samsung has enabled SELinux in such a way that prevents applications from accessing /dev/urandom.
I don't have one of these devices, so it is a little hard for me to test solutions, other than to push out attempts at workarounds on the Android market, but it seems like this is not an error that I can trap with a try catch block. It also appears that File.canRead and canWrite return true. You can see my attempts at workaround in the supportedOnThisDevice method in the following class:
PRNGFixes.java
I'm looking for a reliable way to detect if I am an such a device, and if so, not apply the Google SecureRandom workaround.
This is my way to check if SELinux is in enforce-mode - can be done via any Shell-script, not depending on RootTools:
private static boolean isSELinuxEnforcing() {
try {
CommandCapture command = new CommandCapture(1, "getenforce");
RootTools.getShell(false).add(command).waitForFinish();
boolean isSELinuxEnforcing = command.toString().trim().equalsIgnoreCase("enforcing");
return isSELinuxEnforcing;
} catch (Exception e) {
// handle exception
}
return false;
}
I've heard Samsung is starting to ship devices with the SELinux policy set to Enforce, but I don't know if it's true or not. As far as I know most devices on 4.3 still have it set to permissive.
According to Google, "SELinux reinforcement is invisible to users and developers, and adds robustness to the existing Android security model while maintaining compatibility with existing applications." So you may need to check the system properties or test it through a shell to find out for sure.
If you can get someone to send you their build.prop you may be able to catch it by comparing their ro.build.selinux property via System.getProperty("ro.build.selinux"),
but you'll also want to verify you're able to access it more directly in case it is unreliable or getProperty() for that is broken in future updates.
Root (System user on SELinux) is another option when available, but either way a shell based solution is probably your best bet.
System.getProperty("ro.build.selinux")
Did not work for me on Samsung S4 Android 4.3. So I wrote this
private static final int JELLY_BEAN_MR2 = 18;
public static boolean isSELinuxSupported() {
// Didnt' work
//String selinuxStatus = System.getProperty(PROPERTY_SELINUX_STATUS);
//return selinuxStatus.equals("1") ? true : false;
String selinuxFlag = getSelinuxFlag();
if (!StringUtils.isEmpty(selinuxFlag)) {
return selinuxFlag.equals("1") ? true : false;
} else {
// 4.3 or later ?
if(Build.VERSION.SDK_INT >= JELLY_BEAN_MR2) {
return true;
}
else {
return false;
}
}
}
public static String getSelinuxFlag() {
String selinux = null;
try {
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
selinux = (String) get.invoke(c, "ro.build.selinux");
} catch (Exception ignored) {
}
return selinux;
}
if you have access to the frameworks
import android.os.SELinux;
SELinux.isSELinuxEnforced();
Most devices as as of Jellybean MR2 and onwards will have SELinux enabled on their devices, but if you are working with OEMs or doing platform work this might not necessarily be true.
The method I am using to verify is with the getenforce shell command:
public boolean isSeLinuxEnforcing() {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec("getenforce");
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line);
}
} catch (Exception e) {
Log.e(TAG, "OS does not support getenforce");
// If getenforce is not available to the device, assume the device is not enforcing
e.printStackTrace();
return false;
}
String response = output.toString();
if ("Enforcing".equals(response)) {
return true;
} else if ("Permissive".equals(response)) {
return false;
} else {
Log.e(TAG, "getenforce returned unexpected value, unable to determine selinux!");
// If getenforce is modified on this device, assume the device is not enforcing
return false;
}
}
It appears that most devices are only writing a system property for selinux if they aren't running in enforcing status. You can additionally check the property: ro.boot.selinux to see if the Kernel passed in the permissive parameter on your current build.
I am trying to read WIFI proxy settings
Proxy host
Proxy port
Proxy user (authentication)
Proxy password (authentication)
from devices in android versions 2.X.X – 4.X.X without any success.
Calling:
String proxy = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.HTTP_PROXY);
Always returns null.
I've also added to my android manifest:
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
still it returns null.
Also tried:
android.net.Proxy. getHost(Context ctx) – which is deprecated – returns the IP
android.net.Proxy. getPortt(Context ctx) – which is deprecated – returns always -1.
Java calls:
System.getProperty("http.proxyHost");
System.getProperty("http.proxyCall");
Also returns null.
Is there a working code which retrieves all these settings or at least partially from devices in all android versions?
I found this project: Android Proxy Library
Which provides backward compatible ways of querying Proxy settings as well as setting them for WebViews on older versions of Android.
// Grab Proxy settings in a backwards compatible manner
ProxyConfiguration proxyConfig = ProxySettings.getCurrentHttpProxyConfiguration( context );
// Set Proxy for WebViews on older versions of Android
ProxyUtils.setWebViewProxy( getActivity().getApplicationContext() );
However, there is something you need to understand about Proxy Settings set on a WiFi AP. Since WiFi specific Proxy Settings were not implemented in Android proper until 3.1, all pre-3.1 devices that expose that functionality are using some sort of custom hack. They don't work in any sort of standard way. So libraries like this won't be able to grab any proxy set from one of those hacks.
There is however a System Wide Proxy in pre-3.1 that this sort of library WILL grab. Of course Android saw fit not to provide any official way to modify this setting. But there are apps on the Play Store that will allow you to do it, this is the one I'm using: Proxy Settings and it works well, setting the System Proxy and allowing you to grab it either via this library, or even simpler methods like querying the JVM proxy settings.
I ended up not using the APL and instead went with a much simpler implementation:
private static final boolean IS_ICS_OR_LATER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
...
String proxyAddress;
int proxyPort;
if( IS_ICS_OR_LATER )
{
proxyAddress = System.getProperty( "http.proxyHost" );
String portStr = System.getProperty( "http.proxyPort" );
proxyPort = Integer.parseInt( ( portStr != null ? portStr : "-1" ) );
}
else
{
proxyAddress = android.net.Proxy.getHost( context );
proxyPort = android.net.Proxy.getPort( context );
}
This is what I'm using:
public static String[] getUserProxy(Context context)
{
Method method = null;
try
{
method = ConnectivityManager.class.getMethod("getProxy");
}
catch (NoSuchMethodException e)
{
// Normal situation for pre-ICS devices
return null;
}
catch (Exception e)
{
return null;
}
try
{
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
Object pp = method.invoke(connectivityManager);
if (pp == null)
return null;
return getUserProxy(pp);
}
catch (Exception e)
{
return null;
}
}
private static String[] getUserProxy(Object pp) throws Exception
{
String[] userProxy = new String[3];
String className = "android.net.ProxyProperties";
Class<?> c = Class.forName(className);
Method method;
method = c.getMethod("getHost");
userProxy[0] = (String) method.invoke(pp);
method = c.getMethod("getPort");
userProxy[1] = String.valueOf((Integer) method.invoke(pp));
method = c.getMethod("getExclusionList");
userProxy[2] = (String) method.invoke(pp);
if (userProxy[0] != null)
return userProxy;
else
return null;
}
Following is code snippet to retrieve proxy details
public static String getProxyDetails(Context context) {
String proxyAddress = new String();
try {
if (IsPreIcs()) {
proxyAddress = android.net.Proxy.getHost(context);
if (proxyAddress == null || proxyAddress.equals("")) {
return proxyAddress;
}
proxyAddress += ":" + android.net.Proxy.getPort(context);
} else {
proxyAddress = System.getProperty("http.proxyHost");
proxyAddress += ":" + System.getProperty("http.proxyPort");
}
} catch (Exception ex) {
//ignore
}
return proxyAddress;
}
It'll return enmpty if some exception or no proxy detected;
private fun getUserProxy(context: Context): Data {
return try {
val declaredField = WifiConfiguration::class.java.getDeclaredField("mIpConfiguration")
declaredField.isAccessible = true
val data =
(context.applicationContext.getSystemService(Context.WIFI_SERVICE) as? WifiManager)
?.configuredNetworks
?.asSequence()
?.mapNotNull {
try {
declaredField.get(it)
} catch (e: Exception) {
e.printStackTrace()
null
}
}
?.mapNotNull {
try {
(it.javaClass.getDeclaredField("httpProxy").get(it) as? ProxyInfo)
} catch (e: Exception) {
e.printStackTrace()
null
}
}
?.find { !it.host.isNullOrEmpty() }
?.let { Data(it.host ?: "", it.port.toString()) }
?: Data()
declaredField.isAccessible = false
return data
} catch (e: Exception) {
e.printStackTrace()
Data()
}
}
data class Data(
val host: String = "",
val port: String = ""
)