要繞過手機廠商對於安裝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安裝檢測的還需要去統計不同的機型。如果有更好的想法歡迎拍磚