计算机知识体系2022_0827

一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int gdata1 = 10;
int gdata2 = 0;
int gdata3;

static int gdata4 = 11;
static int gdata5 = 0;
static int gdata6;

int main()
{
int a = 12;
int b = 0;
int c;

static int d = 13;
static int e = 0;
static int f;

return 0;
}

以上,有若干变量。

有全局变量、静态全局变量,有局部变量、静态局部变量。

代码和程序的关系

  1. 任何语言代码产生的程序,无非是产生了两种东西。——指令、数据。程序员应该对自己写的代码了解清楚,哪些是数据,哪些是指令。
  2. 指令和数据加载到内存之后,肯定要区分区域,怎么划分?

计算机组成

三大件:CPU、内存、I/O,每家厂商有每家独自的产品实现。

为了在应用层屏蔽不同产品的差异:

  1. IO的屏蔽是VFS(虚拟文件系统)做的。
  2. 内存和IO一起作为一个资源分配的单位,用虚拟存储器(虚拟内存)屏蔽。
  3. CPU、内存、IO一起作为一个资源调度的单位,用进程来屏蔽。

因此,我们写的程序代码是不可能直接加载到物理内存上的。而是先加载到虚拟内存上。

虚拟内存

虚拟的——不存在,却看得到。那么虚拟这个概念实际上是不存在的,只是逻辑层抽象出来的。

每个程序进行运行时,系统都会给其分配虚拟内存。然后系统内部再去自动管理处理底层的物理内存。

虚拟内存大小有多大?和系统的位数有关。也可以理解为和CPU的位数有关。CPU位数的含义是什么?

系统的位数/CPU的位数——CPU位数!=地址总线位数

位数可以衡量一个CPU的计算能力。

CPU主要是用来运算数据。一句话来说CPU的位数等于它一次性可以计算的数据的长度。所以,CPU的位数实际上指的是CPU中ALU(算术逻辑单元)的宽度。

ALU运算的数据从何而来?从寄存器哪来,那么寄存器的数据从何而来?——数据总线。所以,CPU的位数=ALU宽度=数据总线的条数

所以,CPU位数压根和地址总线没关系,比如说x86体系下的16位CPU,它的数据总线是16位,而地址总线是20位;8位CPU的数据总线是8位,地址总线是16位。

虚拟内存有多大?和地址总线数有关,代表CPU的可寻址能力,假如CPU有32条地址总线,那么该CPU可寻址2322^{32}bit即4G。每个程序都会有4G的虚拟地址空间。但是要注意,CPU位数不一定等于地址总线条数!比如8086,是16位CPU,但是地址总线有20条,所以可寻址能力是220=1MB2^{20}=1MB

总结:

  1. CPU位数表示ALU宽度(数据总线条数)。所以要说CPU是多少位由ALU宽度(数据总线条数)决定。
  2. CPU可寻址能力由CPU芯片上的地址总线条数决定。
  3. CPU地址总线条数不等于CPU位数。

程序虚拟地址空间的布局

略,见“进程地址空间体系”一文。

从编译到链接–从obj到可执行文件

32位系统下,obj文件的地址是按4字节对齐的。

可执行文件是按照页面对齐的,一页的默认大小是4096Byte,即4KB。

第一步:合并、调整

合并相同名字的段?

多个obj文件在进行链接时,第一步,合并每一个obj文件相同的段。这样有利于充分利用每1页来存储信息。

比如有3个obj文件,3个obj的text段合并在一起,若不超过4KB,则占1页。类似地,data段、bss段也合并并且各自占独立的页面。

即,可执行文件的页面是按不同段来划分的,每一页上都有全部的obj的同一个段。

优化:合并相同权限属性的段

其实,可以进一步优化,即按照相同的权限属性去划分页。比如只读数据、可读可写数据、可读可执行等等。

只读数据比如说:常量字符串就存在于.rodata段;另外代码段也是只读的。所以可以把这两者存在同一页中。

可读可写数据比如说:data段和bss段合并,这样就节省了一个页面的空间。

因此,链接的第一步:所有相同属性的段进行合并,组织在同一个页面上

调整段表中每一个段的起始偏移量和段的大小

因为段进行了合并,因此需要调整段的起始偏移量。

合并符号表

每一个obj文件都有符号表,符号表也要合并。

进行符号解析

所有obj符号表中对符号引用的地方都要找到该符号在哪里、哪个文件中定义?

也是在此步,进行强弱符号、符号重定义的判别、决策。

分配内存地址

之前obj符号表中对符号引用的地方的地址是空的,待填,现在要分配其地址。得到合法的虚拟空间的地址。

第二步:链接的核心——符号的重定位

已经做好了符号解析、内存地址的分配,现在要更新之前符号地址错误、待填的地址信息。即在指令段里把没填的地址修改为正确的地址

可执行文件的格式

观察可执行文件其ELF Header,其中有一个Entry point address属性,存储了程序的入口地址,通常是main函数地址。

其次,相比obj文件,其多了一个模块是program headers。

而且program header有很多个,比如,这个可执行文件有3个program header。

1
2
3
4
5
6
7
8
9
10
11
12
13
# readelf -l <可执行文件名>
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x000e2 0x00028 R E 0x1000
LOAD 0x0000e4 0x080490e4 0x080490e4 0x00010 0x00028 RW 0x1000
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4

Section to Segment mapping:
Segment Sections...
00 .text
01 ,data .bss
02

两个Load类型的header的Align是0x1000,说明其对齐方式是4KB,一页。

没有这两个LOAD页操作系统将不知道这个可执行文件如何执行。

下面的Section to Segment mapping说的是每一个header里面对应、包含着哪些段。

我们可以看到,第一个说的是LOAD1,包含了.text段;第二个说的是LOAD2,包含了.data和.bss段;

同时可以在Flg属性看到,第一个LOAD是RE,代表可读、可执行;第二个LOAD是RW,可读可写。也证实了,可执行文件的页面是按照之前obj文件中相同属性的段来合并到一页的。

program headers包含的这两个LOAD页指示了操作系统,要把哪些东西加载到内存上。这是可执行文件和不可执行文件的本质区别。

可执行文件加载/映射到虚拟地址空间

可执行文件中的LOAD页,虚拟地址空间都是按页划分的。

可执行文件加载/映射到虚拟地址空间这个过程是mmap。深入理解计算机系统第9章。

虚拟地址空间加载/映射到物理内存

物理内存也是按页面划分的。

虚拟地址空间加载/映射到物理内存——多级页表映射方式实现。

程序的运行

  1. 创建虚拟地址空间到物理内存的映射(创建内核地址映射结构体),创建页目录和页表。
  2. 加载代码段和数据段。
  3. 把可执行文件的入口地址写入CPU寄存器中。

可以执行strace <可执行文件名>查看可执行文件运行后所做的动作。

image-20220828091756955

可以看到,执行了execve,可想而知和我们的常识相符,一个新的程序要执行,首先要fork,之后再exec替换进程地址空间。

之后,进行若干次mmap。即完成可执行文件LOAD页加载到虚拟地址空间。

再之后,最终程序肯定是要跑到物理内存上运行的,尚需完成虚拟地址空间到物理内存空间的映射。–请见“Linux_地址映射”一文。

相关书籍

  1. 程序员的自我修养1、2、3、4、6、10章;
  2. 深入理解计算机系统5、6、7、8、9章。
  3. 现代操作系统/操作系统精髓与设计原理
  • 虚拟内存
  • 进程管理以及通信
  • 网络
  • 虚拟文件系统

Android_远程抓取设备信息上报服务

需求

远程诊断,诊断信息包括基础系统信息、硬件信息、容量信息、网络信息等,兼容旧平台远程诊断逻辑和旧版本固件。

前端模拟效果:

image-20220825152733320

远程诊断属性 备注
光线传感器 当前数值,或异常
色温传感器 当前数值,或异常
重力传感器 当前数值(x,y,z),或异常
麦克风 正常或异常
DNS 可能有多个地址。
网络诊断模块 - 抖动、丢包、延时 可能需要参考多个地址,如果只按希沃后台来诊断,则不能判断第三方是否有问题
网络诊断模块 - 当前局域网设备数 获取局域网其他设备数可能不具有参考价值,重点需要判断设备本身的网络情况

设备端主要任务

  1. 系统版本
  2. 获取时间
    1. 系统当前时间
    2. 已运行时间
    3. 上次启动时间,无直接获取的API,系统当前时间减去已运行时间即可。
  3. 感知状态
    1. 三个传感器,异步获取其即时值。
    2. 麦克风状态,异步检测。
  4. 硬件负载
    1. CPU负载计算
    2. 内存、硬盘状态信息获取
  5. 网卡信息获取
    1. 内网IP地址
    2. 子网掩码
    3. MAC地址
    4. DNS配置地址
  6. 外网信息
    1. 外网IP地址
    2. 运营商名称

链路

设备端服务程序常运行,监听后端IOT下发指令。设备进行一系列信息抓取、硬件检查后,doput上报。

框架设计

1
2
3
4
5
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
38
39
/**
* 约定分发规则:
* 服务类型: key = method
* 属性类型: key = thing.property.get#属性名 或者 thing.property.get#属性名
*/
object ExtHandlerManager : CoroutineScope {
override val coroutineContext: CoroutineContext
get() = Dispatchers.Default
private val serviceMap = hashMapOf<String, IServiceHandler>()
private val propertyMap = hashMapOf<String, IPropertyHandler>()
var iotClient: ICIoTClient? = null

init {
//service
serviceMap["thing.service.sendDiagnosisFetchMinder"] = SendDiagnosisFetchMinderHandler()
}
fun registerHandlers(manger: IHandlerManager?) {
manger?.apply {
for ((k, v) in serviceMap) {
registerServiceHandler(k, v)
}
for ((k, v) in propertyMap) {
registerPropertyHandler(k, v)
}
startHandle()
}
}

fun unregisterHandlers(manger: IHandlerManager?) {
manger?.apply {
for ((k, v) in serviceMap) {
unregisterServiceHandler(k, v)
}
for ((k, v) in propertyMap) {
unregisterPropertyHandler(k, v)
}
}
}
}

Handler接口

IOT下发指令,向设备请求。设备端收到请求后,用对应的handler进行请求参数的处理。即实现onRequest方法。其中,请求可带参数,约定为json串,可下发一个taskUid,方便后续设备发送响应时回传。

1
2
3
interface IServiceHandler {
fun onRequest(topic: String?, method: String?, params: String?, traceId: String?)
}

接到请求后,启一个Runnable线程,线程函数里执行DiagnosisFetchListener().sendDiagnosisFetchMinder(taskUid),开始进行设备信息的抓取和诊断,之后整合为json串后响应给IOT后台。

1
2
3
4
5
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
38
39
40
/**
* RemoteDetectSystemInfoHandler
* @Author xingchenggong 2022/08/23
*/
class SendDiagnosisFetchMinderHandler : IServiceHandler, CoroutineScope {
private val TAG = "SendDiagnosisFetchMinderHandler"
override val coroutineContext: CoroutineContext
get() = Dispatchers.Default
private val handler = Handler(Looper.getMainLooper())

//{
// "uid": "text, 32字节"
//}
override fun onRequest(topic: String?, method: String?, params: String?, traceId: String?) {
Log.i(TAG, ">>> topic: $topic, method:$method, params:$params, traceId:$traceId")
var taskUid: String? = null
if(!params.isNullOrEmpty()) {
taskUid = JSONObject(params).get("taskUid") as String
val paramsAvailable = checkParamsAvailable(taskUid)
} else {
Log.e(TAG, "DiagnosisFetch error, cause params is empty")
return
}
try {
val diagnosisFetchInfo = Runnable { DiagnosisFetchListener().sendDiagnosisFetchMinder(taskUid) }
handler.post(diagnosisFetchInfo)
} catch (e: IllegalStateException) {
Log.e(TAG, ">>> onRequest:java.lang.IllegalStateException")
}
}
private fun checkParamsAvailable(uid: String): Boolean {
var paramsAvailable = true
if(uid.isBlank()) {
Log.e(TAG, "checkParamsAvailable failed, uid is empty")
paramsAvailable = false
}
return paramsAvailable
}

}

设备信息抓取和诊断以及之后响应后台的实现

数据模型

1
2
3
4
5
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
data class DiagnosisReportInfo(
val taskUid: String,
val diagnosisResultBo: DiagnosisFetchInfo,
val taskStatus: Int
)
data class OuterNetInfo(
val ip: String, //外网ip
val netOperator: String //外网运营商
)
data class InnerNetInfo(
val ip: String, //内网ip
val subNetMask: String, //子网掩码
val mac: String, //网卡地址
val dns: List<String> //dns
)
data class DiagnosisFetchInfo(
val version: String,
val systemTime: Long,
val lastStartTime: Long,
val runningTime: Long,
val lightSensorData: String,
val colorTemperatureSensorData: String,
val gravitySensorData: String,
val microphoneState: String,
val cpuInfo: Float,
val totalMemory: Float,
val usageMemory: Float,
val totalStorage: Float,
val usageStorage: Float,
val outerNetInfo: OuterNetInfo,
val innerNetInfoWifi: InnerNetInfo,
val innerNetInfoEth: InnerNetInfo
)

时间获取

1
2
3
4
5
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
import android.os.SystemClock
/**
* @author: xingchenggong 2022/08/22
*/
object SystemTimeUtils {

fun getCurrentSystemTimestamp(): Long{
try {
return System.currentTimeMillis()
} catch (e: Exception) {
e.printStackTrace()
}
return 0
}
fun getElapsedTimeMillis(): Long {
try {
return SystemClock.elapsedRealtime()
} catch (e: Exception) {
e.printStackTrace()
}
return 0
}
fun getLastStartTimestamp(): Long {
var timestamp = getCurrentSystemTimestamp() - getElapsedTimeMillis()
return if(timestamp >= 0) {
timestamp
} else {
0
}
}
}

传感器状态获取

1
2
3
4
5
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
38
39
40
41
42
43
44
45
46
47
48
49
50
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.List;

public class SensorUtil {
private SensorResultCallback mSensorResultCallback;
private H mHandler;
private Looper mLooper;
private SensorManager mSensorManager = null;
private SensorEventListener mSensorEventListener;
private boolean mIsAlreadyGetResult = false;

private final static int MSG_GET_SENSOR_TIME_OUT = 100;
private final static int MSG_GET_SENSOR_NORMAL = 101;
private String mSensorName;
public SensorUtil(String name) {
mSensorName = name;
}

public Boolean startRegisterListener(Context context, int sensorType, SensorResultCallback callback) {
mSensorResultCallback = callback;
HandlerThread thread = new HandlerThread(mSensorName);
thread.start();
mLooper = thread.getLooper();
mHandler = new H(mLooper);

mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
Sensor sensor = mSensorManager.getDefaultSensor(sensorType);

if (sensor == null) {
return false;
}

mSensorEventListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
if (!mIsAlreadyGetResult) {
mIsAlreadyGetResult = true;
mHandler.removeMessages(MSG_GET_SENSOR_TIME_OUT);
mHandler.removeMessages(MSG_GET_SENSOR_NORMAL);
List<Float> result = new ArrayList<Float>(3); //TODO get result
int i = event.values.length;
for (float val : event.values) {
result.add(val);
}
mHandler.obtainMessage(MSG_GET_SENSOR_NORMAL, 0, 0, result).sendToTarget();
}
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {

}
};

mSensorManager.registerListener(mSensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);

mHandler.removeMessages(MSG_GET_SENSOR_TIME_OUT);
mHandler.sendEmptyMessageDelayed(MSG_GET_SENSOR_TIME_OUT, 5000);
return true;
}

private void stopListener() {
if (mSensorManager != null && mSensorEventListener != null) {
mSensorManager.unregisterListener(mSensorEventListener);
}

if (mLooper != null) {
mLooper.quitSafely();
}
}

public interface SensorResultCallback {
void onDataCallBack(List<Float> result);
void onGetDataTimeout();
}

private class H extends Handler {
private H() {
super();
}

private H(Looper looper) {
super(looper);
}

@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);

if (mSensorResultCallback == null) {
return;
}

switch (msg.what) {
case MSG_GET_SENSOR_NORMAL:
mSensorResultCallback.onDataCallBack((List<Float>)msg.obj);
break;
case MSG_GET_SENSOR_TIME_OUT:
mSensorResultCallback.onGetDataTimeout();
break;
}
stopListener();
mSensorResultCallback = null;
}
}
}

麦克风检测

1
2
3
4
5
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
38
39
40
41
42
object DeviceInfoUtils {
/**
* 判断当前设备是否是XX15
*/
@JvmStatic
fun isXX15Model(): Boolean {
// XX15
return Build.MODEL.contains("XX15")
}

/**
* 判断当前设备是否是XX02
*/
@JvmStatic
fun isXX02Model(): Boolean {
// XX02
return Build.MODEL.contains("XX02")
}
}

