Android14 - Framework- Configuration的创建和更新

本文描述启动一个新进程ActivityFramworkConfiguration创建传导过程

首先我们知道所有Window容器继承于WindowContainerWindowContainer本身ConfigurationContainer子类于此同时WindowProcessController也是ConfigurationContainer子类ConfigurationContainer认为可以Window进程进行Configuration配置基本载体单位

我们启动一个Activity角度出发Configuration的传导过程

启动Activity过程调用ActivityTaskSupervisorrealStartActivityLocked方法

platform/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java

boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
	boolean andResume, boolean checkConfig) throws RemoteException {
    ...
    if (checkConfig) {
		// Deferring resume here because we're going to launch new activity shortly.
		// We don't want to perform a redundant launch of the same record while ensuring
		// configurations and trying to resume top activity of focused root task.
		mRootWindowContainer.ensureVisibilityAndConfig(r, r.getDisplayId(),
				false /* markFrozenIfConfigChanged */, true /* deferResume */);
	}
    ...
}

Activity启动场景checkConfigtrue

看ensureVisibilityAndConfig

platform/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
		boolean markFrozenIfConfigChanged, boolean deferResume) {
	// First ensure visibility without updating the config just yet. We need this to know what
	// activities are affecting configuration now.
	// Passing null here for 'starting' param value, so that visibility of actual starting
	// activity will be properly updated.
	ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
			false /* preserveWindows */, false /* notifyClients */);

	if (displayId == INVALID_DISPLAY) {
		// The caller didn't provide a valid display id, skip updating config.
		return true;
	}

	// Force-update the orientation from the WindowManager, since we need the true configuration
	// to send to the client now.
	final DisplayContent displayContent = getDisplayContent(displayId);
	Configuration config = null;
	if (displayContent != null) {
		config = displayContent.updateOrientation(starting, true /* forceUpdate */);
	}
	// Visibilities may change so let the starting activity have a chance to report. Can't do it
	// when visibility is changed in each AppWindowToken because it may trigger wrong
	// configuration push because the visibility of some activities may not be updated yet.
	if (starting != null) {
		starting.reportDescendantOrientationChangeIfNeeded();
	}
	if (starting != null && markFrozenIfConfigChanged && config != null) {
		starting.frozenBeforeDestroy = true;
	}

	if (displayContent != null) {
		// Update the configuration of the activities on the display.
		return displayContent.updateDisplayOverrideConfigurationLocked(config, starting,
				deferResume, null /* result */);
	} else {
		return true;
	}
}

调用了DisplayContentupdateOrientation注意这里第二个参数forceUpdatetrue强制更新

platform/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

Configuration updateOrientation(WindowContainer<?> freezeDisplayWindow, boolean forceUpdate) {
	if (!mDisplayReady) {
		return null;
	}

	Configuration config = null;
	if (updateOrientation(forceUpdate)) {
		// If we changed the orientation but mOrientationChangeComplete is already true,
		// we used seamless rotation, and we don't need to freeze the screen.
		if (freezeDisplayWindow != null && !mWmService.mRoot.mOrientationChangeComplete) {
			final ActivityRecord activity = freezeDisplayWindow.asActivityRecord();
			if (activity != null && activity.mayFreezeScreenLocked()) {
				activity.startFreezingScreen();
			}
		}
		config = new Configuration();
		computeScreenConfiguration(config);
	}

	return config;
}

如果需要更新Orientation创建new Configuration()调用computeScreenConfiguration

platform/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

/**
 * Compute display configuration based on display properties and policy settings.
 * Do not call if mDisplayReady == false.
 */
