公司内部的一个ROM,基于高通平台的,默认为手势导航,即设置-系统-默认选中手势导航,现在需要改成默认“三按钮”导航。
通过Settings的源码,在手动切换导航模式的时候会调用SystemNavigationGestureSettings.java的以下方法
`static void setCurrentSystemNavigationMode(IOverlayManager overlayManager, String key) {
String overlayPackage = NAV_BAR_MODE_GESTURAL_OVERLAY;
switch (key) {
case KEY_SYSTEM_NAV_GESTURAL:
overlayPackage = NAV_BAR_MODE_GESTURAL_OVERLAY;
break;
case KEY_SYSTEM_NAV_2BUTTONS:
overlayPackage = NAV_BAR_MODE_2BUTTON_OVERLAY;
break;
case KEY_SYSTEM_NAV_3BUTTONS:
overlayPackage = NAV_BAR_MODE_3BUTTON_OVERLAY;
break;
}
try {
overlayManager.setEnabledExclusiveInCategory(overlayPackage, USER_CURRENT);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}`
其中,NAV_BAR_MODE_GESTURAL_OVERLAY,NAV_BAR_MODE_2BUTTON_OVERLAY,NAV_BAR_MODE_3BUTTON_OVERLAY这三个常量代表三个overlay的包名,在源码里可以找到对应的apk
frameworks/base/packages/overlays/NavigationBarModeGesturalOverlay
frameworks/base/packages/overlays/NavigationBarMode2ButtonOverlay
frameworks/base/packages/overlays/NavigationBarMode3ButtonOverlay
setEnabledExclusiveInCategory就是告诉系统使用哪个overlay的资源,这里是切换的时候直接传入overlay的包名就可以了。但这里我们需要找到的是初始化的时候,系统使用的是哪个overlay的资源
未修改前,我们是默认手势导航的,刷完机,可以使用命令行:adb shell dumpsys overlay
查看当前系统所有overlay资源的使用情况
此处我们可以看到对应NavigationBarModeGesturalOverlay这个overlay的一些配置信息,其中一个重要的属性mIsEnabled为true,表示当前系统使用的是这个overlay包的资源,该文件里还能找到对应其他两个overlay的配置信息,他们的mIsEnabled都为false.
关于Overlay的机制,本文不详细展开,有兴趣的同学自己去跟源码,系统启动的时候,OverlayManagerService这个服务会被拉起,该服务会注册以下广播
private final class UserReceiver extends BroadcastReceiver {
522 @Override
523 public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
524 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
525 switch (intent.getAction()) {
526 case ACTION_USER_ADDED:
527 if (userId != UserHandle.USER_NULL) {
528 try {
529 traceBegin(TRACE_TAG_RRO, "OMS ACTION_USER_ADDED");
530 synchronized (mLock) {
531 updatePackageManagerLocked(mImpl.updateOverlaysForUser(userId));
532 }
533 } finally {
534 traceEnd(TRACE_TAG_RRO);
535 }
536 }
537 break;
538
539 case ACTION_USER_REMOVED:
540 if (userId != UserHandle.USER_NULL) {
541 try {
542 traceBegin(TRACE_TAG_RRO, "OMS ACTION_USER_REMOVED");
543 synchronized (mLock) {
544 mImpl.onUserRemoved(userId);
545 mPackageManager.forgetAllPackageInfos(userId);
546 }
547 } finally {
548 traceEnd(TRACE_TAG_RRO);
549 }
550 }
551 break;
552 default:
553 // do nothing
554 break;
555 }
556 }
557 }
用于监听overlay apk的安装,其中mImpl为OverlayManagerServiceImpl对象,初始化如下:
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
OverlayConfig.getSystemInstance(), getDefaultOverlayPackages());
在看看getDefaultOverlayPackages的实现:
private static String[] getDefaultOverlayPackages() {
346 final String str = SystemProperties.get(DEFAULT_OVERLAYS_PROP);
347 if (TextUtils.isEmpty(str)) {
348 return EmptyArray.STRING;
349 }
350
351 final ArraySet<String> defaultPackages = new ArraySet<>();
352 for (String packageName : str.split(";")) {
353 if (!TextUtils.isEmpty(packageName)) {
354 defaultPackages.add(packageName);
355 }
356 }
357 return defaultPackages.toArray(new String[defaultPackages.size()]);
358 }
其中,DEFAULT_OVERLAYS_PROP值如下
private static final String DEFAULT_OVERLAYS_PROP = "ro.boot.vendor.overlay.theme";
在回到该方法mImpl.updateOverlaysForUser(userId)
// Enable the default overlay if its category does not have a single overlay enabled.
212 for (final String defaultOverlay : mDefaultOverlays) {
213 try {
214 // OverlayConfig is the new preferred way to enable overlays by default. This legacy
215 // default enabled method was created before overlays could have a name specified.
216 // Only allow enabling overlays without a name using this mechanism.
217 final OverlayIdentifier overlay = new OverlayIdentifier(defaultOverlay);
218
219 final OverlayInfo oi = mSettings.getOverlayInfo(overlay, newUserId);
220 if (!enabledCategories.contains(oi.category)) {
221 Slog.w(TAG, "Enabling default overlay '" + defaultOverlay + "' for target '"
222 + oi.targetPackageName + "' in category '" + oi.category + "' for user "
223 + newUserId);
224 mSettings.setEnabled(overlay, newUserId, true);
225 if (updateState(oi, newUserId, 0)) {
226 CollectionUtils.add(updatedTargets,
227 new PackageAndUser(oi.targetPackageName, oi.userId));
228 }
229 }
230 } catch (OverlayManagerSettings.BadKeyException e) {
231 Slog.e(TAG, "Failed to set default overlay '" + defaultOverlay + "' for user "
232 + newUserId, e);
233 }
234 }
此处的mDefaultOverlays,就是刚刚从OverlayManagerService中通过构造方法传进来的这个值:
private static final String DEFAULT_OVERLAYS_PROP = “ro.boot.vendor.overlay.theme”,该方法内部调用mSettings.setEnabled把对应的overlay的mIsEnabled属性设为true,从而使系统用该overlay的资源,所以找到该属性赋值的地方,在源码里面全局搜索ro.boot.vendor.overlay.theme,会在vendor下面找到一个mk配置文件,给该属性赋了值
最后把com.android.internal.systemui.navbar.gestural这个包名改为三按钮导航的overlay包名com.android.internal.systemui.navbar.threebutton就可以了.
重新编译,见证奇迹的时刻…