diff --git a/app/src/main/java/com/emanuelef/remote_capture/ActionReceiver.java b/app/src/main/java/com/emanuelef/remote_capture/ActionReceiver.java index 3249a77c..eed4bc55 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/ActionReceiver.java +++ b/app/src/main/java/com/emanuelef/remote_capture/ActionReceiver.java @@ -47,7 +47,7 @@ public class ActionReceiver extends BroadcastReceiver { NotificationManagerCompat man = NotificationManagerCompat.from(context); man.cancel(CaptureService.NOTIFY_ID_APP_BLOCKED); - AppDescriptor app = AppsResolver.resolve(context.getPackageManager(), unblock_app, 0); + AppDescriptor app = AppsResolver.resolveInstalledApp(context.getPackageManager(), unblock_app, 0); String label = (app != null) ? app.getName() : unblock_app; Utils.showToastLong(context, R.string.app_unblocked, label); } diff --git a/app/src/main/java/com/emanuelef/remote_capture/AppsResolver.java b/app/src/main/java/com/emanuelef/remote_capture/AppsResolver.java index 8e6bd5f2..e552c32b 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/AppsResolver.java +++ b/app/src/main/java/com/emanuelef/remote_capture/AppsResolver.java @@ -35,7 +35,6 @@ import androidx.core.content.ContextCompat; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Arrays; public class AppsResolver { private static final String TAG = "AppsResolver"; @@ -92,7 +91,9 @@ public class AppsResolver { virtualIconLoader,"nobody", 9999, true)); } - public static AppDescriptor resolve(PackageManager pm, String packageName, int pm_flags) { + // Get the AppDescriptor corresponding to the given package name + // No caching occurs. Virtual apps cannot be used. + public static AppDescriptor resolveInstalledApp(PackageManager pm, String packageName, int pm_flags) { PackageInfo pinfo; try { @@ -106,11 +107,12 @@ public class AppsResolver { } @SuppressLint("DiscouragedPrivateApi") - public @Nullable AppDescriptor get(int uid, int pm_flags) { + public @Nullable AppDescriptor getAppByUid(int uid, int pm_flags) { AppDescriptor app = mApps.get(uid); if(app != null) return app; + // Map the uid to the package name(s) String[] packages = null; try { @@ -126,13 +128,17 @@ public class AppsResolver { return null; } - // Since multiple packages names may be returned, sort them and get the first to provide a - // consistent name - Arrays.sort(packages); + // Impose order to guarantee that a uid is always mapped to the same package name. + // String packageName = packages[0]; + for(String pkg: packages) { + if(pkg.compareTo(packageName) < 0) + packageName = pkg; + } + // In case of root capture, we may be capturing traffic of different users/work profiles. + // To get the correct label and icon, try to resolve the app as the specific user of the connection. if(!mFallbackToGlobalResolution && CaptureService.isCapturingAsRoot()) { - // Try to resolve for the specific user try { if(getPackageInfoAsUser == null) getPackageInfoAsUser = PackageManager.class.getDeclaredMethod("getPackageInfoAsUser", String.class, int.class, int.class); @@ -148,7 +154,7 @@ public class AppsResolver { } if(app == null) - app = resolve(mPm, packageName, pm_flags); + app = resolveInstalledApp(mPm, packageName, pm_flags); if(app != null) mApps.put(uid, app); @@ -156,16 +162,12 @@ public class AppsResolver { return app; } - public @Nullable AppDescriptor getByPackage(String package_name, int pm_flags) { + public @Nullable AppDescriptor getAppByPackage(String package_name, int pm_flags) { int uid = getUid(package_name); if(uid == Utils.UID_NO_FILTER) return null; - return get(uid, pm_flags); - } - - public @Nullable AppDescriptor lookup(int uid) { - return mApps.get(uid); + return getAppByUid(uid, pm_flags); } /* Lookup a UID by package name (including virtual apps). diff --git a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java index f3963fff..296455a2 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java +++ b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java @@ -132,7 +132,7 @@ public class CaptureService extends VpnService implements Runnable { private NotificationCompat.Builder mMalwareBuilder; private long mMonitoredNetwork; private ConnectivityManager.NetworkCallback mNetworkCallback; - private AppsResolver appsResolver; + private AppsResolver nativeAppsResolver; // can only be accessed by native code to avoid concurrency issues private boolean mMalwareDetectionEnabled; private boolean mBlacklistsUpdateRequested; private boolean mFirewallEnabled; @@ -193,7 +193,7 @@ public class CaptureService extends VpnService implements Runnable { @Override public void onCreate() { Log.d(CaptureService.TAG, "onCreate"); - appsResolver = new AppsResolver(this); + nativeAppsResolver = new AppsResolver(this); mPrefs = PreferenceManager.getDefaultSharedPreferences(this); mSettings = new CaptureSettings(mPrefs); // initialize to prevent NULL pointer exceptions in methods (e.g. isRootCapture) @@ -484,7 +484,7 @@ public class CaptureService extends VpnService implements Runnable { mBlocklist.save(); reloadBlocklist(); - AppDescriptor app = appsResolver.getByPackage(packageName, 0); + AppDescriptor app = AppsResolver.resolveInstalledApp(getPackageManager(), packageName, 0); String label = (app != null) ? app.getName() : packageName; PendingIntent pi = PendingIntent.getActivity(CaptureService.this, 0, @@ -625,7 +625,8 @@ public class CaptureService extends VpnService implements Runnable { public void notifyBlacklistedConnection(ConnectionDescriptor conn) { int uid = conn.uid; - AppDescriptor app = appsResolver.get(conn.uid, 0); + AppsResolver resolver = new AppsResolver(this); + AppDescriptor app = resolver.getAppByUid(conn.uid, 0); if(app == null) return; @@ -1304,7 +1305,7 @@ public class CaptureService extends VpnService implements Runnable { // NOTE: to be invoked only by the native code public String getApplicationByUid(int uid) { - AppDescriptor dsc = appsResolver.get(uid, 0); + AppDescriptor dsc = nativeAppsResolver.getAppByUid(uid, 0); if(dsc == null) return ""; diff --git a/app/src/main/java/com/emanuelef/remote_capture/ConnectionsRegister.java b/app/src/main/java/com/emanuelef/remote_capture/ConnectionsRegister.java index ec630143..7256bccc 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/ConnectionsRegister.java +++ b/app/src/main/java/com/emanuelef/remote_capture/ConnectionsRegister.java @@ -198,7 +198,7 @@ public class ConnectionsRegister { conn.asn = mGeo.getASN(dstAddr); //Log.d(TAG, "IP geolocation: IP=" + conn.dst_ip + " -> country=" + conn.country + ", ASN: " + conn.asn); - AppDescriptor app = mAppsResolver.get(conn.uid, 0); + AppDescriptor app = mAppsResolver.getAppByUid(conn.uid, 0); if(app != null) conn.encrypted_payload = Utils.hasEncryptedPayload(app, conn); diff --git a/app/src/main/java/com/emanuelef/remote_capture/Utils.java b/app/src/main/java/com/emanuelef/remote_capture/Utils.java index 552dde13..3e35d7c1 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/Utils.java +++ b/app/src/main/java/com/emanuelef/remote_capture/Utils.java @@ -1256,7 +1256,7 @@ public class Utils { public static int getPCAPdroidUid(Context context) { // NOTE: when called from a work profile, it correctly returns the work profile UID - AppDescriptor app = AppsResolver.resolve(context.getPackageManager(), BuildConfig.APPLICATION_ID, 0); + AppDescriptor app = AppsResolver.resolveInstalledApp(context.getPackageManager(), BuildConfig.APPLICATION_ID, 0); if(app != null) return app.getUid(); return Utils.UID_UNKNOWN; diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/CaptureCtrl.java b/app/src/main/java/com/emanuelef/remote_capture/activities/CaptureCtrl.java index cc1c5751..c8763aea 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/CaptureCtrl.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/CaptureCtrl.java @@ -152,7 +152,7 @@ public class CaptureCtrl extends AppCompatActivity { private AppDescriptor getCallingApp() { String callp = getCallingPackage(); - return (callp != null) ? AppsResolver.resolve(getPackageManager(), callp, 0) : null; + return (callp != null) ? AppsResolver.resolveInstalledApp(getPackageManager(), callp, 0) : null; } private void controlAction(Intent intent, String action, boolean allow) { diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java index 10dfa8ae..cf59a8bd 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java @@ -315,7 +315,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig final String peerAppPackage = "com.emanuelef.remote_capture"; - AppDescriptor peer = AppsResolver.resolve(getPackageManager(), peerAppPackage, 0); + AppDescriptor peer = AppsResolver.resolveInstalledApp(getPackageManager(), peerAppPackage, 0); if(peer == null) { Log.d(TAG, "Peer app not found"); mIab.clearPeerSkus(); diff --git a/app/src/main/java/com/emanuelef/remote_capture/adapters/AppsStatsAdapter.java b/app/src/main/java/com/emanuelef/remote_capture/adapters/AppsStatsAdapter.java index 3eead886..9fee995e 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/adapters/AppsStatsAdapter.java +++ b/app/src/main/java/com/emanuelef/remote_capture/adapters/AppsStatsAdapter.java @@ -77,7 +77,7 @@ public class AppsStatsAdapter extends RecyclerView.Adapter stats) { Collections.sort(stats, (o1, o2) -> { - AppDescriptor a1 = mApps.get(o1.getUid(), 0); - AppDescriptor a2 = mApps.get(o2.getUid(), 0); + AppDescriptor a1 = mApps.getAppByUid(o1.getUid(), 0); + AppDescriptor a2 = mApps.getAppByUid(o2.getUid(), 0); if((a1 == null) && (a2 == null)) return 0; diff --git a/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java b/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java index 2f0070d9..a8921d78 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java +++ b/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java @@ -58,7 +58,7 @@ public class ConnectionsAdapter extends RecyclerView.Adapter i while(it.hasNext()) { CtrlPermissions.Rule rule = it.next(); - AppDescriptor app = AppsResolver.resolve(pm, rule.package_name, 0); + AppDescriptor app = AppsResolver.resolveInstalledApp(pm, rule.package_name, 0); if(app != null) mPkgToApp.put(rule.package_name, app); diff --git a/app/src/main/java/com/emanuelef/remote_capture/adapters/ListEditAdapter.java b/app/src/main/java/com/emanuelef/remote_capture/adapters/ListEditAdapter.java index 6e2ec175..ecc3e738 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/adapters/ListEditAdapter.java +++ b/app/src/main/java/com/emanuelef/remote_capture/adapters/ListEditAdapter.java @@ -69,7 +69,7 @@ public class ListEditAdapter extends ArrayAdapter implements Tex if(rule.getType() == MatchList.RuleType.APP) { String package_name = (String)rule.getValue(); - AppDescriptor app = mApps.getByPackage(package_name, 0); + AppDescriptor app = mApps.getAppByPackage(package_name, 0); Drawable drawable = ((app != null) && (app.getIcon() != null)) ? app.getIcon() : mUnknownIcon; icon.setImageDrawable(drawable); } else 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 5b63acdc..eabda3c1 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 @@ -88,7 +88,7 @@ public class AppOverview extends Fragment implements MenuProvider { Context ctx = requireContext(); AppsResolver res = new AppsResolver(ctx); - AppDescriptor dsc = res.get(mUid, PackageManager.GET_PERMISSIONS); + AppDescriptor dsc = res.getAppByUid(mUid, PackageManager.GET_PERMISSIONS); if(dsc == null) { mCreateError = true; Utils.showToast(ctx, R.string.app_not_found, mUid); diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionOverview.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionOverview.java index 963069fd..66e5ae32 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionOverview.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionOverview.java @@ -177,7 +177,7 @@ public class ConnectionOverview extends Fragment implements ConnectionDetailsAct info_row.setVisibility(View.GONE); String uid_str = Integer.toString(mConn.uid); - AppDescriptor app = (new AppsResolver(mActivity)).get(mConn.uid, 0); + AppDescriptor app = (new AppsResolver(mActivity)).getAppByUid(mConn.uid, 0); if(app != null) appLabel.setText(String.format(getResources().getString(R.string.app_and_proto), app.getName(), uid_str)); else diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionsFragment.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionsFragment.java index 306cfc07..46674458 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionsFragment.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionsFragment.java @@ -290,7 +290,7 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener if(conn == null) return; - AppDescriptor app = mApps.get(conn.uid, 0); + AppDescriptor app = mApps.getAppByUid(conn.uid, 0); Context ctx = requireContext(); MenuItem item; @@ -473,7 +473,7 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener mask_changed = true; } else if(id == R.id.search_app) setQuery(Objects.requireNonNull( - mApps.get(conn.uid, 0)).getPackageName()); + mApps.getAppByUid(conn.uid, 0)).getPackageName()); else if(id == R.id.search_host) setQuery(conn.info); else if(id == R.id.search_ip) diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java index b79d2977..0ff7f9d0 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java +++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/StatusFragment.java @@ -220,7 +220,7 @@ public class StatusFragment extends Fragment implements AppStateListener, MenuPr mAppFilterSwitch.setChecked(true); - AppDescriptor app = AppsResolver.resolve(context.getPackageManager(), mAppFilter, 0); + AppDescriptor app = AppsResolver.resolveInstalledApp(context.getPackageManager(), mAppFilter, 0); String description; if(app == null) @@ -290,7 +290,7 @@ public class StatusFragment extends Fragment implements AppStateListener, MenuPr // Check if a filter is set if((mAppFilter != null) && (!mAppFilter.isEmpty())) { - AppDescriptor app = AppsResolver.resolve(requireContext().getPackageManager(), mAppFilter, 0); + AppDescriptor app = AppsResolver.resolveInstalledApp(requireContext().getPackageManager(), mAppFilter, 0); if((app != null) && (app.getIcon() != null)) { // Rendering after mCollectorInfo.setText is deferred, so getMeasuredHeight must be postponed diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/ConnectionDescriptor.java b/app/src/main/java/com/emanuelef/remote_capture/model/ConnectionDescriptor.java index 7dcc3655..169596a6 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/ConnectionDescriptor.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/ConnectionDescriptor.java @@ -20,7 +20,6 @@ package com.emanuelef.remote_capture.model; import android.content.Context; -import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -233,7 +232,7 @@ public class ConnectionDescriptor { public boolean matches(AppsResolver res, String filter) { filter = filter.toLowerCase(); - AppDescriptor app = res.get(uid, 0); + AppDescriptor app = res.getAppByUid(uid, 0); return(((info != null) && (info.contains(filter))) || dst_ip.contains(filter) || diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/MatchList.java b/app/src/main/java/com/emanuelef/remote_capture/model/MatchList.java index 25af2567..4f31c54c 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/model/MatchList.java +++ b/app/src/main/java/com/emanuelef/remote_capture/model/MatchList.java @@ -159,7 +159,7 @@ public class MatchList { if(tp == RuleType.APP) { // TODO handle cross-users/profiles? - AppDescriptor app = AppsResolver.resolve(ctx.getPackageManager(), value, 0); + AppDescriptor app = AppsResolver.resolveInstalledApp(ctx.getPackageManager(), value, 0); if(app != null) value = app.getName(); } else if(tp == RuleType.HOST) @@ -223,7 +223,7 @@ public class MatchList { try { int uid = Integer.parseInt(val); - AppDescriptor app = mResolver.get(uid, 0); + AppDescriptor app = mResolver.getAppByUid(uid, 0); if(app != null) { val = app.getPackageName(); Log.i(TAG, String.format("UID %d resolved to package %s", uid, val)); @@ -253,7 +253,7 @@ public class MatchList { public void addCountry(String country_code) { addRule(new Rule(RuleType.COUNTRY, country_code)); } public void addApp(String pkg) { addRule(new Rule(RuleType.APP, pkg)); } public void addApp(int uid) { - AppDescriptor app = mResolver.get(uid, 0); + AppDescriptor app = mResolver.getAppByUid(uid, 0); if(app == null) { Log.e(TAG, "could not resolve UID " + uid); return; @@ -269,7 +269,7 @@ public class MatchList { public void removeCountry(String country_code) { removeRule(new Rule(RuleType.COUNTRY, country_code)); } public void removeApp(String pkg) { removeRule(new Rule(RuleType.APP, pkg)); } public void removeApp(int uid) { - AppDescriptor app = mResolver.get(uid, 0); + AppDescriptor app = mResolver.getAppByUid(uid, 0); if(app == null) { Log.e(TAG, "could not resolve UID " + uid); return;