File: arc_app_utils.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (724 lines) | stat: -rw-r--r-- 28,634 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/app_list/arc/arc_app_utils.h"

#include <map>
#include <memory>
#include <string>
#include <tuple>
#include <utility>

#include "base/check.h"
#include "base/check_is_test.h"
#include "base/json/json_writer.h"
#include "base/metrics/histogram_functions.h"
#include "base/no_destructor.h"
#include "base/observer_list.h"
#include "base/scoped_multi_source_observation.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "chrome/browser/apps/app_service/intent_util.h"
#include "chrome/browser/ash/app_list/app_list_client_impl.h"
#include "chrome/browser/ash/app_list/arc/intent.h"
#include "chrome/browser/ash/app_list/search/ranking/launch_data.h"
#include "chrome/browser/ash/app_list/search/search_controller.h"
#include "chrome/browser/ash/app_list/search/types.h"
#include "chrome/browser/ash/arc/arc_migration_guide_notification.h"
#include "chrome/browser/ash/arc/arc_util.h"
#include "chrome/browser/ash/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h"
#include "chrome/browser/ash/arc/notification/arc_management_transition_notification.h"
#include "chrome/browser/ash/arc/session/arc_session_manager.h"
#include "chrome/browser/ash/arc/vmm/arc_vmm_manager.h"
#include "chrome/browser/ash/arc/window_predictor/window_predictor.h"
#include "chrome/browser/ash/arc/window_predictor/window_predictor_utils.h"
#include "chrome/browser/ash/login/login_pref_names.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_observer.h"
#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
#include "chrome/browser/ui/ash/shelf/arc_shelf_spinner_item_controller.h"
#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
#include "chromeos/ash/experiences/arc/app/arc_app_launch_notifier.h"
#include "chromeos/ash/experiences/arc/arc_features.h"
#include "chromeos/ash/experiences/arc/arc_prefs.h"
#include "chromeos/ash/experiences/arc/arc_util.h"
#include "chromeos/ash/experiences/arc/intent_helper/arc_intent_helper_package.h"
#include "chromeos/ash/experiences/arc/metrics/arc_metrics_constants.h"
#include "chromeos/ash/experiences/arc/metrics/arc_metrics_service.h"
#include "chromeos/ash/experiences/arc/mojom/intent_helper.mojom.h"
#include "chromeos/ash/experiences/arc/session/arc_bridge_service.h"
#include "chromeos/ash/experiences/arc/session/arc_service_manager.h"
#include "components/app_restore/app_restore_utils.h"
#include "components/app_restore/features.h"
#include "components/application_locale_storage/application_locale_storage.h"
#include "components/language/core/browser/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/services/app_service/public/cpp/intent_util.h"
#include "content/public/browser/browser_context.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/event_constants.h"
#include "ui/gfx/geometry/rect.h"

// Helper macro which returns the AppInstance.
#define GET_APP_INSTANCE(method_name)                                    \
  (arc::ArcServiceManager::Get()                                         \
       ? ARC_GET_INSTANCE_FOR_METHOD(                                    \
             arc::ArcServiceManager::Get()->arc_bridge_service()->app(), \
             method_name)                                                \
       : nullptr)

// Helper function which returns the IntentHelperInstance.
#define GET_INTENT_HELPER_INSTANCE(method_name)                    \
  (arc::ArcServiceManager::Get()                                   \
       ? ARC_GET_INSTANCE_FOR_METHOD(arc::ArcServiceManager::Get() \
                                         ->arc_bridge_service()    \
                                         ->intent_helper(),        \
                                     method_name)                  \
       : nullptr)

namespace arc {

namespace {

// TODO(djacobo): Evaluate to build these strings by using
// ArcIntentHelperBridge::AppendStringToIntentHelperPackageName.
// Intent helper strings.
constexpr char kIntentHelperClassName[] =
    "org.chromium.arc.intent_helper.SettingsReceiver";
constexpr char kSetInTouchModeIntent[] =
    "org.chromium.arc.intent_helper.SET_IN_TOUCH_MODE";

constexpr char kAndroidClockAppId[] = "ddmmnabaeomoacfpfjgghfpocfolhjlg";
constexpr char kAndroidFilesAppId[] = "gmiohhmfhgfclpeacmdfancbipocempm";

constexpr char const* kAppIdsHiddenInLauncher[] = {
    kAndroidClockAppId,    kSettingsAppId,  kAndroidFilesAppId,
    kAndroidContactsAppId, kPlayGamesAppId, kPackageInstallerAppId};

// Returns true if |event_flags| came from a mouse or touch event.
bool IsMouseOrTouchEventFromFlags(int event_flags) {
  return (event_flags & (ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON |
                         ui::EF_RIGHT_MOUSE_BUTTON | ui::EF_BACK_MOUSE_BUTTON |
                         ui::EF_FORWARD_MOUSE_BUTTON | ui::EF_FROM_TOUCH)) != 0;
}

bool Launch(Profile* profile,
            const std::string& app_id,
            apps::IntentPtr intent,
            int event_flags,
            arc::mojom::WindowInfoPtr window_info) {
  ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile);
  CHECK(prefs);

  std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id);
  if (!app_info) {
    VLOG(2) << "Cannot launch unavailable app: " << app_id << ".";
    return false;
  }

  if (!app_info->ready) {
    VLOG(2) << "Cannot launch not-ready app: " << app_id << ".";
    return false;
  }

  if (!app_info->launchable) {
    VLOG(2) << "Cannot launch non-launchable app: " << app_id << ".";
    return false;
  }

  if (app_info->suspended) {
    VLOG(2) << "Cannot launch suspended app: " << app_id << ".";
    return false;
  }

  if (IsMouseOrTouchEventFromFlags(event_flags))
    SetTouchMode(IsMouseOrTouchEventFromFlags(event_flags));

  // Unthrottle the ARC instance before launching an ARC app. This is done
  // to minimize lag on an app launch.
  auto* notifier = ArcAppLaunchNotifier::GetForBrowserContext(profile);
  if (notifier) {
    // ArcAppLaunchNotifier may not exist in test environment.
    notifier->NotifyArcAppLaunchRequest(app_info->package_name);
  } else {
    CHECK_IS_TEST();
  }

  if (app_info->shortcut || intent) {
    const std::string intent_uri =
        intent ? apps_util::CreateLaunchIntent(app_info->package_name, intent)
               : app_info->intent_uri;
    if (intent_uri.empty()) {
      // If |intent| can't be converted to a string, call the HandleIntent
      // interface.
      arc::mojom::ActivityNamePtr activity = arc::mojom::ActivityName::New();
      activity->package_name = app_info->package_name;
      if (intent->activity_name.has_value() &&
          !intent->activity_name.value().empty()) {
        activity->activity_name = intent->activity_name.value();
      }

      auto arc_intent =
          apps_util::ConvertAppServiceToArcIntent(std::move(intent));

      if (!arc_intent) {
        LOG(ERROR) << "Launch App failed, launch intent is not valid";
        return false;
      }

      arc::mojom::IntentHelperInstance* instance =
          GET_INTENT_HELPER_INSTANCE(HandleIntentWithWindowInfo);
      if (instance) {
        instance->HandleIntentWithWindowInfo(
            std::move(arc_intent), std::move(activity), std::move(window_info));
      } else {
        return false;
      }
    } else {
      // If |intent| can be converted to a string, call the Launch interface.
      if (auto* app_instance = GET_APP_INSTANCE(LaunchIntentWithWindowInfo)) {
        app_instance->LaunchIntentWithWindowInfo(intent_uri,
                                                 std::move(window_info));
      } else {
        return false;
      }
    }
  } else {
    if (auto* app_instance = GET_APP_INSTANCE(LaunchAppWithWindowInfo)) {
      app_instance->LaunchAppWithWindowInfo(
          app_info->package_name, app_info->activity, std::move(window_info));
    } else {
      return false;
    }
  }
  prefs->SetLastLaunchTime(app_id);

  return true;
}

// Returns primary display id if |display_id| is invalid.
int64_t GetValidDisplayId(int64_t display_id) {
  if (display_id != display::kInvalidDisplayId)
    return display_id;
  if (auto* screen = display::Screen::GetScreen())
    return screen->GetPrimaryDisplay().id();
  return display::kInvalidDisplayId;
}