class DetectMicUtils {
private val audioRecordTest by lazy {
AudioRecordTest()
}
private fun detectMic(): Boolean {
// 不同设备型号不同的检测方式
return if (DeviceInfoUtils.isXX15Model()) {
audioRecordTest.beginTest()
} else {
SystemPropertiesUtils.set("mic.hw.test.req", "1")
Thread.sleep(2000L)
SystemPropertiesUtils.get("mic.hw.test.ret", "0") == "11"
}
}
fun getMicState(): String {
return if (detectMic()) {
"正常"
} else {
"异常"
}
}
}
1
2
3
4
5
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
38
39
40
41
42
43
44
45
46
47
48
49
50
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder

class AudioRecordTest {

private val AUDIO_INPUT = MediaRecorder.AudioSource.VOICE_RECOGNITION
private val AUDIO_SAMPLE_RATE = 32000
private val AUDIO_CHANNEL = AudioFormat.CHANNEL_IN_MONO
private val AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT

private val MAX_ALLOW_DB_BETWEEN_VALUE = 5
private val MIN_DB_VALUE = 3 // 只有底噪时会有 0 ~ 2db值
private var recordBufSize = 0

private lateinit var recordBuffer: ByteArray
private var mAudioRecord: AudioRecord? = null
private lateinit var recordBufferMic: ByteArray
private lateinit var recordBufferMic1: ByteArray
private lateinit var recordBufferMic2: ByteArray

private var isRecording = false
private var mErrorCounts = 0
private var mInValidDataCounts = 0
private var mValidDataCounts = 0

fun isRecordTesting():Boolean {
return isRecording
}

fun beginTest():Boolean {
var result = false

if (isRecording) {
return result
}

recordBufSize =
AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING)

mAudioRecord = AudioRecord(AUDIO_INPUT, AUDIO_SAMPLE_RATE, AUDIO_CHANNEL,
AUDIO_ENCODING, recordBufSize)

recordBuffer = ByteArray(recordBufSize)

if (recordBufSize % 4 == 0) {
recordBufferMic = ByteArray(recordBufSize / 2)
recordBufferMic1 = ByteArray(recordBufSize / 4)
recordBufferMic2 = ByteArray(recordBufSize / 4)
}

isRecording = true
mValidDataCounts = 0
mInValidDataCounts = 0
mErrorCounts = 0

mAudioRecord!!.startRecording()

while (isRecording) {
val read = mAudioRecord!!.read(recordBuffer, 0, recordBufSize)

if (read > AudioRecord.SUCCESS) {
try {
var i = 0
while (i + 7 < read) {
var index = i / 2
recordBufferMic[index] = recordBuffer[i]
recordBufferMic[index + 1] = recordBuffer[i + 1]
recordBufferMic[index + 2] = recordBuffer[i + 2]
recordBufferMic[index + 3] = recordBuffer[i + 3]

index /= 2

recordBufferMic1[index] = recordBuffer[i]
recordBufferMic1[index + 1] = recordBuffer[i + 1]

recordBufferMic2[index] = recordBuffer[i + 2]
recordBufferMic2[index + 1] = recordBuffer[i + 3]

i += 8
}

val origDb = getPcmDB(recordBufferMic, read / 2) // 获取原始mic的分贝值
val mic1Db = getPcmDB(recordBufferMic1, read / 4) // 获取mic1的分贝值
val mic2Db = getPcmDB(recordBufferMic2, read / 4) // 获取mic2的分贝值

if (origDb < 0 && mic1Db < 0 && mic2Db < 0) {
if (++mInValidDataCounts >= 100) { // 大约 1s
stopRecordTest()
result = false
break
}
} else if (Math.abs(origDb - mic1Db) >= MAX_ALLOW_DB_BETWEEN_VALUE
|| Math.abs(origDb - mic2Db) >= MAX_ALLOW_DB_BETWEEN_VALUE
|| mic1Db <= MIN_DB_VALUE && mic2Db <= MIN_DB_VALUE) {
if (++mErrorCounts >= 10) {
stopRecordTest()
result = false
break
}
} else {
mInValidDataCounts = 0
mErrorCounts = 0
if (++mValidDataCounts >= 100) {
stopRecordTest()
result = true
}
}
} catch (e: Exception) {
stopRecordTest()
result = false
e.printStackTrace()
}
} else if (read < AudioRecord.SUCCESS) {
stopRecordTest()
result = false
}
}

return result
}

private fun getPcmDB(pcmData: ByteArray, size: Int): Int {
var sum = 0.0
var temp: Long
var i = 0
while (i < pcmData.size) {
temp = (pcmData[i + 1].toLong() shl 8) or (pcmData[i].toLong() and 0x000000FF.toLong())
sum += Math.abs(temp).toDouble()
i += 2
}
return (20 * Math.log10(sum / size.toDouble())).toInt()
}

private fun stopRecordTest() {
if (!isRecording) {
return
}
isRecording = false
if (null != mAudioRecord) {
mAudioRecord!!.stop()
mAudioRecord!!.release()
mAudioRecord = null
}
}
}

硬件负载情况

CPU负载

