com.PrivacyGuard.Application.Network.FakeVPN.MyVpnService.java Source code

Java tutorial

Introduction

Here is the source code for com.PrivacyGuard.Application.Network.FakeVPN.MyVpnService.java

Source

/*
 * Vpnservice, build the virtual network interface
 * Copyright (C) 2014  Yihang Song
    
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
    
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
    
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package com.PrivacyGuard.Application.Network.FakeVPN;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Context;
import android.content.Intent;
import android.net.VpnService;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.support.v4.app.NotificationCompat;

import com.PrivacyGuard.Application.ActionReceiver;
import com.PrivacyGuard.Application.Activities.AppSummaryActivity;
import com.PrivacyGuard.Application.Activities.R;
import com.PrivacyGuard.Application.Database.DatabaseHandler;
import com.PrivacyGuard.Application.Logger;
import com.PrivacyGuard.Application.Network.Forwarder.ForwarderPools;
import com.PrivacyGuard.Application.Network.LocalServer;
import com.PrivacyGuard.Application.Network.Resolver.MyClientResolver;
import com.PrivacyGuard.Application.Network.Resolver.MyNetworkHostNameResolver;
import com.PrivacyGuard.Application.PrivacyGuard;
import com.PrivacyGuard.Plugin.ContactDetection;
import com.PrivacyGuard.Plugin.IPlugin;
import com.PrivacyGuard.Plugin.KeywordDetection;
import com.PrivacyGuard.Plugin.LeakReport;
import com.PrivacyGuard.Plugin.LocationDetection;
import com.PrivacyGuard.Plugin.PhoneStateDetection;
import com.PrivacyGuard.Utilities.CertificateManager;

import org.sandrop.webscarab.plugin.proxy.SSLSocketFactoryFactory;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

/**
 * Created by frank on 2014-03-26.
 */
public class MyVpnService extends VpnService implements Runnable {
    public static final String CADir = Logger.getDiskCacheDir().getAbsolutePath();
    public static final String CAName = "PrivacyGuard_CA";
    public static final String CertName = "PrivacyGuard_Cert";
    public static final String KeyType = "PKCS12";
    public static final String Password = "";

    private static final String TAG = "MyVpnService";
    private static final boolean DEBUG = true;
    private static boolean running = false;
    private static boolean started = false;
    private static HashMap<String, Integer[]> notificationMap = new HashMap<String, Integer[]>();

    //The virtual network interface, get and return packets to it
    private ParcelFileDescriptor mInterface;
    private TunWriteThread writeThread;
    private TunReadThread readThread;
    private Thread uiThread;
    //Pools
    private ForwarderPools forwarderPools;
    //SSL stuff
    private SSLSocketFactoryFactory sslSocketFactoryFactory;
    //Network
    private MyNetworkHostNameResolver hostNameResolver;
    private MyClientResolver clientAppResolver;
    private LocalServer localServer;

    // Plugin
    private Class pluginClass[] = { LocationDetection.class, PhoneStateDetection.class, ContactDetection.class,
            // newly added for KeywordDetection
            KeywordDetection.class };
    private ArrayList<IPlugin> plugins;

    // Other
    private SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");

    public static boolean isRunning() {
        /** http://stackoverflow.com/questions/600207/how-to-check-if-a-service-is-running-on-android */
        return running;
    }

    public static boolean isStarted() {
        return started;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Logger.d(TAG, "onStartCommand");
        uiThread = new Thread(this);
        uiThread.start();
        return START_STICKY_COMPATIBILITY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new MyVpnServiceBinder();
    }

    @Override
    public void onRevoke() {
        Logger.d(TAG, "onRevoke");
        stop();
        super.onRevoke();
    }

    @Override
    public void onDestroy() {
        Logger.d(TAG, "onDestroy");
        stop();
        super.onDestroy();

    }

    @Override
    public void run() {
        if (!(setup_network())) {
            return;
        }

        started = false;
        running = true;

        //Notify the MainActivity that the VPN is now running.
        Intent i = new Intent(getString(R.string.vpn_running_broadcast_intent));
        sendBroadcast(i);

        setup_workers();
        wait_to_close();
    }

    private boolean setup_network() {
        Builder b = new Builder();
        b.addAddress("10.8.0.1", 32);
        b.addDnsServer("8.8.8.8");
        b.addRoute("0.0.0.0", 0);
        b.setMtu(1500);
        mInterface = b.establish();
        if (mInterface == null) {
            Logger.d(TAG, "Failed to establish Builder interface");
            return false;
        }
        forwarderPools = new ForwarderPools(this);
        sslSocketFactoryFactory = CertificateManager.initiateFactory(CADir, CAName, CertName, KeyType,
                Password.toCharArray());
        return true;
    }

