要绕过手机厂商对于安装apk的检测无非就是在apk下载完成安装时切断用户的网络(这个期间一般也就10s左右的时间安装apk完成的时间也和这个差多不这个视手机而定),so 要实现这样的效果,首先想到的是切断用户的手机网络,但实际的开发中这样做还是有一大堆的问题。之后偶然的发现了通过vpn的这种方式来实现。可以解决这样的问题只是用户在第一次安装的时候让用户选择开启VPN就哦了。如果看到的童鞋有更好的想法欢迎拍砖。
要使用vpn的话用到的是VpnService 这个类是在android.net.VpnService 包底下如图
官网链接
OK代码如下:
1.创建一个MyVpnService
import android.content.Intent;
import android.net.VpnService;
import android.os.ParcelFileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.InetSocketAddress;
import java.nio.channels.DatagramChannel;
/**
* Created by vc on 2017/6/28.
*/
public class MyVpnService extends VpnService {
private Thread mThread;
private ParcelFileDescriptor mParcelFileDescriptor;
private Builder mBuilder =new Builder(); // VpnService.Builder
@Override
public int onStartCommand(Intent intent,int flags, int startId) {
final boolean flag = intent.getBooleanExtra("showtime",true);
mThread =new Thread(new Runnable() {
@Override
public void run() {
try {
mParcelFileDescriptor = mBuilder.setSession("myVpnService") // 给session设置一个名称
.addAddress("192.168.0.1",24)//给vpn的接口添加一个地址
.addDnsServer("8.8.8.8") //添加一个dns的服务给vpn
.addRoute("0.0.0.0",0)//将网络路由器器添加到vpn的接口
.establish() ; //表示使用此构建器的参数创建vpn的接口
FileInputStream inputStream = new FileInputStream(mParcelFileDescriptor.getFileDescriptor()); // 发送数据包排队在这个输入流上
FileOutputStream out = new FileOutputStream( mParcelFileDescriptor.getFileDescriptor()); //接收到的数据包写入输出流
//DatagramChannel 是一个能收发UDP包的通道。因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包。
DatagramChannel datagramChannel =DatagramChannel.open();
datagramChannel.connect(new InetSocketAddress("127.0.0.1",8087));
protect(datagramChannel.socket()); // 保护socket 与vpn链接
/*if(flag){
while (true) { //循环传输数据包
Thread.sleep(100);
}
}else {
Thread.sleep(10000);
}*/
Thread.sleep(10000); // 这里定义了一个时间
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if(mParcelFileDescriptor!=null){
mParcelFileDescriptor.close();
mParcelFileDescriptor=null;
}
}catch (Exception e ){
e.printStackTrace();
}
}
}
});
mThread.start();
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
if(mThread!=null){
mThread.interrupt();
}
}
}
2,在Androidmanifest.xml 注册当前的MyVpnService
<service
android:name=".MyVpnService"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
3.到此主要的vpnservice 已经完成了。接下来是打开vpnservice,主要代码如下:
Intent intent = VpnService.prepare(InstallActivity.this);//准备vpnservice的连接
if(intent!=null){
startActivityForResult(intent,0);
}else {
onActivityResult(0,RESULT_OK,null);
}
处理结果需要在onActivityResult中
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.e(TAG,"onActivityResult");
if(resultCode== RESULT_OK){
Intent intent = new Intent(this,MyVpnService.class);
startService(intent);
}
}
这样就可以开启一个vpn的service了。走到这里你可能觉得这不就是一个开启vpnservice么,别忘了我们要做的是安装apk的时候绕过厂商特殊机型的对apk的检测,有一种解决思路就是每次安装apk的时候都走一个installActivity 这个activity是透明的切只有一个像素的大小
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.Uri;
import android.net.VpnService;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.util.Log;
import android.view.WindowManager;
import java.io.File;
// 安装app的activity
public class InstallActivity extends Activity {
private boolean isShow= true; // 用于判断当前activity的显示状态
private MyVpnService mMyVpnService;
private final String TAG="InstallActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG,"onCreate");
//设置activity的大小
WindowManager.LayoutParams params = getWindow().getAttributes();
params.width = 1;
params.height = 1;
params.dimAmount= 0.0f;
getWindow().setAttributes(params);
Intent intent = VpnService.prepare(InstallActivity.this);
if(intent!=null){
startActivityForResult(intent,0);
}else {
onActivityResult(0,RESULT_OK,null);
}
}
@Override
protected void onResume() {
super.onResume();
Log.e(TAG,"onResume");
if(!isShow){
Log.e(TAG,"onResume finish ");
finish();
}
}
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onPause() {
super.onPause();
isShow =false;
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e(TAG,"onDestroy");
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.e(TAG,"onActivityResult");
if(resultCode== RESULT_OK){
isShow =false;
Intent intent = new Intent(this,MyVpnService.class);
startService(intent);
//处理完成后安装apk这里写个一个本地路径实际开发中需要传入安装apk的路径
inStallApp(Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"q.apk");
}
}
private void inStallApp(String path){
Uri uri = Uri.fromFile(new File(path));
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(uri,"application/vnd.android.package-archive");
startActivity(intent);
}
}
设置activity透明的主题
<style name="transparent_theme" parent="@android:style/Theme.Translucent.NoTitleBar"></style>
需要注意的就是installactivity需求继承activity否则这么干会报错的。这里只能算是一种解决思路实际中对于不同的厂商哪些是对apk安装检测的还需要去统计不同的机型。如果有更好的想法欢迎拍砖