void computeScreenConfiguration(Configuration config) {
	final DisplayInfo displayInfo = updateDisplayAndOrientation(config);
	final int dw = displayInfo.logicalWidth;
	final int dh = displayInfo.logicalHeight;
	mTmpRect.set(0, 0, dw, dh);
	config.windowConfiguration.setBounds(mTmpRect);
	config.windowConfiguration.setMaxBounds(mTmpRect);
	config.windowConfiguration.setWindowingMode(getWindowingMode());
	config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());

	computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation);

	config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
			| ((displayInfo.flags & Display.FLAG_ROUND) != 0
			? Configuration.SCREENLAYOUT_ROUND_YES
			: Configuration.SCREENLAYOUT_ROUND_NO);

	config.densityDpi = displayInfo.logicalDensityDpi;

	config.colorMode =
			((displayInfo.isHdr() && mWmService.hasHdrSupport())
					? Configuration.COLOR_MODE_HDR_YES
					: Configuration.COLOR_MODE_HDR_NO)
					| (displayInfo.isWideColorGamut() && mWmService.hasWideColorGamutSupport()
					? Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES
					: Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO);

	// Update the configuration based on available input devices, lid switch,
	// and platform configuration.
	config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
	config.keyboard = Configuration.KEYBOARD_NOKEYS;
	config.navigation = Configuration.NAVIGATION_NONAV;

	int keyboardPresence = 0;
	int navigationPresence = 0;
	final InputDevice[] devices = mWmService.mInputManager.getInputDevices();
	final int len = devices != null ? devices.length : 0;
	for (int i = 0; i < len; i++) {
		InputDevice device = devices[i];
		// Ignore virtual input device.
		if (device.isVirtual()) {
			continue;
		}

		// Check if input device can dispatch events to current display.
		if (!mWmService.mInputManager.canDispatchToDisplay(device.getId(), mDisplayId)) {
			continue;
		}

		final int sources = device.getSources();
		final int presenceFlag = device.isExternal()
				? WindowManagerPolicy.PRESENCE_EXTERNAL : WindowManagerPolicy.PRESENCE_INTERNAL;

		if (mWmService.mIsTouchDevice) {
			if ((sources & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) {
				config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
			}
		} else {
			config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
		}

		if ((sources & InputDevice.SOURCE_TRACKBALL) == InputDevice.SOURCE_TRACKBALL) {
			config.navigation = Configuration.NAVIGATION_TRACKBALL;
			navigationPresence |= presenceFlag;
		} else if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
				&& config.navigation == Configuration.NAVIGATION_NONAV) {
			config.navigation = Configuration.NAVIGATION_DPAD;
			navigationPresence |= presenceFlag;
		}

		if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
			config.keyboard = Configuration.KEYBOARD_QWERTY;
			keyboardPresence |= presenceFlag;
		}
	}

	if (config.navigation == Configuration.NAVIGATION_NONAV && mWmService.mHasPermanentDpad) {
		config.navigation = Configuration.NAVIGATION_DPAD;
		navigationPresence |= WindowManagerPolicy.PRESENCE_INTERNAL;
	}

	// Determine whether a hard keyboard is available and enabled.
	// TODO(multi-display): Should the hardware keyboard be tied to a display or to a device?
	boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
	if (hardKeyboardAvailable != mWmService.mHardKeyboardAvailable) {
		mWmService.mHardKeyboardAvailable = hardKeyboardAvailable;
		mWmService.mH.removeMessages(REPORT_HARD_KEYBOARD_STATUS_CHANGE);
		mWmService.mH.sendEmptyMessage(REPORT_HARD_KEYBOARD_STATUS_CHANGE);
	}

	mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();

	// Let the policy update hidden states.
	config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
	config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
	config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
	mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
}

computeScreenAppConfiguration用来更新屏幕尺寸密度、rotation

platform/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

/** Compute configuration related to application without changing current display. */
private void computeScreenAppConfiguration(Configuration outConfig, int dw, int dh,
		int rotation) {
	final DisplayPolicy.DecorInsets.Info info =
			mDisplayPolicy.getDecorInsetsInfo(rotation, dw, dh);
	// AppBounds at the root level should mirror the app screen size.
	outConfig.windowConfiguration.setAppBounds(info.mNonDecorFrame);
	outConfig.windowConfiguration.setRotation(rotation);
	outConfig.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;

	final float density = mDisplayMetrics.density;
	outConfig.screenWidthDp = (int) (info.mConfigFrame.width() / density + 0.5f);
	outConfig.screenHeightDp = (int) (info.mConfigFrame.height() / density + 0.5f);
	outConfig.compatScreenWidthDp = (int) (outConfig.screenWidthDp / mCompatibleScreenScale);
	outConfig.compatScreenHeightDp = (int) (outConfig.screenHeightDp / mCompatibleScreenScale);
	outConfig.screenLayout = computeScreenLayout(
			Configuration.resetScreenLayout(outConfig.screenLayout),
			outConfig.screenWidthDp, outConfig.screenHeightDp);

	final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
	outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dw, dh);
	outConfig.windowConfiguration.setDisplayRotation(rotation);
}

回到ensureVisibilityAndConfig 获取Configuration调用displayContent.updateDisplayOverrideConfigurationLocked

platform/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

/**
 * Updates override configuration specific for the selected display. If no config is provided,
 * new one will be computed in WM based on current display info.
 */
boolean updateDisplayOverrideConfigurationLocked(Configuration values,
		ActivityRecord starting, boolean deferResume,
		ActivityTaskManagerService.UpdateConfigurationResult result) {

	int changes = 0;
	boolean kept = true;

	mAtmService.deferWindowLayout();
	try {
		if (values != null) {
			if (mDisplayId == DEFAULT_DISPLAY) {
				// Override configuration of the default display duplicates global config, so
				// we're calling global config update instead for default display. It will also
				// apply the correct override config.
				changes = mAtmService.updateGlobalConfigurationLocked(values,
						false /* initLocale */, false /* persistent */,
						UserHandle.USER_NULL /* userId */);
			} else {
				changes = performDisplayOverrideConfigUpdate(values);
			}
		}

		if (!deferResume) {
			kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
		}
	} finally {
		mAtmService.continueWindowLayout();
	}

	if (result != null) {
		result.changes = changes;
		result.activityRelaunched = !kept;
	}
	return kept;
}

单个屏幕场景mDisplayId == DEFAULT_DISPLAY调用mAtmService.updateGlobalConfigurationLocked

platform/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

/** Update default (global) configuration and notify listeners about changes. */
int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
		boolean persistent, int userId) {

	mTempConfig.setTo(getGlobalConfiguration());
	final int changes = mTempConfig.updateFrom(values);
	if (changes == 0) {
		return 0;
	}

	...

	// Note: certain tests currently run as platform_app which is not allowed
	// to set debug system properties. To ensure that system properties are set
	// only when allowed, we check the current UID.
	if (Process.myUid() == Process.SYSTEM_UID) {
		if (values.mcc != 0) {
			SystemProperties.set("debug.tracing.mcc", Integer.toString(values.mcc));
		}
		if (values.mnc != 0) {
			SystemProperties.set("debug.tracing.mnc", Integer.toString(values.mnc));
		}
	}

	if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
		final LocaleList locales = values.getLocales();
		int bestLocaleIndex = 0;
		if (locales.size() > 1) {
			if (mSupportedSystemLocales == null) {
				mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
			}
			bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales));
		}
		SystemProperties.set("persist.sys.locale",
				locales.get(bestLocaleIndex).toLanguageTag());
		LocaleList.setDefault(locales, bestLocaleIndex);
	}

	mTempConfig.seq = increaseConfigurationSeqLocked();

	Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
	// TODO(multi-display): Update UsageEvents#Event to include displayId.
	mUsageStatsInternal.reportConfigurationChange(mTempConfig, mAmInternal.getCurrentUserId());

	// TODO: If our config changes, should we auto dismiss any currently showing dialogs?
	updateShouldShowDialogsLocked(mTempConfig);

	AttributeCache ac = AttributeCache.instance();
	if (ac != null) {
		ac.updateConfiguration(mTempConfig);
	}

	// Make sure all resources in our process are updated right now, so that anyone who is going
	// to retrieve resource values after we return will be sure to get the new ones. This is
	// especially important during boot, where the first config change needs to guarantee all
	// resources have that config before following boot code is executed.
	mSystemThread.applyConfigurationToResources(mTempConfig);

	if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
		final Message msg = PooledLambda.obtainMessage(
				ActivityTaskManagerService::sendPutConfigurationForUserMsg,
				this, userId, new Configuration(mTempConfig));
		mH.sendMessage(msg);
	}

	SparseArray<WindowProcessController> pidMap = mProcessMap.getPidMap();
	for (int i = pidMap.size() - 1; i >= 0; i--) {
		final int pid = pidMap.keyAt(i);
		final WindowProcessController app = pidMap.get(pid);
		ProtoLog.v(WM_DEBUG_CONFIGURATION, "Update process config of %s to new "
				+ "config %s", app.mName, mTempConfig);
		app.onConfigurationChanged(mTempConfig);
	}

	final Message msg = PooledLambda.obtainMessage(
			ActivityManagerInternal::broadcastGlobalConfigurationChanged,
			mAmInternal, changes, initLocale);
	mH.sendMessage(msg);

	Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RootConfigChange");
	// Update stored global config and notify everyone about the change.
	mRootWindowContainer.onConfigurationChanged(mTempConfig);
	Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);

	Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
	return changes;
}

调用所用进程对应WindowProcessControlleronConfigurationChanged方法 这个方法最终会通知到客户端应用的所有Activity、Service等。随后通过broadcastGlobalConfigurationChanged发送ACTION_CONFIGURATION_CHANGED广播最后调用mRootWindowContainer.onConfigurationChanged(mTempConfig);这个通知服务端RootWindowContainer及其所有子窗口进行configuration更新

客户端传递路线参考序列图最终调用ConfigurationControllerhandleConfigurationChanged

platform/frameworks/base/core/java/android/app/ConfigurationController.java

