Android system_server进程

打印 上一主题 下一主题

主题 865|帖子 865|积分 2595

目录
一、system_server进程先容
二、system_server进程启动流程
2.1 startBootstrapServices
2.2 startCoreServices
2.3 startOtherServices
2.4 startApexServices
三、怎样利用体系服务
3.1 app进程调用体系服务
3.2 native进程调用体系服务
3.3 system_server进程中两个服务之间的调用
四、怎样添加一个体系服务
五、怎样裁剪体系服务
六、别的模块
6.1 NativeCrashListener
6.2 Android watchdog




一、system_server进程先容

Android system_server进程由zygote进程fork创建,是framework层的焦点进程,主要由ASM、PMS等体系服务组成,给app提供Binder调用、与native & hal进程通讯。
二、system_server进程启动流程

system_server进程启动入口:
  1. // frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
  2. public static void main(String[] argv) {
  3.     ...
  4.     if (startSystemServer) {
  5.             Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
  6.     }
  7.     ...
  8. }
  9. private static Runnable forkSystemServer(String abiList, String socketName,
  10.             ZygoteServer zygoteServer) {
  11.     ...
  12.      pid = Zygote.forkSystemServer(
  13.                     parsedArgs.mUid, parsedArgs.mGid,
  14.                     parsedArgs.mGids,
  15.                     parsedArgs.mRuntimeFlags,
  16.                     null,
  17.                     parsedArgs.mPermittedCapabilities,
  18.                     parsedArgs.mEffectiveCapabilities);
  19.     ...
  20. }
  21. // frameworks/base/core/java/com/android/internal/os/Zygote.java
  22. static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
  23.             int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
  24.         ZygoteHooks.preFork();
  25.         int pid = nativeForkSystemServer(
  26.                         uid, gid, gids, runtimeFlags, rlimits,
  27.                         permittedCapabilities, effectiveCapabilities);
  28.         // Set the Java Language thread priority to the default value for new apps.
  29.         Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
  30.         ZygoteHooks.postForkCommon();
  31.         return pid;
  32. }
  33. // frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
  34. static jint com_android_internal_os_Zygote_nativeForkSystemServer(
  35.         JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
  36.         jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
  37.         jlong effective_capabilities) {
  38.         ...
  39.   // fork出system_server子进程
  40.   pid_t pid = zygote::ForkCommon(env, true, fds_to_close, fds_to_ignore, true);
  41.   // 通过JNI调用SystemServer类的main方法
  42.   if (pid == 0) {
  43.       SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities,
  44.                        effective_capabilities, MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
  45.                        false, nullptr, nullptr, /* is_top_app= */ false,
  46.                        /* pkg_data_info_list */ nullptr,
  47.                        /* allowlisted_data_info_list */ nullptr, false, false);
  48.   } else if (pid > 0) {
  49.       ...
  50.   }
  51.   return pid;
  52. }
复制代码
通过JNI调用SystemServer类的main方法:
  1. // frameworks/base/services/java/com/android/server/SystemServer.java
  2. public static void main(String[] args) {
  3.         ...
  4.         new SystemServer().run();
  5. }
  6. private void run() {
  7.     ...
  8.     // Start services.
  9.     startBootstrapServices(t);
  10.     startCoreServices(t);
  11.     startOtherServices(t);
  12.     startApexServices(t);
  13.     updateWatchdogTimeout(t);     
  14.     ...
  15. }
复制代码
2.1 startBootstrapServices

启动体系最重要的服务,执行startService和startBootPhase方法,如下:
  1. // frameworks/base/services/java/com/android/server/SystemServer.java
  2. private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
  3.      // 启动watchdog线程
  4.     final Watchdog watchdog = Watchdog.getInstance();
  5.     watchdog.start();
  6.     ...
  7.     // 启动AMS
  8.     ActivityTaskManagerService atm = mSystemServiceManager.startService(
  9.                     ActivityTaskManagerService.Lifecycle.class).getService();
  10.     mActivityManagerService = ActivityManagerService.Lifecycle.startService(
  11.                     mSystemServiceManager, atm);
  12.     mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
  13.     mActivityManagerService.setInstaller(installer);
  14.     mWindowManagerGlobalLock = atm.getGlobalLock();
  15.     // 启动PMS
  16.      mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
  17.     // 启动ThermalManagerService
  18.     mSystemServiceManager.startService(ThermalManagerService.class);
  19.     ...
  20.     mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
  21. }
复制代码
startService:通过反射的方式调用服务的onStart方法,
  1. // frameworks/base/services/core/java/com/android/server/SystemServiceManager.java
  2. public <T extends SystemService> T startService(Class<T> serviceClass) {
  3.         final String name = serviceClass.getName();
  4.         // Create the service.
  5.         if (!SystemService.class.isAssignableFrom(serviceClass)) {
  6.                 throw new RuntimeException("Failed to create " + name
  7.                                 + ": service must extend " + SystemService.class.getName());
  8.         }
  9.         final T service;
  10.         try {
  11.                 Constructor<T> constructor = serviceClass.getConstructor(Context.class);
  12.                 service = constructor.newInstance(mContext);
  13.         }
  14.         ...
  15.         startService(service);
  16.         return service;
  17. }
  18. public void startService(@NonNull final SystemService service) {
  19.         // Check if already started
  20.         String className = service.getClass().getName();
  21.         if (mServiceClassnames.contains(className)) {
  22.                 Slog.i(TAG, "Not starting an already started service " + className);
  23.                 return;
  24.         }
  25.         mServiceClassnames.add(className);
  26.         // Register it.
  27.         mServices.add(service);
  28.         // Start it.
  29.         long time = SystemClock.elapsedRealtime();
  30.         try {
  31.                 service.onStart();
  32.         }
  33. }