    private void setup_workers() {
        hostNameResolver = new MyNetworkHostNameResolver(this);
        clientAppResolver = new MyClientResolver(this);

        localServer = new LocalServer(this);
        localServer.start();
        readThread = new TunReadThread(mInterface.getFileDescriptor(), this);
        readThread.start();
        writeThread = new TunWriteThread(mInterface.getFileDescriptor(), this);
        writeThread.start();
    }

    private void wait_to_close() {
        // wait until all threads stop
        try {
            while (writeThread.isAlive())
                writeThread.join();

            while (readThread.isAlive())
                readThread.join();

            while (localServer.isAlive())
                localServer.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void fetchResponse(byte[] response) {
        writeThread.write(response);
    }

    public SSLSocketFactoryFactory getSSlSocketFactoryFactory() {
        return sslSocketFactoryFactory;
    }

    public MyNetworkHostNameResolver getHostNameResolver() {
        return hostNameResolver;
    }

    public MyClientResolver getClientAppResolver() {
        return clientAppResolver;
    }

    public ForwarderPools getForwarderPools() {
        return forwarderPools;
    }

    public ArrayList<IPlugin> getNewPlugins() {
        ArrayList<IPlugin> ret = new ArrayList<IPlugin>();
        try {
            for (Class c : pluginClass) {
                IPlugin temp = (IPlugin) c.newInstance();
                temp.setContext(this);
                ret.add(temp);
            }
            return ret;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    ////////////////////////////////////////////////////
    // Notification Methods
    ///////////////////////////////////////////////////

    // w3kim@uwaterloo.ca : added the 1st parameter
    public void notify(String request, LeakReport leak) {
        //update database

        DatabaseHandler db = new DatabaseHandler(this);

        // w3kim@uwaterloo.ca
        db.addUrlIfAny(leak.appName, leak.packageName, request);

        int notifyId = db.findNotificationId(leak);
        if (notifyId < 0) {
            return;
        }

        int frequency = db.findNotificationCounter(notifyId, leak.category.name());
        db.close();

        buildNotification(notifyId, frequency, leak);

    }

    void buildNotification(int notifyId, int frequency, LeakReport leak) {
        String msg = leak.appName + " is leaking " + leak.category.name() + " information";
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_spam)
                .setContentTitle(leak.appName).setContentText(msg).setNumber(frequency).setTicker(msg)
                .setAutoCancel(true);

        Intent ignoreIntent = new Intent(this, ActionReceiver.class);
        ignoreIntent.setAction("Ignore");
        ignoreIntent.putExtra("notificationId", notifyId);
        // use System.currentTimeMillis() to have a unique ID for the pending intent
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this.getApplicationContext(),
                (int) System.currentTimeMillis(), ignoreIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        mBuilder.addAction(R.drawable.ic_cancel, "Ignore this kind of leaks", pendingIntent);

        // Creates an explicit intent for an Activity in your app
        Intent resultIntent = new Intent(this, AppSummaryActivity.class);
        resultIntent.putExtra(PrivacyGuard.EXTRA_PACKAGE_NAME, leak.packageName);
        resultIntent.putExtra(PrivacyGuard.EXTRA_APP_NAME, leak.appName);
        resultIntent.putExtra(PrivacyGuard.EXTRA_IGNORE, 0);

        // The stack builder object will contain an artificial back stack for the
        // started Activity.
        // This ensures that navigating backward from the Activity leads out of home screen
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        // Adds the back stack for the Intent (but not the Intent itself)
        stackBuilder.addParentStack(AppSummaryActivity.class);
        // Adds the Intent that starts the Activity to the top of the stack
        stackBuilder.addNextIntent(resultIntent);
        PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
        mBuilder.setContentIntent(resultPendingIntent);
        NotificationManager mNotificationManager = (NotificationManager) getSystemService(
                Context.NOTIFICATION_SERVICE);

        // builds the notification and sends it
        mNotificationManager.notify(notifyId, mBuilder.build());

    }

    public void deleteNotification(int id) {
        NotificationManager mNotificationManager = (NotificationManager) getSystemService(
                Context.NOTIFICATION_SERVICE);

        mNotificationManager.cancel(id);
    }

    private void stop() {
        running = false;
        if (mInterface == null)
            return;
        Logger.d(TAG, "Stopping");
        try {
            readThread.interrupt();
            writeThread.interrupt();
            localServer.interrupt();
            mInterface.close();
        } catch (IOException e) {
            Logger.e(TAG, e.toString() + "\n" + Arrays.toString(e.getStackTrace()));
        }
        mInterface = null;
    }

    public void startVPN(Context context) {
        Intent intent = new Intent(context, MyVpnService.class);
        context.startService(intent);
        started = true;
    }

    public void stopVPN() {
        stop();
        stopSelf();
    }

    public class MyVpnServiceBinder extends Binder {
        public MyVpnService getService() {
            // Return this instance of MyVpnService so clients can call public methods
            return MyVpnService.this;
        }

        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
            if (code == IBinder.LAST_CALL_TRANSACTION) {
                onRevoke();
                return true;
            }
            return false;
        }
    }
}