1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chromeos/services/machine_learning/public/cpp/handwriting_model_loader.h"
6 
7 #include <string>
8 #include <utility>
9 
10 #include "base/callback_helpers.h"
11 #include "base/command_line.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "chromeos/services/machine_learning/public/cpp/service_connection.h"
14 #include "third_party/cros_system_api/dbus/service_constants.h"
15 
16 namespace chromeos {
17 namespace machine_learning {
18 namespace {
19 
20 using ::chromeos::machine_learning::mojom::HandwritingRecognizerSpecPtr;
21 using ::chromeos::machine_learning::mojom::LoadHandwritingModelResult;
22 using HandwritingRecognizer =
23     mojo::PendingReceiver<mojom::HandwritingRecognizer>;
24 using LoadHandwritingModelCallback = ::chromeos::machine_learning::mojom::
25     MachineLearningService::LoadHandwritingModelCallback;
26 
27 // Records CrOSActionRecorder event.
RecordLoadHandwritingModelResult(const LoadHandwritingModelResult val)28 void RecordLoadHandwritingModelResult(const LoadHandwritingModelResult val) {
29   UMA_HISTOGRAM_ENUMERATION(
30       "MachineLearningService.HandwritingModel.LoadModelResult.Event", val,
31       LoadHandwritingModelResult::LOAD_MODEL_FILES_ERROR);
32 }
33 
34 constexpr char kOndeviceHandwritingSwitch[] = "ondevice_handwriting";
35 constexpr char kLibHandwritingDlcId[] = "libhandwriting";
36 // A list of supported language code.
37 constexpr char kLanguageCodeEn[] = "en";
38 constexpr char kLanguageCodeGesture[] = "gesture_in_context";
39 
40 // Returns whether the `value` is set for command line switch
41 // kOndeviceHandwritingSwitch.
HandwritingSwitchHasValue(const std::string & value)42 bool HandwritingSwitchHasValue(const std::string& value) {
43   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
44   return command_line->HasSwitch(kOndeviceHandwritingSwitch) &&
45          command_line->GetSwitchValueASCII(kOndeviceHandwritingSwitch) == value;
46 }
47 
48 // Returns true if switch kOndeviceHandwritingSwitch is set to use_rootfs.
IsLibHandwritingRootfsEnabled()49 bool IsLibHandwritingRootfsEnabled() {
50   return HandwritingSwitchHasValue("use_rootfs");
51 }
52 
53 // Returns true if switch kOndeviceHandwritingSwitch is set to use_dlc.
IsLibHandwritingDlcEnabled()54 bool IsLibHandwritingDlcEnabled() {
55   return HandwritingSwitchHasValue("use_dlc");
56 }
57 
58 // Called when InstallDlc completes.
59 // Returns an error if the `result.error` is not dlcservice::kErrorNone.
60 // Calls mlservice to LoadHandwritingModel otherwise.
OnInstallDlcComplete(HandwritingRecognizerSpecPtr spec,HandwritingRecognizer receiver,LoadHandwritingModelCallback callback,const chromeos::DlcserviceClient::InstallResult & result)61 void OnInstallDlcComplete(
62     HandwritingRecognizerSpecPtr spec,
63     HandwritingRecognizer receiver,
64     LoadHandwritingModelCallback callback,
65     const chromeos::DlcserviceClient::InstallResult& result) {
66   // Call LoadHandwritingModelWithSpec if no error was found.
67   if (result.error == dlcservice::kErrorNone) {
68     ServiceConnection::GetInstance()->LoadHandwritingModel(
69         std::move(spec), std::move(receiver), std::move(callback));
70     return;
71   }
72 
73   RecordLoadHandwritingModelResult(
74       LoadHandwritingModelResult::DLC_INSTALL_ERROR);
75   std::move(callback).Run(LoadHandwritingModelResult::DLC_INSTALL_ERROR);
76 }
77 
78 // Called when the existing-dlc-list is returned.
79 // Returns an error if libhandwriting is not in the existing-dlc-list.
80 // Calls InstallDlc otherwise.
OnGetExistingDlcsComplete(HandwritingRecognizerSpecPtr spec,HandwritingRecognizer receiver,LoadHandwritingModelCallback callback,DlcserviceClient * const dlc_client,const std::string & err,const dlcservice::DlcsWithContent & dlcs_with_content)81 void OnGetExistingDlcsComplete(
82     HandwritingRecognizerSpecPtr spec,
83     HandwritingRecognizer receiver,
84     LoadHandwritingModelCallback callback,
85     DlcserviceClient* const dlc_client,
86     const std::string& err,
87     const dlcservice::DlcsWithContent& dlcs_with_content) {
88   // Loop over dlcs_with_content, and installs libhandwriting if already exists.
89   // Since we don't want to trigger downloading here, we only install(mount)
90   // the handwriting dlc if it is already on device.
91   for (const auto& dlc_info : dlcs_with_content.dlc_infos()) {
92     if (dlc_info.id() == kLibHandwritingDlcId) {
93       dlc_client->Install(
94           kLibHandwritingDlcId,
95           base::BindOnce(&OnInstallDlcComplete, std::move(spec),
96                          std::move(receiver), std::move(callback)),
97           base::DoNothing());
98       return;
99     }
100   }
101 
102   // Returns error if the handwriting dlc is not on the device.
103   RecordLoadHandwritingModelResult(
104       LoadHandwritingModelResult::DLC_DOES_NOT_EXIST);
105   std::move(callback).Run(LoadHandwritingModelResult::DLC_DOES_NOT_EXIST);
106 }
107 
108 }  // namespace
109 
LoadHandwritingModelFromRootfsOrDlc(HandwritingRecognizerSpecPtr spec,HandwritingRecognizer receiver,LoadHandwritingModelCallback callback,DlcserviceClient * const dlc_client)110 void LoadHandwritingModelFromRootfsOrDlc(HandwritingRecognizerSpecPtr spec,
111                                          HandwritingRecognizer receiver,
112                                          LoadHandwritingModelCallback callback,
113                                          DlcserviceClient* const dlc_client) {
114   // Returns FEATURE_NOT_SUPPORTED_ERROR if both rootfs and dlc are not enabled.
115   if (!IsLibHandwritingRootfsEnabled() && !IsLibHandwritingDlcEnabled()) {
116     RecordLoadHandwritingModelResult(
117         LoadHandwritingModelResult::FEATURE_NOT_SUPPORTED_ERROR);
118     std::move(callback).Run(
119         LoadHandwritingModelResult::FEATURE_NOT_SUPPORTED_ERROR);
120     return;
121   }
122 
123   // Returns LANGUAGE_NOT_SUPPORTED_ERROR if the language is not supported yet.
124   if (spec->language != kLanguageCodeEn &&
125       spec->language != kLanguageCodeGesture) {
126     RecordLoadHandwritingModelResult(
127         LoadHandwritingModelResult::LANGUAGE_NOT_SUPPORTED_ERROR);
128     std::move(callback).Run(
129         LoadHandwritingModelResult::LANGUAGE_NOT_SUPPORTED_ERROR);
130     return;
131   }
132 
133   // Load from rootfs if enabled.
134   if (IsLibHandwritingRootfsEnabled()) {
135     ServiceConnection::GetInstance()->LoadHandwritingModel(
136         std::move(spec), std::move(receiver), std::move(callback));
137     return;
138   }
139 
140   // Gets existing dlc list and based on the presence of libhandwriting
141   // either returns an error or installs the libhandwriting dlc.
142   dlc_client->GetExistingDlcs(
143       base::BindOnce(&OnGetExistingDlcsComplete, std::move(spec),
144                      std::move(receiver), std::move(callback), dlc_client));
145 }
146 
147 }  // namespace machine_learning
148 }  // namespace chromeos
149