复制代码
PMS服务执行onStart和startBootPhase方法:
  1. public void onStart() {
  2.         publishBinderService(Context.POWER_SERVICE, mBinderService, /* allowIsolated= */ false,
  3.                         DUMP_FLAG_PRIORITY_DEFAULT | DUMP_FLAG_PRIORITY_CRITICAL);
  4.         publishLocalService(PowerManagerInternal.class, mLocalService);
  5.         Watchdog.getInstance().addMonitor(this);
  6.         Watchdog.getInstance().addThread(mHandler);
  7. }
  8. @Override
  9. public void onBootPhase(int phase) {
  10.         if (phase == PHASE_SYSTEM_SERVICES_READY) {
  11.                 systemReady();
  12.         } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
  13.                 incrementBootCount();
  14.         } else if (phase == PHASE_BOOT_COMPLETED) {
  15.                 synchronized (mLock) {
  16.                  ...
  17.         }
  18.     ...
  19. }
复制代码

服务名称
作用
1
ArtModuleServiceInitializer
初始化 ART 模块服务管理器
2
Watchdog
体系守护线程,监听体系服务是否发生死锁
3
platformCompat
平台兼容性服务,为其他服务提供兼容性支持
4
FileIntegrityService
文件完整性服务,相应应用和体系的完整性请求
5
Installer
安装器服务,等待 Installd 启动完成
6
DeviceIdentifiersPolicyService
设备标识符策略服务,注册在启动应用程序管理器之前
7
UriGrantsManagerService
URI 授权管理服务
8
PowerStatsService
电源统计数据跟踪服务
9
MemtrackProxyService
内存跟踪署理服务,早于 ActivityManagerService 启动
10
AccessCheckingService
访问检查服务,提供新的权限和应用操纵实现
11
ActivityManagerService
体系焦点服务,管理活动和使命
12
DataLoaderManagerService
数据加载器管理服务
13
IncrementalService
增量安装服务
14
PowerManagerService
电源管理服务
15
ThermalManagerService
热管理服务
16
HintManagerService
提示管理服务
17
RecoverySystemService
设备进入Recovery模式时提供各种功能和操纵
18
LightsService
LED 和显示背光管理服务
19
DisplayOffloadService
显示卸载服务
20
SidekickService
SidekickService服务通常与其他应用程序一起工作,为用户提供额外的功能和支持
21
DisplayManagerService
显示管理器服务
22
DomainVerificationService
selinux权限验证服务
23
PackageManagerService
包管理器服务,管理应用程序包和应用程序
24
DexUseManagerLocal
当地 Dex 利用管理器
25
OtaDexOptService
A/B OTA dexopt 管理服务
26
UserManagerService
用户管理服务
27
OverlayManagerService
用于管理和处理窗口覆盖(Overlay)相干的操纵
28
ResourcesManagerService
资源管理器服务
29
SensorPrivacyService
传感器隐私管理服务
30
SensorService
传感器服务
2.2 startCoreServices

启动体系焦点服务,如下:
  1. private void startCoreServices(@NonNull TimingsTraceAndSlog t) {
  2.         ...
  3.         mSystemServiceManager.startService(SystemConfigService.class);
  4.         mSystemServiceManager.startService(BatteryService.class);
  5.         mSystemServiceManager.startService(UsageStatsService.class);
  6.         mActivityManagerService.setUsageStatsManager(
  7.                 LocalServices.getService(UsageStatsManagerInternal.class));
  8.         mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
  9.         mSystemServiceManager.startService(CachedDeviceStateService.class);
  10.         mSystemServiceManager.startService(BinderCallsStatsService.LifeCycle.class);
  11.         mSystemServiceManager.startService(LooperStatsService.Lifecycle.class);
  12.         mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS);
  13.         mSystemServiceManager.startService(NativeTombstoneManagerService.class);
  14.         mSystemServiceManager.startService(BugreportManagerService.class);
  15.         mSystemServiceManager.startService(GpuService.class);
  16.         mSystemServiceManager.startService(RemoteProvisioningService.class);
  17.         mSystemServiceManager.startService(CpuMonitorService.class);
  18.     }
复制代码

服务名称
作用
1
SystemConfigService
用于管理和提供体系级别的设置信息。SystemConfigService服务为应用程序和体系组件提供了一种访问体系设置信息的方式
2
BatteryService
用于管理和提供电池相干的信息和功能。BatteryService服务为应用程序和体系组件提供了一种访问电池状态和控制电池利用的方式
3
WebViewUpdateService
用于管理和提供WebView组件的更新信息。WebView是Android体系中的一个内置组件,用于在应用程序中显示和处理网页内容
4
CachedDeviceStateService
用于缓存和提供设备状态信息。这个服务会在设备启动时创建,并在整个体系运行过程中提供设备状态信息
5
BinderCallsStatsService
为体系提供了一种网络和分析Binder通讯性能数据的服务
6
LooperStatsService
为体系提供了一种网络和分析Looper线程性能数据的服务
7
RollbackManagerService
为体系提供了一种管理和实现体系回滚功能的方式,以便在出现题目时快速恢复体系状态
8
NativeTombstoneManagerService
为体系提供了一种管理和网络Native堆栈Tombstone信息,以便在进程发生native crash时快速定位题目
9
BugreportManagerService
为体系提供了一种管理和实现Bugreport信息,以便在需要时快速网络体系和程序状态信息
10
GpuService
为体系提供了一种访问GPU信息和控制GPU利用的服务
11
RemoteProvisioningService
为体系提供了一种管理和实现远程设备设置功能,以便在需要时远程设置设备
12
CpuMonitorService
用于管理和提供CPU相干的信息和功能
2.3 startOtherServices

