├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── encodings.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── ReadMe.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── heiko │ │ └── networkdetectionsample │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── heiko │ │ │ └── networkdetectionsample │ │ │ ├── ClipboardUtils.java │ │ │ ├── DeviceUtils.java │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── heiko │ └── networkdetectionsample │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── network_detaction.jpg ├── networkdetection ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── heiko │ │ └── network │ │ └── detection │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ ├── heiko │ │ │ └── network │ │ │ │ └── detection │ │ │ │ └── task │ │ │ │ ├── BaseTask.java │ │ │ │ ├── TaskCallBack.java │ │ │ │ └── TraceTask.java │ │ │ └── netease │ │ │ ├── LDNetDiagnoService │ │ │ ├── LDNetAsyncTaskEx.java │ │ │ ├── LDNetDiagnoListener.java │ │ │ ├── LDNetDiagnoService.java │ │ │ ├── LDNetPing.java │ │ │ ├── LDNetSocket.java │ │ │ └── LDNetTraceRoute.java │ │ │ └── utils │ │ │ ├── LDNetUtil.java │ │ │ └── LDPingParse.java │ └── jniLibs │ │ └── armeabi │ │ └── libtracepath.so │ └── test │ └── java │ └── com │ └── heiko │ └── network │ └── detection │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | xmlns:android 11 | 12 | ^$ 13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 | 21 | xmlns:.* 22 | 23 | ^$ 24 | 25 | 26 | BY_NAME 27 | 28 |
29 |
30 | 31 | 32 | 33 | .*:id 34 | 35 | http://schemas.android.com/apk/res/android 36 | 37 | 38 | 39 |
40 |
41 | 42 | 43 | 44 | .*:name 45 | 46 | http://schemas.android.com/apk/res/android 47 | 48 | 49 | 50 |
51 |
52 | 53 | 54 | 55 | name 56 | 57 | ^$ 58 | 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | style 67 | 68 | ^$ 69 | 70 | 71 | 72 |
73 |
74 | 75 | 76 | 77 | .* 78 | 79 | ^$ 80 | 81 | 82 | BY_NAME 83 | 84 |
85 |
86 | 87 | 88 | 89 | .* 90 | 91 | http://schemas.android.com/apk/res/android 92 | 93 | 94 | ANDROID_ATTRIBUTE_ORDER 95 | 96 |
97 |
98 | 99 | 100 | 101 | .* 102 | 103 | .* 104 | 105 | 106 | BY_NAME 107 | 108 |
109 |
110 |
111 |
112 |
113 |
-------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 38 | 39 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | Android 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 95 | 96 | 97 | 98 | 99 | 1.8 100 | 101 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | ## NetWorkDetection 2 | 3 | Android 网络诊断、网络检测库 4 | 基于[AndroidHttpCapture](https://github.com/JZ-Darkal/AndroidHttpCapture)改造,剔除了不需要的功能,更精简。 5 | 效果如下所示: 6 | ```java 7 | 开始诊断... 8 | 诊断时间:2020-06-11 14:20:09 9 | 应用code: 01 10 | 应用名称: NetworkDetection 11 | 应用版本: 1.0 12 | 机器类型: OnePlus:OnePlus:GM1910 13 | 系统版本: 10 14 | 机器ID: 5a121bc3a9547810 15 | 运营商: 中国电信 16 | ISOCountryCode: cn 17 | MobileCountryCode: 460 18 | MobileNetworkCode: 11 19 | 20 | 诊断域名 www.baidu.com... 21 | 当前是否联网: 已联网 22 | 当前联网类型: WIFI 23 | 本地IP: 10.1.150.157 24 | 本地网关: 10.1.150.1 25 | 本地DNS: 10.96.0.10,114.114.114.114 26 | 远端域名: www.baidu.com 27 | DNS解析结果: 112.80.248.75,112.80.248.76 (25ms) 28 | 29 | 开始TCP连接测试... 30 | Connect to host: 112.80.248.75... 31 | 1's time=17ms, 2's time=22ms, 3's time=15ms, 4's time=14ms, average=17ms 32 | Connect to host: 112.80.248.76... 33 | 1's time=16ms, 2's time=17ms, 3's time=17ms, 4's time=16ms, average=16ms 34 | 35 | 开始ping... 36 | ping...127.0.0.1 37 | 64bytes from 127.0.0.1: icmp_seq=#1 ttl=64 time=0.236ms 38 | 64bytes from 127.0.0.1: icmp_seq=#2 ttl=64 time=0.467ms 39 | 64bytes from 127.0.0.1: icmp_seq=#3 ttl=64 time=0.622ms 40 | 64bytes from 127.0.0.1: icmp_seq=#4 ttl=64 time=0.586ms 41 | ping本机IP...10.1.150.157 42 | 64bytes from 10.1.150.157: icmp_seq=#1 ttl=64 time=0.397ms 43 | 64bytes from 10.1.150.157: icmp_seq=#2 ttl=64 time=0.430ms 44 | 64bytes from 10.1.150.157: icmp_seq=#3 ttl=64 time=0.543ms 45 | 64bytes from 10.1.150.157: icmp_seq=#4 ttl=64 time=0.626ms 46 | ping本地网关...10.1.150.1 47 | 64bytes from 10.1.150.1: icmp_seq=#1 ttl=254 time=13.9ms 48 | 64bytes from 10.1.150.1: icmp_seq=#2 ttl=254 time=17.0ms 49 | 64bytes from 10.1.150.1: icmp_seq=#3 ttl=254 time=15.1ms 50 | 64bytes from 10.1.150.1: icmp_seq=#4 ttl=254 time=13.0ms 51 | ping本地DNS1...10.96.0.10 52 | ping: cannot resolve 10.96.0.10: Timeout 53 | ping本地DNS2...114.114.114.114 54 | 64bytes from 114.114.114.114: icmp_seq=#1 ttl=90 time=23.1ms 55 | 64bytes from 114.114.114.114: icmp_seq=#2 ttl=87 time=34.6ms 56 | 64bytes from 114.114.114.114: icmp_seq=#3 ttl=75 time=33.9ms 57 | 64bytes from 114.114.114.114: icmp_seq=#4 ttl=63 time=34.1ms 58 | 59 | 开始traceroute... 60 | 1?: [LOCALHOST] pmtu 1500 61 | 1: 10.1.150.1 4.795ms 62 | 1: 10.1.150.1 19.414ms 63 | 2: ********** 64 | 3: 60.12.222.1 21.194ms 65 | 4: ********** 66 | 5: ********** 67 | 6: 221.12.35.101 25.570ms 68 | 7: ********** 69 | 8: 221.6.1.254 24.447ms 70 | 9: 58.240.96.30 14.385ms 71 | 10: 182.61.216.0 15.139ms 72 | 11: ********** 73 | 12: ********** 74 | 13: ********** 75 | 76 | 网络诊断结束 77 | ``` 78 | 79 | ## Usage 80 | ### 添加依赖 81 | #### Step 1. Add the JitPack repository to your build file 82 | Add it in your root build.gradle at the end of repositories: 83 | 84 | ```java 85 | allprojects { 86 | repositories { 87 | ... 88 | maven { url 'https://jitpack.io' } 89 | } 90 | } 91 | ``` 92 | 93 | #### Step 2. Add the dependency 94 | 95 | ```java 96 | dependencies { 97 | implementation 'com.github.EthanCo:NetWorkDetection:1.0.4' 98 | } 99 | ``` 100 | 101 | ### 进行使用 102 | ```java 103 | traceTask = new TraceTask(MainActivity.this, "www.baidu.com", new TaskCallBack() { 104 | @Override 105 | public void onUpdated(String log) { 106 | //当诊断状态更新 107 | } 108 | 109 | @Override 110 | public void onFinish(String log) { 111 | //当诊断结束 112 | } 113 | 114 | @Override 115 | public void onFailed(Exception e) { 116 | //当诊断失败 117 | } 118 | }); 119 | traceTask.doTask(); //进行诊断 120 | ``` 121 | 122 | #### 可选方法 123 | ```java 124 | traceTask.setAppName("NetworkDetection"); //设置应用名称 125 | traceTask.setAppCode("01"); //设置AppCode 126 | traceTask.setDeviceId(deviceId); //设置设备ID 127 | traceTask.setAlwaysPing(false); //是否永远进行Ping,如果是false,则根据当前网络环境判断是否要Ping 128 | ``` 129 | 130 | ### 添加混淆 131 | 132 | ```java 133 | -keep class com.netease.LDNetDiagnoService.**{*;} 134 | ``` 135 | 136 | 感谢 [AndroidHttpCapture](https://github.com/JZ-Darkal/AndroidHttpCapture) | [LDNetDiagnoService](https://github.com/Lede-Inc/LDNetDiagnoService_Android) 137 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | 6 | defaultConfig { 7 | applicationId "com.heiko.networkdetectionsample" 8 | minSdkVersion 21 9 | targetSdkVersion 27 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | } 15 | 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 20 | ndk { //设置支持的SO库架构,取交集 21 | //abiFilters "armeabi" 22 | abiFilters "armeabi"/*, "x86", "armeabi-v7a", "x86_64", "arm64-v8a"*/ 23 | } 24 | } 25 | debug{ 26 | ndk { //设置支持的SO库架构,取交集 27 | //abiFilters "armeabi" 28 | abiFilters "armeabi"/*, "x86", "armeabi-v7a", "x86_64", "arm64-v8a"*/ 29 | } 30 | } 31 | 32 | } 33 | 34 | } 35 | 36 | dependencies { 37 | implementation fileTree(dir: 'libs', include: ['*.jar']) 38 | 39 | implementation 'androidx.appcompat:appcompat:1.1.0' 40 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 41 | testImplementation 'junit:junit:4.12' 42 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 43 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 44 | implementation project(path: ':networkdetection') 45 | } 46 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/heiko/networkdetectionsample/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.heiko.networkdetectionsample; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.heiko.networkdetectionsample", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/heiko/networkdetectionsample/ClipboardUtils.java: -------------------------------------------------------------------------------- 1 | package com.heiko.networkdetectionsample; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.ClipData; 5 | import android.content.ClipboardManager; 6 | import android.content.Context; 7 | import android.os.Build; 8 | 9 | /** 10 | * 剪切板工具类 11 | */ 12 | public class ClipboardUtils { 13 | 14 | /** 15 | * 复制到剪切板 16 | * 17 | * @param context 18 | * @param text 19 | */ 20 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 21 | public static void copyToClipboard(Context context, String text) { 22 | ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); 23 | clipboard.setPrimaryClip(ClipData.newPlainText(null, text)); 24 | } 25 | 26 | /** 27 | * 获取剪贴板指定Index的文本 28 | * 29 | * @param context 30 | * @param index 31 | * @return 32 | */ 33 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 34 | public static String getText(Context context, int index) { 35 | ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); 36 | ClipData clip = clipboard.getPrimaryClip(); 37 | if (clip != null && clip.getItemCount() > index) { 38 | return String.valueOf(clip.getItemAt(index).coerceToText(context)); 39 | } 40 | return null; 41 | } 42 | 43 | /** 44 | * 获取剪切板最后的文本 45 | * 46 | * @param context 47 | * @return 48 | */ 49 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 50 | public static String getLatestText(Context context) { 51 | ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); 52 | ClipData clip = clipboard.getPrimaryClip(); 53 | if (clip != null && clip.getItemCount() > 0) { 54 | return String.valueOf(clip.getItemAt(0).coerceToText(context)); 55 | } 56 | return null; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/heiko/networkdetectionsample/DeviceUtils.java: -------------------------------------------------------------------------------- 1 | package com.heiko.networkdetectionsample; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.provider.Settings; 7 | 8 | /** 9 | * Created by Darkal on 2016/9/21. 10 | */ 11 | 12 | public class DeviceUtils { 13 | /** 14 | * 获取版本号 15 | * 16 | * @return 当前应用的版本号 17 | */ 18 | public static String getVersion(Context context) { 19 | try { 20 | PackageManager manager = context.getPackageManager(); 21 | PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0); 22 | return info.versionName; 23 | } catch (Exception e) { 24 | e.printStackTrace(); 25 | return ""; 26 | } 27 | } 28 | 29 | /** 30 | * 获取版本号 31 | * 32 | * @return 当前应用的版本号 33 | */ 34 | public static int getVersionCode(Context context) { 35 | try { 36 | PackageManager manager = context.getPackageManager(); 37 | PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0); 38 | return info.versionCode; 39 | } catch (Exception e) { 40 | e.printStackTrace(); 41 | return 0; 42 | } 43 | } 44 | 45 | public static String getAndroidID(Context context) { 46 | String id = Settings.Secure.getString( 47 | context.getContentResolver(), 48 | Settings.Secure.ANDROID_ID 49 | ); 50 | if ("9774d56d682e549c".equals(id)) return ""; 51 | return id == null ? "" : id; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/heiko/networkdetectionsample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.heiko.networkdetectionsample; 2 | 3 | import android.content.DialogInterface; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.Button; 7 | import android.widget.EditText; 8 | import android.widget.TextView; 9 | import android.widget.Toast; 10 | 11 | import androidx.appcompat.app.AlertDialog; 12 | import androidx.appcompat.app.AppCompatActivity; 13 | 14 | import com.heiko.network.detection.task.TaskCallBack; 15 | import com.heiko.network.detection.task.TraceTask; 16 | 17 | public class MainActivity extends AppCompatActivity implements TaskCallBack { 18 | Button traceButton; 19 | TextView resultTextView; 20 | EditText urlEditText; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_main); 26 | 27 | traceButton = findViewById(R.id.bt_trace); 28 | resultTextView = findViewById(R.id.tv_result); 29 | urlEditText = findViewById(R.id.et_url); 30 | 31 | traceButton.setOnClickListener(new View.OnClickListener() { 32 | @Override 33 | public void onClick(View v) { 34 | resultTextView.setText(""); 35 | TraceTask traceTask = new TraceTask(getApplication(), 36 | urlEditText.getText() + "", MainActivity.this); 37 | traceTask.setAppVersion(DeviceUtils.getVersion(MainActivity.this)); 38 | traceTask.setAppName("NetworkDetection"); 39 | traceTask.setAppCode("01"); 40 | traceTask.setDeviceId(DeviceUtils.getAndroidID(MainActivity.this)); 41 | traceTask.setAlwaysPing(true); //是否永远进行Ping,如果是false,则根据当前网络环境判断是否要Ping 42 | traceTask.doTask(); 43 | } 44 | }); 45 | } 46 | 47 | @Override 48 | public void onUpdated(String log) { 49 | resultTextView.append(log); 50 | } 51 | 52 | @Override 53 | public void onFinish(String log) { 54 | new AlertDialog.Builder(this) 55 | .setTitle("诊断结束") 56 | .setMessage("可复制诊断信息到剪切板") 57 | .setCancelable(false) 58 | .setPositiveButton("复制", new DialogInterface.OnClickListener() { 59 | @Override 60 | public void onClick(DialogInterface dialog, int which) { 61 | String info = resultTextView.getText().toString(); 62 | ClipboardUtils.copyToClipboard(MainActivity.this, info); 63 | Toast.makeText(getApplicationContext(), "复制诊断信息成功!", Toast.LENGTH_SHORT).show(); 64 | } 65 | }) 66 | .setNegativeButton("取消", null) 67 | .show(); 68 | } 69 | 70 | @Override 71 | public void onFailed(Exception e) { 72 | resultTextView.append("诊断失败:" + e.getMessage()); 73 | Toast.makeText(this, "诊断失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | 21 | 22 |