├── .gitignore ├── .gitmodules ├── CHANGES.md ├── LICENSE ├── Makefile ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── release │ └── app-release.apk └── src │ ├── androidTest │ └── java │ │ └── io │ │ └── github │ │ └── xSocks │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── aidl │ └── io │ │ └── github │ │ └── xSocks │ │ └── aidl │ │ ├── Config.aidl │ │ ├── IxSocksService.aidl │ │ └── IxSocksServiceCallback.aidl │ ├── assets │ ├── fonts │ │ └── Iceland.ttf │ └── iptable.txt │ ├── java │ └── io │ │ └── github │ │ └── xSocks │ │ ├── aidl │ │ └── Config.java │ │ ├── model │ │ ├── Profile.java │ │ ├── Profiles.java │ │ └── ProxiedApp.java │ │ ├── preferences │ │ ├── PasswordEditTextPreference.java │ │ ├── ProfileEditTextPreference.java │ │ ├── SummaryEditTextPreference.java │ │ └── SummaryListPreference.java │ │ ├── service │ │ └── xSocksVpnService.java │ │ ├── store │ │ └── ProfileManager.java │ │ ├── ui │ │ ├── AboutActivity.java │ │ ├── AppManagerActivity.java │ │ ├── AppManagerActivityFragment.java │ │ ├── MainActivity.java │ │ ├── PrefsFragment.java │ │ ├── ProfileDrawerItem.java │ │ └── xSocksRunnerActivity.java │ │ ├── utils │ │ ├── ConfigUtils.java │ │ ├── Console.java │ │ ├── Constants.java │ │ ├── MovementCheck.java │ │ └── Utils.java │ │ ├── xSocksApplication.java │ │ └── xSocksReceiver.java │ └── res │ ├── drawable-hdpi │ └── ic_logo.png │ ├── drawable-xhdpi │ └── ic_logo.png │ ├── layout │ ├── activity_about.xml │ ├── activity_app_manager.xml │ ├── activity_main.xml │ ├── apps_item.xml │ └── fragment_app_manager.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-v21 │ └── styles.xml │ ├── values-zh │ └── strings.xml │ ├── values │ ├── about.xml │ ├── arrays.xml │ ├── colors.xml │ ├── dimens.xml │ ├── ids.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── preferences.xml ├── build.gradle ├── gradle.properties ├── replay_pid13256.log ├── replay_pid14580.log ├── replay_pid16644.log ├── replay_pid20120.log ├── settings.gradle └── xsocks ├── build.gradle └── xsocks.aar /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.o 3 | *.a 4 | *.so 5 | .ycm_extra_conf.pyc 6 | 7 | # built application files 8 | #*.apk 9 | #*.ap_ 10 | 11 | # files for the dex VM 12 | *.dex 13 | 14 | # Java class files 15 | *.class 16 | 17 | # generated files 18 | bin/ 19 | out/ 20 | gen/ 21 | lib/ 22 | obj/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | .idea 26 | .navigation 27 | .gradle 28 | local.properties 29 | ant.properties 30 | project.properties 31 | build.xml 32 | build/ 33 | *.iml 34 | proguard-project.txt 35 | 36 | /libs 37 | /app/src/main/assets/x86/* 38 | /app/src/main/assets/armeabi-v7a/* 39 | /app/src/main/jniLibs/* 40 | /jni/x86/ 41 | /jni/armeabi-v7a/ 42 | 43 | .gradle 44 | gradle/ 45 | gradlew* 46 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "jni/xSocks"] 2 | path = jni/xSocks 3 | url = https://github.com/lparam/xSocks.git 4 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | v1.5.0 (2015-10-21) 2 | ----------- 3 | * Change: rename program 4 | 5 | 6 | v1.4.0 (2015-10-18) 7 | ----------- 8 | * Feature: Protect VpnService 9 | * Change: Black list 10 | 11 | 12 | v1.3.0 (2015-10-9) 13 | ----------- 14 | * Feature: Support ACL 15 | * Feature: Add App Manager 16 | * Change: Adjust Route policy 17 | 18 | 19 | v1.2.2 (2015-9-16) 20 | ----------- 21 | * Hotfix: crypto initialization 22 | 23 | 24 | v1.2.1 (2015-9-12) 25 | ----------- 26 | * Change: update 3rd library to latest 27 | * Change: update appcompat 28 | * Change: update xsocks's arguments 29 | 30 | 31 | v1.2.0 (2015-6-07) 32 | ----------- 33 | * Change: json instead of realm for profile store 34 | * Change: Remove libsuperuser 35 | 36 | 37 | v1.1.7 (2015-5-22) 38 | ----------- 39 | * Change: Remove snackbar action 40 | * Change: Remove redundant code and icon 41 | 42 | 43 | v1.1.6 (2015-5-20) 44 | ----------- 45 | * The first public version. 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | LIB_SYSTEM=app/src/main/jniLibs/armeabi/libsystem.so 3 | 4 | all: $(LIB_SYSTEM) 5 | 6 | .PHONY: clean 7 | 8 | clean: 9 | rm -rf libs 10 | rm -rf app/src/main/jniLibs 11 | $(ANDROID_NDK_HOME)/ndk-build clean 12 | 13 | $(LIB_SYSTEM): jni/system.cpp jni/Android.mk 14 | if [ a == a$(ANDROID_NDK_HOME) ]; then \ 15 | echo ANDROID_NDK_HOME is not set ;\ 16 | exit 1 ;\ 17 | fi ;\ 18 | pushd jni/xSocks || exit 1 ;\ 19 | dist-build/android-x86.sh || exit 1 ;\ 20 | dist-build/android-armv7-a.sh || exit 1 ;\ 21 | popd ;\ 22 | pushd jni ;\ 23 | $(ANDROID_NDK_HOME)/ndk-build NDK_LOG=1 V=0 || exit 1 ;\ 24 | popd ;\ 25 | rm -rf app/src/main/assets/x86 ;\ 26 | rm -rf app/src/main/assets/armeabi-v7a ;\ 27 | mkdir -p app/src/main/assets/x86 ;\ 28 | mkdir -p app/src/main/assets/armeabi-v7a ;\ 29 | install -d app/src/main/jniLibs/x86 ;\ 30 | install -d app/src/main/jniLibs/armeabi-v7a ;\ 31 | install libs/x86/pdnsd app/src/main/assets/x86 ;\ 32 | install libs/x86/tun2socks app/src/main/assets/x86 ;\ 33 | install libs/x86/libsystem.so app/src/main/jniLibs/x86 ;\ 34 | install libs/armeabi-v7a/pdnsd app/src/main/assets/armeabi-v7a ;\ 35 | install libs/armeabi-v7a/tun2socks app/src/main/assets/armeabi-v7a ;\ 36 | install libs/armeabi-v7a/libsystem.so app/src/main/jniLibs/armeabi-v7a ;\ 37 | install jni/xSocks/xSocks-android-i686/xSocks app/src/main/assets/x86 ;\ 38 | install jni/xSocks/xSocks-android-i686/xForwarder app/src/main/assets/x86 ;\ 39 | install jni/xSocks/xSocks-android-armv7-a/xSocks app/src/main/assets/armeabi-v7a ;\ 40 | install jni/xSocks/xSocks-android-armv7-a/xForwarder app/src/main/assets/armeabi-v7a ; 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Xsocks for Android 2 | 3 | A xSocks client for Android. 4 | 5 | Most source code sources https://github.com/lparam/xSocks. 6 | 7 | 8 | 9 | ### PREREQUISITES 10 | 11 | * JDK 1.8+ 12 | * Android SDK r23+ 13 | * Android Studio 4.0+ (optional) 14 | 15 | ### BUILD 16 | 17 | * Set environment variable `ANDROID_HOME` 18 | * Set environment variable `ANDROID_NDK_HOME` 19 | * Create your key following the instructions at http://developer.android.com/guide/publishing/app-signing.html#cert 20 | * Create your sign.gradle file like this 21 | 22 | ``` 23 | * Build native binaries 24 | ```bash 25 | git submodule update --init 26 | make 27 | ``` 28 | 29 | #### Gradle Build 30 | ```bash 31 | gradle clean assembleRelease 32 | ``` 33 | 34 | #### Android Studio 35 | * Import the project in Android Studio 36 | * Make Project in Android Studio 37 | 38 | ### LICENSE 39 | 40 | Copyright (C) 2020 dosgo 41 | 42 | This program is free software: you can redistribute it and/or modify 43 | it under the terms of the GNU General Public License as published by 44 | the Free Software Foundation, either version 3 of the License, or 45 | (at your option) any later version. 46 | 47 | This program is distributed in the hope that it will be useful, 48 | but WITHOUT ANY WARRANTY; without even the implied warranty of 49 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 50 | GNU General Public License for more details. 51 | 52 | You should have received a copy of the GNU General Public License 53 | along with this program. If not, see . 54 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | if(file('../sign.gradle').exists()) { 4 | apply from: '../sign.gradle' 5 | } else { 6 | logger.error('no such file: ../sign.gradle') 7 | } 8 | 9 | android { 10 | compileSdkVersion 23 11 | defaultConfig { 12 | applicationId "io.github.xSocks" 13 | minSdkVersion 23 14 | targetSdkVersion 23 15 | versionCode 153 16 | versionName '1.1.7' 17 | } 18 | lintOptions { 19 | abortOnError true 20 | disable 'MissingTranslation' 21 | } 22 | buildTypes { 23 | release { 24 | try { 25 | signingConfig signingConfigs.release 26 | } catch (ex) { 27 | logger.error('signing error: ' + ex) 28 | } 29 | lintOptions { 30 | checkReleaseBuilds false 31 | abortOnError false 32 | } 33 | zipAlignEnabled true 34 | minifyEnabled false 35 | shrinkResources false 36 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 37 | } 38 | applicationVariants.all { variant -> 39 | variant.outputs.each { output -> 40 | appendVersionNameVersionCode(variant, output, defaultConfig) 41 | } 42 | } 43 | } 44 | compileOptions { 45 | sourceCompatibility JavaVersion.VERSION_1_8 46 | targetCompatibility JavaVersion.VERSION_1_8 47 | } 48 | packagingOptions { 49 | exclude 'META-INF/services/javax.annotation.processing.Processor' 50 | } 51 | productFlavors { 52 | } 53 | gradle.projectsEvaluated { 54 | tasks.withType(JavaCompile) { 55 | options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" 56 | } 57 | } 58 | } 59 | 60 | def appendVersionNameVersionCode(variant, output, defaultConfig) { 61 | def versionName = defaultConfig.versionName 62 | if(variant.buildType.name == android.buildTypes.debug.name) { 63 | versionName += "-beta" 64 | } 65 | def file = output.outputFile 66 | //output.outputFile = new File(file.parent, "xSocks.apk".replace(".apk", "-v" + versionName + ".apk")) 67 | } 68 | 69 | dependencies { 70 | implementation fileTree(include: ['*.jar'], dir: 'libs') 71 | implementation 'com.android.support:design:23.1.1' 72 | implementation 'com.android.support:support-annotations:23.1.1' 73 | implementation 'io.reactivex:rxjava:1.0.14' 74 | implementation 'io.reactivex:rxandroid:1.0.1' 75 | implementation 'io.reactivex:rxjava-async-util:0.21.0' 76 | implementation 'com.trello:rxlifecycle:0.3.0' 77 | implementation 'com.trello:rxlifecycle-components:0.3.0' 78 | implementation('com.mikepenz.materialdrawer:library:2.9.7@aar') { 79 | transitive = true 80 | } 81 | implementation 'dnsjava:dnsjava:2.1.7' 82 | implementation 'com.google.code.gson:gson:2.3.1' 83 | implementation 'com.nostra13.universalimageloader:universal-image-loader:1.8.4' 84 | implementation project(":xsocks") 85 | } 86 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # retrolambda 2 | -dontwarn java.lang.invoke.* 3 | 4 | # javadns 5 | -dontwarn org.xbill.DNS.spi.* 6 | 7 | # rxjava 8 | -dontwarn rx.internal.util.unsafe.* 9 | -keep class rx.internal.util.unsafe.** { *; } 10 | 11 | # materialdrawer 12 | -dontwarn com.mikepenz.materialdrawer.* 13 | 14 | # iconics 15 | -dontwarn com.mikepenz.iconics.* 16 | 17 | # gson 18 | -keepattributes Signature 19 | -keepattributes *Annotation* 20 | -keep class sun.misc.Unsafe { *; } 21 | -keep class com.google.gson.** { *; } 22 | 23 | # universal-image-loader 24 | -dontwarn com.nostra13.universalimageloader.** 25 | -keep class com.nostra13.universalimageloader.** { *; } 26 | 27 | # xSocks 28 | -keep class xSocks.model.** { *; } -------------------------------------------------------------------------------- /app/release/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosgo/xSocks-android/691dd126afa3b57c6bb2d4bd8dc0a62979eb1a94/app/release/app-release.apk -------------------------------------------------------------------------------- /app/src/androidTest/java/io/github/xSocks/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 52 | 53 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/aidl/io/github/xSocks/aidl/Config.aidl: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.aidl; 2 | 3 | parcelable Config; 4 | -------------------------------------------------------------------------------- /app/src/main/aidl/io/github/xSocks/aidl/IxSocksService.aidl: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.aidl; 2 | 3 | import io.github.xSocks.aidl.Config; 4 | import io.github.xSocks.aidl.IxSocksServiceCallback; 5 | 6 | interface IxSocksService { 7 | int getState(); 8 | 9 | oneway void registerCallback(IxSocksServiceCallback cb); 10 | oneway void unregisterCallback(IxSocksServiceCallback cb); 11 | 12 | oneway void start(in Config config); 13 | oneway void stop(); 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/aidl/io/github/xSocks/aidl/IxSocksServiceCallback.aidl: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.aidl; 2 | 3 | interface IxSocksServiceCallback { 4 | oneway void stateChanged(int state, String msg); 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Iceland.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosgo/xSocks-android/691dd126afa3b57c6bb2d4bd8dc0a62979eb1a94/app/src/main/assets/fonts/Iceland.ttf -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/aidl/Config.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.aidl; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | public class Config implements Parcelable { 7 | 8 | public boolean isGlobalProxy = true; 9 | public boolean isBypassApps = false; 10 | public boolean isUdpDns = false; 11 | 12 | public String verifyCert="skip"; 13 | public String tunType="1"; 14 | public String profileName = "Untitled"; 15 | public String protocol="wss"; 16 | public String caFile=""; 17 | public String proxy = ""; 18 | public String sitekey = ""; 19 | public String route = "all"; 20 | 21 | public String proxiedAppString = ""; 22 | 23 | public int remotePort = 443; 24 | public int localPort = 1080; 25 | 26 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 27 | public Config createFromParcel(Parcel in) { 28 | return new Config(in); 29 | } 30 | 31 | public Config[] newArray(int size) { 32 | return new Config[size]; 33 | } 34 | }; 35 | 36 | public Config(boolean isGlobalProxy, boolean isBypassApps, 37 | boolean isUdpDns, String profileName, String proxy, String sitekey, 38 | String proxiedAppString, String route,String protocol, String caFile,String verifyCert,String tunType,int remotePort, int localPort) { 39 | this.isGlobalProxy = isGlobalProxy; 40 | this.isBypassApps = isBypassApps; 41 | this.isUdpDns = isUdpDns; 42 | this.profileName = profileName; 43 | this.proxy = proxy; 44 | this.sitekey = sitekey; 45 | this.proxiedAppString = proxiedAppString; 46 | this.route = route; 47 | this.protocol = protocol; 48 | this.caFile=caFile; 49 | this.verifyCert=verifyCert; 50 | this.tunType=tunType; 51 | this.remotePort = remotePort; 52 | this.localPort = localPort == 0 ? this.localPort : localPort; 53 | } 54 | 55 | private Config(Parcel in) { 56 | readFromParcel(in); 57 | } 58 | 59 | public void readFromParcel(Parcel in) { 60 | isGlobalProxy = in.readInt() == 1; 61 | isBypassApps = in.readInt() == 1; 62 | isUdpDns = in.readInt() == 1; 63 | verifyCert = in.readString(); 64 | tunType = in.readString(); 65 | profileName = in.readString(); 66 | proxy = in.readString(); 67 | sitekey = in.readString(); 68 | proxiedAppString = in.readString(); 69 | route = in.readString(); 70 | protocol = in.readString(); 71 | caFile = in.readString(); 72 | remotePort = in.readInt(); 73 | localPort = in.readInt(); 74 | } 75 | 76 | @Override 77 | public int describeContents() { 78 | return 0; 79 | } 80 | 81 | @Override 82 | public void writeToParcel(Parcel out, int flags) { 83 | out.writeInt(isGlobalProxy ? 1 : 0); 84 | out.writeInt(isBypassApps ? 1 : 0); 85 | out.writeInt(isUdpDns ? 1 : 0); 86 | out.writeString(profileName); 87 | out.writeString(proxy); 88 | out.writeString(sitekey); 89 | out.writeString(proxiedAppString); 90 | out.writeString(route); 91 | out.writeString(protocol); 92 | out.writeString(verifyCert); 93 | out.writeString(tunType); 94 | out.writeString(caFile); 95 | out.writeInt(remotePort); 96 | out.writeInt(localPort); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/model/Profile.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | public class Profile { 6 | @SerializedName("id") 7 | private int id = 0; 8 | @SerializedName("name") 9 | private String name = "Untitled"; 10 | @SerializedName("proxy") 11 | private String proxy = ""; 12 | @SerializedName("localPort") 13 | private int localPort = 1080; 14 | @SerializedName("remotePort") 15 | private int remotePort = 1073; 16 | @SerializedName("password") 17 | private String password = ""; 18 | @SerializedName("route") 19 | private String route = "all"; 20 | @SerializedName("protocol") 21 | private String protocol = "wss"; 22 | @SerializedName("global") 23 | private boolean global = true; 24 | @SerializedName("route_bypass") 25 | private boolean bypass = false; 26 | @SerializedName("udpdns") 27 | private boolean udpdns = false; 28 | private String individual = ""; 29 | 30 | public void setId(int id) { 31 | this.id = id; 32 | } 33 | 34 | public void setName(String name) { 35 | this.name = name; 36 | } 37 | 38 | public void setProxy(String host) { 39 | this.proxy = host; 40 | } 41 | 42 | public void setLocalPort(int localPort) { 43 | this.localPort = localPort; 44 | } 45 | 46 | public void setRemotePort(int remotePort) { 47 | this.remotePort = remotePort; 48 | } 49 | 50 | public void setPassword(String password) { 51 | this.password = password; 52 | } 53 | 54 | public void setRoute(String route) { 55 | this.route = route; 56 | } 57 | 58 | public void setGlobal(boolean global) { 59 | this.global = global; 60 | } 61 | 62 | public void setBypass(boolean bypass) { 63 | this.bypass = bypass; 64 | } 65 | 66 | public void setUdpdns(boolean udpdns) { 67 | this.udpdns = udpdns; 68 | } 69 | 70 | public int getId() { 71 | return this.id; 72 | } 73 | 74 | public String getName() { 75 | return this.name; 76 | } 77 | 78 | public String getProxy() { 79 | return this.proxy; 80 | } 81 | 82 | public int getLocalPort() { 83 | return localPort; 84 | } 85 | 86 | public int getRemotePort() { 87 | return remotePort; 88 | } 89 | 90 | public String getPassword() { 91 | return password; 92 | } 93 | 94 | public String getRoute() { 95 | return route; 96 | } 97 | public String getProtocol() { 98 | return protocol; 99 | } 100 | public void setProtocol(String protocol) { 101 | this.protocol = protocol; 102 | } 103 | 104 | public boolean isGlobal() { 105 | return global; 106 | } 107 | 108 | public boolean isBypass() { 109 | return bypass; 110 | } 111 | 112 | public boolean isUdpdns() { 113 | return udpdns; 114 | } 115 | 116 | public void setIndividual(String individual) { 117 | this.individual = individual; 118 | } 119 | 120 | public String getIndividual() { 121 | 122 | return individual; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/model/Profiles.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class Profiles { 9 | @SerializedName("profiles") 10 | private List profiles; 11 | 12 | public List getProfiles() { 13 | return profiles; 14 | } 15 | 16 | public Profiles() { 17 | profiles = new ArrayList<>(); 18 | } 19 | 20 | public void remove(int id) { 21 | for (Profile p : profiles) { 22 | if (p.getId() == id) { 23 | profiles.remove(p); 24 | break; 25 | } 26 | } 27 | } 28 | 29 | public Profile getProfile(int id) { 30 | for (Profile p : profiles) { 31 | if (p.getId() == id) { 32 | return p; 33 | } 34 | } 35 | return null; 36 | } 37 | 38 | public int getMaxId() { 39 | int max = 0; 40 | for (Profile p : profiles) { 41 | if (p.getId() > max) { 42 | max = p.getId(); 43 | } 44 | } 45 | return max; 46 | } 47 | 48 | public void addProfile(Profile profile) { 49 | profiles.add(profile); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/model/ProxiedApp.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.model; 2 | 3 | public class ProxiedApp { 4 | private int uid; 5 | private String name; 6 | private String packageName; 7 | private boolean proxied; 8 | 9 | public ProxiedApp(int uid, String name, String packageName, boolean proxied) { 10 | this.uid = uid; 11 | this.name = name; 12 | this.packageName = packageName; 13 | this.proxied = proxied; 14 | } 15 | 16 | public int getId() { 17 | return uid; 18 | } 19 | 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | public String getPackageName() { 25 | return packageName; 26 | } 27 | 28 | public boolean getProxied() { 29 | return proxied; 30 | } 31 | 32 | public void setProxied(boolean proxied) { 33 | this.proxied = proxied; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/preferences/PasswordEditTextPreference.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.preferences; 2 | 3 | import android.content.Context; 4 | import android.preference.EditTextPreference; 5 | import android.util.AttributeSet; 6 | 7 | public class PasswordEditTextPreference extends EditTextPreference { 8 | private CharSequence mDefaultSummary = getSummary(); 9 | 10 | public PasswordEditTextPreference(Context context, AttributeSet attrs) { 11 | this(context, attrs, android.R.attr.editTextPreferenceStyle); 12 | mDefaultSummary = getSummary(); 13 | } 14 | 15 | public PasswordEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) { 16 | super(context, attrs, defStyleAttr); 17 | } 18 | 19 | @Override 20 | public void setText(String text) { 21 | super.setText(text); 22 | setSummary(text); 23 | } 24 | 25 | @Override 26 | public void setSummary(CharSequence summary) { 27 | if (summary.toString().isEmpty()) { 28 | super.setSummary(mDefaultSummary); 29 | } else { 30 | StringBuilder sb = new StringBuilder(); 31 | int length = summary.toString().length(); 32 | for(int i = 0; i < length; i++) { 33 | sb.append("*"); 34 | } 35 | super.setSummary(sb.toString()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/preferences/ProfileEditTextPreference.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.preferences; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.preference.EditTextPreference; 6 | import android.util.AttributeSet; 7 | 8 | import io.github.xSocks.utils.Constants; 9 | 10 | public class ProfileEditTextPreference extends EditTextPreference { 11 | private CharSequence mDefaultSummary = getSummary(); 12 | private Context context; 13 | 14 | public ProfileEditTextPreference(Context context, AttributeSet attrs) { 15 | this(context, attrs, android.R.attr.editTextPreferenceStyle); 16 | mDefaultSummary = getSummary(); 17 | } 18 | 19 | public ProfileEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) { 20 | super(context, attrs, defStyleAttr); 21 | this.context = context; 22 | } 23 | 24 | @Override 25 | public void setText(String text) { 26 | super.setText(text); 27 | setSummary(text); 28 | } 29 | 30 | @Override 31 | public void setSummary(CharSequence summary) { 32 | if (summary.toString().isEmpty()) { 33 | super.setSummary(mDefaultSummary); 34 | } else { 35 | super.setSummary(summary); 36 | } 37 | context.sendBroadcast(new Intent(Constants.Action.UPDATE_PREFS)); 38 | } 39 | 40 | public void resetSummary(CharSequence summary) { 41 | if (summary.toString().isEmpty()) { 42 | super.setSummary(mDefaultSummary); 43 | } else { 44 | super.setSummary(summary); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/preferences/SummaryEditTextPreference.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.preferences; 2 | 3 | import android.content.Context; 4 | import android.preference.EditTextPreference; 5 | import android.util.AttributeSet; 6 | 7 | public class SummaryEditTextPreference extends EditTextPreference { 8 | private CharSequence mDefaultSummary = getSummary(); 9 | 10 | public SummaryEditTextPreference(Context context, AttributeSet attrs) { 11 | this(context, attrs, android.R.attr.editTextPreferenceStyle); 12 | mDefaultSummary = getSummary(); 13 | } 14 | 15 | public SummaryEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) { 16 | super(context, attrs, defStyleAttr); 17 | } 18 | 19 | @Override 20 | public void setText(String text) { 21 | super.setText(text); 22 | setSummary(text); 23 | } 24 | 25 | @Override 26 | public void setSummary(CharSequence summary) { 27 | if (summary.toString().isEmpty()) { 28 | super.setSummary(mDefaultSummary); 29 | } else { 30 | super.setSummary(summary); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/preferences/SummaryListPreference.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.preferences; 2 | 3 | import android.content.Context; 4 | import android.preference.ListPreference; 5 | import android.util.AttributeSet; 6 | 7 | public class SummaryListPreference extends ListPreference { 8 | public SummaryListPreference(Context context, AttributeSet attrs) { 9 | super(context, attrs); 10 | } 11 | 12 | @Override 13 | public void setValue(String text) { 14 | super.setValue(text); 15 | CharSequence entry = getEntry(); 16 | if (entry != null) 17 | setSummary(entry); 18 | else 19 | setSummary(text); 20 | } 21 | 22 | @Override 23 | public void setSummary(CharSequence summary) { 24 | if (summary == null || summary.toString().isEmpty()) { 25 | super.setSummary(""); 26 | } else { 27 | super.setSummary(summary); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/service/xSocksVpnService.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.service; 2 | 3 | import android.app.PendingIntent; 4 | import android.app.Service; 5 | import android.content.BroadcastReceiver; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.IntentFilter; 9 | import android.content.pm.PackageManager; 10 | import android.net.VpnService; 11 | import android.os.Handler; 12 | import android.os.IBinder; 13 | import android.os.ParcelFileDescriptor; 14 | import android.os.RemoteCallbackList; 15 | import android.os.RemoteException; 16 | import android.support.v4.app.NotificationCompat; 17 | import android.util.Log; 18 | import android.widget.Toast; 19 | 20 | import java.io.BufferedReader; 21 | import java.io.File; 22 | import java.io.FileOutputStream; 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | import java.io.InputStreamReader; 26 | import java.io.OutputStream; 27 | import java.util.ArrayList; 28 | import java.util.Arrays; 29 | import java.util.List; 30 | import java.util.Locale; 31 | import java.util.Scanner; 32 | 33 | import io.github.xSocks.BuildConfig; 34 | import io.github.xSocks.R; 35 | import io.github.xSocks.aidl.Config; 36 | import io.github.xSocks.aidl.IxSocksService; 37 | import io.github.xSocks.aidl.IxSocksServiceCallback; 38 | import io.github.xSocks.model.ProxiedApp; 39 | import io.github.xSocks.ui.AppManagerActivity; 40 | import io.github.xSocks.ui.MainActivity; 41 | import io.github.xSocks.ui.xSocksRunnerActivity; 42 | import io.github.xSocks.utils.ConfigUtils; 43 | import io.github.xSocks.utils.Console; 44 | import io.github.xSocks.utils.Constants; 45 | import io.github.xSocks.utils.Utils; 46 | import rx.schedulers.Schedulers; 47 | import xsocks.Xsocks.*; 48 | import rx.util.async.Async; 49 | 50 | public class xSocksVpnService extends VpnService { 51 | 52 | private String TAG = "xSocks"; 53 | private static final String VPN_ADDRESS = "26.26.26.1"; 54 | private Config config = null; 55 | public ParcelFileDescriptor vpnInterface; 56 | private BroadcastReceiver closeReceiver = null; 57 | private Constants.State state = Constants.State.INIT; 58 | private int callbackCount = 0; 59 | int VPN_MTU = 1500*20; 60 | private final RemoteCallbackList callbacks = new RemoteCallbackList<>(); 61 | 62 | private IxSocksService.Stub binder = new IxSocksService.Stub() { 63 | @Override 64 | public int getState() throws RemoteException { 65 | return state.ordinal(); 66 | } 67 | 68 | @Override 69 | public void registerCallback(IxSocksServiceCallback cb) throws RemoteException { 70 | if (cb != null) { 71 | callbacks.register(cb); 72 | callbackCount += 1; 73 | } 74 | } 75 | 76 | @Override 77 | public void unregisterCallback(IxSocksServiceCallback cb) throws RemoteException { 78 | if (cb != null ) { 79 | callbacks.unregister(cb); 80 | callbackCount -= 1; 81 | } 82 | if (callbackCount == 0 && state != Constants.State.CONNECTING && state != Constants.State.CONNECTED) { 83 | stopSelf(); 84 | } 85 | } 86 | 87 | @Override 88 | public void start(Config config) { 89 | if (state != Constants.State.CONNECTING && state != Constants.State.STOPPING) { 90 | startRunner(config); 91 | } 92 | } 93 | 94 | @Override 95 | public void stop() throws RemoteException { 96 | if (state != Constants.State.CONNECTING && state != Constants.State.STOPPING) { 97 | stopRunner(); 98 | } 99 | } 100 | }; 101 | 102 | private void notifyForegroundAlert(String title, String info, Boolean visible) { 103 | Intent openIntent = new Intent(this, MainActivity.class); 104 | PendingIntent contentIntent = PendingIntent.getActivity(this, 0, openIntent, 0); 105 | Intent closeIntent = new Intent(Constants.Action.CLOSE); 106 | PendingIntent actionIntent = PendingIntent.getBroadcast(this, 0, closeIntent, 0); 107 | NotificationCompat.Builder builder = new NotificationCompat.Builder(this); 108 | builder.setWhen(0) 109 | .setTicker(title) 110 | .setContentTitle(getString(R.string.app_name)) 111 | .setContentText(info) 112 | .setContentIntent(contentIntent) 113 | .setSmallIcon(R.drawable.ic_logo) 114 | .addAction(android.R.drawable.ic_menu_close_clear_cancel, 115 | getString(R.string.stop), actionIntent); 116 | 117 | if (visible) { 118 | builder.setPriority(NotificationCompat.PRIORITY_DEFAULT); 119 | } else { 120 | builder.setPriority(NotificationCompat.PRIORITY_MIN); 121 | } 122 | startForeground(1, builder.build()); 123 | } 124 | 125 | @Override 126 | public void onCreate() { 127 | super.onCreate(); 128 | } 129 | 130 | @Override 131 | public IBinder onBind(Intent intent) { 132 | String action = intent.getAction(); 133 | if (VpnService.SERVICE_INTERFACE.equals(action)) { 134 | return super.onBind(intent); 135 | } else if (Constants.Action.SERVICE.equals(action)) { 136 | return binder; 137 | } 138 | return null; 139 | } 140 | 141 | @Override 142 | public void onRevoke() { 143 | stopRunner(); 144 | } 145 | 146 | 147 | 148 | private void startxSocksDaemon(Integer tunFd) throws IOException { 149 | String serverAddr=config.protocol+"://"+config.proxy+":"+config.remotePort; 150 | String password=config.sitekey; 151 | String caFile=""; 152 | Boolean skipVerify=false; 153 | Integer tunType=0; 154 | String unixSockTun=""; 155 | Integer smartDns=0; 156 | Integer localDns=0; 157 | Integer muxNum=0; 158 | Integer udpProxy=1; 159 | Integer mtu=4500; 160 | Boolean tunSmartProxy=false; 161 | if(config.verifyCert.equals("skip")){ 162 | skipVerify=true; 163 | } 164 | if(config.verifyCert.equals("self_signed")){ 165 | caFile=config.caFile; 166 | } 167 | 168 | tunType= Integer.parseInt(config.tunType); 169 | 170 | mtu=VPN_MTU; 171 | String ipFile=Constants.Path.BASE + "iptable.txt"; 172 | xsocks.Xsocks.start("",serverAddr,password,caFile,skipVerify,tunType,"",tunFd,muxNum,localDns,smartDns,udpProxy,mtu,tunSmartProxy,ipFile); 173 | } 174 | 175 | 176 | 177 | private int startVpn() throws IOException { 178 | 179 | Builder builder = new Builder(); 180 | builder.setSession(config.profileName); 181 | builder.setMtu(VPN_MTU); 182 | builder.addAddress(VPN_ADDRESS, 24); 183 | builder.addDnsServer("114.114.114.114"); 184 | if (Utils.isLollipopOrAbove()) { 185 | try { 186 | if (!config.isGlobalProxy) { 187 | ProxiedApp[] apps = AppManagerActivity.getProxiedApps(this, config.proxiedAppString); 188 | for (ProxiedApp app : apps) { 189 | if (config.isBypassApps) { 190 | builder.addDisallowedApplication(app.getPackageName()); 191 | 192 | } else { 193 | builder.addAllowedApplication(app.getPackageName()); 194 | } 195 | } 196 | if (config.isBypassApps) { 197 | Log.d("isBypassApps",this.getApplicationContext().getPackageName()); 198 | builder.addDisallowedApplication(this.getApplicationContext().getPackageName()); 199 | } 200 | }else{ 201 | //全局的话加自己白名单就OK 202 | Log.d("isBypassApps",this.getApplicationContext().getPackageName()); 203 | builder.addDisallowedApplication(this.getApplicationContext().getPackageName()); 204 | } 205 | } catch (PackageManager.NameNotFoundException e) { 206 | Log.e(TAG, "Package name not found"); 207 | } 208 | } 209 | 210 | builder.addRoute("0.0.0.0", 0); 211 | // builder.setBlocking(true); 212 | vpnInterface = builder.setBlocking(false).establish(); 213 | if (vpnInterface == null) { 214 | Log.e(TAG, "vpn interface is null"); 215 | return -1; 216 | } 217 | int fd = vpnInterface.getFd(); 218 | return fd; 219 | } 220 | 221 | private boolean startDaemons() { 222 | try { 223 | int fd = startVpn(); 224 | if (fd != -1) { 225 | new Thread(new Runnable() { 226 | @Override 227 | public void run() { 228 | try { 229 | startxSocksDaemon(fd); 230 | }catch (IOException e){ 231 | 232 | } 233 | } 234 | }).start(); 235 | return true; 236 | } 237 | } catch (IOException e) { 238 | Log.e(TAG, "Got " + e.getMessage()); 239 | return false; 240 | } 241 | return false; 242 | } 243 | 244 | private void startRunner(Config c) { 245 | config = c; 246 | // register close closeReceiver 247 | IntentFilter filter = new IntentFilter(); 248 | filter.addAction(Intent.ACTION_SHUTDOWN); 249 | filter.addAction(Constants.Action.CLOSE); 250 | closeReceiver = new BroadcastReceiver() { 251 | @Override 252 | public void onReceive(Context context, Intent intent) { 253 | Toast.makeText(context, R.string.stopping, Toast.LENGTH_SHORT).show(); 254 | stopRunner(); 255 | } 256 | }; 257 | registerReceiver(closeReceiver, filter); 258 | // ensure the VPNService is prepared 259 | if (VpnService.prepare(this) != null) { 260 | Intent i = new Intent(this, xSocksRunnerActivity.class); 261 | i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 262 | startActivity(i); 263 | return; 264 | } 265 | changeState(Constants.State.CONNECTING); 266 | Async.runAsync(Schedulers.newThread(), (observer, subscription) -> { 267 | if (config != null) { 268 | //关闭之前的 269 | xsocks.Xsocks.shutdown(); 270 | boolean resolved = true; 271 | //检测连通 272 | if(config.protocol.equals("wss")){ 273 | boolean PortState=Utils.checkConnect(config.proxy,config.remotePort); 274 | if (!PortState) { 275 | resolved = false; 276 | } 277 | Log.e("proxy:",config.proxy); 278 | } 279 | 280 | if (resolved && startDaemons()) { 281 | notifyForegroundAlert(getString(R.string.forward_success), 282 | getString(R.string.service_running, config.profileName), 283 | false); 284 | changeState(Constants.State.CONNECTED); 285 | } else { 286 | changeState(Constants.State.STOPPED, getString(R.string.service_failed)); 287 | stopRunner(); 288 | } 289 | } 290 | }); 291 | } 292 | 293 | private void stopRunner() { 294 | stopForeground(true); 295 | changeState(Constants.State.STOPPING); 296 | xsocks.Xsocks.shutdown(); 297 | if (vpnInterface != null) { 298 | try { 299 | vpnInterface.close(); 300 | } catch (IOException e) { 301 | // close failed 302 | } 303 | } 304 | // stop the service if no callback registered 305 | if (callbackCount == 0) { 306 | stopSelf(); 307 | } 308 | // clean up receiver 309 | if (closeReceiver != null) { 310 | unregisterReceiver(closeReceiver); 311 | closeReceiver = null; 312 | } 313 | changeState(Constants.State.STOPPED); 314 | } 315 | 316 | private void changeState(Constants.State s) { 317 | changeState(s, null); 318 | } 319 | 320 | public void changeState(Constants.State s, String msg) { 321 | Handler handler = new Handler(getBaseContext().getMainLooper()); 322 | handler.post(() -> { 323 | if (state != s) { 324 | if (callbackCount > 0) { 325 | int n = callbacks.beginBroadcast(); 326 | for (int i = 0; i <= n - 1; i++) { 327 | try { 328 | callbacks.getBroadcastItem(i).stateChanged(s.ordinal(), msg); 329 | } catch (RemoteException e) { 330 | // Ignore 331 | } 332 | } 333 | callbacks.finishBroadcast(); 334 | } 335 | state = s; 336 | } 337 | }); 338 | } 339 | @Override 340 | public int onStartCommand(Intent intent, int flags, int startId) { 341 | return Service.START_REDELIVER_INTENT; 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/store/ProfileManager.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.store; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.util.Log; 6 | 7 | import com.google.gson.Gson; 8 | 9 | import java.io.BufferedReader; 10 | import java.io.FileInputStream; 11 | import java.io.FileOutputStream; 12 | import java.io.IOException; 13 | import java.io.InputStreamReader; 14 | import java.util.List; 15 | 16 | import io.github.xSocks.model.Profile; 17 | import io.github.xSocks.model.Profiles; 18 | import io.github.xSocks.utils.Constants; 19 | 20 | public class ProfileManager { 21 | private SharedPreferences settings; 22 | private final Context context; 23 | private String TAG = "XSOCKS"; 24 | private Profiles profiles; 25 | private String filename = "profiles.json"; 26 | 27 | 28 | public ProfileManager(final Context context, SharedPreferences settings) { 29 | this.context = context; 30 | this.settings = settings; 31 | profiles = reloadAll(); 32 | } 33 | 34 | private Profile loadFromPreferences(Profile profile) { 35 | int id = settings.getInt(Constants.Key.profileId, -1); 36 | profile.setId(id); 37 | profile.setGlobal(settings.getBoolean(Constants.Key.isGlobalProxy, false)); 38 | profile.setBypass(settings.getBoolean(Constants.Key.isBypassApps, false)); 39 | profile.setUdpdns(settings.getBoolean(Constants.Key.isUdpDns, false)); 40 | profile.setName(settings.getString(Constants.Key.profileName, "Default")); 41 | profile.setProxy(settings.getString(Constants.Key.proxy, "")); 42 | profile.setPassword(settings.getString(Constants.Key.sitekey, "")); 43 | profile.setRoute(settings.getString(Constants.Key.route, "all")); 44 | profile.setProtocol(settings.getString(Constants.Key.protocol, "wss")); 45 | 46 | try { 47 | profile.setRemotePort(Integer.valueOf(settings.getString(Constants.Key.remotePort, "1073"))); 48 | } catch (NumberFormatException ex) { 49 | profile.setRemotePort(1073); 50 | } 51 | 52 | try { 53 | profile.setLocalPort(Integer.valueOf(settings.getString(Constants.Key.localPort, "1080"))); 54 | } catch (NumberFormatException ex) { 55 | profile.setRemotePort(1080); 56 | } 57 | 58 | profile.setIndividual(settings.getString(Constants.Key.proxied, "")); 59 | 60 | return profile; 61 | } 62 | 63 | private void setPreferences(Profile profile) { 64 | SharedPreferences.Editor edit = settings.edit(); 65 | edit.putBoolean(Constants.Key.isBypassApps, profile.isBypass()); 66 | edit.putBoolean(Constants.Key.isUdpDns, profile.isUdpdns()); 67 | edit.putString(Constants.Key.profileName, profile.getName()); 68 | edit.putString(Constants.Key.proxy, profile.getProxy()); 69 | edit.putString(Constants.Key.sitekey, profile.getPassword()); 70 | edit.putString(Constants.Key.remotePort, Integer.toString(profile.getRemotePort())); 71 | edit.putString(Constants.Key.localPort, Integer.toString(profile.getLocalPort())); 72 | edit.putString(Constants.Key.proxied, profile.getIndividual()); 73 | edit.putInt(Constants.Key.profileId, profile.getId()); 74 | edit.putString(Constants.Key.route, profile.getRoute()); 75 | edit.apply(); 76 | } 77 | 78 | public List getAllProfile() { 79 | profiles = reloadAll(); 80 | return profiles == null ? null : profiles.getProfiles(); 81 | } 82 | 83 | private Profiles reloadAll() { 84 | try { 85 | FileInputStream fis = context.openFileInput(filename); 86 | InputStreamReader isr = new InputStreamReader(fis); 87 | BufferedReader bufferedReader = new BufferedReader(isr); 88 | StringBuilder sb = new StringBuilder(); 89 | String line; 90 | while ((line = bufferedReader.readLine()) != null) { 91 | sb.append(line); 92 | } 93 | String json = sb.toString(); 94 | Gson gson = new Gson(); 95 | return gson.fromJson(json, Profiles.class); 96 | 97 | } catch (IOException e) { 98 | return new Profiles(); 99 | } 100 | } 101 | 102 | private void createProfile(Profile profile) { 103 | profiles.addProfile(profile); 104 | saveAll(); 105 | profiles = reloadAll(); 106 | } 107 | 108 | public Profile firstCreate() { 109 | Profile profile = new Profile(); 110 | profile = loadFromPreferences(profile); 111 | int nextId = profiles == null ? 1 : profiles.getMaxId() + 1; 112 | if (nextId > 1) return profile; 113 | profile.setId(nextId); 114 | createProfile(profile); 115 | setPreferences(profile); 116 | return profile; 117 | } 118 | 119 | public Profile create() { 120 | Profile profile = new Profile(); 121 | int nextId = profiles.getMaxId() + 1; 122 | profile.setId(nextId); 123 | createProfile(profile); 124 | setPreferences(profile); 125 | return profile; 126 | } 127 | 128 | private void saveAll() { 129 | FileOutputStream outputStream; 130 | 131 | try { 132 | Gson gson = new Gson(); 133 | String json = gson.toJson(profiles); 134 | outputStream = context.openFileOutput(filename, Context.MODE_PRIVATE); 135 | outputStream.write(json.getBytes()); 136 | outputStream.close(); 137 | } catch (Exception e) { 138 | Log.e(TAG, "save", e); 139 | } 140 | } 141 | 142 | public Profile save() { 143 | int id = settings.getInt(Constants.Key.profileId, -1); 144 | Profile profile = profiles.getProfile(id); 145 | if (profile != null) { 146 | profile = loadFromPreferences(profile); 147 | saveAll(); 148 | } 149 | return profile; 150 | } 151 | 152 | public Profile getProfile(int id) { 153 | return profiles == null ? null : profiles.getProfile(id); 154 | } 155 | 156 | public void delProfile(int id) { 157 | try { 158 | profiles.remove(id); 159 | saveAll(); 160 | } catch (Exception ex) { 161 | Log.e(TAG, "getProfile", ex); 162 | } 163 | } 164 | 165 | public Profile load(int id) { 166 | Profile profile = profiles.getProfile(id); 167 | if (profile == null) { 168 | profile = create(); 169 | } 170 | setPreferences(profile); 171 | return profile; 172 | } 173 | 174 | public Profile reload(int id) { 175 | save(); 176 | return load(id); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/ui/AboutActivity.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.ui; 2 | 3 | import android.content.pm.PackageInfo; 4 | import android.content.pm.PackageManager; 5 | import android.graphics.Color; 6 | import android.graphics.PorterDuff; 7 | import android.graphics.drawable.Drawable; 8 | import android.os.Build; 9 | import android.os.Bundle; 10 | import android.support.v4.content.ContextCompat; 11 | import android.support.v7.app.ActionBar; 12 | import android.support.v7.app.AppCompatActivity; 13 | import android.support.v7.widget.Toolbar; 14 | import android.text.Html; 15 | import android.widget.TextView; 16 | 17 | import io.github.xSocks.R; 18 | import io.github.xSocks.utils.MovementCheck; 19 | 20 | public class AboutActivity extends AppCompatActivity { 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_about); 26 | 27 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 28 | setSupportActionBar(toolbar); 29 | toolbar.setTitleTextColor(Color.WHITE); 30 | toolbar.setSubtitleTextColor(Color.WHITE); 31 | toolbar.setNavigationOnClickListener(v -> onBackPressed()); 32 | 33 | ActionBar ab = getSupportActionBar(); 34 | if (ab != null) { 35 | Drawable upArrow = ContextCompat.getDrawable(this, R.drawable.abc_ic_ab_back_mtrl_am_alpha); 36 | if (upArrow != null) { 37 | upArrow.setColorFilter(ContextCompat.getColor(this, android.R.color.white), PorterDuff.Mode.SRC_IN); 38 | } 39 | ab.setHomeAsUpIndicator(upArrow); 40 | ab.setDisplayHomeAsUpEnabled(true); 41 | } 42 | 43 | TextView aboutVersion = (TextView) findViewById(R.id.aboutVersion); 44 | try { 45 | PackageInfo manager = getPackageManager().getPackageInfo(getPackageName(), 0); 46 | String version = getResources().getString(R.string.version, manager.versionName, manager.versionCode); 47 | aboutVersion.setText(version); 48 | } catch (PackageManager.NameNotFoundException e) { 49 | aboutVersion.setText(R.string.version_default); 50 | } 51 | 52 | TextView aboutDescription = (TextView) findViewById(R.id.aboutDescription); 53 | aboutDescription.setText(Html.fromHtml(getString(R.string.about_description_text))); 54 | aboutDescription.setMovementMethod(MovementCheck.getInstance()); 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/ui/AppManagerActivity.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.ui; 2 | 3 | import android.app.ProgressDialog; 4 | import android.content.Context; 5 | import android.content.SharedPreferences; 6 | import android.content.pm.ApplicationInfo; 7 | import android.content.pm.PackageManager; 8 | import android.graphics.Bitmap; 9 | import android.graphics.Color; 10 | import android.graphics.PorterDuff; 11 | import android.graphics.drawable.Drawable; 12 | import android.os.Build; 13 | import android.os.Bundle; 14 | import android.preference.PreferenceManager; 15 | import android.support.v4.content.ContextCompat; 16 | import android.support.v7.app.ActionBar; 17 | import android.support.v7.widget.Toolbar; 18 | import android.util.Log; 19 | import android.view.View; 20 | import android.view.ViewGroup; 21 | import android.widget.ArrayAdapter; 22 | import android.widget.CheckBox; 23 | import android.widget.CompoundButton; 24 | import android.widget.ImageView; 25 | import android.widget.ListAdapter; 26 | import android.widget.ListView; 27 | import android.widget.Switch; 28 | import android.widget.TextView; 29 | 30 | import com.nostra13.universalimageloader.core.DisplayImageOptions; 31 | import com.nostra13.universalimageloader.core.ImageLoader; 32 | import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; 33 | import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer; 34 | import com.nostra13.universalimageloader.core.download.BaseImageDownloader; 35 | import com.trello.rxlifecycle.components.support.RxAppCompatActivity; 36 | 37 | import java.io.ByteArrayInputStream; 38 | import java.io.ByteArrayOutputStream; 39 | import java.io.InputStream; 40 | import java.text.CollationKey; 41 | import java.text.Collator; 42 | import java.util.ArrayList; 43 | import java.util.Arrays; 44 | import java.util.List; 45 | import java.util.Locale; 46 | 47 | import io.github.xSocks.R; 48 | import io.github.xSocks.model.ProxiedApp; 49 | import io.github.xSocks.utils.Constants; 50 | import io.github.xSocks.utils.Utils; 51 | import rx.android.schedulers.AndroidSchedulers; 52 | import rx.schedulers.Schedulers; 53 | import rx.util.async.Async; 54 | 55 | 56 | public class AppManagerActivity extends RxAppCompatActivity 57 | implements CompoundButton.OnCheckedChangeListener, View.OnClickListener { 58 | 59 | private boolean appsLoaded; 60 | private ListAdapter adapter; 61 | private ListView appListView; 62 | private ProgressDialog progressDialog; 63 | private int STUB = android.R.drawable.sym_def_app_icon; 64 | private ProxiedApp[] apps; 65 | 66 | private class AppIconDownloader extends BaseImageDownloader { 67 | 68 | public AppIconDownloader(Context context, int connectTimeout, int readTimeout) { 69 | super(context, connectTimeout, readTimeout); 70 | } 71 | 72 | public AppIconDownloader(Context context) { 73 | this(context, 0, 0); 74 | } 75 | 76 | @Override 77 | public InputStream getStreamFromOtherSource(String imageUri, Object extra) { 78 | String packageName = imageUri.substring(Constants.Scheme.APP.length()); 79 | Drawable drawable = Utils.getAppIcon(getBaseContext(), packageName); 80 | Bitmap bitmap = Utils.drawableToBitmap(drawable); 81 | 82 | ByteArrayOutputStream os = new ByteArrayOutputStream(); 83 | bitmap.compress(Bitmap.CompressFormat.PNG, 100, os); 84 | return new ByteArrayInputStream(os.toByteArray()); 85 | } 86 | } 87 | 88 | private class ListEntry { 89 | private CheckBox box; 90 | private TextView text; 91 | private ImageView icon; 92 | 93 | public ListEntry(CheckBox box, TextView text, ImageView icon) { 94 | this.box = box; 95 | this.text = text; 96 | this.icon = icon; 97 | } 98 | 99 | public CheckBox getBox() { 100 | return box; 101 | } 102 | 103 | public TextView getText() { 104 | return text; 105 | } 106 | 107 | public ImageView getIcon() { 108 | return icon; 109 | } 110 | } 111 | 112 | @Override 113 | protected void onCreate(Bundle savedInstanceState) { 114 | super.onCreate(savedInstanceState); 115 | setContentView(R.layout.activity_app_manager); 116 | 117 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 118 | setSupportActionBar(toolbar); 119 | toolbar.setTitleTextColor(Color.WHITE); 120 | toolbar.setSubtitleTextColor(Color.WHITE); 121 | toolbar.setNavigationOnClickListener(v -> onBackPressed()); 122 | 123 | ActionBar ab = getSupportActionBar(); 124 | if (ab != null) { 125 | Drawable upArrow = ContextCompat.getDrawable(this, R.drawable.abc_ic_ab_back_mtrl_am_alpha); 126 | if (upArrow != null) { 127 | upArrow.setColorFilter(ContextCompat.getColor(this, android.R.color.white), PorterDuff.Mode.SRC_IN); 128 | } 129 | ab.setHomeAsUpIndicator(upArrow); 130 | ab.setDisplayHomeAsUpEnabled(true); 131 | } 132 | 133 | ImageLoaderConfiguration config = 134 | new ImageLoaderConfiguration.Builder(this) 135 | .imageDownloader(new AppIconDownloader(this)) 136 | .build(); 137 | ImageLoader.getInstance().init(config); 138 | 139 | Switch bypassSwitch = (Switch) findViewById(R.id.bypassSwitch); 140 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext()); 141 | 142 | bypassSwitch.setOnCheckedChangeListener((button, checked) -> 143 | prefs.edit().putBoolean(Constants.Key.isBypassApps, checked).commit()); 144 | 145 | bypassSwitch.setChecked(prefs.getBoolean(Constants.Key.isBypassApps, false)); 146 | 147 | appListView = (ListView) findViewById(R.id.applistview); 148 | } 149 | 150 | @Override 151 | protected void onResume() { 152 | super.onResume(); 153 | if (!appsLoaded) { 154 | loadApps(); 155 | } 156 | } 157 | 158 | private ProxiedApp[] getApps(Context context) { 159 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 160 | String proxiedAppString = prefs.getString(Constants.Key.proxied, ""); 161 | String[] appString = proxiedAppString.split("\\|"); 162 | Arrays.sort(appString); 163 | 164 | PackageManager packageManager = context.getPackageManager(); 165 | List infoList = packageManager.getInstalledApplications(0); 166 | ArrayList appList = new ArrayList<>(); 167 | 168 | for (ApplicationInfo a : infoList) { 169 | if ((a.uid >= 10000) 170 | && packageManager.getApplicationLabel(a) != null 171 | && packageManager.getApplicationIcon(a) != null) { 172 | String name = packageManager.getApplicationLabel(a).toString(); 173 | String userName = Integer.toString(a.uid); 174 | int index = Arrays.binarySearch(appString, userName); 175 | boolean proxied = index >= 0; 176 | // trim no-break space 177 | ProxiedApp app = new ProxiedApp(a.uid, name.replace("\u00A0",""), a.packageName, proxied); 178 | appList.add(app); 179 | } 180 | } 181 | ProxiedApp[] appArray = new ProxiedApp[appList.size()]; 182 | appList.toArray(appArray); 183 | 184 | Collator c = Collator.getInstance(Locale.CHINESE); 185 | Arrays.sort(appArray, (a, b) -> { 186 | CollationKey k1 = c.getCollationKey(a.getName()); 187 | CollationKey k2 = c.getCollationKey(b.getName()); 188 | 189 | if (a.getProxied()) { 190 | if (b.getProxied()) { 191 | return k1.compareTo(k2); 192 | } 193 | return -1; 194 | 195 | } else if (a.getProxied() == b.getProxied()) { 196 | return k1.compareTo(k2); 197 | 198 | } else { 199 | return 1; 200 | } 201 | }); 202 | 203 | return appArray; 204 | } 205 | 206 | private void loadApps() { 207 | progressDialog = ProgressDialog 208 | .show(AppManagerActivity.this, "", getString(R.string.loading), true, true); 209 | 210 | Async.toAsync(this::getApps).call(this) 211 | .observeOn(Schedulers.newThread()) 212 | .observeOn(AndroidSchedulers.mainThread()) 213 | .compose(bindToLifecycle()) 214 | .subscribe(apps -> { 215 | this.apps = apps; 216 | adapter = new ArrayAdapter(this, R.layout.apps_item, R.id.itemtext, apps) { 217 | @Override 218 | public View getView(int position, View view, ViewGroup parent) { 219 | View convertView = view; 220 | ListEntry entry; 221 | if (convertView == null) { 222 | convertView = getLayoutInflater().inflate(R.layout.apps_item, parent, false); 223 | TextView text = (TextView) convertView.findViewById(R.id.itemtext); 224 | CheckBox box = (CheckBox) convertView.findViewById(R.id.itemcheck); 225 | ImageView icon = (ImageView) convertView.findViewById(R.id.itemicon); 226 | entry = new ListEntry(box, text, icon); 227 | entry.getText().setOnClickListener(AppManagerActivity.this); 228 | entry.getBox().setOnCheckedChangeListener(AppManagerActivity.this); 229 | convertView.setTag(entry); 230 | 231 | } else { 232 | entry = (ListEntry) convertView.getTag(); 233 | } 234 | 235 | ProxiedApp app = apps[position]; 236 | DisplayImageOptions options = 237 | new DisplayImageOptions.Builder() 238 | .showStubImage(STUB) 239 | .showImageForEmptyUri(STUB) 240 | .showImageOnFail(STUB) 241 | .resetViewBeforeLoading() 242 | .cacheInMemory() 243 | .cacheOnDisc() 244 | .displayer(new FadeInBitmapDisplayer(300)) 245 | .build(); 246 | ImageLoader.getInstance().displayImage(Constants.Scheme.APP + app.getPackageName(), entry.icon, options); 247 | 248 | entry.text.setText(app.getName()); 249 | CheckBox box = entry.getBox(); 250 | box.setTag(app); 251 | box.setChecked(app.getProxied()); 252 | entry.text.setTag(box); 253 | 254 | return convertView; 255 | } 256 | }; 257 | 258 | appListView.setAdapter(adapter); 259 | appsLoaded = true; 260 | 261 | clearDialog(); 262 | }, ex -> { 263 | clearDialog(); 264 | }); 265 | } 266 | 267 | private void clearDialog() { 268 | if (progressDialog != null) { 269 | progressDialog.dismiss(); 270 | progressDialog = null; 271 | } 272 | } 273 | 274 | @Override 275 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 276 | ProxiedApp app = (ProxiedApp) buttonView.getTag(); 277 | if (app != null) { 278 | app.setProxied(isChecked); 279 | } 280 | saveAppSettings(this); 281 | } 282 | 283 | @Override 284 | public void onClick(View v) { 285 | CheckBox box = (CheckBox) v.getTag(); 286 | ProxiedApp app = (ProxiedApp) box.getTag(); 287 | if (app != null) { 288 | app.setProxied(!app.getProxied()); 289 | box.setChecked(app.getProxied()); 290 | } 291 | saveAppSettings(this); 292 | } 293 | 294 | private void saveAppSettings(Context context) { 295 | if (apps == null) return; 296 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 297 | StringBuilder proxiedApps = new StringBuilder(); 298 | for (ProxiedApp app : apps) { 299 | if (app.getProxied()) { 300 | proxiedApps.append(Integer.toString(app.getId())); 301 | proxiedApps.append("|"); 302 | } 303 | } 304 | SharedPreferences.Editor edit = prefs.edit(); 305 | edit.putString(Constants.Key.proxied, proxiedApps.toString()); 306 | edit.apply(); 307 | } 308 | 309 | public static ProxiedApp[] getProxiedApps(Context context, String proxiedAppString) { 310 | String[] proxiedApps = proxiedAppString.split("\\|"); 311 | Arrays.sort(proxiedApps); 312 | 313 | PackageManager packageManager = context.getPackageManager(); 314 | List infoList = packageManager.getInstalledApplications(0); 315 | ArrayList appList = new ArrayList<>(); 316 | 317 | for (ApplicationInfo a : infoList) { 318 | if (a.uid >= 10000) { 319 | int uid = a.uid; 320 | String name = packageManager.getApplicationLabel(a).toString(); 321 | String packageName = a.packageName; 322 | int index = Arrays.binarySearch(proxiedApps, Integer.toString(uid)); 323 | boolean proxied = index >= 0; 324 | if (proxied) { 325 | ProxiedApp app = new ProxiedApp(uid, name, packageName, true); 326 | appList.add(app); 327 | } 328 | } 329 | } 330 | ProxiedApp[] appArray = new ProxiedApp[appList.size()]; 331 | appList.toArray(appArray); 332 | return appArray; 333 | } 334 | 335 | } 336 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/ui/AppManagerActivityFragment.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.ui; 2 | 3 | import android.support.v4.app.Fragment; 4 | import android.os.Bundle; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import io.github.xSocks.R; 10 | 11 | /** 12 | * A placeholder fragment containing a simple view. 13 | */ 14 | public class AppManagerActivityFragment extends Fragment { 15 | 16 | public AppManagerActivityFragment() { 17 | } 18 | 19 | @Override 20 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 21 | Bundle savedInstanceState) { 22 | return inflater.inflate(R.layout.fragment_app_manager, container, false); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.ui; 2 | 3 | import android.app.Activity; 4 | import android.app.ProgressDialog; 5 | import android.app.backup.BackupManager; 6 | import android.content.ActivityNotFoundException; 7 | import android.content.BroadcastReceiver; 8 | import android.content.ComponentName; 9 | import android.content.Context; 10 | import android.content.Intent; 11 | import android.content.IntentFilter; 12 | import android.content.ServiceConnection; 13 | import android.content.SharedPreferences; 14 | import android.content.pm.PackageInfo; 15 | import android.content.pm.PackageManager; 16 | import android.content.res.AssetManager; 17 | import android.graphics.Color; 18 | import android.graphics.Typeface; 19 | import android.net.VpnService; 20 | import android.os.Bundle; 21 | import android.os.Handler; 22 | import android.os.IBinder; 23 | import android.os.Message; 24 | import android.os.RemoteException; 25 | import android.preference.PreferenceManager; 26 | import android.support.design.widget.Snackbar; 27 | import android.support.v7.app.AlertDialog; 28 | import android.support.v7.app.AppCompatActivity; 29 | import android.support.v7.widget.Toolbar; 30 | import android.util.Log; 31 | import android.widget.CompoundButton; 32 | import android.widget.Switch; 33 | import android.widget.TextView; 34 | import android.os.Build; 35 | 36 | import com.mikepenz.google_material_typeface_library.GoogleMaterial; 37 | import com.mikepenz.materialdrawer.Drawer; 38 | import com.mikepenz.materialdrawer.adapter.DrawerAdapter; 39 | import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; 40 | import com.mikepenz.materialdrawer.model.SectionDrawerItem; 41 | import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; 42 | 43 | import java.io.FileOutputStream; 44 | import java.io.IOException; 45 | import java.io.InputStream; 46 | import java.io.OutputStream; 47 | import java.util.ArrayList; 48 | import java.util.List; 49 | import java.util.Locale; 50 | 51 | import io.github.xSocks.R; 52 | import io.github.xSocks.aidl.IxSocksService; 53 | import io.github.xSocks.aidl.IxSocksServiceCallback; 54 | import io.github.xSocks.model.Profile; 55 | import io.github.xSocks.service.xSocksVpnService; 56 | import io.github.xSocks.store.ProfileManager; 57 | import io.github.xSocks.utils.ConfigUtils; 58 | import io.github.xSocks.utils.Console; 59 | import io.github.xSocks.utils.Constants; 60 | import io.github.xSocks.utils.Utils; 61 | import rx.android.schedulers.AndroidSchedulers; 62 | import rx.schedulers.Schedulers; 63 | import rx.util.async.Async; 64 | 65 | public class MainActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener { 66 | 67 | public enum Action { 68 | ADD, 69 | RECOVERY, 70 | ABOUT, 71 | } 72 | 73 | private String TAG = "xSocks"; 74 | 75 | private Switch switchButton; 76 | private ProgressDialog progressDialog = null; 77 | private PrefsFragment prefsFragment; 78 | private Drawer.Result drawer; 79 | private DrawerAdapter adapter; 80 | 81 | private Profile currentProfile; 82 | private ProfileManager profileManager; 83 | 84 | private SharedPreferences settings; 85 | 86 | private Constants.State state = Constants.State.INIT; 87 | private Handler handler; 88 | 89 | class PreferenceBroadcastReceiver extends BroadcastReceiver { 90 | @Override 91 | public void onReceive(Context context, Intent intent) { 92 | currentProfile = profileManager.save(); 93 | updateAdapter(); 94 | } 95 | } 96 | private PreferenceBroadcastReceiver preferenceReceiver = new PreferenceBroadcastReceiver(); 97 | 98 | private IxSocksService vpnService; 99 | IxSocksServiceCallback.Stub callback = new IxSocksServiceCallback.Stub() { 100 | @Override 101 | public void stateChanged(int state, String msg) { 102 | onStateChanged(state, msg); 103 | } 104 | }; 105 | 106 | private ServiceConnection connection = new ServiceConnection() { 107 | @Override 108 | public void onServiceConnected(ComponentName name, IBinder service) { 109 | vpnService = IxSocksService.Stub.asInterface(service); 110 | try { 111 | vpnService.registerCallback(callback); 112 | if (switchButton != null) switchButton.setEnabled(true); 113 | if (Constants.State.isAvailable(vpnService.getState())) { 114 | prefsFragment.setPreferenceEnabled(true); 115 | } else { 116 | changeSwitch(true); 117 | prefsFragment.setPreferenceEnabled(false); 118 | } 119 | state = Constants.State.values()[vpnService.getState()]; 120 | } catch (RemoteException e) { 121 | // ignore 122 | } 123 | if (switchButton != null) switchButton.setOnCheckedChangeListener(MainActivity.this); 124 | } 125 | 126 | @Override 127 | public void onServiceDisconnected(ComponentName name) { 128 | if (switchButton != null) switchButton.setEnabled(false); 129 | try { 130 | if (vpnService != null) vpnService.unregisterCallback(callback); 131 | } catch (RemoteException e) { 132 | // ignore 133 | } 134 | vpnService = null; 135 | } 136 | }; 137 | 138 | @Override 139 | protected void onCreate(Bundle savedInstanceState) { 140 | super.onCreate(savedInstanceState); 141 | setContentView(R.layout.activity_main); 142 | 143 | handler = new Handler(); 144 | 145 | settings = PreferenceManager.getDefaultSharedPreferences(this); 146 | profileManager = new ProfileManager(this, settings); 147 | Profile profile = profileManager.getProfile(settings.getInt(Constants.Key.profileId, -1)); 148 | currentProfile = profile != null ? profile : new Profile(); 149 | 150 | SharedPreferences status = getSharedPreferences(Constants.Key.status, Context.MODE_PRIVATE); 151 | if (!status.getBoolean(getVersionName(), false)) { 152 | progressDialog = ProgressDialog.show(this, "", getString(R.string.initializing), true, false); 153 | status.edit().putBoolean(getVersionName(), true).apply(); 154 | Async.toAsync(this::reset).call() 155 | .observeOn(Schedulers.io()) 156 | .observeOn(AndroidSchedulers.mainThread()) 157 | .subscribe(o -> clearDialog()); 158 | currentProfile = profileManager.firstCreate(); 159 | } 160 | 161 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 162 | toolbar.setLogo(R.drawable.ic_logo); 163 | 164 | switchButton = (Switch) toolbar.findViewById(R.id.switchButton); 165 | switchButton.setOnCheckedChangeListener(this); 166 | 167 | TextView title = (TextView) toolbar.findViewById(R.id.title); 168 | Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/Iceland.ttf"); 169 | if (tf != null) title.setTypeface(tf); 170 | 171 | ArrayList items = getDrawerItems(); 172 | adapter = new DrawerAdapter(this, items); 173 | 174 | drawer = new Drawer() 175 | .withActivity(this) 176 | .withToolbar(toolbar) 177 | .withAdapter(adapter) 178 | .withDrawerItems(items) 179 | .withOnDrawerItemClickListener((adapterView, view, position, id, drawerItem) -> { 180 | if (drawerItem instanceof ProfileDrawerItem) { 181 | ProfileDrawerItem item = (ProfileDrawerItem) drawerItem; 182 | int profileId = item.getProfileId(); 183 | updateProfile(profileId); 184 | return; 185 | } 186 | if (drawerItem != null) { 187 | int identity = drawerItem.getIdentifier(); 188 | if (identity == -1) return; 189 | Action action = Action.values()[identity]; 190 | switch (action) { 191 | case ADD: 192 | addProfile(); 193 | break; 194 | case RECOVERY: 195 | recovery(); 196 | break; 197 | case ABOUT: 198 | showAbout(); 199 | break; 200 | default: 201 | break; 202 | } 203 | } 204 | }) 205 | .withOnDrawerItemLongClickListener((adapterView, view, position, id, drawerItem) -> { 206 | if (drawerItem instanceof ProfileDrawerItem) { 207 | ProfileDrawerItem item = (ProfileDrawerItem) drawerItem; 208 | new AlertDialog.Builder(MainActivity.this) 209 | .setMessage(String.format(Locale.ENGLISH, getString(R.string.remove_profile), item.getName())) 210 | .setCancelable(false) 211 | .setNegativeButton(R.string.no, (dialog, which) -> { 212 | dialog.cancel(); 213 | }) 214 | .setPositiveButton(R.string.yes, (dialog, which) -> { 215 | delProfile(item.getProfileId()); 216 | dialog.dismiss(); 217 | }) 218 | .create() 219 | .show(); 220 | 221 | return true; 222 | } 223 | return false; 224 | }) 225 | .build(); 226 | 227 | drawer.getListView().setVerticalScrollBarEnabled(false); 228 | 229 | prefsFragment = new PrefsFragment(); 230 | getFragmentManager().beginTransaction().replace(R.id.content, prefsFragment).commit(); 231 | 232 | registerReceiver(preferenceReceiver, new IntentFilter(Constants.Action.UPDATE_PREFS)); 233 | Async.runAsync(Schedulers.newThread(), (observer, subscription) -> attachService()); 234 | Async.toAsync(this::install,null); 235 | 236 | } 237 | 238 | @Override 239 | public void onDestroy() { 240 | super.onDestroy(); 241 | deattachService(); 242 | unregisterReceiver(preferenceReceiver); 243 | new BackupManager(this).dataChanged(); 244 | if (handler != null) { 245 | handler.removeCallbacksAndMessages(null); 246 | handler = null; 247 | } 248 | } 249 | 250 | @Override 251 | protected void onResume() { 252 | super.onResume(); 253 | invalidateOptionsMenu(); 254 | if (vpnService != null) { 255 | try { 256 | state = Constants.State.values()[vpnService.getState()]; 257 | switch (state) { 258 | case CONNECTED: 259 | changeSwitch(true); 260 | break; 261 | case CONNECTING: 262 | changeSwitch(true); 263 | break; 264 | default: 265 | changeSwitch(false); 266 | break; 267 | } 268 | } catch (RemoteException e) { 269 | // ignore 270 | } 271 | switchButton.setOnCheckedChangeListener(MainActivity.this); 272 | } 273 | } 274 | 275 | @Override 276 | protected void onPause() { 277 | super.onPause(); 278 | if (switchButton != null) { 279 | switchButton.setOnCheckedChangeListener(null); 280 | } 281 | } 282 | 283 | @Override 284 | protected void onStop() { 285 | super.onStop(); 286 | clearDialog(); 287 | } 288 | 289 | public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { 290 | if (compoundButton.equals(switchButton)) { 291 | if (checked) { 292 | if (isReady()) { 293 | prepareStartService(); 294 | } else { 295 | changeSwitch(false); 296 | } 297 | } else { 298 | serviceStop(); 299 | } 300 | if (switchButton.isEnabled()) { 301 | switchButton.setEnabled(false); 302 | handler.postDelayed(() -> switchButton.setEnabled(true), 1000); 303 | } 304 | } 305 | } 306 | 307 | @Override 308 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 309 | super.onActivityResult(requestCode, resultCode, data); 310 | if (resultCode == Activity.RESULT_OK) { 311 | serviceStart(); 312 | } else { 313 | cancelStart(); 314 | Log.e(TAG, "Failed to start VpnService"); 315 | } 316 | } 317 | 318 | private void onStateChanged(int s, String m) { 319 | handler.post(() -> { 320 | if (state.ordinal() != s) { 321 | state = Constants.State.values()[s]; 322 | switch (state) { 323 | case CONNECTING: 324 | if (progressDialog == null) { 325 | progressDialog = ProgressDialog 326 | .show(MainActivity.this, "", getString(R.string.connecting), true, true); 327 | } 328 | prefsFragment.setPreferenceEnabled(false); 329 | break; 330 | case CONNECTED: 331 | clearDialog(); 332 | changeSwitch(true); 333 | prefsFragment.setPreferenceEnabled(false); 334 | break; 335 | case STOPPED: 336 | clearDialog(); 337 | changeSwitch(false); 338 | if (m != null) { 339 | Snackbar.make(drawer.getDrawerLayout(), 340 | String.format(getString(R.string.vpn_error), m), 341 | Snackbar.LENGTH_LONG) 342 | .show(); 343 | } 344 | prefsFragment.setPreferenceEnabled(true); 345 | break; 346 | case STOPPING: 347 | if (progressDialog == null) { 348 | progressDialog = ProgressDialog 349 | .show(MainActivity.this, "", getString(R.string.stopping), true, true); 350 | } 351 | break; 352 | default: 353 | break; 354 | } 355 | } 356 | }); 357 | } 358 | 359 | private void attachService() { 360 | if (vpnService == null) { 361 | Class s = xSocksVpnService.class; 362 | Intent intent = new Intent(this, s); 363 | intent.setAction(Constants.Action.SERVICE); 364 | bindService(intent, connection, Context.BIND_AUTO_CREATE); 365 | startService(new Intent(this, s)); 366 | } 367 | } 368 | 369 | private void deattachService() { 370 | if (vpnService != null) { 371 | try { 372 | vpnService.unregisterCallback(callback); 373 | } catch (RemoteException e) { 374 | // ignore 375 | } 376 | vpnService = null; 377 | unbindService(connection); 378 | } 379 | } 380 | 381 | private ArrayList getProfileList() { 382 | ArrayList items = new ArrayList<>(); 383 | List profiles = profileManager.getAllProfile(); 384 | if (profiles != null) { 385 | for (Profile profile : profiles) { 386 | ProfileDrawerItem item = new ProfileDrawerItem() 387 | .withName(profile.getName()) 388 | .withProfileId(profile.getId()) 389 | .withIcon(GoogleMaterial.Icon.gmd_computer) 390 | .withCheckable(false); 391 | items.add(item); 392 | } 393 | } 394 | return items; 395 | } 396 | 397 | private ArrayList getDrawerItems() { 398 | SectionDrawerItem profileItem = new SectionDrawerItem().withName(R.string.profiles) 399 | .withTextColorRes(R.color.accentColor).setDivider(false); 400 | 401 | PrimaryDrawerItem addItem = new PrimaryDrawerItem().withName(R.string.add_profile) 402 | .withIdentifier(Action.ADD.ordinal()).withIcon(GoogleMaterial.Icon.gmd_add_circle_outline) 403 | .withCheckable(false); 404 | 405 | SectionDrawerItem settingItem = new SectionDrawerItem().withName(R.string.settings) 406 | .withTextColorRes(R.color.accentColor); 407 | 408 | PrimaryDrawerItem recoveryItem = new PrimaryDrawerItem().withName(R.string.recovery) 409 | .withIdentifier(Action.RECOVERY.ordinal()).withIcon(GoogleMaterial.Icon.gmd_restore) 410 | .withCheckable(false); 411 | 412 | PrimaryDrawerItem aboutItem = new PrimaryDrawerItem().withName(R.string.about) 413 | .withIdentifier(Action.ABOUT.ordinal()).withIcon(GoogleMaterial.Icon.gmd_info_outline) 414 | .withCheckable(false); 415 | 416 | ArrayList items = new ArrayList<>(); 417 | items.add(profileItem); 418 | items.addAll(getProfileList()); 419 | items.add(addItem); 420 | items.add(settingItem); 421 | items.add(recoveryItem); 422 | items.add(aboutItem); 423 | return items; 424 | } 425 | 426 | private void updateAdapter() { 427 | ArrayList items = getDrawerItems(); 428 | drawer.setItems(items); 429 | } 430 | 431 | private void addProfile() { 432 | showProgress(getString(R.string.loading)); 433 | Handler h = createDialogHandler(); 434 | handler.postDelayed(() -> { 435 | profileManager.save(); 436 | currentProfile = profileManager.create(); 437 | updateAdapter(); 438 | prefsFragment.updatePreferenceScreen(currentProfile); 439 | h.sendEmptyMessage(0); 440 | }, 600); 441 | } 442 | 443 | private void delProfile(int id) { 444 | profileManager.delProfile(id); 445 | List profiles = profileManager.getAllProfile(); 446 | int profileId = profiles != null && profiles.size() > 0 ? profiles.get(0).getId() : -1; 447 | currentProfile = profileManager.load(profileId); 448 | updateAdapter(); 449 | prefsFragment.updatePreferenceScreen(currentProfile); 450 | } 451 | 452 | private void updateProfile(int id) { 453 | showProgress(getString(R.string.loading)); 454 | Handler h = createDialogHandler(); 455 | handler.postDelayed(() -> { 456 | currentProfile = profileManager.reload(id); 457 | adapter.notifyDataSetChanged(); 458 | prefsFragment.updatePreferenceScreen(currentProfile); 459 | h.sendEmptyMessage(0); 460 | }, 600); 461 | } 462 | 463 | private void showAbout() { 464 | Intent intent = new Intent(this, AboutActivity.class); 465 | try { 466 | startActivity(intent); 467 | } catch (ActivityNotFoundException ex) { 468 | Snackbar.make(drawer.getDrawerLayout(), 469 | "There are no email applications installed.", 470 | Snackbar.LENGTH_LONG) 471 | .setActionTextColor(Color.RED) 472 | .show(); 473 | } 474 | } 475 | 476 | private Handler createDialogHandler() { 477 | return new Handler() { 478 | @Override 479 | public void handleMessage(Message msg) { 480 | clearDialog(); 481 | } 482 | }; 483 | } 484 | 485 | private void clearDialog() { 486 | if (progressDialog != null) { 487 | progressDialog.dismiss(); 488 | progressDialog = null; 489 | } 490 | } 491 | 492 | private void showProgress(String msg) { 493 | progressDialog = ProgressDialog.show(this, "", msg, true, false); 494 | } 495 | 496 | private void prepareStartService() { 497 | showProgress(getString(R.string.connecting)); 498 | Async.toAsync(() -> { 499 | int REQUEST_CONNECT = 1; 500 | Intent intent = VpnService.prepare(this); 501 | if (intent != null) { 502 | startActivityForResult(intent, REQUEST_CONNECT); 503 | } else { 504 | onActivityResult(REQUEST_CONNECT, Activity.RESULT_OK, null); 505 | } 506 | }).call(); 507 | } 508 | 509 | private void serviceStart() { 510 | try { 511 | vpnService.start(ConfigUtils.load(settings)); 512 | } catch (RemoteException e) { 513 | // ignore 514 | } 515 | changeSwitch(false); 516 | } 517 | 518 | 519 | private void install() { 520 | try { 521 | AssetManager assetManager = getAssets(); 522 | InputStream in = assetManager.open("iptable.txt"); 523 | OutputStream out = new FileOutputStream(Constants.Path.BASE + "iptable.txt"); 524 | Log.d("ipFile",Constants.Path.BASE + "iptable.txt"); 525 | copyFile(in, out); 526 | in.close(); 527 | out.flush(); 528 | out.close(); 529 | }catch (Exception e){ 530 | e.printStackTrace(); 531 | } 532 | } 533 | 534 | private void copyFile(InputStream in, OutputStream out) throws IOException { 535 | byte[] buffer = new byte[1024]; 536 | int read; 537 | while ((read = in.read(buffer)) != -1) { 538 | out.write(buffer, 0, read); 539 | } 540 | } 541 | 542 | 543 | private void serviceStop() { 544 | if (vpnService != null) { 545 | try { 546 | vpnService.stop(); 547 | } catch (RemoteException e) { 548 | // ignore 549 | } 550 | } 551 | } 552 | 553 | private void cancelStart() { 554 | clearDialog(); 555 | changeSwitch(false); 556 | } 557 | 558 | private void recovery() { 559 | progressDialog = ProgressDialog.show(this, "", getString(R.string.initializing), true, false); 560 | serviceStop(); 561 | Async.toAsync(this::reset).call().observeOn(AndroidSchedulers.mainThread()).subscribe(o -> clearDialog()); 562 | } 563 | 564 | 565 | 566 | 567 | 568 | private void reset() { 569 | install(); 570 | } 571 | 572 | 573 | 574 | private String getVersionName() { 575 | String version; 576 | try { 577 | PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0); 578 | version = pi.versionName; 579 | } catch (PackageManager.NameNotFoundException e) { 580 | version = "Package name not found"; 581 | } 582 | return version; 583 | } 584 | 585 | private boolean isTextEmpty(String s, String msg) { 586 | if (s == null || s.length() <= 0) { 587 | Snackbar.make(drawer.getDrawerLayout(), msg, Snackbar.LENGTH_LONG).show(); 588 | return true; 589 | } 590 | return false; 591 | } 592 | 593 | private boolean checkText(String key) { 594 | String text = settings.getString(key, ""); 595 | if (key.equals(Constants.Key.proxy)) { 596 | return !isTextEmpty(text, getString(R.string.proxy_empty)); 597 | } else if (key.equals(Constants.Key.sitekey)) { 598 | return !isTextEmpty(text, getString(R.string.password_empty)); 599 | } 600 | return false; 601 | } 602 | 603 | private boolean checkNumber(String key, Boolean low) { 604 | String text = settings.getString(key, ""); 605 | if (isTextEmpty(text, getString(R.string.port_empty))) return false; 606 | try { 607 | int port = Integer.valueOf(text); 608 | if (!low && port <= 1024) { 609 | Snackbar.make(drawer.getDrawerLayout(), getString(R.string.port_alert), Snackbar.LENGTH_LONG).show(); 610 | return false; 611 | } 612 | } catch (Exception ex) { 613 | Snackbar.make(drawer.getDrawerLayout(), getString(R.string.port_alert), Snackbar.LENGTH_LONG).show(); 614 | return false; 615 | } 616 | return true; 617 | } 618 | 619 | private boolean isReady() { 620 | return checkText(Constants.Key.proxy) && 621 | checkText(Constants.Key.sitekey) && 622 | checkNumber(Constants.Key.remotePort, true) && 623 | vpnService != null; 624 | } 625 | 626 | private void changeSwitch(Boolean checked) { 627 | switchButton.setOnCheckedChangeListener(null); 628 | switchButton.setChecked(checked); 629 | if (switchButton.isEnabled()) { 630 | switchButton.setEnabled(false); 631 | handler.postDelayed(() -> switchButton.setEnabled(true), 1000); 632 | } 633 | switchButton.setOnCheckedChangeListener(this); 634 | } 635 | } 636 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/ui/PrefsFragment.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.ui; 2 | 3 | import android.content.ActivityNotFoundException; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.database.Cursor; 8 | import android.net.Uri; 9 | import android.os.Build; 10 | import android.os.Bundle; 11 | import android.preference.CheckBoxPreference; 12 | import android.preference.ListPreference; 13 | import android.preference.Preference; 14 | import android.preference.PreferenceFragment; 15 | import android.preference.PreferenceManager; 16 | import android.util.Log; 17 | import android.widget.Toast; 18 | 19 | import io.github.xSocks.R; 20 | import io.github.xSocks.aidl.Config; 21 | import io.github.xSocks.model.Profile; 22 | import io.github.xSocks.preferences.PasswordEditTextPreference; 23 | import io.github.xSocks.preferences.ProfileEditTextPreference; 24 | import io.github.xSocks.preferences.SummaryEditTextPreference; 25 | import io.github.xSocks.utils.ConfigUtils; 26 | import io.github.xSocks.utils.Constants; 27 | import io.github.xSocks.utils.Utils; 28 | 29 | public class PrefsFragment extends PreferenceFragment { 30 | public static String[] PROXY_PREFS = { 31 | Constants.Key.profileName, 32 | Constants.Key.proxy, 33 | Constants.Key.remotePort, 34 | Constants.Key.localPort, 35 | Constants.Key.protocol, 36 | Constants.Key.sitekey, 37 | }; 38 | 39 | public static String[] FEATURE_PREFS = { 40 | Constants.Key.route, 41 | Constants.Key.isGlobalProxy, 42 | Constants.Key.proxyedApps, 43 | Constants.Key.isUdpDns, 44 | Constants.Key.isAutoConnect 45 | }; 46 | 47 | @Override 48 | public void onCreate(Bundle savedInstanceState) { 49 | super.onCreate(savedInstanceState); 50 | Config config= ConfigUtils.load( this.getPreferenceManager().getSharedPreferences()); 51 | addPreferencesFromResource(R.xml.preferences); 52 | 53 | findPreference("protocol").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { 54 | @Override 55 | public boolean onPreferenceChange(Preference preference, Object o) { 56 | if( o.equals("wss")||o.equals("http2")) { 57 | findPreference("caFile").setEnabled(true); 58 | findPreference("verifycert").setEnabled(true); 59 | }else{ 60 | findPreference("verifycert").setEnabled(false); 61 | findPreference("caFile").setEnabled(false); 62 | } 63 | findPreference("tuntype").setEnabled(true); 64 | return true; 65 | } 66 | }); 67 | findPreference("caFile").setEnabled(false); 68 | findPreference("verifycert").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { 69 | @Override 70 | public boolean onPreferenceChange(Preference preference, Object o) { 71 | if( o.equals("self_signed")) { 72 | findPreference("caFile").setEnabled(true); 73 | }else{ 74 | findPreference("caFile").setEnabled(false); 75 | } 76 | return true; 77 | } 78 | }); 79 | 80 | if(config.verifyCert.equals("self_signed")){ 81 | findPreference("caFile").setEnabled(true); 82 | }else{ 83 | findPreference("caFile").setEnabled(false); 84 | } 85 | findPreference("caFile").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { 86 | @Override 87 | public boolean onPreferenceClick(Preference preference) { 88 | fileChooser(); 89 | return false; 90 | } 91 | }); 92 | findPreference("caFile").setSummary(config.caFile); 93 | } 94 | 95 | private static final int CHOOSE_FILE_CODE = 0; 96 | 97 | /*select self cert*/ 98 | public void fileChooser(){ 99 | Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 100 | intent.setType("file/pem"); 101 | try { 102 | startActivityForResult(intent, CHOOSE_FILE_CODE); 103 | } catch (ActivityNotFoundException e) { 104 | // Toast.makeText(this, "亲,木有文件管理器啊-_-!!", Toast.LENGTH_SHORT).show(); 105 | // Toast.makeText(this,"ddd",Toast.LENGTH_SHORT).show(); 106 | Log.d("tag","木有文件管理器"); 107 | } 108 | } 109 | /*select file back*/ 110 | @Override 111 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 112 | super.onActivityResult(requestCode, resultCode, data); 113 | 114 | 115 | //通过data.getData()方法返回的是Uri 116 | 117 | 118 | String x= getPath(data.getData());//.getPath(); 119 | Log.d("tag","path:"+x); 120 | 121 | /* 122 | if (resultCode == Activity.RESULT_OK) { 123 | if (requestCode == CHOOSE_FILE_CODE) { 124 | Uri uri = data.getData(); 125 | } 126 | } else { 127 | Log.e(TAG1, "onActivityResult() error, resultCode: " + resultCode); 128 | }*/ 129 | 130 | findPreference("caFile").setSummary(x); 131 | 132 | findPreference("caFile").getEditor().putString("caFile",x).commit(); 133 | 134 | } 135 | 136 | 137 | public String getPath(Uri uri) { 138 | if ("content".equalsIgnoreCase(uri.getScheme())) { 139 | String[] projection = {"_data"}; 140 | Cursor cursor = null; 141 | try { 142 | cursor = getActivity().getContentResolver().query(uri, projection, null, null, null); 143 | int column_index = cursor.getColumnIndexOrThrow("_data"); 144 | if (cursor.moveToFirst()) { 145 | return cursor.getString(column_index); 146 | } 147 | } catch (Exception e) { 148 | e.printStackTrace(); 149 | } 150 | } else if ("file".equalsIgnoreCase(uri.getScheme())) { 151 | return uri.getPath(); 152 | } 153 | return null; 154 | } 155 | 156 | 157 | 158 | 159 | public void setPreferenceEnabled(boolean enabled) { 160 | for (String name : PROXY_PREFS) { 161 | Preference pref = findPreference(name); 162 | if (pref != null) { 163 | pref.setEnabled(enabled); 164 | } 165 | } 166 | for (String name : FEATURE_PREFS) { 167 | Preference pref = findPreference(name); 168 | if (pref != null) { 169 | if (name.equals(Constants.Key.isGlobalProxy) || name.equals(Constants.Key.proxyedApps)) { 170 | pref.setEnabled(enabled && (Utils.isLollipopOrAbove())); 171 | 172 | } else { 173 | pref.setEnabled(enabled); 174 | } 175 | } 176 | } 177 | } 178 | 179 | public void updatePreferenceScreen(Profile profile) { 180 | for (String name : PROXY_PREFS) { 181 | Preference pref = findPreference(name); 182 | if (pref != null) { 183 | updatePreference(pref, name, profile); 184 | } 185 | } 186 | for (String name : FEATURE_PREFS) { 187 | Preference pref = findPreference(name); 188 | if (pref != null) { 189 | updatePreference(pref, name, profile); 190 | } 191 | } 192 | } 193 | 194 | private void updateListPreference(Preference pref, String value) { 195 | ((ListPreference)pref).setValue(value); 196 | } 197 | 198 | private void updatePasswordEditTextPreference(Preference pref, String value) { 199 | pref.setSummary(value); 200 | ((PasswordEditTextPreference)pref).setText(value); 201 | } 202 | 203 | private void updateSummaryEditTextPreference(Preference pref, String value) { 204 | pref.setSummary(value); 205 | ((SummaryEditTextPreference)pref).setText(value); 206 | } 207 | 208 | private void updateProfileEditTextPreference(Preference pref, String value) { 209 | ((ProfileEditTextPreference)pref).resetSummary(value); 210 | ((ProfileEditTextPreference)pref).setText(value); 211 | } 212 | 213 | private void updateCheckBoxPreference(Preference pref, boolean value) { 214 | ((CheckBoxPreference)pref).setChecked(value); 215 | } 216 | 217 | public void updatePreference(Preference pref, String name, Profile profile) { 218 | switch (name) { 219 | case Constants.Key.profileName: updateProfileEditTextPreference(pref, profile.getName()); break; 220 | case Constants.Key.proxy: updateSummaryEditTextPreference(pref, profile.getProxy()); break; 221 | case Constants.Key.protocol:updateListPreference(pref, profile.getProtocol());break; 222 | case Constants.Key.remotePort: updateSummaryEditTextPreference(pref, Integer.toString(profile.getRemotePort())); break; 223 | case Constants.Key.localPort: updateSummaryEditTextPreference(pref, Integer.toString(profile.getLocalPort())); break; 224 | case Constants.Key.sitekey: updatePasswordEditTextPreference(pref, profile.getPassword()); break; 225 | case Constants.Key.route: updateListPreference(pref, profile.getRoute()); break; 226 | case Constants.Key.isGlobalProxy: updateCheckBoxPreference(pref, profile.isGlobal()); break; 227 | case Constants.Key.isUdpDns: updateCheckBoxPreference(pref, profile.isUdpdns()); break; 228 | default: break; 229 | } 230 | } 231 | 232 | 233 | } 234 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/ui/ProfileDrawerItem.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.ui; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import com.mikepenz.materialdrawer.R; 12 | import com.mikepenz.materialdrawer.model.BaseDrawerItem; 13 | import com.mikepenz.materialdrawer.model.interfaces.ColorfulBadgeable; 14 | import com.mikepenz.materialdrawer.util.PressedEffectStateListDrawable; 15 | import com.mikepenz.materialdrawer.util.UIUtils; 16 | 17 | public class ProfileDrawerItem extends BaseDrawerItem implements ColorfulBadgeable { 18 | private String description; 19 | private int descriptionRes = -1; 20 | 21 | private String badge; 22 | private int badgeTextColor = 0; 23 | private int id; 24 | 25 | public ProfileDrawerItem withProfileId(int id) { 26 | this.id = id; 27 | return this; 28 | } 29 | 30 | public int getProfileId() { 31 | return id; 32 | } 33 | 34 | public ProfileDrawerItem withDescription(String description) { 35 | this.description = description; 36 | return this; 37 | } 38 | 39 | public ProfileDrawerItem withDescription(int descriptionRes) { 40 | this.descriptionRes = descriptionRes; 41 | return this; 42 | } 43 | 44 | @Override 45 | public ProfileDrawerItem withBadge(String badge) { 46 | this.badge = badge; 47 | return this; 48 | } 49 | 50 | @Override 51 | public ProfileDrawerItem withBadgeTextColor(int color) { 52 | this.badgeTextColor = color; 53 | return this; 54 | } 55 | 56 | public String getDescription() { 57 | return description; 58 | } 59 | 60 | public void setDescription(String description) { 61 | this.description = description; 62 | } 63 | 64 | public int getDescriptionRes() { 65 | return descriptionRes; 66 | } 67 | 68 | public void setDescriptionRes(int descriptionRes) { 69 | this.descriptionRes = descriptionRes; 70 | } 71 | 72 | public String getBadge() { 73 | return badge; 74 | } 75 | 76 | @Override 77 | public int getBadgeTextColor() { 78 | return badgeTextColor; 79 | } 80 | 81 | @Override 82 | public void setBadgeTextColor(int color) { 83 | this.badgeTextColor = color; 84 | } 85 | 86 | @Override 87 | public void setBadge(String badge) { 88 | this.badge = badge; 89 | } 90 | 91 | 92 | @Override 93 | public String getType() { 94 | return "PROFILE_ITEM"; 95 | } 96 | 97 | @Override 98 | public int getLayoutRes() { 99 | return R.layout.material_drawer_item_primary; 100 | } 101 | 102 | @Override 103 | public View convertView(LayoutInflater inflater, View convertView, ViewGroup parent) { 104 | Context ctx = parent.getContext(); 105 | 106 | //get the viewHolder 107 | ViewHolder viewHolder; 108 | if (convertView == null) { 109 | convertView = inflater.inflate(getLayoutRes(), parent, false); 110 | viewHolder = new ViewHolder(convertView); 111 | convertView.setTag(viewHolder); 112 | } else { 113 | viewHolder = (ViewHolder) convertView.getTag(); 114 | } 115 | 116 | //get the correct color for the background 117 | int selectedColor = UIUtils.decideColor(ctx, getSelectedColor(), getSelectedColorRes(), R.attr.material_drawer_selected, R.color.material_drawer_selected); 118 | //get the correct color for the text 119 | int color; 120 | if (this.isEnabled()) { 121 | color = UIUtils.decideColor(ctx, getTextColor(), getTextColorRes(), R.attr.material_drawer_primary_text, R.color.material_drawer_primary_text); 122 | } else { 123 | color = UIUtils.decideColor(ctx, getDisabledTextColor(), getDisabledTextColorRes(), R.attr.material_drawer_hint_text, R.color.material_drawer_hint_text); 124 | } 125 | int selectedTextColor = UIUtils.decideColor(ctx, getSelectedTextColor(), getSelectedTextColorRes(), R.attr.material_drawer_selected_text, R.color.material_drawer_selected_text); 126 | //get the correct color for the icon 127 | int iconColor; 128 | if (this.isEnabled()) { 129 | iconColor = UIUtils.decideColor(ctx, getIconColor(), getIconColorRes(), R.attr.material_drawer_primary_icon, R.color.material_drawer_primary_icon); 130 | } else { 131 | iconColor = UIUtils.decideColor(ctx, getDisabledIconColor(), getDisabledIconColorRes(), R.attr.material_drawer_hint_text, R.color.material_drawer_hint_text); 132 | } 133 | int selectedIconColor = UIUtils.decideColor(ctx, getSelectedIconColor(), getSelectedIconColorRes(), R.attr.material_drawer_selected_text, R.color.material_drawer_selected_text); 134 | 135 | //set the background for the item 136 | UIUtils.setBackground(viewHolder.view, UIUtils.getDrawerItemBackground(selectedColor)); 137 | 138 | //set the text for the name 139 | if (this.getNameRes() != -1) { 140 | viewHolder.name.setText(this.getNameRes()); 141 | } else { 142 | viewHolder.name.setText(this.getName()); 143 | } 144 | 145 | //set the text for the description or hide 146 | viewHolder.description.setVisibility(View.VISIBLE); 147 | if (this.getDescriptionRes() != -1) { 148 | viewHolder.description.setText(this.getDescriptionRes()); 149 | } else if (this.getDescription() != null) { 150 | viewHolder.description.setText(this.getDescription()); 151 | } else { 152 | viewHolder.description.setVisibility(View.GONE); 153 | } 154 | 155 | //set the text for the badge or hide 156 | if (getBadge() != null) { 157 | viewHolder.badge.setText(getBadge()); 158 | viewHolder.badge.setVisibility(View.VISIBLE); 159 | } else { 160 | viewHolder.badge.setVisibility(View.GONE); 161 | } 162 | 163 | //set the colors for textViews 164 | viewHolder.name.setTextColor(UIUtils.getTextColorStateList(color, selectedTextColor)); 165 | viewHolder.description.setTextColor(UIUtils.getTextColorStateList(color, selectedTextColor)); 166 | if (badgeTextColor != 0) { 167 | viewHolder.badge.setTextColor(badgeTextColor); 168 | } else { 169 | viewHolder.badge.setTextColor(UIUtils.getTextColorStateList(color, selectedTextColor)); 170 | } 171 | 172 | //define the typeface for our textViews 173 | if (getTypeface() != null) { 174 | viewHolder.name.setTypeface(getTypeface()); 175 | viewHolder.description.setTypeface(getTypeface()); 176 | viewHolder.badge.setTypeface(getTypeface()); 177 | } 178 | 179 | //get the drawables for our icon 180 | Drawable icon = UIUtils.decideIcon(ctx, getIcon(), getIIcon(), getIconRes(), iconColor, isIconTinted()); 181 | Drawable selectedIcon = UIUtils.decideIcon(ctx, getSelectedIcon(), getIIcon(), getSelectedIconRes(), selectedIconColor, isIconTinted()); 182 | 183 | //if we have an icon then we want to set it 184 | if (icon != null) { 185 | //if we got a different color for the selectedIcon we need a StateList 186 | if (selectedIcon != null) { 187 | viewHolder.icon.setImageDrawable(UIUtils.getIconStateList(icon, selectedIcon)); 188 | } else if (isIconTinted()) { 189 | viewHolder.icon.setImageDrawable(new PressedEffectStateListDrawable(icon, iconColor, selectedIconColor)); 190 | } else { 191 | viewHolder.icon.setImageDrawable(icon); 192 | } 193 | //make sure we display the icon 194 | viewHolder.icon.setVisibility(View.VISIBLE); 195 | } else { 196 | //hide the icon 197 | viewHolder.icon.setVisibility(View.GONE); 198 | } 199 | 200 | return convertView; 201 | } 202 | 203 | private static class ViewHolder { 204 | private View view; 205 | private ImageView icon; 206 | private TextView name; 207 | private TextView description; 208 | private TextView badge; 209 | 210 | private ViewHolder(View view) { 211 | this.view = view; 212 | this.icon = (ImageView) view.findViewById(R.id.icon); 213 | this.name = (TextView) view.findViewById(R.id.name); 214 | this.description = (TextView) view.findViewById(R.id.description); 215 | this.badge = (TextView) view.findViewById(R.id.badge); 216 | } 217 | } 218 | } 219 | 220 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/ui/xSocksRunnerActivity.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.ui; 2 | 3 | import android.app.Activity; 4 | import android.app.KeyguardManager; 5 | import android.content.BroadcastReceiver; 6 | import android.content.ComponentName; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.content.IntentFilter; 10 | import android.content.ServiceConnection; 11 | import android.content.SharedPreferences; 12 | import android.net.VpnService; 13 | import android.os.Bundle; 14 | import android.os.Handler; 15 | import android.os.IBinder; 16 | import android.os.RemoteException; 17 | import android.preference.PreferenceManager; 18 | import android.util.Log; 19 | 20 | import io.github.xSocks.aidl.IxSocksService; 21 | import io.github.xSocks.service.xSocksVpnService; 22 | import io.github.xSocks.utils.ConfigUtils; 23 | import io.github.xSocks.utils.Constants; 24 | 25 | public class xSocksRunnerActivity extends Activity { 26 | 27 | private static final String TAG = "xSocks"; 28 | 29 | private SharedPreferences settings = null; 30 | private BroadcastReceiver receiver; 31 | private IxSocksService bgService = null; 32 | 33 | Handler handler = new Handler(); 34 | 35 | ServiceConnection connection = new ServiceConnection() { 36 | @Override 37 | public void onServiceConnected(ComponentName componentName, IBinder service) { 38 | bgService = IxSocksService.Stub.asInterface(service); 39 | handler.postDelayed(xSocksRunnerActivity.this::startBackgroundService, 1000); 40 | } 41 | 42 | @Override 43 | public void onServiceDisconnected(ComponentName componentName) { 44 | bgService = null; 45 | } 46 | }; 47 | 48 | private void startBackgroundService() { 49 | Intent intent = VpnService.prepare(xSocksRunnerActivity.this); 50 | int REQUEST_CONNECT = 1; 51 | if (intent != null) { 52 | startActivityForResult(intent, REQUEST_CONNECT); 53 | } else { 54 | onActivityResult(REQUEST_CONNECT, Activity.RESULT_OK, null); 55 | } 56 | } 57 | 58 | private void attachService() { 59 | if (bgService == null) { 60 | Intent intent = new Intent(this, xSocksVpnService.class); 61 | intent.setAction(Constants.Action.SERVICE); 62 | bindService(intent, connection, Context.BIND_AUTO_CREATE); 63 | startService(new Intent(this, xSocksVpnService.class)); 64 | } 65 | } 66 | 67 | private void deattachService() { 68 | if (bgService != null) { 69 | unbindService(connection); 70 | bgService = null; 71 | } 72 | } 73 | 74 | @Override 75 | protected void onCreate(Bundle savedInstanceState) { 76 | super.onCreate(savedInstanceState); 77 | KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); 78 | boolean locked = km.inKeyguardRestrictedInputMode(); 79 | if (locked) { 80 | IntentFilter filter = new IntentFilter(Intent.ACTION_USER_PRESENT); 81 | receiver = new BroadcastReceiver() { 82 | @Override 83 | public void onReceive(Context context, Intent intent) { 84 | if (intent.getAction().equalsIgnoreCase(Intent.ACTION_USER_PRESENT)) { 85 | attachService(); 86 | } 87 | } 88 | }; 89 | registerReceiver(receiver, filter); 90 | } else { 91 | attachService(); 92 | } 93 | } 94 | 95 | @Override 96 | protected void onDestroy() { 97 | super.onDestroy(); 98 | deattachService(); 99 | if (receiver != null) { 100 | unregisterReceiver(receiver); 101 | receiver = null; 102 | } 103 | } 104 | 105 | @Override 106 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 107 | switch (resultCode) { 108 | case Activity.RESULT_OK: 109 | if (bgService != null) { 110 | if (settings == null) { 111 | settings = PreferenceManager.getDefaultSharedPreferences(this); 112 | } 113 | try { 114 | bgService.start(ConfigUtils.load(settings)); 115 | } catch (RemoteException e) { 116 | Log.e(TAG, "Failed to start VpnService"); 117 | } 118 | } 119 | break; 120 | default: 121 | Log.e(TAG, "Failed to start VpnService"); 122 | break; 123 | } 124 | finish(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/utils/ConfigUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.utils; 2 | 3 | import android.content.SharedPreferences; 4 | import android.util.Log; 5 | 6 | import io.github.xSocks.aidl.Config; 7 | 8 | public class ConfigUtils { 9 | 10 | public static boolean printToFile(java.io.File file, String content) { 11 | try { 12 | java.io.PrintWriter printer = new java.io.PrintWriter(file); 13 | printer.println(content); 14 | printer.flush(); 15 | return true; 16 | } catch (Exception ex) { 17 | Log.e("XSOCKS", ex.getMessage()); 18 | return false; 19 | } 20 | } 21 | 22 | public static Config load(SharedPreferences settings) { 23 | boolean isGlobalProxy = settings.getBoolean(Constants.Key.isGlobalProxy, false); 24 | 25 | 26 | 27 | boolean isBypassApps = settings.getBoolean(Constants.Key.isBypassApps, false); 28 | boolean isUdpDns = settings.getBoolean(Constants.Key.isUdpDns, false); 29 | 30 | 31 | String verifyCert = settings.getString(Constants.Key.verifyCert, "skip"); 32 | String tunType = settings.getString(Constants.Key.tunType, "1"); 33 | 34 | String profileName = settings.getString(Constants.Key.profileName, "Default"); 35 | String proxy = settings.getString(Constants.Key.proxy, ""); 36 | String sitekey = settings.getString(Constants.Key.sitekey, ""); 37 | String route = settings.getString(Constants.Key.route, "all"); 38 | String protocol = settings.getString(Constants.Key.protocol, "wss"); 39 | String caFile = settings.getString(Constants.Key.caFile, ""); 40 | 41 | int remotePort = Integer.parseInt(settings.getString(Constants.Key.remotePort, "443")); 42 | int localPort = Integer.parseInt(settings.getString(Constants.Key.localPort, "1080")); 43 | 44 | String proxiedAppString = settings.getString(Constants.Key.proxied, ""); 45 | 46 | return new Config(isGlobalProxy, isBypassApps, isUdpDns, profileName, proxy, 47 | sitekey, proxiedAppString, route,protocol,caFile,verifyCert,tunType, remotePort, localPort); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/utils/Console.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.utils; 2 | 3 | public class Console { 4 | 5 | public static void runCommand(String[] cmds) { 6 | for (String cmd : cmds) { 7 | execCommand(cmd); 8 | } 9 | } 10 | 11 | public static Process execCommand(String command) { 12 | try { 13 | return Runtime.getRuntime().exec(command); 14 | }catch (java.io.IOException e){ 15 | e.printStackTrace(); 16 | } 17 | return null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/utils/Constants.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.utils; 2 | 3 | public class Constants { 4 | public class Route { 5 | public static final String ALL = "all"; 6 | public static final String BYPASS_LAN = "bypass-lan"; 7 | public static final String BYPASS_CHN = "bypass-china"; 8 | } 9 | 10 | public enum State { 11 | INIT, 12 | CONNECTING, 13 | CONNECTED, 14 | STOPPING, 15 | STOPPED; 16 | 17 | public static boolean isAvailable(int state) { 18 | return state != CONNECTED.ordinal() && state != CONNECTING.ordinal(); 19 | } 20 | } 21 | 22 | public static String[] executables = {"socksX-cli"}; 23 | 24 | public class Path { 25 | public static final String BASE = "/data/data/io.github.xSocks/"; 26 | } 27 | 28 | public class Action { 29 | public static final String SERVICE = "io.github.xSocks.SERVICE"; 30 | public static final String CLOSE = "io.github.xSocks.CLOSE"; 31 | public static final String UPDATE_PREFS = "io.github.xSocks.ACTION_UPDATE_PREFS"; 32 | } 33 | 34 | public class Key { 35 | public static final String profileId = "profileId"; 36 | public static final String profileName = "profileName"; 37 | 38 | public static final String proxied = "Proxyed"; 39 | 40 | public static final String status = "status"; 41 | public static final String proxyedApps = "proxyedApps"; 42 | public static final String route = "route"; 43 | 44 | public static final String isRunning = "isRunning"; 45 | public static final String isAutoConnect = "isAutoConnect"; 46 | 47 | public static final String isGlobalProxy = "isGlobalProxy"; 48 | public static final String isBypassApps = "isBypassApps"; 49 | public static final String isUdpDns = "isUdpDns"; 50 | public static final String verifyCert="verifycert"; 51 | public static final String tunType="tuntype"; 52 | 53 | public static final String protocol="protocol"; 54 | public static final String caFile="caFile"; 55 | public static final String proxy = "proxy"; 56 | public static final String sitekey = "sitekey"; 57 | public static final String remotePort = "remotePort"; 58 | public static final String localPort = "port"; 59 | } 60 | 61 | public class Scheme { 62 | public static final String APP = "app://"; 63 | } 64 | 65 | } 66 | 67 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/utils/MovementCheck.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.utils; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.text.Spannable; 5 | import android.text.method.LinkMovementMethod; 6 | import android.view.MotionEvent; 7 | import android.widget.TextView; 8 | 9 | public class MovementCheck extends LinkMovementMethod { 10 | 11 | @Override 12 | public boolean onTouchEvent(@NonNull TextView widget, @NonNull Spannable buffer, @NonNull MotionEvent event) { 13 | try { 14 | return super.onTouchEvent(widget, buffer, event); 15 | } catch (Exception ex) { 16 | return true; 17 | } 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks.utils; 2 | 3 | import android.content.Context; 4 | import android.content.pm.ApplicationInfo; 5 | import android.content.pm.PackageManager; 6 | import android.graphics.Bitmap; 7 | import android.graphics.Canvas; 8 | import android.graphics.drawable.BitmapDrawable; 9 | import android.graphics.drawable.Drawable; 10 | import android.os.Build; 11 | import android.support.v4.content.ContextCompat; 12 | import android.util.Log; 13 | 14 | import org.xbill.DNS.AAAARecord; 15 | import org.xbill.DNS.ARecord; 16 | import org.xbill.DNS.Lookup; 17 | import org.xbill.DNS.Record; 18 | import org.xbill.DNS.SimpleResolver; 19 | import org.xbill.DNS.Type; 20 | 21 | import java.io.BufferedReader; 22 | import java.io.File; 23 | import java.io.FileReader; 24 | import java.io.IOException; 25 | import java.io.InputStream; 26 | import java.io.OutputStream; 27 | import java.net.InetAddress; 28 | import java.net.InetSocketAddress; 29 | import java.net.NetworkInterface; 30 | import java.net.Socket; 31 | import java.net.SocketAddress; 32 | import java.net.SocketTimeoutException; 33 | import java.net.UnknownHostException; 34 | import java.util.Enumeration; 35 | import java.util.List; 36 | import java.util.regex.Pattern; 37 | 38 | import io.github.xSocks.BuildConfig; 39 | 40 | public class Utils { 41 | private static String TAG = "xSocks"; 42 | 43 | public static boolean isLollipopOrAbove() { 44 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; 45 | } 46 | 47 | public static String readFromFile(String name) { 48 | File file = new File(Constants.Path.BASE, name); 49 | StringBuilder text = new StringBuilder(); 50 | if (file.exists()) { 51 | try { 52 | BufferedReader br = new BufferedReader(new FileReader(file)); 53 | text.append(br.readLine()); 54 | br.close(); 55 | } 56 | catch (IOException e) { 57 | Log.e(TAG, "Can not read file: " + e.toString()); 58 | } 59 | } else { 60 | Log.e(TAG, "File not found: " + name); 61 | } 62 | return text.toString(); 63 | } 64 | 65 | public static String resolve(String host, int addrType) { 66 | try { 67 | Lookup lookup = new Lookup(host, addrType); 68 | SimpleResolver resolver = new SimpleResolver("114.114.114.114"); 69 | resolver.setTimeout(5); 70 | lookup.setResolver(resolver); 71 | Record[] result = lookup.run(); 72 | if (result == null) return null; 73 | 74 | List records = java.util.Arrays.asList(result); 75 | java.util.Collections.shuffle(records); 76 | for (Record record : records) { 77 | if (addrType == Type.A) { 78 | return ((ARecord) record).getAddress().getHostAddress(); 79 | } else if (addrType == Type.AAAA) { 80 | return ((AAAARecord) record).getAddress().getHostAddress(); 81 | } 82 | } 83 | 84 | } catch (Exception ex) { 85 | return null; 86 | } 87 | 88 | return null; 89 | } 90 | 91 | public static String resolve(String host) { 92 | try { 93 | InetAddress addr = InetAddress.getByName(host); 94 | return addr.getHostAddress(); 95 | } catch (Exception e) { 96 | return null; 97 | } 98 | } 99 | 100 | public static String resolve(String host, boolean enableIPv6) { 101 | String addr; 102 | if (enableIPv6 && isIPv6Support()) { 103 | addr = resolve(host, Type.AAAA); 104 | if (addr != null) { 105 | return addr; 106 | } 107 | } 108 | 109 | addr = resolve(host, Type.A); 110 | if (addr != null) { 111 | return addr; 112 | } 113 | 114 | addr = resolve(host); 115 | 116 | return addr; 117 | } 118 | 119 | public static Boolean checkConnect(String host, int port) { 120 | if (!Utils.isIPv4Address(host) && !Utils.isIPv6Address(host)) { 121 | String addr = Utils.resolve(host, true); 122 | if (addr != null) { 123 | host = addr; 124 | }else{ 125 | return false; 126 | } 127 | } 128 | Socket sClient = null; 129 | try { 130 | SocketAddress saAdd = new InetSocketAddress(host.trim(), port); 131 | sClient = new Socket(); 132 | sClient.connect(saAdd, 1000); 133 | } 134 | catch (UnknownHostException e) { 135 | return false; 136 | } 137 | catch (SocketTimeoutException e) { 138 | return false; 139 | } 140 | catch (IOException e) { 141 | return false; 142 | } 143 | catch (Exception e) { 144 | return false; 145 | } 146 | finally { 147 | try { 148 | if (sClient != null) { 149 | sClient.close(); 150 | } 151 | } 152 | catch (Exception e) { 153 | } 154 | } 155 | return true; 156 | } 157 | 158 | private static boolean isIPv6Support() { 159 | try { 160 | Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); 161 | while (interfaces.hasMoreElements()) { 162 | NetworkInterface intf = interfaces.nextElement(); 163 | Enumeration addrs = intf.getInetAddresses(); 164 | while (addrs.hasMoreElements()) { 165 | InetAddress addr = addrs.nextElement(); 166 | if (!addr.isLoopbackAddress() && !addr.isLinkLocalAddress()) { 167 | String sAddr = addr.getHostAddress().toUpperCase(); 168 | if (isIPv6Address(sAddr)) { 169 | if (BuildConfig.DEBUG) Log.d(TAG, "IPv6 address detected"); 170 | return true; 171 | } 172 | } 173 | } 174 | } 175 | } catch (Exception ex) { 176 | Log.e(TAG, "Failed to get interfaces' addresses.", ex); 177 | } 178 | return false; 179 | } 180 | 181 | public static void copyFile(InputStream in, OutputStream out) throws IOException { 182 | byte[] buffer = new byte[1024]; 183 | int read; 184 | 185 | while ((read = in.read(buffer)) != -1) { 186 | out.write(buffer, 0, read); 187 | } 188 | } 189 | 190 | private static final Pattern IPV4_PATTERN = 191 | Pattern.compile( 192 | "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$"); 193 | 194 | private static final Pattern IPV6_STD_PATTERN = 195 | Pattern.compile( 196 | "^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$"); 197 | 198 | private static final Pattern IPV6_HEX_COMPRESSED_PATTERN = 199 | Pattern.compile( 200 | "^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$"); 201 | 202 | public static boolean isIPv4Address(final String input) { 203 | return IPV4_PATTERN.matcher(input).matches(); 204 | } 205 | 206 | public static boolean isIPv6StdAddress(final String input) { 207 | return IPV6_STD_PATTERN.matcher(input).matches(); 208 | } 209 | 210 | public static boolean isIPv6HexCompressedAddress(final String input) { 211 | return IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches(); 212 | } 213 | 214 | public static boolean isIPv6Address(final String input) { 215 | return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input); 216 | } 217 | 218 | public static Bitmap drawableToBitmap(Drawable drawable) { 219 | if (drawable instanceof BitmapDrawable) { 220 | return ((BitmapDrawable)drawable).getBitmap(); 221 | } 222 | 223 | int width = drawable.getIntrinsicWidth() > 0 ? drawable.getIntrinsicWidth() : 1; 224 | int height = drawable.getIntrinsicWidth() > 0 ? drawable.getIntrinsicWidth() : 1; 225 | 226 | Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 227 | Canvas canvas = new Canvas(bitmap); 228 | drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 229 | drawable.draw(canvas); 230 | 231 | return bitmap; 232 | } 233 | 234 | public static Drawable getAppIcon(Context c, String packageName) { 235 | PackageManager pm = c.getPackageManager(); 236 | Drawable icon = ContextCompat.getDrawable(c, android.R.drawable.sym_def_app_icon); 237 | try { 238 | return pm.getApplicationIcon(packageName); 239 | } catch (PackageManager.NameNotFoundException e) { 240 | return icon; 241 | } 242 | } 243 | 244 | public static Drawable getAppIcon(Context c, int uid) { 245 | PackageManager pm = c.getPackageManager(); 246 | Drawable icon = ContextCompat.getDrawable(c, android.R.drawable.sym_def_app_icon); 247 | String[] packages = pm.getPackagesForUid(uid); 248 | if (packages != null) { 249 | if (packages.length >= 1) { 250 | try { 251 | ApplicationInfo appInfo = pm.getApplicationInfo(packages[0], 0); 252 | return pm.getApplicationIcon(appInfo); 253 | } catch (PackageManager.NameNotFoundException e) { 254 | Log.e(c.getPackageName(), "No package found matching with the uid " + uid); 255 | } 256 | } 257 | } else { 258 | Log.e(c.getPackageName(), "Package not found for uid " + uid); 259 | } 260 | return icon; 261 | } 262 | 263 | 264 | public static void pipe(InputStream in0,InputStream in1,OutputStream out1,OutputStream out2){ 265 | int a=0; 266 | byte[] buff=new byte[32767]; 267 | 268 | try { 269 | while ((a=in0.read(buff))!=-1){ 270 | out1.write(buff,0,a); 271 | } 272 | } catch (IOException e) { 273 | //捕捉到异常时将流写回原来的流 274 | try { 275 | while ((a=in1.read(buff))!=-1){ 276 | out2.write(buff,0,a); 277 | } 278 | } catch (IOException e1) { 279 | e1.printStackTrace(); 280 | } 281 | } 282 | 283 | 284 | } 285 | 286 | } 287 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/xSocksApplication.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks; 2 | 3 | import android.app.Application; 4 | 5 | public class xSocksApplication extends Application { 6 | 7 | @Override 8 | public void onCreate() { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/xSocks/xSocksReceiver.java: -------------------------------------------------------------------------------- 1 | package io.github.xSocks; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.content.pm.PackageManager; 8 | import android.preference.PreferenceManager; 9 | 10 | import io.github.xSocks.ui.xSocksRunnerActivity; 11 | import io.github.xSocks.utils.Constants; 12 | 13 | public class xSocksReceiver extends BroadcastReceiver{ 14 | @Override 15 | public void onReceive(Context context, Intent intent) { 16 | SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context); 17 | SharedPreferences status = context.getSharedPreferences(Constants.Key.status, Context.MODE_PRIVATE); 18 | 19 | String versionName; 20 | try { 21 | versionName = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; 22 | } catch (PackageManager.NameNotFoundException e) { 23 | versionName = null; 24 | } 25 | boolean isAutoConnect = settings.getBoolean(Constants.Key.isAutoConnect, false); 26 | boolean isInstalled = status.getBoolean(versionName, false); 27 | if (isAutoConnect && isInstalled) { 28 | String action = intent.getAction(); 29 | if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { 30 | Intent i = new Intent(context, xSocksRunnerActivity.class); 31 | i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 32 | i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 33 | context.startActivity(i); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosgo/xSocks-android/691dd126afa3b57c6bb2d4bd8dc0a62979eb1a94/app/src/main/res/drawable-hdpi/ic_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosgo/xSocks-android/691dd126afa3b57c6bb2d4bd8dc0a62979eb1a94/app/src/main/res/drawable-xhdpi/ic_logo.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 14 | 15 | 20 | 30 | 31 | 37 | 38 | 47 | 48 | 55 | 56 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_app_manager.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 15 | 18 | 19 | 29 | 30 | 42 | 43 | 44 | 45 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 15 | 18 | 19 | 29 | 30 | 40 | 41 | 42 | 43 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/res/layout/apps_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 16 | 17 | 26 | 27 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_app_manager.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosgo/xSocks-android/691dd126afa3b57c6bb2d4bd8dc0a62979eb1a94/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosgo/xSocks-android/691dd126afa3b57c6bb2d4bd8dc0a62979eb1a94/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosgo/xSocks-android/691dd126afa3b57c6bb2d4bd8dc0a62979eb1a94/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosgo/xSocks-android/691dd126afa3b57c6bb2d4bd8dc0a62979eb1a94/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | xSocks 5 | xSocks 6 | 服务控制 7 | 代理开关 8 | 启动/关闭后台服务 9 | 服务器设置 10 | 配置文件 11 | 配置文件名称 12 | 服务器 13 | 远程服务器的地址 14 | 远程端口 15 | 远程服务器的端口号 16 | 本地端口 17 | 本地客户端的端口号 18 | 密码 19 | 远程服务器的密码 20 | 功能设置 21 | 路由 22 | 绕过指定路由列表中的地址 23 | 全局代理 24 | 设置系统代理 25 | 证书校验 26 | 跳过证书校验 27 | 自签名证书 28 | 使用自签名证书 29 | 分应用代理 30 | 为应用程序分别设置代理 31 | 分应用代理 32 | 绕行模式 33 | 启用该选项,以使所选应用程序的流量不经过代理 34 | 自动连接 35 | 随系统启动后台服务 36 | 后台服务已开始运行 37 | 已连接到「%s」 38 | 当前速率:%d KB/s 39 | 后台服务启动失败。 40 | 后台服务已停止。 41 | 后台服务已停止。 42 | 无法连接远程服务器,请检查地址端口是否正确 43 | 停止服务 44 | 正在关闭… 45 | 后台服务启动失败:%s 46 | 47 | 本地端口号应大于1024 48 | 端口号不能为空 49 | 服务器地址不能为空 50 | 服务器密码不能为空 51 | 检测到意外退出,状态已被重置。 52 | 正在连接… 53 | 正在初始化… 54 | 重置中… 55 | 正在加载… 56 | 57 | 58 | 配置文件 59 | 设置选项 60 | 删除此配置文件「%s」? 61 | 重置 62 | 关于 63 | xSocks %s 64 | 添加配置文件 65 | 错误 66 | 67 | UDP 转发 68 | 由远程服务器转发UDP数据包 69 | 70 | 71 | 全局 72 | 绕过局域网地址 73 | 绕过局域网及中国大陆地址 74 | 75 | 76 | 77 | 跳过证书验证 78 | 自签名证书 79 | 收费证书 80 | 81 | 82 | 83 | tun2socks 84 | 85 | 86 | 协议 87 | 证书校验 88 | 代理模式 89 | 自签名证书文件 90 | 91 | -------------------------------------------------------------------------------- /app/src/main/res/values/about.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | A xSocks client for Android.

6 |

Copyright (C) 2015 lparam

7 |

This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version.

11 |

This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details.

15 |

You should have received a copy of the GNU General Public License 16 | along with this program. If not, see http://www.gnu.org/licenses/.

17 | 18 | ]]> 19 |
20 |
-------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | All 5 | Bypass LAN 6 | Bypass LAN & China 7 | 8 | 9 | 10 | all 11 | bypass-lan 12 | bypass-china 13 | 14 | 15 | 16 |         WebSocket 17 |         Quic 18 | Http2 19 | 20 | 21 | wss 22 | quic 23 | http2 24 | 25 | 26 |         skip verify 27 |         self signed 28 | charge certificate 29 | 30 | 31 | skip 32 | self_signed 33 | root_cert 34 | 35 | 36 |         tun2socks 37 | 38 | 39 | 1 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | #FFFFFF 5 | 6 | #727272 7 | #AAA 8 | #DADADA 9 | #727272 10 | 11 | #4caf50 12 | #388e3c 13 | #1b5e20 14 | #8bc34a 15 | #689f38 16 | #33691e 17 | 18 | @color/sk_material_lightGreen500 19 | 20 | @color/sk_material_lightGreen 21 | @color/sk_material_green500 22 | @color/sk_material_green700 23 | 24 | #ffe2e2e2 25 | #ffeeeeee 26 | #ff6a6a6a 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | 72dp 7 | 14sp 8 | 18sp 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | xSocks 5 | xSocks 6 | 7 | Version %1$s (%2$d) 8 | 9 | 10 | Service Controller 11 | Proxy Switch 12 | Enable / Disable Proxy 13 | 14 | 15 | Server Settings 16 | Profile 17 | Profile name 18 | Server 19 | Hostname or IP of the remote server 20 | Remote Port 21 | Port number of the remote server 22 | Local Port 23 | Port number of the local server 24 | Password 25 | Password of the remote server 26 | 27 | 28 | Feature Settings 29 | Route 30 | Bypass IP addresses in the route list 31 | Global Proxy 32 | Set up system-wide proxy 33 | Verify cert 34 | Skip verify cert 35 | Self signed 36 | Self signed cert 37 | Per-App Proxy 38 | Set proxy for selected apps 39 | Per-App Proxy 40 | Bypass Mode 41 | Enable this option to bypass selected apps 42 | Auto Connect 43 | Enable xSocks on startup 44 | UDP Forwarding 45 | Forward UDP packets to remote server 46 | 47 | 48 | xSocks started. 49 | Connected to "%s" 50 | Current Rate: %d KB/s 51 | xSocks failed to start. 52 | xSocks stopped. 53 | xSocks stopped. 54 | Failed to connect the remote server 55 | Stop the service 56 | Shutting down… 57 | %s 58 | ERROR 59 | 60 | 61 | The local port number should be greater than 1024 62 | Port should not be empty 63 | Proxy should not be empty 64 | Password should not be empty 65 | An unexpected exit detected, the context has been reset. 66 | Connecting… 67 | Initializing… 68 | Resetting… 69 | Loading… 70 | Yes 71 | No 72 | Remove this profile \"%s\"? 73 | 74 | 75 | Profiles 76 | Settings 77 | Reset 78 | About 79 | xSocks %s 80 | Add Profile 81 | Version 1.0.0 (100) 82 | AppManagerActivity 83 | Hello world! 84 | Settings 85 | 86 | Protocol 87 | Verify Cert 88 | Proxy Mode 89 | Ca Cert File 90 | 91 | 92 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 13 | 14 | 20 | 21 | 22 | 28 | 29 | 36 | 37 | 44 | 45 | 46 | 47 | 49 | 59 | 65 | 66 | 71 | 72 | 73 | 81 | 82 | 83 | 89 | 90 | 91 | 97 | 98 | 99 | 103 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | mavenCentral() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:7.4.0' 11 | // classpath 'me.tatarka.retrolambda.projectlombok:lombok.ast:0.2.3.a2' 12 | classpath 'com.jakewharton:butterknife:6.0.0' 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | 17 | // Exclude the version that the android plugin depends on. 18 | configurations.classpath.exclude group: 'com.android.tools.external.lombok' 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | google() 24 | jcenter() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## Project-wide Gradle settings. 2 | # 3 | # For more details on how to configure your build environment visit 4 | # http://www.gradle.org/docs/current/userguide/build_environment.html 5 | # 6 | # Specifies the JVM arguments used for the daemon process. 7 | # The setting is particularly useful for tweaking memory settings. 8 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 10 | # 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | #Thu Oct 08 11:01:01 CST 2015 16 | org.gradle.daemon=true 17 | org.gradle.parallel=true 18 | org.gradle.jvmargs=-Xmx1024m 19 | -------------------------------------------------------------------------------- /replay_pid20120.log: -------------------------------------------------------------------------------- 1 | JvmtiExport can_access_local_variables 0 2 | JvmtiExport can_hotswap_or_post_breakpoint 0 3 | JvmtiExport can_post_on_exceptions 0 4 | # 163 ciObject found 5 | ciMethod java/lang/Object ()V 4097 1 193264 0 -1 6 | ciMethod java/lang/String ([CII)V 2049 1 5447 0 -1 7 | ciMethod java/lang/String charAt (I)C 4097 1 785256 0 -1 8 | ciMethod java/lang/System arraycopy (Ljava/lang/Object;ILjava/lang/Object;II)V 7169 1 896 0 -1 9 | ciMethod java/lang/AbstractStringBuilder (I)V 761 1 18859 0 -1 10 | ciMethod java/lang/AbstractStringBuilder ensureCapacityInternal (I)V 2641 1 79531 0 -1 11 | ciMethod java/lang/AbstractStringBuilder newCapacity (I)I 689 1 4846 0 -1 12 | ciMethod java/lang/AbstractStringBuilder append (Ljava/lang/String;)Ljava/lang/AbstractStringBuilder; 1249 1 5348 0 -1 13 | ciMethod java/lang/AbstractStringBuilder append (C)Ljava/lang/AbstractStringBuilder; 2737 1 69911 0 -1 14 | ciMethod java/lang/StringBuilder ()V 657 1 11881 0 -1 15 | ciMethod java/lang/StringBuilder append (Ljava/lang/String;)Ljava/lang/StringBuilder; 1153 1 19287 0 -1 16 | ciMethod java/lang/StringBuilder append (C)Ljava/lang/StringBuilder; 2097 1 17898 0 -1 17 | ciMethod java/lang/StringBuilder toString ()Ljava/lang/String; 681 1 13311 0 -1 18 | ciMethod java/lang/Math min (II)I 3073 1 53716 0 -1 19 | ciMethod java/util/Arrays copyOf ([CI)[C 913 1 16906 0 -1 20 | ciMethodData java/lang/String charAt (I)C 2 785301 orig 264 88 29 173 82 0 0 0 0 176 61 233 20 0 0 0 0 120 1 0 0 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 77 68 79 32 101 120 116 114 97 32 100 97 116 97 32 108 111 99 107 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 169 204 95 0 1 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 80 0 0 0 255 255 255 255 7 0 1 0 0 0 0 0 data 10 0x10007 0x0 0x40 0xbf995 0xa0007 0xbf995 0x30 0x0 0x120002 0x0 oops 0 -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':xsocks' 2 | include ':app' 3 | -------------------------------------------------------------------------------- /xsocks/build.gradle: -------------------------------------------------------------------------------- 1 | configurations.maybeCreate("default") 2 | artifacts.add("default", file('xsocks.aar')) -------------------------------------------------------------------------------- /xsocks/xsocks.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dosgo/xSocks-android/691dd126afa3b57c6bb2d4bd8dc0a62979eb1a94/xsocks/xsocks.aar --------------------------------------------------------------------------------