当前位置:   article > 正文

Android 进阶——Framework 核心之dumpsys命令浅析_android dumpsys 查看通知渠道

android dumpsys 查看通知渠道

引言

Dumpsys是安卓系统提供用来动态查看系统内部服务运行状态的命令。了解dumpsys命令的使用方法,可以方便我们查看系统中各个进程内存使用情况,各个进程CPU占用率情况,各个应用apk运行情况,系统唤醒源情况等。同时,了解dumpsys命令使用方法的过程也是我们深入了解安卓各个服务的过程。以后更多精选系列文章以高度完整的系列形式发布在公众号,真正的形成一套完整的技术栈,欢迎关注,目前第一个系列是关于Android系统启动系列的文章,大概有二十几篇干货:
在这里插入图片描述

一、Dumpsys 命令概述

在Android系统中,各个系统服务进程之间主要是通过binder或者socket实现跨进程通信,大部分的服务程序使用binder通信的方式提供API接口,所有使用binder通信方式提供API接口的服务均由一个系统核心的服务管理进程——ServiceManager进程进行同一管理。其中Dumpsys命令就是Android系统提供用来查询ServiceManager中所有服务的状态以及服务内部详细信息的工具,其对应的Dumpsys源码文件——frameworks/native/cmds/dumpsys/dumpsys.cpp,在Android系统编译完成后,dumpsys命令会被打包在system.img中。Dumpsys命令可以理解成Android 独有的shell 程序,使用是 首先adb shell 进入到Android 系统环境,再根据以下发育执行相关命令:

dumpsys [-l][service]
  • 1
命令说明
dumpsys输出设备中所有服务以及服务内部详细信息
dumpsys -l输出设备中所有的服务名称
dumpsys service输出名字为service的服务内部详细信息

二、 Dumpsys命令语法详解

1、Package信息查询

命令格式:

dumpsys package [-h][-f][--checkin][cmd]
  • 1
参数说明
-h输出package子命令帮助信息
-f输出intent filter信息
–checkin输出已经登记的安装包
cmd子命令(可以在-h中查看有哪些子命令)

其中子命令:

cmd子命令说明
prov[iders]获取content providers信息
p[ackages]获取安装包信息
s[hared-user]获取共享用户ID的应用
m[essages]打印运行时收集的信息
v[erifiers]打印包校验信息
version打印数据库版本信息
write写当前位置
<package.name>输出给定包的信息
installs安装会话的详细信息
l[ibraries]列出已知的共享库
f[ibraries]列出手机的功能
k[eysets]列出各个包的Signing KeySets
r[esolvers]获取intent filter

2、Activity信息查询

命令格式:

dumpsys activity [-h][-a][-c][-p]
  • 1
参数说明
-h输出activity子命令帮助信息
-a输出所有可用服务端状态
-c输出客户端状态
-p输出指定包的activity详细信息
cmd子命令(可以在-h中查看有哪些子命令)

子命令:

cmd子命令说明
a[ctivities]activity堆栈状态
r[recents]最近activity的状态
b[rodacasts] [package_name] [history [-s]]广播状态
i[ntents] [package_name]挂起的intent状态
p[rocesses] [package_name]进程状态
o[om]oom管理
perm[issions]URI权限授权状态
prov[iders] [comp_spec…]content provider状态
provider [comp_spec]provider客户端状态
s[ervices] [comp_spec…]服务状态
as[sociations]跟踪应用程序的关联
service [comp_spec]服务客户端状态
package [package_name]指定包的所有状态
alldump所有activities信息

3、网络信息查询

安卓系统网络连接和管理服务由四个系统服务ConnectivityService,NetworkPolicyManagerService,NetworkManagementService,NetworkStatsService共同配合完成网络连接和管理功能。因此,要查看设备网络相关信息,就需要使用dumpsys命令分别查看设备中这些服务的详细信息:

命令说明
dumpsys connectivity查看设备当前网络连接状态
dumpsys netpolicy查看设备网络策略
dumpsys netstats查看设备网络状态
dumpsys network_management查看设备网络管理服务信息

4、其他常用服务信息查询

命令说明
dumpsys meminfo查看系统各个进程(某一个进程)内存使用情况
dumpsys cpuinfo查看系统各个进程CPU占用情况
dumpsys power查看系统应用申请wakelock情况
dumpsys batterstats查看系统各个应用(进程)耗电情况
dumpsys alarm查看系统RTC定时器使用情况
dumpsys wifi查看wifi网络使用情况
dumpsys media.player查看系统native播放器以及recorder创建以及使用情况