// Converts an app_id and a shortcut_id, eg. manifest_new_note_shortcut, into a
// full URL for an Arc app shortcut, of the form:
// appshortcutsearch://[app_id]/[shortcut_id].
std::string ConstructArcAppShortcutUrl(const std::string& app_id,
                                       const std::string& shortcut_id) {
  return "appshortcutsearch://" + app_id + "/" + shortcut_id;
}

bool IsArcVmAndSwappedOut(content::BrowserContext* context) {
  return IsArcVmEnabled() &&
         base::FeatureList::IsEnabled(arc::kVmmSwapoutGhostWindow) &&
         ArcVmmManager::GetForBrowserContext(context)->IsSwapped();
}

}  // namespace

bool ShouldShowInLauncher(const std::string& app_id) {
  for (auto* const id : kAppIdsHiddenInLauncher) {
    if (id == app_id)
      return false;
  }
  return true;
}

arc::mojom::WindowInfoPtr MakeWindowInfo(int64_t display_id) {
  arc::mojom::WindowInfoPtr window_info = arc::mojom::WindowInfo::New();
  window_info->display_id = display_id;
  return window_info;
}

bool LaunchApp(content::BrowserContext* context,
               const std::string& app_id,
               int event_flags,
               arc::UserInteractionType user_action) {
  return LaunchAppWithIntent(context, app_id, nullptr /* launch_intent */,
                             event_flags, user_action,
                             MakeWindowInfo(display::kInvalidDisplayId));
}

bool LaunchApp(content::BrowserContext* context,
               const std::string& app_id,
               int event_flags,
               arc::UserInteractionType user_action,
               arc::mojom::WindowInfoPtr window_info) {
  return LaunchAppWithIntent(context, app_id, nullptr /* launch_intent */,
                             event_flags, user_action, std::move(window_info));
}

