书接上文,本章分析 CarOccupantZoneService 在init方法中做了什么,下文都以=============
作为分割线,分章节介绍
@Override
public void init() {
// This does not require connection as binder will be passed directly.
1.====================================================================
Car car = new Car(mContext, /* service= */null, /* handler= */ null);
CarInfoManager infoManager = new CarInfoManager(car, CarLocalServices.getService(
CarPropertyService.class));
int driverSeat = infoManager.getDriverSeat();
2.=====================================================================
synchronized (mLock) {
mDriverSeat = driverSeat;
parseOccupantZoneConfigsLocked();
parseDisplayConfigsLocked();
3.=====================================================================
handleActiveDisplaysLocked();
handleAudioZoneChangesLocked();
handleUserChangesLocked();
}
CarUserService userService = CarLocalServices.getService(CarUserService.class);
userService.addUserLifecycleListener(mUserLifecycleListener);
userService.addPassengerCallback(mPassengerCallback);
mDisplayManager.registerDisplayListener(mDisplayListener,
new Handler(Looper.getMainLooper()));
CarUserService.ZoneUserBindingHelper helper = new CarUserService.ZoneUserBindingHelper() {
@Override
@NonNull
public List<OccupantZoneInfo> getOccupantZones(@OccupantTypeEnum int occupantType) {
List<OccupantZoneInfo> zones = new ArrayList<OccupantZoneInfo>();
for (OccupantZoneInfo ozi : getAllOccupantZones()) {
if (ozi.occupantType == occupantType) {
zones.add(ozi);
}
}
return zones;
}
@Override
public boolean assignUserToOccupantZone(@UserIdInt int userId, int zoneId) {
// Check if the user is already assigned to the other zone.
synchronized (mLock) {
for (int i = 0; i < mActiveOccupantConfigs.size(); ++i) {
OccupantConfig config = mActiveOccupantConfigs.valueAt(i);
if (config.userId == userId && zoneId != mActiveOccupantConfigs.keyAt(i)) {
Slogf.w(TAG, "cannot assign user to two different zone simultaneously");
return false;
}
}
OccupantConfig zoneConfig = mActiveOccupantConfigs.get(zoneId);
if (zoneConfig == null) {
Slogf.w(TAG, "cannot find the zone(%d)", zoneId);
return false;
}
if (zoneConfig.userId != UserHandle.USER_NULL && zoneConfig.userId != userId) {
Slogf.w(TAG, "other user already occupies the zone(%d)", zoneId);
return false;
}
zoneConfig.userId = userId;
return true;
}
}
@Override
public boolean unassignUserFromOccupantZone(@UserIdInt int userId) {
synchronized (mLock) {
for (int i = 0; i < mActiveOccupantConfigs.size(); ++i) {
OccupantConfig config = mActiveOccupantConfigs.valueAt(i);
if (config.userId == userId) {
config.userId = UserHandle.USER_NULL;
break;
}
}
return true;
}
}
@Override
public boolean isPassengerDisplayAvailable() {
for (OccupantZoneInfo ozi : getAllOccupantZones()) {
if (getDisplayForOccupant(ozi.zoneId,
CarOccupantZoneManager.DISPLAY_TYPE_MAIN) != Display.INVALID_DISPLAY
&& ozi.occupantType != CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) {
return true;
}
}
return false;
}
};
userService.setZoneUserBindingHelper(helper);
}
-
首先创建了一个Car 对象,作为 CarInfoManager构造函数的参数,我们先看看CarInfoManager的构造函数:
public final class CarInfoManager extends CarManagerBase { ... public CarInfoManager(Car car, IBinder service) { super(car); //CarInfoManager的基类是CarManagerBase,父类的构造方法只是将父类成员变量mCar指向了car对象 //这里我们先不关注Car中做了什么 ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service); mCarPropertyMgr = new CarPropertyManager(car, mCarPropertyService); } ... }
public abstract class CarManagerBase { protected final Car mCar; public CarManagerBase(Car car) { mCar = car; } protected Context getContext() { return mCar.getContext(); } protected Handler getEventHandler() { return mCar.getEventHandler(); } protected <T> T handleRemoteExceptionFromCarService(RemoteException e, T returnValue) { return mCar.handleRemoteExceptionFromCarService(e, returnValue); } protected void handleRemoteExceptionFromCarService(RemoteException e) { mCar.handleRemoteExceptionFromCarService(e); } /** * Handles runtime and remote exception from CarService. */ protected <T> T handleExceptionFromCarService(Exception e, T returnValue) { if (e instanceof RemoteException) { return handleRemoteExceptionFromCarService((RemoteException) e, returnValue); } if (e instanceof RuntimeException) { Log.w(TAG_CAR, "Car service threw Runtime Exception.", e); return returnValue; } // exception should be either runtime or remote exception Log.wtf(TAG_CAR, "Car service threw Exception.", e); return returnValue; } /** * Handle disconnection of car service (=crash). As car service has crashed already, this call * should only clean up any listeners / callbacks passed from client. Clearing object passed * to car service is not necessary as car service has crashed. Note that Car*Manager will not * work any more as all binders are invalid. Client should re-create all Car*Managers when * car service is restarted. */ protected abstract void onCarDisconnected(); }
在CarOccupantZoneService中:
public class ICarImpl extends ICar.Stub { ... ICarImpl(Context serviceContext, IVehicle vehicle, SystemInterface systemInterface, String vehicleInterfaceName, @Nullable CarUserService carUserService, @Nullable CarWatchdogService carWatchdogService, @Nullable ICarPowerPolicySystemNotification powerPolicyDaemon) { //... //根据我们第一章的分析,ICarImpl在构造方法中创建了CarOccupantZoneService,CarUserService //并且以类Class为键将它们加入到了CarLocalServices的一个静态成员sLocalServiceObjects中, //同时在这个步骤中,它也创建了CarPropertyService并且加入到了sLocalServiceObjects mCarPropertyService = constructWithTrace( t, CarPropertyService.class, () -> new CarPropertyService(serviceContext, mHal.getPropertyHal())); //... allServices.add(mCarPropertyService); //... } } public final class CarOccupantZoneService extends ICarOccupantZone.Stub implements CarServiceBase { //... @Override public void init() { Car car = new Car(mContext, /* service= */null, /* handler= */ null); //这里从CarLocalServices获取到CarPropertyService**用作CarInfoManager的构造函数参数 CarInfoManager infoManager = new CarInfoManager(car, CarLocalServices.getService( CarPropertyService.class)); //通过getDriverSeat方法,获取系统中配置的司机位置 int driverSeat = infoManager.getDriverSeat(); //... } //... }
public final class CarInfoManager extends CarManagerBase { ... public CarInfoManager(Car car, IBinder service) { super(car); ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service); //CarPropertyManager也是CarManagerBase的子类, //这个类的功能主要是监听了一些事件的变化,提供给外部注册事件, //再是使用CarPropertyServic的一些get方法进行binder通信,这里我们先不关注它 mCarPropertyMgr = new CarPropertyManager(car, mCarPropertyService); } ... public @VehicleAreaSeat.Enum int getDriverSeat() { //最终是通过CarPropertyService和硬件抽象层通信,获取到驾驶舱司机的位置 //关于HIDL的知识这里就不描述了,该部分的逻辑代码主要在/hardware/interfaces/automotive/vehicle/2.0 //其中VehicleHalManager是入口,主要是实现在EmulatedVehicleHal中 return getPropertyWithDefaultValue(Integer.class, BASIC_INFO_DRIVER_SEAT, VehicleAreaSeat.SEAT_UNKNOWN); } ... }
-
调用一些列方法,从系统配置中读取一些参数用作初始化。
public final class CarOccupantZoneService extends ICarOccupantZone.Stub implements CarServiceBase { //... @Override public void init() { //... synchronized (mLock) { //将刚才获取到的座位号保存到成员变量mDriverSeat mDriverSeat = driverSeat; //对packages/services/Car/service/res/values/config.xml中的config_occupant_zones解析 parseOccupantZoneConfigsLocked(); //对packages/services/Car/service/res/values/config.xml中的config_occupant_display_mapping解析 parseDisplayConfigsLocked(); ··· } //... } //... }
-
parseOccupantZoneConfigsLocked:
以下为
packages/services/Car/service/res/values/config.xml
中config_occupant_zones
的内容,上面注释部分给了一个模板,occupantType
中DRIVER
代表司机,FRONT_PASSENGER
代表副驾,REAR_PASSENGER
代表后排乘客,seatRow
中的数字代表第几排,seatSide
代表每一排的位置。<!-- Lists all occupant (= driver + passenger) zones available in the car. Some examples are: <item>occupantZoneId=0,occupantType=DRIVER,seatRow=1,seatSide=driver</item> <item>occupantZoneId=1,occupantType=FRONT_PASSENGER,seatRow=1,seatSide=oppositeDriver</item> <item>occupantZoneId=2,occupantType=REAR_PASSENGER,seatRow=2,seatSide=left</item> <item>occupantZoneId=3,occupantType=REAR_PASSENGER,seatRow=2,seatSide=right</item> occupantZoneId: Unique unsigned integer id to represent each passenger zone. Each zone should have different id. occupantType: Occupant type for the display. Use * part from CarOccupantZoneManager.OCCUPANT_TYPE_* like DRIVER, FRONT_PASSENGER, REAR_PASSENGER and etc. seatRow: Integer telling which row the seat is located. Row 1 is for front seats. seatSide: left/center/right for known side. Or can use driver/center/oppositeDriver to handle both right-hand driving and left-hand driving in one place. If car's RHD / LHD is not specified, LHD will be assumed and driver side becomes left. --> <!-- 源码是这样的,为了做更好的多用户配置示例,我改成了下面没注释的样子 <string-array translatable="false" name="config_occupant_zones"> <item>occupantZoneId=0,occupantType=DRIVER,seatRow=1,seatSide=driver</item> </string-array> --> <string-array translatable="false" name="config_occupant_zones"> <item>occupantZoneId=0,occupantType=DRIVER,seatRow=1,seatSide=driver</item> <item>occupantZoneId=1,occupantType=REAR_PASSENGER,seatRow=2,seatSide=right</item> <item>occupantZoneId=2,occupantType=REAR_PASSENGER,seatRow=2,seatSide=left</item> </string-array>
以下为
parseOccupantZoneConfigsLocked
的内容,主要是对mOccupantsConfig
成员变量进行填充,mOccupantsConfig
保存了车内人员位置和类型的映射关系。private void parseOccupantZoneConfigsLocked() { final Resources res = mContext.getResources(); // examples: // <item>occupantZoneId=0,occupantType=DRIVER,seatRow=1,seatSide=driver</item> // <item>occupantZoneId=1,occupantType=FRONT_PASSENGER,seatRow=1, // searSide=oppositeDriver</item> boolean hasDriver = false; //给司机位置一个初始值 int driverSeat = getDriverSeat(); int driverSeatSide = VehicleAreaSeat.SIDE_LEFT; // default LHD : Left Hand Drive if (driverSeat == VehicleAreaSeat.SEAT_ROW_1_RIGHT) { driverSeatSide = VehicleAreaSeat.SIDE_RIGHT; } int maxZoneId = OccupantZoneInfo.INVALID_ZONE_ID; //开始解析config_occupant_zones for (String config : res.getStringArray(R.array.config_occupant_zones)) { Slogf.w(TAG, "parseOccupantZoneConfigsLocked config = "+config); int zoneId = OccupantZoneInfo.INVALID_ZONE_ID; int type = CarOccupantZoneManager.OCCUPANT_TYPE_INVALID; int seatRow = 0; // invalid row int seatSide = VehicleAreaSeat.SIDE_LEFT; String[] entries = config.split(","); for (String entry : entries) { String[] keyValuePair = entry.split("="); if (keyValuePair.length != 2) { throwFormatErrorInOccupantZones("No key/value pair:" + entry); } //keyValuePair[0]为键,即表格中的occupantZoneId、occupantType、seatRow、seatSide switch (keyValuePair[0]) { case "occupantZoneId": //根据表格内容,occupantZoneId应填入一个整形,所以把它转化为int,赋值给zoneId zoneId = Integer.parseInt(keyValuePair[1]); break; case "occupantType": //当key为occupantZoneId时,有DRIVER、FRONT_PASSENGER、REAR_PASSENGER三个分支 //分别将他们对应转换为CarOccupantZoneManager中的常量,赋值给type switch (keyValuePair[1]) { case "DRIVER": type = CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER; break; case "FRONT_PASSENGER": type = CarOccupantZoneManager.OCCUPANT_TYPE_FRONT_PASSENGER; break; case "REAR_PASSENGER": type = CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER; break; default: throwFormatErrorInOccupantZones("Unrecognized type:" + entry); break; } break; case "seatRow": //将作为排数赋值给seatRow seatRow = Integer.parseInt(keyValuePair[1]); break; case "seatSide": //seatSide,driver、oppositeDriver、left、center、right //分别将他们对应转换为VehicleAreaSeat中的常量,赋值给seatSide switch (keyValuePair[1]) { case "driver": seatSide = driverSeatSide; break; case "oppositeDriver": seatSide = -driverSeatSide; break; case "left": seatSide = VehicleAreaSeat.SIDE_LEFT; break; case "center": seatSide = VehicleAreaSeat.SIDE_CENTER; break; case "right": seatSide = VehicleAreaSeat.SIDE_RIGHT; break; default: throwFormatErrorInOccupantZones("Unregognized seatSide:" + entry); break; } break; default: throwFormatErrorInOccupantZones("Unrecognized key:" + entry); break; } } //对座位ID进行合法性检测,INVALID_ZONE_ID的值为-1; if (zoneId == OccupantZoneInfo.INVALID_ZONE_ID) { throwFormatErrorInOccupantZones("Missing zone id:" + config); } //保存最大的zoneId if (zoneId > maxZoneId) { maxZoneId = zoneId; } //对座位类型进行合法性检测,OCCUPANT_TYPE_INVALID的值为-1; if (type == CarOccupantZoneManager.OCCUPANT_TYPE_INVALID) { throwFormatErrorInOccupantZones("Missing type:" + config); } //对司机类型的合法性进行检测,因为一个车只有一个司机 if (type == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) { if (hasDriver) { throwFormatErrorInOccupantZones("Multiple driver:" + config); } else { hasDriver = true; mDriverZoneId = zoneId; } } //同理对座位进行合法性检测 int seat = VehicleAreaSeat.fromRowAndSide(seatRow, seatSide); if (seat == VehicleAreaSeat.SEAT_UNKNOWN) { throwFormatErrorInOccupantZones("Invalid seat:" + config); } //每一个array中的元素解析出来之后,新建一个对象OccupantZoneInfo,将zoneId, type, seat保存起来 OccupantZoneInfo info = new OccupantZoneInfo(zoneId, type, seat); //如果定义了重复的ID,那么就报错 if (mOccupantsConfig.contains(zoneId)) { throwFormatErrorInOccupantZones("Duplicate zone id:" + config); } //以zoneId为key将新建的OccupantZoneInfo保存到mOccupantsConfig中 mOccupantsConfig.put(zoneId, info); } //根据前面对司机类型的判断如果hasDriver==false会报错,说明这种情况是在xml中没有配置,for循环没有进行, //那么就手动创建一个司机 if (!hasDriver) { maxZoneId++; mDriverZoneId = maxZoneId; Slogf.w(TAG, "No driver zone, add one:%d", mDriverZoneId); OccupantZoneInfo info = new OccupantZoneInfo(mDriverZoneId, CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER, getDriverSeat()); mOccupantsConfig.put(mDriverZoneId, info); } }
以下为
packages/services/Car/service/res/values/config.xml
中config_occupant_display_mapping
的内容,上面注释部分给了一个模板,displayPort
代表物理屏幕的端口,displayUniqueId
代表虚拟屏幕的id,displayType
屏幕类型,occupantZoneId
是每个屏幕对应在哪个座位。<!-- Specifies configuration of displays in system telling its usage / type and assigned occupant. DEFAULT_DISPLAY, if assigned here, should be always assigned to the DRIVER zone. Some examples are: <item>displayPort=0,displayType=MAIN,occupantZoneId=0</item> <item>displayPort=1,displayType=INSTRUMENT_CLUSTER,occupantZoneId=0</item> <item>displayPort=2,displayType=MAIN,occupantZoneId=1</item> <item>displayPort=3,displayType=MAIN,occupantZoneId=2</item> <item>displayUniqueId=virtual:com.example:MainD,displayType=MAIN,occupantZoneId=3</item> NOTE: each item should have displayPort or displayUniqueId, if it has both, displayPort will be used. displayPort: Unique Port id for the physical display. displayUniqueId: Unique Id for the display. The unique id of the virtual display will be the form of 'virtual:<PACKAGE>:<ID>'. displayType: Display type for the display. Use * part from CarOccupantZoneManager.DISPLAY_TYPE_* like MAIN, INSTRUMENT_CLUSTER and etc. occupantZoneId: occupantZoneId specified from config_occupant_zones. --> <string-array translatable="false" name="config_occupant_display_mapping"> </string-array>
以下为
parseDisplayConfigsLocked
的内容,起主要是填充成员变量mDisplayPortConfigs
和mDisplayUniqueIdConfigs
,mDisplayPortConfigs
和mDisplayUniqueIdConfigs
主要保存了座位号和屏幕的映射关系,其中物理屏幕保存到了mDisplayPortConfigs
,虚拟屏幕保存到了mDisplayUniqueIdConfigs
private void parseDisplayConfigsLocked() { final Resources res = mContext.getResources(); // examples: // <item>displayPort=0,displayType=MAIN,occupantZoneId=0</item> // <item>displayPort=1,displayType=INSTRUMENT_CLUSTER,occupantZoneId=0</item> for (String config : res.getStringArray(R.array.config_occupant_display_mapping)) { Slogf.w(TAG, "parseDisplayConfigsLocked config = "+config); //读取config_occupant_display_mapping中的值, //和上边儿的parseOccupantZoneConfigsLocked同理,通过switch进行赋值填充 //先将中间变量赋予一个初值 int port = INVALID_PORT; String uniqueId = null; int type = CarOccupantZoneManager.DISPLAY_TYPE_UNKNOWN; int zoneId = OccupantZoneInfo.INVALID_ZONE_ID; String[] entries = config.split(","); for (String entry : entries) { String[] keyValuePair = entry.split("="); if (keyValuePair.length != 2) { throwFormatErrorInDisplayMapping("No key/value pair:" + entry); } switch (keyValuePair[0]) { case "displayPort": //根据配置解析给port赋值 port = Integer.parseInt(keyValuePair[1]); break; case "displayUniqueId": //根据配置解析给displayUniqueId赋值 uniqueId = keyValuePair[1]; break; case "displayType": //根据配置解析给displayType赋值 switch (keyValuePair[1]) { case "MAIN": type = CarOccupantZoneManager.DISPLAY_TYPE_MAIN; break; case "INSTRUMENT_CLUSTER": type = CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER; break; case "HUD": type = CarOccupantZoneManager.DISPLAY_TYPE_HUD; break; case "INPUT": type = CarOccupantZoneManager.DISPLAY_TYPE_INPUT; break; case "AUXILIARY": type = CarOccupantZoneManager.DISPLAY_TYPE_AUXILIARY; break; default: throwFormatErrorInDisplayMapping( "Unrecognized display type:" + entry); break; } break; case "occupantZoneId": //根据配置解析给zoneId赋值 zoneId = Integer.parseInt(keyValuePair[1]); break; default: throwFormatErrorInDisplayMapping("Unrecognized key:" + entry); break; } } //进行一些列检查中间变量的是否成功正确赋值 // Now check validity if (port == INVALID_PORT && uniqueId == null) { throwFormatErrorInDisplayMapping( "Missing or invalid displayPort and displayUniqueId:" + config); } if (type == CarOccupantZoneManager.DISPLAY_TYPE_UNKNOWN) { throwFormatErrorInDisplayMapping("Missing or invalid displayType:" + config); } if (zoneId == OccupantZoneInfo.INVALID_ZONE_ID) { throwFormatErrorInDisplayMapping("Missing or invalid occupantZoneId:" + config); } //检查解析出来的zoneId和之前parseOccupantZoneConfigsLocked中mOccupantsConfig添加的成员是否有对应关系 //config_occupant_display_mapping中定义的乘员ID必须包含在config_occupant_zones if (!mOccupantsConfig.contains(zoneId)) { throwFormatErrorInDisplayMapping( "Missing or invalid occupantZoneId:" + config); } //新建一个对象DisplayConfig DisplayConfig displayConfig = new DisplayConfig(type, zoneId); if (port != INVALID_PORT) { //检查是否重复添加了一个屏幕 if (mDisplayPortConfigs.contains(port)) { throwFormatErrorInDisplayMapping("Duplicate displayPort:" + config); } Slogf.w(TAG, "mDisplayPortConfigs put port " + port); //将DisplayConfig添加到mDisplayPortConfigs中,以屏幕端口为Key mDisplayPortConfigs.put(port, displayConfig); } else { //检查并且将DisplayConfig添加到mDisplayUniqueIdConfigs中 if (mDisplayUniqueIdConfigs.containsKey(uniqueId)) { throwFormatErrorInDisplayMapping("Duplicate displayUniqueId:" + config); } mDisplayUniqueIdConfigs.put(uniqueId, displayConfig); } } Display defaultDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); //获取默认的屏幕配置,通过findDisplayConfigForDisplayLocked方法,看到是通过获取端口之后在 mDisplayPortConfigs中查询,如果没有,则返回null //或者说config.xml中config_occupant_display_mapping没有配置,for循环没有进行,所以说mDisplayPortConfigs中没有port对应的值 //那么将给驾驶员分配一个显示区域。 if (findDisplayConfigForDisplayLocked(defaultDisplay) == null) { Slogf.w(TAG, "No default display configuration, will assign to driver zone"); mDisplayUniqueIdConfigs.put(defaultDisplay.getUniqueId(), new DisplayConfig(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, mDriverZoneId)); } } @Nullable private DisplayConfig findDisplayConfigForDisplayLocked(Display display) { int portAddress = getPortAddress(display); Slogf.i(TAG, "findDisplayConfigForDisplayLocked portAddress = " + portAddress + " display id = " + display.getDisplayId() + " UniqueId = " + display.getUniqueId()); if (portAddress != INVALID_PORT) { DisplayConfig config = mDisplayPortConfigs.get(portAddress); if (config != null) { return config; } } return mDisplayUniqueIdConfigs.get(display.getUniqueId()); } private int getPortAddress(Display display) { //获取一个地址,并且这个地址是物理地址,否则返回INVALID_PORT DisplayAddress address = display.getAddress(); if (address instanceof DisplayAddress.Physical) { DisplayAddress.Physical physicalAddress = (DisplayAddress.Physical) address; if (physicalAddress != null) { return physicalAddress.getPort(); } } return INVALID_PORT; }
-
-
handleActiveDisplaysLocked
和handleUserChangesLocked
主要是将座位号、用户ID、和显示屏幕的映射关系关联起来。-
handleActiveDisplaysLocked
将zoneId和OccupantConfig的对应关系匹配://OccupantConfig 顾名思义是占用者, //内部成员变量 userId,代表用户ID, //displayInfos代表用户ID对应的显示器信息,由此看出其设计用户和屏幕是一对多的关系 static class OccupantConfig { public int userId = UserHandle.USER_NULL; public final ArrayList<DisplayInfo> displayInfos = new ArrayList<>(); public int audioZoneId = CarAudioManager.INVALID_AUDIO_ZONE; @Override public String toString() { // do not include type as this is only used for dump StringBuilder b = new StringBuilder(128); b.append("{userId="); b.append(userId); b.append(" displays="); for (int i = 0; i < displayInfos.size(); i++) { b.append(displayInfos.get(i).toString()); } b.append(" audioZoneId="); if (audioZoneId != CarAudioManager.INVALID_AUDIO_ZONE) { b.append(audioZoneId); } else { b.append("none"); } b.append("}"); return b.toString(); } }
//mActiveOccupantConfigs存储的是座位号对应的占用者OccupantConfig信息 private final SparseArray<OccupantConfig> mActiveOccupantConfigs = new SparseArray<>(); private void handleActiveDisplaysLocked() { mActiveOccupantConfigs.clear(); boolean hasDefaultDisplayConfig = false; //从DisplayManager中获取遍历所有的display for (Display display : mDisplayManager.getDisplays()) { DisplayConfig displayConfig = findDisplayConfigForDisplayLocked(display); if (displayConfig == null) { Slogf.w(TAG, "Display id:%d does not have configurations", display.getDisplayId()); continue; } if (display.getDisplayId() == Display.DEFAULT_DISPLAY) { if (displayConfig.occupantZoneId != mDriverZoneId) { throw new IllegalStateException( "Default display should be only assigned to driver zone"); } hasDefaultDisplayConfig = true; } //建立座位ID和OccupantConfig的映射关系 addDisplayInfoToOccupantZoneLocked(displayConfig.occupantZoneId, new DisplayInfo(display, displayConfig.displayType)); } if (!hasDefaultDisplayConfig) { // This shouldn't happen, since we added the default display config in // parseDisplayConfigsLocked(). throw new IllegalStateException("Default display not assigned"); } } private void addDisplayInfoToOccupantZoneLocked(int zoneId, DisplayInfo info) { OccupantConfig occupantConfig = mActiveOccupantConfigs.get(zoneId); if (occupantConfig == null) { occupantConfig = new OccupantConfig(); //这里只确认了座位和显示的对应关系,但是对userId并没有赋值,在后面handleUserChangesLocked赋值userId mActiveOccupantConfigs.put(zoneId, occupantConfig); Slogf.w(TAG, "addDisplayInfoToOccupantZoneLocked zoneId = " + zoneId); } occupantConfig.displayInfos.add(info); }
-
handleUserChangesLocked
找到驾驶员的zoneId,将当前用户userId设置到对应的OccupantConfig的userIdprivate void handleUserChangesLocked() { //获取当前用户 int driverUserId = getCurrentUser(); /* mEnableProfileUserAssignmentForMultiDisplay在CarOccupantZoneService的构造函数中。 赋值为R.bool.enableProfileUserAssignmentForMultiDisplay 在packages/services/Car/service/res/values/config.xml中 大致意思就是是否开启一个用户对应多个显示器的功能 <!-- Enable profile user assignment per each CarOccupantZone for per display android user assignments. This feature is still experimental. --> <bool name="enableProfileUserAssignmentForMultiDisplay" translatable="false">true</bool> mEnableProfileUserAssignmentForMultiDisplay是false,所以先不管*/ if (mEnableProfileUserAssignmentForMultiDisplay) { updateEnabledProfilesLocked(driverUserId); } //将所有的OccupantConfig的userId先填充为驾驶员用户ID driverUserId for (int i = 0; i < mActiveOccupantConfigs.size(); ++i) { int zoneId = mActiveOccupantConfigs.keyAt(i); OccupantConfig config = mActiveOccupantConfigs.valueAt(i); //先以mEnableProfileUserAssignmentForMultiDisplay = false 来分析逻辑,mProfileUsers此时是空的 //所以循环的时候所有OccupantConfig的userId都被设置为了driverUserId // mProfileUsers empty if not supported if (mProfileUsers.contains(config.userId)) { Slogf.i(TAG, "Profile user:%d already assigned for occupant zone:%d", config.userId, zoneId); } else { config.userId = driverUserId; } } }
/* * 简单看看当mEnableProfileUserAssignmentForMultiDisplay = true 的时候,从UserManager获取由当前用户配置并且开启的子用户,然后把所有子用户存储到mProfileUsers中。 */ private void updateEnabledProfilesLocked(int userId) { mProfileUsers.clear(); List<UserInfo> profileUsers = mUserManager.getEnabledProfiles(userId); for (UserInfo userInfo : profileUsers) { if (userInfo.id != userId) { mProfileUsers.add(userInfo.id); } } Slogf.i(TAG, "userId = " + userId + " mProfileUsers = " + mProfileUsers); } /* 其逻辑主要存在于assignProfileUserToOccupantZone方法中,但是此方法只有CarShellCommand中使用,暂不在系统中使用 */ public boolean assignProfileUserToOccupantZone(int occupantZoneId, int userId) { enforcePermission(android.Manifest.permission.MANAGE_USERS); if (!mEnableProfileUserAssignmentForMultiDisplay) { throw new IllegalStateException("feature not enabled"); } int currentUser = getCurrentUser(); synchronized (mLock) { if (occupantZoneId == mDriverZoneId) { throw new IllegalArgumentException("Driver zone cannot have profile user"); } updateEnabledProfilesLocked(currentUser); if (!mProfileUsers.contains(userId) && userId != UserHandle.USER_NULL) { // current user can change while this call is happening, so return false rather // than throwing exception Slogf.w(TAG, "Invalid profile user id: %d", userId); return false; } if (!mUserManager.isUserRunning(userId)) { Slogf.w(TAG, "User%d is not running.", userId); return false; } OccupantConfig config = mActiveOccupantConfigs.get(occupantZoneId); if (config == null) { throw new IllegalArgumentException("Invalid occupantZoneId:" + occupantZoneId); } if (config.userId == userId && userId != UserHandle.USER_NULL) { Slogf.w(TAG, "assignProfileUserToOccupantZone zone:%d already set to user:%", occupantZoneId, userId); return true; } if (userId == UserHandle.USER_NULL) { Slogf.w(TAG, "config.userId = currentUser = " + currentUser + " occupantZoneId = " + occupantZoneId); config.userId = currentUser; } else { Slogf.w(TAG, "config.userId = userId = " + userId + " occupantZoneId = " + occupantZoneId); config.userId = userId; } } sendConfigChangeEvent(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER); return true; }
-
至此,mActiveOccupantConfigs就装配完成,座位号zoneId
- 占用信息OccupantConfig
之间的对应关系就确定,而OccupantConfig
中又存储了用户userId
和displayInfos
的对应关系