/**
 * Update the configuration to latest.
 * @param config The new configuration.
 * @param compat The new compatibility information.
 */
void handleConfigurationChanged(@Nullable Configuration config,
		@Nullable CompatibilityInfo compat) {
	int configDiff;
	boolean equivalent;

	// Get theme outside of synchronization to avoid nested lock.
	final Resources.Theme systemTheme = mActivityThread.getSystemContext().getTheme();
	final ContextImpl systemUiContext = mActivityThread.getSystemUiContextNoCreate();
	final Resources.Theme systemUiTheme =
			systemUiContext != null ? systemUiContext.getTheme() : null;
	synchronized (mResourcesManager) {
		if (mPendingConfiguration != null) {
			if (!mPendingConfiguration.isOtherSeqNewer(config)) {
				config = mPendingConfiguration;
				updateDefaultDensity(config.densityDpi);
			}
			mPendingConfiguration = null;
		}

		if (config == null) {
			return;
		}

		// This flag tracks whether the new configuration is fundamentally equivalent to the
		// existing configuration. This is necessary to determine whether non-activity callbacks
		// should receive notice when the only changes are related to non-public fields.
		// We do not gate calling {@link #performActivityConfigurationChanged} based on this
		// flag as that method uses the same check on the activity config override as well.
		equivalent = mConfiguration != null && (0 == mConfiguration.diffPublicOnly(config));

		if (DEBUG_CONFIGURATION) {
			Slog.v(TAG, "Handle configuration changed: " + config);
		}

		final Application app = mActivityThread.getApplication();
		final Resources appResources = app.getResources();
		mResourcesManager.applyConfigurationToResources(config, compat);
		updateLocaleListFromAppContext(app.getApplicationContext());

		if (mConfiguration == null) {
			mConfiguration = new Configuration();
		}
		if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
			return;
		}

		configDiff = mConfiguration.updateFrom(config);
		config = applyCompatConfiguration();
		HardwareRenderer.sendDeviceConfigurationForDebugging(config);

		if ((systemTheme.getChangingConfigurations() & configDiff) != 0) {
			systemTheme.rebase();
		}

		if (systemUiTheme != null
				&& (systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
			systemUiTheme.rebase();
		}
	}

	final ArrayList<ComponentCallbacks2> callbacks =
			mActivityThread.collectComponentCallbacks(false /* includeUiContexts */);

	freeTextLayoutCachesIfNeeded(configDiff);

	if (callbacks != null) {
		final int size = callbacks.size();
		for (int i = 0; i < size; i++) {
			ComponentCallbacks2 cb = callbacks.get(i);
			if (!equivalent) {
				performConfigurationChanged(cb, config);
			}
		}
	}
}

mResourcesManager.applyConfigurationToResources(config, compat);config更新resourceManager

configDiff = mConfiguration.updateFrom(config);config = applyCompatConfiguration();更新本地config让后通过performConfigurationChanged传导所有AcivityService

无论WindowProcessControlleronConfigurationChanged还是mRootWindowContainer.onConfigurationChanged 首先执行基类ConfigurationContaineronConfigurationChanged

platform/frameworks/base/services/core/java/com/android/server/wm/ConfigurationContainer.java

/**
 * Notify that parent config changed and we need to update full configuration.
 * @see #mFullConfiguration
 */
public void onConfigurationChanged(Configuration newParentConfig) {
	mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);
	resolveOverrideConfiguration(newParentConfig);
	mFullConfiguration.setTo(newParentConfig);
	// Do not inherit always-on-top property from parent, otherwise the always-on-top
	// property is propagated to all children. In that case, newly added child is
	// always being positioned at bottom (behind the always-on-top siblings).
	mFullConfiguration.windowConfiguration.unsetAlwaysOnTop();
	mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
	onMergedOverrideConfigurationChanged();
	if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
		// This depends on the assumption that change-listeners don't do
		// their own override resolution. This way, dependent hierarchies
		// can stay properly synced-up with a primary hierarchy's constraints.
		// Since the hierarchies will be merged, this whole thing will go away
		// before the assumption will be broken.
		// Inform listeners of the change.
		for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
			mChangeListeners.get(i).onRequestedOverrideConfigurationChanged(
					mResolvedOverrideConfiguration);
		}
	}
	for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
		mChangeListeners.get(i).onMergedOverrideConfigurationChanged(
				mMergedOverrideConfiguration);
	}
	for (int i = getChildCount() - 1; i >= 0; --i) {
		dispatchConfigurationToChild(getChildAt(i), mFullConfiguration);
	}
}

这个方法Configuration相关一些重要属性进行更新详述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值