bool LaunchAppWithIntent(content::BrowserContext* context,
                         const std::string& app_id,
                         apps::IntentPtr launch_intent,
                         int event_flags,
                         arc::UserInteractionType user_action,
                         arc::mojom::WindowInfoPtr window_info) {
  if (user_action != UserInteractionType::NOT_USER_INITIATED)
    arc::ArcMetricsService::RecordArcUserInteraction(context, user_action);

  Profile* const profile = Profile::FromBrowserContext(context);

  // Even when ARC is not allowed for the profile, ARC apps may still show up
  // as a placeholder to show the guide notification for proper configuration.
  // Handle such a case here and shows the desired notification.
  if (IsArcBlockedDueToIncompatibleFileSystem(profile)) {
    VLOG(1) << "Attempt to launch " << app_id
            << " while ARC++ is blocked due to incompatible file system.";
    arc::ShowArcMigrationGuideNotification(profile);
    return false;
  }

  // In case management transition is in progress ARC++ is not available.
  const ArcManagementTransition management_transition =
      GetManagementTransition(profile);
  if (management_transition != ArcManagementTransition::NO_TRANSITION) {
    VLOG(1) << "Attempt to launch " << app_id << " while management transition "
            << management_transition << " is in progress.";
    arc::ShowManagementTransitionNotification(profile);
    return false;
  }

  // Update display id.
  if (window_info)
    window_info->display_id = GetValidDisplayId(window_info->display_id);

  // Activate ARC in case still not active.
  ArcSessionManager::Get()->AllowActivation(
      ArcSessionManager::AllowActivationReason::kUserLaunchAction);

  ArcAppListPrefs* const prefs = ArcAppListPrefs::Get(context);
  std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id);
  apps::IntentPtr launch_intent_to_send = std::move(launch_intent);

  if (!app_info) {
    LOG(WARNING) << "Ignore invalid app launch quest, id = " << app_id;
    return false;
  }

  // Some apps need fixup when ARC version upgrade e.g. from ARC P to ARC R.
  // Before fixup finishes, the |app_info->ready| is true but not launchable.
  if (app_info->need_fixup || !app_info->ready) {
    if (!IsArcPlayStoreEnabledForProfile(profile)) {
      if (prefs->IsDefault(app_id)) {
        // The setting can fail if the preference is managed.  However, the
        // caller is responsible to not call this function in such case.  DCHECK
        // is here to prevent possible mistake.
        if (!SetArcPlayStoreEnabledForProfile(profile, true))
          return false;
        DCHECK(IsArcPlayStoreEnabledForProfile(profile));

        // PlayStore item has special handling for shelf controllers. In order
        // to avoid unwanted initial animation for PlayStore item do not create
        // deferred launch request when PlayStore item enables Google Play
        // Store.
        if (app_id == kPlayStoreAppId) {
          prefs->SetLastLaunchTime(app_id);
          return true;
        }
      } else {
        // Only reachable when ARC always starts.
        DCHECK(arc::ShouldArcAlwaysStart());
      }
    } else {
      // Handle the case when default app tries to re-activate OptIn flow.
      if (IsArcPlayStoreEnabledPreferenceManagedForProfile(profile) &&
          !ArcSessionManager::Get()->enable_requested() &&
          prefs->IsDefault(app_id)) {
        SetArcPlayStoreEnabledForProfile(profile, true);
        // PlayStore item has special handling for shelf controllers. In order
        // to avoid unwanted initial animation for PlayStore item do not create
        // deferred launch request when PlayStore item enables Google Play
        // Store.
        if (app_id == kPlayStoreAppId) {
          prefs->SetLastLaunchTime(app_id);
          return true;
        }
      }
    }

    // TODO(sstan): Triage ghost window for different launch source.
    // App launched by user rather than full restore.
    if (window_info &&
        window_info->window_id <=
            app_restore::kArcSessionIdOffsetForRestoredLaunching) {
      arc::ArcBootPhaseMonitorBridge::RecordFirstAppLaunchDelayUMA(context);
    }

    if (app_info->need_fixup) {
      // TODO(sstan): Use different UI after UX design finalized.
      if (WindowPredictor::GetInstance()->LaunchArcAppWithGhostWindow(
              profile, app_id, *app_info, launch_intent_to_send, event_flags,
              GhostWindowType::kFixup, WindowPredictorUseCase::kArcNotReady,
              window_info)) {
        prefs->SetLastLaunchTime(app_id);
        return true;
      }
      // Block launch request if failed to launch ghost window.
      return false;
    } else if (full_restore::features::IsArcWindowPredictorEnabled() &&
               arc::GetArcAndroidSdkVersionAsInt() >= arc::kArcVersionR) {
      if (WindowPredictor::GetInstance()->LaunchArcAppWithGhostWindow(
              profile, app_id, *app_info, launch_intent_to_send, event_flags,
              GhostWindowType::kAppLaunch, WindowPredictorUseCase::kArcNotReady,
              window_info)) {
        prefs->SetLastLaunchTime(app_id);
        return true;
      }
      VLOG(2) << "Failed to launch ghost window, fallback to use shelf spinner";
    }

    ChromeShelfController* chrome_controller =
        ChromeShelfController::instance();
    // chrome_controller may be null in tests.
    if (chrome_controller) {
      chrome_controller->GetShelfSpinnerController()->AddSpinnerToShelf(
          app_id, std::make_unique<ArcShelfSpinnerItemController>(
                      app_id, std::move(launch_intent_to_send), event_flags,
                      user_action, std::move(window_info)));

      // On some boards, ARC is booted with a restricted set of resources by
      // default to avoid slowing down Chrome's user session restoration.
      // However, the restriction should be lifted once the user explicitly
      // tries to launch an ARC app.
      auto* notifier = ArcAppLaunchNotifier::GetForBrowserContext(profile);
      if (notifier) {
        // ArcAppLaunchNotifier may not exist in test environment.
        notifier->NotifyArcAppLaunchRequest(app_info->package_name);
      } else {
        CHECK_IS_TEST();
      }
    }
    prefs->SetLastLaunchTime(app_id);
    return true;
  } else if (IsArcVmAndSwappedOut(context) &&
             !WindowPredictor::GetInstance()->IsAppPendingLaunch(profile,
                                                                 app_id)) {
    // Assume this condition branch will never be triggered in ARCVM launch (ARC
    // booting) stage. It should be trigger after ARCVM idle for a while.
    if (WindowPredictor::GetInstance()->LaunchArcAppWithGhostWindow(
            profile, app_id, *app_info, launch_intent_to_send, event_flags,
            GhostWindowType::kAppLaunch, WindowPredictorUseCase::kArcVmmSwapped,
            window_info)) {
      return true;
    }
    VLOG(2) << "Failed to launch ghost window for swapped state, fallback to "
               "launch directly.";
  } else if (app_id == kPlayStoreAppId) {
    // Record launch request time in order to track Play Store default launch
    // performance.
    if (!launch_intent_to_send) {
      launch_intent_to_send =
          std::make_unique<apps::Intent>(apps_util::kIntentActionMain);
      launch_intent_to_send->categories.push_back(kCategoryLauncher);
      launch_intent_to_send->activity_name = kPlayStoreActivity;
    }
    launch_intent_to_send->extras[kRequestStartTimeParamKey] =
        base::NumberToString(
            (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds());
  }

  arc::ArcBootPhaseMonitorBridge::RecordFirstAppLaunchDelayUMA(context);
  return Launch(profile, app_id, std::move(launch_intent_to_send), event_flags,
                std::move(window_info));
}

