mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-07-03 21:21:12 +08:00
Access purchased paid features on beta builds
Since PCAPdroid 1.5.3, users which purchased paid features in official builds will be able to use them in beta builds.
This commit is contained in:
parent
c8f61760f8
commit
8446e0962d
@ -40,6 +40,7 @@ import java.security.SignatureException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/* Billing stub */
|
||||
@ -69,6 +70,7 @@ public class Billing {
|
||||
|
||||
protected final Context mContext;
|
||||
protected SharedPreferences mPrefs;
|
||||
private final HashSet<String> mPeerSkus = new HashSet<>();
|
||||
|
||||
protected Billing(Context ctx) {
|
||||
mContext = ctx;
|
||||
@ -84,6 +86,9 @@ public class Billing {
|
||||
}
|
||||
|
||||
public boolean isPurchased(String sku) {
|
||||
if(mPeerSkus.contains(sku))
|
||||
return true;
|
||||
|
||||
return !getLicense().isEmpty();
|
||||
}
|
||||
|
||||
@ -188,4 +193,8 @@ public class Billing {
|
||||
else
|
||||
return !Prefs.isRootCaptureEnabled(mPrefs);
|
||||
}
|
||||
|
||||
public void addPeerSku(String sku) {
|
||||
mPeerSkus.add(sku);
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ public class PCAPdroid extends Application {
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
Utils.BuildType buildtp = Utils.getBuildType(this);
|
||||
Utils.BuildType buildtp = Utils.getVerifiedBuild(this);
|
||||
Log.d(TAG, "Build type: " + buildtp);
|
||||
|
||||
CaocConfig.Builder builder = CaocConfig.Builder.create();
|
||||
|
||||
@ -59,7 +59,6 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.text.Html;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannedString;
|
||||
import android.text.TextUtils;
|
||||
@ -1020,16 +1019,16 @@ public class Utils {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static BuildType getBuildType(Context ctx) {
|
||||
public static BuildType getVerifiedBuild(Context ctx, String package_name) {
|
||||
try {
|
||||
Signature[] signatures;
|
||||
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
// NOTE: PCAPdroid does not use multiple signatures
|
||||
PackageInfo pInfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
PackageInfo pInfo = ctx.getPackageManager().getPackageInfo(package_name, PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
signatures = (pInfo.signingInfo == null) ? null : pInfo.signingInfo.getSigningCertificateHistory();
|
||||
} else {
|
||||
@SuppressLint("PackageManagerGetSignatures")
|
||||
PackageInfo pInfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), PackageManager.GET_SIGNATURES);
|
||||
signatures = pInfo.signatures;
|
||||
}
|
||||
@ -1057,6 +1056,10 @@ public class Utils {
|
||||
return BuildType.UNKNOWN;
|
||||
}
|
||||
|
||||
public static BuildType getVerifiedBuild(Context ctx) {
|
||||
return getVerifiedBuild(ctx, ctx.getPackageName());
|
||||
}
|
||||
|
||||
public static X509Certificate x509FromPem(String pem) {
|
||||
int begin = pem.indexOf('\n') + 1;
|
||||
int end = pem.indexOf('-', begin);
|
||||
|
||||
@ -42,6 +42,7 @@ import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.emanuelef.remote_capture.AppsResolver;
|
||||
import com.emanuelef.remote_capture.Billing;
|
||||
import com.emanuelef.remote_capture.BuildConfig;
|
||||
import com.emanuelef.remote_capture.CaptureHelper;
|
||||
import com.emanuelef.remote_capture.CaptureService;
|
||||
@ -53,10 +54,13 @@ import com.emanuelef.remote_capture.model.CaptureSettings;
|
||||
import com.emanuelef.remote_capture.model.CtrlPermissions;
|
||||
import com.emanuelef.remote_capture.model.CaptureStats;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
public class CaptureCtrl extends AppCompatActivity {
|
||||
public static final String ACTION_START = "start";
|
||||
public static final String ACTION_STOP = "stop";
|
||||
public static final String ACTION_STATUS = "get_status";
|
||||
public static final String ACTION_PEER_INFO = "get_peer_info";
|
||||
public static final String ACTION_NOTIFY_STATUS = "com.emanuelef.remote_capture.CaptureStatus";
|
||||
private static final String TAG = "CaptureCtrl";
|
||||
private static AppDescriptor mStarterApp = null; // the app which started the capture, may be unknown
|
||||
@ -100,6 +104,11 @@ public class CaptureCtrl extends AppCompatActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
if(action.equals(ACTION_PEER_INFO)) {
|
||||
getPeerInfo();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if a control permission rule was set
|
||||
mPermissions = PCAPdroid.getInstance().getCtrlPermissions();
|
||||
AppDescriptor app = getCallingApp();
|
||||
@ -260,4 +269,31 @@ public class CaptureCtrl extends AppCompatActivity {
|
||||
intent.putExtra("pkts_rcvd", stats.pkts_rcvd);
|
||||
intent.putExtra("pkts_dropped", stats.pkts_dropped);
|
||||
}
|
||||
|
||||
// A request sent from a debug build of PCAPdroid to a non-debug one
|
||||
private void getPeerInfo() {
|
||||
// Verify the peer app
|
||||
String package_name = getCallingPackage();
|
||||
if((package_name == null) || !package_name.equals(BuildConfig.APPLICATION_ID + ".debug")) {
|
||||
Log.w(TAG, "getPeerInfo: package name mismatch");
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
|
||||
Billing billing = Billing.newInstance(this);
|
||||
billing.setLicense(billing.getLicense());
|
||||
|
||||
Intent res = new Intent();
|
||||
HashSet<String> purchased = new HashSet<>();
|
||||
|
||||
for(String sku: Billing.ALL_SKUS) {
|
||||
if(billing.isPurchased(sku))
|
||||
purchased.add(sku);
|
||||
}
|
||||
|
||||
res.putExtra("skus", purchased);
|
||||
|
||||
setResult(RESULT_OK, res);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,8 +38,6 @@ import androidx.core.content.res.ResourcesCompat;
|
||||
|
||||
import com.emanuelef.remote_capture.Utils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import cat.ereza.customactivityoncrash.CustomActivityOnCrash;
|
||||
import cat.ereza.customactivityoncrash.R;
|
||||
import cat.ereza.customactivityoncrash.config.CaocConfig;
|
||||
@ -158,7 +156,7 @@ public final class ErrorActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private String getErrorDetails() {
|
||||
return "Build type: " + Utils.getBuildType(this).toString().toLowerCase() + "\n" +
|
||||
return "Build type: " + Utils.getVerifiedBuild(this).toString().toLowerCase() + "\n" +
|
||||
CustomActivityOnCrash.getAllErrorDetailsFromIntent(ErrorActivity.this, getIntent());
|
||||
}
|
||||
}
|
||||
@ -27,6 +27,7 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.UriPermission;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
@ -61,12 +62,15 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.emanuelef.remote_capture.AppsResolver;
|
||||
import com.emanuelef.remote_capture.Billing;
|
||||
import com.emanuelef.remote_capture.BuildConfig;
|
||||
import com.emanuelef.remote_capture.CaptureHelper;
|
||||
import com.emanuelef.remote_capture.MitmReceiver;
|
||||
import com.emanuelef.remote_capture.fragments.ConnectionsFragment;
|
||||
import com.emanuelef.remote_capture.fragments.StatusFragment;
|
||||
import com.emanuelef.remote_capture.interfaces.AppStateListener;
|
||||
import com.emanuelef.remote_capture.model.AppDescriptor;
|
||||
import com.emanuelef.remote_capture.model.AppState;
|
||||
import com.emanuelef.remote_capture.CaptureService;
|
||||
import com.emanuelef.remote_capture.model.CaptureSettings;
|
||||
@ -82,6 +86,8 @@ import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class MainActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener {
|
||||
private Billing mIab;
|
||||
@ -121,6 +127,8 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
registerForActivityResult(new RequestPermission(), isGranted ->
|
||||
Log.d(TAG, "Write permission " + (isGranted ? "granted" : "denied"))
|
||||
);
|
||||
private final ActivityResultLauncher<Intent> peerInfoLauncher =
|
||||
registerForActivityResult(new StartActivityForResult(), this::peerInfoResult);
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@ -143,6 +151,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
mIab = Billing.newInstance(this);
|
||||
mIab.setLicense(mIab.getLicense());
|
||||
|
||||
initPeerAppInfo();
|
||||
initAppState();
|
||||
checkPermissions();
|
||||
|
||||
@ -316,6 +325,65 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
}
|
||||
}
|
||||
|
||||
// On debug builds, if the user also has the non-debug app installed (peer app), unlock the
|
||||
// already-purchased features also on this beta app
|
||||
private void initPeerAppInfo() {
|
||||
if(!BuildConfig.DEBUG)
|
||||
return;
|
||||
|
||||
final String peerAppPackage = "com.emanuelef.remote_capture";
|
||||
|
||||
AppDescriptor peer = AppsResolver.resolve(getPackageManager(), peerAppPackage, 0);
|
||||
if(peer == null) {
|
||||
Log.d(TAG, "Peer app not found");
|
||||
return;
|
||||
}
|
||||
|
||||
PackageInfo pInfo = peer.getPackageInfo();
|
||||
if((pInfo == null) || (pInfo.versionCode < 56)) {
|
||||
Log.d(TAG, "Unsupported peer app version found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify that the peer signature
|
||||
Utils.BuildType buildType = Utils.getVerifiedBuild(this, peerAppPackage);
|
||||
if((buildType != Utils.BuildType.FDROID) && (buildType != Utils.BuildType.PLAYSTORE) && (buildType != Utils.BuildType.GITHUB)) {
|
||||
Log.d(TAG, "Unsupported peer app build: " + buildType.name());
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Valid peer app found (" + pInfo.versionName + " - " + pInfo.versionCode + ")");
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setClassName(peerAppPackage, "com.emanuelef.remote_capture.activities.CaptureCtrl");
|
||||
intent.putExtra("action", "get_peer_info");
|
||||
|
||||
try {
|
||||
peerInfoLauncher.launch(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.d(TAG, "Peer app launch failed");
|
||||
}
|
||||
}
|
||||
|
||||
private void peerInfoResult(final ActivityResult result) {
|
||||
if((result.getResultCode() == RESULT_OK) && (result.getData() != null)) {
|
||||
Intent data = result.getData();
|
||||
Serializable skus_extra = data.getSerializableExtra("skus");
|
||||
if(skus_extra instanceof HashSet) {
|
||||
HashSet<String> skus = (HashSet<String>) skus_extra;
|
||||
Log.d(TAG, "Found peer app info");
|
||||
|
||||
for(String sku: skus) {
|
||||
Log.d(TAG, "Peer sku: " + sku);
|
||||
mIab.addPeerSku(sku);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "Invalid peer app result");
|
||||
}
|
||||
|
||||
private static class MainStateAdapter extends FragmentStateAdapter {
|
||||
MainStateAdapter(final FragmentActivity fa) { super(fa); }
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user