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