bool LaunchAppShortcutItem(content::BrowserContext* context,
                           const std::string& app_id,
                           const std::string& shortcut_id,
                           int64_t display_id) {
  std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
      ArcAppListPrefs::Get(context)->GetApp(app_id);
  if (!app_info) {
    LOG(ERROR) << "App " << app_id << " is not available.";
    return false;
  }

  mojom::AppInstance* app_instance =
      ArcServiceManager::Get()
          ? ARC_GET_INSTANCE_FOR_METHOD(
                ArcServiceManager::Get()->arc_bridge_service()->app(),
                LaunchAppShortcutItem)
          : nullptr;

  if (!app_instance) {
    LOG(ERROR) << "Cannot find a mojo instance, ARC is unreachable or mojom"
               << " version mismatch.";
    return false;
  }

  app_instance->LaunchAppShortcutItem(app_info->package_name, shortcut_id,
                                      GetValidDisplayId(display_id));
  return true;
}

void UpdateWindowInfo(arc::mojom::WindowInfoPtr window_info) {
  arc::mojom::AppInstance* app_instance = GET_APP_INSTANCE(UpdateWindowInfo);
  if (!app_instance) {
    LOG(ERROR) << "Cannot find a mojo instance, ARC is unreachable or mojom"
               << " version mismatch.";
    return;
  }
  app_instance->UpdateWindowInfo(std::move(window_info));
}

void SetTaskActive(int task_id) {
  arc::mojom::AppInstance* app_instance = GET_APP_INSTANCE(SetTaskActive);
  if (!app_instance)
    return;
  app_instance->SetTaskActive(task_id);
}

void CloseTask(int task_id) {
  arc::mojom::AppInstance* app_instance = GET_APP_INSTANCE(CloseTask);
  if (!app_instance)
    return;
  app_instance->CloseTask(task_id);
}

bool SetTouchMode(bool enable) {
  arc::mojom::IntentHelperInstance* intent_helper_instance =
      GET_INTENT_HELPER_INSTANCE(SendBroadcast);
  if (!intent_helper_instance)
    return false;

  base::Value::Dict extras;
  extras.Set("inTouchMode", enable);
  std::string extras_string;
  base::JSONWriter::Write(base::Value(std::move(extras)), &extras_string);
  intent_helper_instance->SendBroadcast(kSetInTouchModeIntent,
                                        kArcIntentHelperPackageName,
                                        kIntentHelperClassName, extras_string);

  return true;
}

