AOne Android SDK接入文檔
1、在工程中導入aar包
下載好aar,并在libs下導入aar包,如下圖所示:
2、修改build.gradle
- 聯系零信任對接人員獲取到 aOne 授權網頁的域名、aOne授權網頁的poolId、aOne授權網頁的appId,aOne授權網頁的scheme,并分別填入build.gradle中
- 在build.gradle的 dependencies 中加入 implementation fileTree(include: ['*.aar'], dir: 'libs')
- 執行android studio的rebuild,生成BuildConfig
3、修改AndroidManifest
在登錄頁增加AOneId授權登錄頁面的
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Secmobileandroidsdk">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="${aOneScheme}" />
</intent-filter>
</activity>
4、繼承VPNApplication
app的Application類繼承VPNApplication,如下所示:
/**
* 需要將自己的Application 繼承Sdk的VPNApplication
*/
public class AppApplication extends VPNApplication {
}
PS: 這部完成之后,可以運行APP,過濾SDK的tag 如果打印出來當前的版本號,則表示SDK集成成功。
5、AOne 賬號登錄
此部分參考com.ctcdn.sdkdemo.login包底下的代碼,代碼介紹如下:
| 代碼路徑 | 詳細功能介紹 |
|---|---|
| com.ctcdn.sdkdemo.login.activity | 登錄頁,包含了登錄的所有流程 |
| com.ctcdn.sdkdemo.login.exception | 自定義的接口異常類 |
| com.ctcdn.sdkdemo.login.model | 接口的請求和解析的一些模型類 |
| com.ctcdn.sdkdemo.login.utils | 工具類 |
| com.ctcdn.sdkdemo.login.AOneLoginService | 使用了retrofit的依賴,這里定義了登錄的一些接口 |
| com.ctcdn.sdkdemo.login.ApiResultCallAdapter | Aone接口解析適配器 |
| com.ctcdn.sdkdemo.login.RetrofitManager | http管理類 |
-
賬號密碼登錄流程
-
短信登陸流程
6、AOne 第三方瀏覽器身份驗證和授權
在用戶同意隱私協議后,進入登錄頁面,在Sdk 初始化之前需要去第三方瀏覽器進行進行AOne 身份驗證,這里提供拼接授權地址和身份驗證調用的方法,方法如下:
/**
* 這里需要填上對接方提供的 aOne 授權網頁的域名
*/
public String aOneUrl = BuildConfig.aOneUrl;
/**
* 這里需要填上對接方提供的 aOne 授權網頁的poolId
*/
public String aOnePoolId = BuildConfig.aOnePoolId;
/**
* 這里需要填上對接方提供的 aOne 授權網頁的appId
*/
public String aOneAppId = BuildConfig.aOneAppId;
/**
* 獲取AOne授權鏈接
*
* @return AOne 授權鏈接
*/
public String GetAOneIdUrl(String aOneUrl, String aOnePoolId, String aOneAppId) {
if (TextUtils.isEmpty(aOnePoolId)) {
return "";
}
return (aOneUrl + "/" + aOnePoolId + "/app/" + aOneAppId).trim();
}
/**
* aOne網頁授權示例
* 描述: 這里會跳轉到第三方瀏覽器, 授權成功后會彈出
*/
public void aOneLogin(View v) {
String aOneIdUrl = GetAOneIdUrl(BuildConfig.AOnePoolId);
if (TextUtils.isEmpty(aOneIdUrl)) {
Toast.makeText(MainActivity.this, getText(R.string.aone_id_url), Toast.LENGTH_SHORT).show();
return;
}
Uri parse;
try {
parse = Uri.parse(aOneIdUrl);
Intent intent = new Intent(Intent.ACTION_VIEW, parse);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} catch (Exception e) {
Toast.makeText(MainActivity.this, getResources().getString(R.string.aone_id_url_invalid, e.getMessage()), Toast.LENGTH_SHORT).show();
}
}
7、增加Sdk的初始化方法
在用戶同意隱私協議后,進入登錄頁面,需要在Activity的OnCreate方法中獲取Intent,并通過intent的getQueryParameter的方法,獲取到授權回調回來的id_token字段的值,這個值就是AoneId授權成功回來的token,傳入SdkInit的入參,完成Sdk的初始化方法,步驟如下:
- 在登錄頁的onCreate方法中增加獲取AOne網頁授權回來的token的方法,如下所示:
/** * 這個方法是Aone網頁授權回調后,獲取到SdkInit的所需要的token的方法 * 調用線程:主線程 */ public void aoneAuthBack() { // 獲取 Intent 中的數據 Intent intent = getIntent(); if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { Uri data = intent.getData(); if (data != null) { String idToken = data.getQueryParameter("id_token"); Log.i(TAG, "[AOne] id_token:" + idToken); Map<String, String> params = new HashMap<>(); params.put(Const.INIT_PARAMS.AONE_ID_TOKEN, idToken); SdkInit(this,params); } else { Toast.makeText(MainActivity.this, getText(R.string.u_should_auth_by_web_first), Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(MainActivity.this, getText(R.string.u_should_auth_by_web_first), Toast.LENGTH_SHORT).show(); } } - 把這個token傳入SdkInit方法中,注意這里有獲取ip的行為,需要在用戶同意隱私協議后才能調用。
- 初始化接口會回調你們一個 token , 需要拿著這個token 下次來初始化,如果在有效期內, 初始化成功, 會在返回給你們一個新的 token ,如果有效期過期, 會通過回調告訴你們有效期過期了, 重新走登錄流程。
/** * 方法名:SdkInit * 描述:這個方法用于Sdk初始化,入參只需要AOneId網頁授權回調回來的token * 使用前置條件:AoneId網頁授權成功后,獲取到token,然后傳入這個方法,注意這里有獲取ip的行為,需要在用戶同意隱私協議后才能調用 * 使用線程: 主線程 * 回調線程: 主線程 * 使用方法: * Map<String, String> params = new HashMap<>(); * params.put(Const.INIT_PARAMS.AONE_ID_TOKEN, idToken); * SdkInit(this,params); * 參數說明: * * @param context 當前的Activity * @param params map類型的入參,這里只需要往map里面傳入一個參數,key是Const.INIT_PARAMS.AONE_ID_TOKEN,value是AoneId授權成功后的token * 接口回調數據: 回調后的數據類型是map類型, 成功回調示例: {code="0x00000", data={"aoneSdkToken":"aoneSdk的token"}, message=成功} 失敗回調示例:{code="0x00999", data={}, message=未知錯誤} * 接口回調數據字段說明: code: 狀態碼, data: 數據, message: 消息 */ private void SdkInit(MainActivity context,Map<String, String> params) { AOneSdkClient.Companion.SdkInit(context, params, new AOneSdkClient.Companion.SdkCallBack() { @Override public void onResponse(@NonNull Map<String, String> map) { Log.i(TAG, "[init] response: " + map); String code = map.get(Const.RESPONSE.CODE); String message = map.get(Const.RESPONSE.MESSAGE); if (ResultCode.SUCCESS.getValue().equals(code)) { String data = map.get(Const.RESPONSE.DATA); if (!TextUtils.isEmpty(data)) { Map<String, String> dataMaps = GsonUtils.Companion.GsonToMaps(data); if (dataMaps != null) { String aoneSdkToken = dataMaps.get(Const.DATA.AoneSdkToken); if (TextUtils.isEmpty(aoneSdkToken)){ return; } Log.i(TAG, "[init] 這里需要保存aoneSdkToken 下次自動登錄的時候回傳給sdk: " + aoneSdkToken); Toast.makeText(MainActivity.this, getText(R.string.init_success), Toast.LENGTH_SHORT).show(); } } } else { findViewById(R.id.aOneAccountLogin).setEnabled(true); findViewById(R.id.aOneWebViewLogin).setEnabled(true); Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show(); } } }); }
8、在需要啟動Vpn的界面增加Vpn啟動的方法,并監聽Vpn啟動的狀態
-
在合適的時機(建議在調用啟動Vpn的方法之前),調用RegistGlobalNotification的方法,監聽Vpn的一些狀態,根據這些狀態來同步界面的樣式
VPN狀態說明:- UNINITED : VPN 未初始化
- INITED : VPN 已初始化
- READY : VPN 配置完成
- CONNECTED : VPN 已上線
- DISCONNECTED : VPN 已下線
/** * 方法名: RegistGlobalNotification * 描述:這個方法用于Sdk VPN狀態的回調,用于同步改變VPN界面的一些上下線的狀態 * VPN狀態說明: * UNINITED : VPN 未初始化 * INITED : VPN 已初始化 * READY : VPN 配置完成 * CONNECTED : VPN 已上線 * DISCONNECTED : VPN 已下線 * 使用前置條件:無 * 使用線程: 主線程 * 回調線程: 主線程 * 使用方法: * AOneSdkClient.Companion.RegistGlobalNotification(new AOneSdkClient.Companion.SdkCallBack() { * @Override * public void onResponse(@NonNull Map<String, String> map) { * } * }); * 參數說明: * 接口回調數據: 回調后的數據類型是map類型, 成功回調示例: {code="0x00000", data={"tunnelState":"INITED"}, message=成功} 失敗回調示例:{code="0x00999", data={}, message=未知錯誤} * 接口回調數據字段說明: code: 狀態碼, data: vpn狀態,里面包含有tunnelState狀態的字段,value的含義見上面的VPN狀態說明, message: 消息 */ private void sdkCallBack() { AOneSdkClient.Companion.RegistGlobalNotification(new AOneSdkClient.Companion.SdkCallBack() { @Override public void onResponse(@NonNull Map<String, String> map) { Log.i(TAG, "[sdkCallBack] response: " + map); Log.i(TAG, "threadid:" + Thread.currentThread().getName()); String code = map.get(Const.RESPONSE.CODE); String message = map.get(Const.RESPONSE.MESSAGE); if (ResultCode.SUCCESS.getValue().equals(code)) { String data = map.get(Const.RESPONSE.DATA); if (!TextUtils.isEmpty(data)) { Map<String, String> dataMaps = GsonUtils.Companion.GsonToMaps(data); if (dataMaps != null) { ((TextView) findViewById(R.id.status)).setText(dataMaps.get(Const.DATA.TUNNEL_STATE)); } } } else { Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show(); } } }); }
9、啟動VPN
- 調用SdkStartVPN方法啟動Vpn
/** * 方法名: SdkStartVPN * 描述:這個方法用于Sdk VPN的啟動 * 使用前置條件:需要先調用SdkInit * 使用線程: 主線程 * 回調線程: 主線程 * 使用方法: * AOneSdkClient.Companion.SdkStartVPN(new AOneSdkClient.Companion.SdkCallBack() { * @Override * public void onResponse(@NonNull Map<String, String> map) { * } * }); * 參數說明: * 接口回調數據: 回調后的數據類型是map類型, 成功回調示例: {code="0x00000", data={}, message=成功} 失敗回調示例:{code="0x00999", data={}, message=未知錯誤} * 接口回調數據字段說明: code: 狀態碼, data: 數據, message: 消息 */ public void startVpn(View view) { HashMap<String, String> params = new HashMap<>(); AOneSdkClient.Companion.SdkStartVPN(this, params, new AOneSdkClient.Companion.SdkCallBack() { @Override public void onResponse(@NonNull Map<String, String> map) { Log.i(TAG, "[startVpn] response: " + map); Log.i(TAG, "threadid:" + Thread.currentThread().getName()); String code = map.get(Const.RESPONSE.CODE); String message = map.get(Const.RESPONSE.MESSAGE); if (ResultCode.SUCCESS.getValue().equals(code)) { Toast.makeText(MainActivity.this, getText(R.string.init_success), Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show(); } } }); }
10、停止Vpn
- 在用戶點擊Vpn停止的事件上,調用SdkStopVPN方法停止Vpn,注意這里停止Vpn的條件是先調用SdkStartVPN且Vpn狀態要在Connect狀態
/** * 方法名: SdkStopVPN * 描述:這個方法用于Sdk VPN的停止 * 使用前置條件:需要先調用SdkStartVPN且Vpn狀態要在Connect狀態 * 使用線程: 主線程 * 回調線程: 主線程 * 使用方法: * AOneSdkClient.Companion.SdkStopVPN(new AOneSdkClient.Companion.SdkCallBack() { * @Override * public void onResponse(@NonNull Map<String, String> map) { * } * }); * 參數說明: * 接口回調數據: 回調后的數據類型是map類型, 成功回調示例: {code="0x00000", data={}, message=成功} 失敗回調示例:{code="0x00999", data={}, message=未知錯誤} * 接口回調數據字段說明: code: 狀態碼, data: 數據, message: 消息 */ public void stopVpn(View view) { HashMap<String, String> params = new HashMap<>(); AOneSdkClient.Companion.SdkStopVPN(this, params, new AOneSdkClient.Companion.SdkCallBack() { @Override public void onResponse(@NonNull Map<String, String> map) { Log.i(TAG, "[startVpn] response: " + map); Log.i(TAG, "threadid:" + Thread.currentThread().getName()); String code = map.get(Const.RESPONSE.CODE); String message = map.get(Const.RESPONSE.MESSAGE); if (ResultCode.SUCCESS.getValue().equals(code)) { Toast.makeText(MainActivity.this, getText(R.string.init_success), Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show(); } } }); }
11、SDK 清理
- 在確定用戶不使用VPN服務的時候,通過調用SdkUnInit釋放 Vpn服務所需要的資源,調用此方法前需要先調用SdkStopVPN且Vpn狀態要在DisConnect狀態,否則會提示狀態錯誤的錯誤碼
/** * 方法名: SdkUnInit * 描述:這個方法用于Sdk 資源釋放, 調用此方法后Vpn的狀態會變成UnInit,要重新啟動VPN需要調用SdkInit方法,否則會提示狀態錯誤的錯誤碼 * 使用前置條件:需要先調用SdkStopVPN且Vpn狀態要在DisConnect狀態,否則會提示狀態錯誤的錯誤碼 * 使用線程: 主線程 * 回調線程: 主線程 * 使用方法: * AOneSdkClient.Companion.SdkUnit(new AOneSdkClient.Companion.SdkCallBack() { * * @Override public void onResponse(@NonNull Map<String, String> map) { * } * }); */ public void SdkUnInit(){ HashMap<String, String> params = new HashMap<>(); AOneSdkClient.Companion.SdkUnInit(this, params, new AOneSdkClient.Companion.SdkCallBack() { @Override public void onResponse(@NonNull Map<String, String> map) { Log.i(TAG, "[stopVpn] response: " + map); String code = map.get(Const.RESPONSE.CODE); String message = map.get(Const.RESPONSE.MESSAGE); if (ResultCode.SUCCESS.getValue().equals(code)) { Toast.makeText(MainActivity.this, getText(R.string.disconnect_success), Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show(); } } }); }
12、SDK狀態查詢
- 調用GetSdkStatus 可以主動查詢Sdk的狀態
/** * 方法名: GetSdkStatus * 描述:這個方法用于Sdk VPN的狀態 * 使用線程: 主線程 * 回調線程: 主線程 * 使用方法: AOneSdkClient.Companion.GetSdkStatus() * VPN狀態說明: * UNINITED : VPN 未初始化 * INITED : VPN 已初始化 * READY : VPN 配置完成 * CONNECTED : VPN 已上線 * DISCONNECTED : VPN 已下線 */ public void GetSdkStatus(View view) { AOneManager.AOneTunnelState aOneTunnelState = AOneSdkClient.Companion.GetSdkStatus(); Toast.makeText(MainActivity.this, "當前Sdk Vpn狀態為:" + aOneTunnelState.name(), Toast.LENGTH_SHORT).show(); }
13、調用方法傳入Sdk用戶對隱私協議的操作情況
-
調用updatePrivacyAgree 傳入用戶是否同意隱私協議,如果同意隱私協議就傳入true,不同意或者撤回隱私協議傳入false
/** * 方法名:updatePrivacyAgree * 描述:這個方法用于客戶Sdk隱私協議傳入到sdk * 使用前置條件:彈出隱私協議窗口后 * 使用線程: 主線程 * 回調線程: 主線程 * 使用方法: * AOneSdkClient.Companion.updatePrivacyAgree(isAgree) * 參數說明:Boolean類型的數據,用戶同意隱私協議則傳入true 拒絕或者撤回則傳入false */ private static void updatePrivacyAgree(boolean isAgree) { AOneSdkClient.Companion.updatePrivacyAgree(isAgree); }
14、SDK使用權限說明
| 權限名稱 | 使用目的 | 功能場景 / 申請時機 | 必選/可選 |
|---|---|---|---|
| android.permission.INTERNET | 查看網絡狀態,用于數據上報,實現開發者查看崩潰信息的目的 | app啟動階段, 用于VPN網絡連接 | 必選 |
| android.permission.ACCESS_WIFI_STATE | 查看WiFi網絡狀態信息,用于查看WiFi網絡狀態信息 | app啟動階段,用于VPN網絡檢測 | 必選 |
| android.permission.ACCESS_NETWORK_STATE | 允許訪問網絡狀態, 用于區分移動網絡或WiFi網絡 | app啟動階段,用于VPN網絡檢測 | 必選 |
| android.permission.BIND_VPN_SERVICE | 綁定VPN服務,用于連接內網 | app啟動階段,用于VPN網絡連接 | 必選 |
15、SDK 產品功能所需收集的個人信息說明
| 個人信息類型 | 可選/必選 | 處理目的/功能場景 | 處理方式 |
|---|---|---|---|
| 獲取IP | 必選 | 用于網絡變化的時候重新解析DNS獲取ip | 僅讀取,不保存到本地,也不上傳服務器 |
常見問題
-
引入aar包后,出現Duplicate class 的錯誤,如下圖所示
原因:因為sdk有依賴一些jar包,如果app工程也依賴相同的jar包會導致沖突,這里需要exclude app 工程的相同的類來解決這類問題,目前sdk層依賴的jar包如下表所示:
庫名字 版本 okhttp 4.9.3 okio 2.8.0 gson 2.9.0 datastore-preferences-core 1.0.0 datastore-core 1.0.0 converter-gson 2.9.0 logging-interceptor 4.9.3 retrofit 2.9.0 commons-validator:commons-validator 1.7 org.conscrypt:conscrypt-android 2.5.2 androidx.security:security-crypto 1.1.0-alpha06 androidx.lifecycle:lifecycle-runtime-ktx 2.5.1 com.elvishew:xlog 1.11.1 解決方式:使用
exclude排除重復的類如果你知道具體哪些類是重復的(通常可以在錯誤消息中看到),可以使用
exclude來排除特定的類。例如,如果common-lib引入了重復的類com.okhttp3,你可以排除該類:dependencies { implementation('com.example:common-lib:1.0.0') { exclude group: 'com.squareup.okhttp3', module: 'okhttp' } } -
獲取設備id
如果需要獲取當前sdk指定的設備指紋(唯一id),則可以調用 DeviceIdUtil.Companion.getUniqueId()獲取當前系統的版本,則可以調用 DeviceIdUtil.Companion.getDeviceOs()
-
日志路徑
如果遇到問題需要協助,開發人員可能會尋求日志定位問題,這里日志的路徑的獲取有三個方式-
方式一、使用如下方法獲取日志
VPNApplication.get().getFileLogPath() -
方式二、在log中獲取,啟動后會在tag為Sdk中獲取到
在用如上的方法獲取到日志路徑后,到路徑下取出今天的日志發給開發者即可
-
方式三、日志分享
調用AOneSdkClient.Companion.ShareLog(this) 方法,日志會打包成一個zip文件,并彈出分享面板,可以通過微信等方式分享/** * 方法名: ShareLog * 描述:這個方法用于Sdk日志的分享,方便快速定位問題 * 使用線程: 主線程 * 回調線程: 主線程 * 使用方法: * AOneSdkClient.Companion.ShareLog(this); */ public void ShareLog(View view) { AOneSdkClient.Companion.ShareLog(this); }
-
-
錯誤碼
錯誤碼 錯誤提示 建議處置方式 0x10301 隧道資源接口請求失敗 聯系管理員 0x10302 隧道資源不存在 聯系管理員 0x10311 aoneid隧道資源接口請求失敗 聯系管理員 0x10312 aoneid隧道資源不存在 聯系管理員 0x10321 獲取解析組接口錯誤 聯系管理員 0x10322 獲取解析組域名為空 聯系管理員 0x10323 獲取解析組ip為空 聯系管理員 0x10331--0x10334 隧道配置接口請求失敗 聯系管理員 0x10335 隧道配置不存在 聯系管理員 0x10336 控制中心無法分配足夠客戶端ip 聯系管理員 0x10337 多鏡像中心隧道接口內部錯誤 聯系管理員 0x10341 token接口刷新錯誤 聯系管理員 0x10351 添加設備的接口報錯 聯系管理員 0x10352 設備未激活 聯系管理員,增加設備的激活數量 0x10353 未支持的AuthStatus字段 聯系管理員 0x11001 配置轉換異常 聯系管理員 0x11002 數據轉換異常 聯系管理員 0x12001 開始認證無Token,一般是Token過期了 1、排查是否登錄成功 2、是否正確傳遞token到sdk,或者token超過了有效期 3、如果還是不行,聯系管理員 0x12002 開始認證無用戶名 1、排查是否正確傳遞token到sdk 2、正確傳遞還是報錯則聯系管理員 0x12003 開始認證無隨機數 1、排查是否正確傳遞token到sdk 2、正確傳遞還是報錯則聯系管理員 0x12004 無權限 app申請VPN權限,用戶是否同意了 0x12005 無網絡 查看網絡是否正常 0X12006 無refreshToken 1、排查是否正確傳遞token到sdk 2、正確傳遞還是報錯則聯系管理員 0X12007 aoneId 用戶池id是空的 1、排查是否正確傳遞token到sdk 2、正確傳遞還是報錯則聯系管理員 0X12008 租戶id是空的 1、排查是否正確傳遞token到sdk 2、正確傳遞還是報錯則聯系管理員 0X12009 token 錯誤 1、排查是否正確傳遞token到sdk 2、正確傳遞還是報錯則聯系管理員 0X12010 啟動VPN一些配置是錯誤的 聯系管理員 0X12011 初始化token已過期 排除token 是否已經過期了 0X13001 狀態錯誤 聯系管理員 0X13002 下線失敗 聯系管理員 0X13003 初始化錯誤 聯系管理員 0X13004 已經在線 VPN已經是在線狀態,又調用了SdkStartVPN導致 建議檢查下VPN的狀態 0X13005 json 序列化錯誤 聯系管理員 0X13006 json 解析失敗 聯系管理員 0X13007 未知錯誤 聯系管理員 0X13008 服務端響應錯誤,一般是502等錯誤狀態碼 聯系管理員 0X13009 服務端無響應 聯系管理員 0X13010 請先傳入用戶是否同意隱私協議再使用sdk 需要彈出隱私協議確認對話框,并調用的方法AOneSdkClient.Companion.updatePrivacyAgree(true)告知sdk 0X13011 需等待用戶同意隱私協議后再使用sdk 用戶同意隱私協議后需要調用AOneSdkClient.Companion.updatePrivacyAgree(true);