Add app details connections tab

Closes #204
This commit is contained in:
emanuele-f 2022-05-17 20:01:02 +02:00
parent 68d661d625
commit c0ec3e6421
8 changed files with 153 additions and 75 deletions

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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 @@
<TableRow
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="4dp"
android:layout_marginTop="16dp">
android:layout_marginBottom="4dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
@ -228,35 +228,6 @@
</TableRow>
</TableLayout>
<LinearLayout
android:id="@+id/buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginHorizontal="10dp"
android:layout_marginTop="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/table">
<Button
android:id="@+id/app_settings"
android:text="@string/title_activity_settings"
android:layout_width="0dp"
android:layout_height="42dip"
android:layout_weight="1"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:textColor="@color/colorTabText" />
<Button
android:id="@+id/show_connections"
android:text="@string/connections_view"
android:nextFocusDown="@id/permissions"
android:layout_marginStart="5dp"
android:layout_width="0dp"
android:layout_height="42dip"
android:layout_weight="1" />
</LinearLayout>
<TextView
android:id="@+id/vapp_info"
android:layout_width="wrap_content"
@ -265,16 +236,16 @@
android:textStyle="italic"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/buttons" />
app:layout_constraintTop_toBottomOf="@id/table" />
<TextView
android:id="@+id/permissions_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginTop="16dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/buttons"
app:layout_constraintTop_toBottomOf="@id/table"
android:text="@string/permissions"/>
<HorizontalScrollView

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/app_info"
android:title="@string/app_info"
android:orderInCategory="5"
android:icon="@drawable/ic_settings"
app:showAsAction="ifRoom" />
<item
android:id="@+id/copy_to_clipboard"
android:title="@string/copy_to_clipboard"
android:orderInCategory="10"
android:icon="@drawable/ic_content_copy"
app:showAsAction="never" />
<item
android:id="@+id/share"
android:title="@string/share"
android:orderInCategory="20"
android:icon="@drawable/ic_share"
app:showAsAction="never" />
</menu>

View File

@ -347,4 +347,5 @@
<string name="last_firewall_block">Last block</string>
<string name="firewall_is_disabled">Firewall is disabled</string>
<string name="firewall_is_enabled">Firewall is enabled</string>
<string name="app_info">App info</string>
</resources>