1
2
3
4
5
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@SuppressLint({"DefaultLocale", "LongLogTag"})
public static float getCpuTotalUsageFloat() {
try {
double cpuIdleTimePre;
double cpuOccupiedTimePre;
double cpuIdleTimeCur;
double cpuOccupiedTimeCur;

ShellCommandUtils.CommandResult cpuInfoResult = ShellCommandUtils.executeCommand("cat /proc/stat | head -n 1");
if (cpuInfoResult.result == 0) {
String line = cpuInfoResult.successMsg;
String[] cpuTimeSubInfo = line.split(" ");
cpuIdleTimePre = Double.parseDouble(cpuTimeSubInfo[5]);//5:idle
cpuOccupiedTimePre = Double.parseDouble(cpuTimeSubInfo[2]) +//2:user
Double.parseDouble(cpuTimeSubInfo[3]) +//3:nice
Double.parseDouble(cpuTimeSubInfo[4]) +//4:system
Double.parseDouble(cpuTimeSubInfo[6]) +//6:iowait
Double.parseDouble(cpuTimeSubInfo[7]) +//7:irq
Double.parseDouble(cpuTimeSubInfo[8]);//8:softirq
} else {
Log.e(TAG, "getCpuTotalUsage: " + cpuInfoResult.errorMsg);
return 0.0f;
}
try {
Thread.sleep(360);
} catch (Exception e) {
e.printStackTrace();
}

cpuInfoResult = ShellCommandUtils.executeCommand("cat /proc/stat | head -n 1");
if (cpuInfoResult.result == 0) {
String line = cpuInfoResult.successMsg;
String[] cpuTimeSubInfo = line.split(" ");
cpuIdleTimeCur = Double.parseDouble(cpuTimeSubInfo[5]);//5:idle
cpuOccupiedTimeCur = Double.parseDouble(cpuTimeSubInfo[2]) +//2:user
Double.parseDouble(cpuTimeSubInfo[3]) +//3:nice
Double.parseDouble(cpuTimeSubInfo[4]) +//4:system
Double.parseDouble(cpuTimeSubInfo[6]) +//6:iowait
Double.parseDouble(cpuTimeSubInfo[7]) +//7:irq
Double.parseDouble(cpuTimeSubInfo[8]);//8:softirq
} else {
Log.e(TAG, "getCpuTotalUsage: " + cpuInfoResult.errorMsg);
return 0.0f;
}
double deltaCpuOccupiedTime = 100 * (cpuOccupiedTimeCur - cpuOccupiedTimePre);
double deltaCpuTotalTime = (cpuOccupiedTimeCur + cpuIdleTimeCur) -
(cpuOccupiedTimePre + cpuIdleTimePre);
BigDecimal deltaCpuOccupiedTimeBigDecimal = new BigDecimal(deltaCpuOccupiedTime);
BigDecimal deltaCpuTotalTimeBigDecimal = new BigDecimal(deltaCpuTotalTime);
float cpuUsageIntValue = deltaCpuOccupiedTimeBigDecimal.divide(deltaCpuTotalTimeBigDecimal,
BigDecimal.ROUND_HALF_UP).intValue();
return cpuUsageIntValue;
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "getCpuTotalUsage: " + e.getMessage());
}
return 0.0f;
}

获取存储信息

1
2
3
4
5
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
38
39
40
41
42
43
44
import android.annotation.SuppressLint
import android.app.ActivityManager
import android.content.Context
import android.content.Context.ACTIVITY_SERVICE
import utils.FormatterUtils.formatFromByteToGB

class SystemMemoryUtils(context: Context) {

companion object {
const val TAG = "SystemStorageUtils"
}

private val mContext = context

@SuppressLint("ServiceCast")
private val mActivityManager =
mContext.getSystemService(ACTIVITY_SERVICE) as ActivityManager

val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
getCurrentSystemMemoryUsage()
}

fun getSystemMemoryTotalFloat(): Float {
try {
val memoryInfo = ActivityManager.MemoryInfo()
mActivityManager.getMemoryInfo(memoryInfo)
return formatFromByteToGB(memoryInfo.totalMem)
} catch (e: Exception) {
e.printStackTrace()
}
return 0.0f
}

fun getCurrentSystemMemoryUsageFloat(): Float {
try {
val memoryInfo = ActivityManager.MemoryInfo()
mActivityManager.getMemoryInfo(memoryInfo)
return formatFromByteToGB(memoryInfo.totalMem - memoryInfo.availMem)
} catch (e: Exception) {
e.printStackTrace()
}
return 0.0f
}
}
1
2
3
4
5
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
import android.app.usage.StorageStatsManager
import android.content.Context
import android.content.Context.STORAGE_STATS_SERVICE
import android.os.Build
import android.os.storage.StorageManager
import androidx.annotation.RequiresApi
import java.io.IOException
import java.text.DecimalFormat

@RequiresApi(Build.VERSION_CODES.O)
class SystemStorageUtils(context: Context) {
companion object{
const val TAG = "SystemStorageUtils"
}
private val mContext = context
private val mStorageStatsManager =
mContext.getSystemService(STORAGE_STATS_SERVICE) as StorageStatsManager

fun getSystemTotalStorageFloat(): Float {
return FormatterUtils.formatFromByteToGB(mStorageStatsManager.getTotalBytes(StorageManager.UUID_DEFAULT))
}

fun getCurrentSystemStorageUsageFloat(): Float {
try {
val totalStorage = mStorageStatsManager.getTotalBytes(StorageManager.UUID_DEFAULT)
val availableStorage = mStorageStatsManager.getFreeBytes(StorageManager.UUID_DEFAULT)
return FormatterUtils.formatFromByteToGB(totalStorage - availableStorage)
} catch (e: IOException) {
e.printStackTrace()
}
return 0.0f
}
}

内网网卡信息获取

1
2
3
4
5
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
38
39
40
41
42
43
44
45
46
47
48
49
50
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
public static String getDeviceSpecifiedMacAddress(String NICName) {
if(NICName == null || NICName.isEmpty())
{
return "";
}
try {
Enumeration<NetworkInterface> enumeration = NetworkInterface.getNetworkInterfaces();
while (enumeration.hasMoreElements()) {
NetworkInterface networkInterface = enumeration.nextElement();
if (networkInterface != null && !TextUtils.isEmpty(networkInterface.getName())) {
if (networkInterface.getName().equalsIgnoreCase(NICName)) {
byte[] arrayOfByte = networkInterface.getHardwareAddress();
if (arrayOfByte != null && arrayOfByte.length > 0) {
StringBuilder deviceMacAddress = new StringBuilder();
// format the mac address
for (byte b : arrayOfByte) {
deviceMacAddress.append(String.format("%02X:", b));
}
if (deviceMacAddress.length() > 0) {
deviceMacAddress.deleteCharAt(deviceMacAddress.length() - 1);
}
return deviceMacAddress.toString();
}
}
}
}
} catch (SocketException e) {
e.printStackTrace();
Log.e(TAG, "getDeviceSpecifiedMacAddress: " + e.getMessage());
}
return "";
}
public static String getDeviceSpecifiedIPAddress(String NICName) {
if(NICName == null || NICName.isEmpty())
{
return "";
}
String IPAddress = "";
try {
Enumeration<NetworkInterface> enumeration = NetworkInterface.getNetworkInterfaces();
while (enumeration.hasMoreElements()) {
NetworkInterface networkInterface = enumeration.nextElement();
if (networkInterface != null) {
if (networkInterface.getName().equalsIgnoreCase(NICName)) {
Enumeration<InetAddress> ei = networkInterface.getInetAddresses();
while (ei.hasMoreElements()) {
InetAddress i = ei.nextElement();
if(i instanceof Inet4Address)
IPAddress = i.getHostAddress();
}
}
}
}
} catch (SocketException e) {
e.printStackTrace();
}
return IPAddress;
}
/**
* Masks a raw IP address byte array with the specified prefix length.
*/
public static void maskRawAddress(byte[] array, int prefixLength) {
if (prefixLength < 0 || prefixLength > array.length * 8) {
throw new RuntimeException("IP address with " + array.length +
" bytes has invalid prefix length " + prefixLength);
}

int offset = prefixLength / 8;
int remainder = prefixLength % 8;
byte mask = (byte)(0xFF << (8 - remainder));

if (offset < array.length) array[offset] = (byte)(array[offset] & mask);

offset++;

for (; offset < array.length; offset++) {
array[offset] = 0;
}
}
/**
* Get InetAddress masked with prefixLength. Will never return null.
* @param address the IP address to mask with
* @param prefixLength the prefixLength used to mask the IP
*/
public static InetAddress getNetworkPart(InetAddress address, int prefixLength) {
byte[] array = address.getAddress();
maskRawAddress(array, prefixLength);

InetAddress netPart = null;
try {
netPart = InetAddress.getByAddress(array);
} catch (UnknownHostException e) {
throw new RuntimeException("getNetworkPart error - " + e.toString());
}
return netPart;
}
private static String ipv4PrefixLengthToSubnetMask(int prefixLength) {
try {
InetAddress all = InetAddress.getByAddress(
new byte[]{(byte) 255, (byte) 255, (byte) 255, (byte) 255});
return getNetworkPart(all, prefixLength).getHostAddress();
} catch (UnknownHostException e) {
return null;
}
}
public static String getDeviceSpecifiedSubNetMaskFromConnectionManager(Context context, String NICName) {
if(NICName == null || NICName.isEmpty())
{
return "";
}
String subnet = "";
if (Build.VERSION.SDK_INT >= 21 && context != null) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE);
if (connectivityManager != null) {
for (Network network : connectivityManager.getAllNetworks()) {
LinkProperties lp = connectivityManager.getLinkProperties(network);
if (lp.getInterfaceName().equals(NICName))
{
Log.w("SUBNETMASK", NICName);
for (LinkAddress addr : lp.getLinkAddresses()) {
if (addr.getAddress() instanceof Inet4Address) {
subnet = ipv4PrefixLengthToSubnetMask(addr.getPrefixLength());
}
}
}
}
}
}
return subnet;
}

public static List<String> getDeviceActiveNetWorkDnsFromConnectionManager(Context context, int type) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return getDeviceSpecifiedDnsFromConnectionManager(context, activeNetworkInfo.getType());
}

public static List<String> getDeviceSpecifiedDnsFromConnectionManager(Context context, int type) {
List<String> dnsServers = new ArrayList<>();
if (Build.VERSION.SDK_INT >= 21 && context != null) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE);
if (connectivityManager != null) {
for (Network network : connectivityManager.getAllNetworks()) {
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network);
if (networkInfo != null && networkInfo.getType() == type) {
LinkProperties lp = connectivityManager.getLinkProperties(network);
for (InetAddress addr : lp.getDnsServers()) {
dnsServers.add(addr.getHostAddress());
}
}
}
}
}
return dnsServers;
}

外网信息获取

1
2
3
4
5
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@SuppressLint("LongLogTag")
public static String getEthernetIPFromJson(String ethernetJsonStr) {
try {
if (ethernetJsonStr.isEmpty()) {
Log.d(TAG, "getEthernetIPFromJson: Analysis Json is empty!");
return "NA";
}
JSONObject jsonObject = new JSONObject(ethernetJsonStr);
String msg = jsonObject.getString("msg");
if (msg.isEmpty()) {
Log.d(TAG, "getEthernetIPFromJson: Analysis Json no msg tag!");
return "NA";
}
if (!msg.equals("ok")) {
Log.d(TAG, "getEthernetIPFromJson: Analysis Json TAG ok failed!");
return "NA";
}
jsonObject = jsonObject.getJSONObject("data");
String ethernetIPFromJson = jsonObject.getString("ip");
if (ethernetIPFromJson == null || ethernetIPFromJson.isEmpty())
ethernetIPFromJson = "NA";
return ethernetIPFromJson;
} catch (JSONException e) {
e.printStackTrace();
Log.e(TAG, "getEthernetIPFromJson: " + e.getMessage());
return "NA";
}
}

@SuppressLint("LongLogTag")
public static String getEthernetOperatorFromJson(String ethernetJsonStr) {
try {
if (ethernetJsonStr.isEmpty()) {
Log.d(TAG, "getEthernetIPFromJson: Analysis Json is empty!");
return "NA";
}
JSONObject jsonObject = new JSONObject(ethernetJsonStr);
String msg = jsonObject.getString("msg");
if (msg.isEmpty()) {
Log.d(TAG, "getEthernetIPFromJson: Analysis Json no msg tag!");
return "NA";
}
if (!msg.equals("ok")) {
Log.d(TAG, "getEthernetOperatorFromJson: Analysis Json TAG ok failed!");
return "NA";
}
jsonObject = jsonObject.getJSONObject("data");
return jsonObject.getString("operator");
} catch (JSONException e) {
e.printStackTrace();
Log.e(TAG, "getEthernetOperatorFromJson: " + e.getMessage());
}
return "NA";
}

汇总

1
2
3
4
5
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
38
39
40
41
42
43
44
45
46
47
48
49
50
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
class DiagnosisFetchListener {
private var mSystemVersion : String? = null

private fun getSystemVersion():String {
if (mSystemVersion.isNullOrEmpty()) {
mSystemVersion = SystemPropertiesUtils.get(BuildConfig.SYSTEM_VERSION_PROP, "xxxOS")
}
return mSystemVersion as String
}
//方法:thing.event.sendDiagnosisFetchMinder.post
//{
// "method": "thing.event.sendDiagnosisFetchMinder.post",
//}
fun sendDiagnosisFetchMinder(taskUid: String) {
GetEthernetInfoRequestJson().getEthernetRequest(object : GetEthernetInfoRequestJson.AnalysisEthernetJson {
@RequiresApi(Build.VERSION_CODES.O)
override fun analysisEthernetFromJson(json: String) {
if (json.isEmpty()){
Log.e(
DeviceReport.TAG,
">>> analysisEthernetFromJson(): origin json string is empty, return")
return
}
val ip: String = RemoteDetectSystemInfoUtil.getEthernetIPFromJson(json)
val netOperator: String = RemoteDetectSystemInfoUtil.getEthernetOperatorFromJson(json)

if (ip.isEmpty() || netOperator.isEmpty()) {
Log.d(DeviceReport.TAG, ">>> CIot-ext-Handler sendDiagnosisFetchMinder!! ip or netOperator is null!")
}
val version: String = getSystemVersion()
//systemTime, millis
val systemTime: Long = SystemTimeUtils.getCurrentSystemTimestamp()
//lastStartTime, millis
val lastStartTime: Long = SystemTimeUtils.getLastStartTimestamp()
//runningTime, millis
val runningTime: Long = SystemTimeUtils.getElapsedTimeMillis()

//lightSensor
val lightSensorUtil = SensorUtil("lightSensor")
var lightSensorData: String = ""
if(! lightSensorUtil.startRegisterListener(
ContextHelper.getApplicationContext(),
Sensor.TYPE_LIGHT, object: SensorUtil.SensorResultCallback {
override fun onDataCallBack(result: List<Float>) {
lightSensorData = result[0].toString() + "lux"
}
override fun onGetDataTimeout() {
lightSensorData = "超时异常"
}

}))
{
lightSensorData = "获取传感器失败,可能是硬件异常"
}
//colorTemperatureSensor
val colorTemperatureSensorUtil = SensorUtil("colorTemperatureSensor")
var colorTemperatureSensorData: String = ""
if(! colorTemperatureSensorUtil.startRegisterListener(
ContextHelper.getApplicationContext(),
36, object: SensorUtil.SensorResultCallback {
override fun onDataCallBack(result: List<Float>) {
colorTemperatureSensorData = result[0].toString() + "k"
}
override fun onGetDataTimeout() {
colorTemperatureSensorData = "超时异常"
}

}))
{
colorTemperatureSensorData = "获取传感器失败,可能是硬件异常"
}
//gravitySensorData
val gravitySensorUtil = SensorUtil("gravitySensor")
var gravitySensorData: String = ""
if(! gravitySensorUtil.startRegisterListener(
ContextHelper.getApplicationContext(),
Sensor.TYPE_ACCELEROMETER, object : SensorUtil.SensorResultCallback {
override fun onDataCallBack(result: List<Float>) {
gravitySensorData = "x:" + result[0].toString() +
" y:" + result[1].toString() +
" z:" + result[2].toString()
}
override fun onGetDataTimeout() {
gravitySensorData = "超时异常"
}
}))
{
gravitySensorData = "获取传感器失败,可能是硬件异常"
}
//microphoneState
val microphoneState: String = DetectMicUtils().getMicState()
// 容量情况
val cpuInfo: Float = RemoteDetectSystemInfoUtil.getCpuTotalUsageFloat()
val totalMemory: Float = SystemMemoryUtils(ContextHelper.getApplicationContext()).getSystemMemoryTotalFloat()
val usageMemory: Float = SystemMemoryUtils(ContextHelper.getApplicationContext()).getCurrentSystemMemoryUsageFloat()
val totalStorage: Float = SystemStorageUtils(ContextHelper.getApplicationContext()).getSystemTotalStorageFloat()
val usageStorage: Float = SystemStorageUtils(ContextHelper.getApplicationContext()).getCurrentSystemStorageUsageFloat()

// 内网情况
// ip, 子网掩码, mac地址, DNS(可能有多个),
val wlan0ip: String = RemoteDetectSystemInfoUtil.getDeviceSpecifiedIPAddress("wlan0")
val wlan0subnetmask: String = RemoteDetectSystemInfoUtil.
getDeviceSpecifiedSubNetMaskFromConnectionManager(ContextHelper.getApplicationContext(),"wlan0")
val wlan0mac: String = RemoteDetectSystemInfoUtil.getDeviceSpecifiedMacAddress("wlan0")
val wlan0dns: List<String> = RemoteDetectSystemInfoUtil.
getDeviceSpecifiedDnsFromConnectionManager(getApplicationContext(), ConnectivityManager.TYPE_WIFI)

val eth0ip: String = RemoteDetectSystemInfoUtil.getDeviceSpecifiedIPAddress("eth0")
val eth0subnetmask: String = RemoteDetectSystemInfoUtil.
getDeviceSpecifiedSubNetMaskFromConnectionManager(ContextHelper.getApplicationContext(), "eth0")
val eth0mac: String = RemoteDetectSystemInfoUtil.getDeviceSpecifiedMacAddress("eth0")
val eth0dns: List<String> = RemoteDetectSystemInfoUtil.
getDeviceSpecifiedDnsFromConnectionManager(getApplicationContext(), ConnectivityManager.TYPE_ETHERNET)

var outerNetInfo: OuterNetInfo = OuterNetInfo(ip, netOperator)

var innerNetInfoWifi: InnerNetInfo = InnerNetInfo(wlan0ip,wlan0subnetmask, wlan0mac, wlan0dns)

var innerNetInfoEth: InnerNetInfo = InnerNetInfo(eth0ip,eth0subnetmask, eth0mac, eth0dns)

val diagnosisFetchInfo = DiagnosisFetchInfo(
version,
systemTime,
lastStartTime,
runningTime,
lightSensorData,
colorTemperatureSensorData,
gravitySensorData,
microphoneState,
cpuInfo,
totalMemory,
usageMemory,
totalStorage,
usageStorage,
outerNetInfo,
innerNetInfoWifi,
innerNetInfoEth
)
val diagnosisReportInfo = DiagnosisReportInfo(taskUid, diagnosisFetchInfo, 2)

HttpHelper.getInstance().doPut(HttpConstants.updateDiagnosisPutUrl(), diagnosisReportInfo, object: Callback {
override fun onFailure(call: Call, e: IOException) {
Log.e(DeviceReport.TAG, "updateDiagnosisPutUrl:response onFailure")
}

override fun onResponse(call: Call, response: Response) {
val body = response.body!!.string()
try {
if (response.isSuccessful && isJsonAndGetCode(body) == 0) {
Log.d(DeviceReport.TAG, "updateDiagnosisPutUrl:response $body")
} else {
Log.e(DeviceReport.TAG, "updateDiagnosisPutUrl failed ${response.code}")
}
} catch (e: Exception) {
Log.e(DeviceReport.TAG, e.printStackTrace().toString())
}
}
})
}
})
}
private fun isJsonAndGetCode(str: String): Int {
try {
val jsonStr = JSONObject(str)
if (jsonStr.has("code")) {
Log.d(DeviceReport.TAG, "code = " + jsonStr.getInt("code"))
return jsonStr.getInt("code")
}
} catch (e: Exception) {
e.printStackTrace()
}
return -1
}
}

