Andorid 12 CarUserService,CarOccupantZoneService 多屏幕流程分析(二)

书接上文,本章分析 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);
    }
  1. 首先创建了一个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);
        }
        ...
    }
    
  2. 调用一些列方法,从系统配置中读取一些参数用作初始化。

    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.xmlconfig_occupant_zones的内容,上面注释部分给了一个模板,occupantTypeDRIVER代表司机,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.xmlconfig_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的内容,起主要是填充成员变量mDisplayPortConfigsmDisplayUniqueIdConfigs,mDisplayPortConfigsmDisplayUniqueIdConfigs主要保存了座位号和屏幕的映射关系,其中物理屏幕保存到了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;
          }
      
  3. handleActiveDisplaysLockedhandleUserChangesLocked主要是将座位号、用户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的userId

          private 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中又存储了用户userIddisplayInfos的对应关系

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容