std::vector<std::string> GetSelectedPackagesFromPrefs(
    content::BrowserContext* context) {
  std::vector<std::string> packages;
  const Profile* const profile = Profile::FromBrowserContext(context);
  const PrefService* prefs = profile->GetPrefs();

  const base::Value::List& selected_package_prefs =
      prefs->GetList(arc::prefs::kArcFastAppReinstallPackages);
  for (const base::Value& item : selected_package_prefs) {
    std::string item_str = item.is_string() ? item.GetString() : std::string();
    packages.push_back(std::move(item_str));
  }

  return packages;
}

void StartFastAppReinstallFlow(const std::vector<std::string>& package_names) {
  arc::mojom::AppInstance* app_instance =
      GET_APP_INSTANCE(StartFastAppReinstallFlow);
  if (!app_instance) {
    LOG(ERROR) << "Failed to start Fast App Reinstall flow because app "
                  "instance is not connected.";
    return;
  }
  app_instance->StartFastAppReinstallFlow(package_names);
}

void UninstallPackage(const std::string& package_name) {
  VLOG(2) << "Uninstalling " << package_name;

  arc::mojom::AppInstance* app_instance = GET_APP_INSTANCE(UninstallPackage);
  if (!app_instance)
    return;

  app_instance->UninstallPackage(package_name);
}

void UninstallArcApp(const std::string& app_id, Profile* profile) {
  ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile);
  DCHECK(arc_prefs);
  std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
      arc_prefs->GetApp(app_id);
  if (!app_info) {
    VLOG(2) << "Package being uninstalled does not exist: " << app_id << ".";
    return;
  }
  // For shortcut we just remove the shortcut instead of the package.
  if (app_info->shortcut)
    arc_prefs->RemoveApp(app_id);
  else
    UninstallPackage(app_info->package_name);
}

void RemoveCachedIcon(const std::string& icon_resource_id) {
  VLOG(2) << "Removing icon " << icon_resource_id;

  arc::mojom::AppInstance* app_instance = GET_APP_INSTANCE(RemoveCachedIcon);
  if (!app_instance)
    return;

  app_instance->RemoveCachedIcon(icon_resource_id);
}

bool ShowPackageInfo(const std::string& package_name,
                     mojom::ShowPackageInfoPage page,
                     int64_t display_id) {
  VLOG(2) << "Showing package info for " << package_name;

  if (auto* app_instance = GET_APP_INSTANCE(ShowPackageInfoOnPage)) {
    app_instance->ShowPackageInfoOnPage(package_name, page, display_id);
    return true;
  }

  if (auto* app_instance = GET_APP_INSTANCE(ShowPackageInfoOnPageDeprecated)) {
    app_instance->ShowPackageInfoOnPageDeprecated(package_name, page,
                                                  gfx::Rect());
    return true;
  }

  if (auto* app_instance = GET_APP_INSTANCE(ShowPackageInfoDeprecated)) {
    app_instance->ShowPackageInfoDeprecated(package_name, gfx::Rect());
    return true;
  }

  return false;
}

bool IsArcItem(content::BrowserContext* context, const std::string& id) {
  DCHECK(context);

  // Some unit tests use empty ids, some app ids are not valid ARC app ids.
  const ArcAppShelfId arc_app_shelf_id = ArcAppShelfId::FromString(id);
  if (!arc_app_shelf_id.valid())
    return false;

  const ArcAppListPrefs* const arc_prefs = ArcAppListPrefs::Get(context);
  if (!arc_prefs)
    return false;

  return arc_prefs->IsRegistered(arc_app_shelf_id.app_id());
}

void GetLocaleAndPreferredLanguages(
    const ApplicationLocaleStorage& application_locale_storage,
    const Profile* profile,
    std::string* out_locale,
    std::string* out_preferred_languages) {
  const PrefService::Preference* locale_pref =
      profile->GetPrefs()->FindPreference(
          ::language::prefs::kApplicationLocale);
  DCHECK(locale_pref);
  const std::string& locale = locale_pref->GetValue()->GetString();
  *out_locale = locale.empty() ? application_locale_storage.Get() : locale;

  // |preferredLanguages| consists of comma separated locale strings. It may be
  // empty or contain empty items, but those are ignored on ARC.  If an item
  // has no country code, it is derived in ARC.  In such a case, it may
  // conflict with another item in the list, then these will be dedupped (the
  // first one is taken) in ARC.
  *out_preferred_languages =
      profile->GetPrefs()->GetString(::language::prefs::kPreferredLanguages);
}

