Commit c4833ddb authored by Max Lv's avatar Max Lv

add aidl

parent abf050a4
......@@ -70,17 +70,22 @@
</activity>
<service
android:name=".ShadowsocksService"
android:name=".ShadowsocksNatService"
android:process=":proxy"
android:exported="false"/>
android:exported="false">
<intent-filter>
<action android:name="com.github.shadowsocks.ShadowsocksNatService"/>
</intent-filter>
</service>
<service
android:name=".ShadowVpnService"
android:name=".ShadowsocksVpnService"
android:label="@string/app_name"
android:process=":vpn"
android:permission="android.permission.BIND_VPN_SERVICE"
android:exported="false">
<intent-filter>
<action android:name="com.github.shadowsocks.ShadowsocksVpnService"/>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
......
package com.github.shadowsocks.aidl;
parcelable Config;
\ No newline at end of file
package com.github.shadowsocks.aidl;
import android.os.Parcel;
import android.os.Parcelable;
public class Config implements Parcelable {
public boolean isGlobalProxy = true;
public boolean isGFWList = true;
public boolean isBypassApps = false;
public boolean isTrafficStat = false;
public String profileName = "Untitled";
public String proxy = "127.0.0.1";
public String sitekey = "null";
public String encMethod = "rc4";
public String proxiedAppString = "";
public int remotePort = 1984;
public int localPort = 1080;
public static final Parcelable.Creator<Config> CREATOR = new
Parcelable.Creator<Config>() {
public Config createFromParcel(Parcel in) {
return new Config(in);
}
public Config[] newArray(int size) {
return new Config[size];
}
};
public Config(boolean isGlobalProxy, boolean isGFWList, boolean isBypassApps,
boolean isTrafficStat, String profileName, String proxy, String sitekey, String encMethod,
String proxiedAppString, int remotePort, int localPort) {
this.isGlobalProxy = isGlobalProxy;
this.isGFWList = isGFWList;
this.isBypassApps = isBypassApps;
this.isTrafficStat = isTrafficStat;
this.profileName = profileName;
this.proxy = proxy;
this.sitekey = sitekey;
this.encMethod = encMethod;
this.proxiedAppString = proxiedAppString;
this.remotePort = remotePort;
this.localPort = localPort;
}
private Config(Parcel in) {
readFromParcel(in);
}
public void readFromParcel(Parcel in) {
isGlobalProxy = in.readInt() == 1;
isGFWList = in.readInt() == 1;
isBypassApps = in.readInt() == 1;
isTrafficStat = in.readInt() == 1;
profileName = in.readString();
proxy = in.readString();
sitekey = in.readString();
encMethod = in.readString();
proxiedAppString = in.readString();
remotePort = in.readInt();
localPort = in.readInt();
}
@Override public int describeContents() {
return 0;
}
@Override public void writeToParcel(Parcel out, int flags) {
out.writeInt(isGlobalProxy ? 1: 0);
out.writeInt(isGFWList ? 1: 0);
out.writeInt(isBypassApps ? 1: 0);
out.writeInt(isTrafficStat ? 1: 0);
out.writeString(profileName);
out.writeString(proxy);
out.writeString(sitekey);
out.writeString(encMethod);
out.writeString(proxiedAppString);
out.writeInt(remotePort);
out.writeInt(localPort);
}
}
package com.github.shadowsocks.aidl;
import com.github.shadowsocks.aidl.Config;
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback;
interface IShadowsocksService {
int getMode();
int getState();
void start(in Config config);
void stop();
void registerCallback(IShadowsocksServiceCallback cb);
void unregisterCallback(IShadowsocksServiceCallback cb);
}
\ No newline at end of file
package com.github.shadowsocks.aidl;
interface IShadowsocksServiceCallback {
void stateChanged(int state, String msg);
}
\ No newline at end of file
......@@ -17,6 +17,7 @@
<org.jraf.android.backport.switchwidget.Switch
android:id="@+id/switchButton"
android:enabled="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
......
package com.github.shadowsocks
import android.os.RemoteCallbackList
import com.github.shadowsocks.aidl.{Config, IShadowsocksService, IShadowsocksServiceCallback}
import com.github.shadowsocks.utils.{Path, State}
import java.io.{IOException, FileNotFoundException, FileReader, BufferedReader}
import android.util.Log
import android.app.Notification
trait BaseService {
var state = State.INIT
final val callbacks = new RemoteCallbackList[IShadowsocksServiceCallback]
protected val binder = new IShadowsocksService.Stub {
override def getMode: Int = {
getServiceMode
}
override def getState: Int = {
state
}
override def unregisterCallback(cb: IShadowsocksServiceCallback) {
if (cb != null ) callbacks.unregister(cb)
}
override def registerCallback(cb: IShadowsocksServiceCallback) {
if (cb != null) callbacks.register(cb)
}
override def stop() {
stopRunner()
}
override def start(config: Config) {
startRunner(config)
}
}
def startRunner(config: Config)
def stopRunner()
def getServiceMode: Int
def getTag: String
def changeState(s: Int) {
changeState(s, null)
}
protected def changeState(s: Int, msg: String) {
if (state != s) {
val n = callbacks.beginBroadcast()
for (i <- 0 to n -1) {
callbacks.getBroadcastItem(i).stateChanged(s, msg)
}
callbacks.finishBroadcast()
state = s
}
}
def getPid(name: String): Int = {
try {
val reader: BufferedReader = new BufferedReader(new FileReader(Path.BASE + name + ".pid"))
val line = reader.readLine
return Integer.valueOf(line)
} catch {
case e: FileNotFoundException =>
Log.e(getTag, "Cannot open pid file: " + name)
case e: IOException =>
Log.e(getTag, "Cannot read pid file: " + name)
case e: NumberFormatException =>
Log.e(getTag, "Invalid pid", e)
}
-1
}
def initSoundVibrateLights(notification: Notification) {
notification.sound = null
}
}
......@@ -40,16 +40,28 @@
package com.github.shadowsocks
import android.app.Activity
import android.os.Bundle
import android.os.{IBinder, Bundle}
import android.net.VpnService
import android.content.Intent
import android.content.{ComponentName, ServiceConnection, Intent}
import android.util.Log
import android.preference.PreferenceManager
import com.github.shadowsocks.utils.Extra
import com.actionbarsherlock.app.SherlockActivity
import com.github.shadowsocks.utils._
import com.github.shadowsocks.aidl.IShadowsocksService
class ShadowVpnActivity extends Activity {
// Services
var bgService: IShadowsocksService = null
val connection = new ServiceConnection {
override def onServiceConnected(name: ComponentName, service: IBinder) {
bgService = IShadowsocksService.Stub.asInterface(service)
}
override def onServiceDisconnected(name: ComponentName) {
bgService = null
}
}
override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
val intent = VpnService.prepare(this)
......@@ -62,15 +74,13 @@ class ShadowVpnActivity extends Activity {
override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
resultCode match {
case Activity.RESULT_OK => {
val intent: Intent = new Intent(this, classOf[ShadowVpnService])
Extra.put(PreferenceManager.getDefaultSharedPreferences(this), intent)
startService(intent)
case Activity.RESULT_OK =>
if (bgService != null) {
bgService.start(ConfigUtils.load(PreferenceManager.getDefaultSharedPreferences(this)))
}
case _ => {
case _ =>
Log.e(Shadowsocks.TAG, "Failed to start VpnService")
}
}
finish()
}
}
......@@ -54,33 +54,17 @@ class ShadowsocksReceiver extends BroadcastReceiver {
val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
val status = context.getSharedPreferences(Key.status, Context.MODE_PRIVATE)
if (intent.getAction == Action.UPDATE_STATE) {
val state = intent.getIntExtra(Extra.STATE, State.INIT)
val running = state match {
case State.CONNECTING => true
case State.CONNECTED => true
case _ => false
}
status.edit.putBoolean(Key.isRunning, running).commit()
return
}
var versionName: String = null
try {
versionName = context.getPackageManager.getPackageInfo(context.getPackageName, 0).versionName
} catch {
case e: PackageManager.NameNotFoundException => {
case e: PackageManager.NameNotFoundException =>
versionName = "NONE"
}
}
val isAutoConnect: Boolean = settings.getBoolean(Key.isAutoConnect, false)
val isInstalled: Boolean = status.getBoolean(versionName, false)
if (isAutoConnect && isInstalled) {
if (Utils.getRoot) {
if (ShadowsocksService.isServiceStarted(context)) return
val intent: Intent = new Intent(context, classOf[ShadowsocksService])
Extra.put(settings, intent)
context.startService(intent)
}
}
}
......
package com.github.shadowsocks.utils
import android.content.Context
import android.content.{Intent, SharedPreferences, Context}
import com.github.shadowsocks.ShadowsocksApplication
import com.google.tagmanager.Container
import scalaj.http.{HttpOptions, Http}
import com.github.shadowsocks.aidl.Config
object Config {
object ConfigUtils {
val SHADOWSOCKS = "{\"server\": [%s], \"server_port\": %d, \"local_port\": %d, \"password\": %s, \"timeout\": %d}"
val REDSOCKS = "base {" +
" log_debug = off;" +
......@@ -83,10 +84,35 @@ object Config {
val method = proxy(3).trim
new Config(config.isGlobalProxy, config.isGFWList, config.isBypassApps, config.isTrafficStat,
config.profileName, host, password, method, port, config.localPort, config.proxiedAppString)
config.profileName, host, password, method, config.proxiedAppString, port, config.localPort)
}
}
case class Config(isGlobalProxy: Boolean, isGFWList: Boolean, isBypassApps: Boolean,
isTrafficStat: Boolean, profileName: String, var proxy: String, sitekey: String,
encMethod: String, remotePort: Int, localPort: Int, proxiedAppString: String)
def load(settings: SharedPreferences): Config = {
val isGlobalProxy = settings.getBoolean(Key.isGlobalProxy, false)
val isGFWList = settings.getBoolean(Key.isGFWList, false)
val isBypassApps = settings.getBoolean(Key.isBypassApps, false)
val isTrafficStat = settings.getBoolean(Key.isTrafficStat, false)
val profileName = settings.getString(Key.profileName, "default")
val proxy = settings.getString(Key.proxy, "127.0.0.1")
val sitekey = settings.getString(Key.sitekey, "default")
val encMethod = settings.getString(Key.encMethod, "table")
val remotePort: Int = try {
settings.getString(Key.remotePort, "1984").toInt
} catch {
case ex: NumberFormatException =>
1984
}
val localPort: Int = try {
settings.getString(Key.localPort, "1984").toInt
} catch {
case ex: NumberFormatException =>
1984
}
val proxiedAppString = settings.getString(Key.proxied, "")
new Config(isGlobalProxy, isGFWList, isBypassApps, isTrafficStat, profileName, proxy, sitekey,
encMethod, proxiedAppString, remotePort, localPort)
}
}
\ No newline at end of file
package com.github.shadowsocks.utils
import android.content.{Intent, SharedPreferences}
import com.github.shadowsocks.aidl.Config
object Msg {
val CONNECT_FINISH = 1
val CONNECT_SUCCESS = 2
val CONNECT_FAIL = 3
val VPN_ERROR = 6
}
object Path {
val BASE = "/data/data/com.github.shadowsocks/"
......@@ -37,100 +45,21 @@ object Scheme {
val SS = "ss"
}
object Mode {
val NAT = 0
val VPN = 1
}
object State {
val INIT = 0
val CONNECTING = 1
val CONNECTED = 2
val STOPPED = 3
def isAvailable(state: Int): Boolean = state != CONNECTED && state != CONNECTING
}
object Action {
val CLOSE = "com.github.shadowsocks.ACTION_SHUTDOWN"
val UPDATE_STATE = "com.github.shadowsocks.ACTION_UPDATE_STATE"
val CLOSE = "com.github.shadowsocks.CLOSE"
val UPDATE_FRAGMENT = "com.github.shadowsocks.ACTION_UPDATE_FRAGMENT"
val UPDATE_PREFS = "com.github.shadowsocks.ACTION_UPDATE_PREFS"
}
\ No newline at end of file
object Extra {
val STATE = "state"
val MESSAGE = "message"
def save(settings: SharedPreferences, config: Config) {
val edit = settings.edit()
edit.putBoolean(Key.isGlobalProxy, config.isGlobalProxy)
edit.putBoolean(Key.isGFWList, config.isGFWList)
edit.putBoolean(Key.isBypassApps, config.isBypassApps)
edit.putBoolean(Key.isTrafficStat, config.isTrafficStat)
edit.putString(Key.profileName, config.profileName)
edit.putString(Key.proxy, config.proxy)
edit.putString(Key.sitekey, config.sitekey)
edit.putString(Key.encMethod, config.encMethod)
edit.putString(Key.remotePort, config.remotePort.toString)
edit.putString(Key.localPort, config.localPort.toString)
edit.apply()
}
def get(intent: Intent): Config = {
val isGlobalProxy = intent.getBooleanExtra(Key.isGlobalProxy, false)
val isGFWList = intent.getBooleanExtra(Key.isGFWList, false)
val isBypassApps = intent.getBooleanExtra(Key.isBypassApps, false)
val isTrafficStat = intent.getBooleanExtra(Key.isTrafficStat, false)
val profileName = intent.getStringExtra(Key.profileName)
val proxy = intent.getStringExtra(Key.proxy)
val sitekey = intent.getStringExtra(Key.sitekey)
val encMethod = intent.getStringExtra(Key.encMethod)
val remotePort = intent.getIntExtra(Key.remotePort, 1984)
val localPort = intent.getIntExtra(Key.localPort, 1984)
val proxiedString = intent.getStringExtra(Key.proxied)
new Config(isGlobalProxy, isGFWList, isBypassApps, isTrafficStat, profileName, proxy, sitekey,
encMethod, remotePort, localPort, proxiedString)
}
def put(settings: SharedPreferences, intent: Intent) {
val isGlobalProxy = settings.getBoolean(Key.isGlobalProxy, false)
val isGFWList = settings.getBoolean(Key.isGFWList, false)
val isBypassApps = settings.getBoolean(Key.isBypassApps, false)
val isTrafficStat = settings.getBoolean(Key.isTrafficStat, false)
val profileName = settings.getString(Key.profileName, "default")
val proxy = settings.getString(Key.proxy, "127.0.0.1")
val sitekey = settings.getString(Key.sitekey, "default")
val encMethod = settings.getString(Key.encMethod, "table")
val remotePort: Int = try {
settings.getString(Key.remotePort, "1984").toInt
} catch {
case ex: NumberFormatException => {
1984
}
}
val localProt: Int = try {
settings.getString(Key.localPort, "1984").toInt
} catch {
case ex: NumberFormatException => {
1984
}
}
val proxiedAppString = settings.getString(Key.proxied, "")
intent.putExtra(Key.isGlobalProxy, isGlobalProxy)
intent.putExtra(Key.isGFWList, isGFWList)
intent.putExtra(Key.isBypassApps, isBypassApps)
intent.putExtra(Key.isTrafficStat, isTrafficStat)
intent.putExtra(Key.profileName, profileName)
intent.putExtra(Key.proxy, proxy)
intent.putExtra(Key.sitekey, sitekey)
intent.putExtra(Key.encMethod, encMethod)
intent.putExtra(Key.remotePort, remotePort)
intent.putExtra(Key.localPort, localProt)
intent.putExtra(Key.proxied, proxiedAppString)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment