Android Framework AMS(02)AMS启动及相关初始化5-8

news2024/11/27 1:36:48

该系列文章总纲链接:专题总纲目录 Android Framework 总纲


本章关键点总结 & 说明:

说明:本章节主要涉及systemserver启动AMS及初始化AMS相关操作。同时由于该部分内容过多,因此拆成2个章节,本章节是第二章节,第一章节文章链接为:

Android Framework AMS(01)AMS启动及相关初始化1-4

systemserver在启动AMS(ActivityManagerService)时,不仅仅是做简单的AMS服务启动,还有很多的其他初始化相关操作,这里我们以SystemServer启动流程为主线,对AMS启动及相关逻辑进行初始化操作进行分析,接下来我们通过代码来看看具体的操作逻辑。相关代码如下:

// SystemServer
	//...
	// 定义系统服务的成员变量
	private ActivityManagerService mActivityManagerService;
	private SystemServiceManager mSystemServiceManager;
	private PowerManagerService mPowerManagerService;
	private PackageManagerService mPackageManagerService;
	//...
	private void startBootstrapServices() {
		// 启动引导服务,这些服务在系统启动的早期阶段被启动
		// ...
		// 关键点1:启动ActivityManagerService服务
		mActivityManagerService = mSystemServiceManager.startService(
				ActivityManagerService.Lifecycle.class).getService();

		// 关键点2:为ActivityManagerService设置系统服务管理器
		mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
		
		// 启动PowerManagerService服务
		mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
		
		// 关键点3:初始化电源管理
		mActivityManagerService.initPowerManagement();

		// 关键点4:将当前进程设置为系统进程
		mActivityManagerService.setSystemProcess();
		// ...
	}
	//...
	private void startCoreServices() {// 启动核心服务
		// ...
		// 为ActivityManagerService设置UsageStatsManager服务
		mActivityManagerService.setUsageStatsManager(
				LocalServices.getService(UsageStatsManagerInternal.class));
		// ...
	}
	//...
	private void startOtherServices() {// 启动其他服务
		// ...
		WindowManagerService wm = null;
		// ...
		
		// 关键点5:安装系统提供的ContentProviders
		mActivityManagerService.installSystemProviders();
		
		// 初始化看门狗监控
		watchdog.init(context, mActivityManagerService);

		// 创建WindowManagerService服务
		wm = WindowManagerService.main(context, inputManager,
				mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
				mActivityManagerService);

		// 关键点6:为ActivityManagerService设置WindowManager
		mActivityManagerService.setWindowManager(wm);

		final boolean safeMode = wm.detectSafeMode();
		if (safeMode) {// 检测安全模式
			// 进入安全模式
			mActivityManagerService.enterSafeMode();
			// 禁用JIT编译
			VMRuntime.getRuntime().disableJitCompilation();
		} else {
			// 启用JIT编译
			VMRuntime.getRuntime().startJitCompilation();
		}

		if (safeMode) {// 如果处于安全模式,显示安全模式覆盖层
			mActivityManagerService.showSafeModeOverlay();
		}
		// ...
		
		// 通知PowerManagerService系统已准备好
		mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
		
		// 关键点7:当ActivityManagerService准备好后执行的操作
		mActivityManagerService.systemReady(new Runnable() {
			@Override
			public void run() {
				// 启动系统服务管理器的下一个启动阶段
				mSystemServiceManager.startBootPhase(
						SystemService.PHASE_ACTIVITY_MANAGER_READY);
				try {
					//关键点8:开始监控本地崩溃
					mActivityManagerService.startObservingNativeCrashes();
				} catch (Throwable e) {
					// 报告错误
					reportWtf("observing native crashes", e);
				}
				// ...
                WebViewFactory.prepareWebViewInSystemServer();
				
                try {
					// 尝试启动系统用户界面
                    startSystemUi(context);
                } catch (Throwable e) {
                    reportWtf("starting System UI", e);
                }
                try {
					//尝试通知挂载服务系统已准备就绪
                    if (mountServiceF != null) mountServiceF.systemReady();
                } catch (Throwable e) {
                    reportWtf("making Mount Service ready", e);
                }
				//...
				//其他各种服务准备就绪
				//...
			}
		});
		// ...
	}

以上代码通过关键点的标注,共列出了8个重要的调用,并对这些调用进行了简单的描述。整理如下:

  • 关键点1:启动ActivityManagerService服务。
  • 关键点2:为ActivityManagerService设置系统服务管理器。
  • 关键点3:初始化电源管理。
  • 关键点4:将当前进程设置为系统进程。
  • 关键点5:安装系统提供的ContentProviders。
  • 关键点6:为ActivityManagerService设置WindowManager。
  • 关键点7:当ActivityManagerService准备好后执行的操作。
  • 关键点8:开始监控本地崩溃。

接下来针对这8个关键点进行详细的解读。上一篇关注关键点1-4部分,本章节主要关注关键点5-8部分。

5 安装系统提供的ContentProviders

这里从代码

mActivityManagerService.installSystemProviders();

