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
3d664377
Commit
3d664377
authored
Apr 29, 2013
by
Max Lv
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
clean up
parent
a74c3fde
Changes
29
Hide whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
2337 additions
and
2465 deletions
+2337
-2465
res/drawable/btn_cab_done_shadow.xml
res/drawable/btn_cab_done_shadow.xml
+6
-6
res/drawable/pressed_background_shadow.xml
res/drawable/pressed_background_shadow.xml
+1
-1
res/drawable/progress_horizontal_shadow.xml
res/drawable/progress_horizontal_shadow.xml
+12
-12
res/drawable/selectable_background_shadow.xml
res/drawable/selectable_background_shadow.xml
+5
-4
res/drawable/spinner_background_ab_shadow.xml
res/drawable/spinner_background_ab_shadow.xml
+7
-7
res/drawable/tab_indicator_ab_shadow.xml
res/drawable/tab_indicator_ab_shadow.xml
+21
-13
res/layout/layout_apps.xml
res/layout/layout_apps.xml
+7
-7
res/layout/layout_apps_item.xml
res/layout/layout_apps_item.xml
+25
-25
res/layout/layout_switch.xml
res/layout/layout_switch.xml
+19
-19
res/layout/overlay.xml
res/layout/overlay.xml
+4
-4
res/values-v14/styles_shadow.xml
res/values-v14/styles_shadow.xml
+34
-33
res/values/colors.xml
res/values/colors.xml
+6
-6
res/values/strings.xml
res/values/strings.xml
+115
-115
res/values/styles_shadow.xml
res/values/styles_shadow.xml
+61
-59
res/xml/pref_feature.xml
res/xml/pref_feature.xml
+44
-44
res/xml/pref_proxy.xml
res/xml/pref_proxy.xml
+31
-31
res/xml/shadowsocks_headers.xml
res/xml/shadowsocks_headers.xml
+10
-10
src/com/github/shadowsocks/AppManager.java
src/com/github/shadowsocks/AppManager.java
+281
-307
src/com/github/shadowsocks/Exec.java
src/com/github/shadowsocks/Exec.java
+33
-38
src/com/github/shadowsocks/ImageLoader.java
src/com/github/shadowsocks/ImageLoader.java
+167
-173
src/com/github/shadowsocks/ImageLoaderFactory.java
src/com/github/shadowsocks/ImageLoaderFactory.java
+6
-6
src/com/github/shadowsocks/ProxyedApp.java
src/com/github/shadowsocks/ProxyedApp.java
+54
-78
src/com/github/shadowsocks/Shadowsocks.java
src/com/github/shadowsocks/Shadowsocks.java
+459
-492
src/com/github/shadowsocks/ShadowsocksApplication.java
src/com/github/shadowsocks/ShadowsocksApplication.java
+9
-10
src/com/github/shadowsocks/ShadowsocksReceiver.java
src/com/github/shadowsocks/ShadowsocksReceiver.java
+36
-36
src/com/github/shadowsocks/ShadowsocksService.java
src/com/github/shadowsocks/ShadowsocksService.java
+505
-525
src/com/github/shadowsocks/System.java
src/com/github/shadowsocks/System.java
+4
-4
src/com/github/shadowsocks/Utils.java
src/com/github/shadowsocks/Utils.java
+356
-376
src/com/github/shadowsocks/preferences/SummaryEditTextPreference.java
...ub/shadowsocks/preferences/SummaryEditTextPreference.java
+19
-24
No files found.
res/drawable/btn_cab_done_shadow.xml
View file @
3d664377
...
...
@@ -18,10 +18,10 @@
-->
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<item
android:state_pressed=
"true"
android:drawable=
"@drawable/btn_cab_done_pressed_shadow"
/>
<item
android:state_focused=
"true"
android:state_enabled=
"true"
android:drawable=
"@drawable/btn_cab_done_focused_shadow"
/>
<item
android:state_enabled=
"true"
android:drawable=
"@drawable/btn_cab_done_default_shadow"
/>
<item
android:state_pressed=
"true"
android:drawable=
"@drawable/btn_cab_done_pressed_shadow"
/>
<item
android:state_focused=
"true"
android:state_enabled=
"true"
android:drawable=
"@drawable/btn_cab_done_focused_shadow"
/>
<item
android:state_enabled=
"true"
android:drawable=
"@drawable/btn_cab_done_default_shadow"
/>
</selector>
res/drawable/pressed_background_shadow.xml
View file @
3d664377
...
...
@@ -18,5 +18,5 @@
-->
<shape
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:shape=
"rectangle"
>
<solid
android:color=
"@color/pressed_shadow"
/>
<solid
android:color=
"@color/pressed_shadow"
/>
</shape>
res/drawable/progress_horizontal_shadow.xml
View file @
3d664377
...
...
@@ -19,17 +19,17 @@
<layer-list
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<item
android:id=
"@android:id/background"
android:drawable=
"@drawable/progress_bg_shadow"
/>
<item
android:id=
"@android:id/secondaryProgress"
>
<scale
android:scaleWidth=
"100%"
android:drawable=
"@drawable/progress_secondary_shadow"
/>
</item>
<item
android:id=
"@android:id/progress"
>
<scale
android:scaleWidth=
"100%"
android:drawable=
"@drawable/progress_primary_shadow"
/>
</item>
<item
android:id=
"@android:id/background"
android:drawable=
"@drawable/progress_bg_shadow"
/>
<item
android:id=
"@android:id/secondaryProgress"
>
<scale
android:scaleWidth=
"100%"
android:drawable=
"@drawable/progress_secondary_shadow"
/>
</item>
<item
android:id=
"@android:id/progress"
>
<scale
android:scaleWidth=
"100%"
android:drawable=
"@drawable/progress_primary_shadow"
/>
</item>
</layer-list>
res/drawable/selectable_background_shadow.xml
View file @
3d664377
...
...
@@ -18,8 +18,9 @@
-->
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:exitFadeDuration=
"@android:integer/config_mediumAnimTime"
>
<item
android:state_pressed=
"false"
android:state_focused=
"true"
android:drawable=
"@drawable/list_focused_shadow"
/>
<item
android:state_pressed=
"true"
android:drawable=
"@drawable/pressed_background_shadow"
/>
<item
android:drawable=
"@android:color/transparent"
/>
android:exitFadeDuration=
"@android:integer/config_mediumAnimTime"
>
<item
android:state_pressed=
"false"
android:state_focused=
"true"
android:drawable=
"@drawable/list_focused_shadow"
/>
<item
android:state_pressed=
"true"
android:drawable=
"@drawable/pressed_background_shadow"
/>
<item
android:drawable=
"@android:color/transparent"
/>
</selector>
\ No newline at end of file
res/drawable/spinner_background_ab_shadow.xml
View file @
3d664377
...
...
@@ -18,11 +18,11 @@
-->
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<item
android:state_enabled=
"false"
android:drawable=
"@drawable/spinner_ab_disabled_shadow"
/>
<item
android:state_pressed=
"true"
android:drawable=
"@drawable/spinner_ab_pressed_shadow"
/>
<item
android:state_pressed=
"false"
android:state_focused=
"true"
android:drawable=
"@drawable/spinner_ab_focused_shadow"
/>
<item
android:drawable=
"@drawable/spinner_ab_default_shadow"
/>
<item
android:state_enabled=
"false"
android:drawable=
"@drawable/spinner_ab_disabled_shadow"
/>
<item
android:state_pressed=
"true"
android:drawable=
"@drawable/spinner_ab_pressed_shadow"
/>
<item
android:state_pressed=
"false"
android:state_focused=
"true"
android:drawable=
"@drawable/spinner_ab_focused_shadow"
/>
<item
android:drawable=
"@drawable/spinner_ab_default_shadow"
/>
</selector>
res/drawable/tab_indicator_ab_shadow.xml
View file @
3d664377
...
...
@@ -18,20 +18,28 @@
-->
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<!-- Non focused states -->
<item
android:state_focused=
"false"
android:state_selected=
"false"
android:state_pressed=
"false"
android:drawable=
"@android:color/transparent"
/>
<item
android:state_focused=
"false"
android:state_selected=
"true"
android:state_pressed=
"false"
android:drawable=
"@drawable/tab_selected_shadow"
/>
<!-- Non focused states -->
<item
android:state_focused=
"false"
android:state_selected=
"false"
android:state_pressed=
"false"
android:drawable=
"@android:color/transparent"
/>
<item
android:state_focused=
"false"
android:state_selected=
"true"
android:state_pressed=
"false"
android:drawable=
"@drawable/tab_selected_shadow"
/>
<!-- Focused states -->
<item
android:state_focused=
"true"
android:state_selected=
"false"
android:state_pressed=
"false"
android:drawable=
"@drawable/tab_unselected_focused_shadow"
/>
<item
android:state_focused=
"true"
android:state_selected=
"true"
android:state_pressed=
"false"
android:drawable=
"@drawable/tab_selected_focused_shadow"
/>
<!-- Focused states -->
<item
android:state_focused=
"true"
android:state_selected=
"false"
android:state_pressed=
"false"
android:drawable=
"@drawable/tab_unselected_focused_shadow"
/>
<item
android:state_focused=
"true"
android:state_selected=
"true"
android:state_pressed=
"false"
android:drawable=
"@drawable/tab_selected_focused_shadow"
/>
<!-- Pressed -->
<!-- Non focused states -->
<item
android:state_focused=
"false"
android:state_selected=
"false"
android:state_pressed=
"true"
android:drawable=
"@drawable/tab_unselected_pressed_shadow"
/>
<item
android:state_focused=
"false"
android:state_selected=
"true"
android:state_pressed=
"true"
android:drawable=
"@drawable/tab_selected_pressed_shadow"
/>
<!-- Pressed -->
<!-- Non focused states -->
<item
android:state_focused=
"false"
android:state_selected=
"false"
android:state_pressed=
"true"
android:drawable=
"@drawable/tab_unselected_pressed_shadow"
/>
<item
android:state_focused=
"false"
android:state_selected=
"true"
android:state_pressed=
"true"
android:drawable=
"@drawable/tab_selected_pressed_shadow"
/>
<!-- Focused states -->
<item
android:state_focused=
"true"
android:state_selected=
"false"
android:state_pressed=
"true"
android:drawable=
"@drawable/tab_unselected_pressed_shadow"
/>
<item
android:state_focused=
"true"
android:state_selected=
"true"
android:state_pressed=
"true"
android:drawable=
"@drawable/tab_selected_pressed_shadow"
/>
<!-- Focused states -->
<item
android:state_focused=
"true"
android:state_selected=
"false"
android:state_pressed=
"true"
android:drawable=
"@drawable/tab_unselected_pressed_shadow"
/>
<item
android:state_focused=
"true"
android:state_selected=
"true"
android:state_pressed=
"true"
android:drawable=
"@drawable/tab_selected_pressed_shadow"
/>
</selector>
res/layout/layout_apps.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width=
"fill_parent"
android:layout_height=
"fill_parent"
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:orientation=
"vertical"
android:duplicateParentState=
"false"
>
<TextView
android:text=
"@string/proxied_help"
android:layout_width=
"fill_parent"
android:layout_height=
"wrap_content"
android:textSize=
"16sp"
android:padding=
"3dp"
/>
<ListView
android:layout_width=
"fill_parent"
android:layout_height=
"wrap_content"
android:id=
"@+id/applistview"
/>
android:layout_height=
"fill_parent"
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:orientation=
"vertical"
android:duplicateParentState=
"false"
>
<TextView
android:text=
"@string/proxied_help"
android:layout_width=
"fill_parent"
android:layout_height=
"wrap_content"
android:textSize=
"16sp"
android:padding=
"3dp"
/>
<ListView
android:layout_width=
"fill_parent"
android:layout_height=
"wrap_content"
android:id=
"@+id/applistview"
/>
</LinearLayout>
\ No newline at end of file
res/layout/layout_apps_item.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<TableLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"fill_parent"
android:layout_height=
"fill_parent"
android:stretchColumns=
"1"
>
android:layout_width=
"fill_parent"
android:layout_height=
"fill_parent"
android:stretchColumns=
"1"
>
<TableRow>
<TableRow>
<ImageView
android:id=
"@+id/itemicon"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:scaleType=
"fitCenter"
android:padding=
"3dp"
/>
<ImageView
android:id=
"@+id/itemicon"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:scaleType=
"fitCenter"
android:padding=
"3dp"
/>
<TextView
android:id=
"@+id/itemtext"
android:layout_width=
"wrap_content"
android:layout_height=
"fill_parent"
android:text=
"uid:packages"
android:gravity=
"center_vertical"
android:maxLength=
"25"
android:textSize=
"18sp"
android:padding=
"3dp"
/>
<TextView
android:id=
"@+id/itemtext"
android:layout_width=
"wrap_content"
android:layout_height=
"fill_parent"
android:text=
"uid:packages"
android:gravity=
"center_vertical"
android:maxLength=
"25"
android:textSize=
"18sp"
android:padding=
"3dp"
/>
<CheckBox
android:id=
"@+id/itemcheck"
android:layout_width=
"wrap_content"
android:layout_height=
"fill_parent"
android:layout_marginRight=
"6dp"
/>
<CheckBox
android:id=
"@+id/itemcheck"
android:layout_width=
"wrap_content"
android:layout_height=
"fill_parent"
android:layout_marginRight=
"6dp"
/>
</TableRow>
</TableRow>
</TableLayout>
res/layout/layout_switch.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<TextView
android:id=
"@+id/title"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:gravity=
"center_vertical"
android:layout_marginLeft=
"16dp"
android:textSize=
"28sp"
android:textColor=
"@color/title"
android:text=
"@string/app_name"
android:layout_alignParentLeft=
"true"
android:layout_centerVertical=
"true"
/>
<TextView
android:id=
"@+id/title"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:gravity=
"center_vertical"
android:layout_marginLeft=
"16dp"
android:textSize=
"28sp"
android:textColor=
"@color/title"
android:text=
"@string/app_name"
android:layout_alignParentLeft=
"true"
android:layout_centerVertical=
"true"
/>
<org.jraf.android.backport.switchwidget.Switch
android:id=
"@+id/switchButton"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_alignParentRight=
"true"
android:layout_centerVertical=
"true"
/>
<org.jraf.android.backport.switchwidget.Switch
android:id=
"@+id/switchButton"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_alignParentRight=
"true"
android:layout_centerVertical=
"true"
/>
</RelativeLayout>
res/layout/overlay.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:minWidth=
"80dp"
android:maxWidth=
"80dp"
android:gravity=
"center"
android:textSize=
"70sp"
android:textColor=
"#ffffffff"
android:background=
"#99000088"
android:padding=
"10dp"
android:visibility=
"invisible"
/>
\ No newline at end of file
android:minWidth=
"80dp"
android:maxWidth=
"80dp"
android:gravity=
"center"
android:textSize=
"70sp"
android:textColor=
"#ffffffff"
android:background=
"#99000088"
android:padding=
"10dp"
android:visibility=
"invisible"
/>
\ No newline at end of file
res/values-v14/styles_shadow.xml
View file @
3d664377
...
...
@@ -19,38 +19,39 @@
<resources>
<style
name=
"Theme.Shadow"
parent=
"@style/Theme.Sherlock.Light.DarkActionBar"
>
<item
name=
"android:actionBarItemBackground"
>
@drawable/selectable_background_shadow
</item>
<item
name=
"android:popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<item
name=
"android:dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
<item
name=
"android:actionBarTabStyle"
>
@style/ActionBarTabStyle.Shadow
</item>
<item
name=
"android:actionDropDownStyle"
>
@style/DropDownNav.Shadow
</item>
<item
name=
"android:actionBarStyle"
>
@style/ActionBar.Solid.Shadow
</item>
<item
name=
"android:actionModeBackground"
>
@drawable/cab_background_top_shadow
</item>
<item
name=
"android:actionModeSplitBackground"
>
@drawable/cab_background_bottom_shadow
</item>
<item
name=
"android:actionModeCloseButtonStyle"
>
@style/ActionButton.CloseMode.Shadow
</item>
<!-- Light.DarkActionBar specific -->
<item
name=
"android:actionBarWidgetTheme"
>
@style/Theme.Shadow.Widget
</item>
</style>
<style
name=
"ActionBar.Solid.Shadow"
parent=
"@style/Widget.Sherlock.Light.ActionBar.Solid.Inverse"
>
<item
name=
"android:background"
>
@drawable/ab_solid_shadow
</item>
<item
name=
"android:backgroundStacked"
>
@drawable/ab_stacked_solid_shadow
</item>
<item
name=
"android:backgroundSplit"
>
@drawable/ab_bottom_solid_shadow
</item>
<item
name=
"android:progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
</style>
<style
name=
"ActionBar.Transparent.Shadow"
parent=
"@style/Widget.Sherlock.ActionBar"
>
<item
name=
"android:background"
>
@drawable/ab_transparent_shadow
</item>
<item
name=
"android:progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
</style>
<!-- this style is only referenced in a Light.DarkActionBar based theme -->
<style
name=
"Theme.Shadow.Widget"
parent=
"@style/Theme.Sherlock"
>
<item
name=
"android:popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<item
name=
"android:dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
</style>
<style
name=
"Theme.Shadow"
parent=
"@style/Theme.Sherlock.Light.DarkActionBar"
>
<item
name=
"android:actionBarItemBackground"
>
@drawable/selectable_background_shadow
</item>
<item
name=
"android:popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<item
name=
"android:dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
<item
name=
"android:actionBarTabStyle"
>
@style/ActionBarTabStyle.Shadow
</item>
<item
name=
"android:actionDropDownStyle"
>
@style/DropDownNav.Shadow
</item>
<item
name=
"android:actionBarStyle"
>
@style/ActionBar.Solid.Shadow
</item>
<item
name=
"android:actionModeBackground"
>
@drawable/cab_background_top_shadow
</item>
<item
name=
"android:actionModeSplitBackground"
>
@drawable/cab_background_bottom_shadow
</item>
<item
name=
"android:actionModeCloseButtonStyle"
>
@style/ActionButton.CloseMode.Shadow
</item>
<!-- Light.DarkActionBar specific -->
<item
name=
"android:actionBarWidgetTheme"
>
@style/Theme.Shadow.Widget
</item>
</style>
<style
name=
"ActionBar.Solid.Shadow"
parent=
"@style/Widget.Sherlock.Light.ActionBar.Solid.Inverse"
>
<item
name=
"android:background"
>
@drawable/ab_solid_shadow
</item>
<item
name=
"android:backgroundStacked"
>
@drawable/ab_stacked_solid_shadow
</item>
<item
name=
"android:backgroundSplit"
>
@drawable/ab_bottom_solid_shadow
</item>
<item
name=
"android:progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
</style>
<style
name=
"ActionBar.Transparent.Shadow"
parent=
"@style/Widget.Sherlock.ActionBar"
>
<item
name=
"android:background"
>
@drawable/ab_transparent_shadow
</item>
<item
name=
"android:progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
</style>
<!-- this style is only referenced in a Light.DarkActionBar based theme -->
<style
name=
"Theme.Shadow.Widget"
parent=
"@style/Theme.Sherlock"
>
<item
name=
"android:popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<item
name=
"android:dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
</style>
</resources>
\ No newline at end of file
res/values/colors.xml
View file @
3d664377
...
...
@@ -16,11 +16,11 @@
<resources>
<color
name=
"title_background"
>
#ff5e5e5e
</color>
<color
name=
"dialog_title_background"
>
#17170443
</color>
<color
name=
"black"
>
#000000
</color>
<color
name=
"grey1"
>
#646464
</color>
<color
name=
"grey2"
>
#c8c8c8
</color>
<color
name=
"grey3"
>
#323232
</color>
<color
name=
"title_background"
>
#ff5e5e5e
</color>
<color
name=
"dialog_title_background"
>
#17170443
</color>
<color
name=
"black"
>
#000000
</color>
<color
name=
"grey1"
>
#646464
</color>
<color
name=
"grey2"
>
#c8c8c8
</color>
<color
name=
"grey3"
>
#323232
</color>
</resources>
\ No newline at end of file
res/values/strings.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string
name=
"app_name"
>
shadowsocks
</string>
<string
name=
"app_name"
>
shadowsocks
</string>
<!-- function category -->
<string
name=
"function_cat"
>
Service Controller
</string>
<string
name=
"service_controller"
>
Proxy Switch
</string>
<string
name=
"service_summary"
>
Enable / Disable Proxy
</string>
<!-- function category -->
<string
name=
"function_cat"
>
Service Controller
</string>
<string
name=
"service_controller"
>
Proxy Switch
</string>
<string
name=
"service_summary"
>
Enable / Disable Proxy
</string>
<!-- proxy category -->
<string
name=
"proxy_cat"
>
Shadowsocks Settings
</string>
<string
name=
"proxy"
>
Server
</string>
<string
name=
"proxy_summary"
>
Hostname of your Shadowsocks server
</string>
<string
name=
"remote_port"
>
Remote Port
</string>
<string
name=
"remote_port_summary"
>
Remote port of shadowsocks server.
</string>
<string
name=
"port"
>
Local Port
</string>
<string
name=
"port_summary"
>
Local port listening on
</string>
<string
name=
"sitekey"
>
Password
</string>
<string
name=
"sitekey_summary"
>
Password of your Shadowsocks server
</string>
<string
name=
"enc_method"
>
Encrypt Method
</string>
<array
name=
"enc_method_entry"
>
<item>
Table
</item>
<item>
RC4
</item>
</array>
<array
name=
"enc_method_value"
>
<item>
table
</item>
<item>
rc4
</item>
</array>
<!-- proxy category -->
<string
name=
"proxy_cat"
>
Shadowsocks Settings
</string>
<string
name=
"proxy"
>
Server
</string>
<string
name=
"proxy_summary"
>
Hostname of your Shadowsocks server
</string>
<string
name=
"remote_port"
>
Remote Port
</string>
<string
name=
"remote_port_summary"
>
Remote port of shadowsocks server.
</string>
<string
name=
"port"
>
Local Port
</string>
<string
name=
"port_summary"
>
Local port listening on
</string>
<string
name=
"sitekey"
>
Password
</string>
<string
name=
"sitekey_summary"
>
Password of your Shadowsocks server
</string>
<string
name=
"enc_method"
>
Encrypt Method
</string>
<array
name=
"enc_method_entry"
>
<item>
Table
</item>
<item>
RC4
</item>
</array>
<array
name=
"enc_method_value"
>
<item>
table
</item>
<item>
rc4
</item>
</array>
<!-- feature category -->
<string
name=
"feature_cat"
>
Feature Settings
</string>
<string
name=
"http_proxy"
>
HTTP Proxy
</string>
<string
name=
"http_proxy_summary"
>
Local HTTP Proxy
</string>
<string
name=
"dns_proxy"
>
DNS Proxy
</string>
<string
name=
"dns_proxy_summary"
>
Redirect DNS queries to remote server.
</string>
<string
name=
"auto_set_gfwlist"
>
CHN Route
</string>
<string
name=
"auto_set_gfwlist_summary"
>
Bypass all sites located in China
</string>
<string
name=
"auto_set_proxy"
>
Global Proxy
</string>
<string
name=
"auto_set_proxy_summary"
>
Set up system wide proxy
</string>
<string
name=
"proxied_apps"
>
Individual Proxy
</string>
<string
name=
"proxied_apps_summary"
>
Set individual proxy for apps
</string>
<string
name=
"proxied_help"
>
Select apps to use with Shadowsocks:
</string>
<string
name=
"bypass_apps"
>
Bypass Mode
</string>
<string
name=
"bypass_apps_summary"
>
Enable this option to bypass selected apps
</string>
<string
name=
"auto_connect"
>
Auto Connect
</string>
<string
name=
"auto_connect_summary"
>
Start Shadowsocks at the startup
</string>
<!-- feature category -->
<string
name=
"feature_cat"
>
Feature Settings
</string>
<string
name=
"http_proxy"
>
HTTP Proxy
</string>
<string
name=
"http_proxy_summary"
>
Local HTTP Proxy
</string>
<string
name=
"dns_proxy"
>
DNS Proxy
</string>
<string
name=
"dns_proxy_summary"
>
Redirect DNS queries to remote server.
</string>
<string
name=
"auto_set_gfwlist"
>
CHN Route
</string>
<string
name=
"auto_set_gfwlist_summary"
>
Bypass all sites located in China
</string>
<string
name=
"auto_set_proxy"
>
Global Proxy
</string>
<string
name=
"auto_set_proxy_summary"
>
Set up system wide proxy
</string>
<string
name=
"proxied_apps"
>
Individual Proxy
</string>
<string
name=
"proxied_apps_summary"
>
Set individual proxy for apps
</string>
<string
name=
"proxied_help"
>
Select apps to use with Shadowsocks:
</string>
<string
name=
"bypass_apps"
>
Bypass Mode
</string>
<string
name=
"bypass_apps_summary"
>
Enable this option to bypass selected apps
</string>
<string
name=
"auto_connect"
>
Auto Connect
</string>
<string
name=
"auto_connect_summary"
>
Start Shadowsocks at the startup
</string>
<!-- notification category -->
<string
name=
"forward_success"
>
Shadowsocks started
</string>
<string
name=
"service_running"
>
Running in the background
</string>
<string
name=
"forward_fail"
>
Shadowsocks failed to start
</string>
<string
name=
"service_stopped"
>
Shadowsocks stopped
</string>
<string
name=
"forward_stop"
>
Shadowsocks stopped
</string>
<string
name=
"service_failed"
>
Please check your network status and try again.
</string>
<!-- notification category -->
<string
name=
"forward_success"
>
Shadowsocks started
</string>
<string
name=
"service_running"
>
Running in the background
</string>
<string
name=
"forward_fail"
>
Shadowsocks failed to start
</string>
<string
name=
"service_stopped"
>
Shadowsocks stopped
</string>
<string
name=
"forward_stop"
>
Shadowsocks stopped
</string>
<string
name=
"service_failed"
>
Please check your network status and try again.
</string>
<!-- alert category -->
<string
name=
"ok_iknow"
>
OK
</string>
<string
name=
"port_alert"
>
The port number should be greater than 1024
</string>
<string
name=
"port_empty"
>
Port should not be empty
</string>
<string
name=
"proxy_empty"
>
Proxy should not be empty
</string>
<string
name=
"crash_alert"
>
An unexpected exit detected, context has been recovered
</string>
<string
name=
"connecting"
>
Connecting…
</string>
<string
name=
"initializing"
>
Initializing…
</string>
<string
name=
"recovering"
>
Reseting…
</string>
<string
name=
"loading"
>
Loading app list…
</string>
<!-- alert category -->
<string
name=
"ok_iknow"
>
OK
</string>
<string
name=
"port_alert"
>
The port number should be greater than 1024
</string>
<string
name=
"port_empty"
>
Port should not be empty
</string>
<string
name=
"proxy_empty"
>
Proxy should not be empty
</string>
<string
name=
"crash_alert"
>
An unexpected exit detected, context has been recovered
</string>
<string
name=
"connecting"
>
Connecting…
</string>
<string
name=
"initializing"
>
Initializing…
</string>
<string
name=
"recovering"
>
Reseting…
</string>
<string
name=
"loading"
>
Loading app list…
</string>
<!-- menu category -->
<string
name=
"recovery"
>
Reset
</string>
<string
name=
"about"
>
About
</string>
<string
name=
"copy_rights"
>
Shadowsocks is an open source socks proxy.\n\nYou can find more details
here:\ngithub.com/shadowsocks
</string>
<!-- menu category -->
<string
name=
"recovery"
>
Reset
</string>
<string
name=
"about"
>
About
</string>
<string
name=
"copy_rights"
>
Shadowsocks is an open source socks proxy.\n\nYou can find more details
here:\ngithub.com/shadowsocks
</string>
<!-- array category -->
<string-array
name=
"chn_list"
>
<item>
0.0.0.0/7
</item>
<item>
10.0.0.0/8
</item>
<item>
14.0.0.0/8
</item>
<item>
27.0.0.0/8
</item>
<item>
36.0.0.0/8
</item>
<item>
39.0.0.0/8
</item>
<item>
42.0.0.0/7
</item>
<item>
49.0.0.0/8
</item>
<item>
58.0.0.0/7
</item>
<item>
60.0.0.0/7
</item>
<item>
101.0.0.0/8
</item>
<item>
103.0.0.0/8
</item>
<item>
106.0.0.0/8
</item>
<item>
110.0.0.0/7
</item>
<item>
112.0.0.0/4
</item>
<item>
134.128.0.0/9
</item>
<item>
139.0.0.0/9
</item>
<item>
139.128.0.0/9
</item>
<item>
140.0.0.0/9
</item>
<item>
140.128.0.0/9
</item>
<item>
144.0.0.0/9
</item>
<item>
144.128.0.0/9
</item>
<item>
150.0.0.0/8
</item>
<item>
153.0.0.0/8
</item>
<item>
157.0.0.0/9
</item>
<item>
157.128.0.0/9
</item>
<item>
159.128.0.0/9
</item>
<item>
161.128.0.0/9
</item>
<item>
162.0.0.0/9
</item>
<item>
163.0.0.0/8
</item>
<item>
166.0.0.0/9
</item>
<item>
167.128.0.0/9
</item>
<item>
168.128.0.0/9
</item>
<item>
171.0.0.0/8
</item>
<item>
172.0.0.0/12
</item>
<item>
175.0.0.0/8
</item>
<item>
180.0.0.0/8
</item>
<item>
182.0.0.0/7
</item>
<item>
192.112.0.0/12
</item>
<item>
192.128.0.0/9
</item>
<item>
202.0.0.0/7
</item>
<item>
210.0.0.0/7
</item>
<item>
218.0.0.0/7
</item>
<item>
220.0.0.0/6
</item>
</string-array>
<!-- array category -->
<string-array
name=
"chn_list"
>
<item>
0.0.0.0/7
</item>
<item>
10.0.0.0/8
</item>
<item>
14.0.0.0/8
</item>
<item>
27.0.0.0/8
</item>
<item>
36.0.0.0/8
</item>
<item>
39.0.0.0/8
</item>
<item>
42.0.0.0/7
</item>
<item>
49.0.0.0/8
</item>
<item>
58.0.0.0/7
</item>
<item>
60.0.0.0/7
</item>
<item>
101.0.0.0/8
</item>
<item>
103.0.0.0/8
</item>
<item>
106.0.0.0/8
</item>
<item>
110.0.0.0/7
</item>
<item>
112.0.0.0/4
</item>
<item>
134.128.0.0/9
</item>
<item>
139.0.0.0/9
</item>
<item>
139.128.0.0/9
</item>
<item>
140.0.0.0/9
</item>
<item>
140.128.0.0/9
</item>
<item>
144.0.0.0/9
</item>
<item>
144.128.0.0/9
</item>
<item>
150.0.0.0/8
</item>
<item>
153.0.0.0/8
</item>
<item>
157.0.0.0/9
</item>
<item>
157.128.0.0/9
</item>
<item>
159.128.0.0/9
</item>
<item>
161.128.0.0/9
</item>
<item>
162.0.0.0/9
</item>
<item>
163.0.0.0/8
</item>
<item>
166.0.0.0/9
</item>
<item>
167.128.0.0/9
</item>
<item>
168.128.0.0/9
</item>
<item>
171.0.0.0/8
</item>
<item>
172.0.0.0/12
</item>
<item>
175.0.0.0/8
</item>
<item>
180.0.0.0/8
</item>
<item>
182.0.0.0/7
</item>
<item>
192.112.0.0/12
</item>
<item>
192.128.0.0/9
</item>
<item>
202.0.0.0/7
</item>
<item>
210.0.0.0/7
</item>
<item>
218.0.0.0/7
</item>
<item>
220.0.0.0/6
</item>
</string-array>
</resources>
res/values/styles_shadow.xml
View file @
3d664377
...
...
@@ -19,64 +19,66 @@
<resources>
<style
name=
"Theme.Shadow"
parent=
"@style/Theme.Sherlock.Light.DarkActionBar"
>
<item
name=
"actionBarItemBackground"
>
@drawable/selectable_background_shadow
</item>
<item
name=
"popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<item
name=
"dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
<item
name=
"actionBarTabStyle"
>
@style/ActionBarTabStyle.Shadow
</item>
<item
name=
"actionDropDownStyle"
>
@style/DropDownNav.Shadow
</item>
<item
name=
"actionBarStyle"
>
@style/ActionBar.Solid.Shadow
</item>
<item
name=
"actionModeBackground"
>
@drawable/cab_background_top_shadow
</item>
<item
name=
"actionModeSplitBackground"
>
@drawable/cab_background_bottom_shadow
</item>
<item
name=
"actionModeCloseButtonStyle"
>
@style/ActionButton.CloseMode.Shadow
</item>
<!-- Light.DarkActionBar specific -->
<item
name=
"actionBarWidgetTheme"
>
@style/Theme.Shadow.Widget
</item>
</style>
<style
name=
"ActionBar.Solid.Shadow"
parent=
"@style/Widget.Sherlock.Light.ActionBar.Solid.Inverse"
>
<item
name=
"background"
>
@drawable/ab_solid_shadow
</item>
<item
name=
"backgroundStacked"
>
@drawable/ab_stacked_solid_shadow
</item>
<item
name=
"backgroundSplit"
>
@drawable/ab_bottom_solid_shadow
</item>
<item
name=
"progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
</style>
<style
name=
"ActionBar.Transparent.Shadow"
parent=
"@style/Widget.Sherlock.ActionBar"
>
<item
name=
"background"
>
@drawable/ab_transparent_shadow
</item>
<item
name=
"progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
</style>
<style
name=
"PopupMenu.Shadow"
parent=
"@style/Widget.Sherlock.ListPopupWindow"
>
<item
name=
"android:popupBackground"
>
@drawable/menu_dropdown_panel_shadow
</item>
</style>
<style
name=
"DropDownListView.Shadow"
parent=
"@style/Widget.Sherlock.ListView.DropDown"
>
<item
name=
"android:listSelector"
>
@drawable/selectable_background_shadow
</item>
</style>
<style
name=
"ActionBarTabStyle.Shadow"
parent=
"@style/Widget.Sherlock.ActionBar.TabView"
>
<item
name=
"android:background"
>
@drawable/tab_indicator_ab_shadow
</item>
</style>
<style
name=
"DropDownNav.Shadow"
parent=
"@style/Widget.Sherlock.Spinner.DropDown.ActionBar"
>
<item
name=
"android:background"
>
@drawable/spinner_background_ab_shadow
</item>
<item
name=
"android:popupBackground"
>
@drawable/menu_dropdown_panel_shadow
</item>
<item
name=
"android:dropDownSelector"
>
@drawable/selectable_background_shadow
</item>
</style>
<style
name=
"ProgressBar.Shadow"
parent=
"@style/Widget.Sherlock.ProgressBar.Horizontal"
>
<item
name=
"android:progressDrawable"
>
@drawable/progress_horizontal_shadow
</item>
</style>
<style
name=
"ActionButton.CloseMode.Shadow"
parent=
"@style/Widget.Sherlock.ActionButton.CloseMode"
>
<item
name=
"android:background"
>
@drawable/btn_cab_done_shadow
</item>
</style>
<!-- this style is only referenced in a Light.DarkActionBar based theme -->
<style
name=
"Theme.Shadow.Widget"
parent=
"@style/Theme.Sherlock"
>
<item
name=
"popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<item
name=
"dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
</style>
<style
name=
"Theme.Shadow"
parent=
"@style/Theme.Sherlock.Light.DarkActionBar"
>
<item
name=
"actionBarItemBackground"
>
@drawable/selectable_background_shadow
</item>
<item
name=
"popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<item
name=
"dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
<item
name=
"actionBarTabStyle"
>
@style/ActionBarTabStyle.Shadow
</item>
<item
name=
"actionDropDownStyle"
>
@style/DropDownNav.Shadow
</item>
<item
name=
"actionBarStyle"
>
@style/ActionBar.Solid.Shadow
</item>
<item
name=
"actionModeBackground"
>
@drawable/cab_background_top_shadow
</item>
<item
name=
"actionModeSplitBackground"
>
@drawable/cab_background_bottom_shadow
</item>
<item
name=
"actionModeCloseButtonStyle"
>
@style/ActionButton.CloseMode.Shadow
</item>
<!-- Light.DarkActionBar specific -->
<item
name=
"actionBarWidgetTheme"
>
@style/Theme.Shadow.Widget
</item>
</style>
<style
name=
"ActionBar.Solid.Shadow"
parent=
"@style/Widget.Sherlock.Light.ActionBar.Solid.Inverse"
>
<item
name=
"background"
>
@drawable/ab_solid_shadow
</item>
<item
name=
"backgroundStacked"
>
@drawable/ab_stacked_solid_shadow
</item>
<item
name=
"backgroundSplit"
>
@drawable/ab_bottom_solid_shadow
</item>
<item
name=
"progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
</style>
<style
name=
"ActionBar.Transparent.Shadow"
parent=
"@style/Widget.Sherlock.ActionBar"
>
<item
name=
"background"
>
@drawable/ab_transparent_shadow
</item>
<item
name=
"progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
</style>
<style
name=
"PopupMenu.Shadow"
parent=
"@style/Widget.Sherlock.ListPopupWindow"
>
<item
name=
"android:popupBackground"
>
@drawable/menu_dropdown_panel_shadow
</item>
</style>
<style
name=
"DropDownListView.Shadow"
parent=
"@style/Widget.Sherlock.ListView.DropDown"
>
<item
name=
"android:listSelector"
>
@drawable/selectable_background_shadow
</item>
</style>
<style
name=
"ActionBarTabStyle.Shadow"
parent=
"@style/Widget.Sherlock.ActionBar.TabView"
>
<item
name=
"android:background"
>
@drawable/tab_indicator_ab_shadow
</item>
</style>
<style
name=
"DropDownNav.Shadow"
parent=
"@style/Widget.Sherlock.Spinner.DropDown.ActionBar"
>
<item
name=
"android:background"
>
@drawable/spinner_background_ab_shadow
</item>
<item
name=
"android:popupBackground"
>
@drawable/menu_dropdown_panel_shadow
</item>
<item
name=
"android:dropDownSelector"
>
@drawable/selectable_background_shadow
</item>
</style>
<style
name=
"ProgressBar.Shadow"
parent=
"@style/Widget.Sherlock.ProgressBar.Horizontal"
>
<item
name=
"android:progressDrawable"
>
@drawable/progress_horizontal_shadow
</item>
</style>
<style
name=
"ActionButton.CloseMode.Shadow"
parent=
"@style/Widget.Sherlock.ActionButton.CloseMode"
>
<item
name=
"android:background"
>
@drawable/btn_cab_done_shadow
</item>
</style>
<!-- this style is only referenced in a Light.DarkActionBar based theme -->
<style
name=
"Theme.Shadow.Widget"
parent=
"@style/Theme.Sherlock"
>
<item
name=
"popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<item
name=
"dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
</style>
</resources>
\ No newline at end of file
res/xml/pref_feature.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<CheckBoxPreference
android:defaultValue=
"false"
android:key=
"isHTTPProxy"
android:summary=
"@string/http_proxy_summary"
android:title=
"@string/http_proxy"
>
</CheckBoxPreference>
<CheckBoxPreference
android:defaultValue=
"true"
android:key=
"isDNSProxy"
android:summary=
"@string/dns_proxy_summary"
android:title=
"@string/dns_proxy"
>
</CheckBoxPreference>
<CheckBoxPreference
android:defaultValue=
"true"
android:key=
"isGFWList"
android:summary=
"@string/auto_set_gfwlist_summary"
android:title=
"@string/auto_set_gfwlist"
>
</CheckBoxPreference>
<CheckBoxPreference
android:defaultValue=
"true"
android:key=
"isGlobalProxy"
android:disableDependentsState=
"true"
android:summary=
"@string/auto_set_proxy_summary"
android:title=
"@string/auto_set_proxy"
>
</CheckBoxPreference>
<CheckBoxPreference
android:defaultValue=
"false"
android:dependency=
"isGlobalProxy"
android:key=
"isBypassApps"
android:summary=
"@string/bypass_apps_summary"
android:title=
"@string/bypass_apps"
>
</CheckBoxPreference>
<Preference
android:key=
"proxyedApps"
android:dependency=
"isGlobalProxy"
android:summary=
"@string/proxied_apps_summary"
android:title=
"@string/proxied_apps"
>
<intent
android:action=
"com.github.shadowsocks.AppManager"
/>
</Preference>
<CheckBoxPreference
android:key=
"isAutoConnect"
android:summary=
"@string/auto_connect_summary"
android:title=
"@string/auto_connect"
>
</CheckBoxPreference>
<CheckBoxPreference
android:defaultValue=
"false"
android:key=
"isHTTPProxy"
android:summary=
"@string/http_proxy_summary"
android:title=
"@string/http_proxy"
>
</CheckBoxPreference>
<CheckBoxPreference
android:defaultValue=
"true"
android:key=
"isDNSProxy"
android:summary=
"@string/dns_proxy_summary"
android:title=
"@string/dns_proxy"
>
</CheckBoxPreference>
<CheckBoxPreference
android:defaultValue=
"true"
android:key=
"isGFWList"
android:summary=
"@string/auto_set_gfwlist_summary"
android:title=
"@string/auto_set_gfwlist"
>
</CheckBoxPreference>
<CheckBoxPreference
android:defaultValue=
"true"
android:key=
"isGlobalProxy"
android:disableDependentsState=
"true"
android:summary=
"@string/auto_set_proxy_summary"
android:title=
"@string/auto_set_proxy"
>
</CheckBoxPreference>
<CheckBoxPreference
android:defaultValue=
"false"
android:dependency=
"isGlobalProxy"
android:key=
"isBypassApps"
android:summary=
"@string/bypass_apps_summary"
android:title=
"@string/bypass_apps"
>
</CheckBoxPreference>
<Preference
android:key=
"proxyedApps"
android:dependency=
"isGlobalProxy"
android:summary=
"@string/proxied_apps_summary"
android:title=
"@string/proxied_apps"
>
<intent
android:action=
"com.github.shadowsocks.AppManager"
/>
</Preference>
<CheckBoxPreference
android:key=
"isAutoConnect"
android:summary=
"@string/auto_connect_summary"
android:title=
"@string/auto_connect"
>
</CheckBoxPreference>
</PreferenceScreen>
res/xml/pref_proxy.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue=
"198.199.101.152"
android:key=
"proxy"
android:summary=
"@string/proxy_summary"
android:title=
"@string/proxy"
>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue=
"443"
android:key=
"remotePort"
android:summary=
"@string/remote_port_summary"
android:title=
"@string/remote_port"
>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue=
"1080"
android:key=
"port"
android:summary=
"@string/port_summary"
android:title=
"@string/port"
>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue=
"u1rRWTssNv0p"
android:key=
"sitekey"
android:summary=
"@string/sitekey_summary"
android:title=
"@string/sitekey"
>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<ListPreference
android:defaultValue=
"rc4"
android:key=
"encMethod"
android:entries=
"@array/enc_method_entry"
android:entryValues=
"@array/enc_method_value"
android:title=
"@string/enc_method"
>
</ListPreference>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue=
"198.199.101.152"
android:key=
"proxy"
android:summary=
"@string/proxy_summary"
android:title=
"@string/proxy"
>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue=
"443"
android:key=
"remotePort"
android:summary=
"@string/remote_port_summary"
android:title=
"@string/remote_port"
>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue=
"1080"
android:key=
"port"
android:summary=
"@string/port_summary"
android:title=
"@string/port"
>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue=
"u1rRWTssNv0p"
android:key=
"sitekey"
android:summary=
"@string/sitekey_summary"
android:title=
"@string/sitekey"
>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<ListPreference
android:defaultValue=
"rc4"
android:key=
"encMethod"
android:entries=
"@array/enc_method_entry"
android:entryValues=
"@array/enc_method_value"
android:title=
"@string/enc_method"
>
</ListPreference>
</PreferenceScreen>
res/xml/shadowsocks_headers.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<preference-headers
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:unified=
"http://schemas.android.com/apk/res-auto"
>
<preference-headers
xmlns:unified=
"http://schemas.android.com/apk/res-auto"
>
<header
unified:fragment=
"com.github.shadowsocks.Shadowsocks$ProxyFragment"
unified:title=
"@string/proxy_cat"
unified:preferenceRes=
"@xml/pref_proxy"
/>
<header
unified:fragment=
"com.github.shadowsocks.Shadowsocks$FeatureFragment"
unified:title=
"@string/feature_cat"
unified:preferenceRes=
"@xml/pref_feature"
/>
<header
unified:fragment=
"com.github.shadowsocks.Shadowsocks$ProxyFragment"
unified:title=
"@string/proxy_cat"
unified:preferenceRes=
"@xml/pref_proxy"
/>
<header
unified:fragment=
"com.github.shadowsocks.Shadowsocks$FeatureFragment"
unified:title=
"@string/feature_cat"
unified:preferenceRes=
"@xml/pref_feature"
/>
</preference-headers>
\ No newline at end of file
src/com/github/shadowsocks/AppManager.java
View file @
3d664377
...
...
@@ -21,388 +21,362 @@ import android.view.View.OnClickListener;
import
android.view.ViewGroup
;
import
android.view.ViewGroup.LayoutParams
;
import
android.view.WindowManager
;
import
android.widget.
*
;
import
android.widget.
AbsListView
;
import
android.widget.AbsListView.OnScrollListener
;
import
android.widget.ArrayAdapter
;
import
android.widget.CheckBox
;
import
android.widget.CompoundButton
;
import
android.widget.CompoundButton.OnCheckedChangeListener
;
import
android.widget.ImageView
;
import
android.widget.ListAdapter
;
import
android.widget.ListView
;
import
android.widget.TextView
;
import
com.actionbarsherlock.app.SherlockActivity
;
import
java.util.Arrays
;
import
java.util.Comparator
;
import
java.util.Iterator
;
import
java.util.List
;
import
java.util.StringTokenizer
;
import
java.util.Vector
;
public
class
AppManager
extends
SherlockActivity
implements
OnCheckedChangeListener
,
OnClickListener
{
public
final
static
String
PREFS_KEY_PROXYED
=
"Proxyed"
;
private
static
final
int
MSG_LOAD_START
=
1
;
private
static
final
int
MSG_LOAD_FINISH
=
2
;
final
Handler
handler
=
new
Handler
()
{
@Override
public
void
handleMessage
(
Message
msg
)
{
switch
(
msg
.
what
)
{
case
MSG_LOAD_START:
pd
=
ProgressDialog
.
show
(
AppManager
.
this
,
""
,
getString
(
R
.
string
.
loading
),
true
,
true
);
break
;
case
MSG_LOAD_FINISH:
import
java.util.*
;
public
class
AppManager
extends
SherlockActivity
implements
OnCheckedChangeListener
,
OnClickListener
{
public
final
static
String
PREFS_KEY_PROXYED
=
"Proxyed"
;
private
static
final
int
MSG_LOAD_START
=
1
;
private
static
final
int
MSG_LOAD_FINISH
=
2
;
final
Handler
handler
=
new
Handler
()
{
@Override
public
void
handleMessage
(
Message
msg
)
{
switch
(
msg
.
what
)
{
case
MSG_LOAD_START:
pd
=
ProgressDialog
.
show
(
AppManager
.
this
,
""
,
getString
(
R
.
string
.
loading
),
true
,
true
);
break
;
case
MSG_LOAD_FINISH:
listApps
.
setAdapter
(
adapter
);
listApps
.
setOnScrollListener
(
new
OnScrollListener
()
{
boolean
visible
;
@Override
public
void
onScroll
(
AbsListView
view
,
int
firstVisibleItem
,
int
visibleItemCount
,
int
totalItemCount
)
{
if
(
visible
)
{
String
name
=
apps
[
firstVisibleItem
].
getName
();
if
(
name
!=
null
&&
name
.
length
()
>
1
)
overlay
.
setText
(
apps
[
firstVisibleItem
]
.
getName
().
substring
(
0
,
1
));
else
overlay
.
setText
(
"*"
);
overlay
.
setVisibility
(
View
.
VISIBLE
);
}
}
@Override
public
void
onScrollStateChanged
(
AbsListView
view
,
int
scrollState
)
{
visible
=
true
;
if
(
scrollState
==
OnScrollListener
.
SCROLL_STATE_IDLE
)
{
overlay
.
setVisibility
(
View
.
INVISIBLE
);
}
}
});
if
(
pd
!=
null
)
{
pd
.
dismiss
();
pd
=
null
;
}
break
;
}
super
.
handleMessage
(
msg
);
}
};
private
ProxyedApp
[]
apps
=
null
;
private
ListView
listApps
;
private
AppManager
mAppManager
;
private
TextView
overlay
;
private
ProgressDialog
pd
=
null
;
private
ListAdapter
adapter
;
private
ImageLoader
dm
;
private
boolean
appsLoaded
=
false
;
public
static
ProxyedApp
[]
getProxyedApps
(
Context
context
)
{
SharedPreferences
prefs
=
PreferenceManager
.
getDefaultSharedPreferences
(
context
);
String
tordAppString
=
prefs
.
getString
(
PREFS_KEY_PROXYED
,
""
);
String
[]
tordApps
;
StringTokenizer
st
=
new
StringTokenizer
(
tordAppString
,
"|"
);
tordApps
=
new
String
[
st
.
countTokens
()];
int
tordIdx
=
0
;
while
(
st
.
hasMoreTokens
())
{
tordApps
[
tordIdx
++]
=
st
.
nextToken
();
}
Arrays
.
sort
(
tordApps
);
// else load the apps up
PackageManager
pMgr
=
context
.
getPackageManager
();
List
<
ApplicationInfo
>
lAppInfo
=
pMgr
.
getInstalledApplications
(
0
);
Iterator
<
ApplicationInfo
>
itAppInfo
=
lAppInfo
.
iterator
();
Vector
<
ProxyedApp
>
vectorApps
=
new
Vector
<
ProxyedApp
>();
ApplicationInfo
aInfo
=
null
;
while
(
itAppInfo
.
hasNext
())
{
aInfo
=
itAppInfo
.
next
();
// ignore system apps
if
(
aInfo
.
uid
<
10000
)
continue
;
ProxyedApp
app
=
new
ProxyedApp
();
listApps
.
setAdapter
(
adapter
);
app
.
setUid
(
aInfo
.
uid
);
listApps
.
setOnScrollListener
(
new
OnScrollListener
()
{
app
.
setUsername
(
pMgr
.
getNameForUid
(
app
.
getUid
()))
;
boolean
visible
;
// check if this application is allowed
if
(
aInfo
.
packageName
!=
null
&&
aInfo
.
packageName
.
equals
(
"com.github.shadowsocks"
))
{
app
.
setProxyed
(
true
);
}
else
if
(
Arrays
.
binarySearch
(
tordApps
,
app
.
getUsername
())
>=
0
)
{
app
.
setProxyed
(
true
);
}
else
{
app
.
setProxyed
(
false
);
@Override
public
void
onScroll
(
AbsListView
view
,
int
firstVisibleItem
,
int
visibleItemCount
,
int
totalItemCount
)
{
if
(
visible
)
{
String
name
=
apps
[
firstVisibleItem
].
getName
();
if
(
name
!=
null
&&
name
.
length
()
>
1
)
{
overlay
.
setText
(
apps
[
firstVisibleItem
].
getName
().
substring
(
0
,
1
));
}
else
{
overlay
.
setText
(
"*"
);
}
overlay
.
setVisibility
(
View
.
VISIBLE
);
}
}
vectorApps
.
add
(
app
);
}
ProxyedApp
[]
apps
=
new
ProxyedApp
[
vectorApps
.
size
()];
vectorApps
.
toArray
(
apps
);
return
apps
;
@Override
public
void
onScrollStateChanged
(
AbsListView
view
,
int
scrollState
)
{
visible
=
true
;
if
(
scrollState
==
OnScrollListener
.
SCROLL_STATE_IDLE
)
{
overlay
.
setVisibility
(
View
.
INVISIBLE
);
}
}
});
if
(
pd
!=
null
)
{
pd
.
dismiss
();
pd
=
null
;
}
break
;
}
super
.
handleMessage
(
msg
);
}
};
private
ProxyedApp
[]
apps
=
null
;
private
ListView
listApps
;
private
AppManager
mAppManager
;
private
TextView
overlay
;
private
ProgressDialog
pd
=
null
;
private
ListAdapter
adapter
;
private
ImageLoader
dm
;
private
boolean
appsLoaded
=
false
;
public
static
ProxyedApp
[]
getProxyedApps
(
Context
context
)
{
SharedPreferences
prefs
=
PreferenceManager
.
getDefaultSharedPreferences
(
context
);
String
tordAppString
=
prefs
.
getString
(
PREFS_KEY_PROXYED
,
""
);
String
[]
tordApps
;
StringTokenizer
st
=
new
StringTokenizer
(
tordAppString
,
"|"
);
tordApps
=
new
String
[
st
.
countTokens
()];
int
tordIdx
=
0
;
while
(
st
.
hasMoreTokens
())
{
tordApps
[
tordIdx
++]
=
st
.
nextToken
();
}
public
void
getApps
(
Context
context
)
{
SharedPreferences
prefs
=
PreferenceManager
.
getDefaultSharedPreferences
(
context
);
Arrays
.
sort
(
tordApps
);
String
tordAppString
=
prefs
.
getString
(
PREFS_KEY_PROXYED
,
""
);
String
[]
tordApps
;
// else load the apps up
PackageManager
pMgr
=
context
.
getPackageManager
()
;
StringTokenizer
st
=
new
StringTokenizer
(
tordAppString
,
"|"
);
tordApps
=
new
String
[
st
.
countTokens
()];
int
tordIdx
=
0
;
while
(
st
.
hasMoreTokens
())
{
tordApps
[
tordIdx
++]
=
st
.
nextToken
();
}
List
<
ApplicationInfo
>
lAppInfo
=
pMgr
.
getInstalledApplications
(
0
);
Arrays
.
sort
(
tordApps
);
Iterator
<
ApplicationInfo
>
itAppInfo
=
lAppInfo
.
iterator
(
);
Vector
<
ProxyedApp
>
vectorApps
=
new
Vector
<
ProxyedApp
>();
Vector
<
ProxyedApp
>
vectorApps
=
new
Vector
<
ProxyedApp
>();
// else load the apps up
PackageManager
pMgr
=
context
.
getPackageManager
();
ApplicationInfo
aInfo
=
null
;
List
<
ApplicationInfo
>
lAppInfo
=
pMgr
.
getInstalledApplications
(
0
);
while
(
itAppInfo
.
hasNext
())
{
aInfo
=
itAppInfo
.
next
();
Iterator
<
ApplicationInfo
>
itAppInfo
=
lAppInfo
.
iterator
();
// ignore system apps
if
(
aInfo
.
uid
<
10000
)
continue
;
ApplicationInfo
aInfo
=
null
;
ProxyedApp
app
=
new
ProxyedApp
()
;
while
(
itAppInfo
.
hasNext
())
{
aInfo
=
itAppInfo
.
next
();
app
.
setUid
(
aInfo
.
uid
);
// ignore system apps
if
(
aInfo
.
uid
<
10000
)
continue
;
app
.
setUsername
(
pMgr
.
getNameForUid
(
app
.
getUid
()));
if
(
aInfo
.
processName
==
null
)
continue
;
if
(
pMgr
.
getApplicationLabel
(
aInfo
)
==
null
||
pMgr
.
getApplicationLabel
(
aInfo
).
toString
().
equals
(
""
))
continue
;
if
(
pMgr
.
getApplicationIcon
(
aInfo
)
==
null
)
continue
;
// check if this application is allowed
if
(
aInfo
.
packageName
!=
null
&&
aInfo
.
packageName
.
equals
(
"com.github.shadowsocks"
))
{
app
.
setProxyed
(
true
);
}
else
if
(
Arrays
.
binarySearch
(
tordApps
,
app
.
getUsername
())
>=
0
)
{
app
.
setProxyed
(
true
);
}
else
{
app
.
setProxyed
(
false
);
}
ProxyedApp
tApp
=
new
ProxyedApp
();
vectorApps
.
add
(
app
);
}
tApp
.
setEnabled
(
aInfo
.
enabled
);
tApp
.
setUid
(
aInfo
.
uid
);
tApp
.
setUsername
(
pMgr
.
getNameForUid
(
tApp
.
getUid
()));
tApp
.
setProcname
(
aInfo
.
processName
);
tApp
.
setName
(
pMgr
.
getApplicationLabel
(
aInfo
).
toString
());
ProxyedApp
[]
apps
=
new
ProxyedApp
[
vectorApps
.
size
()];
vectorApps
.
toArray
(
apps
);
return
apps
;
}
// check if this application is allowed
if
(
Arrays
.
binarySearch
(
tordApps
,
tApp
.
getUsername
())
>=
0
)
{
tApp
.
setProxyed
(
true
);
}
else
{
tApp
.
setProxyed
(
false
);
}
public
void
getApps
(
Context
context
)
{
vectorApps
.
add
(
tApp
);
}
SharedPreferences
prefs
=
PreferenceManager
.
getDefaultSharedPreferences
(
context
);
apps
=
new
ProxyedApp
[
vectorApps
.
size
()]
;
vectorApps
.
toArray
(
apps
)
;
String
tordAppString
=
prefs
.
getString
(
PREFS_KEY_PROXYED
,
""
)
;
String
[]
tordApps
;
StringTokenizer
st
=
new
StringTokenizer
(
tordAppString
,
"|"
);
tordApps
=
new
String
[
st
.
countTokens
()];
int
tordIdx
=
0
;
while
(
st
.
hasMoreTokens
())
{
tordApps
[
tordIdx
++]
=
st
.
nextToken
();
}
private
void
loadApps
()
{
getApps
(
this
);
Arrays
.
sort
(
tordApps
);
Arrays
.
sort
(
apps
,
new
Comparator
<
ProxyedApp
>()
{
@Override
public
int
compare
(
ProxyedApp
o1
,
ProxyedApp
o2
)
{
if
(
o1
==
null
||
o2
==
null
||
o1
.
getName
()
==
null
||
o2
.
getName
()
==
null
)
return
1
;
if
(
o1
.
isProxyed
()
==
o2
.
isProxyed
())
return
o1
.
getName
().
compareTo
(
o2
.
getName
());
if
(
o1
.
isProxyed
())
return
-
1
;
return
1
;
}
});
Vector
<
ProxyedApp
>
vectorApps
=
new
Vector
<
ProxyedApp
>();
final
LayoutInflater
inflater
=
getLayoutInflater
();
// else load the apps up
PackageManager
pMgr
=
context
.
getPackageManager
();
adapter
=
new
ArrayAdapter
<
ProxyedApp
>(
this
,
R
.
layout
.
layout_apps_item
,
R
.
id
.
itemtext
,
apps
)
{
@Override
public
View
getView
(
int
position
,
View
convertView
,
ViewGroup
parent
)
{
ListEntry
entry
;
if
(
convertView
==
null
)
{
// Inflate a new view
convertView
=
inflater
.
inflate
(
R
.
layout
.
layout_apps_item
,
parent
,
false
);
entry
=
new
ListEntry
();
entry
.
icon
=
(
ImageView
)
convertView
.
findViewById
(
R
.
id
.
itemicon
);
entry
.
box
=
(
CheckBox
)
convertView
.
findViewById
(
R
.
id
.
itemcheck
);
entry
.
text
=
(
TextView
)
convertView
.
findViewById
(
R
.
id
.
itemtext
);
entry
.
text
.
setOnClickListener
(
mAppManager
);
entry
.
text
.
setOnClickListener
(
mAppManager
);
convertView
.
setTag
(
entry
);
entry
.
box
.
setOnCheckedChangeListener
(
mAppManager
);
}
else
{
// Convert an existing view
entry
=
(
ListEntry
)
convertView
.
getTag
();
}
List
<
ApplicationInfo
>
lAppInfo
=
pMgr
.
getInstalledApplications
(
0
);
final
ProxyedApp
app
=
apps
[
position
]
;
Iterator
<
ApplicationInfo
>
itAppInfo
=
lAppInfo
.
iterator
()
;
entry
.
icon
.
setTag
(
app
.
getUid
())
;
ApplicationInfo
aInfo
=
null
;
dm
.
DisplayImage
(
app
.
getUid
(),
(
Activity
)
convertView
.
getContext
(),
entry
.
icon
);
while
(
itAppInfo
.
hasNext
())
{
aInfo
=
itAppInfo
.
next
(
);
entry
.
text
.
setText
(
app
.
getName
());
// ignore system apps
if
(
aInfo
.
uid
<
10000
)
continue
;
final
CheckBox
box
=
entry
.
box
;
box
.
setTag
(
app
);
box
.
setChecked
(
app
.
isProxyed
());
if
(
aInfo
.
processName
==
null
)
continue
;
if
(
pMgr
.
getApplicationLabel
(
aInfo
)
==
null
||
pMgr
.
getApplicationLabel
(
aInfo
)
.
toString
()
.
equals
(
""
))
{
continue
;
}
if
(
pMgr
.
getApplicationIcon
(
aInfo
)
==
null
)
continue
;
entry
.
text
.
setTag
(
box
);
ProxyedApp
tApp
=
new
ProxyedApp
(
);
return
convertView
;
}
};
tApp
.
setEnabled
(
aInfo
.
enabled
);
tApp
.
setUid
(
aInfo
.
uid
);
tApp
.
setUsername
(
pMgr
.
getNameForUid
(
tApp
.
getUid
()));
tApp
.
setProcname
(
aInfo
.
processName
);
tApp
.
setName
(
pMgr
.
getApplicationLabel
(
aInfo
).
toString
());
appsLoaded
=
true
;
// check if this application is allowed
if
(
Arrays
.
binarySearch
(
tordApps
,
tApp
.
getUsername
())
>=
0
)
{
tApp
.
setProxyed
(
true
);
}
else
{
tApp
.
setProxyed
(
false
);
}
vectorApps
.
add
(
tApp
);
}
/**
* Called an application is check/unchecked
*/
@Override
public
void
onCheckedChanged
(
CompoundButton
buttonView
,
boolean
isChecked
)
{
final
ProxyedApp
app
=
(
ProxyedApp
)
buttonView
.
getTag
();
if
(
app
!=
null
)
{
app
.
setProxyed
(
isChecked
);
apps
=
new
ProxyedApp
[
vectorApps
.
size
()];
vectorApps
.
toArray
(
apps
);
}
private
void
loadApps
()
{
getApps
(
this
);
Arrays
.
sort
(
apps
,
new
Comparator
<
ProxyedApp
>()
{
@Override
public
int
compare
(
ProxyedApp
o1
,
ProxyedApp
o2
)
{
if
(
o1
==
null
||
o2
==
null
||
o1
.
getName
()
==
null
||
o2
.
getName
()
==
null
)
return
1
;
if
(
o1
.
isProxyed
()
==
o2
.
isProxyed
())
return
o1
.
getName
().
compareTo
(
o2
.
getName
());
if
(
o1
.
isProxyed
())
return
-
1
;
return
1
;
}
});
final
LayoutInflater
inflater
=
getLayoutInflater
();
adapter
=
new
ArrayAdapter
<
ProxyedApp
>(
this
,
R
.
layout
.
layout_apps_item
,
R
.
id
.
itemtext
,
apps
)
{
@Override
public
View
getView
(
int
position
,
View
convertView
,
ViewGroup
parent
)
{
ListEntry
entry
;
if
(
convertView
==
null
)
{
// Inflate a new view
convertView
=
inflater
.
inflate
(
R
.
layout
.
layout_apps_item
,
parent
,
false
);
entry
=
new
ListEntry
();
entry
.
icon
=
(
ImageView
)
convertView
.
findViewById
(
R
.
id
.
itemicon
);
entry
.
box
=
(
CheckBox
)
convertView
.
findViewById
(
R
.
id
.
itemcheck
);
entry
.
text
=
(
TextView
)
convertView
.
findViewById
(
R
.
id
.
itemtext
);
entry
.
text
.
setOnClickListener
(
mAppManager
);
entry
.
text
.
setOnClickListener
(
mAppManager
);
convertView
.
setTag
(
entry
);
entry
.
box
.
setOnCheckedChangeListener
(
mAppManager
);
}
else
{
// Convert an existing view
entry
=
(
ListEntry
)
convertView
.
getTag
();
}
saveAppSettings
(
this
)
;
final
ProxyedApp
app
=
apps
[
position
]
;
}
entry
.
icon
.
setTag
(
app
.
getUid
());
@Override
public
void
onClick
(
View
v
)
{
dm
.
DisplayImage
(
app
.
getUid
(),
(
Activity
)
convertView
.
getContext
(),
entry
.
icon
);
CheckBox
cbox
=
(
CheckBox
)
v
.
getTag
(
);
entry
.
text
.
setText
(
app
.
getName
()
);
final
ProxyedApp
app
=
(
ProxyedApp
)
cbox
.
getTag
();
if
(
app
!=
null
)
{
app
.
setProxyed
(!
app
.
isProxyed
());
cbox
.
setChecked
(
app
.
isProxyed
());
}
final
CheckBox
box
=
entry
.
box
;
box
.
setTag
(
app
);
box
.
setChecked
(
app
.
isProxyed
());
saveAppSettings
(
this
);
entry
.
text
.
setTag
(
box
);
}
return
convertView
;
}
};
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
appsLoaded
=
true
;
}
this
.
setContentView
(
R
.
layout
.
layout_apps
);
/** Called an application is check/unchecked */
@Override
public
void
onCheckedChanged
(
CompoundButton
buttonView
,
boolean
isChecked
)
{
final
ProxyedApp
app
=
(
ProxyedApp
)
buttonView
.
getTag
();
if
(
app
!=
null
)
{
app
.
setProxyed
(
isChecked
);
}
this
.
dm
=
ImageLoaderFactory
.
getImageLoader
(
this
);
saveAppSettings
(
this
);
}
this
.
overlay
=
(
TextView
)
View
.
inflate
(
this
,
R
.
layout
.
overlay
,
null
);
getWindowManager
()
.
addView
(
overlay
,
new
WindowManager
.
LayoutParams
(
LayoutParams
.
WRAP_CONTENT
,
LayoutParams
.
WRAP_CONTENT
,
WindowManager
.
LayoutParams
.
TYPE_APPLICATION
,
WindowManager
.
LayoutParams
.
FLAG_NOT_FOCUSABLE
|
WindowManager
.
LayoutParams
.
FLAG_NOT_TOUCHABLE
,
PixelFormat
.
TRANSLUCENT
));
@Override
public
void
onClick
(
View
v
)
{
mAppManager
=
this
;
CheckBox
cbox
=
(
CheckBox
)
v
.
getTag
()
;
final
ProxyedApp
app
=
(
ProxyedApp
)
cbox
.
getTag
();
if
(
app
!=
null
)
{
app
.
setProxyed
(!
app
.
isProxyed
());
cbox
.
setChecked
(
app
.
isProxyed
());
}
@Override
protected
void
onResume
()
{
super
.
onResume
();
saveAppSettings
(
this
);
}
new
Thread
()
{
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
@Override
public
void
run
()
{
handler
.
sendEmptyMessage
(
MSG_LOAD_START
);
this
.
setContentView
(
R
.
layout
.
layout_apps
);
listApps
=
(
ListView
)
findViewById
(
R
.
id
.
applistview
);
this
.
dm
=
ImageLoaderFactory
.
getImageLoader
(
this
);
if
(!
appsLoaded
)
loadApps
();
handler
.
sendEmptyMessage
(
MSG_LOAD_FINISH
);
}
}.
start
();
this
.
overlay
=
(
TextView
)
View
.
inflate
(
this
,
R
.
layout
.
overlay
,
null
);
getWindowManager
().
addView
(
overlay
,
new
WindowManager
.
LayoutParams
(
LayoutParams
.
WRAP_CONTENT
,
LayoutParams
.
WRAP_CONTENT
,
WindowManager
.
LayoutParams
.
TYPE_APPLICATION
,
WindowManager
.
LayoutParams
.
FLAG_NOT_FOCUSABLE
|
WindowManager
.
LayoutParams
.
FLAG_NOT_TOUCHABLE
,
PixelFormat
.
TRANSLUCENT
));
}
mAppManager
=
this
;
}
/*
* (non-Javadoc)
*
* @see android.app.Activity#onStop()
*/
@Override
protected
void
onStop
()
{
super
.
onStop
();
@Override
protected
void
onResume
()
{
super
.
onResume
();
// Log.d(getClass().getName(),"Exiting Preferences");
}
new
Thread
()
{
public
void
saveAppSettings
(
Context
context
)
{
if
(
apps
==
null
)
return
;
@Override
public
void
run
()
{
handler
.
sendEmptyMessage
(
MSG_LOAD_START
)
;
SharedPreferences
prefs
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
listApps
=
(
ListView
)
findViewById
(
R
.
id
.
applistview
);
// final SharedPreferences prefs =
// context.getSharedPreferences(PREFS_KEY, 0);
if
(!
appsLoaded
)
loadApps
();
handler
.
sendEmptyMessage
(
MSG_LOAD_FINISH
);
}
}.
start
();
}
StringBuilder
tordApps
=
new
StringBuilder
();
/*
* (non-Javadoc)
*
* @see android.app.Activity#onStop()
*/
@Override
protected
void
onStop
()
{
super
.
onStop
();
for
(
int
i
=
0
;
i
<
apps
.
length
;
i
++)
{
if
(
apps
[
i
].
isProxyed
())
{
tordApps
.
append
(
apps
[
i
].
getUsername
());
tordApps
.
append
(
"|"
);
}
}
// Log.d(getClass().getName(),"Exiting Preferences");
}
Editor
edit
=
prefs
.
edit
();
edit
.
putString
(
PREFS_KEY_PROXYED
,
tordApps
.
toString
());
edit
.
commit
();
public
void
saveAppSettings
(
Context
context
)
{
if
(
apps
==
null
)
return
;
}
SharedPreferences
prefs
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
private
static
class
ListEntry
{
private
CheckBox
box
;
private
TextView
text
;
private
ImageView
icon
;
// final SharedPreferences prefs =
// context.getSharedPreferences(PREFS_KEY, 0);
StringBuilder
tordApps
=
new
StringBuilder
();
for
(
int
i
=
0
;
i
<
apps
.
length
;
i
++)
{
if
(
apps
[
i
].
isProxyed
())
{
tordApps
.
append
(
apps
[
i
].
getUsername
());
tordApps
.
append
(
"|"
);
}
}
Editor
edit
=
prefs
.
edit
();
edit
.
putString
(
PREFS_KEY_PROXYED
,
tordApps
.
toString
());
edit
.
commit
();
}
private
static
class
ListEntry
{
private
CheckBox
box
;
private
TextView
text
;
private
ImageView
icon
;
}
}
src/com/github/shadowsocks/Exec.java
View file @
3d664377
...
...
@@ -27,46 +27,41 @@ import java.io.FileDescriptor;
*/
public
class
Exec
{
static
{
java
.
lang
.
System
.
loadLibrary
(
"exec"
);
}
static
{
java
.
lang
.
System
.
loadLibrary
(
"exec"
);
}
/**
* Close a given file descriptor.
*/
public
static
native
void
close
(
FileDescriptor
fd
);
/** Close a given file descriptor. */
public
static
native
void
close
(
FileDescriptor
fd
);
/**
* Create a subprocess. Differs from java.lang.ProcessBuilder in that a pty
* is used to communicate with the subprocess.
* <p/>
* Callers are responsible for calling Exec.close() on the returned file
* descriptor.
*
* @param rdt Whether redirect stdout and stderr
* @param cmd The command to execute.
* @param args An array of arguments to the command.
* @param envVars An array of strings of the form "VAR=value" to be added to the
* environment of the process.
* @param scripts The scripts to execute.
* @param processId A one-element array to which the process ID of the started
* process will be written.
* @return File descriptor
*/
public
static
native
FileDescriptor
createSubprocess
(
int
rdt
,
String
cmd
,
String
[]
args
,
String
[]
envVars
,
String
scripts
,
int
[]
processId
);
/**
* Create a subprocess. Differs from java.lang.ProcessBuilder in that a pty
* is used to communicate with the subprocess.
* <p/>
* Callers are responsible for calling Exec.close() on the returned file
* descriptor.
*
* @param rdt Whether redirect stdout and stderr
* @param cmd The command to execute.
* @param args An array of arguments to the command.
* @param envVars An array of strings of the form "VAR=value" to be added to the
* environment of the process.
* @param scripts The scripts to execute.
* @param processId A one-element array to which the process ID of the started
* process will be written.
* @return File descriptor
*/
public
static
native
FileDescriptor
createSubprocess
(
int
rdt
,
String
cmd
,
String
[]
args
,
String
[]
envVars
,
String
scripts
,
int
[]
processId
);
/**
* Send SIGHUP to a process group.
*/
public
static
native
void
hangupProcessGroup
(
int
processId
);
/** Send SIGHUP to a process group. */
public
static
native
void
hangupProcessGroup
(
int
processId
);
/**
* Causes the calling thread to wait for the process associated with the
* receiver to finish executing.
*
* @return The exit value of the Process being waited on
*/
public
static
native
int
waitFor
(
int
processId
);
/**
* Causes the calling thread to wait for the process associated with the
* receiver to finish executing.
*
* @return The exit value of the Process being waited on
*/
public
static
native
int
waitFor
(
int
processId
);
}
src/com/github/shadowsocks/ImageLoader.java
View file @
3d664377
...
...
@@ -45,7 +45,6 @@ import android.graphics.Bitmap;
import
android.graphics.BitmapFactory
;
import
android.graphics.drawable.BitmapDrawable
;
import
android.widget.ImageView
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.FileNotFoundException
;
...
...
@@ -54,195 +53,190 @@ import java.util.Stack;
public
class
ImageLoader
{
final
int
stub_id
=
R
.
drawable
.
sym_def_app_icon
;
PhotosQueue
photosQueue
=
new
PhotosQueue
();
PhotosLoader
photoLoaderThread
=
new
PhotosLoader
();
// the simplest in-memory cache implementation. This should be replaced with
// something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
private
HashMap
<
Integer
,
Bitmap
>
cache
=
new
HashMap
<
Integer
,
Bitmap
>();
private
File
cacheDir
;
private
Context
context
;
public
ImageLoader
(
Context
c
)
{
// Make the background thead low priority. This way it will not affect
// the UI performance
photoLoaderThread
.
setPriority
(
Thread
.
NORM_PRIORITY
-
1
);
context
=
c
;
// Find the dir to save cached images
cacheDir
=
context
.
getCacheDir
();
final
int
stub_id
=
R
.
drawable
.
sym_def_app_icon
;
PhotosQueue
photosQueue
=
new
PhotosQueue
();
PhotosLoader
photoLoaderThread
=
new
PhotosLoader
();
// the simplest in-memory cache implementation. This should be replaced with
// something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
private
HashMap
<
Integer
,
Bitmap
>
cache
=
new
HashMap
<
Integer
,
Bitmap
>();
private
File
cacheDir
;
private
Context
context
;
public
ImageLoader
(
Context
c
)
{
// Make the background thead low priority. This way it will not affect
// the UI performance
photoLoaderThread
.
setPriority
(
Thread
.
NORM_PRIORITY
-
1
);
context
=
c
;
// Find the dir to save cached images
cacheDir
=
context
.
getCacheDir
();
}
public
void
clearCache
()
{
// clear memory cache
cache
.
clear
();
// clear SD cache
File
[]
files
=
cacheDir
.
listFiles
();
for
(
File
f
:
files
)
f
.
delete
();
}
// decodes image and scales it to reduce memory consumption
private
Bitmap
decodeFile
(
File
f
)
{
try
{
// decode image size
BitmapFactory
.
Options
o
=
new
BitmapFactory
.
Options
();
o
.
inJustDecodeBounds
=
true
;
BitmapFactory
.
decodeStream
(
new
FileInputStream
(
f
),
null
,
o
);
// Find the correct scale value. It should be the power of 2.
final
int
REQUIRED_SIZE
=
70
;
int
width_tmp
=
o
.
outWidth
,
height_tmp
=
o
.
outHeight
;
int
scale
=
1
;
while
(
true
)
{
if
(
width_tmp
/
2
<
REQUIRED_SIZE
||
height_tmp
/
2
<
REQUIRED_SIZE
)
break
;
width_tmp
/=
2
;
height_tmp
/=
2
;
scale
*=
2
;
}
// decode with inSampleSize
BitmapFactory
.
Options
o2
=
new
BitmapFactory
.
Options
();
o2
.
inSampleSize
=
scale
;
return
BitmapFactory
.
decodeStream
(
new
FileInputStream
(
f
),
null
,
o2
);
}
catch
(
FileNotFoundException
ignored
)
{
}
public
void
clearCache
()
{
// clear memory cache
cache
.
clear
();
// clear SD cache
File
[]
files
=
cacheDir
.
listFiles
();
for
(
File
f
:
files
)
f
.
delete
(
);
return
null
;
}
public
void
DisplayImage
(
int
uid
,
Activity
activity
,
ImageView
imageView
)
{
if
(
cache
.
containsKey
(
uid
))
{
imageView
.
setImageBitmap
(
cache
.
get
(
uid
));
}
else
{
queuePhoto
(
uid
,
activity
,
imageView
);
imageView
.
setImageResource
(
stub_id
);
}
// decodes image and scales it to reduce memory consumption
private
Bitmap
decodeFile
(
File
f
)
{
try
{
// decode image size
BitmapFactory
.
Options
o
=
new
BitmapFactory
.
Options
();
o
.
inJustDecodeBounds
=
true
;
BitmapFactory
.
decodeStream
(
new
FileInputStream
(
f
),
null
,
o
);
// Find the correct scale value. It should be the power of 2.
final
int
REQUIRED_SIZE
=
70
;
int
width_tmp
=
o
.
outWidth
,
height_tmp
=
o
.
outHeight
;
int
scale
=
1
;
while
(
true
)
{
if
(
width_tmp
/
2
<
REQUIRED_SIZE
||
height_tmp
/
2
<
REQUIRED_SIZE
)
break
;
width_tmp
/=
2
;
height_tmp
/=
2
;
scale
*=
2
;
}
// decode with inSampleSize
BitmapFactory
.
Options
o2
=
new
BitmapFactory
.
Options
();
o2
.
inSampleSize
=
scale
;
return
BitmapFactory
.
decodeStream
(
new
FileInputStream
(
f
),
null
,
o2
);
}
catch
(
FileNotFoundException
ignored
)
{
}
return
null
;
}
private
Bitmap
getBitmap
(
int
uid
)
{
// I identify images by hashcode. Not a perfect solution, good for the
// demo.
String
filename
=
String
.
valueOf
(
uid
);
File
f
=
new
File
(
cacheDir
,
filename
);
// from SD cache
Bitmap
b
=
decodeFile
(
f
);
if
(
b
!=
null
)
return
b
;
// from web
try
{
BitmapDrawable
icon
=
(
BitmapDrawable
)
Utils
.
getAppIcon
(
context
,
uid
);
return
icon
.
getBitmap
();
}
catch
(
Exception
ex
)
{
return
null
;
}
public
void
DisplayImage
(
int
uid
,
Activity
activity
,
ImageView
imageView
)
{
if
(
cache
.
containsKey
(
uid
))
imageView
.
setImageBitmap
(
cache
.
get
(
uid
));
else
{
queuePhoto
(
uid
,
activity
,
imageView
);
imageView
.
setImageResource
(
stub_id
);
}
}
private
void
queuePhoto
(
int
uid
,
Activity
activity
,
ImageView
imageView
)
{
// This ImageView may be used for other images before. So there may be
// some old tasks in the queue. We need to discard them.
photosQueue
.
Clean
(
imageView
);
PhotoToLoad
p
=
new
PhotoToLoad
(
uid
,
imageView
);
synchronized
(
photosQueue
.
photosToLoad
)
{
photosQueue
.
photosToLoad
.
push
(
p
);
photosQueue
.
photosToLoad
.
notifyAll
();
}
private
Bitmap
getBitmap
(
int
uid
)
{
// I identify images by hashcode. Not a perfect solution, good for the
// demo.
String
filename
=
String
.
valueOf
(
uid
);
File
f
=
new
File
(
cacheDir
,
filename
);
// from SD cache
Bitmap
b
=
decodeFile
(
f
);
if
(
b
!=
null
)
return
b
;
// from web
try
{
BitmapDrawable
icon
=
(
BitmapDrawable
)
Utils
.
getAppIcon
(
context
,
uid
);
return
icon
.
getBitmap
();
}
catch
(
Exception
ex
)
{
return
null
;
}
}
// start thread if it's not started yet
if
(
photoLoaderThread
.
getState
()
==
Thread
.
State
.
NEW
)
photoLoaderThread
.
start
();
}
private
void
queuePhoto
(
int
uid
,
Activity
activity
,
ImageView
imageView
)
{
// This ImageView may be used for other images before. So there may be
// some old tasks in the queue. We need to discard them.
photosQueue
.
Clean
(
imageView
);
PhotoToLoad
p
=
new
PhotoToLoad
(
uid
,
imageView
);
synchronized
(
photosQueue
.
photosToLoad
)
{
photosQueue
.
photosToLoad
.
push
(
p
);
photosQueue
.
photosToLoad
.
notifyAll
();
}
public
void
stopThread
()
{
photoLoaderThread
.
interrupt
();
}
// start thread if it's not started yet
if
(
photoLoaderThread
.
getState
()
==
Thread
.
State
.
NEW
)
photoLoaderThread
.
start
()
;
}
// Used to display bitmap in the UI thread
class
BitmapDisplayer
implements
Runnable
{
Bitmap
bitmap
;
ImageView
imageView
;
public
void
stopThread
()
{
photoLoaderThread
.
interrupt
();
public
BitmapDisplayer
(
Bitmap
b
,
ImageView
i
)
{
bitmap
=
b
;
imageView
=
i
;
}
// Used to display bitmap in the UI thread
class
BitmapDisplayer
implements
Runnable
{
Bitmap
bitmap
;
ImageView
imageView
;
public
BitmapDisplayer
(
Bitmap
b
,
ImageView
i
)
{
bitmap
=
b
;
imageView
=
i
;
}
@Override
public
void
run
()
{
if
(
bitmap
!=
null
)
imageView
.
setImageBitmap
(
bitmap
);
else
imageView
.
setImageResource
(
stub_id
);
}
@Override
public
void
run
()
{
if
(
bitmap
!=
null
)
{
imageView
.
setImageBitmap
(
bitmap
);
}
else
{
imageView
.
setImageResource
(
stub_id
);
}
}
class
PhotosLoader
extends
Thread
{
@Override
public
void
run
()
{
try
{
while
(
true
)
{
// thread waits until there are any images to load in the
// queue
if
(
photosQueue
.
photosToLoad
.
size
()
==
0
)
synchronized
(
photosQueue
.
photosToLoad
)
{
photosQueue
.
photosToLoad
.
wait
();
}
if
(
photosQueue
.
photosToLoad
.
size
()
!=
0
)
{
PhotoToLoad
photoToLoad
;
synchronized
(
photosQueue
.
photosToLoad
)
{
photoToLoad
=
photosQueue
.
photosToLoad
.
pop
();
}
Bitmap
bmp
=
getBitmap
(
photoToLoad
.
uid
);
cache
.
put
(
photoToLoad
.
uid
,
bmp
);
Object
tag
=
photoToLoad
.
imageView
.
getTag
();
if
(
tag
!=
null
&&
((
Integer
)
tag
)
==
photoToLoad
.
uid
)
{
BitmapDisplayer
bd
=
new
BitmapDisplayer
(
bmp
,
photoToLoad
.
imageView
);
Activity
a
=
(
Activity
)
photoToLoad
.
imageView
.
getContext
();
a
.
runOnUiThread
(
bd
);
}
}
if
(
Thread
.
interrupted
())
break
;
}
}
catch
(
InterruptedException
e
)
{
// allow thread to exit
}
class
PhotosLoader
extends
Thread
{
@Override
public
void
run
()
{
try
{
while
(
true
)
{
// thread waits until there are any images to load in the
// queue
if
(
photosQueue
.
photosToLoad
.
size
()
==
0
)
{
synchronized
(
photosQueue
.
photosToLoad
)
{
photosQueue
.
photosToLoad
.
wait
();
}
}
if
(
photosQueue
.
photosToLoad
.
size
()
!=
0
)
{
PhotoToLoad
photoToLoad
;
synchronized
(
photosQueue
.
photosToLoad
)
{
photoToLoad
=
photosQueue
.
photosToLoad
.
pop
();
}
Bitmap
bmp
=
getBitmap
(
photoToLoad
.
uid
);
cache
.
put
(
photoToLoad
.
uid
,
bmp
);
Object
tag
=
photoToLoad
.
imageView
.
getTag
();
if
(
tag
!=
null
&&
((
Integer
)
tag
)
==
photoToLoad
.
uid
)
{
BitmapDisplayer
bd
=
new
BitmapDisplayer
(
bmp
,
photoToLoad
.
imageView
);
Activity
a
=
(
Activity
)
photoToLoad
.
imageView
.
getContext
();
a
.
runOnUiThread
(
bd
);
}
}
if
(
Thread
.
interrupted
())
break
;
}
}
catch
(
InterruptedException
e
)
{
// allow thread to exit
}
}
// stores list of photos to download
class
PhotosQueue
{
private
final
Stack
<
PhotoToLoad
>
photosToLoad
=
new
Stack
<
PhotoToLoad
>();
// removes all instances of this ImageView
public
void
Clean
(
ImageView
image
)
{
synchronized
(
photosToLoad
)
{
for
(
int
j
=
0
;
j
<
photosToLoad
.
size
();
)
{
if
(
photosToLoad
.
get
(
j
).
imageView
==
image
)
photosToLoad
.
remove
(
j
);
else
++
j
;
}
}
}
// stores list of photos to download
class
PhotosQueue
{
private
final
Stack
<
PhotoToLoad
>
photosToLoad
=
new
Stack
<
PhotoToLoad
>();
// removes all instances of this ImageView
public
void
Clean
(
ImageView
image
)
{
synchronized
(
photosToLoad
)
{
for
(
int
j
=
0
;
j
<
photosToLoad
.
size
();
)
{
if
(
photosToLoad
.
get
(
j
).
imageView
==
image
)
{
photosToLoad
.
remove
(
j
);
}
else
{
++
j
;
}
}
}
}
}
// Task for the queue
private
class
PhotoToLoad
{
public
int
uid
;
public
ImageView
imageView
;
// Task for the queue
private
class
PhotoToLoad
{
public
int
uid
;
public
ImageView
imageView
;
public
PhotoToLoad
(
int
u
,
ImageView
i
)
{
uid
=
u
;
imageView
=
i
;
}
public
PhotoToLoad
(
int
u
,
ImageView
i
)
{
uid
=
u
;
imageView
=
i
;
}
}
}
src/com/github/shadowsocks/ImageLoaderFactory.java
View file @
3d664377
...
...
@@ -42,12 +42,12 @@ import android.content.Context;
public
class
ImageLoaderFactory
{
private
static
ImageLoader
il
=
null
;
private
static
ImageLoader
il
=
null
;
public
static
ImageLoader
getImageLoader
(
Context
context
)
{
if
(
il
==
null
)
{
il
=
new
ImageLoader
(
context
);
}
return
il
;
public
static
ImageLoader
getImageLoader
(
Context
context
)
{
if
(
il
==
null
)
{
il
=
new
ImageLoader
(
context
);
}
return
il
;
}
}
src/com/github/shadowsocks/ProxyedApp.java
View file @
3d664377
...
...
@@ -40,94 +40,70 @@ package com.github.shadowsocks;
public
class
ProxyedApp
{
private
boolean
enabled
;
private
int
uid
;
private
String
username
;
private
String
procname
;
private
String
name
;
private
boolean
proxyed
=
false
;
private
boolean
enabled
;
private
int
uid
;
private
String
username
;
private
String
procname
;
private
String
name
;
private
boolean
proxyed
=
false
;
/**
* @return the name
*/
public
String
getName
()
{
return
name
;
}
/** @return the name */
public
String
getName
()
{
return
name
;
}
/**
* @param name the name to set
*/
public
void
setName
(
String
name
)
{
this
.
name
=
name
;
}
/** @param name the name to set */
public
void
setName
(
String
name
)
{
this
.
name
=
name
;
}
/**
* @return the procname
*/
public
String
getProcname
()
{
return
procname
;
}
/** @return the procname */
public
String
getProcname
()
{
return
procname
;
}
/**
* @param procname the procname to set
*/
public
void
setProcname
(
String
procname
)
{
this
.
procname
=
procname
;
}
/** @param procname the procname to set */
public
void
setProcname
(
String
procname
)
{
this
.
procname
=
procname
;
}
/**
* @return the uid
*/
public
int
getUid
()
{
return
uid
;
}
/** @return the uid */
public
int
getUid
()
{
return
uid
;
}
/**
* @param uid the uid to set
*/
public
void
setUid
(
int
uid
)
{
this
.
uid
=
uid
;
}
/** @param uid the uid to set */
public
void
setUid
(
int
uid
)
{
this
.
uid
=
uid
;
}
/**
* @return the username
*/
public
String
getUsername
()
{
return
username
;
}
/** @return the username */
public
String
getUsername
()
{
return
username
;
}
/**
* @param username the username to set
*/
public
void
setUsername
(
String
username
)
{
this
.
username
=
username
;
}
/** @param username the username to set */
public
void
setUsername
(
String
username
)
{
this
.
username
=
username
;
}
/**
* @return the enabled
*/
public
boolean
isEnabled
()
{
return
enabled
;
}
/** @return the enabled */
public
boolean
isEnabled
()
{
return
enabled
;
}
/**
* @param enabled the enabled to set
*/
public
void
setEnabled
(
boolean
enabled
)
{
this
.
enabled
=
enabled
;
}
/** @param enabled the enabled to set */
public
void
setEnabled
(
boolean
enabled
)
{
this
.
enabled
=
enabled
;
}
/**
* @return the proxyed
*/
public
boolean
isProxyed
()
{
return
proxyed
;
}
/** @return the proxyed */
public
boolean
isProxyed
()
{
return
proxyed
;
}
/**
* @param proxyed the proxyed to set
*/
public
void
setProxyed
(
boolean
proxyed
)
{
this
.
proxyed
=
proxyed
;
}
/** @param proxyed the proxyed to set */
public
void
setProxyed
(
boolean
proxyed
)
{
this
.
proxyed
=
proxyed
;
}
}
src/com/github/shadowsocks/Shadowsocks.java
View file @
3d664377
...
...
@@ -52,7 +52,8 @@ import android.graphics.Typeface;
import
android.os.Bundle
;
import
android.os.Handler
;
import
android.os.Message
;
import
android.preference.*
;
import
android.preference.Preference
;
import
android.preference.PreferenceManager
;
import
android.util.Log
;
import
android.view.KeyEvent
;
import
android.widget.CompoundButton
;
...
...
@@ -61,588 +62,554 @@ import android.widget.TextView;
import
com.actionbarsherlock.view.Menu
;
import
com.actionbarsherlock.view.MenuItem
;
import
com.google.analytics.tracking.android.EasyTracker
;
import
net.saik0.android.unifiedpreference.UnifiedPreferenceFragment
;
import
net.saik0.android.unifiedpreference.UnifiedSherlockPreferenceActivity
;
import
org.jraf.android.backport.switchwidget.Switch
;
import
de.keyboardsurfer.android.widget.crouton.Crouton
;
import
de.keyboardsurfer.android.widget.crouton.Style
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
java.util.Hashtable
;
import
net.saik0.android.unifiedpreference.UnifiedPreferenceFragment
;
import
net.saik0.android.unifiedpreference.UnifiedSherlockPreferenceActivity
;
import
org.jraf.android.backport.switchwidget.Switch
;
public
class
Shadowsocks
extends
UnifiedSherlockPreferenceActivity
implements
CompoundButton
.
OnCheckedChangeListener
,
OnSharedPreferenceChangeListener
{
public
static
final
String
PREFS_NAME
=
"Shadowsocks"
;
public
static
final
String
[]
PROXY_PREFS
=
{
"proxy"
,
"remotePort"
,
"port"
,
"sitekey"
,
"encMethod"
};
public
static
final
String
[]
FEATRUE_PREFS
=
{
"isHTTPProxy"
,
"isDNSProxy"
,
"isGFWList"
,
"isGlobalProxy"
,
"isBypassApps"
,
"proxyedApps"
,
"isAutoConnect"
};
private
static
final
String
TAG
=
"Shadowsocks"
;
private
static
final
int
MSG_CRASH_RECOVER
=
1
;
private
static
final
int
MSG_INITIAL_FINISH
=
2
;
private
static
ProgressDialog
mProgressDialog
=
null
;
final
Handler
handler
=
new
Handler
()
{
@Override
public
void
handleMessage
(
Message
msg
)
{
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
Shadowsocks
.
this
);
Editor
ed
=
settings
.
edit
();
switch
(
msg
.
what
)
{
case
MSG_CRASH_RECOVER:
Crouton
.
makeText
(
Shadowsocks
.
this
,
R
.
string
.
crash_alert
,
Style
.
ALERT
).
show
();
ed
.
putBoolean
(
"isRunning"
,
false
);
break
;
case
MSG_INITIAL_FINISH:
if
(
mProgressDialog
!=
null
)
{
mProgressDialog
.
dismiss
();
mProgressDialog
=
null
;
}
break
;
}
ed
.
commit
();
super
.
handleMessage
(
msg
);
}
};
private
Switch
switchButton
;
implements
CompoundButton
.
OnCheckedChangeListener
,
OnSharedPreferenceChangeListener
{
public
static
final
String
PREFS_NAME
=
"Shadowsocks"
;
public
static
final
String
[]
PROXY_PREFS
=
{
"proxy"
,
"remotePort"
,
"port"
,
"sitekey"
,
"encMethod"
};
public
static
final
String
[]
FEATRUE_PREFS
=
{
"isHTTPProxy"
,
"isDNSProxy"
,
"isGFWList"
,
"isGlobalProxy"
,
"isBypassApps"
,
"proxyedApps"
,
"isAutoConnect"
};
private
static
final
String
TAG
=
"Shadowsocks"
;
private
static
final
int
MSG_CRASH_RECOVER
=
1
;
private
static
final
int
MSG_INITIAL_FINISH
=
2
;
private
static
ProgressDialog
mProgressDialog
=
null
;
final
Handler
handler
=
new
Handler
()
{
@Override
public
void
handleMessage
(
Message
msg
)
{
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
Shadowsocks
.
this
);
Editor
ed
=
settings
.
edit
();
switch
(
msg
.
what
)
{
case
MSG_CRASH_RECOVER:
Crouton
.
makeText
(
Shadowsocks
.
this
,
R
.
string
.
crash_alert
,
Style
.
ALERT
).
show
();
ed
.
putBoolean
(
"isRunning"
,
false
);
break
;
case
MSG_INITIAL_FINISH:
if
(
mProgressDialog
!=
null
)
{
mProgressDialog
.
dismiss
();
mProgressDialog
=
null
;
}
break
;
}
ed
.
commit
();
super
.
handleMessage
(
msg
);
}
};
private
Switch
switchButton
;
private
void
copyAssets
(
String
path
)
{
private
void
copyAssets
(
String
path
)
{
AssetManager
assetManager
=
getAssets
();
String
[]
files
=
null
;
AssetManager
assetManager
=
getAssets
();
String
[]
files
=
null
;
try
{
files
=
assetManager
.
list
(
path
);
}
catch
(
IOException
e
)
{
Log
.
e
(
TAG
,
e
.
getMessage
());
}
if
(
files
!=
null
)
{
for
(
String
file
:
files
)
{
InputStream
in
=
null
;
OutputStream
out
=
null
;
try
{
files
=
assetManager
.
list
(
path
);
}
catch
(
IOException
e
)
{
Log
.
e
(
TAG
,
e
.
getMessage
());
}
if
(
files
!=
null
)
{
for
(
String
file
:
files
)
{
InputStream
in
=
null
;
OutputStream
out
=
null
;
try
{
if
(
path
.
length
()
>
0
)
{
in
=
assetManager
.
open
(
path
+
"/"
+
file
);
}
else
{
in
=
assetManager
.
open
(
file
);
}
out
=
new
FileOutputStream
(
"/data/data/com.github.shadowsocks/"
+
file
);
copyFile
(
in
,
out
);
in
.
close
();
in
=
null
;
out
.
flush
();
out
.
close
();
out
=
null
;
}
catch
(
Exception
e
)
{
Log
.
e
(
TAG
,
e
.
getMessage
());
}
}
if
(
path
.
length
()
>
0
)
{
in
=
assetManager
.
open
(
path
+
"/"
+
file
);
}
else
{
in
=
assetManager
.
open
(
file
);
}
out
=
new
FileOutputStream
(
"/data/data/com.github.shadowsocks/"
+
file
);
copyFile
(
in
,
out
);
in
.
close
();
in
=
null
;
out
.
flush
();
out
.
close
();
out
=
null
;
}
catch
(
Exception
e
)
{
Log
.
e
(
TAG
,
e
.
getMessage
());
}
}
}
}
private
void
copyFile
(
InputStream
in
,
OutputStream
out
)
throws
IOException
{
byte
[]
buffer
=
new
byte
[
1024
];
int
read
;
while
((
read
=
in
.
read
(
buffer
))
!=
-
1
)
{
out
.
write
(
buffer
,
0
,
read
);
}
private
void
copyFile
(
InputStream
in
,
OutputStream
out
)
throws
IOException
{
byte
[]
buffer
=
new
byte
[
1024
];
int
read
;
while
((
read
=
in
.
read
(
buffer
))
!=
-
1
)
{
out
.
write
(
buffer
,
0
,
read
);
}
private
void
crash_recovery
()
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
Utils
.
getIptables
()).
append
(
" -t nat -F OUTPUT"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/pdnsd.pid`"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/shadowsocks.pid`"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/polipo.pid`"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 pdnsd"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 redsocks"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 shadowsocks"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 polipo"
).
append
(
"\n"
);
Utils
.
runRootCommand
(
sb
.
toString
());
}
private
void
crash_recovery
()
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
Utils
.
getIptables
()).
append
(
" -t nat -F OUTPUT"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/pdnsd.pid`"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/shadowsocks.pid`"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/polipo.pid`"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 pdnsd"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 redsocks"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 shadowsocks"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 polipo"
).
append
(
"\n"
);
Utils
.
runRootCommand
(
sb
.
toString
());
}
private
boolean
isTextEmpty
(
String
s
,
String
msg
)
{
if
(
s
==
null
||
s
.
length
()
<=
0
)
{
showAToast
(
msg
);
return
true
;
}
private
boolean
isTextEmpty
(
String
s
,
String
msg
)
{
if
(
s
==
null
||
s
.
length
()
<=
0
)
{
showAToast
(
msg
);
return
true
;
}
return
false
;
return
false
;
}
@Override
public
void
onCheckedChanged
(
CompoundButton
compoundButton
,
boolean
b
)
{
if
(
compoundButton
==
switchButton
)
{
if
(!
serviceStart
())
{
switchButton
.
setChecked
(
false
);
}
}
}
@Override
public
void
onCheckedChanged
(
CompoundButton
compoundButton
,
boolean
b
)
{
if
(
compoundButton
==
switchButton
)
{
if
(!
serviceStart
())
{
switchButton
.
setChecked
(
false
);
}
}
}
/** Called when the activity is first created. */
@Override
public
void
onCreate
(
Bundle
savedInstanceState
)
{
setHeaderRes
(
R
.
xml
.
shadowsocks_headers
);
super
.
onCreate
(
savedInstanceState
);
/**
* Called when the activity is first created.
*/
@Override
public
void
onCreate
(
Bundle
savedInstanceState
)
{
setHeaderRes
(
R
.
xml
.
shadowsocks_headers
);
super
.
onCreate
(
savedInstanceState
);
RelativeLayout
switchLayout
=
(
RelativeLayout
)
getLayoutInflater
().
inflate
(
R
.
layout
.
layout_switch
,
null
);
RelativeLayout
switchLayout
=
(
RelativeLayout
)
getLayoutInflater
().
inflate
(
R
.
layout
.
layout_switch
,
null
);
getSupportActionBar
().
setCustomView
(
switchLayout
);
getSupportActionBar
().
setDisplayShowTitleEnabled
(
false
);
getSupportActionBar
().
setDisplayShowCustomEnabled
(
true
);
getSupportActionBar
().
setDisplayShowHomeEnabled
(
false
);
getSupportActionBar
().
setCustomView
(
switchLayout
);
getSupportActionBar
().
setDisplayShowTitleEnabled
(
false
);
getSupportActionBar
().
setDisplayShowCustomEnabled
(
true
);
getSupportActionBar
().
setDisplayShowHomeEnabled
(
false
);
switchButton
=
(
Switch
)
switchLayout
.
findViewById
(
R
.
id
.
switchButton
);
TextView
title
=
(
TextView
)
switchLayout
.
findViewById
(
R
.
id
.
title
);
Typeface
tf
=
Typefaces
.
get
(
this
,
"fonts/Iceland.ttf"
);
if
(
tf
!=
null
)
title
.
setTypeface
(
tf
);
title
.
setText
(
R
.
string
.
app_name
);
switchButton
=
(
Switch
)
switchLayout
.
findViewById
(
R
.
id
.
switchButton
);
final
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
Shadowsocks
.
this
);
TextView
title
=
(
TextView
)
switchLayout
.
findViewById
(
R
.
id
.
title
);
Typeface
tf
=
Typefaces
.
get
(
this
,
"fonts/Iceland.ttf"
);
if
(
tf
!=
null
)
title
.
setTypeface
(
tf
);
title
.
setText
(
R
.
string
.
app_name
);
boolean
init
=
!
settings
.
getBoolean
(
"isRunning"
,
false
)
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
}
if
(
init
)
{
if
(
mProgressDialog
==
null
)
{
mProgressDialog
=
ProgressDialog
.
show
(
this
,
""
,
getString
(
R
.
string
.
initializing
),
true
,
true
);
}
@Override
public
boolean
onCreateOptionsMenu
(
Menu
menu
)
{
menu
.
add
(
0
,
0
,
0
,
R
.
string
.
recovery
)
.
setIcon
(
android
.
R
.
drawable
.
ic_menu_revert
)
.
setShowAsAction
(
MenuItem
.
SHOW_AS_ACTION_WITH_TEXT
);
menu
.
add
(
0
,
1
,
1
,
R
.
string
.
about
)
.
setIcon
(
android
.
R
.
drawable
.
ic_menu_info_details
)
.
setShowAsAction
(
MenuItem
.
SHOW_AS_ACTION_WITH_TEXT
);
return
true
;
}
new
Thread
()
{
@Override
public
void
run
()
{
/**
* Called when the activity is closed.
*/
@Override
public
void
onDestroy
()
{
SharedPreferences
settings
=
getSharedPreferences
(
PREFS_NAME
,
0
);
Utils
.
isRoot
();
SharedPreferences
.
Editor
editor
=
settings
.
edit
();
editor
.
putBoolean
(
"isConnected"
,
ShadowsocksService
.
isServiceStarted
());
editor
.
commit
();
String
versionName
;
try
{
versionName
=
getPackageManager
().
getPackageInfo
(
getPackageName
(),
0
).
versionName
;
}
catch
(
NameNotFoundException
e
)
{
versionName
=
"NONE"
;
}
super
.
onDestroy
();
}
if
(!
settings
.
getBoolean
(
versionName
,
false
))
{
Editor
edit
=
settings
.
edit
();
edit
.
putBoolean
(
versionName
,
true
);
edit
.
commit
();
reset
();
}
@Override
public
boolean
onKeyDown
(
int
keyCode
,
KeyEvent
event
)
{
if
(
keyCode
==
KeyEvent
.
KEYCODE_BACK
&&
event
.
getRepeatCount
()
==
0
)
{
try
{
finish
();
}
catch
(
Exception
ignore
)
{
// Nothing
}
return
true
;
handler
.
sendEmptyMessage
(
MSG_INITIAL_FINISH
);
}
return
super
.
onKeyDown
(
keyCode
,
event
);
}.
start
(
);
}
@Override
public
boolean
onOptionsItemSelected
(
MenuItem
item
)
{
switch
(
item
.
getItemId
())
{
case
0
:
recovery
();
break
;
case
1
:
String
versionName
=
""
;
try
{
versionName
=
getPackageManager
().
getPackageInfo
(
getPackageName
(),
0
).
versionName
;
}
catch
(
NameNotFoundException
e
)
{
versionName
=
""
;
}
showAToast
(
getString
(
R
.
string
.
about
)
+
" ("
+
versionName
+
")\n\n"
+
getString
(
R
.
string
.
copy_rights
));
break
;
}
@Override
public
boolean
onCreateOptionsMenu
(
Menu
menu
)
{
menu
.
add
(
0
,
0
,
0
,
R
.
string
.
recovery
)
.
setIcon
(
android
.
R
.
drawable
.
ic_menu_revert
)
.
setShowAsAction
(
MenuItem
.
SHOW_AS_ACTION_WITH_TEXT
);
menu
.
add
(
0
,
1
,
1
,
R
.
string
.
about
)
.
setIcon
(
android
.
R
.
drawable
.
ic_menu_info_details
)
.
setShowAsAction
(
MenuItem
.
SHOW_AS_ACTION_WITH_TEXT
);
return
true
;
}
/** Called when the activity is closed. */
@Override
public
void
onDestroy
()
{
SharedPreferences
settings
=
getSharedPreferences
(
PREFS_NAME
,
0
);
SharedPreferences
.
Editor
editor
=
settings
.
edit
();
editor
.
putBoolean
(
"isConnected"
,
ShadowsocksService
.
isServiceStarted
());
editor
.
commit
();
super
.
onDestroy
();
}
@Override
public
boolean
onKeyDown
(
int
keyCode
,
KeyEvent
event
)
{
if
(
keyCode
==
KeyEvent
.
KEYCODE_BACK
&&
event
.
getRepeatCount
()
==
0
)
{
try
{
finish
();
}
catch
(
Exception
ignore
)
{
// Nothing
}
return
true
;
}
return
super
.
onKeyDown
(
keyCode
,
event
);
}
@Override
public
boolean
onOptionsItemSelected
(
MenuItem
item
)
{
switch
(
item
.
getItemId
())
{
case
0
:
recovery
();
break
;
case
1
:
String
versionName
=
""
;
try
{
versionName
=
getPackageManager
().
getPackageInfo
(
getPackageName
(),
0
).
versionName
;
}
catch
(
NameNotFoundException
e
)
{
versionName
=
""
;
}
return
super
.
onOptionsItemSelected
(
item
);
showAToast
(
getString
(
R
.
string
.
about
)
+
" ("
+
versionName
+
")\n\n"
+
getString
(
R
.
string
.
copy_rights
));
break
;
}
@Override
protected
void
onPause
()
{
super
.
onPause
();
PreferenceManager
.
getDefaultSharedPreferences
(
this
)
.
unregisterOnSharedPreferenceChangeListener
(
this
);
}
return
super
.
onOptionsItemSelected
(
item
);
}
@Override
protected
void
onResume
()
{
super
.
onResume
();
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
@Override
protected
void
onPause
()
{
super
.
onPause
();
PreferenceManager
.
getDefaultSharedPreferences
(
this
)
.
unregisterOnSharedPreferenceChangeListener
(
this
);
}
Editor
edit
=
settings
.
edit
();
@Override
protected
void
onResume
()
{
super
.
onResume
();
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
if
(
ShadowsocksService
.
isServiceStarted
())
{
edit
.
putBoolean
(
"isRunning"
,
true
);
switchButton
.
setChecked
(
true
);
}
else
{
if
(
settings
.
getBoolean
(
"isRunning"
,
false
))
{
new
Thread
()
{
@Override
public
void
run
()
{
crash_recovery
();
handler
.
sendEmptyMessage
(
MSG_CRASH_RECOVER
);
}
}.
start
();
}
edit
.
putBoolean
(
"isRunning"
,
false
);
switchButton
.
setChecked
(
false
);
}
edit
.
commit
();
setPreferenceEnabled
();
switchButton
.
setOnCheckedChangeListener
(
this
);
PreferenceManager
.
getDefaultSharedPreferences
(
this
)
.
registerOnSharedPreferenceChangeListener
(
this
);
Editor
edit
=
settings
.
edit
();
if
(
ShadowsocksService
.
isServiceStarted
())
{
edit
.
putBoolean
(
"isRunning"
,
true
);
switchButton
.
setChecked
(
true
);
}
else
{
if
(
settings
.
getBoolean
(
"isRunning"
,
false
))
{
new
Thread
()
{
@Override
public
void
run
()
{
crash_recovery
();
handler
.
sendEmptyMessage
(
MSG_CRASH_RECOVER
);
}
}.
start
();
}
edit
.
putBoolean
(
"isRunning"
,
false
);
switchButton
.
setChecked
(
false
);
}
private
void
setPreferenceEnabled
()
{
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
Shadowsocks
.
this
);
edit
.
commit
();
boolean
enabled
=
!
settings
.
getBoolean
(
"isRunning"
,
false
)
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
setPreferenceEnabled
();
for
(
String
name
:
PROXY_PREFS
)
{
Preference
pref
=
findPreference
(
name
);
if
(
pref
!=
null
)
{
pref
.
setEnabled
(
enabled
);
}
}
for
(
String
name
:
FEATRUE_PREFS
)
{
Preference
pref
=
findPreference
(
name
);
if
(
pref
!=
null
)
{
if
(
name
.
equals
(
"isBypassApps"
)
||
name
.
equals
(
"proxyedApps"
))
{
boolean
isGlobalProxy
=
settings
.
getBoolean
(
"isGlobalProxy"
,
false
);
pref
.
setEnabled
(
enabled
&&
!
isGlobalProxy
);
}
else
{
pref
.
setEnabled
(
enabled
);
}
}
}
}
switchButton
.
setOnCheckedChangeListener
(
this
);
PreferenceManager
.
getDefaultSharedPreferences
(
this
)
.
registerOnSharedPreferenceChangeListener
(
this
);
}
@Override
public
void
onSharedPreferenceChanged
(
SharedPreferences
sharedPreferences
,
String
key
)
{
// Let's do something a preference value changes
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
if
(
key
.
equals
(
"isRunning"
))
{
if
(
settings
.
getBoolean
(
"isRunning"
,
false
))
{
switchButton
.
setChecked
(
true
);
}
else
{
switchButton
.
setChecked
(
false
);
}
}
private
void
setPreferenceEnabled
()
{
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
Shadowsocks
.
this
);
if
(
key
.
equals
(
"isConnecting"
))
{
if
(
settings
.
getBoolean
(
"isConnecting"
,
false
))
{
Log
.
d
(
TAG
,
"Connecting start"
);
if
(
mProgressDialog
==
null
)
mProgressDialog
=
ProgressDialog
.
show
(
this
,
""
,
getString
(
R
.
string
.
connecting
),
true
,
true
);
}
else
{
Log
.
d
(
TAG
,
"Connecting finish"
);
if
(
mProgressDialog
!=
null
)
{
mProgressDialog
.
dismiss
();
mProgressDialog
=
null
;
}
}
}
boolean
enabled
=
!
settings
.
getBoolean
(
"isRunning"
,
false
)
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
if
(
key
.
equals
(
"isRunning"
)
||
key
.
equals
(
"isGlobalProxy"
))
{
setPreferenceEnabled
();
}
for
(
String
name
:
PROXY_PREFS
)
{
Preference
pref
=
findPreference
(
name
);
if
(
pref
!=
null
)
{
pref
.
setEnabled
(
enabled
);
}
}
@Override
public
void
onStart
()
{
super
.
onStart
();
EasyTracker
.
getInstance
().
activityStart
(
this
);
final
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
Shadowsocks
.
this
);
boolean
init
=
!
settings
.
getBoolean
(
"isRunning"
,
false
)
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
if
(
init
)
{
if
(
mProgressDialog
==
null
)
{
mProgressDialog
=
ProgressDialog
.
show
(
this
,
""
,
getString
(
R
.
string
.
initializing
),
true
,
true
);
}
new
Thread
()
{
@Override
public
void
run
()
{
Utils
.
isRoot
();
String
versionName
;
try
{
versionName
=
getPackageManager
().
getPackageInfo
(
getPackageName
(),
0
).
versionName
;
}
catch
(
NameNotFoundException
e
)
{
versionName
=
"NONE"
;
}
if
(!
settings
.
getBoolean
(
versionName
,
false
))
{
Editor
edit
=
settings
.
edit
();
edit
.
putBoolean
(
versionName
,
true
);
edit
.
commit
();
reset
();
}
handler
.
sendEmptyMessage
(
MSG_INITIAL_FINISH
);
}
}.
start
();
for
(
String
name
:
FEATRUE_PREFS
)
{
Preference
pref
=
findPreference
(
name
);
if
(
pref
!=
null
)
{
if
(
name
.
equals
(
"isBypassApps"
)
||
name
.
equals
(
"proxyedApps"
))
{
boolean
isGlobalProxy
=
settings
.
getBoolean
(
"isGlobalProxy"
,
false
);
pref
.
setEnabled
(
enabled
&&
!
isGlobalProxy
);
}
else
{
pref
.
setEnabled
(
enabled
);
}
}
}
}
@Override
public
void
onSharedPreferenceChanged
(
SharedPreferences
sharedPreferences
,
String
key
)
{
// Let's do something a preference value changes
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
if
(
key
.
equals
(
"isRunning"
))
{
if
(
settings
.
getBoolean
(
"isRunning"
,
false
))
{
switchButton
.
setChecked
(
true
);
}
else
{
switchButton
.
setChecked
(
false
);
}
}
@Override
public
void
onStop
()
{
super
.
onStop
();
EasyTracker
.
getInstance
().
activityStop
(
this
);
if
(
key
.
equals
(
"isConnecting"
))
{
if
(
settings
.
getBoolean
(
"isConnecting"
,
false
))
{
Log
.
d
(
TAG
,
"Connecting start"
);
if
(
mProgressDialog
==
null
)
{
mProgressDialog
=
ProgressDialog
.
show
(
this
,
""
,
getString
(
R
.
string
.
connecting
),
true
,
true
);
}
}
else
{
Log
.
d
(
TAG
,
"Connecting finish"
);
if
(
mProgressDialog
!=
null
)
{
mProgressDialog
.
dismiss
();
mProgressDialog
=
null
;
mProgressDialog
.
dismiss
();
mProgressDialog
=
null
;
}
}
}
public
void
reset
()
{
crash_recovery
();
copyAssets
(
""
);
copyAssets
(
Utils
.
getABI
());
Utils
.
runCommand
(
"chmod 755 /data/data/com.github.shadowsocks/iptables\n"
+
"chmod 755 /data/data/com.github.shadowsocks/redsocks\n"
+
"chmod 755 /data/data/com.github.shadowsocks/pdnsd\n"
+
"chmod 755 /data/data/com.github.shadowsocks/shadowsocks\n"
+
"chmod 755 /data/data/com.github.shadowsocks/polipo\n"
);
if
(
key
.
equals
(
"isRunning"
)
||
key
.
equals
(
"isGlobalProxy"
))
{
setPreferenceEnabled
();
}
}
@Override
public
void
onStart
()
{
super
.
onStart
();
EasyTracker
.
getInstance
().
activityStart
(
this
);
}
@Override
public
void
onStop
()
{
super
.
onStop
();
EasyTracker
.
getInstance
().
activityStop
(
this
);
if
(
mProgressDialog
!=
null
)
{
mProgressDialog
.
dismiss
();
mProgressDialog
=
null
;
}
}
private
void
recovery
()
{
if
(
mProgressDialog
==
null
)
mProgressDialog
=
ProgressDialog
.
show
(
this
,
""
,
getString
(
R
.
string
.
recovering
),
true
,
true
);
final
Handler
h
=
new
Handler
()
{
@Override
public
void
handleMessage
(
Message
msg
)
{
if
(
mProgressDialog
!=
null
)
{
mProgressDialog
.
dismiss
();
mProgressDialog
=
null
;
}
}
};
public
void
reset
()
{
stopService
(
new
Intent
(
this
,
ShadowsocksService
.
class
)
);
crash_recovery
(
);
new
Thread
()
{
@Override
public
void
run
()
{
copyAssets
(
""
);
copyAssets
(
Utils
.
getABI
());
reset
();
Utils
.
runCommand
(
"chmod 755 /data/data/com.github.shadowsocks/iptables\n"
+
"chmod 755 /data/data/com.github.shadowsocks/redsocks\n"
+
"chmod 755 /data/data/com.github.shadowsocks/pdnsd\n"
+
"chmod 755 /data/data/com.github.shadowsocks/shadowsocks\n"
+
"chmod 755 /data/data/com.github.shadowsocks/polipo\n"
);
}
h
.
sendEmptyMessage
(
0
);
}
}.
start
();
private
void
recovery
()
{
if
(
mProgressDialog
==
null
)
{
mProgressDialog
=
ProgressDialog
.
show
(
this
,
""
,
getString
(
R
.
string
.
recovering
),
true
,
true
);
}
/**
* Called when connect button is clicked.
*/
public
boolean
serviceStart
()
{
if
(
ShadowsocksService
.
isServiceStarted
())
{
stopService
(
new
Intent
(
this
,
ShadowsocksService
.
class
));
return
false
;
final
Handler
h
=
new
Handler
()
{
@Override
public
void
handleMessage
(
Message
msg
)
{
if
(
mProgressDialog
!=
null
)
{
mProgressDialog
.
dismiss
();
mProgressDialog
=
null
;
}
}
};
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
stopService
(
new
Intent
(
this
,
ShadowsocksService
.
class
));
final
String
proxy
=
settings
.
getString
(
"proxy"
,
""
);
if
(
isTextEmpty
(
proxy
,
getString
(
R
.
string
.
proxy_empty
)))
return
false
;
new
Thread
()
{
@Override
public
void
run
()
{
String
portText
=
settings
.
getString
(
"port"
,
""
);
if
(
isTextEmpty
(
portText
,
getString
(
R
.
string
.
port_empty
)))
return
false
;
try
{
int
port
=
Integer
.
valueOf
(
portText
);
if
(
port
<=
1024
)
{
this
.
showAToast
(
getString
(
R
.
string
.
port_alert
));
return
false
;
}
}
catch
(
Exception
e
)
{
this
.
showAToast
(
getString
(
R
.
string
.
port_alert
));
return
false
;
}
reset
();
Intent
it
=
new
Intent
(
this
,
ShadowsocksService
.
class
);
startService
(
it
);
h
.
sendEmptyMessage
(
0
);
}
}.
start
();
}
return
true
;
}
/** Called when connect button is clicked. */
public
boolean
serviceStart
()
{
private
void
showAToast
(
String
msg
)
{
AlertDialog
.
Builder
builder
=
new
AlertDialog
.
Builder
(
this
);
builder
.
setMessage
(
msg
)
.
setCancelable
(
false
)
.
setNegativeButton
(
getString
(
R
.
string
.
ok_iknow
),
new
DialogInterface
.
OnClickListener
()
{
@Override
public
void
onClick
(
DialogInterface
dialog
,
int
id
)
{
dialog
.
cancel
();
}
});
AlertDialog
alert
=
builder
.
create
();
alert
.
show
();
if
(
ShadowsocksService
.
isServiceStarted
())
{
stopService
(
new
Intent
(
this
,
ShadowsocksService
.
class
));
return
false
;
}
public
static
class
ProxyFragment
extends
UnifiedPreferenceFragment
implements
OnSharedPreferenceChangeListener
{
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
private
void
setPreferenceEnabled
()
{
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
getActivity
());
final
String
proxy
=
settings
.
getString
(
"proxy"
,
""
);
if
(
isTextEmpty
(
proxy
,
getString
(
R
.
string
.
proxy_empty
)))
return
false
;
boolean
enabled
=
!
settings
.
getBoolean
(
"isRunning"
,
false
)
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
String
portText
=
settings
.
getString
(
"port"
,
""
);
if
(
isTextEmpty
(
portText
,
getString
(
R
.
string
.
port_empty
)))
return
false
;
try
{
int
port
=
Integer
.
valueOf
(
portText
);
if
(
port
<=
1024
)
{
this
.
showAToast
(
getString
(
R
.
string
.
port_alert
));
return
false
;
}
}
catch
(
Exception
e
)
{
this
.
showAToast
(
getString
(
R
.
string
.
port_alert
));
return
false
;
}
for
(
String
name
:
PROXY_PREFS
)
{
Preference
pref
=
findPreference
(
name
);
if
(
pref
!=
null
)
{
pref
.
setEnabled
(
enabled
);
}
}
}
Intent
it
=
new
Intent
(
this
,
ShadowsocksService
.
class
);
startService
(
it
);
return
true
;
}
private
void
showAToast
(
String
msg
)
{
AlertDialog
.
Builder
builder
=
new
AlertDialog
.
Builder
(
this
);
builder
.
setMessage
(
msg
)
.
setCancelable
(
false
)
.
setNegativeButton
(
getString
(
R
.
string
.
ok_iknow
),
new
DialogInterface
.
OnClickListener
()
{
@Override
public
void
onClick
(
DialogInterface
dialog
,
int
id
)
{
dialog
.
cancel
();
}
});
AlertDialog
alert
=
builder
.
create
();
alert
.
show
();
}
public
static
class
ProxyFragment
extends
UnifiedPreferenceFragment
implements
OnSharedPreferenceChangeListener
{
@Override
public
void
onResume
()
{
super
.
onResume
();
setPreferenceEnabled
();
private
void
setPreferenceEnabled
()
{
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
getActivity
());
// Set up a listener whenever a key changes
getPreferenceScreen
().
getSharedPreferences
()
.
registerOnSharedPreferenceChangeListener
(
this
);
boolean
enabled
=
!
settings
.
getBoolean
(
"isRunning"
,
false
)
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
for
(
String
name
:
PROXY_PREFS
)
{
Preference
pref
=
findPreference
(
name
);
if
(
pref
!=
null
)
{
pref
.
setEnabled
(
enabled
);
}
}
}
@Override
public
void
onPause
()
{
super
.
onPause
();
// Unregister the listener whenever a key changes
getPreferenceScreen
().
getSharedPreferences
()
.
unregisterOnSharedPreferenceChangeListener
(
this
);
}
@Override
public
void
onResume
()
{
super
.
onResume
();
setPreferenceEnabled
();
@Override
public
void
onSharedPreferenceChanged
(
SharedPreferences
sharedPreferences
,
String
key
)
{
if
(
key
.
equals
(
"isRunning"
)
||
key
.
equals
(
"isGlobalProxy"
))
{
setPreferenceEnabled
();
}
}
// Set up a listener whenever a key changes
getPreferenceScreen
().
getSharedPreferences
().
registerOnSharedPreferenceChangeListener
(
this
);
}
public
static
class
FeatureFragment
extends
UnifiedPreferenceFragment
implements
OnSharedPreferenceChangeListener
{
private
void
setPreferenceEnabled
()
{
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
getActivity
());
boolean
enabled
=
!
settings
.
getBoolean
(
"isRunning"
,
false
)
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
for
(
String
name
:
FEATRUE_PREFS
)
{
Preference
pref
=
findPreference
(
name
);
if
(
pref
!=
null
)
{
if
(
name
.
equals
(
"isBypassApps"
)
||
name
.
equals
(
"proxyedApps"
))
{
boolean
isGlobalProxy
=
settings
.
getBoolean
(
"isGlobalProxy"
,
false
);
pref
.
setEnabled
(
enabled
&&
!
isGlobalProxy
);
}
else
{
pref
.
setEnabled
(
enabled
);
}
}
}
}
@Override
public
void
onPause
()
{
super
.
onPause
();
// Unregister the listener whenever a key changes
getPreferenceScreen
().
getSharedPreferences
().
unregisterOnSharedPreferenceChangeListener
(
this
);
}
@Override
public
void
onResume
()
{
super
.
onResume
();
setPreferenceEnabled
();
@Override
public
void
onSharedPreferenceChanged
(
SharedPreferences
sharedPreferences
,
String
key
)
{
if
(
key
.
equals
(
"isRunning"
)
||
key
.
equals
(
"isGlobalProxy"
))
{
setPreferenceEnabled
();
}
}
}
// Set up a listener whenever a key changes
getPreferenceScreen
().
getSharedPreferences
()
.
registerOnSharedPreferenceChangeListener
(
this
);
}
public
static
class
FeatureFragment
extends
UnifiedPreferenceFragment
implements
OnSharedPreferenceChangeListener
{
@Override
public
void
onPause
()
{
super
.
onPause
();
// Unregister the listener whenever a key changes
getPreferenceScreen
().
getSharedPreferences
()
.
unregisterOnSharedPreferenceChangeListener
(
this
);
private
void
setPreferenceEnabled
()
{
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
getActivity
());
boolean
enabled
=
!
settings
.
getBoolean
(
"isRunning"
,
false
)
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
for
(
String
name
:
FEATRUE_PREFS
)
{
Preference
pref
=
findPreference
(
name
);
if
(
pref
!=
null
)
{
if
(
name
.
equals
(
"isBypassApps"
)
||
name
.
equals
(
"proxyedApps"
))
{
boolean
isGlobalProxy
=
settings
.
getBoolean
(
"isGlobalProxy"
,
false
);
pref
.
setEnabled
(
enabled
&&
!
isGlobalProxy
);
}
else
{
pref
.
setEnabled
(
enabled
);
}
}
}
}
@Override
public
void
onSharedPreferenceChanged
(
SharedPreferences
sharedPreferences
,
String
key
)
{
if
(
key
.
equals
(
"isRunning"
)
||
key
.
equals
(
"isGlobalProxy"
))
{
setPreferenceEnabled
();
}
}
@Override
public
void
onResume
()
{
super
.
onResume
();
setPreferenceEnabled
();
// Set up a listener whenever a key changes
getPreferenceScreen
().
getSharedPreferences
().
registerOnSharedPreferenceChangeListener
(
this
);
}
public
static
class
Typefaces
{
private
static
final
String
TAG
=
"Typefaces"
;
private
static
final
Hashtable
<
String
,
Typeface
>
cache
=
new
Hashtable
<
String
,
Typeface
>();
public
static
Typeface
get
(
Context
c
,
String
assetPath
)
{
synchronized
(
cache
)
{
if
(!
cache
.
containsKey
(
assetPath
))
{
try
{
Typeface
t
=
Typeface
.
createFromAsset
(
c
.
getAssets
(),
assetPath
);
cache
.
put
(
assetPath
,
t
);
}
catch
(
Exception
e
)
{
Log
.
e
(
TAG
,
"Could not get typeface '"
+
assetPath
+
"' because "
+
e
.
getMessage
());
return
null
;
}
}
return
cache
.
get
(
assetPath
);
}
}
@Override
public
void
onPause
()
{
super
.
onPause
();
// Unregister the listener whenever a key changes
getPreferenceScreen
().
getSharedPreferences
().
unregisterOnSharedPreferenceChangeListener
(
this
);
}
@Override
public
void
onSharedPreferenceChanged
(
SharedPreferences
sharedPreferences
,
String
key
)
{
if
(
key
.
equals
(
"isRunning"
)
||
key
.
equals
(
"isGlobalProxy"
))
{
setPreferenceEnabled
();
}
}
}
public
static
class
Typefaces
{
private
static
final
String
TAG
=
"Typefaces"
;
private
static
final
Hashtable
<
String
,
Typeface
>
cache
=
new
Hashtable
<
String
,
Typeface
>();
public
static
Typeface
get
(
Context
c
,
String
assetPath
)
{
synchronized
(
cache
)
{
if
(!
cache
.
containsKey
(
assetPath
))
{
try
{
Typeface
t
=
Typeface
.
createFromAsset
(
c
.
getAssets
(),
assetPath
);
cache
.
put
(
assetPath
,
t
);
}
catch
(
Exception
e
)
{
Log
.
e
(
TAG
,
"Could not get typeface '"
+
assetPath
+
"' because "
+
e
.
getMessage
());
return
null
;
}
}
return
cache
.
get
(
assetPath
);
}
}
}
}
src/com/github/shadowsocks/ShadowsocksApplication.java
View file @
3d664377
...
...
@@ -43,16 +43,15 @@ import com.google.analytics.tracking.android.EasyTracker;
public
class
ShadowsocksApplication
extends
Application
{
private
static
String
sTmpDir
;
private
static
String
sTmpDir
;
public
static
String
getTmpDir
()
{
return
sTmpDir
;
}
@Override
public
void
onCreate
()
{
EasyTracker
.
getInstance
().
setContext
(
this
);
sTmpDir
=
getCacheDir
().
getAbsolutePath
();
}
public
static
String
getTmpDir
()
{
return
sTmpDir
;
}
@Override
public
void
onCreate
()
{
EasyTracker
.
getInstance
().
setContext
(
this
);
sTmpDir
=
getCacheDir
().
getAbsolutePath
();
}
}
src/com/github/shadowsocks/ShadowsocksReceiver.java
View file @
3d664377
...
...
@@ -47,47 +47,47 @@ import android.preference.PreferenceManager;
public
class
ShadowsocksReceiver
extends
BroadcastReceiver
{
private
static
final
String
TAG
=
"Shadowsocks"
;
private
static
final
String
TAG
=
"Shadowsocks"
;
@Override
public
void
onReceive
(
Context
context
,
Intent
intent
)
{
@Override
public
void
onReceive
(
Context
context
,
Intent
intent
)
{
if
(
intent
!=
null
)
{
final
String
action
=
intent
.
getAction
();
if
(
action
.
equals
(
Intent
.
ACTION_SHUTDOWN
))
{
context
.
stopService
(
new
Intent
(
context
,
ShadowsocksService
.
class
));
return
;
}
}
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
context
);
String
versionName
;
try
{
versionName
=
context
.
getPackageManager
().
getPackageInfo
(
context
.
getPackageName
(),
0
).
versionName
;
}
catch
(
PackageManager
.
NameNotFoundException
e
)
{
versionName
=
"NONE"
;
}
if
(
intent
!=
null
)
{
final
String
action
=
intent
.
getAction
();
if
(
action
.
equals
(
Intent
.
ACTION_SHUTDOWN
))
{
context
.
stopService
(
new
Intent
(
context
,
ShadowsocksService
.
class
));
return
;
}
}
final
boolean
isAutoConnect
=
settings
.
getBoolean
(
"isAutoConnect"
,
false
);
final
boolean
isInstalled
=
settings
.
getBoolean
(
versionName
,
false
);
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
context
);
String
versionName
;
try
{
versionName
=
context
.
getPackageManager
().
getPackageInfo
(
context
.
getPackageName
(),
0
).
versionName
;
}
catch
(
PackageManager
.
NameNotFoundException
e
)
{
versionName
=
"NONE"
;
}
if
(
isAutoConnect
&&
isInstalled
)
{
final
String
portText
=
settings
.
getString
(
"port"
,
""
);
if
(
portText
==
null
||
portText
.
length
()
<=
0
)
{
return
;
}
try
{
int
port
=
Integer
.
valueOf
(
portText
);
if
(
port
<=
1024
)
{
return
;
}
}
catch
(
Exception
e
)
{
return
;
}
final
boolean
isAutoConnect
=
settings
.
getBoolean
(
"isAutoConnect"
,
false
);
final
boolean
isInstalled
=
settings
.
getBoolean
(
versionName
,
false
);
Intent
it
=
new
Intent
(
context
,
ShadowsocksService
.
class
);
context
.
startService
(
it
);
if
(
isAutoConnect
&&
isInstalled
)
{
final
String
portText
=
settings
.
getString
(
"port"
,
""
);
if
(
portText
==
null
||
portText
.
length
()
<=
0
)
{
return
;
}
try
{
int
port
=
Integer
.
valueOf
(
portText
);
if
(
port
<=
1024
)
{
return
;
}
}
}
catch
(
Exception
e
)
{
return
;
}
Intent
it
=
new
Intent
(
context
,
ShadowsocksService
.
class
);
context
.
startService
(
it
);
}
}
}
src/com/github/shadowsocks/ShadowsocksService.java
View file @
3d664377
...
...
@@ -55,8 +55,6 @@ import android.os.PowerManager;
import
android.preference.PreferenceManager
;
import
android.util.Log
;
import
com.google.analytics.tracking.android.EasyTracker
;
import
org.apache.http.conn.util.InetAddressUtils
;
import
java.io.BufferedReader
;
import
java.io.FileNotFoundException
;
import
java.io.FileReader
;
...
...
@@ -68,601 +66,583 @@ import java.net.Inet6Address;
import
java.net.InetAddress
;
import
java.net.UnknownHostException
;
import
java.util.HashSet
;
import
org.apache.http.conn.util.InetAddressUtils
;
public
class
ShadowsocksService
extends
Service
{
public
static
final
String
BASE
=
"/data/data/com.github.shadowsocks/"
;
final
static
String
REDSOCKS_CONF
=
"base {"
+
" log_debug = off;"
+
" log_info = off;"
+
" log = stderr;"
+
" daemon = on;"
+
" redirector = iptables;"
+
"}"
+
"redsocks {"
+
" local_ip = 127.0.0.1;"
+
" local_port = 8123;"
+
" ip = 127.0.0.1;"
+
" port = %d;"
+
" type = socks5;"
+
"}"
;
final
static
String
SHADOWSOCKS_CONF
=
"{\"server\": [%s], \"server_port\": %d, \"local_port\": %d, \"password\": %s, \"timeout\": %d}"
;
final
static
String
CMD_IPTABLES_RETURN
=
" -t nat -A OUTPUT -p tcp -d 0.0.0.0 -j RETURN\n"
;
final
static
String
CMD_IPTABLES_REDIRECT_ADD_SOCKS
=
" -t nat -A OUTPUT -p tcp "
+
"-j REDIRECT --to 8123\n"
;
final
static
String
CMD_IPTABLES_DNAT_ADD_SOCKS
=
" -t nat -A OUTPUT -p tcp "
+
"-j DNAT --to-destination 127.0.0.1:8123\n"
;
private
static
final
int
MSG_CONNECT_START
=
0
;
private
static
final
int
MSG_CONNECT_FINISH
=
1
;
private
static
final
int
MSG_CONNECT_SUCCESS
=
2
;
private
static
final
int
MSG_CONNECT_FAIL
=
3
;
private
static
final
int
MSG_HOST_CHANGE
=
4
;
private
static
final
int
MSG_STOP_SELF
=
5
;
private
static
final
String
TAG
=
"ShadowsocksService"
;
private
final
static
int
DNS_PORT
=
8153
;
private
static
final
Class
<?>[]
mStartForegroundSignature
=
new
Class
[]{
int
.
class
,
Notification
.
class
};
private
static
final
Class
<?>[]
mStopForegroundSignature
=
new
Class
[]{
boolean
.
class
};
private
static
final
Class
<?>[]
mSetForegroundSignature
=
new
Class
[]{
boolean
.
class
};
/*
* This is a hack see
* http://www.mail-archive.com/android-developers@googlegroups
* .com/msg18298.html we are not really able to decide if the service was
* started. So we remember a week reference to it. We set it if we are
* running and clear it if we are stopped. If anything goes wrong, the
* reference will hopefully vanish
*/
private
static
WeakReference
<
ShadowsocksService
>
sRunningInstance
=
null
;
final
Handler
handler
=
new
Handler
()
{
@Override
public
void
handleMessage
(
Message
msg
)
{
Editor
ed
=
settings
.
edit
();
switch
(
msg
.
what
)
{
case
MSG_CONNECT_START:
ed
.
putBoolean
(
"isConnecting"
,
true
);
PowerManager
pm
=
(
PowerManager
)
getSystemService
(
Context
.
POWER_SERVICE
);
mWakeLock
=
pm
.
newWakeLock
(
PowerManager
.
SCREEN_DIM_WAKE_LOCK
|
PowerManager
.
ON_AFTER_RELEASE
,
"GAEProxy"
);
mWakeLock
.
acquire
();
break
;
case
MSG_CONNECT_FINISH:
ed
.
putBoolean
(
"isConnecting"
,
false
);
if
(
mWakeLock
!=
null
&&
mWakeLock
.
isHeld
())
mWakeLock
.
release
();
break
;
case
MSG_CONNECT_SUCCESS:
ed
.
putBoolean
(
"isRunning"
,
true
);
break
;
case
MSG_CONNECT_FAIL:
ed
.
putBoolean
(
"isRunning"
,
false
);
break
;
case
MSG_HOST_CHANGE:
ed
.
putString
(
"appHost"
,
appHost
);
break
;
case
MSG_STOP_SELF:
stopSelf
();
break
;
}
ed
.
commit
();
super
.
handleMessage
(
msg
);
}
};
private
Notification
notification
;
private
NotificationManager
notificationManager
;
private
PendingIntent
pendIntent
;
private
PowerManager
.
WakeLock
mWakeLock
;
private
String
appHost
;
private
int
remotePort
;
private
int
localPort
;
private
String
sitekey
;
private
SharedPreferences
settings
=
null
;
private
boolean
hasRedirectSupport
=
true
;
private
boolean
isGlobalProxy
=
false
;
private
boolean
isGFWList
=
false
;
private
boolean
isBypassApps
=
false
;
private
boolean
isDNSProxy
=
false
;
private
boolean
isHTTPProxy
=
false
;
private
String
encMethod
;
private
ProxyedApp
apps
[];
private
Method
mSetForeground
;
private
Method
mStartForeground
;
private
Method
mStopForeground
;
private
Object
[]
mSetForegroundArgs
=
new
Object
[
1
];
private
Object
[]
mStartForegroundArgs
=
new
Object
[
2
];
private
Object
[]
mStopForegroundArgs
=
new
Object
[
1
];
public
static
boolean
isServiceStarted
()
{
final
boolean
isServiceStarted
;
if
(
sRunningInstance
==
null
)
{
isServiceStarted
=
false
;
}
else
if
(
sRunningInstance
.
get
()
==
null
)
{
isServiceStarted
=
false
;
sRunningInstance
=
null
;
}
else
{
isServiceStarted
=
true
;
}
return
isServiceStarted
;
public
static
final
String
BASE
=
"/data/data/com.github.shadowsocks/"
;
final
static
String
REDSOCKS_CONF
=
"base {"
+
" log_debug = off;"
+
" log_info = off;"
+
" log = stderr;"
+
" daemon = on;"
+
" redirector = iptables;"
+
"}"
+
"redsocks {"
+
" local_ip = 127.0.0.1;"
+
" local_port = 8123;"
+
" ip = 127.0.0.1;"
+
" port = %d;"
+
" type = socks5;"
+
"}"
;
final
static
String
SHADOWSOCKS_CONF
=
"{\"server\": [%s], \"server_port\": %d, \"local_port\": %d, \"password\": %s, \"timeout\": %d}"
;
final
static
String
CMD_IPTABLES_RETURN
=
" -t nat -A OUTPUT -p tcp -d 0.0.0.0 -j RETURN\n"
;
final
static
String
CMD_IPTABLES_REDIRECT_ADD_SOCKS
=
" -t nat -A OUTPUT -p tcp "
+
"-j REDIRECT --to 8123\n"
;
final
static
String
CMD_IPTABLES_DNAT_ADD_SOCKS
=
" -t nat -A OUTPUT -p tcp "
+
"-j DNAT --to-destination 127.0.0.1:8123\n"
;
private
static
final
int
MSG_CONNECT_START
=
0
;
private
static
final
int
MSG_CONNECT_FINISH
=
1
;
private
static
final
int
MSG_CONNECT_SUCCESS
=
2
;
private
static
final
int
MSG_CONNECT_FAIL
=
3
;
private
static
final
int
MSG_HOST_CHANGE
=
4
;
private
static
final
int
MSG_STOP_SELF
=
5
;
private
static
final
String
TAG
=
"ShadowsocksService"
;
private
final
static
int
DNS_PORT
=
8153
;
private
static
final
Class
<?>[]
mStartForegroundSignature
=
new
Class
[]
{
int
.
class
,
Notification
.
class
};
private
static
final
Class
<?>[]
mStopForegroundSignature
=
new
Class
[]
{
boolean
.
class
};
private
static
final
Class
<?>[]
mSetForegroundSignature
=
new
Class
[]
{
boolean
.
class
};
/*
* This is a hack see
* http://www.mail-archive.com/android-developers@googlegroups
* .com/msg18298.html we are not really able to decide if the service was
* started. So we remember a week reference to it. We set it if we are
* running and clear it if we are stopped. If anything goes wrong, the
* reference will hopefully vanish
*/
private
static
WeakReference
<
ShadowsocksService
>
sRunningInstance
=
null
;
final
Handler
handler
=
new
Handler
()
{
@Override
public
void
handleMessage
(
Message
msg
)
{
Editor
ed
=
settings
.
edit
();
switch
(
msg
.
what
)
{
case
MSG_CONNECT_START:
ed
.
putBoolean
(
"isConnecting"
,
true
);
PowerManager
pm
=
(
PowerManager
)
getSystemService
(
Context
.
POWER_SERVICE
);
mWakeLock
=
pm
.
newWakeLock
(
PowerManager
.
SCREEN_DIM_WAKE_LOCK
|
PowerManager
.
ON_AFTER_RELEASE
,
"GAEProxy"
);
mWakeLock
.
acquire
();
break
;
case
MSG_CONNECT_FINISH:
ed
.
putBoolean
(
"isConnecting"
,
false
);
if
(
mWakeLock
!=
null
&&
mWakeLock
.
isHeld
())
mWakeLock
.
release
();
break
;
case
MSG_CONNECT_SUCCESS:
ed
.
putBoolean
(
"isRunning"
,
true
);
break
;
case
MSG_CONNECT_FAIL:
ed
.
putBoolean
(
"isRunning"
,
false
);
break
;
case
MSG_HOST_CHANGE:
ed
.
putString
(
"appHost"
,
appHost
);
break
;
case
MSG_STOP_SELF:
stopSelf
();
break
;
}
ed
.
commit
();
super
.
handleMessage
(
msg
);
}
private
int
getPid
(
String
name
)
{
try
{
BufferedReader
reader
=
new
BufferedReader
(
new
FileReader
(
BASE
+
name
+
".pid"
));
final
String
line
=
reader
.
readLine
();
return
Integer
.
valueOf
(
line
);
}
catch
(
FileNotFoundException
e
)
{
Log
.
e
(
TAG
,
"Cannot open pid file: "
+
name
);
}
catch
(
IOException
e
)
{
Log
.
e
(
TAG
,
"Cannot read pid file: "
+
name
);
}
catch
(
NumberFormatException
e
)
{
Log
.
e
(
TAG
,
"Invalid pid"
,
e
);
}
return
-
1
;
};
private
Notification
notification
;
private
NotificationManager
notificationManager
;
private
PendingIntent
pendIntent
;
private
PowerManager
.
WakeLock
mWakeLock
;
private
String
appHost
;
private
int
remotePort
;
private
int
localPort
;
private
String
sitekey
;
private
SharedPreferences
settings
=
null
;
private
boolean
hasRedirectSupport
=
true
;
private
boolean
isGlobalProxy
=
false
;
private
boolean
isGFWList
=
false
;
private
boolean
isBypassApps
=
false
;
private
boolean
isDNSProxy
=
false
;
private
boolean
isHTTPProxy
=
false
;
private
String
encMethod
;
private
ProxyedApp
apps
[];
private
Method
mSetForeground
;
private
Method
mStartForeground
;
private
Method
mStopForeground
;
private
Object
[]
mSetForegroundArgs
=
new
Object
[
1
];
private
Object
[]
mStartForegroundArgs
=
new
Object
[
2
];
private
Object
[]
mStopForegroundArgs
=
new
Object
[
1
];
public
static
boolean
isServiceStarted
()
{
final
boolean
isServiceStarted
;
if
(
sRunningInstance
==
null
)
{
isServiceStarted
=
false
;
}
else
if
(
sRunningInstance
.
get
()
==
null
)
{
isServiceStarted
=
false
;
sRunningInstance
=
null
;
}
else
{
isServiceStarted
=
true
;
}
private
void
startShadowsocksDaemon
()
{
new
Thread
()
{
@Override
public
void
run
()
{
final
String
cmd
=
String
.
format
(
BASE
+
"shadowsocks -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f "
+
BASE
+
"shadowsocks.pid"
,
appHost
,
remotePort
,
localPort
,
sitekey
,
encMethod
);
System
.
exec
(
cmd
);
}
}.
start
();
return
isServiceStarted
;
}
private
int
getPid
(
String
name
)
{
try
{
BufferedReader
reader
=
new
BufferedReader
(
new
FileReader
(
BASE
+
name
+
".pid"
));
final
String
line
=
reader
.
readLine
();
return
Integer
.
valueOf
(
line
);
}
catch
(
FileNotFoundException
e
)
{
Log
.
e
(
TAG
,
"Cannot open pid file: "
+
name
);
}
catch
(
IOException
e
)
{
Log
.
e
(
TAG
,
"Cannot read pid file: "
+
name
);
}
catch
(
NumberFormatException
e
)
{
Log
.
e
(
TAG
,
"Invalid pid"
,
e
);
}
private
void
startPolipoDaemon
()
{
new
Thread
()
{
@Override
public
void
run
()
{
final
String
cmd
=
String
.
format
(
BASE
+
"polipo proxyPort=%d socksParentProxy=127.0.0.1:%d daemonise=true pidFile=\"%s\" logLevel=1 logFile=\"%s\""
,
localPort
+
1
,
localPort
,
BASE
+
"polipo.pid"
,
BASE
+
"polipo.log"
);
System
.
exec
(
cmd
);
}
}.
start
();
return
-
1
;
}
private
void
startShadowsocksDaemon
()
{
new
Thread
()
{
@Override
public
void
run
()
{
final
String
cmd
=
String
.
format
(
BASE
+
"shadowsocks -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f "
+
BASE
+
"shadowsocks.pid"
,
appHost
,
remotePort
,
localPort
,
sitekey
,
encMethod
);
System
.
exec
(
cmd
);
}
}.
start
();
}
private
void
startPolipoDaemon
()
{
new
Thread
()
{
@Override
public
void
run
()
{
final
String
cmd
=
String
.
format
(
BASE
+
"polipo proxyPort=%d socksParentProxy=127.0.0.1:%d daemonise=true pidFile=\"%s\" logLevel=1 logFile=\"%s\""
,
localPort
+
1
,
localPort
,
BASE
+
"polipo.pid"
,
BASE
+
"polipo.log"
);
System
.
exec
(
cmd
);
}
}.
start
();
}
private
void
startDnsDaemon
()
{
final
String
cmd
=
BASE
+
"pdnsd -c "
+
BASE
+
"pdnsd.conf"
;
Utils
.
runCommand
(
cmd
);
}
private
String
getVersionName
()
{
String
version
;
try
{
PackageInfo
pi
=
getPackageManager
().
getPackageInfo
(
getPackageName
(),
0
);
version
=
pi
.
versionName
;
}
catch
(
PackageManager
.
NameNotFoundException
e
)
{
version
=
"Package name not found"
;
}
return
version
;
}
private
void
startDnsDaemon
()
{
final
String
cmd
=
BASE
+
"pdnsd -c "
+
BASE
+
"pdnsd.conf"
;
Utils
.
runCommand
(
cmd
);
}
public
void
handleCommand
(
Intent
intent
)
{
private
String
getVersionName
()
{
String
version
;
try
{
PackageInfo
pi
=
getPackageManager
().
getPackageInfo
(
getPackageName
(),
0
);
version
=
pi
.
versionName
;
}
catch
(
PackageManager
.
NameNotFoundException
e
)
{
version
=
"Package name not found"
;
}
return
version
;
if
(
intent
==
null
)
{
stopSelf
();
return
;
}
public
void
handleCommand
(
Intent
intent
)
{
if
(
intent
==
null
)
{
stopSelf
();
return
;
}
appHost
=
settings
.
getString
(
"proxy"
,
"127.0.0.1"
);
sitekey
=
settings
.
getString
(
"sitekey"
,
"default"
);
encMethod
=
settings
.
getString
(
"encMethod"
,
"table"
);
try
{
remotePort
=
Integer
.
valueOf
(
settings
.
getString
(
"remotePort"
,
"1984"
));
}
catch
(
NumberFormatException
ex
)
{
remotePort
=
1984
;
}
try
{
localPort
=
Integer
.
valueOf
(
settings
.
getString
(
"port"
,
"1984"
));
}
catch
(
NumberFormatException
ex
)
{
localPort
=
1984
;
}
isGlobalProxy
=
settings
.
getBoolean
(
"isGlobalProxy"
,
false
);
isGFWList
=
settings
.
getBoolean
(
"isGFWList"
,
false
);
isBypassApps
=
settings
.
getBoolean
(
"isBypassApps"
,
false
);
isDNSProxy
=
settings
.
getBoolean
(
"isDNSProxy"
,
false
);
isHTTPProxy
=
settings
.
getBoolean
(
"isHTTPProxy"
,
false
);
appHost
=
settings
.
getString
(
"proxy"
,
"127.0.0.1"
);
sitekey
=
settings
.
getString
(
"sitekey"
,
"default"
);
encMethod
=
settings
.
getString
(
"encMethod"
,
"table"
);
try
{
remotePort
=
Integer
.
valueOf
(
settings
.
getString
(
"remotePort"
,
"1984"
));
}
catch
(
NumberFormatException
ex
)
{
remotePort
=
1984
;
}
try
{
localPort
=
Integer
.
valueOf
(
settings
.
getString
(
"port"
,
"1984"
));
}
catch
(
NumberFormatException
ex
)
{
localPort
=
1984
;
}
if
(
isHTTPProxy
)
{
localPort
--;
}
isGlobalProxy
=
settings
.
getBoolean
(
"isGlobalProxy"
,
false
);
isGFWList
=
settings
.
getBoolean
(
"isGFWList"
,
false
);
isBypassApps
=
settings
.
getBoolean
(
"isBypassApps"
,
false
);
isDNSProxy
=
settings
.
getBoolean
(
"isDNSProxy"
,
false
);
isHTTPProxy
=
settings
.
getBoolean
(
"isHTTPProxy"
,
false
);
new
Thread
(
new
Runnable
(
)
{
@Override
public
void
run
()
{
if
(
isHTTPProxy
)
{
localPort
--;
}
handler
.
sendEmptyMessage
(
MSG_CONNECT_START
);
new
Thread
(
new
Runnable
()
{
@Override
public
void
run
()
{
boolean
resolved
=
false
;
handler
.
sendEmptyMessage
(
MSG_CONNECT_START
)
;
if
(
appHost
!=
null
)
{
boolean
resolved
=
false
;
boolean
isIPv6Support
=
Utils
.
isIPv6Support
();
InetAddress
addr
=
null
;
if
(
appHost
!=
null
)
{
if
(
isIPv6Support
)
{
try
{
addr
=
Inet6Address
.
getByName
(
appHost
);
}
catch
(
UnknownHostException
ignored
)
{
addr
=
null
;
}
}
boolean
isIPv6Support
=
Utils
.
isIPv6Support
();
InetAddress
addr
=
null
;
if
(
addr
==
null
)
{
try
{
addr
=
Inet
Address
.
getByName
(
appHost
);
}
catch
(
UnknownHostException
ignored
)
{
addr
=
null
;
}
}
if
(
isIPv6Support
)
{
try
{
addr
=
Inet6
Address
.
getByName
(
appHost
);
}
catch
(
UnknownHostException
ignored
)
{
addr
=
null
;
}
}
if
(
addr
!=
null
)
{
appHost
=
addr
.
getHostAddress
();
resolved
=
true
;
}
}
if
(
addr
==
null
)
{
try
{
addr
=
InetAddress
.
getByName
(
appHost
);
}
catch
(
UnknownHostException
ignored
)
{
addr
=
null
;
}
}
Log
.
d
(
TAG
,
"IPTABLES: "
+
Utils
.
getIptables
());
if
(
addr
!=
null
)
{
appHost
=
addr
.
getHostAddress
();
resolved
=
true
;
}
}
// Test for Redirect Support
hasRedirectSupport
=
Utils
.
getHasRedirectSupport
();
Log
.
d
(
TAG
,
"IPTABLES: "
+
Utils
.
getIptables
());
if
(
resolved
&&
handleConnection
())
{
// Connection and forward successful
notifyAlert
(
getString
(
R
.
string
.
forward_success
),
getString
(
R
.
string
.
service_running
));
// Test for Redirect Support
hasRedirectSupport
=
Utils
.
getHasRedirectSupport
();
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_SUCCESS
,
500
);
if
(
resolved
&&
handleConnection
())
{
// Connection and forward successful
notifyAlert
(
getString
(
R
.
string
.
forward_success
),
getString
(
R
.
string
.
service_running
));
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_SUCCESS
,
500
);
}
else
{
// Connection or forward unsuccessful
notifyAlert
(
getString
(
R
.
string
.
forward_fail
),
getString
(
R
.
string
.
service_failed
));
}
else
{
// Connection or forward unsuccessful
notifyAlert
(
getString
(
R
.
string
.
forward_fail
),
getString
(
R
.
string
.
service_failed
));
stopSelf
();
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_FAIL
,
500
);
}
stopSelf
();
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_FAIL
,
500
);
}
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_FINISH
,
500
);
}
}).
start
();
markServiceStarted
();
}
private
void
startRedsocksDaemon
()
{
String
conf
=
String
.
format
(
REDSOCKS_CONF
,
localPort
);
String
cmd
=
String
.
format
(
"%sredsocks -p %sredsocks.pid -c %sredsocks.conf"
,
BASE
,
BASE
,
BASE
);
Utils
.
runRootCommand
(
"echo \""
+
conf
+
"\" > "
+
BASE
+
"redsocks.conf\n"
+
cmd
);
}
private
boolean
waitForProcess
(
final
String
name
)
{
final
int
pid
=
getPid
(
name
);
if
(
pid
==
-
1
)
return
false
;
Exec
.
hangupProcessGroup
(-
pid
);
Thread
t
=
new
Thread
()
{
@Override
public
void
run
()
{
Exec
.
waitFor
(-
pid
);
Log
.
d
(
TAG
,
"Successfully exit pid: "
+
pid
);
}
};
t
.
start
();
try
{
t
.
join
(
300
);
}
catch
(
InterruptedException
ignored
)
{
}
return
!
t
.
isAlive
();
}
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_FINISH
,
500
);
/** Called when the activity is first created. */
public
boolean
handleConnection
()
{
}
}).
start
();
markServiceStarted
();
if
(
isHTTPProxy
)
{
startPolipoDaemon
();
}
private
void
startRedsocksDaemon
()
{
String
conf
=
String
.
format
(
REDSOCKS_CONF
,
localPort
);
String
cmd
=
String
.
format
(
"%sredsocks -p %sredsocks.pid -c %sredsocks.conf"
,
BASE
,
BASE
,
BASE
);
Utils
.
runRootCommand
(
"echo \""
+
conf
+
"\" > "
+
BASE
+
"redsocks.conf\n"
+
cmd
);
startShadowsocksDaemon
();
startDnsDaemon
();
startRedsocksDaemon
();
setupIptables
();
return
true
;
}
private
void
initSoundVibrateLights
(
Notification
notification
)
{
notification
.
sound
=
null
;
notification
.
defaults
|=
Notification
.
DEFAULT_LIGHTS
;
}
void
invokeMethod
(
Method
method
,
Object
[]
args
)
{
try
{
method
.
invoke
(
this
,
mStartForegroundArgs
);
}
catch
(
InvocationTargetException
e
)
{
// Should not happen.
Log
.
w
(
TAG
,
"Unable to invoke method"
,
e
);
}
catch
(
IllegalAccessException
e
)
{
// Should not happen.
Log
.
w
(
TAG
,
"Unable to invoke method"
,
e
);
}
}
private
void
markServiceStarted
()
{
sRunningInstance
=
new
WeakReference
<
ShadowsocksService
>(
this
);
}
private
void
markServiceStopped
()
{
sRunningInstance
=
null
;
}
private
void
notifyAlert
(
String
title
,
String
info
)
{
notification
.
icon
=
R
.
drawable
.
ic_stat_shadowsocks
;
notification
.
tickerText
=
title
;
notification
.
flags
=
Notification
.
FLAG_ONGOING_EVENT
;
initSoundVibrateLights
(
notification
);
// notification.defaults = Notification.DEFAULT_SOUND;
notification
.
setLatestEventInfo
(
this
,
getString
(
R
.
string
.
app_name
),
info
,
pendIntent
);
startForegroundCompat
(
1
,
notification
);
}
private
void
notifyAlert
(
String
title
,
String
info
,
int
flags
)
{
notification
.
icon
=
R
.
drawable
.
ic_stat_shadowsocks
;
notification
.
tickerText
=
title
;
notification
.
flags
=
flags
;
initSoundVibrateLights
(
notification
);
notification
.
setLatestEventInfo
(
this
,
getString
(
R
.
string
.
app_name
),
info
,
pendIntent
);
notificationManager
.
notify
(
0
,
notification
);
}
@Override
public
IBinder
onBind
(
Intent
intent
)
{
return
null
;
}
@Override
public
void
onCreate
()
{
super
.
onCreate
();
EasyTracker
.
getTracker
().
trackEvent
(
"service"
,
"start"
,
getVersionName
(),
0L
);
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
notificationManager
=
(
NotificationManager
)
this
.
getSystemService
(
NOTIFICATION_SERVICE
);
Intent
intent
=
new
Intent
(
this
,
Shadowsocks
.
class
);
intent
.
setFlags
(
Intent
.
FLAG_ACTIVITY_CLEAR_TOP
);
pendIntent
=
PendingIntent
.
getActivity
(
this
,
0
,
intent
,
0
);
notification
=
new
Notification
();
try
{
mStartForeground
=
getClass
().
getMethod
(
"startForeground"
,
mStartForegroundSignature
);
mStopForeground
=
getClass
().
getMethod
(
"stopForeground"
,
mStopForegroundSignature
);
}
catch
(
NoSuchMethodException
e
)
{
// Running on an older platform.
mStartForeground
=
mStopForeground
=
null
;
}
private
boolean
waitForProcess
(
final
String
name
)
{
final
int
pid
=
getPid
(
name
);
if
(
pid
==
-
1
)
return
false
;
Exec
.
hangupProcessGroup
(-
pid
);
Thread
t
=
new
Thread
()
{
@Override
public
void
run
()
{
Exec
.
waitFor
(-
pid
);
Log
.
d
(
TAG
,
"Successfully exit pid: "
+
pid
);
}
};
t
.
start
();
try
{
t
.
join
(
300
);
}
catch
(
InterruptedException
ignored
)
{
}
return
!
t
.
isAlive
();
try
{
mSetForeground
=
getClass
().
getMethod
(
"setForeground"
,
mSetForegroundSignature
);
}
catch
(
NoSuchMethodException
e
)
{
throw
new
IllegalStateException
(
"OS doesn't have Service.startForeground OR Service.setForeground!"
);
}
}
/**
* Called when the activity is first created.
*/
public
boolean
handleConnection
()
{
/** Called when the activity is closed. */
@Override
public
void
onDestroy
()
{
if
(
isHTTPProxy
)
{
startPolipoDaemon
();
}
startShadowsocksDaemon
();
startDnsDaemon
();
startRedsocksDaemon
();
setupIptables
();
EasyTracker
.
getTracker
().
trackEvent
(
"service"
,
"stop"
,
getVersionName
(),
0L
);
return
true
;
}
stopForegroundCompat
(
1
);
private
void
initSoundVibrateLights
(
Notification
notification
)
{
notification
.
sound
=
null
;
notification
.
defaults
|=
Notification
.
DEFAULT_LIGHTS
;
}
notifyAlert
(
getString
(
R
.
string
.
forward_stop
),
getString
(
R
.
string
.
service_stopped
),
Notification
.
FLAG_AUTO_CANCEL
);
void
invokeMethod
(
Method
method
,
Object
[]
args
)
{
try
{
method
.
invoke
(
this
,
mStartForegroundArgs
);
}
catch
(
InvocationTargetException
e
)
{
// Should not happen.
Log
.
w
(
TAG
,
"Unable to invoke method"
,
e
);
}
catch
(
IllegalAccessException
e
)
{
// Should not happen.
Log
.
w
(
TAG
,
"Unable to invoke method"
,
e
);
}
}
new
Thread
()
{
@Override
public
void
run
()
{
// Make sure the connection is closed, important here
onDisconnect
();
}
}.
start
();
private
void
markServiceStarted
()
{
sRunningInstance
=
new
WeakReference
<
ShadowsocksService
>(
this
);
}
Editor
ed
=
settings
.
edit
();
ed
.
putBoolean
(
"isRunning"
,
false
);
ed
.
putBoolean
(
"isConnecting"
,
false
);
ed
.
commit
();
private
void
markServiceStopped
()
{
sRunningInstance
=
null
;
try
{
notificationManager
.
cancel
(
0
);
}
catch
(
Exception
ignore
)
{
// Nothing
}
private
void
notifyAlert
(
String
title
,
String
info
)
{
notification
.
icon
=
R
.
drawable
.
ic_stat_shadowsocks
;
notification
.
tickerText
=
title
;
notification
.
flags
=
Notification
.
FLAG_ONGOING_EVENT
;
initSoundVibrateLights
(
notification
);
// notification.defaults = Notification.DEFAULT_SOUND;
notification
.
setLatestEventInfo
(
this
,
getString
(
R
.
string
.
app_name
),
info
,
pendIntent
);
startForegroundCompat
(
1
,
notification
);
}
super
.
onDestroy
();
private
void
notifyAlert
(
String
title
,
String
info
,
int
flags
)
{
notification
.
icon
=
R
.
drawable
.
ic_stat_shadowsocks
;
notification
.
tickerText
=
title
;
notification
.
flags
=
flags
;
initSoundVibrateLights
(
notification
);
notification
.
setLatestEventInfo
(
this
,
getString
(
R
.
string
.
app_name
),
info
,
pendIntent
);
notificationManager
.
notify
(
0
,
notification
);
}
markServiceStopped
();
}
@Override
public
IBinder
onBind
(
Intent
intent
)
{
return
null
;
private
void
onDisconnect
()
{
Utils
.
runRootCommand
(
Utils
.
getIptables
()
+
" -t nat -F OUTPUT"
);
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`"
).
append
(
"\n"
);
if
(!
waitForProcess
(
"pdnsd"
))
{
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/pdnsd.pid`"
).
append
(
"\n"
);
}
@Override
public
void
onCreate
()
{
super
.
onCreate
();
EasyTracker
.
getTracker
().
trackEvent
(
"service"
,
"start"
,
getVersionName
(),
0L
);
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
notificationManager
=
(
NotificationManager
)
this
.
getSystemService
(
NOTIFICATION_SERVICE
);
Intent
intent
=
new
Intent
(
this
,
Shadowsocks
.
class
);
intent
.
setFlags
(
Intent
.
FLAG_ACTIVITY_CLEAR_TOP
);
pendIntent
=
PendingIntent
.
getActivity
(
this
,
0
,
intent
,
0
);
notification
=
new
Notification
();
try
{
mStartForeground
=
getClass
().
getMethod
(
"startForeground"
,
mStartForegroundSignature
);
mStopForeground
=
getClass
().
getMethod
(
"stopForeground"
,
mStopForegroundSignature
);
}
catch
(
NoSuchMethodException
e
)
{
// Running on an older platform.
mStartForeground
=
mStopForeground
=
null
;
}
try
{
mSetForeground
=
getClass
().
getMethod
(
"setForeground"
,
mSetForegroundSignature
);
}
catch
(
NoSuchMethodException
e
)
{
throw
new
IllegalStateException
(
"OS doesn't have Service.startForeground OR Service.setForeground!"
);
}
if
(!
waitForProcess
(
"shadowsocks"
))
{
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/shadowsocks.pid`"
).
append
(
"\n"
);
}
if
(
isHTTPProxy
)
{
if
(!
waitForProcess
(
"polipo"
))
{
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/polipo.pid`"
).
append
(
"\n"
);
}
}
Utils
.
runRootCommand
(
sb
.
toString
());
}
/**
* Called when the activity is closed.
*/
@Override
public
void
onDestroy
(
)
{
// This is the old onStart method that will be called on the pre-2.0
// platform. On 2.0 or later we override onStartCommand() so this
// method will not be called.
@Override
public
void
onStart
(
Intent
intent
,
int
startId
)
{
EasyTracker
.
getTracker
().
trackEvent
(
"service"
,
"stop"
,
getVersionName
(),
0L
);
handleCommand
(
intent
);
}
stopForegroundCompat
(
1
);
@Override
public
int
onStartCommand
(
Intent
intent
,
int
flags
,
int
startId
)
{
handleCommand
(
intent
);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return
START_STICKY
;
}
notifyAlert
(
getString
(
R
.
string
.
forward_stop
),
getString
(
R
.
string
.
service_stopped
),
Notification
.
FLAG_AUTO_CANCEL
);
private
boolean
setupIptables
()
{
new
Thread
()
{
@Override
public
void
run
()
{
// Make sure the connection is closed, important here
onDisconnect
();
}
}.
start
();
StringBuilder
init_sb
=
new
StringBuilder
();
Editor
ed
=
settings
.
edit
();
ed
.
putBoolean
(
"isRunning"
,
false
);
ed
.
putBoolean
(
"isConnecting"
,
false
);
ed
.
commit
();
StringBuilder
http_sb
=
new
StringBuilder
();
try
{
notificationManager
.
cancel
(
0
);
}
catch
(
Exception
ignore
)
{
// Nothing
}
init_sb
.
append
(
Utils
.
getIptables
()).
append
(
" -t nat -F OUTPUT\n"
);
super
.
onDestroy
()
;
String
cmd_bypass
=
Utils
.
getIptables
()
+
CMD_IPTABLES_RETURN
;
markServiceStopped
();
if
(!
InetAddressUtils
.
isIPv6Address
(
appHost
.
toUpperCase
()))
{
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"-d "
+
appHost
));
}
private
void
onDisconnect
()
{
Utils
.
runRootCommand
(
Utils
.
getIptables
()
+
" -t nat -F OUTPUT"
);
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`"
).
append
(
"\n"
);
if
(!
waitForProcess
(
"pdnsd"
))
{
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/pdnsd.pid`"
).
append
(
"\n"
);
}
if
(!
waitForProcess
(
"shadowsocks"
))
{
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/shadowsocks.pid`"
).
append
(
"\n"
);
}
if
(
isHTTPProxy
)
{
if
(!
waitForProcess
(
"polipo"
))
{
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/polipo.pid`"
).
append
(
"\n"
);
}
}
Utils
.
runRootCommand
(
sb
.
toString
());
init_sb
.
append
(
cmd_bypass
.
replace
(
"0.0.0.0"
,
"127.0.0.1"
));
if
(!
isDNSProxy
)
{
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"--dport "
+
53
));
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"-m owner --uid-owner "
+
getApplicationInfo
().
uid
));
}
// This is the old onStart method that will be called on the pre-2.0
// platform. On 2.0 or later we override onStartCommand() so this
// method will not be called.
@Override
public
void
onStart
(
Intent
intent
,
int
startId
)
{
if
(
hasRedirectSupport
)
{
init_sb
.
append
(
Utils
.
getIptables
())
.
append
(
" -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to "
)
.
append
(
DNS_PORT
)
.
append
(
"\n"
);
}
else
{
init_sb
.
append
(
Utils
.
getIptables
())
.
append
(
" -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:"
)
.
append
(
DNS_PORT
)
.
append
(
"\n"
);
}
handleCommand
(
intent
);
if
(
isGFWList
)
{
String
[]
chn_list
=
getResources
().
getStringArray
(
R
.
array
.
chn_list
);
for
(
String
item
:
chn_list
)
{
init_sb
.
append
(
cmd_bypass
.
replace
(
"0.0.0.0"
,
item
));
}
}
@Override
public
int
onStartCommand
(
Intent
intent
,
int
flags
,
int
startId
)
{
handleCommand
(
intent
);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return
START_STICKY
;
if
(
isGlobalProxy
||
isBypassApps
)
{
http_sb
.
append
(
hasRedirectSupport
?
Utils
.
getIptables
()
+
CMD_IPTABLES_REDIRECT_ADD_SOCKS
:
Utils
.
getIptables
()
+
CMD_IPTABLES_DNAT_ADD_SOCKS
);
}
private
boolean
setupIptables
()
{
StringBuilder
init_sb
=
new
StringBuilder
();
StringBuilder
http_sb
=
new
StringBuilder
();
init_sb
.
append
(
Utils
.
getIptables
()).
append
(
" -t nat -F OUTPUT\n"
);
String
cmd_bypass
=
Utils
.
getIptables
()
+
CMD_IPTABLES_RETURN
;
if
(!
InetAddressUtils
.
isIPv6Address
(
appHost
.
toUpperCase
()))
{
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"-d "
+
appHost
));
}
init_sb
.
append
(
cmd_bypass
.
replace
(
"0.0.0.0"
,
"127.0.0.1"
));
if
(!
isDNSProxy
)
{
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"--dport "
+
53
));
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"-m owner --uid-owner "
+
getApplicationInfo
().
uid
));
if
(!
isGlobalProxy
)
{
// for proxy specified apps
if
(
apps
==
null
||
apps
.
length
<=
0
)
apps
=
AppManager
.
getProxyedApps
(
this
);
HashSet
<
Integer
>
uidSet
=
new
HashSet
<
Integer
>();
for
(
ProxyedApp
app
:
apps
)
{
if
(
app
.
isProxyed
())
{
uidSet
.
add
(
app
.
getUid
());
}
if
(
hasRedirectSupport
)
{
init_sb
.
append
(
Utils
.
getIptables
()).
append
(
" -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to "
).
append
(
DNS_PORT
).
append
(
"\n"
);
}
for
(
int
uid
:
uidSet
)
{
if
(!
isBypassApps
)
{
http_sb
.
append
((
hasRedirectSupport
?
Utils
.
getIptables
()
+
CMD_IPTABLES_REDIRECT_ADD_SOCKS
:
Utils
.
getIptables
()
+
CMD_IPTABLES_DNAT_ADD_SOCKS
).
replace
(
"-t nat"
,
"-t nat -m owner --uid-owner "
+
uid
));
}
else
{
init_sb
.
append
(
Utils
.
getIptables
()).
append
(
" -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:"
).
append
(
DNS_PORT
).
append
(
"\n"
);
}
if
(
isGFWList
)
{
String
[]
chn_list
=
getResources
().
getStringArray
(
R
.
array
.
chn_list
);
for
(
String
item
:
chn_list
)
{
init_sb
.
append
(
cmd_bypass
.
replace
(
"0.0.0.0"
,
item
));
}
}
if
(
isGlobalProxy
||
isBypassApps
)
{
http_sb
.
append
(
hasRedirectSupport
?
Utils
.
getIptables
()
+
CMD_IPTABLES_REDIRECT_ADD_SOCKS
:
Utils
.
getIptables
()
+
CMD_IPTABLES_DNAT_ADD_SOCKS
);
}
if
(!
isGlobalProxy
)
{
// for proxy specified apps
if
(
apps
==
null
||
apps
.
length
<=
0
)
apps
=
AppManager
.
getProxyedApps
(
this
);
HashSet
<
Integer
>
uidSet
=
new
HashSet
<
Integer
>();
for
(
ProxyedApp
app
:
apps
)
{
if
(
app
.
isProxyed
())
{
uidSet
.
add
(
app
.
getUid
());
}
}
for
(
int
uid
:
uidSet
)
{
if
(!
isBypassApps
)
{
http_sb
.
append
((
hasRedirectSupport
?
Utils
.
getIptables
()
+
CMD_IPTABLES_REDIRECT_ADD_SOCKS
:
Utils
.
getIptables
()
+
CMD_IPTABLES_DNAT_ADD_SOCKS
).
replace
(
"-t nat"
,
"-t nat -m owner --uid-owner "
+
uid
));
}
else
{
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"-m owner --uid-owner "
+
uid
));
}
}
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"-m owner --uid-owner "
+
uid
));
}
}
}
String
init_rules
=
init_sb
.
toString
();
Utils
.
runRootCommand
(
init_rules
,
30
*
1000
);
String
init_rules
=
init_sb
.
toString
();
Utils
.
runRootCommand
(
init_rules
,
30
*
1000
);
String
redt_rules
=
http_sb
.
toString
();
String
redt_rules
=
http_sb
.
toString
();
Utils
.
runRootCommand
(
redt_rules
);
Utils
.
runRootCommand
(
redt_rules
);
return
true
;
return
true
;
}
/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
void
startForegroundCompat
(
int
id
,
Notification
notification
)
{
// If we have the new startForeground API, then use it.
if
(
mStartForeground
!=
null
)
{
mStartForegroundArgs
[
0
]
=
id
;
mStartForegroundArgs
[
1
]
=
notification
;
invokeMethod
(
mStartForeground
,
mStartForegroundArgs
);
return
;
}
/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
void
startForegroundCompat
(
int
id
,
Notification
notification
)
{
// If we have the new startForeground API, then use it.
if
(
mStartForeground
!=
null
)
{
mStartForegroundArgs
[
0
]
=
id
;
mStartForegroundArgs
[
1
]
=
notification
;
invokeMethod
(
mStartForeground
,
mStartForegroundArgs
);
return
;
}
// Fall back on the old API.
mSetForegroundArgs
[
0
]
=
Boolean
.
TRUE
;
invokeMethod
(
mSetForeground
,
mSetForegroundArgs
);
notificationManager
.
notify
(
id
,
notification
);
}
/**
* This is a wrapper around the new stopForeground method, using the older
* APIs if it is not available.
*/
void
stopForegroundCompat
(
int
id
)
{
// If we have the new stopForeground API, then use it.
if
(
mStopForeground
!=
null
)
{
mStopForegroundArgs
[
0
]
=
Boolean
.
TRUE
;
try
{
mStopForeground
.
invoke
(
this
,
mStopForegroundArgs
);
}
catch
(
InvocationTargetException
e
)
{
// Should not happen.
Log
.
w
(
TAG
,
"Unable to invoke stopForeground"
,
e
);
}
catch
(
IllegalAccessException
e
)
{
// Should not happen.
Log
.
w
(
TAG
,
"Unable to invoke stopForeground"
,
e
);
}
return
;
}
// Fall back on the old API. Note to cancel BEFORE changing the
// foreground state, since we could be killed at that point.
notificationManager
.
cancel
(
id
);
mSetForegroundArgs
[
0
]
=
Boolean
.
FALSE
;
invokeMethod
(
mSetForeground
,
mSetForegroundArgs
);
// Fall back on the old API.
mSetForegroundArgs
[
0
]
=
Boolean
.
TRUE
;
invokeMethod
(
mSetForeground
,
mSetForegroundArgs
);
notificationManager
.
notify
(
id
,
notification
);
}
/**
* This is a wrapper around the new stopForeground method, using the older
* APIs if it is not available.
*/
void
stopForegroundCompat
(
int
id
)
{
// If we have the new stopForeground API, then use it.
if
(
mStopForeground
!=
null
)
{
mStopForegroundArgs
[
0
]
=
Boolean
.
TRUE
;
try
{
mStopForeground
.
invoke
(
this
,
mStopForegroundArgs
);
}
catch
(
InvocationTargetException
e
)
{
// Should not happen.
Log
.
w
(
TAG
,
"Unable to invoke stopForeground"
,
e
);
}
catch
(
IllegalAccessException
e
)
{
// Should not happen.
Log
.
w
(
TAG
,
"Unable to invoke stopForeground"
,
e
);
}
return
;
}
// Fall back on the old API. Note to cancel BEFORE changing the
// foreground state, since we could be killed at that point.
notificationManager
.
cancel
(
id
);
mSetForegroundArgs
[
0
]
=
Boolean
.
FALSE
;
invokeMethod
(
mSetForeground
,
mSetForegroundArgs
);
}
}
src/com/github/shadowsocks/System.java
View file @
3d664377
...
...
@@ -39,9 +39,9 @@
package
com.github.shadowsocks
;
public
class
System
{
static
{
java
.
lang
.
System
.
loadLibrary
(
"system"
);
}
static
{
java
.
lang
.
System
.
loadLibrary
(
"system"
);
}
public
static
native
void
exec
(
String
cmd
);
public
static
native
void
exec
(
String
cmd
);
}
src/com/github/shadowsocks/Utils.java
View file @
3d664377
...
...
@@ -45,471 +45,451 @@ import android.content.pm.PackageManager.NameNotFoundException;
import
android.graphics.drawable.Drawable
;
import
android.os.Environment
;
import
android.util.Log
;
import
org.apache.http.conn.util.InetAddressUtils
;
import
java.io.*
;
import
java.io.BufferedReader
;
import
java.io.File
;
import
java.io.FileDescriptor
;
import
java.io.FileInputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
import
java.net.InetAddress
;
import
java.net.NetworkInterface
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
org.apache.http.conn.util.InetAddressUtils
;
public
class
Utils
{
public
final
static
String
TAG
=
"Shadowsocks"
;
public
final
static
String
ABI_PROP
=
"ro.product.cpu.abi"
;
public
final
static
String
ABI2_PROP
=
"ro.product.cpu.abi2"
;
public
final
static
String
ARM_ABI
=
"arm"
;
public
final
static
String
X86_ABI
=
"x86"
;
public
final
static
String
MIPS_ABI
=
"mips"
;
public
final
static
String
DEFAULT_SHELL
=
"/system/bin/sh"
;
public
final
static
String
DEFAULT_ROOT
=
"/system/bin/su"
;
public
final
static
String
ALTERNATIVE_ROOT
=
"/system/xbin/su"
;
public
final
static
String
DEFAULT_IPTABLES
=
"/data/data/com.github.shadowsocks/iptables"
;
public
final
static
String
ALTERNATIVE_IPTABLES
=
"/system/bin/iptables"
;
public
final
static
int
TIME_OUT
=
-
99
;
private
static
boolean
initialized
=
false
;
private
static
int
hasRedirectSupport
=
-
1
;
private
static
int
isRoot
=
-
1
;
private
static
String
shell
=
null
;
private
static
String
root_shell
=
null
;
private
static
String
iptables
=
null
;
private
static
String
data_path
=
null
;
/**
* Get IP address from first non-localhost interface
*/
public
static
boolean
isIPv6Support
()
{
try
{
List
<
NetworkInterface
>
interfaces
=
Collections
.
list
(
NetworkInterface
.
getNetworkInterfaces
());
for
(
NetworkInterface
intf
:
interfaces
)
{
List
<
InetAddress
>
addrs
=
Collections
.
list
(
intf
.
getInetAddresses
());
for
(
InetAddress
addr
:
addrs
)
{
if
(!
addr
.
isLoopbackAddress
()
&&
!
addr
.
isLinkLocalAddress
())
{
String
sAddr
=
addr
.
getHostAddress
().
toUpperCase
();
boolean
isIPv6
=
InetAddressUtils
.
isIPv6Address
(
sAddr
);
if
(
isIPv6
)
{
Log
.
d
(
TAG
,
"IPv6 address detected"
);
return
true
;
}
}
}
}
}
catch
(
Exception
ex
)
{
// Ignore
}
// for now eat exceptions
return
false
;
}
public
static
String
getABI
()
{
String
abi
=
getSystemProperty
(
ABI_PROP
);
if
(
abi
!=
null
)
{
if
(
abi
.
toLowerCase
().
contains
(
ARM_ABI
))
{
return
ARM_ABI
;
}
else
if
(
abi
.
toLowerCase
().
contains
(
X86_ABI
))
{
return
X86_ABI
;
public
final
static
String
TAG
=
"Shadowsocks"
;
public
final
static
String
ABI_PROP
=
"ro.product.cpu.abi"
;
public
final
static
String
ABI2_PROP
=
"ro.product.cpu.abi2"
;
public
final
static
String
ARM_ABI
=
"arm"
;
public
final
static
String
X86_ABI
=
"x86"
;
public
final
static
String
MIPS_ABI
=
"mips"
;
public
final
static
String
DEFAULT_SHELL
=
"/system/bin/sh"
;
public
final
static
String
DEFAULT_ROOT
=
"/system/bin/su"
;
public
final
static
String
ALTERNATIVE_ROOT
=
"/system/xbin/su"
;
public
final
static
String
DEFAULT_IPTABLES
=
"/data/data/com.github.shadowsocks/iptables"
;
public
final
static
String
ALTERNATIVE_IPTABLES
=
"/system/bin/iptables"
;
public
final
static
int
TIME_OUT
=
-
99
;
private
static
boolean
initialized
=
false
;
private
static
int
hasRedirectSupport
=
-
1
;
private
static
int
isRoot
=
-
1
;
private
static
String
shell
=
null
;
private
static
String
root_shell
=
null
;
private
static
String
iptables
=
null
;
private
static
String
data_path
=
null
;
/** Get IP address from first non-localhost interface */
public
static
boolean
isIPv6Support
()
{
try
{
List
<
NetworkInterface
>
interfaces
=
Collections
.
list
(
NetworkInterface
.
getNetworkInterfaces
());
for
(
NetworkInterface
intf
:
interfaces
)
{
List
<
InetAddress
>
addrs
=
Collections
.
list
(
intf
.
getInetAddresses
());
for
(
InetAddress
addr
:
addrs
)
{
if
(!
addr
.
isLoopbackAddress
()
&&
!
addr
.
isLinkLocalAddress
())
{
String
sAddr
=
addr
.
getHostAddress
().
toUpperCase
();
boolean
isIPv6
=
InetAddressUtils
.
isIPv6Address
(
sAddr
);
if
(
isIPv6
)
{
Log
.
d
(
TAG
,
"IPv6 address detected"
);
return
true
;
}
}
}
}
}
catch
(
Exception
ex
)
{
// Ignore
}
// for now eat exceptions
return
false
;
}
public
static
String
getABI
()
{
String
abi
=
getSystemProperty
(
ABI_PROP
);
if
(
abi
!=
null
)
{
if
(
abi
.
toLowerCase
().
contains
(
ARM_ABI
))
{
return
ARM_ABI
;
}
else
if
(
abi
.
toLowerCase
().
contains
(
X86_ABI
))
{
return
X86_ABI
;
}
}
/**
* Returns a SystemProperty
*
* @param propName The Property to retrieve
* @return The Property, or NULL if not found
*/
private
static
String
getSystemProperty
(
String
propName
)
{
String
line
;
BufferedReader
input
=
null
;
return
ARM_ABI
;
}
/**
* Returns a SystemProperty
*
* @param propName The Property to retrieve
* @return The Property, or NULL if not found
*/
private
static
String
getSystemProperty
(
String
propName
)
{
String
line
;
BufferedReader
input
=
null
;
try
{
Process
p
=
Runtime
.
getRuntime
().
exec
(
"getprop "
+
propName
);
input
=
new
BufferedReader
(
new
InputStreamReader
(
p
.
getInputStream
()),
1024
);
line
=
input
.
readLine
();
input
.
close
();
}
catch
(
IOException
ex
)
{
Log
.
e
(
TAG
,
"Unable to read sysprop "
+
propName
,
ex
);
return
null
;
}
finally
{
if
(
input
!=
null
)
{
try
{
Process
p
=
Runtime
.
getRuntime
().
exec
(
"getprop "
+
propName
);
input
=
new
BufferedReader
(
new
InputStreamReader
(
p
.
getInputStream
()),
1024
);
line
=
input
.
readLine
();
input
.
close
();
}
catch
(
IOException
ex
)
{
Log
.
e
(
TAG
,
"Unable to read sysprop "
+
propName
,
ex
);
return
null
;
}
finally
{
if
(
input
!=
null
)
{
try
{
input
.
close
();
}
catch
(
IOException
e
)
{
Log
.
e
(
TAG
,
"Exception while closing InputStream"
,
e
);
}
}
input
.
close
();
}
catch
(
IOException
e
)
{
Log
.
e
(
TAG
,
"Exception while closing InputStream"
,
e
);
}
return
line
;
}
}
return
line
;
}
private
static
void
checkIptables
()
{
if
(!
isRoot
())
{
iptables
=
DEFAULT_IPTABLES
;
return
;
}
private
static
void
checkIptables
()
{
// Check iptables binary
iptables
=
DEFAULT_IPTABLES
;
if
(!
isRoot
())
{
iptables
=
DEFAULT_IPTABLES
;
return
;
}
String
lines
=
null
;
// Check iptables binary
iptables
=
DEFAULT_IPTABLES
;
boolean
compatible
=
false
;
boolean
version
=
false
;
String
lines
=
null
;
StringBuilder
sb
=
new
StringBuilder
();
String
command
=
iptables
+
" --version\n"
+
iptables
+
" -L -t nat -n\n"
+
"exit\n"
;
boolean
compatible
=
false
;
boolean
version
=
false
;
int
exitcode
=
runScript
(
command
,
sb
,
10
*
1000
,
true
);
StringBuilder
sb
=
new
StringBuilder
();
String
command
=
iptables
+
" --version\n"
+
iptables
+
" -L -t nat -n\n"
+
"exit\n"
;
if
(
exitcode
==
TIME_OUT
)
return
;
int
exitcode
=
runScript
(
command
,
sb
,
10
*
1000
,
true
);
lines
=
sb
.
toString
()
;
if
(
exitcode
==
TIME_OUT
)
return
;
if
(
lines
.
contains
(
"OUTPUT"
))
{
compatible
=
true
;
}
if
(
lines
.
contains
(
"v1.4."
))
{
version
=
true
;
}
lines
=
sb
.
toString
();
if
(!
compatible
||
!
version
)
{
iptables
=
ALTERNATIVE_IPTABLES
;
if
(!
new
File
(
iptables
).
exists
())
iptables
=
"iptables"
;
}
if
(
lines
.
contains
(
"OUTPUT"
))
{
compatible
=
true
;
}
if
(
lines
.
contains
(
"v1.4."
))
{
version
=
true
;
}
if
(!
compatible
||
!
version
)
{
iptables
=
ALTERNATIVE_IPTABLES
;
if
(!
new
File
(
iptables
).
exists
())
iptables
=
"iptables"
;
}
}
public
static
Drawable
getAppIcon
(
Context
c
,
int
uid
)
{
PackageManager
pm
=
c
.
getPackageManager
();
Drawable
appIcon
=
c
.
getResources
().
getDrawable
(
android
.
R
.
drawable
.
sym_def_app_icon
);
String
[]
packages
=
pm
.
getPackagesForUid
(
uid
);
if
(
packages
!=
null
)
{
if
(
packages
.
length
==
1
)
{
try
{
ApplicationInfo
appInfo
=
pm
.
getApplicationInfo
(
packages
[
0
],
0
);
appIcon
=
pm
.
getApplicationIcon
(
appInfo
);
}
catch
(
NameNotFoundException
e
)
{
Log
.
e
(
c
.
getPackageName
(),
"No package found matching with the uid "
+
uid
);
}
}
}
else
{
Log
.
e
(
c
.
getPackageName
(),
"Package not found for uid "
+
uid
);
}
public
static
Drawable
getAppIcon
(
Context
c
,
int
uid
)
{
PackageManager
pm
=
c
.
getPackageManager
();
Drawable
appIcon
=
c
.
getResources
().
getDrawable
(
android
.
R
.
drawable
.
sym_def_app_icon
);
String
[]
packages
=
pm
.
getPackagesForUid
(
uid
);
return
appIcon
;
if
(
packages
!=
null
)
{
if
(
packages
.
length
==
1
)
{
try
{
ApplicationInfo
appInfo
=
pm
.
getApplicationInfo
(
packages
[
0
],
0
);
appIcon
=
pm
.
getApplicationIcon
(
appInfo
);
}
catch
(
NameNotFoundException
e
)
{
Log
.
e
(
c
.
getPackageName
(),
"No package found matching with the uid "
+
uid
);
}
}
}
else
{
Log
.
e
(
c
.
getPackageName
(),
"Package not found for uid "
+
uid
);
}
public
static
String
getDataPath
(
Context
ctx
)
{
return
appIcon
;
}
if
(
data_path
==
null
)
{
public
static
String
getDataPath
(
Context
ctx
)
{
if
(
Environment
.
MEDIA_MOUNTED
.
equals
(
Environment
.
getExternalStorageState
()))
{
data_path
=
Environment
.
getExternalStorageDirectory
()
.
getAbsolutePath
();
}
else
{
data_path
=
ctx
.
getFilesDir
().
getAbsolutePath
();
}
if
(
data_path
==
null
)
{
Log
.
d
(
TAG
,
"Python Data Path: "
+
data_path
);
}
if
(
Environment
.
MEDIA_MOUNTED
.
equals
(
Environment
.
getExternalStorageState
()))
{
data_path
=
Environment
.
getExternalStorageDirectory
().
getAbsolutePath
();
}
else
{
data_path
=
ctx
.
getFilesDir
().
getAbsolutePath
();
}
return
data_path
;
Log
.
d
(
TAG
,
"Python Data Path: "
+
data_path
)
;
}
public
static
boolean
getHasRedirectSupport
()
{
if
(
hasRedirectSupport
==
-
1
)
initHasRedirectSupported
();
return
hasRedirectSupport
==
1
;
}
return
data_path
;
}
public
static
String
getIptables
()
{
if
(
iptables
==
null
)
checkIptables
();
return
iptables
;
}
public
static
boolean
getHasRedirectSupport
()
{
if
(
hasRedirectSupport
==
-
1
)
initHasRedirectSupported
();
return
hasRedirectSupport
==
1
;
}
private
static
String
getShell
()
{
if
(
shell
==
null
)
{
shell
=
DEFAULT_SHELL
;
if
(!
new
File
(
shell
).
exists
())
shell
=
"sh"
;
}
return
shell
;
public
static
String
getIptables
()
{
if
(
iptables
==
null
)
checkIptables
();
return
iptables
;
}
private
static
String
getShell
()
{
if
(
shell
==
null
)
{
shell
=
DEFAULT_SHELL
;
if
(!
new
File
(
shell
).
exists
())
shell
=
"sh"
;
}
return
shell
;
}
public
static
void
initHasRedirectSupported
()
{
public
static
void
initHasRedirectSupported
()
{
if
(!
Utils
.
isRoot
())
return
;
if
(!
Utils
.
isRoot
())
return
;
StringBuilder
sb
=
new
StringBuilder
();
String
command
=
Utils
.
getIptables
()
+
" -t nat -A OUTPUT -p udp --dport 54 -j REDIRECT --to 8154"
;
StringBuilder
sb
=
new
StringBuilder
();
String
command
=
Utils
.
getIptables
()
+
" -t nat -A OUTPUT -p udp --dport 54 -j REDIRECT --to 8154"
;
int
exitcode
=
runScript
(
command
,
sb
,
10
*
1000
,
true
);
int
exitcode
=
runScript
(
command
,
sb
,
10
*
1000
,
true
);
String
lines
=
sb
.
toString
();
String
lines
=
sb
.
toString
();
hasRedirectSupport
=
1
;
hasRedirectSupport
=
1
;
// flush the check command
Utils
.
runRootCommand
(
command
.
replace
(
"-A"
,
"-D"
));
// flush the check command
Utils
.
runRootCommand
(
command
.
replace
(
"-A"
,
"-D"
));
if
(
exitcode
==
TIME_OUT
)
return
;
if
(
exitcode
==
TIME_OUT
)
return
;
if
(
lines
.
contains
(
"No chain/target/match"
))
{
hasRedirectSupport
=
0
;
}
if
(
lines
.
contains
(
"No chain/target/match"
))
{
hasRedirectSupport
=
0
;
}
public
static
boolean
isInitialized
()
{
if
(
initialized
)
return
true
;
else
{
initialized
=
true
;
return
false
;
}
}
public
static
boolean
isInitialized
()
{
if
(
initialized
)
{
return
true
;
}
else
{
initialized
=
true
;
return
false
;
}
}
public
static
boolean
isRoot
()
{
if
(
isRoot
!=
-
1
)
return
isRoot
==
1
;
public
static
boolean
isRoot
()
{
// switch between binaries
if
(
new
File
(
DEFAULT_ROOT
).
exists
())
{
root_shell
=
DEFAULT_ROOT
;
}
else
if
(
new
File
(
ALTERNATIVE_ROOT
).
exists
())
{
root_shell
=
ALTERNATIVE_ROOT
;
}
else
{
root_shell
=
"su"
;
}
if
(
isRoot
!=
-
1
)
return
isRoot
==
1
;
String
lines
=
null
;
// switch between binaries
if
(
new
File
(
DEFAULT_ROOT
).
exists
())
{
root_shell
=
DEFAULT_ROOT
;
}
else
if
(
new
File
(
ALTERNATIVE_ROOT
).
exists
())
{
root_shell
=
ALTERNATIVE_ROOT
;
}
else
{
root_shell
=
"su"
;
}
StringBuilder
sb
=
new
StringBuilder
();
String
command
=
"ls /\n exit\n"
;
String
lines
=
null
;
int
exitcode
=
runScript
(
command
,
sb
,
10
*
1000
,
true
);
StringBuilder
sb
=
new
StringBuilder
();
String
command
=
"ls /\n exit\n"
;
if
(
exitcode
==
TIME_OUT
)
{
return
false
;
}
int
exitcode
=
runScript
(
command
,
sb
,
10
*
1000
,
true
);
lines
=
sb
.
toString
();
if
(
exitcode
==
TIME_OUT
)
{
return
false
;
}
if
(
lines
.
contains
(
"system"
))
{
isRoot
=
1
;
}
lines
=
sb
.
toString
();
return
isRoot
==
1
;
if
(
lines
.
contains
(
"system"
))
{
isRoot
=
1
;
}
public
static
boolean
runCommand
(
String
command
)
{
return
isRoot
==
1
;
}
return
runCommand
(
command
,
10
*
1000
);
public
static
boolean
runCommand
(
String
command
)
{
}
return
runCommand
(
command
,
10
*
1000
);
}
public
static
boolean
runCommand
(
String
command
,
int
timeout
)
{
public
static
boolean
runCommand
(
String
command
,
int
timeout
)
{
Log
.
d
(
TAG
,
command
);
Log
.
d
(
TAG
,
command
);
runScript
(
command
,
null
,
timeout
,
false
);
runScript
(
command
,
null
,
timeout
,
false
);
return
true
;
}
return
true
;
}
public
static
boolean
runRootCommand
(
String
command
)
{
public
static
boolean
runRootCommand
(
String
command
)
{
return
runRootCommand
(
command
,
10
*
1000
);
return
runRootCommand
(
command
,
10
*
1000
);
}
}
public
static
boolean
runRootCommand
(
String
command
,
int
timeout
)
{
public
static
boolean
runRootCommand
(
String
command
,
int
timeout
)
{
if
(!
isRoot
())
{
Log
.
e
(
TAG
,
"Cannot get ROOT permission: "
+
root_shell
);
return
false
;
}
if
(!
isRoot
())
{
Log
.
e
(
TAG
,
"Cannot get ROOT permission: "
+
root_shell
);
return
false
;
}
Log
.
d
(
TAG
,
command
);
runScript
(
command
,
null
,
timeout
,
true
);
return
true
;
}
private
synchronized
static
int
runScript
(
String
script
,
StringBuilder
result
,
long
timeout
,
boolean
asroot
)
{
final
ScriptRunner
runner
=
new
ScriptRunner
(
script
,
result
,
asroot
);
runner
.
start
();
try
{
if
(
timeout
>
0
)
{
runner
.
join
(
timeout
);
}
else
{
runner
.
join
();
}
if
(
runner
.
isAlive
())
{
// Timed-out
runner
.
destroy
();
runner
.
join
(
1000
);
return
TIME_OUT
;
}
}
catch
(
InterruptedException
ex
)
{
return
TIME_OUT
;
}
return
runner
.
exitcode
;
}
/** Internal thread used to execute scripts (as root or not). */
private
static
final
class
ScriptRunner
extends
Thread
{
private
final
String
scripts
;
private
final
StringBuilder
result
;
private
final
boolean
asroot
;
public
int
exitcode
=
-
1
;
private
int
[]
pid
=
new
int
[
1
];
private
FileDescriptor
pipe
;
@Override
public
void
destroy
()
{
if
(
pid
[
0
]
!=
-
1
)
{
Exec
.
hangupProcessGroup
(
pid
[
0
]);
pid
[
0
]
=
-
1
;
}
if
(
pipe
!=
null
)
{
Exec
.
close
(
pipe
);
pipe
=
null
;
}
}
Log
.
d
(
TAG
,
command
);
/**
* Creates a new script runner.
*
* @param script script to run
* @param result result output
* @param asroot if true, executes the script as root
*/
public
ScriptRunner
(
String
script
,
StringBuilder
result
,
boolean
asroot
)
{
this
.
scripts
=
script
;
this
.
result
=
result
;
this
.
asroot
=
asroot
;
}
runScript
(
command
,
null
,
timeout
,
true
);
private
FileDescriptor
createSubprocess
(
int
[]
processId
,
String
cmd
)
{
ArrayList
<
String
>
argList
=
parse
(
cmd
);
String
arg0
=
argList
.
get
(
0
);
String
[]
args
=
argList
.
toArray
(
new
String
[
1
]);
return
true
;
return
Exec
.
createSubprocess
(
result
!=
null
?
1
:
0
,
arg0
,
args
,
null
,
scripts
+
"\nexit\n"
,
processId
);
}
private
synchronized
static
int
runScript
(
String
script
,
StringBuilder
result
,
long
timeout
,
boolean
asroot
)
{
final
ScriptRunner
runner
=
new
ScriptRunner
(
script
,
result
,
asroot
);
runner
.
start
();
try
{
if
(
timeout
>
0
)
{
runner
.
join
(
timeout
);
}
else
{
runner
.
join
();
private
ArrayList
<
String
>
parse
(
String
cmd
)
{
final
int
PLAIN
=
0
;
final
int
WHITESPACE
=
1
;
final
int
INQUOTE
=
2
;
int
state
=
WHITESPACE
;
ArrayList
<
String
>
result
=
new
ArrayList
<
String
>();
int
cmdLen
=
cmd
.
length
();
StringBuilder
builder
=
new
StringBuilder
();
for
(
int
i
=
0
;
i
<
cmdLen
;
i
++)
{
char
c
=
cmd
.
charAt
(
i
);
if
(
state
==
PLAIN
)
{
if
(
Character
.
isWhitespace
(
c
))
{
result
.
add
(
builder
.
toString
());
builder
.
delete
(
0
,
builder
.
length
());
state
=
WHITESPACE
;
}
else
if
(
c
==
'"'
)
{
state
=
INQUOTE
;
}
else
{
builder
.
append
(
c
);
}
}
else
if
(
state
==
WHITESPACE
)
{
if
(
Character
.
isWhitespace
(
c
))
{
// do nothing
}
else
if
(
c
==
'"'
)
{
state
=
INQUOTE
;
}
else
{
state
=
PLAIN
;
builder
.
append
(
c
);
}
}
else
if
(
state
==
INQUOTE
)
{
if
(
c
==
'\\'
)
{
if
(
i
+
1
<
cmdLen
)
{
i
+=
1
;
builder
.
append
(
cmd
.
charAt
(
i
));
}
if
(
runner
.
isAlive
())
{
// Timed-out
runner
.
destroy
();
runner
.
join
(
1000
);
return
TIME_OUT
;
}
}
catch
(
InterruptedException
ex
)
{
return
TIME_OUT
;
}
else
if
(
c
==
'"'
)
{
state
=
PLAIN
;
}
else
{
builder
.
append
(
c
);
}
}
return
runner
.
exitcode
;
}
if
(
builder
.
length
()
>
0
)
{
result
.
add
(
builder
.
toString
());
}
return
result
;
}
/**
* Internal thread used to execute scripts (as root or not).
*/
private
static
final
class
ScriptRunner
extends
Thread
{
private
final
String
scripts
;
private
final
StringBuilder
result
;
private
final
boolean
asroot
;
public
int
exitcode
=
-
1
;
private
int
[]
pid
=
new
int
[
1
];
private
FileDescriptor
pipe
;
@Override
public
void
destroy
()
{
if
(
pid
[
0
]
!=
-
1
)
{
Exec
.
hangupProcessGroup
(
pid
[
0
]);
pid
[
0
]
=
-
1
;
}
if
(
pipe
!=
null
)
{
Exec
.
close
(
pipe
);
pipe
=
null
;
}
@Override
public
void
run
()
{
pid
[
0
]
=
-
1
;
try
{
if
(
this
.
asroot
)
{
// Create the "su" request to run the script
pipe
=
createSubprocess
(
pid
,
root_shell
);
}
else
{
// Create the "sh" request to run the script
pipe
=
createSubprocess
(
pid
,
getShell
());
}
/**
* Creates a new script runner.
*
* @param script script to run
* @param result result output
* @param asroot if true, executes the script as root
*/
public
ScriptRunner
(
String
script
,
StringBuilder
result
,
boolean
asroot
)
{
this
.
scripts
=
script
;
this
.
result
=
result
;
this
.
asroot
=
asroot
;
if
(
pid
[
0
]
!=
-
1
)
{
exitcode
=
Exec
.
waitFor
(
pid
[
0
]);
}
private
FileDescriptor
createSubprocess
(
int
[]
processId
,
String
cmd
)
{
ArrayList
<
String
>
argList
=
parse
(
cmd
);
String
arg0
=
argList
.
get
(
0
);
String
[]
args
=
argList
.
toArray
(
new
String
[
1
]);
if
(
result
==
null
||
pipe
==
null
)
return
;
return
Exec
.
createSubprocess
(
result
!=
null
?
1
:
0
,
arg0
,
args
,
null
,
scripts
+
"\nexit\n"
,
processId
)
;
}
final
InputStream
stdout
=
new
FileInputStream
(
pipe
);
final
byte
buf
[]
=
new
byte
[
8192
]
;
int
read
=
0
;
private
ArrayList
<
String
>
parse
(
String
cmd
)
{
final
int
PLAIN
=
0
;
final
int
WHITESPACE
=
1
;
final
int
INQUOTE
=
2
;
int
state
=
WHITESPACE
;
ArrayList
<
String
>
result
=
new
ArrayList
<
String
>();
int
cmdLen
=
cmd
.
length
();
StringBuilder
builder
=
new
StringBuilder
();
for
(
int
i
=
0
;
i
<
cmdLen
;
i
++)
{
char
c
=
cmd
.
charAt
(
i
);
if
(
state
==
PLAIN
)
{
if
(
Character
.
isWhitespace
(
c
))
{
result
.
add
(
builder
.
toString
());
builder
.
delete
(
0
,
builder
.
length
());
state
=
WHITESPACE
;
}
else
if
(
c
==
'"'
)
{
state
=
INQUOTE
;
}
else
{
builder
.
append
(
c
);
}
}
else
if
(
state
==
WHITESPACE
)
{
if
(
Character
.
isWhitespace
(
c
))
{
// do nothing
}
else
if
(
c
==
'"'
)
{
state
=
INQUOTE
;
}
else
{
state
=
PLAIN
;
builder
.
append
(
c
);
}
}
else
if
(
state
==
INQUOTE
)
{
if
(
c
==
'\\'
)
{
if
(
i
+
1
<
cmdLen
)
{
i
+=
1
;
builder
.
append
(
cmd
.
charAt
(
i
));
}
}
else
if
(
c
==
'"'
)
{
state
=
PLAIN
;
}
else
{
builder
.
append
(
c
);
}
}
}
if
(
builder
.
length
()
>
0
)
{
result
.
add
(
builder
.
toString
());
}
return
result
;
// Read stdout
while
(
stdout
.
available
()
>
0
)
{
read
=
stdout
.
read
(
buf
);
result
.
append
(
new
String
(
buf
,
0
,
read
));
}
@Override
public
void
run
()
{
pid
[
0
]
=
-
1
;
try
{
if
(
this
.
asroot
)
{
// Create the "su" request to run the script
pipe
=
createSubprocess
(
pid
,
root_shell
);
}
else
{
// Create the "sh" request to run the script
pipe
=
createSubprocess
(
pid
,
getShell
());
}
if
(
pid
[
0
]
!=
-
1
)
{
exitcode
=
Exec
.
waitFor
(
pid
[
0
]);
}
if
(
result
==
null
||
pipe
==
null
)
return
;
final
InputStream
stdout
=
new
FileInputStream
(
pipe
);
final
byte
buf
[]
=
new
byte
[
8192
];
int
read
=
0
;
// Read stdout
while
(
stdout
.
available
()
>
0
)
{
read
=
stdout
.
read
(
buf
);
result
.
append
(
new
String
(
buf
,
0
,
read
));
}
}
catch
(
Exception
ex
)
{
Log
.
e
(
TAG
,
"Cannot execute command"
,
ex
);
if
(
result
!=
null
)
result
.
append
(
"\n"
).
append
(
ex
);
}
finally
{
if
(
pipe
!=
null
)
{
Exec
.
close
(
pipe
);
}
if
(
pid
[
0
]
!=
-
1
)
{
Exec
.
hangupProcessGroup
(
pid
[
0
]);
}
}
}
catch
(
Exception
ex
)
{
Log
.
e
(
TAG
,
"Cannot execute command"
,
ex
);
if
(
result
!=
null
)
result
.
append
(
"\n"
).
append
(
ex
);
}
finally
{
if
(
pipe
!=
null
)
{
Exec
.
close
(
pipe
);
}
if
(
pid
[
0
]
!=
-
1
)
{
Exec
.
hangupProcessGroup
(
pid
[
0
]);
}
}
}
}
}
src/com/github/shadowsocks/preferences/SummaryEditTextPreference.java
View file @
3d664377
...
...
@@ -3,36 +3,31 @@ package com.github.shadowsocks.preferences;
import
android.content.Context
;
import
android.preference.EditTextPreference
;
import
android.util.AttributeSet
;
import
com.github.shadowsocks.R
;
public
class
SummaryEditTextPreference
extends
EditTextPreference
{
private
CharSequence
mDefaultSummary
;
private
CharSequence
mDefaultSummary
;
public
SummaryEditTextPreference
(
Context
context
,
AttributeSet
attrs
,
int
defStyle
)
{
super
(
context
,
attrs
,
defStyle
);
mDefaultSummary
=
getSummary
();
}
public
SummaryEditTextPreference
(
Context
context
,
AttributeSet
attrs
,
int
defStyle
)
{
super
(
context
,
attrs
,
defStyle
);
mDefaultSummary
=
getSummary
();
}
public
SummaryEditTextPreference
(
Context
context
,
AttributeSet
attrs
)
{
super
(
context
,
attrs
);
mDefaultSummary
=
getSummary
();
}
public
SummaryEditTextPreference
(
Context
context
,
AttributeSet
attrs
)
{
super
(
context
,
attrs
);
mDefaultSummary
=
getSummary
();
}
public
SummaryEditTextPreference
(
Context
context
)
{
super
(
context
);
}
public
SummaryEditTextPreference
(
Context
context
)
{
super
(
context
);
}
@Override
public
void
onDialogClosed
(
boolean
positiveResult
)
{
super
.
onDialogClosed
(
positiveResult
);
if
(
positiveResult
)
{
String
value
=
getEditText
().
getText
().
toString
();
if
(
value
.
isEmpty
())
{
setSummary
(
mDefaultSummary
);
}
else
{
setSummary
(
value
);
}
}
@Override
public
void
setSummary
(
CharSequence
summary
)
{
if
(
summary
.
toString
().
isEmpty
())
{
super
.
setSummary
(
mDefaultSummary
);
}
else
{
super
.
setSummary
(
summary
);
}
}
}
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