• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "mtp_packet_tools.h"
26 
27 using namespace std;
28 namespace OHOS {
29 namespace Media {
30 bool MtpFileObserver::isRunning_ = false;
31 int MtpFileObserver::inotifyFd_ = 0;
32 std::map<int, std::string> MtpFileObserver::watchMap_;
33 std::mutex MtpFileObserver::eventLock_;
34 std::queue<std::pair<uint16_t, uint32_t>> MtpFileObserver::eventQueue_;
35 std::atomic<bool> MtpFileObserver::isEventThreadRunning_ = false;
36 std::mutex MtpFileObserver::mutex_;
37 std::condition_variable MtpFileObserver::cv_;
38 constexpr uint16_t EVENT_CONTAINER_TYPE = 4;
39 constexpr uint32_t EVENT_LENGTH = 16;
40 const int BUF_SIZE = 1024;
41 const int32_t SIZE_ONE = 1;
42 #ifdef HAS_BATTERY_MANAGER_PART
43 const int LOW_BATTERY = 50;
44 #endif
45 const std::string PATH_SEPARATOR = "/";
46 struct MoveInfo {
47     uint32_t cookie;
48     std::string path;
49 } g_moveInfo;
50 
EraseFromWatchMap(const std::string & path)51 void MtpFileObserver::EraseFromWatchMap(const std::string &path)
52 {
53     CHECK_AND_RETURN_LOG(!path.empty(), "EraseFromWatchMap path is empty");
54     {
55         lock_guard<mutex> lock(eventLock_);
56         std::vector<int> eraseList;
57         std::string separatorPath = path + PATH_SEPARATOR;
58         for (const auto &item : watchMap_) {
59             // remove the path in watchMap_ which is the subdirectory of the deleted path
60             if (separatorPath.compare(item.second.substr(0, separatorPath.size())) == 0) {
61                 eraseList.push_back(item.first);
62             } else if (item.second.compare(path) == 0) {
63                 // remove the path in watchMap_
64                 eraseList.push_back(item.first);
65             }
66         }
67         for (const auto &i : eraseList) {
68             inotify_rm_watch(inotifyFd_, i);
69             watchMap_.erase(i);
70         }
71         std::vector<int>().swap(eraseList);
72     }
73 }
74 
UpdateWatchMap(const std::string & path)75 void MtpFileObserver::UpdateWatchMap(const std::string &path)
76 {
77     CHECK_AND_RETURN_LOG(!path.empty(), "UpdateWatchMap path is empty");
78     CHECK_AND_RETURN_LOG(!g_moveInfo.path.empty(), "UpdateWatchMap removeInfo.path is empty");
79     {
80         lock_guard<mutex> lock(eventLock_);
81         std::string separatorPath = g_moveInfo.path + PATH_SEPARATOR;
82         for (auto &item : watchMap_) {
83             // update the path in watchMap_ which is the subdirectory of the moved path
84             if (separatorPath.compare(item.second.substr(0, separatorPath.size())) == 0) {
85                 item.second = path + PATH_SEPARATOR + item.second.substr(separatorPath.size());
86             } else if (item.second.compare(g_moveInfo.path) == 0) {
87                 // update the path in watchMap_
88                 item.second = path;
89             }
90         }
91     }
92 }
93 
DealWatchMap(const inotify_event & event,const std::string & path)94 void MtpFileObserver::DealWatchMap(const inotify_event &event, const std::string &path)
95 {
96     CHECK_AND_RETURN_LOG(!path.empty(), "DealWatchMap path is empty");
97     CHECK_AND_RETURN_LOG((event.mask & IN_ISDIR), "DealWatchMap path is not dir");
98     if (event.mask & IN_DELETE) {
99         // if the path is deleted, remove it from watchMap_
100         EraseFromWatchMap(path);
101     } else if (event.mask & IN_MOVED_FROM) {
102         // if the path is moved from, record the cookie and path
103         g_moveInfo = {
104             .cookie = event.cookie,
105             .path = path
106         };
107     } else if (event.mask & IN_MOVED_TO) {
108         // if the path is moved to, update the path in watchMap_
109         if (g_moveInfo.cookie == event.cookie) {
110             UpdateWatchMap(path);
111         }
112     }
113 }
114 
SendEvent(const inotify_event & event,const std::string & path,const ContextSptr & context)115 void MtpFileObserver::SendEvent(const inotify_event &event, const std::string &path, const ContextSptr &context)
116 {
117     string fileName = path + "/" + event.name;
118     std::shared_ptr<MtpEvent> eventPtr = std::make_shared<OHOS::Media::MtpEvent>(context);
119     CHECK_AND_RETURN_LOG(eventPtr != nullptr, "MtpFileObserver SendEvent eventPtr is null");
120     uint32_t handle = 0;
121     auto mtpMedialibrary = MtpMediaLibrary::GetInstance();
122     CHECK_AND_RETURN_LOG(mtpMedialibrary != nullptr, "MtpFileObserver SendEvent mtpMedialibrary is null");
123     if ((event.mask & IN_CREATE) || (event.mask & IN_MOVED_TO)) {
124         MEDIA_DEBUG_LOG("MtpFileObserver AddInotifyEvents create/MOVED_TO: path:%{private}s", fileName.c_str());
125         if (mtpMedialibrary->GetIdByPath(fileName, handle) != 0) {
126             handle = mtpMedialibrary->ObserverAddPathToMap(fileName);
127             AddToQueue(MTP_EVENT_OBJECT_ADDED_CODE, handle);
128         }
129     } else if ((event.mask & IN_DELETE) || (event.mask & IN_MOVED_FROM)) {
130         MEDIA_DEBUG_LOG("MtpFileObserver AddInotifyEvents delete/MOVED_FROM: path:%{private}s", fileName.c_str());
131         if (mtpMedialibrary->GetIdByPath(fileName, handle) == 0) {
132             mtpMedialibrary->ObserverDeletePathToMap(fileName);
133             AddToQueue(MTP_EVENT_OBJECT_REMOVED_CODE, handle);
134         }
135     } else if (event.mask & IN_CLOSE_WRITE) {
136         MEDIA_DEBUG_LOG("MtpFileObserver AddInotifyEvents IN_CLOSE_WRITE : path:%{private}s", fileName.c_str());
137         if (mtpMedialibrary->GetIdByPath(fileName, handle) == 0) {
138             AddToQueue(MTP_EVENT_OBJECT_INFO_CHANGED_CODE, handle);
139         }
140     }
141     // if the path is a directory and it is moved or deleted, deal with the watchMap_
142     if ((event.mask & IN_ISDIR) && (event.mask & (IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO))) {
143         DealWatchMap(event, fileName);
144     }
145     // when create or delete or modifiy file path, the parent path mtime changed
146     if (event.mask & (IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE | IN_DELETE)) {
147         MEDIA_INFO_LOG("path:%{public}s mask:0x%{public}x name:%{public}s",
148             path.c_str(), event.mask, event.name);
149         if (mtpMedialibrary->GetIdByPath(path, handle) == 0) {
150             AddToQueue(MTP_EVENT_OBJECT_INFO_CHANGED_CODE, handle);
151         }
152     }
153 }
154 
AddInotifyEvents(const int & inotifyFd,const ContextSptr & context)155 bool MtpFileObserver::AddInotifyEvents(const int &inotifyFd, const ContextSptr &context)
156 {
157     char eventBuf[BUF_SIZE] = {0};
158 
159     int ret = read(inotifyFd, eventBuf, sizeof(eventBuf) - SIZE_ONE);
160     bool cond = (ret < static_cast<int>(sizeof(struct inotify_event)));
161     CHECK_AND_RETURN_RET_LOG(!cond, false, "MtpFileObserver AddInotifyEvents no event");
162 
163     struct inotify_event *positionEvent = (struct inotify_event *)eventBuf;
164     struct inotify_event *event;
165     while (ret >= static_cast<int>(sizeof(struct inotify_event)) && isRunning_) {
166         event = positionEvent;
167         if (event->len) {
168             bool isFind;
169             map<int, string>::iterator iter;
170             {
171                 lock_guard<mutex> lock(eventLock_);
172                 iter = watchMap_.find(event->wd);
173                 isFind = iter != watchMap_.end();
174             }
175             if (isFind && isRunning_) {
176                 string path = iter->second;
177                 SendEvent(*event, path, context);
178             }
179         }
180         positionEvent++;
181         ret -= static_cast<int>(sizeof(struct inotify_event));
182     }
183     return true;
184 }
185 
SendBattery(const ContextSptr & context)186 void MtpFileObserver::SendBattery(const ContextSptr &context)
187 {
188 #ifdef HAS_BATTERY_MANAGER_PART
189     std::shared_ptr<MtpEvent> eventPtr = std::make_shared<OHOS::Media::MtpEvent>(context);
190     auto battery = make_shared<MtpOperationUtils>(context);
191     if (LOW_BATTERY >= battery->GetBatteryLevel()) {
192         eventPtr->SendDevicePropertyChanged();
193     }
194 #endif
195 }
196 
StopFileInotify()197 bool MtpFileObserver::StopFileInotify()
198 {
199     CHECK_AND_RETURN_RET_LOG(isRunning_, false, "MtpFileObserver FileInotify is not running");
200     StopSendEventThread();
201     isRunning_ = false;
202     lock_guard<mutex> lock(eventLock_);
203     for (auto ret : watchMap_) {
204         CHECK_AND_RETURN_RET_LOG(inotify_rm_watch(inotifyFd_, ret.first) != -1, false,
205             "MtpFileObserver StopFileInotify inotify_rm_watch error = [%{public}d]", errno);
206     }
207     close(inotifyFd_);
208     watchMap_.clear();
209     startThread_ = false;
210     inotifySuccess_ = false;
211     inotifyFd_ = 0;
212     return true;
213 }
214 
StartFileInotify()215 bool MtpFileObserver::StartFileInotify()
216 {
217     isRunning_ = true;
218     inotifyFd_ = inotify_init();
219     CHECK_AND_RETURN_RET_LOG(inotifyFd_ != -1, false, "MtpFileObserver inotify_init false");
220     inotifySuccess_ = true;
221     return true;
222 }
223 
WatchPathThread(const ContextSptr & context)224 bool MtpFileObserver::WatchPathThread(const ContextSptr &context)
225 {
226     while (isRunning_) {
227         SendBattery(context);
228         size_t size;
229         {
230             lock_guard<mutex> lock(eventLock_);
231             size = watchMap_.size();
232         }
233         if (size > 0) {
234             AddInotifyEvents(inotifyFd_, context);
235         }
236     }
237     return true;
238 }
239 
AddFileInotify(const std::string & path,const std::string & realPath,const ContextSptr & context)240 void MtpFileObserver::AddFileInotify(const std::string &path, const std::string &realPath, const ContextSptr &context)
241 {
242     if (inotifySuccess_) {
243         lock_guard<mutex> lock(eventLock_);
244         if (!path.empty() && !realPath.empty()) {
245             int ret = inotify_add_watch(inotifyFd_, path.c_str(),
246                 IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE | IN_DELETE | IN_ISDIR);
247             watchMap_.insert(make_pair(ret, path));
248         }
249         if (!startThread_) {
250             std::thread watchThread([&context] { WatchPathThread(context); });
251             watchThread.detach();
252             startThread_ = true;
253             StartSendEventThread(context);
254         }
255     }
256 }
257 
AddPathToWatchMap(const std::string & path)258 void MtpFileObserver::AddPathToWatchMap(const std::string &path)
259 {
260     CHECK_AND_RETURN_LOG(!path.empty(), "AddPathToWatchMap path is empty");
261     {
262         lock_guard<mutex> lock(eventLock_);
263         int ret = inotify_add_watch(inotifyFd_, path.c_str(),
264             IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE | IN_DELETE | IN_ISDIR);
265         if (ret > 0) {
266             watchMap_.insert(make_pair(ret, path));
267         }
268     }
269 }
270 
AddToQueue(uint16_t code,uint32_t handle)271 void MtpFileObserver::AddToQueue(uint16_t code, uint32_t handle)
272 {
273     MEDIA_DEBUG_LOG("MtpFileObserver AddToQueue code[0x%{public}x] handle[%{public}d]", code, handle);
274     CHECK_AND_RETURN_LOG(isEventThreadRunning_.load(), "MTP:AddToQueue EventThread is not running.");
275     {
276         std::lock_guard<std::mutex> lock(mutex_);
277         eventQueue_.push(std::make_pair(code, handle));
278         cv_.notify_all();
279     }
280 }
281 
SendEventThread(const ContextSptr & context)282 void MtpFileObserver::SendEventThread(const ContextSptr &context)
283 {
284     MEDIA_DEBUG_LOG("MtpFileObserver:SendEventThread Start.");
285     while (isEventThreadRunning_.load()) {
286         {
287             std::unique_lock<std::mutex> lock(mutex_);
288             cv_.wait(lock, [&] {
289                 return !isEventThreadRunning_.load() || !eventQueue_.empty();
290             });
291         }
292         CHECK_AND_RETURN_LOG(isEventThreadRunning_.load(), "MTP:MtpFileObserver SendEventThread Exit.");
293 
294         std::pair<uint16_t, uint32_t> event;
295         {
296             std::lock_guard<std::mutex> lock(mutex_);
297             event = eventQueue_.front();
298             eventQueue_.pop();
299         }
300         CHECK_AND_RETURN_LOG(context != nullptr, "MTP:SendEventThread eventPtr is null");
301         CHECK_AND_RETURN_LOG(context->mtpDriver != nullptr, "MTP:SendEventThread mtpDriver is null");
302         EventMtp eventMtp;
303         eventMtp.length = EVENT_LENGTH;
304         vector<uint8_t> outBuffer;
305         MtpPacketTool::PutUInt32(outBuffer, eventMtp.length);
306         MtpPacketTool::PutUInt16(outBuffer, EVENT_CONTAINER_TYPE);
307         MtpPacketTool::PutUInt16(outBuffer, event.first);
308         MtpPacketTool::PutUInt32(outBuffer, context->transactionID);
309         MtpPacketTool::PutUInt32(outBuffer, event.second);
310 
311         eventMtp.data = std::move(outBuffer);
312         context->mtpDriver->WriteEvent(eventMtp);
313     }
314 }
315 
StartSendEventThread(const ContextSptr & context)316 void MtpFileObserver::StartSendEventThread(const ContextSptr &context)
317 {
318     MEDIA_DEBUG_LOG("MTP:MtpFileObserver StartSendEventThread is called.");
319     CHECK_AND_RETURN_LOG(context != nullptr, "MTP:StartSendEventThread context is null");
320     if (isEventThreadRunning_.load()) {
321         StopSendEventThread();
322     }
323     isEventThreadRunning_.store(true);
324     {
325         std::lock_guard<std::mutex> lock(mutex_);
326         std::queue<std::pair<uint16_t, uint32_t>>().swap(eventQueue_);
327     }
328     std::thread([&context] { SendEventThread(context); }).detach();
329 }
330 
StopSendEventThread()331 void MtpFileObserver::StopSendEventThread()
332 {
333     MEDIA_DEBUG_LOG("MTP:MtpFileObserver StopSendEventThread is called.");
334     isEventThreadRunning_.store(false);
335     cv_.notify_all();
336     {
337         std::lock_guard<std::mutex> lock(mutex_);
338         std::queue<std::pair<uint16_t, uint32_t>>().swap(eventQueue_);
339     }
340 }
341 } // namespace Media
342 } // namespace OHOS
343