• 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 
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     // when create or delete or modifiy file path, the parent path mtime changed
133     if (event.mask & (IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE | IN_DELETE)) {
134         MEDIA_INFO_LOG("path:%{public}s mask:0x%{public}x name:%{public}s",
135             path.c_str(), event.mask, event.name);
136         eventPtr->SendObjectInfoChanged(path);
137     }
138 }
139 
AddInotifyEvents(const int & inotifyFd,const ContextSptr & context)140 bool MtpFileObserver::AddInotifyEvents(const int &inotifyFd, const ContextSptr &context)
141 {
142     char eventBuf[BUF_SIZE] = {0};
143 
144     int ret = read(inotifyFd, eventBuf, sizeof(eventBuf) - SIZE_ONE);
145     bool cond = (ret < static_cast<int>(sizeof(struct inotify_event)));
146     CHECK_AND_RETURN_RET_LOG(!cond, false, "MtpFileObserver AddInotifyEvents no event");
147 
148     struct inotify_event *positionEvent = (struct inotify_event *)eventBuf;
149     struct inotify_event *event;
150     while (ret >= static_cast<int>(sizeof(struct inotify_event))) {
151         event = positionEvent;
152         if (event->len) {
153             bool isFind;
154             map<int, string>::iterator iter;
155             {
156                 lock_guard<mutex> lock(eventLock_);
157                 iter = watchMap_.find(event->wd);
158                 isFind = iter != watchMap_.end();
159             }
160             if (isFind) {
161                 string path = iter->second;
162                 SendEvent(*event, path, context);
163             }
164         }
165         positionEvent++;
166         ret -= static_cast<int>(sizeof(struct inotify_event));
167     }
168     return true;
169 }
170 
SendBattery(const ContextSptr & context)171 void MtpFileObserver::SendBattery(const ContextSptr &context)
172 {
173 #ifdef HAS_BATTERY_MANAGER_PART
174     std::shared_ptr<MtpEvent> eventPtr = std::make_shared<OHOS::Media::MtpEvent>(context);
175     auto battery = make_shared<MtpOperationUtils>(context);
176     if (LOW_BATTERY >= battery->GetBatteryLevel()) {
177         eventPtr->SendDevicePropertyChanged();
178     }
179 #endif
180 }
181 
StopFileInotify()182 bool MtpFileObserver::StopFileInotify()
183 {
184     CHECK_AND_RETURN_RET_LOG(isRunning_, false, "MtpFileObserver FileInotify is not running");
185     isRunning_ = false;
186     lock_guard<mutex> lock(eventLock_);
187     for (auto ret : watchMap_) {
188         CHECK_AND_RETURN_RET_LOG(inotify_rm_watch(inotifyFd_, ret.first) != -1, false,
189             "MtpFileObserver StopFileInotify inotify_rm_watch error = [%{public}d]", errno);
190     }
191     close(inotifyFd_);
192     watchMap_.clear();
193     startThread_ = false;
194     inotifySuccess_ = false;
195     inotifyFd_ = 0;
196     return true;
197 }
198 
StartFileInotify()199 bool MtpFileObserver::StartFileInotify()
200 {
201     isRunning_ = true;
202     inotifyFd_ = inotify_init();
203     CHECK_AND_RETURN_RET_LOG(inotifyFd_ != -1, false, "MtpFileObserver inotify_init false");
204     inotifySuccess_ = true;
205     return true;
206 }
207 
WatchPathThread(const ContextSptr & context)208 bool MtpFileObserver::WatchPathThread(const ContextSptr &context)
209 {
210     while (isRunning_) {
211         SendBattery(context);
212         size_t size;
213         {
214             lock_guard<mutex> lock(eventLock_);
215             size = watchMap_.size();
216         }
217         if (size > 0) {
218             AddInotifyEvents(inotifyFd_, context);
219         }
220     }
221     return true;
222 }
223 
AddFileInotify(const std::string & path,const std::string & realPath,const ContextSptr & context)224 void MtpFileObserver::AddFileInotify(const std::string &path, const std::string &realPath, const ContextSptr &context)
225 {
226     if (inotifySuccess_) {
227         lock_guard<mutex> lock(eventLock_);
228         if (!path.empty() && !realPath.empty()) {
229             int ret = inotify_add_watch(inotifyFd_, path.c_str(),
230                 IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE | IN_DELETE | IN_ISDIR);
231             watchMap_.insert(make_pair(ret, path));
232         }
233         if (!startThread_) {
234             std::thread watchThread([&context] { WatchPathThread(context); });
235             watchThread.detach();
236             startThread_ = true;
237         }
238     }
239 }
240 
AddPathToWatchMap(const std::string & path)241 void MtpFileObserver::AddPathToWatchMap(const std::string &path)
242 {
243     CHECK_AND_RETURN_LOG(!path.empty(), "AddPathToWatchMap path is empty");
244     {
245         lock_guard<mutex> lock(eventLock_);
246         int ret = inotify_add_watch(inotifyFd_, path.c_str(),
247             IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE | IN_DELETE | IN_ISDIR);
248         if (ret > 0) {
249             watchMap_.insert(make_pair(ret, path));
250         }
251     }
252 }
253 } // namespace Media
254 } // namespace OHOS
255