1 /* 2 * Copyright (c) 2014 - 2017, The Linux Foundation. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without modification, are permitted 5 * provided that the following conditions are met: 6 * * Redistributions of source code must retain the above copyright notice, this list of 7 * conditions and the following disclaimer. 8 * * Redistributions in binary form must reproduce the above copyright notice, this list of 9 * conditions and the following disclaimer in the documentation and/or other materials provided 10 * with the distribution. 11 * * Neither the name of The Linux Foundation nor the names of its contributors may be used to 12 * endorse or promote products derived from this software without specific prior written 13 * permission. 14 * 15 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 21 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #include <utils/constants.h> 26 #include <utils/debug.h> 27 #include <core/buffer_allocator.h> 28 29 #include "comp_manager.h" 30 #include "strategy.h" 31 32 #define __CLASS__ "CompManager" 33 34 namespace sdm { 35 36 static bool NeedsScaledComposition(const DisplayConfigVariableInfo &fb_config, 37 const HWMixerAttributes &mixer_attributes) { 38 return ((fb_config.x_pixels != mixer_attributes.width) || 39 (fb_config.y_pixels != mixer_attributes.height)); 40 } 41 42 DisplayError CompManager::Init(const HWResourceInfo &hw_res_info, 43 ExtensionInterface *extension_intf, 44 BufferAllocator *buffer_allocator, 45 BufferSyncHandler *buffer_sync_handler, 46 SocketHandler *socket_handler) { 47 SCOPE_LOCK(locker_); 48 49 DisplayError error = kErrorNone; 50 51 if (extension_intf) { 52 error = extension_intf->CreateResourceExtn(hw_res_info, buffer_allocator, buffer_sync_handler, 53 &resource_intf_); 54 extension_intf->CreateDppsControlExtn(&dpps_ctrl_intf_, socket_handler); 55 } else { 56 error = ResourceDefault::CreateResourceDefault(hw_res_info, &resource_intf_); 57 } 58 59 if (error != kErrorNone) { 60 if (extension_intf) { 61 extension_intf->DestroyDppsControlExtn(dpps_ctrl_intf_); 62 } 63 return error; 64 } 65 66 hw_res_info_ = hw_res_info; 67 buffer_allocator_ = buffer_allocator; 68 extension_intf_ = extension_intf; 69 70 return error; 71 } 72 73 DisplayError CompManager::Deinit() { 74 SCOPE_LOCK(locker_); 75 76 if (extension_intf_) { 77 extension_intf_->DestroyResourceExtn(resource_intf_); 78 extension_intf_->DestroyDppsControlExtn(dpps_ctrl_intf_); 79 } else { 80 ResourceDefault::DestroyResourceDefault(resource_intf_); 81 } 82 83 return kErrorNone; 84 } 85 86 DisplayError CompManager::RegisterDisplay(DisplayType type, 87 const HWDisplayAttributes &display_attributes, 88 const HWPanelInfo &hw_panel_info, 89 const HWMixerAttributes &mixer_attributes, 90 const DisplayConfigVariableInfo &fb_config, 91 Handle *display_ctx) { 92 SCOPE_LOCK(locker_); 93 94 DisplayError error = kErrorNone; 95 96 DisplayCompositionContext *display_comp_ctx = new DisplayCompositionContext(); 97 if (!display_comp_ctx) { 98 return kErrorMemory; 99 } 100 101 Strategy *&strategy = display_comp_ctx->strategy; 102 strategy = new Strategy(extension_intf_, buffer_allocator_, type, hw_res_info_, hw_panel_info, 103 mixer_attributes, display_attributes, fb_config); 104 if (!strategy) { 105 DLOGE("Unable to create strategy"); 106 delete display_comp_ctx; 107 return kErrorMemory; 108 } 109 110 error = strategy->Init(); 111 if (error != kErrorNone) { 112 delete strategy; 113 delete display_comp_ctx; 114 return error; 115 } 116 117 error = resource_intf_->RegisterDisplay(type, display_attributes, hw_panel_info, mixer_attributes, 118 &display_comp_ctx->display_resource_ctx); 119 if (error != kErrorNone) { 120 strategy->Deinit(); 121 delete strategy; 122 delete display_comp_ctx; 123 display_comp_ctx = NULL; 124 return error; 125 } 126 127 registered_displays_[type] = 1; 128 display_comp_ctx->is_primary_panel = hw_panel_info.is_primary_panel; 129 display_comp_ctx->display_type = type; 130 display_comp_ctx->fb_config = fb_config; 131 *display_ctx = display_comp_ctx; 132 // New non-primary display device has been added, so move the composition mode to safe mode until 133 // resources for the added display is configured properly. 134 if (!display_comp_ctx->is_primary_panel) { 135 safe_mode_ = true; 136 max_sde_ext_layers_ = UINT32(Debug::GetExtMaxlayers()); 137 } 138 139 display_comp_ctx->scaled_composition = NeedsScaledComposition(fb_config, mixer_attributes); 140 DLOGV_IF(kTagCompManager, "registered display bit mask 0x%x, configured display bit mask 0x%x, " \ 141 "display type %d", registered_displays_.to_ulong(), configured_displays_.to_ulong(), 142 display_comp_ctx->display_type); 143 144 return kErrorNone; 145 } 146 147 DisplayError CompManager::UnregisterDisplay(Handle display_ctx) { 148 SCOPE_LOCK(locker_); 149 150 DisplayCompositionContext *display_comp_ctx = 151 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 152 153 if (!display_comp_ctx) { 154 return kErrorParameters; 155 } 156 157 resource_intf_->UnregisterDisplay(display_comp_ctx->display_resource_ctx); 158 159 Strategy *&strategy = display_comp_ctx->strategy; 160 strategy->Deinit(); 161 delete strategy; 162 163 registered_displays_[display_comp_ctx->display_type] = 0; 164 configured_displays_[display_comp_ctx->display_type] = 0; 165 166 if (display_comp_ctx->display_type == kHDMI) { 167 max_layers_ = kMaxSDELayers; 168 } 169 170 DLOGV_IF(kTagCompManager, "registered display bit mask 0x%x, configured display bit mask 0x%x, " \ 171 "display type %d", registered_displays_.to_ulong(), configured_displays_.to_ulong(), 172 display_comp_ctx->display_type); 173 174 delete display_comp_ctx; 175 display_comp_ctx = NULL; 176 return kErrorNone; 177 } 178 179 DisplayError CompManager::ReconfigureDisplay(Handle comp_handle, 180 const HWDisplayAttributes &display_attributes, 181 const HWPanelInfo &hw_panel_info, 182 const HWMixerAttributes &mixer_attributes, 183 const DisplayConfigVariableInfo &fb_config) { 184 SCOPE_LOCK(locker_); 185 186 DisplayError error = kErrorNone; 187 DisplayCompositionContext *display_comp_ctx = 188 reinterpret_cast<DisplayCompositionContext *>(comp_handle); 189 190 error = resource_intf_->ReconfigureDisplay(display_comp_ctx->display_resource_ctx, 191 display_attributes, hw_panel_info, mixer_attributes); 192 if (error != kErrorNone) { 193 return error; 194 } 195 196 if (display_comp_ctx->strategy) { 197 error = display_comp_ctx->strategy->Reconfigure(hw_panel_info, display_attributes, 198 mixer_attributes, fb_config); 199 if (error != kErrorNone) { 200 DLOGE("Unable to Reconfigure strategy."); 201 display_comp_ctx->strategy->Deinit(); 202 delete display_comp_ctx->strategy; 203 display_comp_ctx->strategy = NULL; 204 return error; 205 } 206 } 207 208 // For HDMI S3D mode, set max_layers_ to 0 so that primary display would fall back 209 // to GPU composition to release pipes for HDMI. 210 if (display_comp_ctx->display_type == kHDMI) { 211 if (hw_panel_info.s3d_mode != kS3DModeNone) { 212 max_layers_ = 0; 213 } else { 214 max_layers_ = kMaxSDELayers; 215 } 216 } 217 218 display_comp_ctx->scaled_composition = NeedsScaledComposition(fb_config, mixer_attributes); 219 // Update new resolution. 220 display_comp_ctx->fb_config = fb_config; 221 222 return error; 223 } 224 225 void CompManager::PrepareStrategyConstraints(Handle comp_handle, HWLayers *hw_layers) { 226 DisplayCompositionContext *display_comp_ctx = 227 reinterpret_cast<DisplayCompositionContext *>(comp_handle); 228 StrategyConstraints *constraints = &display_comp_ctx->constraints; 229 bool low_end_hw = ((hw_res_info_.num_vig_pipe + hw_res_info_.num_rgb_pipe + 230 hw_res_info_.num_dma_pipe) <= kSafeModeThreshold); 231 232 constraints->safe_mode = safe_mode_; 233 constraints->use_cursor = false; 234 constraints->max_layers = max_layers_; 235 236 // Limit 2 layer SDE Comp if its not a Primary Display. 237 // Safe mode is the policy for External display on a low end device. 238 if (!display_comp_ctx->is_primary_panel) { 239 constraints->max_layers = max_sde_ext_layers_; 240 constraints->safe_mode = (low_end_hw && !hw_res_info_.separate_rotator) ? true : safe_mode_; 241 if(hw_layers->info.stack->flags.secure_present) 242 secure_external_layer_ = true; 243 else 244 secure_external_layer_ = false; 245 } 246 247 // When Secure layer is present on external, GPU composition should be policy 248 // for Primary on low end devices 249 if(display_comp_ctx->is_primary_panel && (registered_displays_.count() > 1) 250 && low_end_hw && secure_external_layer_) { 251 DLOGV_IF(kTagCompManager,"Secure layer present for LET. Fallingback to GPU"); 252 hw_layers->info.stack->flags.skip_present = 1; 253 for(auto &layer : hw_layers->info.stack->layers) { 254 if(layer->composition != kCompositionGPUTarget) { 255 layer->flags.skip = 1; 256 } 257 } 258 } 259 260 // If a strategy fails after successfully allocating resources, then set safe mode 261 if (display_comp_ctx->remaining_strategies != display_comp_ctx->max_strategies) { 262 constraints->safe_mode = true; 263 } 264 265 // Set use_cursor constraint to Strategy 266 constraints->use_cursor = display_comp_ctx->valid_cursor; 267 268 // TODO(user): App layer count will change for hybrid composition 269 uint32_t app_layer_count = UINT32(hw_layers->info.stack->layers.size()) - 1; 270 if (display_comp_ctx->idle_fallback || display_comp_ctx->thermal_fallback_) { 271 // Handle the idle timeout by falling back 272 constraints->safe_mode = true; 273 } 274 275 // Avoid safe mode, if there is only one app layer. 276 if (app_layer_count == 1) { 277 constraints->safe_mode = false; 278 } 279 } 280 281 void CompManager::PrePrepare(Handle display_ctx, HWLayers *hw_layers) { 282 SCOPE_LOCK(locker_); 283 DisplayCompositionContext *display_comp_ctx = 284 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 285 display_comp_ctx->valid_cursor = SupportLayerAsCursor(display_comp_ctx, hw_layers); 286 287 // pu constraints 288 display_comp_ctx->pu_constraints.enable_cursor_pu = display_comp_ctx->valid_cursor; 289 290 display_comp_ctx->strategy->Start(&hw_layers->info, &display_comp_ctx->max_strategies, 291 display_comp_ctx->pu_constraints); 292 display_comp_ctx->remaining_strategies = display_comp_ctx->max_strategies; 293 } 294 295 DisplayError CompManager::Prepare(Handle display_ctx, HWLayers *hw_layers) { 296 SCOPE_LOCK(locker_); 297 298 DisplayCompositionContext *display_comp_ctx = 299 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 300 Handle &display_resource_ctx = display_comp_ctx->display_resource_ctx; 301 302 DisplayError error = kErrorUndefined; 303 304 PrepareStrategyConstraints(display_ctx, hw_layers); 305 306 // Select a composition strategy, and try to allocate resources for it. 307 resource_intf_->Start(display_resource_ctx); 308 309 bool exit = false; 310 uint32_t &count = display_comp_ctx->remaining_strategies; 311 for (; !exit && count > 0; count--) { 312 error = display_comp_ctx->strategy->GetNextStrategy(&display_comp_ctx->constraints); 313 if (error != kErrorNone) { 314 // Composition strategies exhausted. Resource Manager could not allocate resources even for 315 // GPU composition. This will never happen. 316 exit = true; 317 } 318 319 if (!exit) { 320 error = resource_intf_->Prepare(display_resource_ctx, hw_layers); 321 // Exit if successfully prepared resource, else try next strategy. 322 exit = (error == kErrorNone); 323 } 324 } 325 326 if (error != kErrorNone) { 327 resource_intf_->Stop(display_resource_ctx, hw_layers); 328 DLOGE("Composition strategies exhausted for display = %d", display_comp_ctx->display_type); 329 return error; 330 } 331 332 error = resource_intf_->Stop(display_resource_ctx, hw_layers); 333 334 return error; 335 } 336 337 DisplayError CompManager::PostPrepare(Handle display_ctx, HWLayers *hw_layers) { 338 SCOPE_LOCK(locker_); 339 DisplayCompositionContext *display_comp_ctx = 340 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 341 Handle &display_resource_ctx = display_comp_ctx->display_resource_ctx; 342 343 DisplayError error = kErrorNone; 344 error = resource_intf_->PostPrepare(display_resource_ctx, hw_layers); 345 if (error != kErrorNone) { 346 return error; 347 } 348 349 display_comp_ctx->strategy->Stop(); 350 351 return kErrorNone; 352 } 353 354 DisplayError CompManager::Commit(Handle display_ctx, HWLayers *hw_layers) { 355 SCOPE_LOCK(locker_); 356 357 DisplayCompositionContext *display_comp_ctx = 358 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 359 360 return resource_intf_->Commit(display_comp_ctx->display_resource_ctx, hw_layers); 361 } 362 363 DisplayError CompManager::ReConfigure(Handle display_ctx, HWLayers *hw_layers) { 364 SCOPE_LOCK(locker_); 365 366 DisplayCompositionContext *display_comp_ctx = 367 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 368 Handle &display_resource_ctx = display_comp_ctx->display_resource_ctx; 369 370 DisplayError error = kErrorUndefined; 371 resource_intf_->Start(display_resource_ctx); 372 error = resource_intf_->Prepare(display_resource_ctx, hw_layers); 373 374 if (error != kErrorNone) { 375 DLOGE("Reconfigure failed for display = %d", display_comp_ctx->display_type); 376 } 377 378 resource_intf_->Stop(display_resource_ctx, hw_layers); 379 if (error != kErrorNone) { 380 error = resource_intf_->PostPrepare(display_resource_ctx, hw_layers); 381 } 382 383 return error; 384 } 385 386 DisplayError CompManager::PostCommit(Handle display_ctx, HWLayers *hw_layers) { 387 SCOPE_LOCK(locker_); 388 389 DisplayError error = kErrorNone; 390 DisplayCompositionContext *display_comp_ctx = 391 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 392 configured_displays_[display_comp_ctx->display_type] = 1; 393 if (configured_displays_ == registered_displays_) { 394 safe_mode_ = false; 395 } 396 397 error = resource_intf_->PostCommit(display_comp_ctx->display_resource_ctx, hw_layers); 398 if (error != kErrorNone) { 399 return error; 400 } 401 402 display_comp_ctx->idle_fallback = false; 403 404 DLOGV_IF(kTagCompManager, "registered display bit mask 0x%x, configured display bit mask 0x%x, " \ 405 "display type %d", registered_displays_, configured_displays_, 406 display_comp_ctx->display_type); 407 408 return kErrorNone; 409 } 410 411 void CompManager::Purge(Handle display_ctx) { 412 SCOPE_LOCK(locker_); 413 414 DisplayCompositionContext *display_comp_ctx = 415 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 416 417 resource_intf_->Purge(display_comp_ctx->display_resource_ctx); 418 419 display_comp_ctx->strategy->Purge(); 420 } 421 422 DisplayError CompManager::SetIdleTimeoutMs(Handle display_ctx, uint32_t active_ms) { 423 SCOPE_LOCK(locker_); 424 425 DisplayCompositionContext *display_comp_ctx = 426 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 427 428 return display_comp_ctx->strategy->SetIdleTimeoutMs(active_ms); 429 } 430 431 void CompManager::ProcessIdleTimeout(Handle display_ctx) { 432 SCOPE_LOCK(locker_); 433 434 DisplayCompositionContext *display_comp_ctx = 435 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 436 437 if (!display_comp_ctx) { 438 return; 439 } 440 441 display_comp_ctx->idle_fallback = true; 442 } 443 444 void CompManager::ProcessThermalEvent(Handle display_ctx, int64_t thermal_level) { 445 SCOPE_LOCK(locker_); 446 447 DisplayCompositionContext *display_comp_ctx = 448 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 449 450 if (thermal_level >= kMaxThermalLevel) { 451 display_comp_ctx->thermal_fallback_ = true; 452 } else { 453 display_comp_ctx->thermal_fallback_ = false; 454 } 455 } 456 457 void CompManager::ProcessIdlePowerCollapse(Handle display_ctx) { 458 SCOPE_LOCK(locker_); 459 460 DisplayCompositionContext *display_comp_ctx = 461 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 462 463 if (display_comp_ctx) { 464 resource_intf_->Perform(ResourceInterface::kCmdResetScalarLUT, 465 display_comp_ctx->display_resource_ctx); 466 } 467 } 468 469 DisplayError CompManager::SetMaxMixerStages(Handle display_ctx, uint32_t max_mixer_stages) { 470 SCOPE_LOCK(locker_); 471 472 DisplayError error = kErrorNone; 473 DisplayCompositionContext *display_comp_ctx = 474 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 475 476 if (display_comp_ctx) { 477 error = resource_intf_->SetMaxMixerStages(display_comp_ctx->display_resource_ctx, 478 max_mixer_stages); 479 } 480 481 return error; 482 } 483 484 void CompManager::ControlPartialUpdate(Handle display_ctx, bool enable) { 485 SCOPE_LOCK(locker_); 486 487 DisplayCompositionContext *display_comp_ctx = 488 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 489 display_comp_ctx->pu_constraints.enable = enable; 490 } 491 492 DisplayError CompManager::ValidateScaling(const LayerRect &crop, const LayerRect &dst, 493 bool rotate90) { 494 BufferLayout layout = Debug::IsUbwcTiledFrameBuffer() ? kUBWC : kLinear; 495 return resource_intf_->ValidateScaling(crop, dst, rotate90, layout, true); 496 } 497 498 DisplayError CompManager::ValidateAndSetCursorPosition(Handle display_ctx, HWLayers *hw_layers, 499 int x, int y) { 500 DisplayCompositionContext *display_comp_ctx = 501 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 502 Handle &display_resource_ctx = display_comp_ctx->display_resource_ctx; 503 return resource_intf_->ValidateAndSetCursorPosition(display_resource_ctx, hw_layers, x, y, 504 &display_comp_ctx->fb_config); 505 } 506 507 bool CompManager::SupportLayerAsCursor(Handle comp_handle, HWLayers *hw_layers) { 508 DisplayCompositionContext *display_comp_ctx = 509 reinterpret_cast<DisplayCompositionContext *>(comp_handle); 510 Handle &display_resource_ctx = display_comp_ctx->display_resource_ctx; 511 LayerStack *layer_stack = hw_layers->info.stack; 512 bool supported = false; 513 int32_t gpu_index = -1; 514 515 // HW Cursor cannot be used, if Display configuration needs scaled composition. 516 if (display_comp_ctx->scaled_composition || !layer_stack->flags.cursor_present) { 517 return supported; 518 } 519 520 for (int32_t i = INT32(layer_stack->layers.size() - 1); i >= 0; i--) { 521 Layer *layer = layer_stack->layers.at(UINT32(i)); 522 if (layer->composition == kCompositionGPUTarget) { 523 gpu_index = i; 524 break; 525 } 526 } 527 if (gpu_index <= 0) { 528 return supported; 529 } 530 Layer *cursor_layer = layer_stack->layers.at(UINT32(gpu_index) - 1); 531 if (cursor_layer->flags.cursor && !cursor_layer->flags.skip && 532 resource_intf_->ValidateCursorConfig(display_resource_ctx, 533 cursor_layer, true) == kErrorNone) { 534 supported = true; 535 } 536 537 return supported; 538 } 539 540 DisplayError CompManager::SetMaxBandwidthMode(HWBwModes mode) { 541 if ((hw_res_info_.has_dyn_bw_support == false) || (mode >= kBwModeMax)) { 542 return kErrorNotSupported; 543 } 544 545 return resource_intf_->SetMaxBandwidthMode(mode); 546 } 547 548 DisplayError CompManager::GetScaleLutConfig(HWScaleLutInfo *lut_info) { 549 return resource_intf_->GetScaleLutConfig(lut_info); 550 } 551 552 DisplayError CompManager::SetDetailEnhancerData(Handle display_ctx, 553 const DisplayDetailEnhancerData &de_data) { 554 SCOPE_LOCK(locker_); 555 556 DisplayCompositionContext *display_comp_ctx = 557 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 558 559 return resource_intf_->SetDetailEnhancerData(display_comp_ctx->display_resource_ctx, de_data); 560 } 561 562 DisplayError CompManager::SetCompositionState(Handle display_ctx, 563 LayerComposition composition_type, bool enable) { 564 SCOPE_LOCK(locker_); 565 566 DisplayCompositionContext *display_comp_ctx = 567 reinterpret_cast<DisplayCompositionContext *>(display_ctx); 568 569 return display_comp_ctx->strategy->SetCompositionState(composition_type, enable); 570 } 571 572 DisplayError CompManager::ControlDpps(bool enable) { 573 if (dpps_ctrl_intf_) { 574 return enable ? dpps_ctrl_intf_->On() : dpps_ctrl_intf_->Off(); 575 } 576 577 return kErrorNone; 578 } 579 580 bool CompManager::SetDisplayState(Handle display_ctx, 581 DisplayState state, DisplayType display_type) { 582 display_state_[display_type] = state; 583 584 switch (state) { 585 case kStateOff: 586 Purge(display_ctx); 587 configured_displays_.reset(display_type); 588 DLOGV_IF(kTagCompManager, "configured_displays_ = 0x%x", configured_displays_); 589 break; 590 591 case kStateOn: 592 if (registered_displays_.count() > 1) { 593 safe_mode_ = true; 594 DLOGV_IF(kTagCompManager, "safe_mode = %d", safe_mode_); 595 } 596 break; 597 598 default: 599 break; 600 } 601 602 return true; 603 } 604 605 } // namespace sdm 606