From c0ec3e64212e5d179637fe10fa93f6176cda6597 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Tue, 17 May 2022 20:01:02 +0200 Subject: [PATCH] Add app details connections tab Closes #204 --- .../activities/AppDetailsActivity.java | 81 +++++++++++++++++-- .../activities/EditFilterActivity.java | 2 +- .../remote_capture/fragments/AppOverview.java | 44 +++++----- .../fragments/FirewallStatus.java | 7 +- .../model/FilterDescriptor.java | 28 +++++-- app/src/main/res/layout/app_overview.xml | 39 ++------- app/src/main/res/menu/app_overview_menu.xml | 26 ++++++ app/src/main/res/values/strings.xml | 1 + 8 files changed, 153 insertions(+), 75 deletions(-) create mode 100644 app/src/main/res/menu/app_overview_menu.xml diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/AppDetailsActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/AppDetailsActivity.java index 276ed447..247fc3d6 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/AppDetailsActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/AppDetailsActivity.java @@ -19,28 +19,97 @@ package com.emanuelef.remote_capture.activities; +import android.content.Intent; import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.viewpager2.adapter.FragmentStateAdapter; +import androidx.viewpager2.widget.ViewPager2; + import com.emanuelef.remote_capture.R; import com.emanuelef.remote_capture.Utils; import com.emanuelef.remote_capture.fragments.AppOverview; +import com.emanuelef.remote_capture.fragments.ConnectionsFragment; +import com.emanuelef.remote_capture.model.FilterDescriptor; +import com.google.android.material.tabs.TabLayoutMediator; public class AppDetailsActivity extends BaseActivity { private static final String TAG = "AppDetailsActivity"; public static final String APP_UID_EXTRA = "app_uid"; + private ViewPager2 mPager; + private int mUid; + + private static final int POS_OVERVIEW = 0; + private static final int POS_CONNECTIONS = 1; + private static final int POS_TOTAL = 2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setTitle(R.string.app_details); displayBackAction(); - setContentView(R.layout.fragment_activity); + setContentView(R.layout.tabs_activity); - int uid = getIntent().getIntExtra(APP_UID_EXTRA, Utils.UID_UNKNOWN); + mUid = getIntent().getIntExtra(APP_UID_EXTRA, Utils.UID_UNKNOWN); + setupUidFilter(); - getSupportFragmentManager() - .beginTransaction() - .replace(R.id.fragment, AppOverview.newInstance(uid)) - .commit(); + mPager = findViewById(R.id.pager); + setupTabs(); + } + + private void setupUidFilter() { + Intent intent = getIntent(); + if(intent == null) { + intent = new Intent(); + setIntent(intent); + } + + FilterDescriptor filter = new FilterDescriptor(); + filter.uid = mUid; + intent.putExtra(ConnectionsFragment.FILTER_EXTRA, filter); + } + + private class StateAdapter extends FragmentStateAdapter { + StateAdapter(final FragmentActivity fa) { super(fa); } + + @NonNull + @Override + public Fragment createFragment(int position) { + //Log.d(TAG, "createFragment"); + switch (position) { + case POS_CONNECTIONS: + // APP_UID_EXTRA is passed (see setupUidFilter) + return new ConnectionsFragment(); + case POS_OVERVIEW: + default: + return AppOverview.newInstance(mUid); + } + } + + @Override + public int getItemCount() { + return POS_TOTAL; + } + + public int getPageTitle(final int position) { + switch(position) { + case POS_CONNECTIONS: + return R.string.connections_view; + case POS_OVERVIEW: + default: + return R.string.overview; + } + } + } + + private void setupTabs() { + StateAdapter pageAdapter = new StateAdapter(this); + mPager.setAdapter(pageAdapter); + + new TabLayoutMediator(findViewById(R.id.tablayout), mPager, (tab, position) -> + tab.setText(getString(pageAdapter.getPageTitle(position))) + ).attach(); } } diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/EditFilterActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/EditFilterActivity.java index 2bf3f641..5cedf66f 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/EditFilterActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/EditFilterActivity.java @@ -229,7 +229,7 @@ public class EditFilterActivity extends BaseActivity { @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { if(item.getItemId() == R.id.reset_changes) { - mFilter = new FilterDescriptor(); + mFilter.clear(); model2view(); return true; } diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/AppOverview.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/AppOverview.java index 1344cc9f..1fc46214 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/AppOverview.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/AppOverview.java @@ -47,7 +47,6 @@ import com.emanuelef.remote_capture.CaptureService; import com.emanuelef.remote_capture.ConnectionsRegister; import com.emanuelef.remote_capture.R; import com.emanuelef.remote_capture.Utils; -import com.emanuelef.remote_capture.activities.ConnectionsActivity; import com.emanuelef.remote_capture.model.AppDescriptor; import com.emanuelef.remote_capture.model.AppStats; @@ -61,6 +60,7 @@ public class AppOverview extends Fragment { private TextView mBlockedConnections; private TableLayout mTable; private TextView mPermissions; + private PackageInfo mPinfo; public static AppOverview newInstance(int uid) { AppOverview fragment = new AppOverview(); @@ -100,21 +100,22 @@ public class AppOverview extends Fragment { ((TextView)view.findViewById(R.id.uid)).setText(Utils.formatInteger(ctx, dsc.getUid())); ((TextView)view.findViewById(R.id.name)).setText(dsc.getName()); - PackageInfo pinfo = dsc.getPackageInfo(); ((ImageView)view.findViewById(R.id.app_icon)).setImageDrawable(dsc.getIcon()); - if(pinfo != null) { - ((TextView)view.findViewById(R.id.package_name)).setText(dsc.getPackageName()); - ((TextView)view.findViewById(R.id.version)).setText(pinfo.versionName); - ((TextView)view.findViewById(R.id.target_sdk)).setText(Utils.formatInteger(ctx, pinfo.applicationInfo.targetSdkVersion)); - ((TextView)view.findViewById(R.id.install_date)).setText(Utils.formatEpochFull(ctx, pinfo.firstInstallTime / 1000)); - ((TextView)view.findViewById(R.id.last_update)).setText(Utils.formatEpochFull(ctx, pinfo.lastUpdateTime / 1000)); + mPinfo = dsc.getPackageInfo(); - if((pinfo.requestedPermissions != null) && (pinfo.requestedPermissions.length != 0)) { + if(mPinfo != null) { + ((TextView)view.findViewById(R.id.package_name)).setText(dsc.getPackageName()); + ((TextView)view.findViewById(R.id.version)).setText(mPinfo.versionName); + ((TextView)view.findViewById(R.id.target_sdk)).setText(Utils.formatInteger(ctx, mPinfo.applicationInfo.targetSdkVersion)); + ((TextView)view.findViewById(R.id.install_date)).setText(Utils.formatEpochFull(ctx, mPinfo.firstInstallTime / 1000)); + ((TextView)view.findViewById(R.id.last_update)).setText(Utils.formatEpochFull(ctx, mPinfo.lastUpdateTime / 1000)); + + if((mPinfo.requestedPermissions != null) && (mPinfo.requestedPermissions.length != 0)) { StringBuilder builder = new StringBuilder(); boolean first = true; - for(String perm: pinfo.requestedPermissions) { + for(String perm: mPinfo.requestedPermissions) { if(first) first = false; else @@ -146,22 +147,11 @@ public class AppOverview extends Fragment { view.findViewById(R.id.last_update_row).setVisibility(View.GONE); view.findViewById(R.id.permissions_label).setVisibility(View.GONE); view.findViewById(R.id.permissions).setVisibility(View.GONE); - view.findViewById(R.id.app_settings).setVisibility(View.GONE); } mTable = view.findViewById(R.id.table); - view.findViewById(R.id.app_settings).setOnClickListener(v -> { - Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - intent.setData(Uri.fromParts("package", dsc.getPackageName(), null)); - Utils.startActivity(ctx, intent); - }); - view.findViewById(R.id.show_connections).setOnClickListener(v -> { - Intent intent = new Intent(ctx, ConnectionsActivity.class); - intent.putExtra(ConnectionsFragment.QUERY_EXTRA, dsc.getPackageName()); - startActivity(intent); - }); } @Override @@ -189,14 +179,22 @@ public class AppOverview extends Fragment { @Override public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater menuInflater) { - menuInflater.inflate(R.menu.copy_share_menu, menu); + menuInflater.inflate(R.menu.app_overview_menu, menu); + + if(mPinfo == null) + menu.findItem(R.id.app_info).setVisible(false); } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { int id = item.getItemId(); - if(id == R.id.copy_to_clipboard) { + if(id == R.id.app_info) { + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.fromParts("package", mPinfo.packageName, null)); + Utils.startActivity(requireContext(), intent); + return true; + } else if(id == R.id.copy_to_clipboard) { Utils.copyToClipboard(requireContext(), asString()); return true; } else if(id == R.id.share) { diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/FirewallStatus.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/FirewallStatus.java index 6588f69c..1969426a 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/FirewallStatus.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/FirewallStatus.java @@ -33,7 +33,6 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; @@ -58,10 +57,9 @@ import com.emanuelef.remote_capture.model.Prefs; public class FirewallStatus extends Fragment { private static final String TAG = "FirewallStatus"; private Handler mHandler; - SharedPreferences mPrefs; + private SharedPreferences mPrefs; private SwitchCompat mToggle; private ImageView mStatusIcon; - private View mConnectionsCard; private TextView mStatus; private TextView mNumBlocked; private TextView mNumChecked; @@ -86,7 +84,6 @@ public class FirewallStatus extends Fragment { mStatus = view.findViewById(R.id.status); mHandler = new Handler(Looper.getMainLooper()); mStatusIcon = view.findViewById(R.id.status_icon); - mConnectionsCard = view.findViewById(R.id.show_connections); mNumBlocked = view.findViewById(R.id.num_blocked); mNumChecked = view.findViewById(R.id.num_checked); mNumRules = view.findViewById(R.id.num_rules); @@ -96,7 +93,7 @@ public class FirewallStatus extends Fragment { mWarnColor = ContextCompat.getColor(ctx, R.color.warning); mGrayColor = ContextCompat.getColor(ctx, R.color.lightGray); - mConnectionsCard.setOnClickListener(v -> { + view.findViewById(R.id.show_connections).setOnClickListener(v -> { FilterDescriptor filter = new FilterDescriptor(); filter.onlyBLocked = true; diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/FilterDescriptor.java b/app/src/main/java/com/emanuelef/remote_capture/model/FilterDescriptor.java index c484399f..89df701c 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/FilterDescriptor.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/FilterDescriptor.java @@ -33,12 +33,17 @@ import com.google.android.material.chip.ChipGroup; import java.io.Serializable; public class FilterDescriptor implements Serializable { - public Status status = Status.STATUS_INVALID; - public boolean showMasked = true; - public boolean onlyBLocked = false; - public boolean onlyBlacklisted = false; - public DecryptionStatus decStatus = DecryptionStatus.INVALID; + public Status status; + public boolean showMasked; + public boolean onlyBLocked; + public boolean onlyBlacklisted; + public DecryptionStatus decStatus; public String iface; + public int uid = -2; // this is persistent and used internally (AppDetailsActivity) + + public FilterDescriptor() { + clear(); + } public boolean isSet() { return (status != Status.STATUS_INVALID) @@ -46,6 +51,7 @@ public class FilterDescriptor implements Serializable { || (iface != null) || onlyBLocked || onlyBlacklisted + || (uid != 2) || (!showMasked && !PCAPdroid.getInstance().getVisualizationMask().isEmpty()); } @@ -55,7 +61,8 @@ public class FilterDescriptor implements Serializable { && (!onlyBlacklisted || conn.isBlacklisted()) && ((status == Status.STATUS_INVALID) || (conn.getStatus().equals(status))) && ((decStatus == DecryptionStatus.INVALID) || (conn.getDecryptionStatus() == decStatus)) - && ((iface == null) || (CaptureService.getInterfaceName(conn.ifidx).equals(iface))); + && ((iface == null) || (CaptureService.getInterfaceName(conn.ifidx).equals(iface))) + && ((uid == -2) || (uid == conn.uid)); } private void addChip(LayoutInflater inflater, ChipGroup group, int id, String text) { @@ -100,4 +107,13 @@ public class FilterDescriptor implements Serializable { else if(filter_id == R.id.capture_interface) iface = null; } + + public void clear() { + showMasked = true; + onlyBLocked = false; + onlyBlacklisted = false; + status = Status.STATUS_INVALID; + decStatus = DecryptionStatus.INVALID; + iface = null; + } } diff --git a/app/src/main/res/layout/app_overview.xml b/app/src/main/res/layout/app_overview.xml index d6864968..7a94b9b1 100644 --- a/app/src/main/res/layout/app_overview.xml +++ b/app/src/main/res/layout/app_overview.xml @@ -18,6 +18,7 @@ android:layout_marginTop="10dp" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:maxHeight="80dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -171,8 +172,7 @@ + android:layout_marginBottom="4dp"> - - -