开始分析,对应代码实现如下:

//ActivityManagerService
	public final void installSystemProviders() {
		List<ProviderInfo> providers;
		synchronized (this) {
			// 获取系统进程的记录
			// 这个进程是Android系统框架的核心部分,包含了许多系统服务和系统应用
			ProcessRecord app = mProcessNames.get("system", Process.SYSTEM_UID);
			
			// 生成系统应用的ContentProvider列表
			// 这些 ContentProvider 属于系统应用包,是系统框架的一部分。
			providers = generateApplicationProvidersLocked(app);

			if (providers != null) {
				// 遍历列表,检查每个Provider是否属于系统应用
				for (int i = providers.size() - 1; i >= 0; i--) {
					ProviderInfo pi = providers.get(i);
					// 检查Provider是否设置了FLAG_SYSTEM标志
					if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
						// 如果没有设置FLAG_SYSTEM标志,记录警告日志并从列表中移除
						providers.remove(i);
					}
				}
			}
		}
		// 如果最终的Provider列表不为空,则安装这些Provider
		if (providers != null) {
			mSystemThread.installSystemProviders(providers);
		}

		// 创建一个核心设置观察者,用于监听系统设置的变化
		mCoreSettingsObserver = new CoreSettingsObserver(this);
	}

installSystemProviders方法的主要作用是安装系统应用的ContentProvider。这些ContentProvider是系统正常运行所必需的,它们提供了对系统数据的访问和管理。通过同步代码块和检查FLAG_SYSTEM标志,确保只有属于系统应用的ContentProvider会被安装。此外,该方法还负责创建系统设置观察者,用于监控系统设置的变化。这个过程是系统启动和初始化阶段的关键部分,确保了系统应用能够正常提供服务。

installSystemProviders方法在Android的ActivityManagerService(AMS)中执行,其主要目的是初始化和注册系统级别的ContentProvider。那么为什么要这么做呢?总结如下:

  • 系统数据管理:系统级的ContentProvider提供了对核心系统数据的访问,如联系人、日历事件、系统设置等。这些数据需要被多个应用程序共享和访问。
  • 服务注册:通过installSystemProviders方法,系统可以将这些关键的ContentProvider注册到服务管理器(ServiceManager),使得其他应用和系统组件能够发现并与之通信。
  • 安全性:系统级的ContentProvider可以实施严格的安全策略,包括权限检查,确保只有授权的应用可以访问敏感数据。
  • 性能优化:预先加载和初始化这些ContentProvider有助于提高系统性能,因为它们在系统启动早期就已经准备就绪,减少了应用运行时的等待时间。
  • 跨应用通信:ContentProvider是Android中实现跨应用通信的重要机制。它们允许应用之间共享数据,而无需应用直接相互引用。
  • 系统服务支持:许多系统服务依赖于ContentProvider来存储和检索数据。这些服务可能包括设备策略管理器、备份服务等。
  • 一致性和可靠性:确保所有必要的系统ContentProvider都在系统启动时正确初始化,有助于提高整个系统的一致性和可靠性。
  • 用户体验:快速响应用户操作和提供流畅的用户体验需要系统服务和应用能够及时访问所需的数据。
  • 系统监控和管理:一些系统ContentProvider提供了系统运行状况和性能数据的访问,有助于系统监控和管理。
  • 初始化和配置:在系统启动过程中,installSystemProviders方法确保了所有必要的系统级ContentProvider被正确配置和初始化。

总结来说,installSystemProviders方法对于建立一个功能齐全、安全、高效的Android系统至关重要。它确保了系统应用和系统服务能够访问和管理核心数据,同时为应用提供了必要的数据共享机制。

最后,这里详细解读下涉及到的generateApplicationProvidersLocked方法,代码实现如下:

//ActivityManagerService
	private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
		List<ProviderInfo> providers = null;
		try {
			// 查询该应用进程的ContentProvider
			providers = AppGlobals.getPackageManager().
				queryContentProviders(app.processName, app.uid,
						STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
		} catch (RemoteException ex) {
		}
		int userId = app.userId;
		if (providers != null) {
			int N = providers.size();
			app.pubProviders.ensureCapacity(N + app.pubProviders.size());
			for (int i=0; i<N; i++) {
				ProviderInfo cpi = providers.get(i);
				boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags);
				// 单例ContentProvider且不是默认用户,移除
				if (singleton && UserHandle.getUserId(app.uid) != 0) {
					providers.remove(i);
					N--;
					i--;
					continue;
				}

				// 创建ComponentName
				ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
				// 尝试获取已有的ContentProviderRecord
				ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
				if (cpr == null) {
					// 如果没有找到,创建新的ContentProviderRecord
					cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
					mProviderMap.putProviderByClass(comp, cpr);
				}
				app.pubProviders.put(cpi.name, cpr);

				// 如果ContentProvider不是多进程的,添加到应用包列表
				if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
					app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode, mProcessStats);
				}
				ensurePackageDexOpt(cpi.applicationInfo.packageName);
			}
		}
		return providers;
	}

generateApplicationProvidersLocked方法的目的是初始化和注册系统应用的ContentProvider,确保它们可以为整个系统提供必要的数据访问服务。具体来说,该方法会查询系统应用包中的所有 ContentProvider,包括:

  • 系统设置 ContentProvider:如 SettingsProvider,提供了对系统设置的访问。
  • 账户 ContentProvider:如 AccountsProvider,管理账户信息。
  • 联系人 ContentProvider:如 ContactsProvider,管理联系人数据。
  • 其他系统 ContentProvider:任何其他包含在系统应用包中的 ContentProvider。

这些 ContentProvider 通常位于系统应用的包名(如 android)下,并且对于系统的正常运行和应用的数据访问至关重要。

6 为ActivityManagerService设置WindowManager

这里从代码

mActivityManagerService.setWindowManager(wm);

开始分析,对应代码实现如下:

//ActivityManagerService
    public void setWindowManager(WindowManagerService wm) {
        mWindowManager = wm;
        mStackSupervisor.setWindowManager(wm);
    }

这里的设计很简单,但我们要了解这样设计背后的意义:

  • 协调窗口和活动管理:通过设置窗口管理器的引用,AMS和ActivityStackSupervisor可以与窗口管理器进行交互,确保活动和窗口的正确创建和显示。
  • 提供窗口上下文:WindowManagerService提供了必要的上下文和工具,使AMS能够为应用程序创建和管理窗口。
  • 支持动态界面更新:在Android系统中,窗口和活动经常需要动态更新。例如,当用户切换应用程序或启动新活动时,窗口管理器负责更新用户界面。
  • 确保系统组件一致性:setWindowManager方法确保AMS和ActivityStackSupervisor使用的是同一个WindowManagerService实例,这有助于保持系统组件之间的一致性和同步。

总之,setWindowManager方法在AMS中至关重要,因为它确保了活动管理器(AMS)和活动堆栈监管者(ActivityStackSupervisor)能够与窗口管理器(WindowManagerService)进行通信和协调。这种设置对于正确管理应用程序的窗口和活动至关重要,特别是在处理复杂的用户界面场景时。

这里详细解读下涉及到的mStackSupervisor.setWindowManager方法,代码实现如下:

//ActivityStackSupervisor
	void setWindowManager(WindowManagerService wm) {
		synchronized (mService) {
			// 设置ActivityStackSupervisor使用的窗口管理器
			mWindowManager = wm;

			// 获取显示管理服务
			mDisplayManager =
					(DisplayManager)mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
			// 注册显示管理服务的监听器
			mDisplayManager.registerDisplayListener(this, null);

			// 获取所有显示的数组
			Display[] displays = mDisplayManager.getDisplays();
			// 遍历所有显示
			for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
				// 获取显示ID
				final int displayId = displays[displayNdx].getDisplayId();
				// 创建ActivityDisplay对象
				ActivityDisplay activityDisplay = new ActivityDisplay(displayId);
				//...
				// 将ActivityDisplay对象添加到映射中
				mActivityDisplays.put(displayId, activityDisplay);
			}

			// 在默认显示上创建主堆栈
			createStackOnDisplay(HOME_STACK_ID, Display.DEFAULT_DISPLAY);

			// 设置主堆栈、焦点堆栈和最后焦点的堆栈为刚创建的主堆栈
			mHomeStack = mFocusedStack = mLastFocusedStack = getStack(HOME_STACK_ID);

			// 获取输入管理服务的内部服务
			mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);

			// 初始化LeanbackOnlyDevice标志,现在可以获得有效的PackageManager引用
			mLeanbackOnlyDevice = isLeanbackOnlyDevice();
		}
	}

mStackSupervisor.setWindowManager方法的主要作用是设置窗口管理器并进行一系列初始化工作,包括获取显示管理服务、注册显示监听器、创建显示相关的数据结构、初始化主堆栈和焦点堆栈,以及获取输入管理服务。这些步骤确保了ActivityStackSupervisor能够正确管理活动的显示和堆栈,无论设备有多少个显示,以及是否为特定模式(如Leanback模式)的设备。

7 当ActivityManagerService准备好后执行的操作

这里从代码

//SystemServer
    private void startOtherServices() {// 启动其他服务
        //...
        mActivityManagerService.systemReady(new Runnable() {
			@Override
			public void run() {
				//...
			}
		});
        //...
    }

开始分析,因ActivityManagerService.systemReady的代码较长,因此这里分成3个阶段进行解读。具体如下。

7.1 systemReady第一阶段(主要以升级相关逻辑为主)

systemReady第一阶段代码实现如下:

//ActivityManagerService
// 该方法在系统启动时调用,用于执行系统服务的初始化
	public void systemReady(final Runnable goingCallback) {
		//第一阶段
		synchronized(this) {
			// 检查系统是否已经准备好,如果已经准备好,运行传入的回调并返回
			if (mSystemReady) {
				if (goingCallback != null) {
					goingCallback.run();
				}
				return;
			}

			// 更新当前的用户配置文件信息,这些信息用于安全检查
			updateCurrentProfileIdsLocked();

			// 如果最近任务列表为空,则尝试从持久化存储中恢复最近任务
			if (mRecentTasks == null) {
				mRecentTasks = mTaskPersister.restoreTasksLocked();
				// 如果恢复了最近任务,创建对应的任务堆栈
				if (!mRecentTasks.isEmpty()) {
					mStackSupervisor.createStackForRestoredTaskHistory(mRecentTasks);
				}
				// 清理最近任务列表,移除不再需要的任务
				cleanupRecentTasksLocked(UserHandle.USER_ALL);
				// 开始持久化保存任务列表
				mTaskPersister.startPersisting();
			}
			
			// 检查是否有系统更新的广播接收器需要运行
			if (!mDidUpdate) {
				// 如果已经在等待更新,那么直接返回
				if (mWaitingUpdate) {
					return;
				}
				// 创建一个列表来记录完成的广播接收器
				final ArrayList<ComponentName> doneReceivers = new ArrayList<ComponentName>();
				// 设置正在等待更新的标志
				mWaitingUpdate = true;
				// 调用deliverPreBootCompleted来执行系统更新相关的广播接收器
				deliverPreBootCompleted(new Runnable() {
					public void run() {
						// 同步代码块,确保线程安全
						synchronized (ActivityManagerService.this) {
							// 设置系统更新完成的标志
							mDidUpdate = true;
						}
						// 记录完成的广播接收器
						writeLastDonePreBootReceivers(doneReceivers);
						// 显示系统更新完成的消息
						showBootMessage(mContext.getText(
								R.string.android_upgrading_complete),
								false);
						// 递归调用systemReady,继续执行系统启动的下一阶段
						systemReady(goingCallback);
					}
				}, doneReceivers, UserHandle.USER_OWNER);

				// 如果还在等待更新,那么返回
				if (mWaitingUpdate) {
					return;
				}
				// 设置系统更新完成的标志
				mDidUpdate = true;
			}

			// 通知AppOpsService系统已经准备好
			mAppOpsService.systemReady();
			// 设置系统已经准备好的标志
			mSystemReady = true;
		}
		//...
	}

systemReady方法的第一阶段主要负责初始化系统的关键部分,包括更新用户配置文件信息、恢复最近任务、执行系统更新广播接收器等。这些步骤确保了系统在启动过程中能够正确地恢复状态,为用户使用做好准备。

7.2 systemReady第二阶段(以kill掉AMS之前启动的应用进程为主)

systemReady第二阶段代码实现如下:

//ActivityManagerService
// 该方法在系统启动时调用,用于执行系统服务的初始化
	public void systemReady(final Runnable goingCallback) {
		//...
		// 第二阶段:清理不应该在启动过程中运行的进程
		ArrayList<ProcessRecord> procsToKill = null;
		synchronized(mPidsSelfLocked) {
			for (int i = mPidsSelfLocked.size()-1; i >= 0; i--) {
				ProcessRecord proc = mPidsSelfLocked.valueAt(i);
				// 检查进程是否允许在启动过程中运行
				if (!isAllowedWhileBooting(proc.info)){
					if (procsToKill == null) {
						procsToKill = new ArrayList<ProcessRecord>();
					}
					procsToKill.add(proc);
				}
			}
		}

		// 同步代码块,确保线程安全
		synchronized(this) {
			// 清理更新进程
			if (procsToKill != null) {
				for (int i = procsToKill.size()-1; i >= 0; i--) {
					ProcessRecord proc = procsToKill.get(i);
					Slog.i(TAG, "Removing system update proc: " + proc);
					// 移除不应该在启动过程中运行的进程
					removeProcessLocked(proc, true, false, "system update done");
				}
			}
			
			// 标记为不再清理更新进程,准备启动真正的进程
			mProcessesReady = true;
		}

		// 同步代码块,确保线程安全
		synchronized(this) {
			// 处理工厂测试模式
			if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
				ResolveInfo ri = mContext.getPackageManager()
						.resolveActivity(new Intent(Intent.ACTION_FACTORY_TEST),
								STOCK_PM_FLAGS);
				CharSequence errorMsg = null;
				if (ri != null) {
					ActivityInfo ai = ri.activityInfo;
					ApplicationInfo app = ai.applicationInfo;
					if ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
						mTopAction = Intent.ACTION_FACTORY_TEST;
						mTopData = null;
						mTopComponent = new ComponentName(app.packageName, ai.name);
					} else {
						errorMsg = mContext.getResources().getText(
								com.android.internal.R.string.factorytest_not_system);
					}
				} else {
					errorMsg = mContext.getResources().getText(
							com.android.internal.R.string.factorytest_no_action);
				}
				if (errorMsg != null) {
					mTopAction = null;
					mTopData = null;
					mTopComponent = null;
					Message msg = Message.obtain();
					msg.what = SHOW_FACTORY_ERROR_MSG;
					msg.getData().putCharSequence("msg", errorMsg);
					mHandler.sendMessage(msg);
				}
			}
		}

		// 检索系统设置
		retrieveSettings();
		// 加载系统资源
		loadResourcesOnSystemReady();

		// 同步代码块,确保线程安全
		synchronized (this) {
			// 读取授权的URI权限
			readGrantedUriPermissionsLocked();
		}

		// 如果有传入的回调,运行它
		if (goingCallback != null) goingCallback.run();

		// 通知电池统计服务用户开始运行
		mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
				Integer.toString(mCurrentUserId), mCurrentUserId);
		// 通知电池统计服务用户进入前台
		mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
				Integer.toString(mCurrentUserId), mCurrentUserId);
		// 启动用户会话
		mSystemServiceManager.startUser(mCurrentUserId);
		//...

systemReady方法的第二阶段主要负责清理更新进程、记录系统就绪事件、应用设置、加载资源以及启动用户会话。这些步骤确保了系统在启动完成后能够正常运行应用程序,并为用户提供服务。

7.3 systemReady第三阶段(以启动系统应用、发广播、恢复前台活动为主)

systemReady第三阶段代码实现如下:

//ActivityManagerService
// 该方法在系统启动时调用,用于执行系统服务的初始化
	public void systemReady(final Runnable goingCallback) {
		//...
		// 第三阶段:启动应用程序和发送用户启动广播
		synchronized (this) {
			// 如果不是工厂测试模式
			if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
				try {
					// 获取所有持久应用的列表
					List apps = AppGlobals.getPackageManager().
						getPersistentApplications(STOCK_PM_FLAGS);
					if (apps != null) {
						int N = apps.size();
						for (int i = 0; i < N; i++) {
							ApplicationInfo info = (ApplicationInfo)apps.get(i);
							// 添加非"android"包的应用到系统中
							if (info != null && !info.packageName.equals("android")) {
								addAppLocked(info, false, null /* ABI override */);
							}
						}
					}
				} catch (RemoteException ex) {
					// 由于PackageManagerService在同一个进程中,这个异常不会发生
				}
			}

			// 启动初始活动
			mBooting = true;
			startHomeActivityLocked(mCurrentUserId);

			try {
				// 检查是否有系统UID错误
				if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
					Message msg = Message.obtain();
					msg.what = SHOW_UID_ERROR_MSG;
					mHandler.sendMessage(msg);
				}
			} catch (RemoteException e) {
				// 忽略异常
			}

			// 清除调用者身份,以便以系统身份发送广播
			long ident = Binder.clearCallingIdentity();
			try {
				// 发送ACTION_USER_STARTED广播,通知所有用户相关的应用
				Intent intent = new Intent(Intent.ACTION_USER_STARTED);
				intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
						| Intent.FLAG_RECEIVER_FOREGROUND);
				intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
				broadcastIntentLocked(null, null, intent,
						null, null, 0, null, null, null, AppOpsManager.OP_NONE,
						false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId);

				// 发送ACTION_USER_STARTING广播,通知用户启动
				intent = new Intent(Intent.ACTION_USER_STARTING);
				intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
				intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
				broadcastIntentLocked(null, null, intent,
						null, new IIntentReceiver.Stub() {
							@Override
							public void performReceive(Intent intent, int resultCode, String data,
									Bundle extras, boolean ordered, boolean sticky, int sendingUser)
									throws RemoteException {
							}
						}, 0, null, null,
						INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE,
						true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
			} catch (Throwable t) {
				Slog.wtf(TAG, "Failed sending first user broadcasts", t);
			} finally {
				// 恢复调用者身份
				Binder.restoreCallingIdentity(ident);
			}

			// 恢复前台活动
			mStackSupervisor.resumeTopActivitiesLocked();
			// 发送用户切换广播
			sendUserSwitchBroadcastsLocked(-1, mCurrentUserId);
		}
	}

systemReady方法的第三阶段主要负责处理工厂测试模式、启动系统的应用、发送用户启动广播、恢复前台活动等。这些步骤确保了系统在启动完成后能够正常运行应用程序,并为用户提供服务。

8 开始监控本地崩溃

这里从代码

mActivityManagerService.startObservingNativeCrashes();

开始分析,对应代码实现如下:

//ActivityManagerService
    public void startObservingNativeCrashes() {
        final NativeCrashListener ncl = new NativeCrashListener(this);
        ncl.start();
    }

NativeCrashListener 是继承Thread的,实际上就是开启了一个线程。

8.1 解读 NativeCrashListener线程逻辑

NativeCrashListener 线程的实现是run方法,该部分代码忽略了异常处理和日志的打印的逻辑,关键代码如下:

// NativeCrashListener
	public void run() {
		// 用于接收信号的字节数组
		final byte[] ackSignal = new byte[1];

		// 删除已存在的socket文件
		{
			File socketFile = new File(DEBUGGERD_SOCKET_PATH);
			if (socketFile.exists()) {
				socketFile.delete();
			}
		}

		try {
			// 创建一个UNIX domain socket
			FileDescriptor serverFd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
			final InetUnixAddress sockAddr = new InetUnixAddress(DEBUGGERD_SOCKET_PATH);
			// 绑定socket到指定路径
			Os.bind(serverFd, sockAddr, 0);
			// 监听socket连接
			Os.listen(serverFd, 1);

			// 无限循环等待debuggerd的连接
			while (true) {
				InetSocketAddress peer = new InetSocketAddress();
				FileDescriptor peerFd = null;
				try {
					// 接受连接
					peerFd = Os.accept(serverFd, peer);
					// 检查连接的凭证,只允许超级用户连接
					StructUcred credentials =
							Os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED);
					if (credentials.uid == 0) {
						// 消费崩溃数据
						consumeNativeCrashData(peerFd);
					}
				} finally {
					// 无论处理过程中是否发生异常,都要发送确认信号给debuggerd
					if (peerFd != null) {
						try {
							Os.write(peerFd, ackSignal, 0, 1);
						} finally {
							// 关闭socket连接
							Os.close(peerFd);
						}
					}
				}
			}
		}
		//...
	}

NativeCrashListener的run方法主要负责监听和处理由debuggerd发送的native崩溃信号。它通过创建一个socket服务器,等待debuggerd的连接,并在连接建立后处理崩溃数据。这个过程确保了系统能够及时响应native崩溃,从而提高系统的稳定性。

接下来我们来看看对于这些崩溃数据是如何处理的,即consumeNativeCrashData的代码实现,具体如下:

// NativeCrashListener
	// 处理崩溃的native进程发送的数据
	void consumeNativeCrashData(FileDescriptor fd) {
		// 创建一个字节缓冲区用于读取数据
		final byte[] buf = new byte[4096];
		// 创建一个字节输出流用于收集崩溃数据
		final ByteArrayOutputStream os = new ByteArrayOutputStream(4096);

		try {
			// 设置读取和写入的超时时间
			StructTimeval timeout = StructTimeval.fromMillis(SOCKET_TIMEOUT_MILLIS);
			Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout);
			Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout);

			// 首先读取崩溃的进程ID和信号号
			int headerBytes = readExactly(fd, buf, 0, 8);

			int pid = unpackInt(buf, 0);  // 进程ID
			int signal = unpackInt(buf, 4);  // 信号号

			if (pid > 0) {
				// 根据进程ID获取崩溃进程的记录
				final ProcessRecord pr;
				synchronized (mAm.mPidsSelfLocked) {
					pr = mAm.mPidsSelfLocked.get(pid);
				}
				if (pr != null) {
					// 持久进程不进行崩溃报告
					if (pr.persistent) {
						return;
					}

					int bytes;
					// 循环读取崩溃数据直到读取完毕
					do {
						bytes = Os.read(fd, buf, 0, buf.length);
						if (bytes > 0) {
							if (buf[bytes-1] == 0) {
								os.write(buf, 0, bytes-1);
								break;
							}
							os.write(buf, 0, bytes);
						}
					} while (bytes > 0);

					// 标记进程为正在崩溃状态
					synchronized (mAm) {
						pr.crashing = true;
						pr.forceCrashReport = true;
					}

					// 将崩溃报告转换为字符串
					final String reportString = new String(os.toByteArray(), "UTF-8");
					// 创建一个崩溃报告线程
					(new NativeCrashReporter(pr, signal, reportString)).start();
				}
			}
		}
		// 异常处理略...
	}

consumeNativeCrashData方法用于处理崩溃的native进程发送的数据。它读取崩溃数据,生成崩溃报告,并启动一个线程来处理这个报告。对应的处理报告的线程为NativeCrashReporter,它的run方法代码实现如下:

//NativeCrashListener
    class NativeCrashReporter extends Thread {
        //...
        @Override
        public void run() {
            try {
				// 创建CrashInfo对象,封装崩溃信息
                CrashInfo ci = new CrashInfo();
                ci.exceptionClassName = "Native crash";
                ci.exceptionMessage = Os.strsignal(mSignal);
                ci.throwFileName = "unknown";
                ci.throwClassName = "unknown";
                ci.throwMethodName = "unknown";
                ci.stackTrace = mCrashReport;

				// 调用AMS的handleApplicationCrashInner方法处理崩溃
                mAm.handleApplicationCrashInner("native_crash", mApp, mApp.processName, ci);
            }
			//...
        }
    }

继续分析AMS的handleApplicationCrashInner方法,代码实现如下:

//ActivityManagerService
	// 处理应用程序崩溃的方法
	void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
			ApplicationErrorReport.CrashInfo crashInfo) {
		// 将崩溃事件记录到系统事件日志中,包括崩溃的进程ID、用户ID、进程名称
		// 、进程标志、异常类名、异常信息、发生异常的文件名和行号等信息
		EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),
				UserHandle.getUserId(Binder.getCallingUid()), processName,
				r == null ? -1 : r.info.flags,
				crashInfo.exceptionClassName,
				crashInfo.exceptionMessage,
				crashInfo.throwFileName,
				crashInfo.throwLineNumber);

		// 将崩溃的错误信息添加到DropBox中
		// DropBox是一个系统服务,用于收集和存储系统和应用程序的错误报告,供后续分析和调试使用
		addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);

		// 执行崩溃处理逻辑,包括重启应用程序、通知用户、记录崩溃信息等
		crashApplication(r, crashInfo);
	}

handleApplicationCrashInner方法是一个关键的系统方法,用于处理应用程序崩溃事件。它通过记录崩溃信息、添加错误报告到DropBox以及执行崩溃处理,确保了系统能够适当地响应崩溃事件,同时为后续的错误分析和调试提供了必要的信息。这部分至此就不再继续分析了,再往后就是一堆崩溃信息的详细处理了,我们只需要了解到最终崩溃的处理是在AMS中处理即可。其他的可根据自己的需要继续深入分析。

8.2 关于崩溃处理的理解总结

当应用程序发生崩溃时,系统会通过一系列机制来处理崩溃并生成崩溃报告。以下是整个过程的概述:

  1. 应用崩溃:当应用程序发生崩溃(通常是由于native代码异常,如段错误、总线错误等)时,系统会捕获到这个崩溃信号。
  2. 生成崩溃报告:系统服务debuggerd会生成崩溃报告。这个报告包含了崩溃时的堆栈跟踪、寄存器状态、运行的线程等信息。
  3. 建立socket连接:debuggerd会尝试建立一个socket连接,将崩溃报告发送给ActivityManagerService(AMS)或其他监听崩溃的服务。
  4. 监听崩溃:在AMS中,NativeCrashListener线程会监听崩溃。它会创建一个socket服务器,并等待debuggerd的连接。
  5. 接收崩溃报告:当debuggerd与AMS的socket服务器建立连接后,AMS的NativeCrashListener线程会接收到崩溃报告。
  6. 处理崩溃:AMS会处理崩溃报告,这可能包括记录崩溃信息、通知开发者、重启应用程序或执行其他错误恢复操作。
  7. 发送确认信号:AMS会向debuggerd发送一个确认信号,表明崩溃报告已经收到。
  8. 关闭socket连接:处理完崩溃报告后,AMS会关闭socket连接。
  9. 用户通知:如果应用程序崩溃导致用户体验受到影响,系统可能会向用户显示一个崩溃通知,让用户知道应用程序发生了问题。
  10. 崩溃转储(可选):在某些情况下,系统可能会将崩溃信息转储到磁盘上的一个文件中,以供后续分析。
  11. 重启应用程序:如果可能,系统会尝试重启崩溃的应用程序,为用户提供尽可能无缝的体验。

这个过程确保了应用程序崩溃时,系统能够捕获必要的信息,并采取适当的措施来恢复或通知用户。通过这种方式,系统能够提高应用程序的稳定性和用户体验。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2190050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSS元素显示类型

display 属性是 CSS 中最重要的属性之一&#xff0c;主要用来控制元素的布局&#xff0c;通过 display 属性您可以设置元素是否显示以及如何显示。 根据元素类型的不同&#xff0c;每个元素都有一个默认的 display 属性值&#xff0c;例如<div>默认的 display 属性值为 …

Pandas基础学习

导入 导入pandas一般是这样导入的 import pandas as pdSeries 创建 s1 pd.Series([5, 17, 3, 26, 31])注意Series的第一个字母要大写&#xff0c;表明这其实是Series类的构建函数, 返回的是Series类的实例 获得元素或者索引 单独获得元素 s1.values单独获得索引值 s…

基于springboot的校园物流管理系统(含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于springboot的校园物流管理系统1拥有两种角色 管理员&#xff1a;物流管理&#xff08;揽件、寄出&#xff09;、用户管理等 用户&#xff1a;收件、寄件、个人物流信息管理等 1.1 …

proto3语法

文章目录 字段规则消息类型定义与使用序列化写入文件hexdump工具反序列化读取文件decode命令选项enum类型设置电话类型 Any类型设置地址信息 oneof类型设置其他联系人信息 map类型添加备注信息 默认值更新消息更新规则 未知字段输出未知字段消息 option选项常用选项 本章代码仓…

Leetcode 剑指 Offer II 097.不同的子序列

题目难度: 困难 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定一个字符串 s 和一个字符串 t &#xff0c;计算在 s 的子序列…

NVIDIA网卡系列之ConnectX-5规格信息(100G-PCIe 3.0x16-8PF512VF-2016年发布)

背景 NVIDIA ConnectX-5系列的网卡&#xff0c;早期还在Mellanox未被NVIDIA收购的时候就发布了&#xff0c;主流支持100G&#xff0c;主要用在PCIe3.0&#xff0c;最大支持200G的产品。虽然已经发布多年&#xff0c;但是目前还是在大量使用。100Gbps的速率对比普通网卡来讲&am…

基于SSM的电影院售票系统设计与实现

文未可获取一份本项目的java源码和数据库参考。 前言 近些年的电影在人们文娱活动中占据重要地位&#xff0c;另外&#xff0c;由于人们的生活越来越富有&#xff0c;越来越多的人们不再选择在家里看电影&#xff0c;而是选择去电影院看电影。但是&#xff0c;以往的售票方式是…

在Visual Studio中使用CMakeLists.txt集成EasyX库的详细指南

EasyX库是一款专为Windows平台设计的轻量级C图形库&#xff0c;适合初学者和教育领域使用。结合Visual Studio和CMake工具链&#xff0c;用户可以轻松创建C项目&#xff0c;并集成EasyX库&#xff0c;实现丰富的图形编程效果。本文将详细介绍如何在Visual Studio中通过CMakeLis…

分布式事务(Seata-AT模式)

角色说明 TC (Transaction Coordinator) - 事务协调者 维护全局和分支事务的状态,驱动全局事务提交或回滚。 TM (Transaction Manager) - 事务管理器 定义全局事务的范围:开始全局事务、提交或回滚全局事务。 RM (Resource Manager) - 资源管理器 管理分…

macOS编译和运行prometheus2.54

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 本篇概览 本文详述了在macOS(M2芯片)上编译和运行prometheus2.54版本的过程&#xff0c;以及安装node_exporter和grafana并使用prometheus指标进行展示 本地…

【含文档】基于Springboot+Vue的活力健身馆管理系统(含源码+数据库+lw)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统定…

HTB:Preignition[WriteUP]

连接至HTB服务器并启动靶机 靶机IP&#xff1a;10.129.157.49 分配IP&#xff1a;10.10.16.12 1.Directory Brute-forcing is a technique used to check a lot of paths on a web server to find hidden pages. Which is another name for this? (i) Local File Inclusion, (…

喜欢听音乐的朋友,怎么能少这个音乐插件?

喜欢听音乐的朋友有福利了&#xff0c;今天小编给大家带来一个非常酷的音乐插件&#xff0c;让你的音乐在桌面跳动起来~&#xff01;想象一下&#xff0c;随着旋律的起伏&#xff0c;你的桌面仿佛被赋予了生命&#xff0c;音符跳跃、光影交错&#xff0c;每一首歌都化作了眼前流…

电源入口防护电路

电源入口防护电路 原则:先防护&#xff0c;后防反&#xff0c;最后滤波基本防护器件防反措施 原则:先防护&#xff0c;后防反&#xff0c;最后滤波 在设计电路保护方案时&#xff0c;遵循“先防护&#xff0c;后防反&#xff0c;最后滤波”的原则是非常重要的。这种顺序确保了…

Python入门--函数

目录 1. 函数介绍 2. 函数的定义 3. 函数的参数 4. 函数的返回值 5. 函数说明文档 6. 函数的嵌套调用 7. 函数的作用域 (1). 局部变量 (2). 全局变量 (3). global关键字 1. 函数介绍 函数&#xff1a;是组织好的&#xff0c;可重复使用的&#xff0c;用来实现特定功能…

【无题】夜入伊人笑愉,泪湿心夜难眠。

在这句诗中&#xff0c;意境描绘了一种深沉的情感体验&#xff0c;充满了温柔与哀愁。诗人通过“夜入伊人笑愉”开启了一段梦境之旅&#xff0c;其中“夜入”象征着进入梦境的状态。在这个梦幻的世界里&#xff0c;诗人与心爱的人欢笑嬉戏&#xff0c;那份快乐和亲昵如同真实的…

java高并发场景RabbitMQ的使用

场景是面试时被问到&#xff0c;一次性请求100多万个前端请求&#xff0c;请问你如果进行后端处理。因为之前的电商也没有一次性这么大的业务量&#xff0c;所以只是前端nginx做了对应的负载均衡技术。所以回答的不是那么流畅。面试官的回答你可以用RabbitMQ做分流,削峰,异步处…

什么是 ARP 欺骗和缓存中毒攻击?

如果您熟悉蒙面歌王&#xff0c;您就会明白蒙面歌王的概念&#xff1a;有人伪装成别人。然后&#xff0c;当面具掉下来时&#xff0c;您会大吃一惊&#xff0c;知道了这位名人是谁。类似的事情也发生在 ARP 欺骗攻击中&#xff0c;只是令人惊讶的是&#xff0c;威胁行为者利用他…

中小型网络系统综合实验

一、实验要求 1.pc端自动获取ip地址&#xff0c;可以互通访问&#xff0c;可以访问域名解析服务器 2.设置vlan&#xff0c;三层交换机实现不同vlan之间的交流 3.设置静态路由&#xff0c;配置nat地址转换&#xff0c;实现全网可达 二、实验思路 1.首先给LSW2配置vlan 10 &a…

双十一不能错过的好物推荐!强推五款超好用的品牌好物

双十一快到了&#xff0c;这个时候的优惠力度都是最大的&#xff0c;还不知道买啥的小伙伴们赶紧来看这篇好物推荐&#xff01;以下五款产品是我花了几天时间精心挑选出来的&#xff0c;看完之后保证你想加入购物车&#xff01; 品牌好物推荐一、希亦CG超声波清洗机 如果你带眼…