| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h" | 5 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 #include <string> | 8 #include <string> |
| 9 | 9 |
| 10 #include "ash/shell.h" | 10 #include "ash/shell.h" |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/json/json_writer.h" | 12 #include "base/json/json_writer.h" |
| 13 #include "base/synchronization/waitable_event.h" | 13 #include "base/synchronization/waitable_event.h" |
| 14 #include "base/values.h" | 14 #include "base/values.h" |
| 15 #include "chrome/browser/profiles/profile.h" | 15 #include "chrome/browser/profiles/profile.h" |
| 16 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" | 16 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" |
| 17 #include "chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.h" | 17 #include "chrome/browser/ui/ash/launcher/arc_app_deferred_launcher_controller.h" |
| 18 #include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h" | 18 #include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h" |
| 19 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" | 19 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" |
| 20 #include "chromeos/dbus/dbus_thread_manager.h" | 20 #include "chromeos/dbus/dbus_thread_manager.h" |
| 21 #include "chromeos/dbus/session_manager_client.h" | 21 #include "chromeos/dbus/session_manager_client.h" |
| 22 #include "components/arc/arc_bridge_service.h" | 22 #include "components/arc/arc_bridge_service.h" |
| 23 #include "components/arc/arc_service_manager.h" | 23 #include "components/arc/arc_service_manager.h" |
| 24 #include "components/arc/common/intent_helper.mojom.h" | 24 #include "components/arc/common/intent_helper.mojom.h" |
| 25 #include "ui/aura/window.h" | 25 #include "ui/aura/window.h" |
| 26 #include "ui/display/display.h" | 26 #include "ui/display/display.h" |
| 27 #include "ui/display/screen.h" | 27 #include "ui/display/screen.h" |
| 28 #include "ui/events/event_constants.h" | 28 #include "ui/events/event_constants.h" |
| 29 | 29 |
| 30 // Helper macro which returns the AppInstance. |
| 31 #define GET_APP_INSTANCE(method_name) \ |
| 32 (arc::ArcServiceManager::Get() \ |
| 33 ? ARC_GET_INSTANCE_FOR_METHOD( \ |
| 34 arc::ArcServiceManager::Get()->arc_bridge_service()->app(), \ |
| 35 method_name) \ |
| 36 : nullptr) |
| 37 |
| 38 // Helper function which returns the IntentHelperInstance. |
| 39 #define GET_INTENT_HELPER_INSTANCE(method_name) \ |
| 40 (arc::ArcServiceManager::Get() \ |
| 41 ? ARC_GET_INSTANCE_FOR_METHOD(arc::ArcServiceManager::Get() \ |
| 42 ->arc_bridge_service() \ |
| 43 ->intent_helper(), \ |
| 44 method_name) \ |
| 45 : nullptr) |
| 46 |
| 30 namespace arc { | 47 namespace arc { |
| 31 | 48 |
| 32 namespace { | 49 namespace { |
| 33 | 50 |
| 34 // Default sizes to use. | 51 // Default sizes to use. |
| 35 constexpr int kNexus7Width = 960; | 52 constexpr int kNexus7Width = 960; |
| 36 constexpr int kNexus7Height = 600; | 53 constexpr int kNexus7Height = 600; |
| 37 constexpr int kNexus5Width = 410; | 54 constexpr int kNexus5Width = 410; |
| 38 constexpr int kNexus5Height = 690; | 55 constexpr int kNexus5Height = 690; |
| 39 | 56 |
| 40 // Minimum required versions. | |
| 41 constexpr uint32_t kMinVersion = 0; | |
| 42 constexpr uint32_t kCanHandleResolutionMinVersion = 1; | |
| 43 constexpr uint32_t kSendBroadcastMinVersion = 1; | |
| 44 constexpr uint32_t kUninstallPackageMinVersion = 2; | |
| 45 constexpr uint32_t kTaskSupportMinVersion = 3; | |
| 46 constexpr uint32_t kShowPackageInfoMinVersion = 5; | |
| 47 constexpr uint32_t kRemoveIconMinVersion = 9; | |
| 48 constexpr uint32_t kShowPackageInfoOnPageMinVersion = 10; | |
| 49 | |
| 50 // Service name strings. | |
| 51 constexpr char kCanHandleResolutionStr[] = "get resolution capability"; | |
| 52 constexpr char kCloseTaskStr[] = "close task"; | |
| 53 constexpr char kLaunchAppStr[] = "launch app"; | |
| 54 constexpr char kRemoveIconStr[] = "remove icon"; | |
| 55 constexpr char kSetActiveTaskStr[] = "set active task"; | |
| 56 constexpr char kShowPackageInfoStr[] = "show package info"; | |
| 57 constexpr char kUninstallPackageStr[] = "uninstall package"; | |
| 58 | |
| 59 // Intent helper strings. | 57 // Intent helper strings. |
| 60 constexpr char kIntentHelperClassName[] = | 58 constexpr char kIntentHelperClassName[] = |
| 61 "org.chromium.arc.intent_helper.SettingsReceiver"; | 59 "org.chromium.arc.intent_helper.SettingsReceiver"; |
| 62 constexpr char kIntentHelperPackageName[] = "org.chromium.arc.intent_helper"; | 60 constexpr char kIntentHelperPackageName[] = "org.chromium.arc.intent_helper"; |
| 63 constexpr char kSendBroadcastStr[] = "SendBroadcast"; | |
| 64 constexpr char kSetInTouchModeIntent[] = | 61 constexpr char kSetInTouchModeIntent[] = |
| 65 "org.chromium.arc.intent_helper.SET_IN_TOUCH_MODE"; | 62 "org.chromium.arc.intent_helper.SET_IN_TOUCH_MODE"; |
| 66 constexpr char kShowTalkbackSettingsIntent[] = | 63 constexpr char kShowTalkbackSettingsIntent[] = |
| 67 "org.chromium.arc.intent_helper.SHOW_TALKBACK_SETTINGS"; | 64 "org.chromium.arc.intent_helper.SHOW_TALKBACK_SETTINGS"; |
| 68 | 65 |
| 69 // Helper function which returns the AppInstance. Create related logs when error | |
| 70 // happens. | |
| 71 arc::mojom::AppInstance* GetAppInstance(uint32_t required_version, | |
| 72 const std::string& service_name) { | |
| 73 auto* arc_service_manager = arc::ArcServiceManager::Get(); | |
| 74 if (!arc_service_manager) { | |
| 75 VLOG(2) << "Request to " << service_name | |
| 76 << " when bridge service is not ready."; | |
| 77 return nullptr; | |
| 78 } | |
| 79 | |
| 80 return arc_service_manager->arc_bridge_service() | |
| 81 ->app() | |
| 82 ->GetInstanceForVersion(required_version, service_name.c_str()); | |
| 83 } | |
| 84 | |
| 85 // Helper function which returns the IntentHelperInstance. Create related logs | |
| 86 // when error happens. | |
| 87 arc::mojom::IntentHelperInstance* GetIntentHelperInstance( | |
| 88 uint32_t required_version, | |
| 89 const std::string& service_name) { | |
| 90 auto* arc_service_manager = arc::ArcServiceManager::Get(); | |
| 91 if (!arc_service_manager) { | |
| 92 VLOG(2) << "Request to " << service_name | |
| 93 << " when bridge service is not ready."; | |
| 94 return nullptr; | |
| 95 } | |
| 96 | |
| 97 return arc_service_manager->arc_bridge_service() | |
| 98 ->intent_helper() | |
| 99 ->GetInstanceForVersion(required_version, service_name.c_str()); | |
| 100 } | |
| 101 | |
| 102 void PrioritizeArcInstanceCallback(bool success) { | 66 void PrioritizeArcInstanceCallback(bool success) { |
| 103 VLOG(2) << "Finished prioritizing the instance: result=" << success; | 67 VLOG(2) << "Finished prioritizing the instance: result=" << success; |
| 104 if (!success) | 68 if (!success) |
| 105 LOG(ERROR) << "Failed to prioritize ARC"; | 69 LOG(ERROR) << "Failed to prioritize ARC"; |
| 106 } | 70 } |
| 107 | 71 |
| 108 // Find a proper size and position for a given rectangle on the screen. | 72 // Find a proper size and position for a given rectangle on the screen. |
| 109 // TODO(skuhne): This needs more consideration, but it is lacking | 73 // TODO(skuhne): This needs more consideration, but it is lacking |
| 110 // WindowPositioner functionality since we do not have an Aura::Window yet. | 74 // WindowPositioner functionality since we do not have an Aura::Window yet. |
| 111 gfx::Rect GetTargetRect(const gfx::Size& size) { | 75 gfx::Rect GetTargetRect(const gfx::Size& size) { |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 236 if (!app_info->ready) { | 200 if (!app_info->ready) { |
| 237 VLOG(2) << "Cannot launch not-ready app: " << app_id << "."; | 201 VLOG(2) << "Cannot launch not-ready app: " << app_id << "."; |
| 238 return false; | 202 return false; |
| 239 } | 203 } |
| 240 | 204 |
| 241 if (!app_info->launchable) { | 205 if (!app_info->launchable) { |
| 242 VLOG(2) << "Cannot launch non-launchable app: " << app_id << "."; | 206 VLOG(2) << "Cannot launch non-launchable app: " << app_id << "."; |
| 243 return false; | 207 return false; |
| 244 } | 208 } |
| 245 | 209 |
| 246 arc::mojom::AppInstance* app_instance = | 210 arc::mojom::AppInstance* app_instance = GET_APP_INSTANCE(LaunchApp); |
| 247 GetAppInstance(kMinVersion, kLaunchAppStr); | |
| 248 if (!app_instance) | 211 if (!app_instance) |
| 249 return false; | 212 return false; |
| 250 | 213 |
| 251 arc::mojom::IntentHelperInstance* intent_helper_instance = | 214 arc::mojom::IntentHelperInstance* intent_helper_instance = |
| 252 GetIntentHelperInstance(kSendBroadcastMinVersion, kSendBroadcastStr); | 215 GET_INTENT_HELPER_INSTANCE(SendBroadcast); |
| 253 if (intent_helper_instance) { | 216 if (intent_helper_instance) { |
| 254 base::DictionaryValue extras; | 217 base::DictionaryValue extras; |
| 255 extras.SetBoolean("inTouchMode", IsMouseOrTouchEventFromFlags(event_flags)); | 218 extras.SetBoolean("inTouchMode", IsMouseOrTouchEventFromFlags(event_flags)); |
| 256 std::string extras_string; | 219 std::string extras_string; |
| 257 base::JSONWriter::Write(extras, &extras_string); | 220 base::JSONWriter::Write(extras, &extras_string); |
| 258 intent_helper_instance->SendBroadcast( | 221 intent_helper_instance->SendBroadcast( |
| 259 kSetInTouchModeIntent, kIntentHelperPackageName, kIntentHelperClassName, | 222 kSetInTouchModeIntent, kIntentHelperPackageName, kIntentHelperClassName, |
| 260 extras_string); | 223 extras_string); |
| 261 } | 224 } |
| 262 | 225 |
| 263 if (app_info->shortcut) { | 226 if (app_info->shortcut) { |
| 227 // Before calling LaunchIntent, check if the interface is supported. Reusing |
| 228 // the same |app_instance| for LaunchIntent is allowed. |
| 229 if (!GET_APP_INSTANCE(LaunchIntent)) |
| 230 return false; |
| 264 app_instance->LaunchIntent(app_info->intent_uri, target_rect); | 231 app_instance->LaunchIntent(app_info->intent_uri, target_rect); |
| 265 } else { | 232 } else { |
| 266 app_instance->LaunchApp(app_info->package_name, app_info->activity, | 233 app_instance->LaunchApp(app_info->package_name, app_info->activity, |
| 267 target_rect); | 234 target_rect); |
| 268 } | 235 } |
| 269 prefs->SetLastLaunchTime(app_id, base::Time::Now()); | 236 prefs->SetLastLaunchTime(app_id, base::Time::Now()); |
| 270 | 237 |
| 271 return true; | 238 return true; |
| 272 } | 239 } |
| 273 | 240 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 335 prefs->SetLastLaunchTime(app_id, base::Time::Now()); | 302 prefs->SetLastLaunchTime(app_id, base::Time::Now()); |
| 336 return true; | 303 return true; |
| 337 } | 304 } |
| 338 | 305 |
| 339 return (new LaunchAppWithoutSize(context, app_id, landscape_layout, | 306 return (new LaunchAppWithoutSize(context, app_id, landscape_layout, |
| 340 event_flags)) | 307 event_flags)) |
| 341 ->LaunchAndRelease(); | 308 ->LaunchAndRelease(); |
| 342 } | 309 } |
| 343 | 310 |
| 344 void SetTaskActive(int task_id) { | 311 void SetTaskActive(int task_id) { |
| 345 arc::mojom::AppInstance* app_instance = | 312 arc::mojom::AppInstance* app_instance = GET_APP_INSTANCE(SetTaskActive); |
| 346 GetAppInstance(kTaskSupportMinVersion, kSetActiveTaskStr); | |
| 347 if (!app_instance) | 313 if (!app_instance) |
| 348 return; | 314 return; |
| 349 app_instance->SetTaskActive(task_id); | 315 app_instance->SetTaskActive(task_id); |
| 350 } | 316 } |
| 351 | 317 |
| 352 void CloseTask(int task_id) { | 318 void CloseTask(int task_id) { |
| 353 arc::mojom::AppInstance* app_instance = | 319 arc::mojom::AppInstance* app_instance = GET_APP_INSTANCE(CloseTask); |
| 354 GetAppInstance(kTaskSupportMinVersion, kCloseTaskStr); | |
| 355 if (!app_instance) | 320 if (!app_instance) |
| 356 return; | 321 return; |
| 357 app_instance->CloseTask(task_id); | 322 app_instance->CloseTask(task_id); |
| 358 } | 323 } |
| 359 | 324 |
| 360 void ShowTalkBackSettings() { | 325 void ShowTalkBackSettings() { |
| 361 arc::mojom::IntentHelperInstance* intent_helper_instance = | 326 arc::mojom::IntentHelperInstance* intent_helper_instance = |
| 362 GetIntentHelperInstance(kSendBroadcastMinVersion, kSendBroadcastStr); | 327 GET_INTENT_HELPER_INSTANCE(SendBroadcast); |
| 363 if (!intent_helper_instance) | 328 if (!intent_helper_instance) |
| 364 return; | 329 return; |
| 365 | 330 |
| 366 intent_helper_instance->SendBroadcast(kShowTalkbackSettingsIntent, | 331 intent_helper_instance->SendBroadcast(kShowTalkbackSettingsIntent, |
| 367 kIntentHelperPackageName, | 332 kIntentHelperPackageName, |
| 368 kIntentHelperClassName, "{}"); | 333 kIntentHelperClassName, "{}"); |
| 369 } | 334 } |
| 370 | 335 |
| 371 bool CanHandleResolution(content::BrowserContext* context, | 336 bool CanHandleResolution(content::BrowserContext* context, |
| 372 const std::string& app_id, | 337 const std::string& app_id, |
| 373 const gfx::Rect& rect, | 338 const gfx::Rect& rect, |
| 374 const CanHandleResolutionCallback& callback) { | 339 const CanHandleResolutionCallback& callback) { |
| 375 const ArcAppListPrefs* prefs = ArcAppListPrefs::Get(context); | 340 const ArcAppListPrefs* prefs = ArcAppListPrefs::Get(context); |
| 376 DCHECK(prefs); | 341 DCHECK(prefs); |
| 377 std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id); | 342 std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id); |
| 378 if (!app_info) { | 343 if (!app_info) { |
| 379 VLOG(2) << "Cannot test resolution capability of unavailable app:" << app_id | 344 VLOG(2) << "Cannot test resolution capability of unavailable app:" << app_id |
| 380 << "."; | 345 << "."; |
| 381 return false; | 346 return false; |
| 382 } | 347 } |
| 383 | 348 |
| 384 arc::mojom::AppInstance* app_instance = | 349 arc::mojom::AppInstance* app_instance = GET_APP_INSTANCE(CanHandleResolution); |
| 385 GetAppInstance(kCanHandleResolutionMinVersion, kCanHandleResolutionStr); | |
| 386 if (!app_instance) | 350 if (!app_instance) |
| 387 return false; | 351 return false; |
| 388 | 352 |
| 389 app_instance->CanHandleResolution(app_info->package_name, app_info->activity, | 353 app_instance->CanHandleResolution(app_info->package_name, app_info->activity, |
| 390 rect, callback); | 354 rect, callback); |
| 391 return true; | 355 return true; |
| 392 } | 356 } |
| 393 | 357 |
| 394 void UninstallPackage(const std::string& package_name) { | 358 void UninstallPackage(const std::string& package_name) { |
| 395 VLOG(2) << "Uninstalling " << package_name; | 359 VLOG(2) << "Uninstalling " << package_name; |
| 396 | 360 |
| 397 arc::mojom::AppInstance* app_instance = | 361 arc::mojom::AppInstance* app_instance = GET_APP_INSTANCE(UninstallPackage); |
| 398 GetAppInstance(kUninstallPackageMinVersion, kUninstallPackageStr); | |
| 399 if (!app_instance) | 362 if (!app_instance) |
| 400 return; | 363 return; |
| 401 | 364 |
| 402 app_instance->UninstallPackage(package_name); | 365 app_instance->UninstallPackage(package_name); |
| 403 } | 366 } |
| 404 | 367 |
| 405 void UninstallArcApp(const std::string& app_id, Profile* profile) { | 368 void UninstallArcApp(const std::string& app_id, Profile* profile) { |
| 406 ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile); | 369 ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile); |
| 407 DCHECK(arc_prefs); | 370 DCHECK(arc_prefs); |
| 408 std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = | 371 std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = |
| 409 arc_prefs->GetApp(app_id); | 372 arc_prefs->GetApp(app_id); |
| 410 if (!app_info) { | 373 if (!app_info) { |
| 411 VLOG(2) << "Package being uninstalled does not exist: " << app_id << "."; | 374 VLOG(2) << "Package being uninstalled does not exist: " << app_id << "."; |
| 412 return; | 375 return; |
| 413 } | 376 } |
| 414 // For shortcut we just remove the shortcut instead of the package. | 377 // For shortcut we just remove the shortcut instead of the package. |
| 415 if (app_info->shortcut) | 378 if (app_info->shortcut) |
| 416 arc_prefs->RemoveApp(app_id); | 379 arc_prefs->RemoveApp(app_id); |
| 417 else | 380 else |
| 418 UninstallPackage(app_info->package_name); | 381 UninstallPackage(app_info->package_name); |
| 419 } | 382 } |
| 420 | 383 |
| 421 void RemoveCachedIcon(const std::string& icon_resource_id) { | 384 void RemoveCachedIcon(const std::string& icon_resource_id) { |
| 422 VLOG(2) << "Removing icon " << icon_resource_id; | 385 VLOG(2) << "Removing icon " << icon_resource_id; |
| 423 | 386 |
| 424 arc::mojom::AppInstance* app_instance = | 387 arc::mojom::AppInstance* app_instance = GET_APP_INSTANCE(RemoveCachedIcon); |
| 425 GetAppInstance(kRemoveIconMinVersion, kRemoveIconStr); | |
| 426 if (!app_instance) | 388 if (!app_instance) |
| 427 return; | 389 return; |
| 428 | 390 |
| 429 app_instance->RemoveCachedIcon(icon_resource_id); | 391 app_instance->RemoveCachedIcon(icon_resource_id); |
| 430 } | 392 } |
| 431 | 393 |
| 432 // Deprecated. | 394 // Deprecated. |
| 433 bool ShowPackageInfo(const std::string& package_name) { | 395 bool ShowPackageInfo(const std::string& package_name) { |
| 434 VLOG(2) << "Showing package info for " << package_name; | 396 VLOG(2) << "Showing package info for " << package_name; |
| 435 | 397 |
| 436 arc::mojom::AppInstance* app_instance = | 398 arc::mojom::AppInstance* app_instance = |
| 437 GetAppInstance(kShowPackageInfoMinVersion, kShowPackageInfoStr); | 399 GET_APP_INSTANCE(ShowPackageInfoDeprecated); |
| 438 if (!app_instance) | 400 if (!app_instance) |
| 439 return false; | 401 return false; |
| 440 | 402 |
| 441 app_instance->ShowPackageInfoDeprecated( | 403 app_instance->ShowPackageInfoDeprecated( |
| 442 package_name, GetTargetRect(gfx::Size(kNexus7Width, kNexus7Height))); | 404 package_name, GetTargetRect(gfx::Size(kNexus7Width, kNexus7Height))); |
| 443 return true; | 405 return true; |
| 444 } | 406 } |
| 445 | 407 |
| 446 bool ShowPackageInfoOnPage(const std::string& package_name, | 408 bool ShowPackageInfoOnPage(const std::string& package_name, |
| 447 mojom::ShowPackageInfoPage page) { | 409 mojom::ShowPackageInfoPage page) { |
| 448 VLOG(2) << "Showing package info for " << package_name; | 410 VLOG(2) << "Showing package info for " << package_name; |
| 449 | 411 |
| 450 arc::mojom::AppInstance* app_instance = | 412 arc::mojom::AppInstance* app_instance = |
| 451 GetAppInstance(kShowPackageInfoOnPageMinVersion, kShowPackageInfoStr); | 413 GET_APP_INSTANCE(ShowPackageInfoOnPage); |
| 452 if (!app_instance) | 414 if (!app_instance) |
| 453 return false; | 415 return false; |
| 454 | 416 |
| 455 app_instance->ShowPackageInfoOnPage( | 417 app_instance->ShowPackageInfoOnPage( |
| 456 package_name, page, | 418 package_name, page, |
| 457 GetTargetRect(gfx::Size(kNexus7Width, kNexus7Height))); | 419 GetTargetRect(gfx::Size(kNexus7Width, kNexus7Height))); |
| 458 return true; | 420 return true; |
| 459 } | 421 } |
| 460 | 422 |
| 461 bool IsArcItem(content::BrowserContext* context, const std::string& id) { | 423 bool IsArcItem(content::BrowserContext* context, const std::string& id) { |
| 462 DCHECK(context); | 424 DCHECK(context); |
| 463 | 425 |
| 464 // Some unit tests use empty id. | 426 // Some unit tests use empty id. |
| 465 if (id.empty()) | 427 if (id.empty()) |
| 466 return false; | 428 return false; |
| 467 | 429 |
| 468 const ArcAppListPrefs* const arc_prefs = ArcAppListPrefs::Get(context); | 430 const ArcAppListPrefs* const arc_prefs = ArcAppListPrefs::Get(context); |
| 469 if (!arc_prefs) | 431 if (!arc_prefs) |
| 470 return false; | 432 return false; |
| 471 | 433 |
| 472 return arc_prefs->IsRegistered(ArcAppShelfId::FromString(id).app_id()); | 434 return arc_prefs->IsRegistered(ArcAppShelfId::FromString(id).app_id()); |
| 473 } | 435 } |
| 474 | 436 |
| 475 } // namespace arc | 437 } // namespace arc |
| OLD | NEW |