使用sh*dows**ks-android构建V*N客户端

1 环境配置

  • jdk 11 下载jdk11压缩包。然后解压到指定目录,也可以使用android studio自带的jdk11。

    sudo tar -zxf  openjdk-11.0.1_osx-x64_bin.tar.gz -C /Library/Java/JavaVirtualMachines/
    
  • ndk

    ndk使用r19以上

  • android studio

    Android Studio Bumblebee | 2021.1.1 Patch 1

  • rust

    curl https://sh.rustup.rs -sSf | sh
    

2 编译

以上环境准备好之后,可以直接clone sh*dows**ks-android项目进行编译。当然我们也可以基于sh*dows**ks-android的core lib进行代码整合。

3 代码改动

  1. 代码拷贝

    copy core和plugin中的ss代码到新项目中

  2. 移除sh*dows**ks-android中的firebase代码

    移除com.github.sh*dows**ks中的firebase代码引用

  3. 拷贝资源文件

    拷贝string、color、drawable(ic_navigation_close、ic_file_cloud_download、ic_service_active)

  4. 引入sh*dows**ks组件

    <service
            android:name="com.github.sh*dows**ks.bg.V*nService"
            android:directBootAware="true"
            android:exported="false"
            android:label="@string/app_name"
            android:permission="android.permission.BIND_V*N_SERVICE"
            android:process=":bg">
        <intent-filter>
            <action android:name="android.net.V*nService" />
        </intent-filter>
    </service>
    <service
            android:name="com.github.sh*dows**ks.bg.TransproxyService"
            android:directBootAware="true"
            android:exported="false"
            android:process=":bg" />
    <service
            android:name="com.github.sh*dows**ks.bg.ProxyService"
            android:directBootAware="true"
            android:exported="false"
            android:process=":bg" />
    <service
            android:name="com.github.sh*dows**ks.subscription.SubscriptionService"
            android:exported="false" />
    
    <activity
            android:name="com.github.sh*dows**ks.V*nRequestActivity"
            android:excludeFromRecents="true"
            android:exported="false"
            android:taskAffinity="" />
    <receiver
            android:name="com.github.sh*dows**ks.BootReceiver"
            android:directBootAware="true"
            android:enabled="false"
            android:exported="true"
            android:process=":bg">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
            <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
        </intent-filter>
    </receiver>
    
  5. 配置权限

    <permission
             android:name="${applicationId}.SERVICE"
             android:protectionLevel="signature" />
    
     <uses-permission android:name="${applicationId}.SERVICE" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission
             android:name="android.permission.WRITE_EXTERNAL_STORAGE"
             android:maxSdkVersion="28" />
    
     <queries>
         <intent>
             <action android:name="com.github.sh*dows**ks.plugin.ACTION_NATIVE_PLUGIN" />
         </intent>
         <intent>
             <action android:name="android.intent.action.VIEW" />
             <category android:name="android.intent.category.BROWSABLE" />
             <data android:scheme="https" />
         </intent>
         <intent>
             <action android:name="android.support.customtabs.action.CustomTabsService" />
         </intent>
     </queries>
    
  6. extractNativeLibs属性配置

    在manifest的Application节点中加入android:extractNativeLibs="true"属性,确保安装之后的sh*dows**ks so文件能够正确被安装。

4 如何调用

  1. Application中初始化sh*dows**ks core

    // application onreate中初始化
    Core.init(this, MainActivity::class)
    
  2. 启动和停用

    // 停用
    private fun disconnect() {
        Core.stopService()
    }
    // 启动
    private fun toggle() {
        val profile:Profile = ProfileManager.createProfile()
        profile.bypass = true
        profile.dirty = false
        profile.proxyApps = true
        profile.ipv6 = false
        profile.metered = false
        profile.host = "129.226.69.*"
        profile.remotePort = 60915
        profile.password = "***"
        profile.name = "Mytest1"
        profile.method = "aes-256-gcm"
        profile.individual = application.packageName
        ProfileManager.updateProfile(profile)
        DataStore.publicStore.putLong("profileId", 9)
        // Const.Log("profile is ${profile.id}")
        Core.startService()
    }
    
    1. sh*dows**ks状态监测接口 可以通过aidl跨进程调用获取sh*dows**ks状态,使用Sh*Dows**KsConnection并且实现Sh*Dows**KsConnection.Callback接口即可返回sh*dows**ks状态(Idle、Connecting、Connected、Stopping、Stopped)

      在Activity的onCreate中,调用Sh*Dows**KsConnection的connect方法,并且在onDestroy中调用disconnect方法。

      override fun onCreate() {
          connection.connect(this)
      }
      override fun onDestroy() {
          super.onDestroy()
              connection.disconnect(this)
      }
      

      实现Sh*Dows**KsConnection.Callback可以获取状态变更、流量统计等功能。

      interface Callback {
          fun stateChanged(state: BaseService.State, profileName: String?, msg: String?)
          fun trafficUpdated(profileId: Long, stats: TrafficStats) { }
          fun trafficPersisted(profileId: Long) { }
          fun onServiceConnected(service: ISh*Dows**KsService)
          /**
           * Different from Android framework, this method will be called even when you call `detachService`.
           */
          fun onServiceDisconnected() { }
          fun onBinderDied() { }
      }