termux-app
270 строк · 12.8 Кб
1package com.termux.shared.android;
2
3import android.annotation.SuppressLint;
4import android.content.Context;
5import android.content.pm.ApplicationInfo;
6import android.content.pm.PackageInfo;
7import android.os.Build;
8
9import androidx.annotation.NonNull;
10
11import com.google.common.base.Joiner;
12import com.termux.shared.R;
13import com.termux.shared.data.DataUtils;
14import com.termux.shared.logger.Logger;
15import com.termux.shared.markdown.MarkdownUtils;
16
17import java.io.BufferedReader;
18import java.io.IOException;
19import java.io.InputStream;
20import java.io.InputStreamReader;
21import java.text.SimpleDateFormat;
22import java.util.Date;
23import java.util.Properties;
24import java.util.TimeZone;
25import java.util.regex.Matcher;
26import java.util.regex.Pattern;
27
28public class AndroidUtils {
29
30/**
31* Get a markdown {@link String} for the app info for the package associated with the {@code context}.
32* This will contain additional info about the app in addition to the one returned by
33* {@link #getAppInfoMarkdownString(Context, String)}, which will be got via the {@code context}
34* object.
35*
36* @param context The context for operations for the package.
37* @return Returns the markdown {@link String}.
38*/
39public static String getAppInfoMarkdownString(@NonNull final Context context) {
40StringBuilder markdownString = new StringBuilder();
41
42String appInfo = getAppInfoMarkdownString(context, context.getPackageName());
43if (appInfo == null)
44return markdownString.toString();
45else
46markdownString.append(appInfo);
47
48String filesDir = context.getFilesDir().getAbsolutePath();
49if (!filesDir.equals("/data/user/0/" + context.getPackageName() + "/files") &&
50!filesDir.equals("/data/data/" + context.getPackageName() + "/files"))
51AndroidUtils.appendPropertyToMarkdown(markdownString,"FILES_DIR", filesDir);
52
53
54if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
55Long userId = PackageUtils.getUserIdForPackage(context);
56if (userId == null || userId != 0)
57AndroidUtils.appendPropertyToMarkdown(markdownString, "USER_ID", userId);
58}
59
60AndroidUtils.appendPropertyToMarkdownIfSet(markdownString,"PROFILE_OWNER", PackageUtils.getProfileOwnerPackageNameForUser(context));
61
62return markdownString.toString();
63}
64
65/**
66* Get a markdown {@link String} for the app info for the {@code packageName}.
67*
68* @param context The {@link Context} for operations.
69* @param packageName The package name of the package.
70* @return Returns the markdown {@link String}.
71*/
72public static String getAppInfoMarkdownString(@NonNull final Context context, @NonNull final String packageName) {
73PackageInfo packageInfo = PackageUtils.getPackageInfoForPackage(context, packageName);
74if (packageInfo == null) return null;
75ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoForPackage(context, packageName);
76if (applicationInfo == null) return null;
77
78StringBuilder markdownString = new StringBuilder();
79
80AndroidUtils.appendPropertyToMarkdown(markdownString,"APP_NAME", PackageUtils.getAppNameForPackage(context, applicationInfo));
81AndroidUtils.appendPropertyToMarkdown(markdownString,"PACKAGE_NAME", PackageUtils.getPackageNameForPackage(applicationInfo));
82AndroidUtils.appendPropertyToMarkdown(markdownString,"VERSION_NAME", PackageUtils.getVersionNameForPackage(packageInfo));
83AndroidUtils.appendPropertyToMarkdown(markdownString,"VERSION_CODE", PackageUtils.getVersionCodeForPackage(packageInfo));
84AndroidUtils.appendPropertyToMarkdown(markdownString,"UID", PackageUtils.getUidForPackage(applicationInfo));
85AndroidUtils.appendPropertyToMarkdown(markdownString,"TARGET_SDK", PackageUtils.getTargetSDKForPackage(applicationInfo));
86AndroidUtils.appendPropertyToMarkdown(markdownString,"IS_DEBUGGABLE_BUILD", PackageUtils.isAppForPackageADebuggableBuild(applicationInfo));
87
88if (PackageUtils.isAppInstalledOnExternalStorage(applicationInfo)) {
89AndroidUtils.appendPropertyToMarkdown(markdownString,"APK_PATH", PackageUtils.getBaseAPKPathForPackage(applicationInfo));
90AndroidUtils.appendPropertyToMarkdown(markdownString,"IS_INSTALLED_ON_EXTERNAL_STORAGE", true);
91}
92
93AndroidUtils.appendPropertyToMarkdown(markdownString,"SE_PROCESS_CONTEXT", SELinuxUtils.getContext());
94AndroidUtils.appendPropertyToMarkdown(markdownString,"SE_FILE_CONTEXT", SELinuxUtils.getFileContext(context.getFilesDir().getAbsolutePath()));
95
96String seInfoUser = PackageUtils.getApplicationInfoSeInfoUserForPackage(applicationInfo);
97AndroidUtils.appendPropertyToMarkdown(markdownString,"SE_INFO", PackageUtils.getApplicationInfoSeInfoForPackage(applicationInfo) +
98(DataUtils.isNullOrEmpty(seInfoUser) ? "" : seInfoUser));
99
100return markdownString.toString();
101}
102
103public static String getDeviceInfoMarkdownString(@NonNull final Context context) {
104return getDeviceInfoMarkdownString(context, false);
105}
106
107/**
108* Get a markdown {@link String} for the device info.
109*
110* @param context The context for operations.
111* @param addPhantomProcessesInfo If phantom processes info should be added on Android >= 12.
112* @return Returns the markdown {@link String}.
113*/
114public static String getDeviceInfoMarkdownString(@NonNull final Context context, boolean addPhantomProcessesInfo) {
115// Some properties cannot be read with {@link System#getProperty(String)} but can be read
116// directly by running getprop command
117Properties systemProperties = getSystemProperties();
118
119StringBuilder markdownString = new StringBuilder();
120
121markdownString.append("## Device Info");
122
123markdownString.append("\n\n### Software\n");
124appendPropertyToMarkdown(markdownString,"OS_VERSION", getSystemPropertyWithAndroidAPI("os.version"));
125appendPropertyToMarkdown(markdownString, "SDK_INT", Build.VERSION.SDK_INT);
126// If its a release version
127if ("REL".equals(Build.VERSION.CODENAME))
128appendPropertyToMarkdown(markdownString, "RELEASE", Build.VERSION.RELEASE);
129else
130appendPropertyToMarkdown(markdownString, "CODENAME", Build.VERSION.CODENAME);
131appendPropertyToMarkdown(markdownString, "ID", Build.ID);
132appendPropertyToMarkdown(markdownString, "DISPLAY", Build.DISPLAY);
133appendPropertyToMarkdown(markdownString, "INCREMENTAL", Build.VERSION.INCREMENTAL);
134appendPropertyToMarkdownIfSet(markdownString, "SECURITY_PATCH", systemProperties.getProperty("ro.build.version.security_patch"));
135appendPropertyToMarkdownIfSet(markdownString, "IS_DEBUGGABLE", systemProperties.getProperty("ro.debuggable"));
136appendPropertyToMarkdownIfSet(markdownString, "IS_EMULATOR", systemProperties.getProperty("ro.boot.qemu"));
137appendPropertyToMarkdownIfSet(markdownString, "IS_TREBLE_ENABLED", systemProperties.getProperty("ro.treble.enabled"));
138appendPropertyToMarkdown(markdownString, "TYPE", Build.TYPE);
139appendPropertyToMarkdown(markdownString, "TAGS", Build.TAGS);
140
141// If on Android >= 12
142if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
143Integer maxPhantomProcesses = PhantomProcessUtils.getActivityManagerMaxPhantomProcesses(context);
144if (maxPhantomProcesses != null)
145appendPropertyToMarkdown(markdownString, "MAX_PHANTOM_PROCESSES", maxPhantomProcesses);
146else
147appendLiteralPropertyToMarkdown(markdownString, "MAX_PHANTOM_PROCESSES", "- (*" + context.getString(R.string.msg_requires_dump_and_package_usage_stats_permissions) + "*)");
148
149appendPropertyToMarkdown(markdownString, "MONITOR_PHANTOM_PROCS", PhantomProcessUtils.getFeatureFlagMonitorPhantomProcsValueString(context).getName());
150appendPropertyToMarkdown(markdownString, "DEVICE_CONFIG_SYNC_DISABLED", PhantomProcessUtils.getSettingsGlobalDeviceConfigSyncDisabled(context));
151}
152
153markdownString.append("\n\n### Hardware\n");
154appendPropertyToMarkdown(markdownString, "MANUFACTURER", Build.MANUFACTURER);
155appendPropertyToMarkdown(markdownString, "BRAND", Build.BRAND);
156appendPropertyToMarkdown(markdownString, "MODEL", Build.MODEL);
157appendPropertyToMarkdown(markdownString, "PRODUCT", Build.PRODUCT);
158appendPropertyToMarkdown(markdownString, "BOARD", Build.BOARD);
159appendPropertyToMarkdown(markdownString, "HARDWARE", Build.HARDWARE);
160appendPropertyToMarkdown(markdownString, "DEVICE", Build.DEVICE);
161appendPropertyToMarkdown(markdownString, "SUPPORTED_ABIS", Joiner.on(", ").skipNulls().join(Build.SUPPORTED_ABIS));
162
163markdownString.append("\n##\n");
164
165return markdownString.toString();
166}
167
168
169
170public static Properties getSystemProperties() {
171Properties systemProperties = new Properties();
172
173// getprop commands returns values in the format `[key]: [value]`
174// Regex matches string starting with a literal `[`,
175// followed by one or more characters that do not match a closing square bracket as the key,
176// followed by a literal `]: [`,
177// followed by one or more characters as the value,
178// followed by string ending with literal `]`
179// multiline values will be ignored
180Pattern propertiesPattern = Pattern.compile("^\\[([^]]+)]: \\[(.+)]$");
181
182try {
183Process process = new ProcessBuilder()
184.command("/system/bin/getprop")
185.redirectErrorStream(true)
186.start();
187
188InputStream inputStream = process.getInputStream();
189BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
190String line, key, value;
191
192while ((line = bufferedReader.readLine()) != null) {
193Matcher matcher = propertiesPattern.matcher(line);
194if (matcher.matches()) {
195key = matcher.group(1);
196value = matcher.group(2);
197if (key != null && value != null && !key.isEmpty() && !value.isEmpty())
198systemProperties.put(key, value);
199}
200}
201
202bufferedReader.close();
203process.destroy();
204
205} catch (IOException e) {
206Logger.logStackTraceWithMessage("Failed to get run \"/system/bin/getprop\" to get system properties.", e);
207}
208
209//for (String key : systemProperties.stringPropertyNames()) {
210// Logger.logVerbose(key + ": " + systemProperties.get(key));
211//}
212
213return systemProperties;
214}
215
216public static String getSystemPropertyWithAndroidAPI(@NonNull String property) {
217try {
218return System.getProperty(property);
219} catch (Exception e) {
220Logger.logVerbose("Failed to get system property \"" + property + "\":" + e.getMessage());
221return null;
222}
223}
224
225public static void appendPropertyToMarkdownIfSet(StringBuilder markdownString, String label, Object value) {
226if (value == null) return;
227if (value instanceof String && (((String) value).isEmpty()) || "REL".equals(value)) return;
228markdownString.append("\n").append(getPropertyMarkdown(label, value));
229}
230
231public static void appendPropertyToMarkdown(StringBuilder markdownString, String label, Object value) {
232markdownString.append("\n").append(getPropertyMarkdown(label, value));
233}
234
235public static String getPropertyMarkdown(String label, Object value) {
236return MarkdownUtils.getSingleLineMarkdownStringEntry(label, value, "-");
237}
238
239public static void appendLiteralPropertyToMarkdown(StringBuilder markdownString, String label, Object value) {
240markdownString.append("\n").append(getLiteralPropertyMarkdown(label, value));
241}
242
243public static String getLiteralPropertyMarkdown(String label, Object value) {
244return MarkdownUtils.getLiteralSingleLineMarkdownStringEntry(label, value, "-");
245}
246
247
248
249public static String getCurrentTimeStamp() {
250@SuppressLint("SimpleDateFormat")
251final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
252df.setTimeZone(TimeZone.getTimeZone("UTC"));
253return df.format(new Date());
254}
255
256public static String getCurrentMilliSecondUTCTimeStamp() {
257@SuppressLint("SimpleDateFormat")
258final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z");
259df.setTimeZone(TimeZone.getTimeZone("UTC"));
260return df.format(new Date());
261}
262
263public static String getCurrentMilliSecondLocalTimeStamp() {
264@SuppressLint("SimpleDateFormat")
265final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss.SSS");
266df.setTimeZone(TimeZone.getDefault());
267return df.format(new Date());
268}
269
270}
271