diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/misc/DeviceInfo2.java b/app/src/main/java/io/github/muntashirakon/AppManager/misc/DeviceInfo2.java index 320c525d9a8..bd5bd276f86 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/misc/DeviceInfo2.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/misc/DeviceInfo2.java @@ -2,6 +2,9 @@ package io.github.muntashirakon.AppManager.misc; +import static io.github.muntashirakon.AppManager.utils.UIUtils.getStyledKeyValue; +import static io.github.muntashirakon.AppManager.utils.UIUtils.getTitleText; + import android.annotation.SuppressLint; import android.app.ActivityManager; import android.content.Context; @@ -28,7 +31,6 @@ import com.android.internal.util.TextUtils; -import java.io.BufferedReader; import java.security.Provider; import java.security.Security; import java.text.ParseException; @@ -46,14 +48,9 @@ import io.github.muntashirakon.AppManager.users.UserInfo; import io.github.muntashirakon.AppManager.users.Users; import io.github.muntashirakon.AppManager.utils.Utils; -import io.github.muntashirakon.io.Path; -import io.github.muntashirakon.io.PathReader; -import io.github.muntashirakon.io.Paths; +import io.github.muntashirakon.proc.ProcFs; import io.github.muntashirakon.util.LocalizedString; -import static io.github.muntashirakon.AppManager.utils.UIUtils.getStyledKeyValue; -import static io.github.muntashirakon.AppManager.utils.UIUtils.getTitleText; - public class DeviceInfo2 implements LocalizedString { public final String osVersion = Build.VERSION.RELEASE; public final String bootloader = Build.BOOTLOADER; @@ -364,21 +361,7 @@ private String getEncryptionStatus() { @Nullable private String getCpuHardware() { - Path cpuInfoPath = Paths.getUnprivileged("/proc/cpuinfo"); - try (BufferedReader reader = new BufferedReader(new PathReader(cpuInfoPath))) { - String line; - while ((line = reader.readLine()) != null) { - if (line.trim().startsWith("Hardware")) { - int colonLoc = line.indexOf(':'); - if (colonLoc == -1) continue; - colonLoc += 2; - return line.substring(colonLoc).trim(); - } - } - } catch (Throwable e) { - e.printStackTrace(); - } - return null; + return ProcFs.getInstance().getCpuInfoHardware(); } @SuppressLint("PrivateApi") diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/utils/FileUtils.java b/app/src/main/java/io/github/muntashirakon/AppManager/utils/FileUtils.java index ed526c1b892..40fee083015 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/utils/FileUtils.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/utils/FileUtils.java @@ -2,6 +2,13 @@ package io.github.muntashirakon.AppManager.utils; +import static android.system.OsConstants.O_ACCMODE; +import static android.system.OsConstants.O_APPEND; +import static android.system.OsConstants.O_RDONLY; +import static android.system.OsConstants.O_RDWR; +import static android.system.OsConstants.O_TRUNC; +import static android.system.OsConstants.O_WRONLY; + import android.content.Context; import android.content.res.AssetFileDescriptor; import android.os.Environment; @@ -213,4 +220,24 @@ public static boolean canRead(@NonNull File file) { } return false; } + + public static String translateModePosixToString(int mode) { + String res = ""; + if ((mode & O_ACCMODE) == O_RDWR) { + res = "rw"; + } else if ((mode & O_ACCMODE) == O_WRONLY) { + res = "w"; + } else if ((mode & O_ACCMODE) == O_RDONLY) { + res = "r"; + } else { + throw new IllegalArgumentException("Bad mode: " + mode); + } + if ((mode & O_TRUNC) == O_TRUNC) { + res += "t"; + } + if ((mode & O_APPEND) == O_APPEND) { + res += "a"; + } + return res; + } } diff --git a/app/src/main/java/io/github/muntashirakon/proc/ProcFdInfoList.java b/app/src/main/java/io/github/muntashirakon/proc/ProcFdInfoList.java new file mode 100644 index 00000000000..133df284bb7 --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/proc/ProcFdInfoList.java @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package io.github.muntashirakon.proc; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import io.github.muntashirakon.AppManager.utils.FileUtils; +import io.github.muntashirakon.io.Path; + +public class ProcFdInfoList { + public static class ProcFdInfo { + public final int id; + public final long offset; + public final int mode; + public final int mountId; + @NonNull + public final Path realPath; + @NonNull + public final String[] extras; + + private ProcFdInfo(int id, @NonNull Path realPath, @NonNull String[] info) { + this.id = id; + this.realPath = realPath; + this.extras = new String[info.length - 3]; // Ignore three mandatory fields + int i = 0; + long offset = -1; + int mode = -1; + int mountId = -1; + for (String line : info) { + if (line.startsWith("pos:")) { + offset = Long.decode(line.substring(4).trim()); + } else if (line.startsWith("flags:")) { + mode = Integer.decode(line.substring(6).trim()); + } else if (line.startsWith("mnt_id:")) { + mountId = Integer.decode(line.substring(7).trim()); + } else { + extras[i++] = line; + } + } + assert offset != -1; + assert mode != -1; + assert mountId != -1; + + this.offset = offset; + this.mode = mode; + this.mountId = mountId; + } + + public String getModeString() { + return FileUtils.translateModePosixToString(mode); + } + } + + private final Map mFdInfoMap; + + ProcFdInfoList(@NonNull Path[] fdFiles, @NonNull String[] fdInfoList) { + assert fdFiles.length == fdInfoList.length; + mFdInfoMap = new HashMap<>(fdFiles.length); + for (int i = 0; i < fdFiles.length; ++i) { + String fdInfo = fdInfoList[i]; + if (fdInfo == null) { + // FD no longer exists + continue; + } + Path fdFile = fdFiles[i]; + int fd = Integer.decode(fdFile.getName()); + ProcFdInfo procFdInfo = new ProcFdInfo(fd, fdFile, fdInfo.split("\\n")); + mFdInfoMap.put(fd, procFdInfo); + } + } + + public Collection getFds() { + return mFdInfoMap.keySet(); + } + + @Nullable + public ProcFdInfo getFdInfo(int fd) { + return mFdInfoMap.get(fd); + } +} diff --git a/app/src/main/java/io/github/muntashirakon/proc/ProcFs.java b/app/src/main/java/io/github/muntashirakon/proc/ProcFs.java index fdeb8f00402..398a8cd80f9 100644 --- a/app/src/main/java/io/github/muntashirakon/proc/ProcFs.java +++ b/app/src/main/java/io/github/muntashirakon/proc/ProcFs.java @@ -10,7 +10,11 @@ import com.android.internal.util.TextUtils; +import java.io.BufferedReader; +import java.io.IOException; + import io.github.muntashirakon.io.Path; +import io.github.muntashirakon.io.PathReader; import io.github.muntashirakon.io.Paths; public class ProcFs { @@ -90,6 +94,30 @@ public ProcFs(Path procRoot) { this.procRoot = procRoot; } + @Nullable + public String getCpuInfoHardware() { + // Only hardware is the relevant output, other outputs can be parsed from + // /sys/devices/system/cpu/ + Path cpuInfoPath = Paths.build(procRoot, CPU_INFO); + if (cpuInfoPath == null) { + return null; + } + try (BufferedReader reader = new BufferedReader(new PathReader(cpuInfoPath))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.trim().startsWith("Hardware")) { + int colonLoc = line.indexOf(':'); + if (colonLoc == -1) continue; + colonLoc += 2; + return line.substring(colonLoc).trim(); + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + return null; + } + public ProcMemoryInfo getMemoryInfo() { String statFileContents = getStringOrNull(Paths.build(procRoot, MEM_INFO)); if (statFileContents == null) { @@ -124,6 +152,58 @@ public String getCmdline(int pid) { return getStringOrNull(Paths.build(procRoot, String.valueOf(pid), CMDLINE)); } + @Nullable + public String getCwd(int pid) { + Path cwdPath = Paths.build(procRoot, String.valueOf(pid), CWD); + if (cwdPath != null) { + try { + return cwdPath.getRealFilePath(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return null; + } + + public String[] getEnvVars(int pid) { + String data = getStringOrNull(Paths.build(procRoot, String.valueOf(pid), ENVIRON)); + return data != null ? data.split("\0") : null; + } + + @Nullable + public String getExe(int pid) { + Path exePath = Paths.build(procRoot, String.valueOf(pid), EXE); + if (exePath != null) { + try { + return exePath.getRealFilePath(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return null; + } + + public ProcFdInfoList getFdInfo(int pid) { + Path fdDir = Paths.build(procRoot, String.valueOf(pid), FD); + Path fdInfoDir = Paths.build(procRoot, String.valueOf(pid), FD_INFO); + if (fdDir == null || fdInfoDir == null) { + return null; + } + Path[] fdList = fdDir.listFiles(); + String[] fdInfoList = new String[fdList.length]; + for (int i = 0; i < fdList.length; ++i) { + Path fdInfoFile = Paths.build(fdInfoDir, fdList[i].getName()); + fdInfoList[i] = fdInfoFile != null ? fdInfoFile.getContentAsString(null) : null; + } + return new ProcFdInfoList(fdList, fdInfoList); + } + + @Nullable + public ProcMappedFiles getMapFiles(int pid) { + Path mapFilesDir = Paths.build(procRoot, String.valueOf(pid), MAP_FILES); + return mapFilesDir != null ? new ProcMappedFiles(mapFilesDir.listFiles()) : null; + } + @Nullable public ProcStat getStat(int pid) { String statFileContents = getStringOrNull(Paths.build(procRoot, String.valueOf(pid), STAT)); @@ -142,6 +222,18 @@ public ProcMemStat getMemStat(int pid) { return ProcMemStat.parse(statFileContents); } + @Nullable + public String getRoot(int pid) { + Path rootPath = Paths.build(procRoot, String.valueOf(pid), ROOT); + if (rootPath != null) { + try { + return rootPath.getRealFilePath(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return null; + } @Nullable public ProcStatus getStatus(int pid) { String statFileContents = getStringOrNull(Paths.build(procRoot, String.valueOf(pid), STATUS)); diff --git a/app/src/main/java/io/github/muntashirakon/proc/ProcMappedFiles.java b/app/src/main/java/io/github/muntashirakon/proc/ProcMappedFiles.java new file mode 100644 index 00000000000..d442dcd9370 --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/proc/ProcMappedFiles.java @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package io.github.muntashirakon.proc; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import io.github.muntashirakon.io.Path; +import kotlin.collections.ArrayDeque; + +public class ProcMappedFiles { + public static class MappedFile { + public final Path memoryPath; + public final String realPath; + public final long vmStart; + public final long vmEnd; + + private MappedFile(@NonNull Path memoryPath, @NonNull String realPath) { + this.memoryPath = memoryPath; + this.realPath = realPath; + String[] vmAreaStruct = memoryPath.getName().split("-"); + vmStart = Long.decode("0x" + vmAreaStruct[0]); + vmEnd = Long.decode("0x" + vmAreaStruct[1]); + } + } + + private final Map> mMappedFiles; + + ProcMappedFiles(@NonNull Path[] mappedFiles) { + mMappedFiles = new HashMap<>(mappedFiles.length); + for (Path file : mappedFiles) { + try { + String realPath = Objects.requireNonNull(file.getRealFilePath()); + MappedFile mappedFile = new MappedFile(file, realPath); + List mappedFileList = mMappedFiles.get(realPath); + if (mappedFileList == null) { + mappedFileList = new ArrayDeque<>(); + mMappedFiles.put(realPath, mappedFileList); + } + mappedFileList.add(mappedFile); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @NonNull + public Collection getRealFiles() { + return mMappedFiles.keySet(); + } + + @Nullable + public List getMappedFiles(@NonNull String realPath) { + return mMappedFiles.get(realPath); + } +}