Rick Li
Rick Li

Reputation: 1477

Is there a way to get current process name in Android

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.

Upvotes: 53

Views: 49439

Answers (11)

Sam
Sam

Reputation: 42357

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

Upvotes: 36

ultraon
ultraon

Reputation: 2400

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
    }

Upvotes: 5

Johnson_145
Johnson_145

Reputation: 2032

Since Android Pie (SDK v28), there is actually an official method for this in the Application class:

public static String getProcessName ()

See the docs

Upvotes: 2

C.L. Wang
C.L. Wang

Reputation: 29

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;
}

Upvotes: 2

user10318067
user10318067

Reputation: 1

The main process's father process should be zygote, this should be the accurate solution

  1. first judge the process's name from /proc/pid/cmdline which should equal to package name
  2. judge the process's father whether Zygote(why do this? because some APP have different processes with same name)

Upvotes: 0

Rick Li
Rick Li

Reputation: 1477

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;
        }
    }

Upvotes: 34

wukong
wukong

Reputation: 2597

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")

Upvotes: 1

Snow Albert
Snow Albert

Reputation: 577

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;

Upvotes: 4

Mark Renouf
Mark Renouf

Reputation: 30990

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);
  }
}

Upvotes: 4

David Burstr&#246;m
David Burstr&#246;m

Reputation: 1610

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.

Upvotes: 29

Richard Horrocks
Richard Horrocks

Reputation: 439

If I've understood your question correctly, you should be able to use ActivityManager, as per this thread.

Upvotes: 1

Related Questions