52

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.

Chelseawillrecover
  • 2,596
  • 1
  • 31
  • 51
Rick Li
  • 1,457
  • 2
  • 14
  • 19
  • 1
    Would you consider changing the accepted answer to [this one](https://stackoverflow.com/a/55842542/238753)? It gets the process name the same way the the OS does, and I think it's faster and more reliable than the other answers. Thanks – Sam Feb 08 '20 at 00:40

11 Answers11

33

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;
        }
    }
Sufian
  • 6,405
  • 16
  • 66
  • 120
Rick Li
  • 1,457
  • 2
  • 14
  • 19
33

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
Sam
  • 40,644
  • 36
  • 176
  • 219
29

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.

David Burström
  • 1,592
  • 14
  • 14
  • 5
    This is likely because your process is a remote service. If you call am.getRunningServices() and iterate through the RunningServiceInfo objects, you'll find your service process as well as the name you gave your process in the manifest in the .process field of the info object – Mike Venzke Aug 20 '14 at 18:19
  • 1
    What exactly happens that causes this to not work when the user runs Xposed? – Sam Sep 02 '15 at 09:38
  • 1
    Then /proc/your_pid/cmdline becomes unreadable even for yourself. – David Burström Sep 03 '15 at 12:37
  • Thanks! Did you ever find more info about the issue of Activity Manager not returning your process? Was Mike's suspicion above correct - that it was related to your service being in a separate process? – Sam Sep 13 '15 at 20:35
  • Yes, that was the case. – David Burström Sep 14 '15 at 14:29
  • @DavidBurström I don't have a device running Xposed so I can check this my self - Does the whole `/proc/*` directory structure becomes unreadable? or only parts of it? – guy.gc Sep 15 '15 at 14:28
  • @MikeVenzke, do you have any ideas about why the process for a running remote service sometimes isn't included in `getRunningAppProcesses`? – Sam Sep 16 '15 at 11:35
  • I just tested Xposed v74 for Lollipop on my Sony Xperia M running Cyanogenmod 12.1, and the code in this answer worked fine. – Sam Sep 16 '15 at 11:53
  • We saw it two years ago or so in crash reports, so it could have been fixed in Xposed so that you're allowed to read your own process information. – David Burström Sep 17 '15 at 08:56
  • may get unexpected process name with unreadable char. we should use 'ActivityManager#getRunningAppProcesses' api – hoot Nov 02 '16 at 02:04
  • I just found that the ACRA library does this using `"/proc/self/cmdline"` instead of `"/proc/" + Process.myPid() + "/cmdline"`. Anyone know if one approach is safer than the other? – Sam Mar 10 '19 at 22:23
5

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
    }
ultraon
  • 2,220
  • 2
  • 28
  • 27
4

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);
  }
}
Mark Renouf
  • 30,697
  • 19
  • 94
  • 123
  • Please note that your snippet is assuming that there will be no arguments to the process. That's why my code snippet checks for null characters: http://man7.org/linux/man-pages/man5/proc.5.html – David Burström Nov 12 '18 at 14:27
4

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;

Snow Albert
  • 547
  • 7
  • 15
2

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;
}
weston
  • 54,145
  • 21
  • 145
  • 203
C.L. Wang
  • 29
  • 3
  • Welcome to StackOverflow! Generally, answers are much more helpful if they include an explanation of what the code is intended to do, and why that solves the problem without introducing others. – Dan Cornilescu Apr 25 '16 at 02:55
2

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

public static String getProcessName ()

See the docs

Johnson_145
  • 1,994
  • 1
  • 17
  • 26
  • Nice find! I've done a comprehensive write-up about this approach in [my answer](https://stackoverflow.com/a/55842542/238753). – Sam Apr 25 '19 at 05:46
1

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

Community
  • 1
  • 1
Richard Horrocks
  • 419
  • 3
  • 19
1

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")
wukong
  • 2,430
  • 2
  • 26
  • 33
  • Nice find! Note that the method is called `currentPackageName()` prior to API 18. I've done a comprehensive write-up about this approach in [my answer](https://stackoverflow.com/a/55842542/238753). – Sam Apr 25 '19 at 05:45
0

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)