Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
com.ccwangluo.accelerator
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
sheteng
com.ccwangluo.accelerator
Commits
0b8114ad
Commit
0b8114ad
authored
Feb 09, 2014
by
Max Lv
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #64 from shadowsocks/aidl
AIDL
parents
abf050a4
59d304ea
Changes
14
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
794 additions
and
697 deletions
+794
-697
src/main/AndroidManifest.xml
src/main/AndroidManifest.xml
+9
-4
src/main/java/com/github/shadowsocks/aidl/Config.aidl
src/main/java/com/github/shadowsocks/aidl/Config.aidl
+3
-0
src/main/java/com/github/shadowsocks/aidl/Config.java
src/main/java/com/github/shadowsocks/aidl/Config.java
+86
-0
src/main/java/com/github/shadowsocks/aidl/IShadowsocksService.aidl
...java/com/github/shadowsocks/aidl/IShadowsocksService.aidl
+13
-0
src/main/java/com/github/shadowsocks/aidl/IShadowsocksServiceCallback.aidl
.../github/shadowsocks/aidl/IShadowsocksServiceCallback.aidl
+5
-0
src/main/res/layout/layout_switch.xml
src/main/res/layout/layout_switch.xml
+1
-0
src/main/scala/com/github/shadowsocks/BaseService.scala
src/main/scala/com/github/shadowsocks/BaseService.scala
+88
-0
src/main/scala/com/github/shadowsocks/Shadowsocks.scala
src/main/scala/com/github/shadowsocks/Shadowsocks.scala
+208
-176
src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
.../scala/com/github/shadowsocks/ShadowsocksNatService.scala
+163
-217
src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala
...in/scala/com/github/shadowsocks/ShadowsocksReceiver.scala
+2
-19
src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala
...la/com/github/shadowsocks/ShadowsocksRunnerActivity.scala
+55
-18
src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
.../scala/com/github/shadowsocks/ShadowsocksVpnService.scala
+112
-169
src/main/scala/com/github/shadowsocks/utils/ConfigUtils.scala
...main/scala/com/github/shadowsocks/utils/ConfigUtils.scala
+33
-7
src/main/scala/com/github/shadowsocks/utils/Constants.scala
src/main/scala/com/github/shadowsocks/utils/Constants.scala
+16
-87
No files found.
src/main/AndroidManifest.xml
View file @
0b8114ad
...
...
@@ -54,7 +54,7 @@
</activity>
<activity
android:name=
".Shadow
Vpn
Activity"
android:name=
".Shadow
socksRunner
Activity"
android:theme=
"@style/PopupTheme"
android:launchMode=
"singleTask"
>
</activity>
...
...
@@ -70,17 +70,22 @@
</activity>
<service
android:name=
".ShadowsocksService"
android:name=
".Shadowsocks
Nat
Service"
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=
".Shadow
socks
VpnService"
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>
...
...
src/main/java/com/github/shadowsocks/aidl/Config.aidl
0 → 100644
View file @
0b8114ad
package
com
.
github
.
shadowsocks
.
aidl
;
parcelable
Config
;
\ No newline at end of file
src/main/java/com/github/shadowsocks/aidl/Config.java
0 → 100644
View file @
0b8114ad
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
);
}
}
src/main/java/com/github/shadowsocks/aidl/IShadowsocksService.aidl
0 → 100644
View file @
0b8114ad
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
src/main/java/com/github/shadowsocks/aidl/IShadowsocksServiceCallback.aidl
0 → 100644
View file @
0b8114ad
package
com
.
github
.
shadowsocks
.
aidl
;
interface
IShadowsocksServiceCallback
{
void
stateChanged
(
int
state
,
String
msg
);
}
\ No newline at end of file
src/main/res/layout/layout_switch.xml
View file @
0b8114ad
...
...
@@ -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"
...
...
src/main/scala/com/github/shadowsocks/BaseService.scala
0 → 100644
View file @
0b8114ad
package
com.github.shadowsocks
import
android.os.
{
Handler
,
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
import
android.content.Context
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
getContext
:
Context
def
changeState
(
s
:
Int
)
{
changeState
(
s
,
null
)
}
protected
def
changeState
(
s
:
Int
,
msg
:
String
)
{
val
handler
=
new
Handler
(
getContext
.
getMainLooper
)
handler
.
post
(
new
Runnable
{
override
def
run
()
{
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
}
}
src/main/scala/com/github/shadowsocks/Shadowsocks.scala
View file @
0b8114ad
...
...
@@ -67,12 +67,11 @@ import scala.collection.mutable.ListBuffer
import
com.github.shadowsocks.database.Profile
import
com.nostra13.universalimageloader.core.download.BaseImageDownloader
import
com.github.shadowsocks.preferences.
{
ProfileEditTextPreference
,
PasswordEditTextPreference
,
SummaryEditTextPreference
}
import
com.github.shadowsocks.database.Item
import
com.github.shadowsocks.database.Category
import
com.github.shadowsocks.utils._
import
com.github.shadowsocks.database.Item
import
com.github.shadowsocks.database.Category
import
com.google.zxing.integration.android.IntentIntegrator
import
com.github.shadowsocks.aidl.
{
IShadowsocksServiceCallback
,
IShadowsocksService
}
class
ProfileIconDownloader
(
context
:
Context
,
connectTimeout
:
Int
,
readTimeout
:
Int
)
extends
BaseImageDownloader
(
context
,
connectTimeout
,
readTimeout
)
{
...
...
@@ -103,12 +102,11 @@ object Typefaces {
val
t
:
Typeface
=
Typeface
.
createFromAsset
(
c
.
getAssets
,
assetPath
)
cache
.
put
(
assetPath
,
t
)
}
catch
{
case
e
:
Exception
=>
{
case
e
:
Exception
=>
Log
.
e
(
TAG
,
"Could not get typeface '"
+
assetPath
+
"' because "
+
e
.
getMessage
)
return
null
}
}
}
return
cache
.
get
(
assetPath
)
}
}
...
...
@@ -131,8 +129,7 @@ object Shadowsocks {
// Flags
var
vpnEnabled
=
-
1
// Help functions
// Helper functions
def
updateListPreference
(
pref
:
Preference
,
value
:
String
)
{
pref
.
setSummary
(
value
)
pref
.
asInstanceOf
[
ListPreference
].
setValue
(
value
)
...
...
@@ -172,10 +169,6 @@ object Shadowsocks {
}
}
def
isServiceStarted
(
context
:
Context
)
:
Boolean
=
{
ShadowsocksService
.
isServiceStarted
(
context
)
||
ShadowVpnService
.
isServiceStarted
(
context
)
}
class
ProxyFragment
extends
UnifiedPreferenceFragment
{
var
receiver
:
BroadcastReceiver
=
null
...
...
@@ -194,8 +187,8 @@ object Shadowsocks {
private
def
updatePreferenceScreen
()
{
for
(
name
<-
Shadowsocks
.
PROXY_PREFS
)
{
val
pref
=
findPreference
(
name
)
Shadowsocks
.
updatePreference
(
pref
,
name
,
getActivity
.
asInstanceOf
[
Shadowsocks
].
currentProfile
)
Shadowsocks
.
updatePreference
(
pref
,
name
,
getActivity
.
asInstanceOf
[
Shadowsocks
].
currentProfile
)
}
}
...
...
@@ -252,8 +245,8 @@ object Shadowsocks {
private
def
updatePreferenceScreen
()
{
for
(
name
<-
Shadowsocks
.
FEATRUE_PREFS
)
{
val
pref
=
findPreference
(
name
)
Shadowsocks
.
updatePreference
(
pref
,
name
,
getActivity
.
asInstanceOf
[
Shadowsocks
].
currentProfile
)
Shadowsocks
.
updatePreference
(
pref
,
name
,
getActivity
.
asInstanceOf
[
Shadowsocks
].
currentProfile
)
}
}
...
...
@@ -304,9 +297,49 @@ class Shadowsocks
var
prepared
=
false
var
currentProfile
=
new
Profile
// Services
var
currentServiceName
=
classOf
[
ShadowsocksNatService
].
getName
var
bgService
:
IShadowsocksService
=
null
val
callback
=
new
IShadowsocksServiceCallback
.
Stub
{
override
def
stateChanged
(
state
:
Int
,
msg
:
String
)
{
onStateChanged
(
state
,
msg
)
}
}
val
connection
=
new
ServiceConnection
{
override
def
onServiceConnected
(
name
:
ComponentName
,
service
:
IBinder
)
{
// Initialize the background service
bgService
=
IShadowsocksService
.
Stub
.
asInterface
(
service
)
try
{
bgService
.
registerCallback
(
callback
)
}
catch
{
case
ignored
:
RemoteException
=>
// Nothing
}
// Update the UI
switchButton
.
setEnabled
(
true
)
if
(
State
.
isAvailable
(
bgService
.
getState
))
{
if
(
status
.
getBoolean
(
Key
.
isRunning
,
false
))
{
spawn
{
crash_recovery
()
handler
.
sendEmptyMessage
(
MSG_CRASH_RECOVER
)
}
}
Crouton
.
cancelAllCroutons
()
setPreferenceEnabled
(
enabled
=
true
)
}
else
{
changeSwitch
(
checked
=
true
)
setPreferenceEnabled
(
enabled
=
false
)
}
state
=
bgService
.
getState
}
override
def
onServiceDisconnected
(
name
:
ComponentName
)
{
switchButton
.
setEnabled
(
false
)
bgService
=
null
}
}
lazy
val
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
)
lazy
val
status
=
getSharedPreferences
(
Key
.
status
,
Context
.
MODE_PRIVATE
)
lazy
val
stateReceiver
=
new
StateBroadcastReceiver
lazy
val
preferenceReceiver
=
new
PreferenceBroadcastReceiver
lazy
val
drawer
=
MenuDrawer
.
attach
(
this
)
lazy
val
menuAdapter
=
new
MenuAdapter
(
this
,
getMenuList
)
...
...
@@ -325,6 +358,18 @@ class Shadowsocks
}
}
private
def
changeSwitch
(
checked
:
Boolean
)
{
switchButton
.
setOnCheckedChangeListener
(
null
)
switchButton
.
setChecked
(
checked
)
if
(
switchButton
.
isEnabled
)
{
switchButton
.
setEnabled
(
false
)
handler
.
postDelayed
(
new
Runnable
{
override
def
run
()
{
switchButton
.
setEnabled
(
true
)
}
},
1000
)
}
switchButton
.
setOnCheckedChangeListener
(
this
)
}
private
def
showProgress
(
msg
:
String
)
:
Handler
=
{
clearDialog
()
progressDialog
=
ProgressDialog
.
show
(
this
,
""
,
msg
,
true
,
false
)
...
...
@@ -341,10 +386,9 @@ class Shadowsocks
try
{
files
=
assetManager
.
list
(
path
)
}
catch
{
case
e
:
IOException
=>
{
case
e
:
IOException
=>
Log
.
e
(
Shadowsocks
.
TAG
,
e
.
getMessage
)
}
}
if
(
files
!=
null
)
{
for
(
file
<-
files
)
{
var
in
:
InputStream
=
null
...
...
@@ -363,13 +407,12 @@ class Shadowsocks
out
.
close
()
out
=
null
}
catch
{
case
ex
:
Exception
=>
{
case
ex
:
Exception
=>
Log
.
e
(
Shadowsocks
.
TAG
,
ex
.
getMessage
)
}
}
}
}
}
private
def
copyFile
(
in
:
InputStream
,
out
:
OutputStream
)
{
val
buffer
:
Array
[
Byte
]
=
new
Array
[
Byte
](
1024
)
...
...
@@ -409,21 +452,29 @@ class Shadowsocks
val
pi
:
PackageInfo
=
getPackageManager
.
getPackageInfo
(
getPackageName
,
0
)
version
=
pi
.
versionName
}
catch
{
case
e
:
PackageManager.NameNotFoundException
=>
{
case
e
:
PackageManager.NameNotFoundException
=>
version
=
"Package name not found"
}
}
version
}
private
def
isTextEmpty
(
s
:
String
,
msg
:
String
)
:
Boolean
=
{
def
isTextEmpty
(
s
:
String
,
msg
:
String
)
:
Boolean
=
{
if
(
s
==
null
||
s
.
length
<=
0
)
{
showDialog
(
msg
)
Crouton
.
makeText
(
this
,
msg
,
Style
.
ALERT
).
show
(
)
return
true
}
false
}
def
cancelStart
()
{
handler
.
postDelayed
(
new
Runnable
{
override
def
run
()
{
clearDialog
()
changeSwitch
(
checked
=
false
)
}
},
1000
)
}
def
prepareStartService
()
{
showProgress
(
getString
(
R
.
string
.
connecting
))
spawn
{
...
...
@@ -436,7 +487,7 @@ class Shadowsocks
}
}
else
{
if
(!
serviceStart
)
{
switchButton
.
setChecked
(
false
)
cancelStart
(
)
}
}
}
...
...
@@ -445,12 +496,16 @@ class Shadowsocks
def
onCheckedChanged
(
compoundButton
:
CompoundButton
,
checked
:
Boolean
)
{
if
(
compoundButton
eq
switchButton
)
{
checked
match
{
case
true
=>
{
case
true
=>
prepareStartService
()
}
case
false
=>
{
case
false
=>
serviceStop
()
}
if
(
switchButton
.
isEnabled
)
{
switchButton
.
setEnabled
(
false
)
handler
.
postDelayed
(
new
Runnable
{
override
def
run
()
{
switchButton
.
setEnabled
(
true
)
}
},
1000
)
}
}
}
...
...
@@ -491,18 +546,28 @@ class Shadowsocks
onContentChanged
()
}
/** Called when the activity is first created. */
override
def
onCreate
(
savedInstanceState
:
Bundle
)
{
// Initialize preference
// Initialize
the
preference
setHeaderRes
(
R
.
xml
.
shadowsocks_headers
)
super
.
onCreate
(
savedInstanceState
)
// Initialize profile
// Initialize
the
profile
currentProfile
=
{
profileManager
.
getProfile
(
settings
.
getInt
(
Key
.
profileId
,
-
1
))
getOrElse
currentProfile
}
// Update the profile
if
(!
status
.
getBoolean
(
getVersionName
,
false
))
{
val
h
=
showProgress
(
getString
(
R
.
string
.
initializing
))
status
.
edit
.
putBoolean
(
getVersionName
,
true
).
apply
()
spawn
{
reset
()
currentProfile
=
profileManager
.
create
()
h
.
sendEmptyMessage
(
0
)
}
}
// Initialize drawer
menuAdapter
.
setActiveId
(
settings
.
getInt
(
Key
.
profileId
,
-
1
))
menuAdapter
.
setListener
(
this
)
...
...
@@ -527,23 +592,34 @@ class Shadowsocks
getSupportActionBar
.
setIcon
(
R
.
drawable
.
ic_stat_shadowsocks
)
// Register broadcast receiver
registerReceiver
(
stateReceiver
,
new
IntentFilter
(
Action
.
UPDATE_STATE
))
registerReceiver
(
preferenceReceiver
,
new
IntentFilter
(
Action
.
UPDATE_PREFS
))
// Update status
if
(!
Shadowsocks
.
isServiceStarted
(
this
))
{
// Bind to the service
spawn
{
status
.
edit
.
putBoolean
(
Key
.
isRoot
,
Utils
.
getRoot
).
apply
()
val
isRoot
=
Utils
.
getRoot
handler
.
post
(
new
Runnable
{
override
def
run
()
{
status
.
edit
.
putBoolean
(
Key
.
isRoot
,
isRoot
).
commit
()
attachService
()
}
if
(!
status
.
getBoolean
(
getVersionName
,
false
))
{
val
h
=
showProgress
(
getString
(
R
.
string
.
initializing
))
status
.
edit
.
putBoolean
(
getVersionName
,
true
).
apply
()
spawn
{
reset
()
currentProfile
=
profileManager
.
create
()
h
.
sendEmptyMessage
(
0
)
})
}
}
def
attachService
()
{
if
(
bgService
==
null
)
{
val
isRoot
=
status
.
getBoolean
(
Key
.
isRoot
,
false
)
val
s
=
if
(
isRoot
)
classOf
[
ShadowsocksNatService
]
else
classOf
[
ShadowsocksVpnService
]
bindService
(
new
Intent
(
s
.
getName
),
connection
,
Context
.
BIND_AUTO_CREATE
)
startService
(
new
Intent
(
Shadowsocks
.
this
,
s
))
}
}
def
deattachService
()
{
if
(
bgService
!=
null
)
{
bgService
.
unregisterCallback
(
callback
)
unbindService
(
connection
)
bgService
=
null
}
}
...
...
@@ -571,15 +647,15 @@ class Shadowsocks
drawer
.
setActiveView
(
v
,
pos
)
}
def
newProfile
(
id
:
Int
)
{
val
builder
=
new
AlertDialog
.
Builder
(
this
)
builder
.
setTitle
(
R
.
string
.
add_profile
)
builder
.
setTitle
(
R
.
string
.
add_profile
)
.
setItems
(
R
.
array
.
add_profile_methods
,
new
DialogInterface
.
OnClickListener
()
{
def
onClick
(
dialog
:
DialogInterface
,
which
:
Int
)
{
which
match
{
case
0
=>
{
case
0
=>
dialog
.
dismiss
()
val
h
=
showProgress
(
getString
(
R
.
string
.
loading
))
h
.
postDelayed
(
new
Runnable
()
{
...
...
@@ -589,17 +665,14 @@ class Shadowsocks
h
.
sendEmptyMessage
(
0
)
}
},
600
)
}
case
1
=>
{
case
1
=>
dialog
.
dismiss
()
addProfile
(
id
)
}
case
_
=>
}
}
})
builder
.
create
().
show
()
}
def
addProfile
(
profile
:
Profile
)
{
...
...
@@ -753,53 +826,35 @@ class Shadowsocks
override
def
onOptionsItemSelected
(
item
:
com.actionbarsherlock.view.MenuItem
)
:
Boolean
=
{
item
.
getItemId
match
{
case
android
.
R
.
id
.
home
=>
{
case
android
.
R
.
id
.
home
=>
drawer
.
toggleMenu
()
return
true
}
}
super
.
onOptionsItemSelected
(
item
)
}
protected
override
def
onPause
()
{
super
.
onPause
()
switchButton
.
setOnCheckedChangeListener
(
null
)
prepared
=
false
}
protected
override
def
onResume
()
{
super
.
onResume
()
if
(!
prepared
)
{
if
(
Shadowsocks
.
isServiceStarted
(
this
))
{
switchButton
.
setChecked
(
true
)
if
(
ShadowVpnService
.
isServiceStarted
(
this
))
{
val
style
=
new
Style
.
Builder
().
setBackgroundColorValue
(
Style
.
holoBlueLight
).
build
()
val
config
=
new
Configuration
.
Builder
().
setDuration
(
Configuration
.
DURATION_LONG
).
build
()
switchButton
.
setEnabled
(
false
)
Crouton
.
makeText
(
Shadowsocks
.
this
,
R
.
string
.
vpn_status
,
style
)
.
setConfiguration
(
config
)
.
show
()
}
setPreferenceEnabled
(
enabled
=
false
)
onStateChanged
(
State
.
CONNECTED
,
null
)
}
else
{
switchButton
.
setEnabled
(
true
)
switchButton
.
setChecked
(
false
)
Crouton
.
cancelAllCroutons
()
setPreferenceEnabled
(
enabled
=
true
)
if
(
status
.
getBoolean
(
Key
.
isRunning
,
false
))
{
spawn
{
crash_recovery
()
handler
.
sendEmptyMessage
(
MSG_CRASH_RECOVER
)
}
}
onStateChanged
(
State
.
STOPPED
,
null
)
if
(
bgService
!=
null
)
{
bgService
.
getState
match
{
case
State
.
CONNECTED
=>
changeSwitch
(
checked
=
true
)
case
State
.
CONNECTING
=>
changeSwitch
(
checked
=
true
)
case
_
=>
changeSwitch
(
checked
=
false
)
}
state
=
bgService
.
getState
}
switchButton
.
setOnCheckedChangeListener
(
this
)
Config
.
refresh
(
this
)
// set the listener
switchButton
.
setOnCheckedChangeListener
(
Shadowsocks
.
this
)
ConfigUtils
.
refresh
(
this
)
}
private
def
setPreferenceEnabled
(
enabled
:
Boolean
)
{
...
...
@@ -848,8 +903,8 @@ class Shadowsocks
override
def
onDestroy
()
{
super
.
onDestroy
()
deattachService
()
Crouton
.
cancelAllCroutons
()
unregisterReceiver
(
stateReceiver
)
unregisterReceiver
(
preferenceReceiver
)
new
BackupManager
(
this
).
dataChanged
()
}
...
...
@@ -866,7 +921,6 @@ class Shadowsocks
private
def
recovery
()
{
val
h
=
showProgress
(
getString
(
R
.
string
.
recovering
))
serviceStop
()
spawn
{
reset
()
...
...
@@ -891,24 +945,22 @@ class Shadowsocks
}
}
else
{
resultCode
match
{
case
Activity
.
RESULT_OK
=>
{
case
Activity
.
RESULT_OK
=>
prepared
=
true
if
(!
serviceStart
)
{
switchButton
.
setChecked
(
false
)
cancelStart
(
)
}
}
case
_
=>
{
clearDialog
()
case
_
=>
cancelStart
()
Log
.
e
(
Shadowsocks
.
TAG
,
"Failed to start VpnService"
)
}
}
}
}
def
isVpnEnabled
:
Boolean
=
{
if
(
Shadowsocks
.
vpnEnabled
<
0
)
{
Shadowsocks
.
vpnEnabled
=
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
ICE_CREAM_SANDWICH
&&
!
Utils
.
getRoot
)
{
>=
Build
.
VERSION_CODES
.
ICE_CREAM_SANDWICH
&&
!
status
.
getBoolean
(
Key
.
isRoot
,
false
)
)
{
1
}
else
{
0
...
...
@@ -918,43 +970,48 @@ class Shadowsocks
}
def
serviceStop
()
{
sendBroadcast
(
new
Intent
(
Action
.
CLOSE
)
)
if
(
bgService
!=
null
)
bgService
.
stop
(
)
}
/** Called when connect button is clicked. */
def
serviceStart
:
Boolean
=
{
def
checkText
(
key
:
String
)
:
Boolean
=
{
val
text
=
settings
.
getString
(
key
,
""
)
!
isTextEmpty
(
text
,
getString
(
R
.
string
.
proxy_empty
))
}
val
proxy
=
settings
.
getString
(
Key
.
proxy
,
""
)
if
(
isTextEmpty
(
proxy
,
getString
(
R
.
string
.
proxy_empty
)))
return
false
val
portText
=
settings
.
getString
(
Key
.
localPort
,
""
)
if
(
isTextEmpty
(
portText
,
getString
(
R
.
string
.
port_empty
)))
return
false
def
checkNumber
(
key
:
String
)
:
Boolean
=
{
val
text
=
settings
.
getString
(
key
,
""
)
if
(
isTextEmpty
(
text
,
getString
(
R
.
string
.
port_empty
)))
return
false
try
{
val
port
:
Int
=
Integer
.
valueOf
(
portT
ext
)
val
port
:
Int
=
Integer
.
valueOf
(
t
ext
)
if
(
port
<=
1024
)
{
this
.
showDialog
(
getString
(
R
.
string
.
port_alert
)
)
Crouton
.
makeText
(
this
,
R
.
string
.
port_alert
,
Style
.
ALERT
).
show
(
)
return
false
}
}
catch
{
case
ex
:
Exception
=>
{
this
.
showDialog
(
getString
(
R
.
string
.
port_alert
)
)
case
ex
:
Exception
=>
Crouton
.
makeText
(
this
,
R
.
string
.
port_alert
,
Style
.
ALERT
).
show
(
)
return
false
}
true
}
/** Called when connect button is clicked. */
def
serviceStart
:
Boolean
=
{
if
(!
checkText
(
Key
.
proxy
))
return
false
if
(!
checkText
(
Key
.
sitekey
))
return
false
if
(!
checkNumber
(
Key
.
localPort
))
return
false
if
(!
checkNumber
(
Key
.
remotePort
))
return
false
if
(
bgService
==
null
)
return
false
bgService
.
start
(
ConfigUtils
.
load
(
settings
))
if
(
isVpnEnabled
)
{
if
(
ShadowVpnService
.
isServiceStarted
(
this
))
return
false
val
intent
:
Intent
=
new
Intent
(
this
,
classOf
[
ShadowVpnService
])
Extra
.
put
(
settings
,
intent
)
startService
(
intent
)
val
style
=
new
Style
.
Builder
().
setBackgroundColorValue
(
Style
.
holoBlueLight
).
build
()
val
config
=
new
Configuration
.
Builder
().
setDuration
(
Configuration
.
DURATION_LONG
).
build
()
Crouton
.
makeText
(
Shadowsocks
.
this
,
R
.
string
.
vpn_status
,
style
).
setConfiguration
(
config
).
show
()
switchButton
.
setEnabled
(
false
)
}
else
{
if
(
ShadowsocksService
.
isServiceStarted
(
this
))
return
false
val
intent
:
Intent
=
new
Intent
(
this
,
classOf
[
ShadowsocksService
])
Extra
.
put
(
settings
,
intent
)
startService
(
intent
)
changeSwitch
(
checked
=
false
)
}
true
}
...
...
@@ -974,10 +1031,9 @@ class Shadowsocks
try
{
versionName
=
getPackageManager
.
getPackageInfo
(
getPackageName
,
0
).
versionName
}
catch
{
case
ex
:
PackageManager.NameNotFoundException
=>
{
case
ex
:
PackageManager.NameNotFoundException
=>
versionName
=
""
}
}
new
AlertDialog
.
Builder
(
this
)
.
setTitle
(
getString
(
R
.
string
.
about_title
).
format
(
versionName
))
...
...
@@ -992,20 +1048,6 @@ class Shadowsocks
.
show
()
}
private
def
showDialog
(
msg
:
String
)
{
val
builder
:
AlertDialog.Builder
=
new
AlertDialog
.
Builder
(
this
)
builder
.
setMessage
(
msg
)
.
setCancelable
(
false
)
.
setNegativeButton
(
getString
(
R
.
string
.
ok_iknow
),
new
DialogInterface
.
OnClickListener
{
def
onClick
(
dialog
:
DialogInterface
,
id
:
Int
)
{
dialog
.
cancel
()
}
})
val
alert
:
AlertDialog
=
builder
.
create
alert
.
show
()
}
def
clearDialog
()
{
if
(
progressDialog
!=
null
)
{
progressDialog
.
dismiss
()
...
...
@@ -1014,28 +1056,25 @@ class Shadowsocks
}
def
onStateChanged
(
s
:
Int
,
m
:
String
)
{
handler
.
post
(
new
Runnable
{
override
def
run
()
{
if
(
state
!=
s
)
{
state
=
s
state
match
{
case
State
.
CONNECTING
=>
{
case
State
.
CONNECTING
=>
if
(
progressDialog
==
null
)
{
progressDialog
=
ProgressDialog
.
show
(
Shadowsocks
.
this
,
""
,
getString
(
R
.
string
.
connecting
),
true
,
true
)
}
setPreferenceEnabled
(
enabled
=
false
)
}
case
State
.
CONNECTED
=>
{
case
State
.
CONNECTED
=>
clearDialog
()
if
(!
switchButton
.
isChecked
)
switchButton
.
setChecked
(
true
)
changeSwitch
(
checked
=
true
)
setPreferenceEnabled
(
enabled
=
false
)
}
case
State
.
STOPPED
=>
{
case
State
.
STOPPED
=>
clearDialog
()
if
(
switchButton
.
isChecked
)
{
switchButton
.
setEnabled
(
true
)
switchButton
.
setChecked
(
false
)
changeSwitch
(
checked
=
false
)
Crouton
.
cancelAllCroutons
()
}
if
(
m
!=
null
)
{
Crouton
.
cancelAllCroutons
()
val
style
=
new
Style
.
Builder
().
setBackgroundColorValue
(
Style
.
holoRedLight
).
build
()
...
...
@@ -1049,10 +1088,11 @@ class Shadowsocks
}
setPreferenceEnabled
(
enabled
=
true
)
}
}
if
(!
isSinglePane
)
sendBroadcast
(
new
Intent
(
Action
.
UPDATE_FRAGMENT
))
}
}
})
}
class
PreferenceBroadcastReceiver
extends
BroadcastReceiver
{
override
def
onReceive
(
context
:
Context
,
intent
:
Intent
)
{
...
...
@@ -1061,12 +1101,4 @@ class Shadowsocks
}
}
class
StateBroadcastReceiver
extends
BroadcastReceiver
{
override
def
onReceive
(
context
:
Context
,
intent
:
Intent
)
{
val
state
=
intent
.
getIntExtra
(
Extra
.
STATE
,
State
.
INIT
)
val
message
=
intent
.
getStringExtra
(
Extra
.
MESSAGE
)
onStateChanged
(
state
,
message
)
}
}
}
src/main/scala/com/github/shadowsocks/ShadowsocksService.scala
→
src/main/scala/com/github/shadowsocks/Shadowsocks
Nat
Service.scala
View file @
0b8114ad
...
...
@@ -61,18 +61,13 @@ import scala.concurrent.ops._
import
com.github.shadowsocks.utils._
import
scala.Some
import
android.graphics.Color
import
com.github.shadowsocks.aidl.Config
case
class
TrafficStat
(
tx
:
Long
,
rx
:
Long
,
timestamp
:
Long
)
object
ShadowsocksService
{
def
isServiceStarted
(
context
:
Context
)
:
Boolean
=
{
Utils
.
isServiceStarted
(
"com.github.shadowsocks.ShadowsocksService"
,
context
)
}
}
class
ShadowsocksNatService
extends
Service
with
BaseService
{
class
ShadowsocksService
extends
Service
{
val
TAG
=
"ShadowsocksService"
val
TAG
=
"ShadowsocksNatService"
val
CMD_IPTABLES_RETURN
=
" -t nat -A OUTPUT -p tcp -d 0.0.0.0 -j RETURN\n"
val
CMD_IPTABLES_REDIRECT_ADD_SOCKS
=
" -t nat -A OUTPUT -p tcp "
+
"-j REDIRECT --to 8123\n"
...
...
@@ -80,12 +75,6 @@ class ShadowsocksService extends Service {
"-j DNAT --to-destination 127.0.0.1:8123\n"
val
DNS_PORT
=
8153
val
MSG_CONNECT_FINISH
=
1
val
MSG_CONNECT_SUCCESS
=
2
val
MSG_CONNECT_FAIL
=
3
val
MSG_STOP_SELF
=
4
val
MSG_VPN_ERROR
=
5
private
val
mStartForegroundSignature
=
Array
[
Class
[
_
]](
classOf
[
Int
],
classOf
[
Notification
])
private
val
mStopForegroundSignature
=
Array
[
Class
[
_
]](
classOf
[
Boolean
])
private
val
mSetForegroundSignature
=
Array
[
Class
[
_
]](
classOf
[
Boolean
])
...
...
@@ -104,56 +93,25 @@ class ShadowsocksService extends Service {
private
var
mStartForegroundArgs
=
new
Array
[
AnyRef
](
2
)
private
var
mStopForegroundArgs
=
new
Array
[
AnyRef
](
1
)
private
var
state
=
State
.
INIT
private
var
last
:
TrafficStat
=
null
private
var
lastTxRate
=
0
private
var
lastRxRate
=
0
private
var
timer
:
Timer
=
null
private
val
TIMER_INTERVAL
=
2
private
def
changeState
(
s
:
Int
)
{
if
(
state
!=
s
)
{
state
=
s
val
intent
=
new
Intent
(
Action
.
UPDATE_STATE
)
intent
.
putExtra
(
Extra
.
STATE
,
state
)
sendBroadcast
(
intent
)
}
}
val
handler
:
Handler
=
new
Handler
{
override
def
handleMessage
(
msg
:
Message
)
{
msg
.
what
match
{
case
M
SG_
CONNECT_SUCCESS
=>
case
M
sg
.
CONNECT_SUCCESS
=>
changeState
(
State
.
CONNECTED
)
case
M
SG_
CONNECT_FAIL
=>
case
M
sg
.
CONNECT_FAIL
=>
changeState
(
State
.
STOPPED
)
case
MSG_STOP_SELF
=>
stopSelf
()
case
_
=>
}
super
.
handleMessage
(
msg
)
}
}
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
(
TAG
,
"Cannot open pid file: "
+
name
)
}
case
e
:
IOException
=>
{
Log
.
e
(
TAG
,
"Cannot read pid file: "
+
name
)
}
case
e
:
NumberFormatException
=>
{
Log
.
e
(
TAG
,
"Invalid pid"
,
e
)
}
}
-
1
}
def
startShadowsocksDaemon
()
{
val
cmd
:
String
=
(
Path
.
BASE
+
"shadowsocks -b 127.0.0.1 -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f "
+
...
...
@@ -165,8 +123,8 @@ class ShadowsocksService extends Service {
def
startDnsDaemon
()
{
val
cmd
:
String
=
Path
.
BASE
+
"pdnsd -c "
+
Path
.
BASE
+
"pdnsd.conf"
val
conf
:
String
=
Config
.
PDNSD
.
format
(
"127.0.0.1"
)
Config
.
printToFile
(
new
File
(
Path
.
BASE
+
"pdnsd.conf"
))(
p
=>
{
val
conf
:
String
=
Config
Utils
.
PDNSD
.
format
(
"127.0.0.1"
)
Config
Utils
.
printToFile
(
new
File
(
Path
.
BASE
+
"pdnsd.conf"
))(
p
=>
{
p
.
println
(
conf
)
})
Utils
.
runCommand
(
cmd
)
...
...
@@ -178,101 +136,17 @@ class ShadowsocksService extends Service {
val
pi
:
PackageInfo
=
getPackageManager
.
getPackageInfo
(
getPackageName
,
0
)
version
=
pi
.
versionName
}
catch
{
case
e
:
PackageManager.NameNotFoundException
=>
{
case
e
:
PackageManager.NameNotFoundException
=>
version
=
"Package name not found"
}
}
version
}
def
handleCommand
(
intent
:
Intent
)
{
if
(
intent
==
null
)
{
stopSelf
()
return
}
changeState
(
State
.
CONNECTING
)
config
=
Extra
.
get
(
intent
)
if
(
config
.
isTrafficStat
)
{
// initialize timer
val
task
=
new
TimerTask
{
def
run
()
{
val
pm
=
getSystemService
(
Context
.
POWER_SERVICE
).
asInstanceOf
[
PowerManager
]
val
now
=
new
TrafficStat
(
TrafficStats
.
getUidTxBytes
(
uid
),
TrafficStats
.
getUidRxBytes
(
uid
),
java
.
lang
.
System
.
currentTimeMillis
())
val
txRate
=
((
now
.
tx
-
last
.
tx
)
/
1024
/
TIMER_INTERVAL
).
toInt
val
rxRate
=
((
now
.
rx
-
last
.
rx
)
/
1024
/
TIMER_INTERVAL
).
toInt
last
=
now
if
(
lastTxRate
==
txRate
&&
lastRxRate
==
rxRate
)
{
return
}
else
{
lastTxRate
=
txRate
lastRxRate
=
rxRate
}
if
((
pm
.
isScreenOn
&&
state
==
State
.
CONNECTED
)
||
(
txRate
==
0
&&
rxRate
==
0
))
{
notifyForegroundAlert
(
getString
(
R
.
string
.
forward_success
),
getString
(
R
.
string
.
service_status
).
format
(
math
.
max
(
txRate
,
rxRate
)),
math
.
max
(
txRate
,
rxRate
))
}
}
}
last
=
new
TrafficStat
(
TrafficStats
.
getUidTxBytes
(
uid
),
TrafficStats
.
getUidRxBytes
(
uid
),
java
.
lang
.
System
.
currentTimeMillis
())
timer
=
new
Timer
(
true
)
timer
.
schedule
(
task
,
TIMER_INTERVAL
*
1000
,
TIMER_INTERVAL
*
1000
)
}
spawn
{
if
(
config
.
proxy
==
"198.199.101.152"
)
{
val
container
=
getApplication
.
asInstanceOf
[
ShadowsocksApplication
].
tagContainer
try
{
config
=
Config
.
getPublicConfig
(
getBaseContext
,
container
,
config
)
}
catch
{
case
ex
:
Exception
=>
{
notifyAlert
(
getString
(
R
.
string
.
forward_fail
),
getString
(
R
.
string
.
service_failed
))
stopSelf
()
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_FAIL
,
500
)
return
}
}
}
killProcesses
()
var
resolved
:
Boolean
=
false
if
(!
InetAddressUtils
.
isIPv4Address
(
config
.
proxy
)
&&
!
InetAddressUtils
.
isIPv6Address
(
config
.
proxy
))
{
Utils
.
resolve
(
config
.
proxy
,
enableIPv6
=
true
)
match
{
case
Some
(
a
)
=>
config
.
proxy
=
a
resolved
=
true
case
None
=>
resolved
=
false
}
}
else
{
resolved
=
true
}
hasRedirectSupport
=
Utils
.
getHasRedirectSupport
if
(
resolved
&&
handleConnection
)
{
notifyForegroundAlert
(
getString
(
R
.
string
.
forward_success
),
getString
(
R
.
string
.
service_running
).
format
(
config
.
profileName
))
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_SUCCESS
,
500
)
}
else
{
notifyAlert
(
getString
(
R
.
string
.
forward_fail
),
getString
(
R
.
string
.
service_failed
))
stopSelf
()
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_FAIL
,
500
)
}
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_FINISH
,
500
)
}
}
def
startRedsocksDaemon
()
{
val
conf
=
Config
.
REDSOCKS
.
format
(
config
.
localPort
)
val
cmd
=
"%sredsocks -p %sredsocks.pid -c %sredsocks.conf"
.
format
(
Path
.
BASE
,
Path
.
BASE
,
Path
.
BASE
)
Config
.
printToFile
(
new
File
(
Path
.
BASE
+
"redsocks.conf"
))(
p
=>
{
val
conf
=
ConfigUtils
.
REDSOCKS
.
format
(
config
.
localPort
)
val
cmd
=
"%sredsocks -p %sredsocks.pid -c %sredsocks.conf"
.
format
(
Path
.
BASE
,
Path
.
BASE
,
Path
.
BASE
)
ConfigUtils
.
printToFile
(
new
File
(
Path
.
BASE
+
"redsocks.conf"
))(
p
=>
{
p
.
println
(
conf
)
})
Utils
.
runRootCommand
(
cmd
)
...
...
@@ -291,14 +165,14 @@ class ShadowsocksService extends Service {
try
{
t
.
join
(
300
)
}
catch
{
case
ignored
:
InterruptedException
=>
{
}
case
ignored
:
InterruptedException
=>
}
!
t
.
isAlive
}
/** Called when the activity is first created. */
def
handleConnection
:
Boolean
=
{
startShadowsocksDaemon
()
startDnsDaemon
()
startRedsocksDaemon
()
...
...
@@ -308,23 +182,16 @@ class ShadowsocksService extends Service {
true
}
def
initSoundVibrateLights
(
notification
:
Notification
)
{
notification
.
sound
=
null
notification
.
defaults
|=
Notification
.
DEFAULT_LIGHTS
}
def
invokeMethod
(
method
:
Method
,
args
:
Array
[
AnyRef
])
{
try
{
method
.
invoke
(
this
,
mStartForegroundArgs
:
_
*
)
}
catch
{
case
e
:
InvocationTargetException
=>
{
case
e
:
InvocationTargetException
=>
Log
.
w
(
TAG
,
"Unable to invoke method"
,
e
)
}
case
e
:
IllegalAccessException
=>
{
case
e
:
IllegalAccessException
=>
Log
.
w
(
TAG
,
"Unable to invoke method"
,
e
)
}
}
}
def
notifyForegroundAlert
(
title
:
String
,
info
:
String
)
{
notifyForegroundAlert
(
title
,
info
,
-
1
)
...
...
@@ -340,8 +207,9 @@ class ShadowsocksService extends Service {
val
icon
=
getResources
.
getDrawable
(
R
.
drawable
.
ic_stat_shadowsocks
)
if
(
rate
>=
0
)
{
val
bitmap
=
Utils
.
getBitmap
(
rate
.
toString
,
icon
.
getIntrinsicWidth
*
4
,
icon
.
getIntrinsicHeight
*
4
,
Color
.
TRANSPARENT
)
val
bitmap
=
Utils
.
getBitmap
(
rate
.
toString
,
icon
.
getIntrinsicWidth
*
4
,
icon
.
getIntrinsicHeight
*
4
,
Color
.
TRANSPARENT
)
builder
.
setLargeIcon
(
bitmap
)
if
(
rate
<
1000
)
{
...
...
@@ -352,7 +220,6 @@ class ShadowsocksService extends Service {
}
else
{
builder
.
setSmallIcon
(
R
.
drawable
.
ic_stat_speed
,
1091
)
}
}
else
{
builder
.
setSmallIcon
(
R
.
drawable
.
ic_stat_shadowsocks
)
}
...
...
@@ -386,20 +253,17 @@ class ShadowsocksService extends Service {
}
def
onBind
(
intent
:
Intent
)
:
IBinder
=
{
if
(
classOf
[
ShadowsocksNatService
].
getName
equals
intent
.
getAction
)
{
binder
}
else
{
null
}
}
override
def
onCreate
()
{
super
.
onCreate
()
Config
.
refresh
(
this
)
EasyTracker
.
getInstance
(
this
)
.
send
(
MapBuilder
.
createEvent
(
TAG
,
"start"
,
getVersionName
,
0L
)
.
set
(
Fields
.
SESSION_CONTROL
,
"start"
)
.
build
())
ConfigUtils
.
refresh
(
this
)
notificationManager
=
this
.
getSystemService
(
Context
.
NOTIFICATION_SERVICE
)
...
...
@@ -408,65 +272,21 @@ class ShadowsocksService extends Service {
mStartForeground
=
getClass
.
getMethod
(
"startForeground"
,
mStartForegroundSignature
:
_
*
)
mStopForeground
=
getClass
.
getMethod
(
"stopForeground"
,
mStopForegroundSignature
:
_
*
)
}
catch
{
case
e
:
NoSuchMethodException
=>
{
case
e
:
NoSuchMethodException
=>
mStartForeground
=
{
mStopForeground
=
null
mStopForeground
}
}
}
try
{
mSetForeground
=
getClass
.
getMethod
(
"setForeground"
,
mSetForegroundSignature
:
_
*
)
}
catch
{
case
e
:
NoSuchMethodException
=>
{
case
e
:
NoSuchMethodException
=>
throw
new
IllegalStateException
(
"OS doesn't have Service.startForeground OR Service.setForeground!"
)
}
}
// register close receiver
val
filter
=
new
IntentFilter
()
filter
.
addAction
(
Intent
.
ACTION_SHUTDOWN
)
filter
.
addAction
(
Action
.
CLOSE
)
receiver
=
new
BroadcastReceiver
()
{
def
onReceive
(
p1
:
Context
,
p2
:
Intent
)
{
stopSelf
()
}
}
registerReceiver
(
receiver
,
filter
)
}
/** Called when the activity is closed. */
override
def
onDestroy
()
{
// reset timer
if
(
timer
!=
null
)
{
timer
.
cancel
()
timer
=
null
}
// clean up context
changeState
(
State
.
STOPPED
)
EasyTracker
.
getInstance
(
this
)
.
send
(
MapBuilder
.
createEvent
(
TAG
,
"stop"
,
getVersionName
,
0L
)
.
set
(
Fields
.
SESSION_CONTROL
,
"stop"
)
.
build
())
stopForegroundCompat
(
1
)
if
(
receiver
!=
null
)
{
unregisterReceiver
(
receiver
)
receiver
=
null
}
// reset NAT
killProcesses
()
super
.
onDestroy
()
}
def
killProcesses
()
{
Utils
.
runRootCommand
(
Utils
.
getIptables
+
" -t nat -F OUTPUT"
)
...
...
@@ -489,12 +309,7 @@ class ShadowsocksService extends Service {
Utils
.
runCommand
(
sb
.
toString
())
}
override
def
onStart
(
intent
:
Intent
,
startId
:
Int
)
{
handleCommand
(
intent
)
}
override
def
onStartCommand
(
intent
:
Intent
,
flags
:
Int
,
startId
:
Int
)
:
Int
=
{
handleCommand
(
intent
)
Service
.
START_STICKY
}
...
...
@@ -593,17 +408,148 @@ class ShadowsocksService extends Service {
try
{
mStopForeground
.
invoke
(
this
,
mStopForegroundArgs
:
_
*
)
}
catch
{
case
e
:
InvocationTargetException
=>
{
case
e
:
InvocationTargetException
=>
Log
.
w
(
TAG
,
"Unable to invoke stopForeground"
,
e
)
}
case
e
:
IllegalAccessException
=>
{
case
e
:
IllegalAccessException
=>
Log
.
w
(
TAG
,
"Unable to invoke stopForeground"
,
e
)
}
}
return
}
notificationManager
.
cancel
(
id
)
mSetForegroundArgs
(
0
)
=
boolean2Boolean
(
x
=
false
)
invokeMethod
(
mSetForeground
,
mSetForegroundArgs
)
}
override
def
startRunner
(
c
:
Config
)
{
config
=
c
// register close receiver
val
filter
=
new
IntentFilter
()
filter
.
addAction
(
Intent
.
ACTION_SHUTDOWN
)
filter
.
addAction
(
Action
.
CLOSE
)
receiver
=
new
BroadcastReceiver
()
{
def
onReceive
(
p1
:
Context
,
p2
:
Intent
)
{
stopRunner
()
}
}
registerReceiver
(
receiver
,
filter
)
// start tracker
EasyTracker
.
getInstance
(
this
)
.
send
(
MapBuilder
.
createEvent
(
TAG
,
"start"
,
getVersionName
,
0L
)
.
set
(
Fields
.
SESSION_CONTROL
,
"start"
)
.
build
())
changeState
(
State
.
CONNECTING
)
if
(
config
.
isTrafficStat
)
{
// initialize timer
val
task
=
new
TimerTask
{
def
run
()
{
val
pm
=
getSystemService
(
Context
.
POWER_SERVICE
).
asInstanceOf
[
PowerManager
]
val
now
=
new
TrafficStat
(
TrafficStats
.
getUidTxBytes
(
uid
),
TrafficStats
.
getUidRxBytes
(
uid
),
java
.
lang
.
System
.
currentTimeMillis
())
val
txRate
=
((
now
.
tx
-
last
.
tx
)
/
1024
/
TIMER_INTERVAL
).
toInt
val
rxRate
=
((
now
.
rx
-
last
.
rx
)
/
1024
/
TIMER_INTERVAL
).
toInt
last
=
now
if
(
lastTxRate
==
txRate
&&
lastRxRate
==
rxRate
)
{
return
}
else
{
lastTxRate
=
txRate
lastRxRate
=
rxRate
}
if
((
pm
.
isScreenOn
&&
state
==
State
.
CONNECTED
)
||
(
txRate
==
0
&&
rxRate
==
0
))
{
notifyForegroundAlert
(
getString
(
R
.
string
.
forward_success
),
getString
(
R
.
string
.
service_status
).
format
(
math
.
max
(
txRate
,
rxRate
)),
math
.
max
(
txRate
,
rxRate
))
}
}
}
last
=
new
TrafficStat
(
TrafficStats
.
getUidTxBytes
(
uid
),
TrafficStats
.
getUidRxBytes
(
uid
),
java
.
lang
.
System
.
currentTimeMillis
())
timer
=
new
Timer
(
true
)
timer
.
schedule
(
task
,
TIMER_INTERVAL
*
1000
,
TIMER_INTERVAL
*
1000
)
}
spawn
{
if
(
config
.
proxy
==
"198.199.101.152"
)
{
val
container
=
getApplication
.
asInstanceOf
[
ShadowsocksApplication
].
tagContainer
try
{
config
=
ConfigUtils
.
getPublicConfig
(
getBaseContext
,
container
,
config
)
}
catch
{
case
ex
:
Exception
=>
notifyAlert
(
getString
(
R
.
string
.
forward_fail
),
getString
(
R
.
string
.
service_failed
))
stopRunner
()
handler
.
sendEmptyMessageDelayed
(
Msg
.
CONNECT_FAIL
,
500
)
return
}
}
killProcesses
()
var
resolved
:
Boolean
=
false
if
(!
InetAddressUtils
.
isIPv4Address
(
config
.
proxy
)
&&
!
InetAddressUtils
.
isIPv6Address
(
config
.
proxy
))
{
Utils
.
resolve
(
config
.
proxy
,
enableIPv6
=
true
)
match
{
case
Some
(
a
)
=>
config
.
proxy
=
a
resolved
=
true
case
None
=>
resolved
=
false
}
}
else
{
resolved
=
true
}
hasRedirectSupport
=
Utils
.
getHasRedirectSupport
if
(
resolved
&&
handleConnection
)
{
notifyForegroundAlert
(
getString
(
R
.
string
.
forward_success
),
getString
(
R
.
string
.
service_running
).
format
(
config
.
profileName
))
handler
.
sendEmptyMessageDelayed
(
Msg
.
CONNECT_SUCCESS
,
500
)
}
else
{
notifyAlert
(
getString
(
R
.
string
.
forward_fail
),
getString
(
R
.
string
.
service_failed
))
stopRunner
()
handler
.
sendEmptyMessageDelayed
(
Msg
.
CONNECT_FAIL
,
500
)
}
handler
.
sendEmptyMessageDelayed
(
Msg
.
CONNECT_FINISH
,
500
)
}
}
override
def
stopRunner
()
{
// change the state
changeState
(
State
.
STOPPED
)
// stop the tracker
EasyTracker
.
getInstance
(
this
)
.
send
(
MapBuilder
.
createEvent
(
TAG
,
"stop"
,
getVersionName
,
0L
)
.
set
(
Fields
.
SESSION_CONTROL
,
"stop"
)
.
build
())
// reset timer
if
(
timer
!=
null
)
{
timer
.
cancel
()
timer
=
null
}
// clean up context
stopForegroundCompat
(
1
)
if
(
receiver
!=
null
)
{
unregisterReceiver
(
receiver
)
receiver
=
null
}
// reset NAT
killProcesses
()
}
override
def
getTag
=
TAG
override
def
getServiceMode
=
Mode
.
NAT
override
def
getContext
=
getBaseContext
}
src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala
View file @
0b8114ad
...
...
@@ -54,34 +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
)
}
context
.
startActivity
(
new
Intent
(
context
,
classOf
[
ShadowsocksRunnerActivity
]))
}
}
}
src/main/scala/com/github/shadowsocks/Shadow
Vpn
Activity.scala
→
src/main/scala/com/github/shadowsocks/Shadow
socksRunner
Activity.scala
View file @
0b8114ad
...
...
@@ -40,37 +40,74 @@
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.
{
Context
,
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
Shadow
Vpn
Activity
extends
Activity
{
class
Shadow
socksRunner
Activity
extends
Activity
{
override
def
onCreate
(
savedInstanceState
:
Bundle
)
{
super
.
onCreate
(
savedInstanceState
)
val
intent
=
VpnService
.
prepare
(
this
)
lazy
val
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
)
lazy
val
isRoot
=
Utils
.
getRoot
// Services
var
bgService
:
IShadowsocksService
=
null
val
connection
=
new
ServiceConnection
{
override
def
onServiceConnected
(
name
:
ComponentName
,
service
:
IBinder
)
{
bgService
=
IShadowsocksService
.
Stub
.
asInterface
(
service
)
if
(!
isRoot
)
{
val
intent
=
VpnService
.
prepare
(
ShadowsocksRunnerActivity
.
this
)
if
(
intent
!=
null
)
{
startActivityForResult
(
intent
,
Shadowsocks
.
REQUEST_CONNECT
)
}
else
{
onActivityResult
(
Shadowsocks
.
REQUEST_CONNECT
,
Activity
.
RESULT_OK
,
null
)
}
}
else
{
bgService
.
start
(
ConfigUtils
.
load
(
settings
))
}
}
override
def
onServiceDisconnected
(
name
:
ComponentName
)
{
bgService
=
null
}
}
def
attachService
()
{
if
(
bgService
==
null
)
{
val
s
=
if
(
isRoot
)
classOf
[
ShadowsocksNatService
]
else
classOf
[
ShadowsocksVpnService
]
bindService
(
new
Intent
(
s
.
getName
),
connection
,
Context
.
BIND_AUTO_CREATE
)
startService
(
new
Intent
(
this
,
s
))
}
}
def
deattachService
()
{
if
(
bgService
!=
null
)
{
unbindService
(
connection
)
bgService
=
null
}
}
override
def
onCreate
(
savedInstanceState
:
Bundle
)
{
super
.
onCreate
(
savedInstanceState
)
attachService
()
}
override
def
onDestroy
()
{
super
.
onDestroy
()
deattachService
()
}
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
(
settings
))
}
case
_
=>
{
case
_
=>
Log
.
e
(
Shadowsocks
.
TAG
,
"Failed to start VpnService"
)
}
}
finish
()
}
}
src/main/scala/com/github/shadowsocks/ShadowVpnService.scala
→
src/main/scala/com/github/shadowsocks/Shadow
socks
VpnService.scala
View file @
0b8114ad
...
...
@@ -56,22 +56,11 @@ import org.apache.commons.net.util.SubnetUtils
import
java.net.InetAddress
import
com.github.shadowsocks.utils._
import
scala.Some
import
com.github.shadowsocks.aidl.
{
IShadowsocksService
,
Config
}
object
ShadowVpnService
{
def
isServiceStarted
(
context
:
Context
)
:
Boolean
=
{
Utils
.
isServiceStarted
(
"com.github.shadowsocks.ShadowVpnService"
,
context
)
}
}
class
ShadowVpnService
extends
VpnService
{
class
ShadowsocksVpnService
extends
VpnService
with
BaseService
{
val
TAG
=
"ShadowVpnService"
val
MSG_CONNECT_FINISH
=
1
val
MSG_CONNECT_SUCCESS
=
2
val
MSG_CONNECT_FAIL
=
3
val
MSG_STOP_SELF
=
5
val
MSG_VPN_ERROR
=
6
val
TAG
=
"ShadowsocksVpnService"
val
VPN_MTU
=
1500
...
...
@@ -83,60 +72,21 @@ class ShadowVpnService extends VpnService {
var
apps
:
Array
[
ProxiedApp
]
=
null
var
config
:
Config
=
null
private
var
state
=
State
.
INIT
private
var
message
:
String
=
null
def
changeState
(
s
:
Int
)
{
changeState
(
s
,
null
)
}
def
changeState
(
s
:
Int
,
m
:
String
)
{
if
(
state
!=
s
)
{
state
=
s
if
(
m
!=
null
)
message
=
m
val
intent
=
new
Intent
(
Action
.
UPDATE_STATE
)
intent
.
putExtra
(
Extra
.
STATE
,
state
)
intent
.
putExtra
(
Extra
.
MESSAGE
,
message
)
sendBroadcast
(
intent
)
}
}
val
handler
:
Handler
=
new
Handler
{
override
def
handleMessage
(
msg
:
Message
)
{
msg
.
what
match
{
case
M
SG_
CONNECT_SUCCESS
=>
case
M
sg
.
CONNECT_SUCCESS
=>
changeState
(
State
.
CONNECTED
)
case
M
SG_
CONNECT_FAIL
=>
case
M
sg
.
CONNECT_FAIL
=>
changeState
(
State
.
STOPPED
)
case
M
SG_
VPN_ERROR
=>
case
M
sg
.
VPN_ERROR
=>
if
(
msg
.
obj
!=
null
)
changeState
(
State
.
STOPPED
,
msg
.
obj
.
asInstanceOf
[
String
])
case
MSG_STOP_SELF
=>
stopSelf
()
case
_
=>
}
super
.
handleMessage
(
msg
)
}
}
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
(
TAG
,
"Cannot open pid file: "
+
name
)
}
case
e
:
IOException
=>
{
Log
.
e
(
TAG
,
"Cannot read pid file: "
+
name
)
}
case
e
:
NumberFormatException
=>
{
Log
.
e
(
TAG
,
"Invalid pid"
,
e
)
}
}
-
1
}
def
startShadowsocksDaemon
()
{
val
cmd
:
String
=
(
Path
.
BASE
+
"shadowsocks -b 127.0.0.1 -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f "
+
...
...
@@ -148,8 +98,8 @@ class ShadowVpnService extends VpnService {
def
startDnsDaemon
()
{
val
cmd
:
String
=
Path
.
BASE
+
"pdnsd -c "
+
Path
.
BASE
+
"pdnsd.conf"
val
conf
:
String
=
Config
.
PDNSD
.
format
(
"0.0.0.0"
)
Config
.
printToFile
(
new
File
(
Path
.
BASE
+
"pdnsd.conf"
))(
p
=>
{
val
conf
:
String
=
Config
Utils
.
PDNSD
.
format
(
"0.0.0.0"
)
Config
Utils
.
printToFile
(
new
File
(
Path
.
BASE
+
"pdnsd.conf"
))(
p
=>
{
p
.
println
(
conf
)
})
Utils
.
runCommand
(
cmd
)
...
...
@@ -161,74 +111,12 @@ class ShadowVpnService extends VpnService {
val
pi
:
PackageInfo
=
getPackageManager
.
getPackageInfo
(
getPackageName
,
0
)
version
=
pi
.
versionName
}
catch
{
case
e
:
PackageManager.NameNotFoundException
=>
{
case
e
:
PackageManager.NameNotFoundException
=>
version
=
"Package name not found"
}
}
version
}
def
handleCommand
(
intent
:
Intent
)
{
if
(
intent
==
null
)
{
stopSelf
()
return
}
if
(
VpnService
.
prepare
(
this
)
!=
null
)
{
val
i
=
new
Intent
(
this
,
classOf
[
ShadowVpnActivity
])
i
.
addFlags
(
Intent
.
FLAG_ACTIVITY_NEW_TASK
)
startActivity
(
i
)
stopSelf
()
return
}
changeState
(
State
.
CONNECTING
)
config
=
Extra
.
get
(
intent
)
spawn
{
if
(
config
.
proxy
==
"198.199.101.152"
)
{
val
container
=
getApplication
.
asInstanceOf
[
ShadowsocksApplication
].
tagContainer
try
{
config
=
Config
.
getPublicConfig
(
getBaseContext
,
container
,
config
)
}
catch
{
case
ex
:
Exception
=>
{
notifyAlert
(
getString
(
R
.
string
.
forward_fail
),
getString
(
R
.
string
.
service_failed
))
stopSelf
()
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_FAIL
,
500
)
return
}
}
}
killProcesses
()
// Resolve server address
var
resolved
:
Boolean
=
false
if
(!
InetAddressUtils
.
isIPv4Address
(
config
.
proxy
)
&&
!
InetAddressUtils
.
isIPv6Address
(
config
.
proxy
))
{
Utils
.
resolve
(
config
.
proxy
,
enableIPv6
=
true
)
match
{
case
Some
(
addr
)
=>
config
.
proxy
=
addr
resolved
=
true
case
None
=>
resolved
=
false
}
}
else
{
resolved
=
true
}
if
(
resolved
&&
handleConnection
)
{
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_SUCCESS
,
300
)
}
else
{
notifyAlert
(
getString
(
R
.
string
.
forward_fail
),
getString
(
R
.
string
.
service_failed
))
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_FAIL
,
300
)
handler
.
sendEmptyMessageDelayed
(
MSG_STOP_SELF
,
500
)
}
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_FINISH
,
300
)
}
}
def
waitForProcess
(
name
:
String
)
:
Boolean
=
{
val
pid
:
Int
=
getPid
(
name
)
if
(
pid
==
-
1
)
return
false
...
...
@@ -242,8 +130,7 @@ class ShadowVpnService extends VpnService {
try
{
t
.
join
(
300
)
}
catch
{
case
ignored
:
InterruptedException
=>
{
}
case
ignored
:
InterruptedException
=>
}
!
t
.
isAlive
}
...
...
@@ -308,18 +195,17 @@ class ShadowVpnService extends VpnService {
try
{
conn
=
builder
.
establish
()
}
catch
{
case
ex
:
IllegalStateException
=>
{
case
ex
:
IllegalStateException
=>
val
msg
=
new
Message
()
msg
.
what
=
M
SG_
VPN_ERROR
msg
.
what
=
M
sg
.
VPN_ERROR
msg
.
obj
=
ex
.
getMessage
handler
.
sendMessage
(
msg
)
conn
=
null
}
case
ex
:
Exception
=>
conn
=
null
}
if
(
conn
==
null
)
{
stop
Self
()
stop
Runner
()
return
}
...
...
@@ -348,11 +234,6 @@ class ShadowVpnService extends VpnService {
true
}
def
initSoundVibrateLights
(
notification
:
Notification
)
{
notification
.
sound
=
null
notification
.
defaults
|=
Notification
.
DEFAULT_LIGHTS
}
def
notifyAlert
(
title
:
String
,
info
:
String
)
{
val
openIntent
:
Intent
=
new
Intent
(
this
,
classOf
[
Shadowsocks
])
openIntent
.
setFlags
(
Intent
.
FLAG_ACTIVITY_CLEAR_TOP
)
...
...
@@ -373,6 +254,8 @@ class ShadowVpnService extends VpnService {
val
action
=
intent
.
getAction
if
(
VpnService
.
SERVICE_INTERFACE
==
action
)
{
return
super
.
onBind
(
intent
)
}
else
if
(
classOf
[
ShadowsocksVpnService
].
getName
==
action
)
{
return
binder
}
null
}
...
...
@@ -380,8 +263,47 @@ class ShadowVpnService extends VpnService {
override
def
onCreate
()
{
super
.
onCreate
()
Config
.
refresh
(
this
)
ConfigUtils
.
refresh
(
this
)
notificationManager
=
getSystemService
(
Context
.
NOTIFICATION_SERVICE
)
.
asInstanceOf
[
NotificationManager
]
}
def
killProcesses
()
{
val
sb
=
new
StringBuilder
if
(!
waitForProcess
(
"shadowsocks"
))
{
sb
++=
"kill -9 `cat /data/data/com.github.shadowsocks/shadowsocks.pid`"
++=
"\n"
sb
++=
"killall -9 shadowsocks"
++=
"\n"
}
if
(!
waitForProcess
(
"tun2socks"
))
{
sb
++=
"kill -9 `cat /data/data/com.github.shadowsocks/tun2socks.pid`"
++=
"\n"
sb
++=
"killall -9 tun2socks"
++=
"\n"
}
if
(!
waitForProcess
(
"pdnsd"
))
{
sb
++=
"kill -9 `cat /data/data/com.github.shadowsocks/pdnsd.pid`"
++=
"\n"
sb
++=
"killall -9 pdnsd"
++=
"\n"
}
Utils
.
runCommand
(
sb
.
toString
())
}
override
def
onStartCommand
(
intent
:
Intent
,
flags
:
Int
,
startId
:
Int
)
:
Int
=
{
Service
.
START_STICKY
}
override
def
startRunner
(
c
:
Config
)
{
config
=
c
// ensure the VPNService is prepared
if
(
VpnService
.
prepare
(
this
)
!=
null
)
{
val
i
=
new
Intent
(
this
,
classOf
[
ShadowsocksRunnerActivity
])
i
.
addFlags
(
Intent
.
FLAG_ACTIVITY_NEW_TASK
)
startActivity
(
i
)
return
}
// start the tracker
EasyTracker
.
getInstance
(
this
)
.
send
(
MapBuilder
...
...
@@ -389,26 +311,66 @@ class ShadowVpnService extends VpnService {
.
set
(
Fields
.
SESSION_CONTROL
,
"start"
)
.
build
())
notificationManager
=
getSystemService
(
Context
.
NOTIFICATION_SERVICE
)
.
asInstanceOf
[
NotificationManager
]
// register close receiver
val
filter
=
new
IntentFilter
()
filter
.
addAction
(
Intent
.
ACTION_SHUTDOWN
)
filter
.
addAction
(
Action
.
CLOSE
)
receiver
=
new
BroadcastReceiver
{
def
onReceive
(
p1
:
Context
,
p2
:
Intent
)
{
stop
Self
()
stop
Runner
()
}
}
registerReceiver
(
receiver
,
filter
)
changeState
(
State
.
CONNECTING
)
spawn
{
if
(
config
.
proxy
==
"198.199.101.152"
)
{
val
container
=
getApplication
.
asInstanceOf
[
ShadowsocksApplication
].
tagContainer
try
{
config
=
ConfigUtils
.
getPublicConfig
(
getBaseContext
,
container
,
config
)
}
catch
{
case
ex
:
Exception
=>
notifyAlert
(
getString
(
R
.
string
.
forward_fail
),
getString
(
R
.
string
.
service_failed
))
stopRunner
()
handler
.
sendEmptyMessageDelayed
(
Msg
.
CONNECT_FAIL
,
500
)
return
}
}
override
def
onDestroy
()
{
// reset the context
killProcesses
()
// Resolve the server address
var
resolved
:
Boolean
=
false
if
(!
InetAddressUtils
.
isIPv4Address
(
config
.
proxy
)
&&
!
InetAddressUtils
.
isIPv6Address
(
config
.
proxy
))
{
Utils
.
resolve
(
config
.
proxy
,
enableIPv6
=
true
)
match
{
case
Some
(
addr
)
=>
config
.
proxy
=
addr
resolved
=
true
case
None
=>
resolved
=
false
}
}
else
{
resolved
=
true
}
if
(
resolved
&&
handleConnection
)
{
handler
.
sendEmptyMessageDelayed
(
Msg
.
CONNECT_SUCCESS
,
300
)
}
else
{
notifyAlert
(
getString
(
R
.
string
.
forward_fail
),
getString
(
R
.
string
.
service_failed
))
handler
.
sendEmptyMessageDelayed
(
Msg
.
CONNECT_FAIL
,
300
)
stopRunner
()
}
handler
.
sendEmptyMessageDelayed
(
Msg
.
CONNECT_FINISH
,
300
)
}
}
override
def
stopRunner
()
{
// channge the state
changeState
(
State
.
STOPPED
)
// stop the tracker
EasyTracker
.
getInstance
(
this
)
.
send
(
MapBuilder
...
...
@@ -416,45 +378,26 @@ class ShadowVpnService extends VpnService {
.
set
(
Fields
.
SESSION_CONTROL
,
"stop"
)
.
build
())
// clean up the context
if
(
receiver
!=
null
)
{
unregisterReceiver
(
receiver
)
receiver
=
null
}
// reset VPN
killProcesses
()
// close connections
if
(
conn
!=
null
)
{
conn
.
close
()
conn
=
null
}
// reset notifications
notificationManager
.
cancel
(
1
)
super
.
onDestroy
()
}
def
killProcesses
()
{
val
sb
=
new
StringBuilder
if
(!
waitForProcess
(
"shadowsocks"
))
{
sb
++=
"kill -9 `cat /data/data/com.github.shadowsocks/shadowsocks.pid`"
++=
"\n"
sb
++=
"killall -9 shadowsocks"
++=
"\n"
}
if
(!
waitForProcess
(
"tun2socks"
))
{
sb
++=
"kill -9 `cat /data/data/com.github.shadowsocks/tun2socks.pid`"
++=
"\n"
sb
++=
"killall -9 tun2socks"
++=
"\n"
}
if
(!
waitForProcess
(
"pdnsd"
))
{
sb
++=
"kill -9 `cat /data/data/com.github.shadowsocks/pdnsd.pid`"
++=
"\n"
sb
++=
"killall -9 pdnsd"
++=
"\n"
}
Utils
.
runCommand
(
sb
.
toString
())
}
override
def
onStart
(
intent
:
Intent
,
startId
:
Int
)
{
handleCommand
(
intent
)
}
override
def
onStartCommand
(
intent
:
Intent
,
flags
:
Int
,
startId
:
Int
)
:
Int
=
{
handleCommand
(
intent
)
Service
.
START_STICKY
}
override
def
getTag
=
TAG
override
def
getServiceMode
=
Mode
.
VPN
override
def
getContext
=
getBaseContext
}
src/main/scala/com/github/shadowsocks/utils/Config.scala
→
src/main/scala/com/github/shadowsocks/utils/Config
Utils
.scala
View file @
0b8114ad
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
Config
Utils
{
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
src/main/scala/com/github/shadowsocks/utils/Constants.scala
View file @
0b8114ad
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
)
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment