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

该系列文章总纲链接:专题总纲目录 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. 重启应用程序:如果可能,系统会尝试重启崩溃的应用程序,为用户提供尽可能无缝的体验。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【技术】Jaskson的序列化与反序列化

文章目录 概念解释1.Jasksona.JSONJSON 的基本特点JSON 的基本结构JSON 示例 b.ObjectMapper类 2.序列化与反序列化a.序列化对象序列化集合序列化ListSetMap b.反序列化反序列化单个对象反序列化集合对象 概念解释 1.Jaskson Jackson 是一个用于处理 JSON 数据的 Java 库,所以…

vue-插槽作用域实用场景

vue-插槽作用域实用场景 1.插槽1.1 自定义列表渲染1.2 数据表格组件1.3 树形组件1.4 表单验证组件1.5 无限滚动组件 1.插槽 插槽感觉知道有这个东西&#xff0c;但是挺少用过的&#xff0c;每次看到基本都会再去看一遍用法和概念。但是在项目里&#xff0c;自己还是没有用到过…

基于SpringBoot+Vue的疫情物资管理系统(带1w+文档)

基于SpringBootVue的疫情物资管理系统(带1w文档) 基于SpringBootVue的疫情物资管理系统(带1w文档) 本课题研究和开发疫情物资管理系统管理系统&#xff0c;让安装在计算机上的该系统变成管理人员的小帮手&#xff0c;提高疫情物资管理系统信息处理速度&#xff0c;规范疫情物资…

C++入门基础知识107—【关于C++continue 语句】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C continue 语句的相关内容&#xff01;…

关于Java部署项目,文件上传路径问题 、Windows是\ linux是/

Windows是\ linux是/ &#xff0c;踩坑。报错如下&#xff1a;

SpringBootWeb快速入门!详解如何创建一个简单的SpringBoot项目?

在现代Web开发中&#xff0c;SpringBoot以其简化的配置和快速的开发效率而受到广大开发者的青睐。本篇文章将带领你从零开始&#xff0c;搭建一个基于SpringBoot的简单Web应用~ 一、前提准备 想要创建一个SpringBoot项目&#xff0c;需要做如下准备&#xff1a; idea集成开发…

信息安全工程师(28)机房安全分析与防护

前言 机房安全分析与防护是一个复杂而细致的过程&#xff0c;涉及到物理安全、环境控制、电力供应、数据安全、设备管理、人员管理以及紧急预案等多个方面。 一、机房安全分析 1. 物理安全威胁 非法入侵&#xff1a;未经授权的人员可能通过门窗、通风口等进入机房&#xff0c;…

《大规模语言模型从理论到实践》第一轮学习笔记

第一章 绪论 本章主要介绍大规模语言模型基本概念、发展历程和构建流程。 大规模语言模型&#xff08;Large Language Models&#xff0c;LLM&#xff09;&#xff0c;也称大语言模型 或大型语言模型。 1.1 大规模语言模型基本概念 1.语言模型&#xff08;Language Model&a…

LeetCode 3310. 移除可疑的方法

LeetCode 3310. 移除可疑的方法 你正在维护一个项目&#xff0c;该项目有 n 个方法&#xff0c;编号从 0 到 n - 1。 给你两个整数 n 和 k&#xff0c;以及一个二维整数数组 invocations&#xff0c;其中 invocations[i] [ai, bi] 表示方法 ai 调用了方法 bi。 已知如果方法 k…

Leetcode 37. 解数独

1.题目基本信息 1.1.题目描述 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 33 宫内只能出现一次。&#xff08;请参考…

文件IO及目录操作

一、文件IO 1.1 close函数&#xff08;关闭文件&#xff09; #include <unistd.h>---所需头文件 int close(int fd); 功能&#xff1a;关闭文件 参数&#xff1a;fd&#xff1a;文件描述符 返回值&#xff1a;成功返回0&#xff0c;失败返回-1&#xff0c;置位错误码 …

主机加固的关键要素:服务器防病毒

在数字化浪潮中&#xff0c;网络安全已成为企业不可忽视的一环。尤其是安全运维人员&#xff0c;他们肩负着保护企业数据不受侵害的重任。MCK主机加固解决方案&#xff0c;正是为了应对这一挑战而生。 网络安全的严峻现实 不久前&#xff0c;一家知名企业因勒索病毒攻击而被迫…

二分查找一>0~n-1中缺失的数字(点名)

1.题目&#xff1a; 2.解析&#xff1a;方法一&#xff1a;用哈希表&#xff1a;记录存在的数字&#xff0c;找到哈希表为空的数字输出 Set<Integer> set new HashSet<>();for(int x : records) set.add(x);for(int i 0; i < set.size(); i){if(!set.contain…

Linux系列-Linux的常见指令

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” Linux基本指令 ls指令 语法&#xff1a;ls 【选项】【目录或文件】 功能&#xff1a;对于目录&#xff0c;该命令列出该目录下的所有子目录与文件&#xff0c;对于文件&#xf…

【GO基础学习】环境安装到基础语法(1)

文章目录 环境安装GoLand 安装GO基础GO特点类型和函数Init函数和main函数GO命令下划线变量和常量数组切片Slice 引用 环境安装 下载地址&#xff1a;https://www.golangroadmap.com/ 安装目录文件说明&#xff1a; api&#xff1a;每个版本的 api 变更差异。 bin&#xff1…

基于SpringBoot+Vue的船舶监造系统(带1w+文档)

基于SpringBootVue的船舶监造系统(带1w文档) 基于SpringBootVue的船舶监造系统(带1w文档) 大概在20世纪90年代&#xff0c;我国才开始研发船舶监造系统&#xff0c;与一些发达国家相比&#xff0c;系统研发起步比较晚。当时的计算机技术刚开始发展起来&#xff0c;国家经济力量…

Map的实现类:HashMap

在API获取HsahMap类的全部信息 实例代码&#xff1a;创建一个Student类和Demo02 package com.map;public class Student {private String name;private int stuNo;public Student(String name, int stuNo) {this.name name;this.stuNo stuNo;}public String getName() {retu…

从零开始构建:Python自定义脚本自动化你的日常任务

从零开始构建&#xff1a;Python自定义脚本自动化你的日常任务 Python 作为一种简洁且功能强大的编程语言&#xff0c;被广泛应用于各种自动化任务中。通过编写 Python 脚本&#xff0c;你可以轻松地将日常重复性工作自动化&#xff0c;例如文件操作、数据处理、网络爬虫、系统…

C++ | Leetcode C++题解之第457题环形数组是否存在循环

题目&#xff1a; 题解&#xff1a; class Solution { public:bool circularArrayLoop(vector<int>& nums) {int n nums.size();auto next [&](int cur) {return ((cur nums[cur]) % n n) % n; // 保证返回值在 [0,n) 中};for (int i 0; i < n; i) {if …

STM32 407 RS485通信实现数据收发【我的创作纪念日】

1. 前言 本例中的485驱动&#xff0c;基于标准库编写&#xff0c;不是HAL库&#xff0c;请大家注意。 最近搞嵌入式程序&#xff0c;踩了不少坑&#xff0c;这里统一记录一下。 2. 收获 1.串口通信&#xff0c;数据是一个字节一个字节的发送&#xff0c;对方收到的数据是放在…