1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <hdi_session.h>
17 #include <cerrno>
18 #include <mutex>
19 #include "display_common.h"
20 #include "display_device.h"
21 #include "display_layer.h"
22 #include "hdi_netlink_monitor.h"
23
24 namespace OHOS {
25 namespace HDI {
26 namespace DISPLAY {
GetInstance()27 HdiSession &HdiSession::GetInstance()
28 {
29 static HdiSession instance;
30 static std::once_flag once;
31 std::call_once(once, [&]() { instance.Init(); });
32 return instance;
33 }
34
Init()35 void HdiSession::Init()
36 {
37 DISPLAY_DEBUGLOG();
38 mHdiDevices = HdiDeviceInterface::DiscoveryDevice();
39 DISPLAY_DEBUGLOG("devices size %{public}zd", mHdiDevices.size());
40 mHdiDisplays.clear();
41 for (auto device : mHdiDevices) {
42 auto displays = device->DiscoveryDisplay();
43 /* Register the connectors instead of display device.
44 * There are several connectors in one display device
45 * in rockchip platform.
46 */
47 for (auto display : displays) {
48 mHdiDisplays[display.first] = display.second;
49 }
50 }
51 mNetLinkMonitor = std::make_shared<HdiNetLinkMonitor>();
52 mNetLinkMonitor->Init();
53 }
54
HandleHotplug(bool plugIn)55 void HdiSession::HandleHotplug(bool plugIn)
56 {
57 for (auto device : mHdiDevices) {
58 for (auto displayMap : mHdiDisplays) {
59 auto display = displayMap.second;
60 auto isSuccess = device->HandleHotplug(display->GetId(), plugIn);
61 if (isSuccess == true) {
62 DoHotPlugCallback(display->GetId(), plugIn);
63 }
64 }
65 }
66 }
67
RegHotPlugCallback(HotPlugCallback callback, void *data)68 int32_t HdiSession::RegHotPlugCallback(HotPlugCallback callback, void *data)
69 {
70 DISPLAY_CHK_RETURN((callback == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("the callback is nullptr"));
71 mHotPlugCallBacks.emplace(callback, data);
72 for (auto displayMap : mHdiDisplays) {
73 auto display = displayMap.second;
74 if (display->IsConnected()) {
75 DoHotPlugCallback(display->GetId(), true);
76 }
77 }
78 return DISPLAY_SUCCESS;
79 }
80
DoHotPlugCallback(uint32_t devId, bool connect)81 void HdiSession::DoHotPlugCallback(uint32_t devId, bool connect)
82 {
83 DISPLAY_DEBUGLOG();
84 for (const auto &callback : mHotPlugCallBacks) {
85 callback.first(devId, connect, callback.second);
86 }
87 }
88 } // OHOS
89 } // HDI
90 } // DISPLAY
91
92 using namespace OHOS::HDI::DISPLAY;
RegHotPlugCallback(HotPlugCallback callback, void *data)93 static int32_t RegHotPlugCallback(HotPlugCallback callback, void *data)
94 {
95 DISPLAY_DEBUGLOG();
96 HdiSession::GetInstance().RegHotPlugCallback(callback, data);
97 return DISPLAY_SUCCESS;
98 }
99
GetDisplayCapability(uint32_t devId, DisplayCapability *info)100 static int32_t GetDisplayCapability(uint32_t devId, DisplayCapability *info)
101 {
102 DISPLAY_DEBUGLOG();
103 DISPLAY_CHK_RETURN(info == nullptr, DISPLAY_NULL_PTR, DISPLAY_LOGE("info is nullptr"));
104 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::GetDisplayCapability, info);
105 }
106
GetDisplaySupportedModes(uint32_t devId, uint32_t *num, DisplayModeInfo *modes)107 static int32_t GetDisplaySupportedModes(uint32_t devId, uint32_t *num, DisplayModeInfo *modes)
108 {
109 DISPLAY_DEBUGLOG();
110 DISPLAY_CHK_RETURN(num == nullptr, DISPLAY_NULL_PTR, DISPLAY_LOGE("num is nullptr"));
111 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::GetDisplaySupportedModes, num, modes);
112 }
113
GetDisplayMode(uint32_t devId, uint32_t *mode)114 static int32_t GetDisplayMode(uint32_t devId, uint32_t *mode)
115 {
116 DISPLAY_DEBUGLOG();
117 DISPLAY_CHK_RETURN((mode == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("mode is nullptr"));
118 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::GetDisplayMode, mode);
119 }
120
SetDisplayMode(uint32_t devId, uint32_t mode)121 static int32_t SetDisplayMode(uint32_t devId, uint32_t mode)
122 {
123 DISPLAY_DEBUGLOG();
124 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::SetDisplayMode, mode);
125 }
126
GetDisplayPowerStatus(uint32_t devId, DispPowerStatus *status)127 static int32_t GetDisplayPowerStatus(uint32_t devId, DispPowerStatus *status)
128 {
129 DISPLAY_DEBUGLOG();
130 DISPLAY_CHK_RETURN((status == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("status is nullptr"));
131 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::GetDisplayPowerStatus, status);
132 }
133
SetDisplayPowerStatus(uint32_t devId, DispPowerStatus status)134 static int32_t SetDisplayPowerStatus(uint32_t devId, DispPowerStatus status)
135 {
136 DISPLAY_DEBUGLOG();
137 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::SetDisplayPowerStatus, status);
138 }
139
GetDisplayBacklight(uint32_t devId, uint32_t *value)140 static int32_t GetDisplayBacklight(uint32_t devId, uint32_t *value)
141 {
142 DISPLAY_DEBUGLOG();
143 DISPLAY_CHK_RETURN((value == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("value is nullptr"));
144 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::GetDisplayBacklight, value);
145 }
146
SetDisplayBacklight(uint32_t devId, uint32_t value)147 static int32_t SetDisplayBacklight(uint32_t devId, uint32_t value)
148 {
149 DISPLAY_DEBUGLOG();
150 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::SetDisplayBacklight, value);
151 }
152
GetDisplayProperty(uint32_t devId, uint32_t id, uint64_t *value)153 static int32_t GetDisplayProperty(uint32_t devId, uint32_t id, uint64_t *value)
154 {
155 DISPLAY_DEBUGLOG();
156 (void)id;
157 DISPLAY_CHK_RETURN((value == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("value is nullptr"));
158 return DISPLAY_NOT_SUPPORT;
159 }
160
SetDisplayProperty(uint32_t devId, uint32_t id, uint64_t value)161 static int32_t SetDisplayProperty(uint32_t devId, uint32_t id, uint64_t value)
162 {
163 DISPLAY_DEBUGLOG();
164 (void)id;
165 return DISPLAY_NOT_SUPPORT;
166 }
167
PrepareDisplayLayers(uint32_t devId, bool *needFlushFb)168 static int32_t PrepareDisplayLayers(uint32_t devId, bool *needFlushFb)
169 {
170 DISPLAY_DEBUGLOG();
171 DISPLAY_CHK_RETURN((needFlushFb == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("needFlushFb is nullptr"));
172 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::PrepareDisplayLayers, needFlushFb);
173 }
174
GetDisplayCompChange(uint32_t devId, uint32_t *num, uint32_t *layers, int32_t *type)175 static int32_t GetDisplayCompChange(uint32_t devId, uint32_t *num, uint32_t *layers, int32_t *type)
176 {
177 DISPLAY_DEBUGLOG();
178 DISPLAY_CHK_RETURN((num == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("num is nullptr"));
179 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::GetDisplayCompChange, num, layers, type);
180 }
181
SetDisplayClientCrop(uint32_t devId, IRect *rect)182 static int32_t SetDisplayClientCrop(uint32_t devId, IRect *rect)
183 {
184 DISPLAY_DEBUGLOG();
185 DISPLAY_CHK_RETURN((rect == NULL), DISPLAY_NULL_PTR, DISPLAY_LOGE("rect is nullptr"));
186 return DISPLAY_NOT_SUPPORT;
187 }
188
SetDisplayClientDestRect(uint32_t devId, IRect *rect)189 static int32_t SetDisplayClientDestRect(uint32_t devId, IRect *rect)
190 {
191 DISPLAY_DEBUGLOG();
192 DISPLAY_CHK_RETURN((rect == NULL), DISPLAY_NULL_PTR, DISPLAY_LOGE("rect is nullptr"));
193 return DISPLAY_NOT_SUPPORT;
194 }
195
SetDisplayClientBuffer(uint32_t devId, const BufferHandle *buffer, int32_t fence)196 static int32_t SetDisplayClientBuffer(uint32_t devId, const BufferHandle *buffer, int32_t fence)
197 {
198 DISPLAY_DEBUGLOG();
199 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::SetDisplayClientBuffer, buffer, fence);
200 }
201
SetDisplayClientDamage(uint32_t devId, uint32_t num, IRect *rect)202 static int32_t SetDisplayClientDamage(uint32_t devId, uint32_t num, IRect *rect)
203 {
204 DISPLAY_DEBUGLOG();
205 (void)num;
206 DISPLAY_CHK_RETURN((rect == NULL), DISPLAY_NULL_PTR, DISPLAY_LOGE("rect is nullptr"));
207 return DISPLAY_NOT_SUPPORT;
208 }
209
SetDisplayVsyncEnabled(uint32_t devId, bool enabled)210 static int32_t SetDisplayVsyncEnabled(uint32_t devId, bool enabled)
211 {
212 DISPLAY_DEBUGLOG();
213 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::SetDisplayVsyncEnabled, enabled);
214 }
215
RegDisplayVBlankCallback(uint32_t devId, VBlankCallback callback, void *data)216 static int32_t RegDisplayVBlankCallback(uint32_t devId, VBlankCallback callback, void *data)
217 {
218 DISPLAY_DEBUGLOG();
219 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::RegDisplayVBlankCallback, callback, data);
220 }
221
GetDisplayReleaseFence(uint32_t devId, uint32_t *num, uint32_t *layers, int32_t *fences)222 static int32_t GetDisplayReleaseFence(uint32_t devId, uint32_t *num, uint32_t *layers, int32_t *fences)
223 {
224 DISPLAY_DEBUGLOG();
225 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::GetDisplayReleaseFence, num, layers,
226 fences);
227 }
228
Commit(uint32_t devId, int32_t *fence)229 static int32_t Commit(uint32_t devId, int32_t *fence)
230 {
231 DISPLAY_DEBUGLOG();
232 DISPLAY_CHK_RETURN((fence == NULL), DISPLAY_NULL_PTR, DISPLAY_LOGE("fence is nullptr"));
233 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::Commit, fence);
234 }
235
CreateVirtualDisplay(uint32_t width, uint32_t height, int32_t *format, uint32_t *devId)236 static int32_t CreateVirtualDisplay(uint32_t width, uint32_t height, int32_t *format, uint32_t *devId)
237 {
238 DISPLAY_DEBUGLOG();
239 return DISPLAY_NOT_SUPPORT;
240 }
DestroyVirtualDisplay(uint32_t devId)241 static int32_t DestroyVirtualDisplay(uint32_t devId)
242 {
243 DISPLAY_DEBUGLOG();
244 return DISPLAY_NOT_SUPPORT;
245 }
SetVirtualDisplayBuffer(uint32_t devId, BufferHandle *buffer, int32_t releaseFence)246 static int32_t SetVirtualDisplayBuffer(uint32_t devId, BufferHandle *buffer, int32_t releaseFence)
247 {
248 DISPLAY_DEBUGLOG();
249 return DISPLAY_NOT_SUPPORT;
250 }
251
252 // Layer function
CreateLayer(uint32_t devId, const LayerInfo *layerInfo, uint32_t *layerId)253 static int32_t CreateLayer(uint32_t devId, const LayerInfo *layerInfo, uint32_t *layerId)
254 {
255 DISPLAY_DEBUGLOG();
256 DISPLAY_CHK_RETURN((layerId == NULL), DISPLAY_NULL_PTR, DISPLAY_LOGE("layerId is nullptr"));
257 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::CreateLayer, layerInfo, layerId);
258 }
259
CloseLayer(uint32_t devId, uint32_t layerId)260 static int32_t CloseLayer(uint32_t devId, uint32_t layerId)
261 {
262 DISPLAY_DEBUGLOG();
263 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::CloseLayer, layerId);
264 }
265
SetLayerSize(uint32_t devId, uint32_t layerId, IRect *rect)266 static int32_t SetLayerSize(uint32_t devId, uint32_t layerId, IRect *rect)
267 {
268 DISPLAY_CHK_RETURN((rect == NULL), DISPLAY_NULL_PTR, DISPLAY_LOGE("rect is nullptr"));
269 DISPLAY_DEBUGLOG();
270 return HdiSession::GetInstance().CallLayerFunction(devId, layerId, &HdiLayer::SetLayerSize, rect);
271 }
272
SetLayerCrop(uint32_t devId, uint32_t layerId, IRect *rect)273 static int32_t SetLayerCrop(uint32_t devId, uint32_t layerId, IRect *rect)
274 {
275 DISPLAY_DEBUGLOG();
276 DISPLAY_CHK_RETURN((rect == NULL), DISPLAY_NULL_PTR, DISPLAY_LOGE("rect is nullptr"));
277 return HdiSession::GetInstance().CallLayerFunction(devId, layerId, &HdiLayer::SetLayerCrop, rect);
278 }
279
SetLayerZorder(uint32_t devId, uint32_t layerId, uint32_t zorder)280 static int32_t SetLayerZorder(uint32_t devId, uint32_t layerId, uint32_t zorder)
281 {
282 DISPLAY_DEBUGLOG();
283 return HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::SetLayerZorder, layerId, zorder);
284 }
285
SetLayerPreMulti(uint32_t devId, uint32_t layerId, bool preMul)286 static int32_t SetLayerPreMulti(uint32_t devId, uint32_t layerId, bool preMul)
287 {
288 DISPLAY_DEBUGLOG();
289 return HdiSession::GetInstance().CallLayerFunction(devId, layerId, &HdiLayer::SetLayerPreMulti, preMul);
290 }
291
SetLayerAlpha(uint32_t devId, uint32_t layerId, LayerAlpha *alpha)292 static int32_t SetLayerAlpha(uint32_t devId, uint32_t layerId, LayerAlpha *alpha)
293 {
294 DISPLAY_DEBUGLOG();
295 DISPLAY_CHK_RETURN((alpha == NULL), DISPLAY_NULL_PTR, DISPLAY_LOGE("alpha is nullptr"));
296 return HdiSession::GetInstance().CallLayerFunction(devId, layerId, &HdiLayer::SetLayerAlpha, alpha);
297 }
298
SetTransformMode(uint32_t devId, uint32_t layerId, TransformType type)299 static int32_t SetTransformMode(uint32_t devId, uint32_t layerId, TransformType type)
300 {
301 DISPLAY_DEBUGLOG();
302 return HdiSession::GetInstance().CallLayerFunction(devId, layerId, &HdiLayer::SetTransformMode, type);
303 }
304
SetLayerDirtyRegion(uint32_t devId, uint32_t layerId, IRect *region)305 static int32_t SetLayerDirtyRegion(uint32_t devId, uint32_t layerId, IRect *region)
306 {
307 DISPLAY_DEBUGLOG();
308 DISPLAY_CHK_RETURN((region == NULL), DISPLAY_NULL_PTR, DISPLAY_LOGE("region is nullptr"));
309 return HdiSession::GetInstance().CallLayerFunction(devId, layerId, &HdiLayer::SetLayerDirtyRegion, region);
310 }
311
SetLayerVisibleRegion(uint32_t devId, uint32_t layerId, uint32_t num, IRect *rect)312 static int32_t SetLayerVisibleRegion(uint32_t devId, uint32_t layerId, uint32_t num, IRect *rect)
313 {
314 DISPLAY_DEBUGLOG();
315 DISPLAY_CHK_RETURN((rect == NULL), DISPLAY_NULL_PTR, DISPLAY_LOGE("rect is nullptr"));
316 return HdiSession::GetInstance().CallLayerFunction(devId, layerId, &HdiLayer::SetLayerVisibleRegion, num, rect);
317 }
318
SetLayerBuffer(uint32_t devId, uint32_t layerId, const BufferHandle *buffer, int32_t fence)319 static int32_t SetLayerBuffer(uint32_t devId, uint32_t layerId, const BufferHandle *buffer, int32_t fence)
320 {
321 DISPLAY_DEBUGLOG();
322 return HdiSession::GetInstance().CallLayerFunction(devId, layerId, &HdiLayer::SetLayerBuffer, buffer, fence);
323 }
324
SetLayerCompositionType(uint32_t devId, uint32_t layerId, CompositionType type)325 static int32_t SetLayerCompositionType(uint32_t devId, uint32_t layerId, CompositionType type)
326 {
327 DISPLAY_DEBUGLOG();
328 return HdiSession::GetInstance().CallLayerFunction(devId, layerId, &HdiLayer::SetLayerCompositionType, type);
329 }
330
SetLayerBlendType(uint32_t devId, uint32_t layerId, BlendType type)331 static int32_t SetLayerBlendType(uint32_t devId, uint32_t layerId, BlendType type)
332 {
333 DISPLAY_DEBUGLOG();
334 return HdiSession::GetInstance().CallLayerFunction(devId, layerId, &HdiLayer::SetLayerBlendType, type);
335 }
336
337 extern "C" {
DeviceInitialize(DeviceFuncs **funcs)338 int32_t DeviceInitialize(DeviceFuncs **funcs)
339 {
340 DISPLAY_CHK_RETURN((funcs == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("in funcs is null"));
341 DeviceFuncs *dFuncs = (DeviceFuncs *)calloc(1, sizeof(DeviceFuncs));
342 if (dFuncs == nullptr) {
343 DISPLAY_LOGE("can not calloc");
344 return DISPLAY_FAILURE;
345 }
346
347 dFuncs->RegHotPlugCallback = RegHotPlugCallback;
348 dFuncs->GetDisplayCapability = GetDisplayCapability;
349 dFuncs->GetDisplaySupportedModes = GetDisplaySupportedModes;
350 dFuncs->GetDisplayMode = GetDisplayMode;
351 dFuncs->SetDisplayMode = SetDisplayMode;
352 dFuncs->GetDisplayPowerStatus = GetDisplayPowerStatus;
353 dFuncs->SetDisplayPowerStatus = SetDisplayPowerStatus;
354 dFuncs->PrepareDisplayLayers = PrepareDisplayLayers;
355 dFuncs->GetDisplayBacklight = GetDisplayBacklight;
356 dFuncs->SetDisplayBacklight = SetDisplayBacklight;
357 dFuncs->GetDisplayProperty = GetDisplayProperty;
358 dFuncs->GetDisplayCompChange = GetDisplayCompChange;
359 dFuncs->SetDisplayClientCrop = SetDisplayClientCrop;
360 dFuncs->SetDisplayClientDestRect = SetDisplayClientDestRect;
361 dFuncs->SetDisplayClientBuffer = SetDisplayClientBuffer;
362 dFuncs->SetDisplayClientDamage = SetDisplayClientDamage;
363 dFuncs->SetDisplayVsyncEnabled = SetDisplayVsyncEnabled;
364 dFuncs->RegDisplayVBlankCallback = RegDisplayVBlankCallback;
365 dFuncs->GetDisplayReleaseFence = GetDisplayReleaseFence;
366 dFuncs->CreateVirtualDisplay = CreateVirtualDisplay;
367 dFuncs->DestroyVirtualDisplay = DestroyVirtualDisplay;
368 dFuncs->SetVirtualDisplayBuffer = SetVirtualDisplayBuffer;
369 dFuncs->SetDisplayProperty = SetDisplayProperty;
370 dFuncs->Commit = Commit;
371 *funcs = dFuncs;
372 DISPLAY_DEBUGLOG("%{public}s: device initialize success", __func__);
373 HdiSession::GetInstance();
374 return DISPLAY_SUCCESS;
375 }
376
DeviceUninitialize(DeviceFuncs *funcs)377 int32_t DeviceUninitialize(DeviceFuncs *funcs)
378 {
379 DISPLAY_CHK_RETURN((funcs == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("in funcs is null"));
380 free(funcs);
381 return DISPLAY_SUCCESS;
382 }
383
LayerInitialize(LayerFuncs **funcs)384 int32_t LayerInitialize(LayerFuncs **funcs)
385 {
386 DISPLAY_CHK_RETURN((funcs == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("the in funcs is nullptr"));
387 LayerFuncs *lFuncs = (LayerFuncs *)calloc(1, sizeof(LayerFuncs));
388 if (lFuncs == nullptr) {
389 DISPLAY_LOGE("can not calloc errno: %{public}d", errno);
390 return DISPLAY_FAILURE;
391 }
392 lFuncs->SetLayerAlpha = SetLayerAlpha;
393 lFuncs->CreateLayer = CreateLayer;
394 lFuncs->CloseLayer = CloseLayer;
395 lFuncs->SetLayerSize = SetLayerSize;
396 lFuncs->SetLayerCrop = SetLayerCrop;
397 lFuncs->SetLayerZorder = SetLayerZorder;
398 lFuncs->SetLayerPreMulti = SetLayerPreMulti;
399 lFuncs->SetTransformMode = SetTransformMode;
400 lFuncs->SetLayerDirtyRegion = SetLayerDirtyRegion;
401 lFuncs->SetLayerVisibleRegion = SetLayerVisibleRegion;
402 lFuncs->SetLayerBuffer = SetLayerBuffer;
403 lFuncs->SetLayerCompositionType = SetLayerCompositionType;
404 lFuncs->SetLayerBlendType = SetLayerBlendType;
405
406 *funcs = lFuncs;
407 DISPLAY_DEBUGLOG("%{public}s: layer initialize success", __func__);
408 return DISPLAY_SUCCESS;
409 }
410
LayerUninitialize(LayerFuncs *funcs)411 int32_t LayerUninitialize(LayerFuncs *funcs)
412 {
413 DISPLAY_CHK_RETURN((funcs == nullptr), DISPLAY_FAILURE, DISPLAY_LOGE("the funcs is nullptr"));
414 free(funcs);
415 DISPLAY_DEBUGLOG("%{public}s: layer uninitialize success", __func__);
416 return DISPLAY_SUCCESS;
417 }
418 }
419