启动体系别的的服务,如下:
  1. private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
  2.     ...
  3.     mSystemServiceManager.startService(DropBoxManagerService.class);
  4.     mSystemServiceManager.startService(ALARM_MANAGER_SERVICE_CLASS);
  5.     mSystemServiceManager.startService(CameraServiceProxy.class);
  6.     WindowManagerService.main(context, inputManager, !mFirstBoot,
  7.                             pwm, mActivityManagerService.mActivityTaskManager);
  8.     // 这类系统服务可以做裁剪               
  9.     if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
  10.                 t.traceBegin("StartPrintManager");
  11.                 mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
  12.                 t.traceEnd();
  13.             }
  14.     }
  15.     if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP)) {
  16.             t.traceBegin("StartCompanionDeviceManager");
  17.             mSystemServiceManager.startService(COMPANION_DEVICE_MANAGER_SERVICE_CLASS);
  18.             t.traceEnd();
  19.    
  20.             // VirtualDeviceManager depends on CDM to control the associations.
  21.             t.traceBegin("StartVirtualDeviceManager");
  22.             mSystemServiceManager.startService(VIRTUAL_DEVICE_MANAGER_SERVICE_CLASS);
  23.             t.traceEnd();
  24.     }
  25.    
  26.     ...
  27.     // 这里会启动launcher进程
  28.     mActivityManagerService.systemReady(() -> { ...}
  29.     ...
  30.     // 启动systemui
  31.     startSystemUi(context, windowManagerF);
  32.     ...
  33. }
复制代码

服务名称
作用
1
KeyChainSystemService
用于管理和提供密钥链(KeyChain)相干的信息和功能。密钥链是一种在Android体系中存储和管理敏感信息(如暗码、密钥等)的机制,通常用于实现设备安全、数据加密等场景。
2
DropBoxManagerService
app进程发生ANR、native crash时,网络相干debug信息
3
ContentService
为体系提供了一种管理和实现ContentProvider功能的方式,以便在需要时实现数据共享和访问
4
AlarmManagerService
定时使命
5
InputManagerService
管理和提供输入设备相干的信息和功能
6
DeviceStateManagerService
用于管理设备的状态信息
7
WindowManagerService
负责管理设备上的窗口和界面。它为应用程序提供了一个同一的接口,用于创建、显示、隐藏、移动、调整巨细等操纵窗口。
8
BluetoothService
理蓝牙设备的毗连和通讯。它提供了一系列API,允许应用程序通过蓝牙进行数据互换、文件传输、音频流传输等操纵。
9
StorageManagerService
负责管理设备的存储空间。它为应用程序提供了一个同一的接口,用于访问设备的内部存储(如闪存)和外部存储(如SD卡)。
10
OemLockService
管理设备的OEM锁定状态
11
DeviceIdleController
管理设备的空闲状态。当设备处于空闲状态时,DeviceIdleController可以自动执行一些节能措施,如低落屏幕亮度、关闭无线网络等,以延长设备的电池寿命。
12
DevicePolicyManagerService
管理设备的策略和安全设置。
13
StatusBarManagerService
管理设备的状态栏。它提供了一系列API,允许应用程序在状态栏上显示通知、图标、进度条等信息。
14
NetworkManagementService
管理设备的网络毗连。它提供了一系列API,允许应用程序获取设备的网络状态、毗连类型、IP地址等信息,并可以控制设备的网络毗连。
15
NetworkStatsService
统计设备的网络利用情况。它提供了一系列API,允许应用程序获取设备的网络流量统计信息,如总流量、各个应用程序的流量等。
16
NetworkPolicyManagerService
管理设备的网络策略。它提供了一系列API,允许设备管理员对设备的网络毗连进行策略管理,如限定应用程序的网络访问、设置网络优先级等。
17
SystemUpdateManagerService
管理设备的体系更新。它提供了一系列API,允许对设备的体系更新进行管理,如检查体系更新、下载体系更新、安装体系更新等。
18
NotificationManagerService
管理设备的通知。它提供了一系列API,允许应用程序在通知栏上显示通知,如消息通知、提示通知等。
19
DeviceStorageMonitorService
监控设备的存储空间。它提供了一系列API,允许应用程序获取设备的存储空间信息,如总容量、已用容量、可用容量等。
20
AudioService
管理设备的音频。允许应用程序控制设备的音频输出,如播放音乐、播放铃声等。
21
BroadcastRadioService
管理设备的广播电台。允许应用程序吸收广播电台的节目,如消息、音乐等。
22
AdbService
管理设备的ADB毗连。允许开发人员通过ADB毗连设备,进行调试和测试。
23
UsbService
管理设备的USB毗连。允许应用程序通过USB毗连设备,进行数据传输和通讯
24
GestureLauncher
用于识别和相应用户的手势操纵。允许应用程序检测和相应用户的手势操纵,如滑动、捏合、旋转等。
25
SensorNotificationService
管理设备的传感器通知。允许应用程序获取设备的传感器信息,如加速率计、陀螺仪等
26
DiskStatsService
管理设备的磁盘状态。允许应用程序获取设备的磁盘状态信息,如分区巨细、利用情况等。
27
RuntimeService
管理设备的运行时环境。允许应用程序获取设备的运行时信息,如内存利用情况、CPU利用情况等。
28
GraphicsStatsService
管理设备的图形性能
29
FaceSensorService
管理设备的面部识别功能
30
HealthService
用于管理设备的康健状态。允许应用程序获取设备的康健状态信息,如电池电量、温度等。
31
SdkSandboxManagerService
管理设备的SDK沙盒环境。允许应用程序获取设备的SDK沙盒信息,如应用程序的权限、资源等。
32
PermissionPolicyService
管理设备的权限策略
33
LockSettingsService
管理设备的锁定设置,如屏幕锁定方式、暗码等。
34
LauncherAppsService
管理设备的启动器应用程序。允许应用程序获取设备的启动器应用程序信息,如启动器应用程序的名称、图标等。
35
AppServiceManager
管理设备的应用程序服务
36
GameManagerService
用于管理游戏的运行和状态
37
LocationManagerService
用于管理设备的位置信息,允许应用程序获取设备的位置信息,如经纬度、海拔等
38
FingerprintSensor
管理设备的指纹识别功能
39
IoTSystemService
理设备的物联网功能
40
SafetyCenterService
理设备的安全功能。允许应用程序获取设备的安全信息,如病毒扫描、防火墙等

