diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8411d0003..d6464156c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -17,6 +17,7 @@
+
@@ -141,6 +142,15 @@
android:grantUriPermissions="true"
android:exported="true" />
+
+
+
0) {
+ Signature signature = signatures[0];
+ byte[] certBytes = signature.toByteArray();
+ InputStream input = new ByteArrayInputStream(certBytes);
+ try {
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ X509Certificate cert = (X509Certificate) factory.generateCertificate(input);
+ out.name("sign_algorithm").value(cert.getSigAlgName());
+ out.name("valid_from").value(cert.getNotBefore().getTime()/1000);
+ out.name("valid_to").value(cert.getNotAfter().getTime()/1000);
+ out.name("issuer").value(cert.getIssuerDN().getName());
+ out.name("subject").value(cert.getSubjectDN().getName());
+ } catch (CertificateException e) {}
+ }
+ out.endObject();
+ out.endObject();
+ }
+ }
+ });
+ }
+
+ public static void onReceivePMListPackages(TermuxApiReceiver apiReceiver, final Context context, Intent intent) {
+ Logger.logDebug(LOG_TAG, "onReceivePMListPackages");
+ ResultReturner.returnData(apiReceiver, intent, new ResultReturner.ResultJsonWriter() {
+ @Override
+ public void writeJson(JsonWriter out) throws IOException {
+ PackageManager manager = context.getPackageManager();
+ List packages = manager.getInstalledPackages(PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES);
+
+ boolean querySystem = intent.getBooleanExtra("query_system", false);
+ boolean queryAll = intent.getBooleanExtra("query_all", false);
+ String queryName = intent.getStringExtra("query");
+ boolean queryPackageName = intent.getBooleanExtra("query_package_name", false);
+ boolean queryExact = intent.getBooleanExtra("query_exact", false);
+
+ out.beginArray();
+ for (PackageInfo info : packages) {
+ ApplicationInfo appInfo = info.applicationInfo;
+
+ if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && !querySystem && !queryAll) {
+ continue;
+ } else if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 && querySystem && !queryAll) {
+ continue;
+ }
+
+
+ if (queryName != null && !queryName.isEmpty()) {
+ if (!queryExact) {
+ if (!manager.getApplicationLabel(appInfo).toString().contains(queryName) && !queryPackageName) { continue; }
+ if (!info.packageName.contains(queryName) && queryPackageName) { continue; }
+ } else {
+ if (!manager.getApplicationLabel(appInfo).toString().equals(queryName) && !queryPackageName) { continue; }
+ if (!info.packageName.equals(queryName) && queryPackageName) { continue; }
+ }
+ }
+ out.beginObject();
+ out.name("name").value(manager.getApplicationLabel(appInfo).toString());
+ out.name("package").value(info.packageName);
+ out.name("version").value(info.versionName);
+ out.name("version_code").value(info.versionCode);
+ out.name("target_sdk").value(appInfo.targetSdkVersion);
+ out.name("min_sdk").value(appInfo.minSdkVersion);
+ out.name("size").value(new File(appInfo.sourceDir).length());
+ out.name("src").value(appInfo.sourceDir);
+ Intent launchIntent = manager.getLaunchIntentForPackage(info.packageName);
+ if (launchIntent != null) {
+ out.name("launch_activity").value(launchIntent.getComponent().getClassName());
+ }
+ out.name("activities");
+ out.beginArray();
+ ActivityInfo[] activities = info.activities;
+ if (activities != null) {
+ for (ActivityInfo activity : activities) {
+ out.value(activity.name);
+ }
+ }
+ out.endArray();
+ out.name("permissions");
+ out.beginObject();
+ String[] perms = info.requestedPermissions;
+ if (perms != null) {
+ for (String permission : perms) {
+ int state = manager.checkPermission(permission, appInfo.packageName);
+ out.name(permission).value(state == PackageManager.PERMISSION_GRANTED);
+ }
+ }
+ out.endObject();
+
+ out.name("uid").value(appInfo.uid);
+ out.name("debuggable").value((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
+ out.name("process").value(appInfo.processName);
+ out.name("install_location");
+ switch (info.installLocation) {
+ case PackageInfo.INSTALL_LOCATION_AUTO:
+ out.value("auto");
+ break;
+ case PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY:
+ out.value("internal");
+ break;
+ case PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL:
+ out.value("external");
+ break;
+ default:
+ out.value("unknown");
+ break;
+ }
+ out.name("is_system").value((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+ try {
+ String init_installer = null;
+ String installer = null;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ init_installer = manager.getInstallSourceInfo(appInfo.packageName).getInitiatingPackageName();
+ installer = manager.getInstallSourceInfo(appInfo.packageName).getInstallingPackageName();
+
+ } else {
+ installer = manager.getInstallerPackageName(appInfo.packageName);
+ }
+
+ if (installer != null) {
+ out.name("installer").value(installer);
+ }
+ if (init_installer != null) {
+ out.name("init_installer").value(init_installer);
+ }
+
+ } catch (PackageManager.NameNotFoundException e) {
+ // isnt this always true?
+ }
+ Signature[] signatures = info.signatures;
+ out.name("certificate");
+ out.beginObject();
+ if (signatures != null && signatures.length > 0) {
+ Signature signature = signatures[0];
+ byte[] certBytes = signature.toByteArray();
+ try {
+ InputStream input = new ByteArrayInputStream(certBytes);
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ X509Certificate cert = (X509Certificate) factory.generateCertificate(input);
+ out.name("sign_algorithm").value(cert.getSigAlgName());
+ out.name("valid_from").value(cert.getNotBefore().getTime()/1000);
+ out.name("valid_to").value(cert.getNotAfter().getTime()/1000);
+ out.name("issuer").value(cert.getIssuerDN().getName());
+ out.name("subject").value(cert.getSubjectDN().getName());
+ } catch (CertificateException e) {}
+ }
+ out.endObject();
+ out.endObject();
+ }
+ out.endArray();
+
+ }
+ });
+ }
+
+ public static void onReceivePMInstallPackage(TermuxApiReceiver apiReceiver, final Context context, Intent intent) {
+ Logger.logDebug(LOG_TAG, "onReceivePMInstallPackage");
+ ResultReturner.returnData(apiReceiver, intent, new ResultReturner.ResultJsonWriter() {
+ @Override
+ public void writeJson(JsonWriter out) throws IOException {
+ String filePath = intent.getStringExtra("file");
+ if (filePath == null || filePath.isEmpty()) {
+ out.beginObject().name("API_ERROR").value("No file path specified").endObject();
+ return;
+ }
+
+ PackageManager manager = context.getPackageManager();
+
+ File apk = new File(filePath);
+
+ if (!apk.exists()) {
+ out.beginObject().name("API_ERROR").value("The file path does not exist").endObject();
+ return;
+ }
+
+ if (!apk.isFile()){
+ out.beginObject().name("API_ERROR").value("Not a file").endObject();
+ return;
+ }
+
+
+ Intent install = new Intent(Intent.ACTION_INSTALL_PACKAGE);
+
+ install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ Uri apkUri;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ apkUri = FileProvider.getUriForFile(context, context.getPackageName() + ".provider", apk);
+ install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } else {
+ apkUri = Uri.fromFile(apk);
+ }
+
+ install.setDataAndType(apkUri, "application/vnd.android.package-archive");
+ install.putExtra(Intent.EXTRA_REFERRER, Uri.parse("android-app://" + context.getPackageName()));
+ install.putExtra(Intent.EXTRA_ORIGINATING_URI, apkUri);
+
+ context.startActivity(install);
+ }
+ });
+ }
+
+ public static void onReceivePMUninstallPackage(TermuxApiReceiver apiReceiver, final Context context, Intent intent) {
+ Logger.logDebug(LOG_TAG, "onReceivePMUninstallPackage");
+ ResultReturner.returnData(apiReceiver, intent, new ResultReturner.ResultJsonWriter() {
+ @Override
+ public void writeJson(JsonWriter out) throws IOException {
+ String packageName = intent.getStringExtra("package");
+ if (packageName == null || packageName.isEmpty()) {
+ out.beginObject().name("API_ERROR").value("No package name specified").endObject();
+ }
+
+ PackageManager manager = context.getPackageManager();
+ try {
+ ApplicationInfo appInfo = manager.getApplicationInfo(packageName, 0);
+
+ if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ out.beginObject().name("API_ERROR").value("You can't uninstall a system application").endObject();
+ } else {
+ Uri pkgUri = Uri.parse("package:" + packageName);
+
+ Intent uninstall = new Intent(Intent.ACTION_DELETE, pkgUri);
+
+ uninstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ context.startActivity(uninstall);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ out.beginObject().name("API_ERROR").value(packageName + " does not exist or not installed").endObject();
+ }
+ }
+ });
+ }
+}
diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml
new file mode 100644
index 000000000..679d58476
--- /dev/null
+++ b/app/src/main/res/xml/provider_paths.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+