响应:doPut方法

实现于HttpHelper类。

1
2
3
4
5
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
private static final String TAG = "HttpHelper";
private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
private static final String KEY_HEADER_APP_CODE = "x-app-code";
private static final String KEY_HEADER_APP_VERSION = "x-app-version";
private static final String KEY_HEADER_CONTENT_TYPE = "Content-Type";
private static final String KEY_HEADER_REQ_IP = "x-req-ip";
public static final String KEY_HEADER_AUTH_TOKEN = "x-auth-token";


private Request.Builder addHeaders() {
// todo fix version name to xxx.xxx.xxx
Request.Builder builder = new Request.Builder()
.addHeader("Connection", "keep-alive")
.addHeader(KEY_HEADER_APP_CODE, BuildConfig.DEF_APP_CODE)
.addHeader(KEY_HEADER_APP_VERSION, "3.0.0");
if (mToken != null) {
builder.addHeader(KEY_HEADER_AUTH_TOKEN, mToken);
}
return builder;
}

public int doPut(String url, Object src, Callback callback) {
return localDoPut(url, src, callback);
}

private int localDoPut(String url, Object src, Callback callback) {
String json = new Gson().toJson(src);
Log.d(TAG, json);
RequestBody requestBody = RequestBody.create(MEDIA_TYPE, json);
Request request = addHeaders().addHeader(KEY_HEADER_CONTENT_TYPE, "application/json").url(url).put(requestBody).build();
Call call = mOkHttpClient.newCall(request); // 开始调用请求put
if (callback != null) {
call.enqueue(callback);
}
mId++;
return mId;
}

最终后台收到的json串

1
2
3
4
5
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
38
{
"diagnosisResultBo": {
"colorTemperatureSensorData": "4652.254k",
"cpuInfo": 4.0,
"gravitySensorData": "x:9.122001 y:-0.162 z:0.722",
"innerNetInfoEth": {
"dns": [],
"ip": "",
"mac": "",
"subNetMask": ""
},
"innerNetInfoWifi": {
"dns": [
"10.254.254.254",
"10.21.41.2"
],
"ip": "176.20.209.204",
"mac": "FA:04:1B:4C:E4:99",
"subNetMask": "255.255.240.0"
},
"lastStartTime": 1661398955912,
"lightSensorData": "219.675lux",
"microphoneState": "正常",
"outerNetInfo": {
"ip": "172.20.209.204",
"netOperator": ""
},
"runningTime": 11675621,
"systemTime": 1661410631533,
"totalMemory": 6.1607113,
"totalStorage": 128.0,
"usageMemory": 1.7563075,
"usageStorage": 24.85746,
"version": "20220805_1045_userdebug_V3.0.1"
},
"taskStatus": 2,
"taskUid": "123321"
}