别的体系服务:
KeyAttestationApplicationIdProviderService、BinaryTransparencyService、SchedulingPolicyService、TelecomLoaderService、TelephonyRegistry、EntropyMixer、AccountManagerService、InstallSystemProviders、
RoleManagerService、VibratorManagerService、DynamicSystemService、ConsumerIrService、ResourceEconomy、
CameraServiceProxy、VrManagerService、IpConnectivityMetrics、NetworkWatchlistService、PinnerService、
ProfcollectForwardingService、SignedConfigService、AppIntegrityService、LogcatManagerService、
InputMethodManagerService、AccessibilityManagerService、UiModeManagerService、LocaleManagerService、
GrammaticalInflectionService、AppHibernationService、LockSettingsService、PersistentDataBlockService、
MusicRecognitionManagerService、SpeechRecognitionManagerService、AppPredictionService、
ContentSuggestionsService、SearchUiService、SmartspaceService、FontManagerService、TextServicesManager、
TextClassificationManagerService、NetworkScoreService、RttService、WifiAwareService、WifiP2PService、
LowpanService、PacProxyService、ConnectivityService、MiCarNetService、VpnManagerService、VcnManagementService、UpdateLockService、TimeDetectorService、CountryDetectorService、
TimeZoneDetectorService、AltitudeService、LocationTimeZoneManagerService、GnssTimeUpdateService、
SearchManagerService、WallpaperManagerService、WallpaperEffectsGenerationService、SoundTriggerMiddlewareService、DockObserver、ThermalObserver、WiredAccessoryManagerService、
MidiManagerService、SerialService、HardwarePropertiesManagerService、TwilightService、ColorDisplayService、
SoundTriggerService、TrustManagerService、BackupManager、AppWidgetService、VoiceRecognitionManager、
ContextHubSystemService、NetworkTimeUpdateService、EmergencyAffordanceService、DreamManagerService、
CoverageService、PrintManager、AttestationVerificationService、CompanionDeviceManager、VirtualDeviceManager、RestrictionManager、MediaSessionService、HdmiControlService、TvInteractiveAppManager、TvInputManager、TunerResourceManager、MediaResourceMonitor、
TvRemoteService、MediaRouterService、IrisSensor、FingerprintSensor、BiometricService、
AuthService、DynamicCodeLoggingService、PruneInstantAppsJobService、ShortcutService、
CrossProfileAppsService、PeopleService、MediaMetricsManager、BackgroundInstallControlService、
MediaProjectionManager、WearPowerService、WearConnectivityService、WearDisplayService、
WearTimeService、WearGlobalActionsService、SliceManagerService、
StatsCompanion、StatsPullAtomService、BootstrapAtomService、IncidentCompanionService、
AdServicesManagerService、OnDevicePersonalizationSystemService、MmsService、AutoFillService、
CredentialManagerService、TranslationManagerService、SelectionToolbarManagerService、ClipboardService、
TracingServiceProxy、DeviceSpecificServices、MediaCommunicationService、
AppCompatOverridesService、HealthConnectManagerService等
2.4 startApexServices

启动体系apex服务,如下:
  1. private void startApexServices(@NonNull TimingsTraceAndSlog t) {
  2.     List<ApexSystemServiceInfo> services = ApexManager.getInstance().getApexSystemServices();
  3.     for (ApexSystemServiceInfo info : services) {
  4.             String name = info.getName();
  5.             String jarPath = info.getJarPath();
  6.             t.traceBegin("starting " + name);
  7.             if (TextUtils.isEmpty(jarPath)) {
  8.                     mSystemServiceManager.startService(name);
  9.             } else {
  10.                     mSystemServiceManager.startServiceFromJar(name, jarPath);
  11.             }
  12.             t.traceEnd();
  13.     }
  14. }
复制代码
三、怎样利用体系服务

3.1 app进程调用体系服务

以PMS服务为例,app进程通过获取PMS服务署理---PowerManager对象,调用PowerManager中的接口:
  1. // 获取PowerManager实例
  2. PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
  3. // 调用PowerManager类中的接口
  4. PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLockTag");
复制代码
3.2 native进程调用体系服务

通过binder对parcel数据通讯:
  1. #include <binder/IServiceManager.h>
  2. #include <binder/Parcel.h>
  3. sp<IServiceManager> sm = defaultServiceManager();
  4. sp<IBinder> binder = sm->getService(String16("power"));
  5. if (!binder) {
  6.     return EXIT_FAILURE;
  7. }
  8. Parcel data, result;
  9. data.writeInterfaceToken(String16("android.os.IPowerManager"));
  10. char d[] = {0x00};
  11. data.write(d, sizeof(d));
  12. // 6表示对应的方法ID
  13. binder->transact(6, data, &result);
复制代码
大概直接调用binder接口:
  1. #include <binder/IActivityManager.h>
  2. #include <binder/IBinder.h>
  3. #include <binder/IServiceManager.h>
  4. int openContentProviderFile(const String16& uri)
  5. {
  6.     int fd = -1;
  7.     sp<IServiceManager> sm = defaultServiceManager();
  8.     sp<IBinder> binder = sm->getService(String16("activity"));
  9.     sp<IActivityManager> am = interface_cast<IActivityManager>(binder);
  10.     if (am != NULL) {
  11.         fd = am->openContentUri(uri);
  12.     }
  13.     return fd;
  14. }
复制代码
3.3 system_server进程中两个服务之间的调用

system_server进程中的服务间调用属于进程内部通讯。如PMS服务启动时会将内部的mLocalService对象注册到当地service管理中,对外提供一个PowerManagerInternal类及接口,给别的服务调用,如下:
  1. // 其它服务中通过获取PowerManagerInternal对象,调用接口
  2. private PackageManagerInternal mPackageManagerInternal;
  3. mLocalPowerManager = getLocalService(PowerManagerInternal.class);
  4. mLocalPowerManager.getLowPowerState(
  5.                                 ServiceType.QUICK_DOZE).batterySaverEnabled);
  6.                                 
  7. // frameworks/base/core/java/android/os/PowerManagerInternal.java
  8. // 抽象方法,在具体实现类中实现
  9. public abstract PowerSaveState getLowPowerState(int serviceType);
  10.                                 
  11. // frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
  12. public final class PowerManagerService extends SystemService
  13.         implements Watchdog.Monitor {
  14.         ...
  15.         @Override
  16.         public void onStart() {
  17.             // 注册到servicemanager中,用于进程间通信
  18.             publishBinderService(Context.POWER_SERVICE, mBinderService, /* allowIsolated= */ false,
  19.                             DUMP_FLAG_PRIORITY_DEFAULT | DUMP_FLAG_PRIORITY_CRITICAL);
  20.             // 加入到本地service中,用于进程内部调用         
  21.             publishLocalService(PowerManagerInternal.class, mLocalService);
  22.             ...
  23.         }
  24.         // 内部类继承PowerManagerInternal,并实现父类抽象方法
  25.         final class LocalService extends PowerManagerInternal {
  26.                 ...
  27.                 @Override
  28.                 public PowerSaveState getLowPowerState(@ServiceType int serviceType) {
  29.                         return mBatterySaverPolicy.getBatterySaverPolicy(serviceType);
  30.                 }
  31.                 ...
  32.         }
  33.         ...
  34. }
复制代码
四、怎样添加一个体系服务

添加步调:
1)准备体系服务代码DemoSystemService.java和Android.bp文件
  1. // 继承SystemService,实现Watchdog.Monitor接口
  2. public final class DemoManagerService extends SystemService
  3.         implements Watchdog.Monitor {
  4.     // 服务名称为 "demo"
  5.     String SERVICE_NAME = "demo";
  6.         ...
  7.     // 重写onStart和onBootPhase方法
  8.     @Override
  9.     public void onStart() {
  10.         // 发布服务
  11.         publishBinderService(SERVICE_NAME, mBinderService, /* allowIsolated= */ false,
  12.                 DUMP_FLAG_PRIORITY_DEFAULT | DUMP_FLAG_PRIORITY_CRITICAL);
  13.         // 用于监控DemoManagerService是否发生死锁
  14.         Watchdog.getInstance().addMonitor(this);
  15.         Watchdog.getInstance().addThread(mHandler);
  16.     }
  17.         
  18.          @Override
  19.     public void onBootPhase(int phase) {
  20.             ...
  21.     }
  22.         ...
  23. }
复制代码
  1. filegroup {
  2.     name: "demo--system-service-sources",
  3.     srcs: ["DemoSystemService.java"],
  4. }
复制代码
2)将services.jar模块编译依赖新增模块
修改frameworks/base/services/Android.bp,如下:
  1. java_library {
  2.     name: "services",
  3.     ...
  4.     srcs: [":services-main-sources",
  5.        ":demo--system-service-sources"
  6.     ],
  7.     ...
  8. }
复制代码
3)SystemServer.java中启动Demo服务
修改frameworks/base/services/java/com/android/server/SystemServer.java, 启动DemoSystemService,如下:
  1. public final class SystemServer implements Dumpable {
  2.     ...
  3.     private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
  4.         t.traceBegin("StartDemoSystemService");
  5.         mSystemServiceManager.startService(DemoSystemService.class);
  6.         t.traceEnd();
  7.     }
  8.     ...
  9. }
复制代码
4)添加selinux规则
修改system/sepolicy库,添加demo服务的标签和规则,留意添加的服务名称为demo,如下:
  1. // system/sepolicy/private/compat/32.0/32.0.ignore.cil
  2. vendor_vm_file
  3. virtual_device_service
  4. wallpaper_effects_generation_service
  5. demo_service
  6. // system/sepolicy/prebuilts/api/32.0/private/service_contexts
  7. demo                   u:object_r:demo_service:s0
  8. // system/sepolicy/prebuilts/api/32.0/public/service.te
  9. type demo_service, system_server_service, service_manager_type;
复制代码
五、怎样裁剪体系服务

所谓的体系服务裁剪,就是不让服务启动且别的获取该服务的地方有判空,避免运行时空指针报错。以printmanagerservice服务为例:
  1. // packagemanager
  2. public static final String FEATURE_PRINTING = "android.software.print";
  3. private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
  4.    
  5.     // 这类系统服务可以做裁剪,只需让hasSystemFeature方法返回false              
  6.     if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
  7.                 t.traceBegin("StartPrintManager");
  8.                 mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
  9.                 t.traceEnd();
  10.             }
  11.     }
  12.     ...
  13. }
复制代码
hasSystemFeature实现:
  1. // 通过mAvailableFeatures得到所有的feature
  2. public boolean hasSystemFeature(String name, int version) {
  3.     // allow instant applications
  4.     synchronized (mAvailableFeatures) {
  5.         final FeatureInfo feat = mAvailableFeatures.get(name);
  6.         if (feat == null) {
  7.                 return false;
  8.         } else {
  9.                 return feat.version >= version;
  10.         }
  11.     }
  12. }
  13. // 该方法维护mAvailableFeatures列表
  14. private void addFeature(String name, int version) {
  15.         FeatureInfo fi = mAvailableFeatures.get(name);
  16.         if (fi == null) {
  17.                 fi = new FeatureInfo();
  18.                 fi.name = name;
  19.                 fi.version = version;
  20.                 mAvailableFeatures.put(name, fi);
  21.         } else {
  22.                 fi.version = Math.max(fi.version, version);
  23.         }
  24. }
  25. // 系统启动时会去读取handheld_core_hardware.xml配置文件,存到mAvailableFeatures列表
  26. SystemConfig() {
  27.         TimingsTraceLog log = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
  28.         log.traceBegin("readAllPermissions");
  29.         try {
  30.                 readAllPermissions();
  31.                 readPublicNativeLibrariesList();
  32.         } finally {
  33.                 log.traceEnd();
  34.         }
  35. }
  36. // 读取handheld_core_hardware.xml配置文件
  37. private void readPermissionsFromXml(final XmlPullParser parser, File permFile,
  38.             int permissionFlag) {
  39.         ...
  40.          case "feature": {
  41.                 if (allowFeatures) {
  42.                         String fname = parser.getAttributeValue(null, "name");
  43.                         int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
  44.                         boolean allowed;
  45.                         if (!lowRam) {
  46.                                 allowed = true;
  47.                         } else {
  48.                                 String notLowRam = parser.getAttributeValue(null, "notLowRam");
  49.                                 allowed = !"true".equals(notLowRam);
  50.                         }
  51.                         if (fname == null) {
  52.                                 Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
  53.                                                 + parser.getPositionDescription());
  54.                         } else if (allowed) {
  55.                                 addFeature(fname, fversion);
  56.                         }
  57.                 } else {
  58.                         logNotAllowedInPartition(name, permFile, parser);
  59.                 }
  60.                 XmlUtils.skipCurrentTag(parser);
  61.         } break;
  62.         ...
  63. }
复制代码
只要解释掉<feature name="android.software.print" />,hasSystemFeature方法返回false,printmanagerservice服务就不会启动,如下:
  1. // frameworks/native/data/etc/handheld_core_hardware.xml
  2. <!-- basic system services -->
  3.     <feature name="android.software.app_widgets" />
  4.     <feature name="android.software.telecom" />
  5.     <feature name="android.software.voice_recognizers" notLowRam="true" />
  6.     <feature name="android.software.backup" />
  7.     <feature name="android.software.home_screen" />
  8.     <feature name="android.software.input_methods" />
  9.     <feature name="android.software.picture_in_picture" notLowRam="true" />
  10.     <feature name="android.software.activities_on_secondary_displays" notLowRam="true" />
  11.     // <feature name="android.software.print" />
  12.     <feature name="android.software.companion_device_setup" />
  13.     <feature name="android.software.autofill" />
  14.     <feature name="android.software.credentials" />
  15.     <feature name="android.software.cant_save_state" />
  16.     <feature name="android.software.secure_lock_screen" />
  17.     <feature name="android.software.window_magnification" />
  18.         ...
  19. </permissions>
复制代码
  1. PRODUCT_COPY_FILES := \ frameworks/native/data/etc/handheld_core_hardware.xml:system/etc/permissions/handheld_core_hardware.xml
复制代码
app进程或体系别的服务在利用该服务前会做空判断,避免服务获取不到引入的空指针错误:
  1. // 服务注册
  2. registerService(Context.PRINT_SERVICE, PrintManager.class,
  3.                 new CachedServiceFetcher<PrintManager>() {
  4.         @Override
  5.         public PrintManager createService(ContextImpl ctx) throws ServiceNotFoundException {
  6.                 IPrintManager service = null;
  7.                 // If the feature not present, don't try to look up every time
  8.                 if (ctx.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
  9.                         service = IPrintManager.Stub.asInterface(ServiceManager
  10.                                         .getServiceOrThrow(Context.PRINT_SERVICE));
  11.                 }
  12.                 final int userId = ctx.getUserId();
  13.                 final int appId = UserHandle.getAppId(ctx.getApplicationInfo().uid);
  14.                 return new PrintManager(ctx.getOuterContext(), service, userId, appId);
  15. }});
  16. public PrintManager(Context context, IPrintManager service, int userId, int appId) {
  17.         mContext = context;
  18.         mService = service;
  19.         ...
  20. }
  21. // app进程调用服务相关接口之前会做null判断,避免服务获取失败导致空指针错误
  22. public PrintJob getPrintJob(PrintJobId printJobId) {
  23.         if (mService == null) {
  24.                 Log.w(LOG_TAG, "Feature android.software.print not available");
  25.                 return null;
  26.         }
  27.         try {
  28.                 PrintJobInfo printJob = mService.getPrintJobInfo(printJobId, mAppId, mUserId);
  29.                 if (printJob != null) {
  30.                         return new PrintJob(printJob, this);
  31.                 }
  32.         } catch (RemoteException re) {
  33.                 throw re.rethrowFromSystemServer();
  34.         }
  35.         return null;
  36. }
  37. // 其它服务在调用该服务之前会做hasSystemFeature判断是否可用
  38. @Override
  39. public int getAvailabilityStatus() {
  40.         return mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)
  41.                         && mPrintManager != null
  42.                         ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
  43. }
复制代码
六、别的模块

6.1 NativeCrashListener

NativeCrashListener线程模块用于监听native crash变乱,即当某个进程发生native crash时,由crash_dump64进程通过socket(ndebugsocket)方式将crash debug信息传给NativeCrashListener模块,并传给AMS服务写入dropbox.


  • 流程框架

system_server进程启动的systemReady阶段创建NativeCrashListener线程,该线程创建"ndebugsocket" socket服务端,当有进程发生native crash时,crash_dump64客户端进程通过socket与system_server通讯,最后将数据存到"data/system/dropbox"目录。


  • 焦点代码段
  1. // frameworks/base/services/java/com/android/server/SystemServer.java
  2. mActivityManagerService.systemReady(() -> {
  3.     ...
  4.     mSystemServiceManager.startBootPhase(t, SystemService.PHASE_ACTIVITY_MANAGER_READY);
  5.     ...
  6.     mActivityManagerService.startObservingNativeCrashes();
  7.     ...
  8. }
  9. // rameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
  10. public void startObservingNativeCrashes() {
  11.     final NativeCrashListener ncl = new NativeCrashListener(this);
  12.     ncl.start();
  13. }
复制代码
  1. // frameworks/base/services/core/java/com/android/server/am/NativeCrashListener.java
  2. final class NativeCrashListener extends Thread {
  3.     static final String DEBUGGERD_SOCKET_PATH = "/data/system/ndebugsocket";
  4.     static final long SOCKET_TIMEOUT_MILLIS = 10000;
  5.     final ActivityManagerService mAm;
  6.     // 内部类线程
  7.     class NativeCrashReporter extends Thread {
  8.         ProcessRecord mApp;
  9.         int mSignal;
  10.         boolean mGwpAsanRecoverableCrash;
  11.         String mCrashReport;
  12.         NativeCrashReporter(ProcessRecord app, int signal, boolean gwpAsanRecoverableCrash,
  13.                             String report) {
  14.             super("NativeCrashReport");
  15.             mApp = app;
  16.             mSignal = signal;
  17.             mGwpAsanRecoverableCrash = gwpAsanRecoverableCrash;
  18.             mCrashReport = report;
  19.         }
  20.         @Override
  21.         public void run() {
  22.             try {
  23.                 CrashInfo ci = new CrashInfo();
  24.                 ci.exceptionClassName = "Native crash";
  25.                 ci.exceptionMessage = Os.strsignal(mSignal);
  26.                 ci.throwFileName = "unknown";
  27.                 ci.throwClassName = "unknown";
  28.                 ci.throwMethodName = "unknown";
  29.                 ci.stackTrace = mCrashReport;
  30.                 // crash数据传给AMS,由AMS写入dropbox
  31.                 mAm.handleApplicationCrashInner(
  32.                         mGwpAsanRecoverableCrash ? "native_recoverable_crash" : "native_crash",
  33.                         mApp, mApp.processName, ci);
  34.                 if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned");
  35.             } catch (Exception e) {
  36.                 Slog.e(TAG, "Unable to report native crash", e);
  37.             }
  38.         }
  39.     }
  40.     // 在system_server进程启动的systemReady阶段执行,创建socke server,进入循环监听
  41.     @Override
  42.     public void run() {
  43.         final byte[] ackSignal = new byte[1];
  44.         {
  45.             File socketFile = new File(DEBUGGERD_SOCKET_PATH);
  46.             if (socketFile.exists()) {
  47.                 socketFile.delete();
  48.             }
  49.         }
  50.         try {
  51.             FileDescriptor serverFd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
  52.             final UnixSocketAddress sockAddr = UnixSocketAddress.createFileSystem(
  53.                     DEBUGGERD_SOCKET_PATH);
  54.             Os.bind(serverFd, sockAddr);
  55.             Os.listen(serverFd, 1);
  56.             Os.chmod(DEBUGGERD_SOCKET_PATH, 0777);
  57.             while (true) {
  58.                 FileDescriptor peerFd = null;
  59.                 try {
  60.                     if (MORE_DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");
  61.                     peerFd = Os.accept(serverFd, null /* peerAddress */);
  62.                     if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
  63.                     if (peerFd != null) {
  64.                         // 数据客户端的信息
  65.                         consumeNativeCrashData(peerFd);
  66.                     }
  67.                 } catch (Exception e) {
  68.                     Slog.w(TAG, "Error handling connection", e);
  69.                 } finally {
  70.                     ...
  71.                 }
  72.                     
  73.     }
  74.     // 读取client socket数据,存到byte[] buffer
  75.     static int readExactly(FileDescriptor fd, byte[] buffer, int offset, int numBytes)
  76.             throws ErrnoException, InterruptedIOException {
  77.         int totalRead = 0;
  78.         while (numBytes > 0) {
  79.             int n = Os.read(fd, buffer, offset + totalRead, numBytes);
  80.             if (n <= 0) {
  81.                 if (DEBUG) {
  82.                     Slog.w(TAG, "Needed " + numBytes + " but saw " + n);
  83.                 }
  84.                 return -1;  // premature EOF or timeout
  85.             }
  86.             numBytes -= n;
  87.             totalRead += n;
  88.         }
  89.         return totalRead;
  90.     }
  91.     // Read a crash report from the connection
  92.     void consumeNativeCrashData(FileDescriptor fd) {
  93.         final byte[] buf = new byte[4096];
  94.         final ByteArrayOutputStream os = new ByteArrayOutputStream(4096);
  95.         try {
  96.             StructTimeval timeout = StructTimeval.fromMillis(SOCKET_TIMEOUT_MILLIS);
  97.             Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout);
  98.             Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout);
  99.             int headerBytes = readExactly(fd, buf, 0, 9);
  100.             boolean gwpAsanRecoverableCrash = buf[8] != 0;
  101.            ...
  102.             // byte[]转String
  103.             int bytes;
  104.             do {
  105.                 // get some data
  106.                 bytes = Os.read(fd, buf, 0, buf.length);
  107.                 if (bytes > 0) {
  108.                     if (MORE_DEBUG) {
  109.                         String s = new String(buf, 0, bytes, "UTF-8");
  110.                         Slog.v(TAG, "READ=" + bytes + "> " + s);
  111.                     }
  112.                     // did we just get the EOD null byte?
  113.                     if (buf[bytes - 1] == 0) {
  114.                         os.write(buf, 0, bytes - 1); // exclude the EOD token
  115.                         break;
  116.                     }
  117.                     // no EOD, so collect it and read more
  118.                     os.write(buf, 0, bytes);
  119.                 }
  120.             } while (bytes > 0);
  121.             if (!gwpAsanRecoverableCrash) {
  122.                 synchronized (mAm) {
  123.                     synchronized (mAm.mProcLock) {
  124.                         pr.mErrorState.setCrashing(true);
  125.                         pr.mErrorState.setForceCrashReport(true);
  126.                     }
  127.                 }
  128.             }
  129.             final String reportString = new String(os.toByteArray(), "UTF-8");
  130.             (new NativeCrashReporter(pr, signal, gwpAsanRecoverableCrash, reportString)).start();
  131.         } catch (Exception e) {
  132.             Slog.e(TAG, "Exception dealing with report", e);
  133.             // ugh, fail.
  134.         }
  135.     }
  136. }
复制代码
6.2 Android watchdog

Android watchdog用于监测体系服务、UI线程、动画线程等是否出现死锁或卡死,system_server进程启动时会启动watchdog线程,进入监听,体系服务启动时会加入watchdog监听。
  1. private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
  2.         ......
  3.         // Start the watchdog as early as possible so we can crash the system server// if we deadlock during early boott.traceBegin("StartWatchdog");
  4.         final Watchdog watchdog = Watchdog.getInstance();    // 对象创建
  5.         watchdog.start();    // 启动watchdong线程
  6.         mDumper.addDumpable(watchdog);
  7.         ......
  8. }
复制代码


  • 实现原理
以PMS服务为例,
1)PMS服务实现Watchdog.Monitor接口,服务启动时,将PMS服务对象加入到watchdong的Monitor列表
2)system_server进程启动时,会启动watchdog线程,每隔30s会启动monitor线程调用monitor列表中的PMS对象加锁的monitor方法,如果超过30s没有返回,阐明一直在等待锁,即PMS服务卡死。
  1. // PowerManagerService.java
  2. public final class PowerManagerService extends SystemService
  3.         implements Watchdog.Monitor {
  4.     ...
  5.     @Override
  6.     public void onStart() {
  7.         // 加入watchdog监听
  8.         Watchdog.getInstance().addMonitor(this);    // pms服务实现了Watchdog.Monitor接口
  9.         ...
  10.     }
  11.    
  12.     // 在watch dog中调用,超过30s没有返回,说明一直在等待锁,即服务卡死
  13.     @Override
  14.     public void monitor() {
  15.         // Grab and release lock for watchdog monitor to detect deadlocks.
  16.         synchronized (mLock) {
  17.         }
  18.     }
  19.     ...
  20. }
  21. // frameworks/base/services/core/java/com/android/server/Watchdog.java
  22. public class Watchdog implements Dumpable {
  23.     ...
  24.     private final Thread mThread;
  25.     private final HandlerChecker mMonitorChecker;
  26.     private Watchdog() {
  27.         // 创建watchdog线程
  28.         mThread = new Thread(this::run, "watchdog");
  29.         // 创建monitor线程
  30.          mMonitorChecker = new HandlerChecker(new Handler(t.getLooper()), "monitor thread");
  31.         ...
  32.     }
  33.    
  34.     public void start() {
  35.         // 启动watchdog线程,执行Watchdog类中的run方法
  36.         mThread.start();
  37.     }
  38.    
  39.     public void addMonitor(Monitor monitor) {
  40.             synchronized (mLock) {
  41.                     mMonitorChecker.addMonitorLocked(monitor);
  42.             }
  43.     }
  44.    
  45.     private void run() {
  46.         boolean waitedHalf = false;
  47.         while (true) {
  48.             List<HandlerChecker> blockedCheckers = Collections.emptyList();
  49.             String subject = "";
  50.             boolean allowRestart = true;
  51.             int debuggerWasConnected = 0;
  52.             boolean doWaitedHalfDump = false;
  53.             final long watchdogTimeoutMillis = mWatchdogTimeoutMillis;
  54.             final long checkIntervalMillis = watchdogTimeoutMillis / 2;
  55.             final ArrayList<Integer> pids;
  56.             synchronized (mLock) {
  57.                 long timeout = checkIntervalMillis;
  58.                 for (int i=0; i<mHandlerCheckers.size(); i++) {
  59.                         HandlerCheckerAndTimeout hc = mHandlerCheckers.get(i);
  60.                         // 执行内部类HandlerChecker中的scheduleCheckLocked方法
  61.                         hc.checker().scheduleCheckLocked(hc.customTimeoutMillis()
  62.                                         .orElse(watchdogTimeoutMillis * Build.HW_TIMEOUT_MULTIPLIER));
  63.                 }
  64.                 ...
  65.     }               
  66.    
  67.     public final class HandlerChecker implements Runnable {
  68.         ...
  69.         private Monitor mCurrentMonitor;
  70.         private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
  71.         private final ArrayList<Monitor> mMonitorQueue = new ArrayList<Monitor>();
  72.         
  73.         HandlerChecker(Handler handler, String name) {
  74.                 mHandler = handler;
  75.                 mName = name;
  76.                 mCompleted = true;
  77.         }
  78.         
  79.         void addMonitorLocked(Monitor monitor) {
  80.                 mMonitorQueue.add(monitor);
  81.         }
  82.         
  83.             
  84.         public void scheduleCheckLocked(long handlerCheckerTimeoutMillis) {
  85.                 mWaitMaxMillis = handlerCheckerTimeoutMillis;
  86.                 if (mCompleted) {
  87.                         // Safe to update monitors in queue, Handler is not in the middle of work
  88.                         mMonitors.addAll(mMonitorQueue);
  89.                         mMonitorQueue.clear();
  90.                 }
  91.                 if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())
  92.                                 || (mPauseCount > 0)) {
  93.                         mCompleted = true;
  94.                         return;
  95.                 }
  96.                 if (!mCompleted) {
  97.                         // we already have a check in flight, so no need
  98.                         return;
  99.                 }
  100.         
  101.                 mCompleted = false;
  102.                 mCurrentMonitor = null;
  103.                 mStartTimeMillis = SystemClock.uptimeMillis();
  104.                 // 发送消息,插入消息队列最开头,执行下面的run()方法
  105.                 mHandler.postAtFrontOfQueue(this);
  106.         }
  107.    
  108.         @Override
  109.         public void run() {
  110.                 final int size = mMonitors.size();
  111.                 for (int i = 0 ; i < size ; i++) {
  112.                         synchronized (mLock) {
  113.                                 mCurrentMonitor = mMonitors.get(i);
  114.                         }
  115.                         // 开始监测服务是否出现死锁或卡死
  116.                         mCurrentMonitor.monitor();
  117.                 }
  118.         
  119.                 synchronized (mLock) {
  120.                         mCompleted = true;
  121.                         mCurrentMonitor = null;
  122.                 }
  123.         }
  124.             ...
  125.     } // HandlerChecker内部类
  126.     ...
  127. } // Watchdog
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

天空闲话

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表