三、Dumpsys命令实现原理

1、安卓binder服务管理

Dumpsys基于安卓的binder通信机制实现,因此了解安卓binder通信机制是基础。安卓binder通信框架如图1所示(Binder通信机制详细内容请参考[1]&[2])。它由客户端程序,服务端程序,ServiceManager进程,binder驱动四大部分组成。Binder即可作为进程间通信也可以作为进程内通信,它的优势在于客户端与服务端通信只需要一次数据拷贝。
在这里插入图片描述

如上图所示Binder通信是一个C/S架构,它们通过kernel的binder driver建立通信桥梁。ServiceManager在此框架中的作用相当于一个“路由器”,所有的服务通过addService接口把服务的“句柄”和名字注册到ServiceManager中,应用程序(客户端)调用服务接口时,首先使用ServiceManager的checkService/getService接口通过服务名字获取该服务的“句柄”,然后通过服务“句柄”调用相应的服务接口(BpBinder是client端创建的用于消息发送的代理,而BBinder是server端用于接收消息的通道)。其交互过程如图2所示服务“句柄”管理机制:

在这里插入图片描述

2、Dumpsys源码分析

Dumpsys命令在源码位于:frameworks/native/cmds/dumpsys/dumpsys.cpp。

int main(int argc, char* const argv[])
{
	//捕捉信号
    signal(SIGPIPE, SIG_IGN);
	//获取 ServiceManager服务的binder代理对象
    sp<IServiceManager> sm = defaultServiceManager();
    fflush(stdout);
    if (sm == NULL) {
        aerr << "dumpsys: Unable to get default service manager!" << endl;
        return 20;
    }
    Vector<String16> services;
    Vector<String16> args;
    Vector<String16> skippedServices;
    bool showListOnly = false;
    bool skipServices = false;
    int timeoutArg = 10;
    static struct option longOptions[] = {
        {"skip", no_argument, 0,  0 },
        {"help", no_argument, 0,  0 },
        {     0,           0, 0,  0 }
    };

    while (1) {
        int c;
        int optionIndex = 0;
        //传入的参数转为case 的值
        c = getopt_long(argc, argv, "+t:l", longOptions, &optionIndex);

        if (c == -1) {
            break;
        }

        switch (c) {
        case 0:
            if (!strcmp(longOptions[optionIndex].name, "skip")) {
                skipServices = true;
            } else if (!strcmp(longOptions[optionIndex].name, "help")) {
                usage();
                return 0;
            }
            break;

        case 't':
            {
                char *endptr;
                timeoutArg = strtol(optarg, &endptr, 10);
                if (*endptr != '\0' || timeoutArg <= 0) {
                    fprintf(stderr, "Error: invalid timeout number: '%s'\n", optarg);
                    return -1;
                }
            }
            break;

        case 'l':
            showListOnly = true;
            break;

        default:
            fprintf(stderr, "\n");
            usage();
            return -1;
        }
    }

    for (int i = optind; i < argc; i++) {
        if (skipServices) {
            skippedServices.add(String16(argv[i]));
        } else {
            if (i == optind) {
                services.add(String16(argv[i]));
            } else {
                args.add(String16(argv[i]));
            }
        }
    }

    if ((skipServices && skippedServices.empty()) ||
            (showListOnly && (!services.empty() || !skippedServices.empty()))) {
        usage();
        return -1;
    }

    if (services.empty() || showListOnly) {
        // gets all services 通过ServiceManager获取所有的服务
        services = sm->listServices();
        services.sort(sort_func);
        args.add(String16("-a"));
    }

    const size_t N = services.size();

    if (N > 1) {
        // first print a list of the current services
        aout << "Currently running services:" << endl;

        for (size_t i=0; i<N; i++) {
            sp<IBinder> service = sm->checkService(services[i]);
            if (service != NULL) {
                bool skipped = IsSkipped(skippedServices, services[i]);
                aout << "  " << services[i] << (skipped ? " (skipped)" : "") << endl;
            }
        }
    }

    if (showListOnly) {
        return 0;
    }

    for (size_t i = 0; i < N; i++) {
        String16 service_name = std::move(services[i]);
        if (IsSkipped(skippedServices, service_name)) continue;

        sp<IBinder> service = sm->checkService(service_name);
        if (service != NULL) {
            int sfd[2];

            if (pipe(sfd) != 0) {
                aerr << "Failed to create pipe to dump service info for " << service_name
                     << ": " << strerror(errno) << endl;
                continue;
            }

            unique_fd local_end(sfd[0]);
            unique_fd remote_end(sfd[1]);
            sfd[0] = sfd[1] = -1;

            if (N > 1) {
                aout << "-------------" << endl;
                aout << "DUMP OF SERVICE " << service_name << ":" << endl;
            }

            // dump blocks until completion, so spawn a thread..
            std::thread dump_thread([=, remote_end { std::move(remote_end) }]() mutable {
				//调用各自服务里的dump函数
                int err = service->dump(remote_end.get(), args);

                // It'd be nice to be able to close the remote end of the socketpair before the dump
                // call returns, to terminate our reads if the other end closes their copy of the
                // file descriptor, but then hangs for some reason. There doesn't seem to be a good
                // way to do this, though.
                remote_end.clear();

                if (err != 0) {
                    aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
                         << endl;
                }
            });

            auto timeout = std::chrono::seconds(timeoutArg);
            auto start = std::chrono::steady_clock::now();
            auto end = start + timeout;

            struct pollfd pfd = {
                .fd = local_end.get(),
                .events = POLLIN
            };

            bool timed_out = false;
            bool error = false;
            while (true) {
                // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
                auto time_left_ms = [end]() {
                    auto now = std::chrono::steady_clock::now();
                    auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
                    return std::max(diff.count(), 0ll);
                };

                int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
                if (rc < 0) {
                    aerr << "Error in poll while dumping service " << service_name << " : "
                         << strerror(errno) << endl;
                    error = true;
                    break;
                } else if (rc == 0) {
                    timed_out = true;
                    break;
                }

                char buf[4096];
                rc = TEMP_FAILURE_RETRY(read(local_end.get(), buf, sizeof(buf)));
                if (rc < 0) {
                    aerr << "Failed to read while dumping service " << service_name << ": "
                         << strerror(errno) << endl;
                    error = true;
                    break;
                } else if (rc == 0) {
                    // EOF.
                    break;
                }

                if (!WriteFully(STDOUT_FILENO, buf, rc)) {
                    aerr << "Failed to write while dumping service " << service_name << ": "
                         << strerror(errno) << endl;
                    error = true;
                    break;
                }
            }

            if (timed_out) {
                aout << endl << "*** SERVICE DUMP TIMEOUT EXPIRED ***" << endl << endl;
            }
            if (timed_out || error) {
                dump_thread.detach();
            } else {
                dump_thread.join();
            }
            if (N > 1) {
              std::chrono::duration<double> elapsed_seconds =
                  std::chrono::steady_clock::now() - start;
              aout << StringPrintf("--------- %.3fs ", elapsed_seconds.count()).c_str()
                   << "was the duration of dumpsys " << service_name << endl;
            }
        } else {
            aerr << "Can't find service: " << service_name << endl;
        }
    }
    return 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
  • 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
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219

从上面源码中可知

  • sp< IServiceManager > sm = defaultServiceManager(); 获取ServiceManager Binder代理对象
  • services = sm->listServices(); 通过ServiceManager获取所有的服务名称
  • sp< IBinder > service = sm->checkService(service_name); 通过服务名称获取对应服务的Binder代理对象
  • 通过服务的Binder代理对象调用各自的dump 函数

dumpsys调用ServiceManager服务的listServices来查询系统中的所有服务的名称,并且通过checkService方法获取对应服务的binder代理对象,然后通过每个服务binder代理对象调用dump函数来输出该服务的详细信息,因此dumpsys命令输出各个服务的详细信息是没有统一格式的,另外,dump方法提供一个用户输入参数的通道,利用此特性,可以动态修改服务中的某些参数。例如,安卓系统中native层的camera服务,其服务内部的调试打印信息由一个全局变量gLogLevel控制(源码:frameworks/av/services/camera/libcameraservice/CameraService.cpp)。在dump方法中提供了设置gLogLevel的接口:

status_t CameraService::dump(int fd, const Vector<String16>& args) {
  //输出CameraService内部详细状态信息
  int n = args.size();
  for (int i = 0; i + 1 < n; i++) {
   //-v 参数用于修改gLogLevel变量
    String16 verboseOption("-v");
    if (args[i] == verboseOption) {
      String8 levelStr(args[i+1]);
      int level = atoi(levelStr.string());
      result = String8::format("\nSetting log level to %d.\n", level);
      setLogLevel(level);
      write(fd, result.string(), result.size());
    }
}
	return NO_ERROR;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/537542?site
推荐阅读
相关标签
  

闽ICP备14008679号