1.三个虚拟键的显示初始化是在Frameworks下的SystemUI中。
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
// ================================================================================
// Constructing the view
// ================================================================================
protected PhoneStatusBarView makeStatusBarView() {
final Context context = mContext;
Resources res = context.getResources();
......
mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
mNotificationData.setHeadsUpManager(mHeadsUpManager);
if (MULTIUSER_DEBUG) {
mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
R.id.header_debug_info);
mNotificationPanelDebugText.setVisibility(View.VISIBLE);
}
try {
boolean showNav = mWindowManagerService.hasNavigationBar();//控制Navigationbar中是否显示3个虚拟按键
if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
if (showNav) {
mNavigationBarView =
(NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);//本布局中定义了3个虚拟键
mNavigationBarView.setDisabledFlags(mDisabled1);
mNavigationBarView.setBar(this);
mNavigationBarView.setOnVerticalChangedListener(
new NavigationBarView.OnVerticalChangedListener() {
@Override
public void onVerticalChanged(boolean isVertical) {
if (mAssistManager != null) {
mAssistManager.onConfigurationChanged();
}
mNotificationPanel.setQsScrimEnabled(!isVertical);
}
});
mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
checkUserAutohide(v, event);
return false;
}});
}
} catch (RemoteException ex) {
// no window manager? good luck with that
}
mAssistManager = new AssistManager(this, context);
2.由以上代码可知,3个虚拟键的显示与否取决于showNav,而这又取决于mWindowManagerService和它的方法hasNavigationBar()返回的值。而mWindowManagerService并非再本类中声明,而是从其父类中继承而来,并在父类中初始化。
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
public abstract class BaseStatusBar extends SystemUI implements
CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
RecentsComponent.Callbacks, ExpandableNotificationRow.ExpansionLogger,
NotificationData.Environment {
public static final String TAG = "StatusBar";
public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static final boolean MULTIUSER_DEBUG = false;
......
private UserManager mUserManager;
// UI-specific methods
/**
* Create all windows necessary for the status bar (including navigation, overlay panels, etc)
* and add them to the window manager.
*/
protected abstract void createAndAddWindows();
protected WindowManager mWindowManager;
protected IWindowManager mWindowManagerService;
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
3.在WindowManagerGlobal中获取IWindowManager实例。
/frameworks/base/core/java/android/view/WindowManagerGlobal.java
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));//
try {
sWindowManagerService = getWindowManagerService();
ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
} catch (RemoteException e) {
Log.e(TAG, "Failed to get WindowManagerService, cannot set animator scale", e);
}
}
return sWindowManagerService;
}
}
4.上面的sWindowManagerService也是IWindowManager声明的,可以看出上面方法中,sWindowManagerService递归从IWindowManager.Stub中获取实例。我们来看看IWindowManager中的声明。
/frameworks/base/core/java/android/view/IWindowManager.aidl
/**
* System private interface to the window manager.
*
* {@hide}
*/
interface IWindowManager
{
/**
* ===== NOTICE =====
* The first three methods must remain the first three methods. Scripts
* and tools rely on their transaction number to work properly.
*/
// This is used for debugging
boolean startViewServer(int port); // Transaction #1
boolean stopViewServer(); // Transaction #2
boolean isViewServerRunning(); // Transaction #3
......
/**
* Device has a software navigation bar (separate from the status bar).
*/
boolean hasNavigationBar();
......
}
5.回归到PhoneStatusBar中调用的hasNavigationBar()方法,正是IWindowManager接口中的,至于如何返回值怎么来决定,还得取决于实现此接口的方法实现。
6.经过查找,有两个类实现了IWindowManager接口,分别为IWindowManagerImpl和WindowManagerService,先看IWindowManagerImpl类。
/frameworks/base/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
public class IWindowManagerImpl implements IWindowManager {
private final Configuration mConfig;
private final DisplayMetrics mMetrics;
private final int mRotation;
private final boolean mHasNavigationBar;
public IWindowManagerImpl(Configuration config, DisplayMetrics metrics, int rotation,
boolean hasNavigationBar) {
mConfig = config;
mMetrics = metrics;
mRotation = rotation;
mHasNavigationBar = hasNavigationBar;//在IWindowManagerImpl初始化时传入
}
// custom API.
public DisplayMetrics getMetrics() {
return mMetrics;
}
// ---- implementation of IWindowManager that we care about ----
@Override
public int getRotation() throws RemoteException {
return mRotation;
}
@Override
public boolean hasNavigationBar() {
return mHasNavigationBar;//直接返回类属性
}
......
}
7.IWindowManagerImpl先看到这里,再看一下WindowManagerService类,它是个隐藏类,不对上层应用开放。
/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
static final String TAG = "WindowManager";
/// M: Enable/Disable WMS log
static boolean DEBUG = false;
static boolean DEBUG_ADD_REMOVE = false;
......
final WindowManagerPolicy mPolicy = MtkPhoneWindowUtility.makeNewWindowManager();//MTK
//final WindowManagerPolicy mPolicy = new PhoneWindowManager();//QCOM
@Override
public boolean hasNavigationBar() {
return mPolicy.hasNavigationBar();
}
......
}
8.从上方可以看出,最终使用的是PhoneWindowManager中实现的hasNavigationBar方法。而PhoneWindowManager实现了WindowManagerPolicy接口中的方法。
/local/sdb/talkback/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
package com.android.server.policy;
/**
* WindowManagerPolicy implementation for the Android phone UI. This
* introduces a new method suffix, Lp, for an internal lock of the
* PhoneWindowManager. This is used to protect some internal state, and
* can be acquired with either the Lw and Li lock held, so has the restrictions
* of both of those when held.
*/
public class PhoneWindowManager implements WindowManagerPolicy {
static final String TAG = "WindowManager";
/// M: runtime switch debug flags @{
static boolean DEBUG = false;
......
boolean mHasNavigationBar = false;
@Override
public void setInitialDisplaySize(Display display, int width, int height, int density) {
// This method might be called before the policy has been fully initialized
// or for other displays we don't care about.
if (mContext == null || display.getDisplayId() != Display.DEFAULT_DISPLAY) {
return;
}
mDisplay = display;
......
// SystemUI (status bar) layout policy
int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / density;
int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / density;
// Allow the navigation bar to move on non-square small devices (phones).
mNavigationBarCanMove = width != height && shortSizeDp < 600;
mHasNavigationBar = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);//首先给予一个默认值
// Allow a system property to override this. Used by the emulator.
// See also hasNavigationBar().
String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");//通过读取系统属性来覆盖默认值,厂家可以很容易通过定制系统属性来控制虚拟键显示与否
if ("1".equals(navBarOverride)) {
mHasNavigationBar = false;
} else if ("0".equals(navBarOverride)) {
mHasNavigationBar = true;
}
// For demo purposes, allow the rotation of the HDMI display to be controlled.
// By default, HDMI locks rotation to landscape.
if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
mDemoHdmiRotation = mPortraitRotation;
} else {
mDemoHdmiRotation = mLandscapeRotation;
}
mDemoHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
// For demo purposes, allow the rotation of the remote display to be controlled.
// By default, remote display locks rotation to landscape.
if ("portrait".equals(SystemProperties.get("persist.demo.remoterotation"))) {
mDemoRotation = mPortraitRotation;
} else {
mDemoRotation = mLandscapeRotation;
}
mDemoRotationLock = SystemProperties.getBoolean(
"persist.demo.rotationlock", false);
// Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per
// http://developer.android.com/guide/practices/screens_support.html#range
mForceDefaultOrientation = longSizeDp >= 960 && shortSizeDp >= 720 &&
res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
// For debug purposes the next line turns this feature off with:
// $ adb shell setprop config.override_forced_orient true
// $ adb shell wm size reset
!"true".equals(SystemProperties.get("config.override_forced_orient"));
}
// Use this instead of checking config_showNavigationBar so that it can be consistently
// overridden by qemu.hw.mainkeys in the emulator.
@Override
public boolean hasNavigationBar() {
return mHasNavigationBar;//返回的boolean值
}
......
}
9.如此,需要跟踪系统属性从那里来。经过查询,该系统属性是编译时加入。
/device/{XXXX}/{Project_name}/system.prop
# temporary enables NAV bar (soft keys)
qemu.hw.mainkeys=0