void GetAndroidId(
    base::OnceCallback<void(bool ok, int64_t android_id)> callback) {
  auto* app_instance = GET_APP_INSTANCE(GetAndroidId);
  if (!app_instance) {
    std::move(callback).Run(false, 0);
    return;
  }

  app_instance->GetAndroidId(base::BindOnce(std::move(callback), true));
}

std::string AppIdToArcPackageName(const std::string& app_id, Profile* profile) {
  ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile);
  std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
      arc_prefs->GetApp(app_id);

  if (!app_info) {
    DLOG(ERROR) << "Couldn't retrieve ARC package name for AppID: " << app_id;
    return std::string();
  }
  return app_info->package_name;
}

std::string ArcPackageNameToAppId(const std::string& package_name,
                                  Profile* profile) {
  DCHECK(profile);
  ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile);
  return arc_prefs ? arc_prefs->GetAppIdByPackageName(package_name)
                   : std::string();
}

const std::string GetAppFromAppOrGroupId(content::BrowserContext* context,
                                         const std::string& app_or_group_id) {
  const arc::ArcAppShelfId app_shelf_id =
      arc::ArcAppShelfId::FromString(app_or_group_id);
  if (!app_shelf_id.has_shelf_group_id())
    return app_shelf_id.app_id();

  const ArcAppListPrefs* const prefs = ArcAppListPrefs::Get(context);
  DCHECK(prefs);

  // Try to find a shortcut with requested shelf group id.
  const std::vector<std::string> app_ids = prefs->GetAppIds();
  for (const auto& app_id : app_ids) {
    std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id);
    DCHECK(app_info);
    if (!app_info || !app_info->shortcut)
      continue;
    const arc::ArcAppShelfId shortcut_shelf_id =
        arc::ArcAppShelfId::FromIntentAndAppId(app_info->intent_uri, app_id);
    if (shortcut_shelf_id.has_shelf_group_id() &&
        shortcut_shelf_id.shelf_group_id() == app_shelf_id.shelf_group_id()) {
      return app_id;
    }
  }

  // Shortcut with requested shelf group id was not found, use app id as
  // fallback.
  return app_shelf_id.app_id();
}

void ExecuteArcShortcutCommand(content::BrowserContext* context,
                               const std::string& id,
                               const std::string& shortcut_id,
                               int64_t display_id) {
  const arc::ArcAppShelfId arc_shelf_id = arc::ArcAppShelfId::FromString(id);
  DCHECK(arc_shelf_id.valid());
  arc::LaunchAppShortcutItem(context, arc_shelf_id.app_id(), shortcut_id,
                             display_id);

  // Send a training signal to the search controller.
  AppListClientImpl* app_list_client_impl = AppListClientImpl::GetInstance();
  if (!app_list_client_impl)
    return;

  app_list::LaunchData launch_data;
  // TODO(crbug.com/40177716): This should set launch_data.launched_from.
  launch_data.id =
      ConstructArcAppShortcutUrl(arc_shelf_id.app_id(), shortcut_id),
  launch_data.result_type = ash::AppListSearchResultType::kArcAppShortcut;
  launch_data.category = app_list::Category::kAppShortcuts;
  app_list_client_impl->search_controller()->Train(std::move(launch_data));
}

void RecordPlayStoreLaunchWithinAWeek(PrefService* prefs, bool launched) {
  if (!prefs->GetBoolean(arc::prefs::kArcPlayStoreLaunchMetricCanBeRecorded))
    return;
  auto time_oobe_finished = prefs->GetTime(ash::prefs::kOobeOnboardingTime);
  if (time_oobe_finished.is_null())
    return;
  bool within_a_week = base::Time::Now() - time_oobe_finished < base::Days(7);
  if (within_a_week && !launched)
    return;
  base::UmaHistogramBoolean("Arc.PlayStoreLaunchWithinAWeek", within_a_week);
  prefs->ClearPref(arc::prefs::kArcPlayStoreLaunchMetricCanBeRecorded);
}

}  // namespace arc