/** * Display firewall rules and interface info * * Copyright (C) 2011-2013 Kevin Cernekee * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * @author Kevin Cernekee * @version 1.0 */ package dev.ukanth.ufirewall.activity; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.net.ConnectivityManager; import android.os.AsyncTask; import android.os.Bundle; import android.view.MenuItem; import android.view.SubMenu; import com.afollestad.materialdialogs.MaterialDialog; import java.io.File; import java.util.Map; import java.util.TreeSet; import dev.ukanth.ufirewall.Api; import dev.ukanth.ufirewall.InterfaceDetails; import dev.ukanth.ufirewall.InterfaceTracker; import dev.ukanth.ufirewall.R; import dev.ukanth.ufirewall.service.RootShell.RootCommand; import dev.ukanth.ufirewall.log.Log; import dev.ukanth.ufirewall.util.G; public class RulesActivity extends DataDumpActivity { protected static final int MENU_FLUSH_RULES = 12; protected static final int MENU_IPV6_RULES = 19; protected static final int MENU_IPV4_RULES = 20; protected static final int MENU_SEND_REPORT = 25; protected boolean showIPv6 = false; protected static StringBuilder result; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setTitle(getString(R.string.showrules_title)); sdDumpFile = "rules.log"; } protected void populateMenu(SubMenu sub) { if (G.enableIPv6()) { sub.add(0, MENU_IPV6_RULES, 0, R.string.switch_ipv6).setIcon(R.drawable.rules); sub.add(0, MENU_IPV4_RULES, 0, R.string.switch_ipv4).setIcon(R.drawable.rules); } sub.add(0, MENU_FLUSH_RULES, 0, R.string.flush).setIcon(R.drawable.clearlog); sub.add(0, MENU_SEND_REPORT, 0, R.string.send_report).setIcon(R.drawable.ic_dialog_email); } private void writeHeading(StringBuilder res, boolean initialNewline, String title) { StringBuilder eq = new StringBuilder(); for (int i = 0; i < title.length(); i++) { eq.append('='); } if (initialNewline) { res.append("\n"); } res.append(eq + "\n" + title + "\n" + eq + "\n\n"); } protected void appendPreferences(final Context ctx) { // Fifth section: "Preferences" writeHeading(result, true, "Preferences"); try { Map<String,?> prefs = G.gPrefs.getAll(); for (String s : new TreeSet<String>(prefs.keySet())) { Object entry = prefs.get(s); result.append(s + ": " + entry.toString() + "\n"); } } catch (NullPointerException e) { result.append("Error retrieving preferences\n"); } // Sixth section: "Logcat" writeHeading(result, true, "Logcat"); result.append(Log.getLog()); // finished: post result to the user setData(result.toString()); } protected String getFileInfo(String filename) { File f = new File(filename); if (f.exists() && f.isFile()) { return filename + ": " + f.length() + " bytes\n"; } else { return filename + ": not present\n"; } } protected String getSuInfo(PackageManager pm) { String suPackages[] = { "com.koushikdutta.superuser", "com.noshufou.android.su", "com.noshufou.android.su.elite", "com.koushikdutta.superuser", "com.kingroot.kinguser", "com.kingroot.master", "com.kingouser.com", "com.m0narx.su", "com.miui.uac", "eu.chainfire.supersu", "eu.chainfire.supersu.pro" }; String found = "none found"; for (String s : suPackages) { try { PackageInfo info = pm.getPackageInfo(s, 0); found = s + " v" + info.versionName; break; } catch (NameNotFoundException e) { } } return found; } protected void appendSystemInfo(final Context ctx) { // Fourth section: "System info" writeHeading(result, true, "System info"); InterfaceDetails cfg = InterfaceTracker.getCurrentCfg(ctx); result.append("Android version: " + android.os.Build.VERSION.RELEASE + "\n"); result.append("Manufacturer: " + android.os.Build.MANUFACTURER + "\n"); result.append("Model: " + android.os.Build.MODEL + "\n"); result.append("Build: " + android.os.Build.DISPLAY + "\n"); if (cfg.netType == ConnectivityManager.TYPE_MOBILE) { result.append("Active interface: mobile\n"); } else if (cfg.netType == ConnectivityManager.TYPE_WIFI) { result.append("Active interface: wifi\n"); } else { result.append("Active interface: unknown\n"); } result.append("Tether status: " + (cfg.tetherStatusKnown ? (cfg.isTethered ? "yes" : "no") : "unknown") + "\n"); result.append("Roam status: " + (cfg.isRoaming ? "yes" : "no") + "\n"); result.append("IPv4 subnet: " + cfg.lanMaskV4 + "\n"); result.append("IPv6 subnet: " + cfg.lanMaskV6 + "\n"); // filesystem calls can block, so run in another thread new AsyncTask<Void, Void, String>() { @Override public String doInBackground(Void... args) { StringBuilder ret = new StringBuilder(); ret.append(getFileInfo("/system/bin/su")); ret.append(getFileInfo("/system/xbin/su")); ret.append(getFileInfo("/system/app/Superuser.apk")); PackageManager pm = ctx.getPackageManager(); ret.append("Superuser: " + getSuInfo(pm)); ret.append("\n"); return ret.toString(); } @Override public void onPostExecute(String suInfo) { result.append(suInfo); appendPreferences(ctx); } }.execute(); } protected void appendIfconfig(final Context ctx) { // Third section: "ifconfig" (for interface info obtained through busybox) writeHeading(result, true, "ifconfig"); Api.runIfconfig(ctx, new RootCommand() .setLogging(true) .setCallback(new RootCommand.Callback() { public void cbFunc(RootCommand state) { result.append(state.res); appendSystemInfo(ctx); } })); } protected void appendNetworkInterfaces(final Context ctx) { // Second section: "Network Interfaces" (for interface info obtained through Android APIs) writeHeading(result, true, "Network interfaces"); new AsyncTask<Void, Void, String>() { @Override public String doInBackground(Void... args) { StringBuilder ret = new StringBuilder(); // filesystem calls can block, so run in another thread for (String s : Api.interfaceInfo(true)) { ret.append(s + "\n"); } return ret.toString(); } @Override public void onPostExecute(String ifaceList) { result.append(ifaceList); appendIfconfig(ctx); } }.execute(); } protected void populateData(final Context ctx) { result = new StringBuilder(); // First section: "IPxx Rules" writeHeading(result, false, showIPv6 ? "IPv6 Rules" : "IPv4 Rules"); Api.fetchIptablesRules(ctx, showIPv6, new RootCommand() .setLogging(true) .setReopenShell(true) .setFailureToast(R.string.error_fetch) .setCallback(new RootCommand.Callback() { public void cbFunc(RootCommand state) { result.append(state.res); appendNetworkInterfaces(ctx); } })); } @Override public boolean onOptionsItemSelected(MenuItem item) { final Context ctx = this; switch (item.getItemId()) { case android.R.id.home: { onBackPressed(); return true; } case MENU_FLUSH_RULES: flushAllRules(ctx); return true; case MENU_IPV6_RULES: showIPv6 = true; populateData(this); return true; case MENU_IPV4_RULES: showIPv6 = false; populateData(this); return true; case MENU_SEND_REPORT: String ver; try { ver = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0).versionName; } catch (NameNotFoundException e) { ver = "???"; } String body = dataText + "\n\n" + getString(R.string.enter_problem) + "\n\n"; /*String uriText = "mailto:afwall-report@googlegroups.com?subject=" + Uri.encode("AFWall+ problem report - v" + ver) + "&body=" + Uri.encode(body);*/ final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND); //email.setData(Uri.parse(uriText)); emailIntent.setType("plain/text"); emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{"afwall-report@googlegroups.com"}); emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "AFWall+ problem report - v" + ver); emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, body); startActivity(Intent.createChooser(emailIntent, getString(R.string.send_mail))); // this shouldn't be necessary, but the default Android email client overrides // "body=" from the URI. See MessageCompose.initFromIntent() //email.putExtra(Intent.EXTRA_TEXT, body); //startActivity(Intent.createChooser(email, getString(R.string.send_mail))); return true; } return super.onOptionsItemSelected(item); } private void flushAllRules(final Context ctx) { new MaterialDialog.Builder(this) .title(R.string.confirmation) .content(R.string.flushRulesConfirm) .positiveText(R.string.Yes) .negativeText(R.string.No) .callback(new MaterialDialog.ButtonCallback() { @Override public void onPositive(MaterialDialog dialog) { Api.flushAllRules(ctx, new RootCommand() .setReopenShell(true) .setSuccessToast(R.string.flushed) .setFailureToast(R.string.error_purge) .setCallback(new RootCommand.Callback() { public void cbFunc(RootCommand state) { populateData(ctx); } })); } }) .show(); } }