1 /*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #define MLOG_TAG "MtpFileObserver"
17 #include "mtp_file_observer.h"
18 #include <memory>
19 #include <securec.h>
20 #include <string>
21 #include <sys/inotify.h>
22 #include <unistd.h>
23 #include "media_log.h"
24 #include "mtp_media_library.h"
25
26 using namespace std;
27 namespace OHOS {
28 namespace Media {
29 bool MtpFileObserver::isRunning_ = false;
30 int MtpFileObserver::inotifyFd_ = 0;
31 std::map<int, std::string> MtpFileObserver::watchMap_;
32 std::mutex MtpFileObserver::eventLock_;
33 const int BUF_SIZE = 1024;
34 const int32_t SIZE_ONE = 1;
35 #ifdef HAS_BATTERY_MANAGER_PART
36 const int LOW_BATTERY = 50;
37 #endif
38 const std::string PATH_SEPARATOR = "/";
39 struct MoveInfo {
40 uint32_t cookie;
41 std::string path;
42 } g_moveInfo;
43
EraseFromWatchMap(const std::string & path)44 void MtpFileObserver::EraseFromWatchMap(const std::string &path)
45 {
46 CHECK_AND_RETURN_LOG(!path.empty(), "EraseFromWatchMap path is empty");
47 {
48 lock_guard<mutex> lock(eventLock_);
49 std::vector<int> eraseList;
50 std::string separatorPath = path + PATH_SEPARATOR;
51 for (const auto &item : watchMap_) {
52 // remove the path in watchMap_ which is the subdirectory of the deleted path
53 if (separatorPath.compare(item.second.substr(0, separatorPath.size())) == 0) {
54 eraseList.push_back(item.first);
55 } else if (item.second.compare(path) == 0) {
56 // remove the path in watchMap_
57 eraseList.push_back(item.first);
58 }
59 }
60 for (const auto &i : eraseList) {
61 inotify_rm_watch(inotifyFd_, i);
62 watchMap_.erase(i);
63 }
64 std::vector<int>().swap(eraseList);
65 }
66 }
67
UpdateWatchMap(const std::string & path)68 void MtpFileObserver::UpdateWatchMap(const std::string &path)
69 {
70 CHECK_AND_RETURN_LOG(!path.empty(), "UpdateWatchMap path is empty");
71 CHECK_AND_RETURN_LOG(!g_moveInfo.path.empty(), "UpdateWatchMap removeInfo.path is empty");
72 {
73 lock_guard<mutex> lock(eventLock_);
74 std::string separatorPath = g_moveInfo.path + PATH_SEPARATOR;
75 for (auto &item : watchMap_) {
76 // update the path in watchMap_ which is the subdirectory of the moved path
77 if (separatorPath.compare(item.second.substr(0, separatorPath.size())) == 0) {
78 item.second = path + PATH_SEPARATOR + item.second.substr(separatorPath.size());
79 } else if (item.second.compare(g_moveInfo.path) == 0) {
80 // update the path in watchMap_
81 item.second = path;
82 }
83 }
84 }
85 }
86
DealWatchMap(const inotify_event & event,const std::string & path)87 void MtpFileObserver::DealWatchMap(const inotify_event &event, const std::string &path)
88 {
89 CHECK_AND_RETURN_LOG(!path.empty(), "DealWatchMap path is empty");
90 CHECK_AND_RETURN_LOG((event.mask & IN_ISDIR), "DealWatchMap path is not dir");
91 if (event.mask & IN_DELETE) {
92 // if the path is deleted, remove it from watchMap_
93 EraseFromWatchMap(path);
94 } else if (event.mask & IN_MOVED_FROM) {
95 // if the path is moved from, record the cookie and path
96 g_moveInfo = {
97 .cookie = event.cookie,
98 .path = path
99 };
100 } else if (event.mask & IN_MOVED_TO) {
101 // if the path is moved to, update the path in watchMap_
102 if (g_moveInfo.cookie == event.cookie) {
103 UpdateWatchMap(path);
104 }
105 }
106 }
107
SendEvent(const inotify_event & event,const std::string & path,const ContextSptr & context)108 void MtpFileObserver::SendEvent(const inotify_event &event, const std::string &path, const ContextSptr &context)
109 {
110 string fileName = path + "/" + event.name;
111 std::shared_ptr<MtpEvent> eventPtr = std::make_shared<OHOS::Media::MtpEvent>(context);
112 CHECK_AND_RETURN_LOG(eventPtr != nullptr, "MtpFileObserver SendEvent eventPtr is null");
113 if ((event.mask & IN_CREATE) || (event.mask & IN_MOVED_TO)) {
114 MEDIA_DEBUG_LOG("MtpFileObserver AddInotifyEvents create/MOVED_TO: path:%{private}s", fileName.c_str());
115 MtpMediaLibrary::GetInstance()->ObserverAddPathToMap(fileName);
116 eventPtr->SendObjectAdded(fileName);
117 } else if ((event.mask & IN_DELETE) || (event.mask & IN_MOVED_FROM)) {
118 MEDIA_DEBUG_LOG("MtpFileObserver AddInotifyEvents delete/MOVED_FROM: path:%{private}s", fileName.c_str());
119 uint32_t id = 0;
120 if (MtpMediaLibrary::GetInstance()->GetIdByPath(fileName, id) == 0) {
121 MtpMediaLibrary::GetInstance()->ObserverDeletePathToMap(fileName);
122 eventPtr->SendObjectRemovedByHandle(id);
123 }
124 } else if (event.mask & IN_CLOSE_WRITE) {
125 MEDIA_DEBUG_LOG("MtpFileObserver AddInotifyEvents IN_CLOSE_WRITE : path:%{private}s", fileName.c_str());
126 eventPtr->SendObjectInfoChanged(fileName);
127 }
128 // if the path is a directory and it is moved or deleted, deal with the watchMap_
129 if ((event.mask & IN_ISDIR) && (event.mask & (IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO))) {
130 DealWatchMap(event, fileName);
131 }
132 }
133
AddInotifyEvents(const int & inotifyFd,const ContextSptr & context)134 bool MtpFileObserver::AddInotifyEvents(const int &inotifyFd, const ContextSptr &context)
135 {
136 char eventBuf[BUF_SIZE] = {0};
137
138 int ret = read(inotifyFd, eventBuf, sizeof(eventBuf) - SIZE_ONE);
139 bool cond = (ret < static_cast<int>(sizeof(struct inotify_event)));
140 CHECK_AND_RETURN_RET_LOG(!cond, false, "MtpFileObserver AddInotifyEvents no event");
141
142 struct inotify_event *positionEvent = (struct inotify_event *)eventBuf;
143 struct inotify_event *event;
144 while (ret >= static_cast<int>(sizeof(struct inotify_event))) {
145 event = positionEvent;
146 if (event->len) {
147 bool isFind;
148 map<int, string>::iterator iter;
149 {
150 lock_guard<mutex> lock(eventLock_);
151 iter = watchMap_.find(event->wd);
152 isFind = iter != watchMap_.end();
153 }
154 if (isFind) {
155 string path = iter->second;
156 SendEvent(*event, path, context);
157 }
158 }
159 positionEvent++;
160 ret -= static_cast<int>(sizeof(struct inotify_event));
161 }
162 return true;
163 }
164
SendBattery(const ContextSptr & context)165 void MtpFileObserver::SendBattery(const ContextSptr &context)
166 {
167 #ifdef HAS_BATTERY_MANAGER_PART
168 std::shared_ptr<MtpEvent> eventPtr = std::make_shared<OHOS::Media::MtpEvent>(context);
169 auto battery = make_shared<MtpOperationUtils>(context);
170 if (LOW_BATTERY >= battery->GetBatteryLevel()) {
171 eventPtr->SendDevicePropertyChanged();
172 }
173 #endif
174 }
175
StopFileInotify()176 bool MtpFileObserver::StopFileInotify()
177 {
178 isRunning_ = false;
179 lock_guard<mutex> lock(eventLock_);
180 for (auto ret : watchMap_) {
181 CHECK_AND_RETURN_RET_LOG(inotify_rm_watch(inotifyFd_, ret.first) != -1, false,
182 "MtpFileObserver StopFileInotify inotify_rm_watch error = [%{public}d]", errno);
183 }
184 close(inotifyFd_);
185 watchMap_.clear();
186 startThread_ = false;
187 inotifySuccess_ = false;
188 inotifyFd_ = 0;
189 return true;
190 }
191
StartFileInotify()192 bool MtpFileObserver::StartFileInotify()
193 {
194 isRunning_ = true;
195 inotifyFd_ = inotify_init();
196 CHECK_AND_RETURN_RET_LOG(inotifyFd_ != -1, false, "MtpFileObserver inotify_init false");
197 inotifySuccess_ = true;
198 return true;
199 }
200
WatchPathThread(const ContextSptr & context)201 bool MtpFileObserver::WatchPathThread(const ContextSptr &context)
202 {
203 while (isRunning_) {
204 SendBattery(context);
205 size_t size;
206 {
207 lock_guard<mutex> lock(eventLock_);
208 size = watchMap_.size();
209 }
210 if (size > 0) {
211 AddInotifyEvents(inotifyFd_, context);
212 }
213 }
214 return true;
215 }
216
AddFileInotify(const std::string & path,const std::string & realPath,const ContextSptr & context)217 void MtpFileObserver::AddFileInotify(const std::string &path, const std::string &realPath, const ContextSptr &context)
218 {
219 if (inotifySuccess_) {
220 lock_guard<mutex> lock(eventLock_);
221 if (!path.empty() && !realPath.empty()) {
222 int ret = inotify_add_watch(inotifyFd_, path.c_str(),
223 IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE | IN_DELETE | IN_ISDIR);
224 watchMap_.insert(make_pair(ret, path));
225 }
226 if (!startThread_) {
227 std::thread watchThread([&context] { WatchPathThread(context); });
228 watchThread.detach();
229 startThread_ = true;
230 }
231 }
232 }
233
AddPathToWatchMap(const std::string & path)234 void MtpFileObserver::AddPathToWatchMap(const std::string &path)
235 {
236 CHECK_AND_RETURN_LOG(!path.empty(), "AddPathToWatchMap path is empty");
237 {
238 lock_guard<mutex> lock(eventLock_);
239 int ret = inotify_add_watch(inotifyFd_, path.c_str(),
240 IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE | IN_DELETE | IN_ISDIR);
241 if (ret > 0) {
242 watchMap_.insert(make_pair(ret, path));
243 }
244 }
245 }
246 } // namespace Media
247 } // namespace OHOS
248