1 // Copyright 2017 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 "chrome/browser/chromeos/app_mode/kiosk_app_data_base.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/files/file_util.h"
11 #include "base/task/post_task.h"
12 #include "base/task/task_traits.h"
13 #include "base/task/thread_pool.h"
14 #include "base/threading/scoped_blocking_call.h"
15 #include "chrome/browser/browser_process.h"
16 #include "components/prefs/pref_service.h"
17 #include "components/prefs/scoped_user_pref_update.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "ui/gfx/codec/png_codec.h"
20 
21 using content::BrowserThread;
22 
23 namespace chromeos {
24 
25 namespace {
26 
27 // Keys for local state data.
28 constexpr char kKeyName[] = "name";
29 constexpr char kKeyIcon[] = "icon";
30 
31 // Icon file extension.
32 constexpr char kIconFileExtension[] = ".png";
33 
34 // Save |raw_icon| for given |app_id|.
SaveIconToLocalOnBlockingPool(const base::FilePath & icon_path,std::vector<unsigned char> image_data)35 void SaveIconToLocalOnBlockingPool(const base::FilePath& icon_path,
36                                    std::vector<unsigned char> image_data) {
37   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
38                                                 base::BlockingType::MAY_BLOCK);
39   const base::FilePath dir = icon_path.DirName();
40   if (!base::PathExists(dir) && !base::CreateDirectory(dir)) {
41     LOG(ERROR) << "Failed to create directory to store kiosk icons";
42     return;
43   }
44 
45   const int wrote = base::WriteFile(
46       icon_path, reinterpret_cast<char*>(image_data.data()), image_data.size());
47   if (wrote != static_cast<int>(image_data.size())) {
48     LOG(ERROR) << "Failed to write kiosk icon file";
49   }
50 }
51 
52 }  // namespace
53 
54 // static
55 const char KioskAppDataBase::kKeyApps[] = "apps";
56 
KioskAppDataBase(const std::string & dictionary_name,const std::string & app_id,const AccountId & account_id)57 KioskAppDataBase::KioskAppDataBase(const std::string& dictionary_name,
58                                    const std::string& app_id,
59                                    const AccountId& account_id)
60     : dictionary_name_(dictionary_name),
61       app_id_(app_id),
62       account_id_(account_id) {}
63 
64 KioskAppDataBase::~KioskAppDataBase() = default;
65 
SaveToDictionary(DictionaryPrefUpdate & dict_update)66 void KioskAppDataBase::SaveToDictionary(DictionaryPrefUpdate& dict_update) {
67   DCHECK_CURRENTLY_ON(BrowserThread::UI);
68   const std::string app_key = std::string(kKeyApps) + '.' + app_id_;
69   const std::string name_key = app_key + '.' + kKeyName;
70   const std::string icon_path_key = app_key + '.' + kKeyIcon;
71 
72   dict_update->SetString(name_key, name_);
73   dict_update->SetString(icon_path_key, icon_path_.value());
74 }
75 
SaveIconToDictionary(DictionaryPrefUpdate & dict_update)76 void KioskAppDataBase::SaveIconToDictionary(DictionaryPrefUpdate& dict_update) {
77   DCHECK_CURRENTLY_ON(BrowserThread::UI);
78   const std::string app_key = std::string(kKeyApps) + '.' + app_id_;
79   const std::string icon_path_key = app_key + '.' + kKeyIcon;
80 
81   dict_update->SetString(icon_path_key, icon_path_.value());
82 }
83 
LoadFromDictionary(const base::DictionaryValue & dict,bool lazy_icon_load)84 bool KioskAppDataBase::LoadFromDictionary(const base::DictionaryValue& dict,
85                                           bool lazy_icon_load) {
86   DCHECK_CURRENTLY_ON(BrowserThread::UI);
87   const std::string app_key =
88       std::string(KioskAppDataBase::kKeyApps) + '.' + app_id_;
89   const std::string name_key = app_key + '.' + kKeyName;
90   const std::string icon_path_key = app_key + '.' + kKeyIcon;
91 
92   std::string icon_path_string;
93   // If there is no title stored, do not stop, sometimes only icon is cached.
94   dict.GetString(name_key, &name_);
95 
96   if (!dict.GetString(icon_path_key, &icon_path_string)) {
97     return false;
98   }
99 
100   icon_path_ = base::FilePath(icon_path_string);
101 
102   if (!lazy_icon_load) {
103     DecodeIcon();
104   }
105 
106   return true;
107 }
108 
DecodeIcon()109 void KioskAppDataBase::DecodeIcon() {
110   DCHECK(!icon_path_.empty());
111   kiosk_app_icon_loader_ = std::make_unique<KioskAppIconLoader>(this);
112   kiosk_app_icon_loader_->Start(icon_path_);
113 }
114 
SaveIcon(const SkBitmap & icon,const base::FilePath & cache_dir)115 void KioskAppDataBase::SaveIcon(const SkBitmap& icon,
116                                 const base::FilePath& cache_dir) {
117   DCHECK_CURRENTLY_ON(BrowserThread::UI);
118   std::vector<unsigned char> image_data;
119   if (!gfx::PNGCodec::EncodeBGRASkBitmap(icon, false, &image_data)) {
120     LOG(ERROR) << "Failed to encode kiosk icon";
121     return;
122   }
123 
124   const base::FilePath icon_path =
125       cache_dir.AppendASCII(app_id_).AddExtension(kIconFileExtension);
126   base::ThreadPool::PostTask(FROM_HERE, {base::MayBlock()},
127                              base::BindOnce(&SaveIconToLocalOnBlockingPool,
128                                             icon_path, std::move(image_data)));
129 
130   icon_path_ = icon_path;
131 }
132 
ClearCache()133 void KioskAppDataBase::ClearCache() {
134   DCHECK_CURRENTLY_ON(BrowserThread::UI);
135   PrefService* local_state = g_browser_process->local_state();
136 
137   DictionaryPrefUpdate dict_update(local_state, dictionary_name());
138 
139   const std::string app_key =
140       std::string(KioskAppDataBase::kKeyApps) + '.' + app_id_;
141   dict_update->Remove(app_key, nullptr);
142 
143   if (!icon_path_.empty()) {
144     base::ThreadPool::PostTask(
145         FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
146         base::BindOnce(base::GetDeleteFileCallback(), icon_path_));
147   }
148 }
149 
150 }  // namespace chromeos
151