• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 "EnhancementDatabaseOperations"
17 
18 #include "enhancement_database_operations.h"
19 
20 #include "enhancement_manager.h"
21 #include "medialibrary_unistore_manager.h"
22 #include "media_file_uri.h"
23 #include "media_file_utils.h"
24 #include "media_log.h"
25 #include "scanner_utils.h"
26 #include "mimetype_utils.h"
27 #include "result_set_utils.h"
28 #include "rdb_utils.h"
29 #include "photo_album_column.h"
30 #include "media_app_uri_permission_column.h"
31 #include "media_library_extend_manager.h"
32 
33 using namespace std;
34 using namespace OHOS::NativeRdb;
35 using namespace OHOS::RdbDataShareAdapter;
36 
37 namespace OHOS {
38 namespace Media {
39 static const int32_t MAX_UPDATE_RETRY_TIMES = 5;
40 const size_t UPDATE_BATCH_SIZE = 200;
41 
Query(MediaLibraryCommand & cmd,RdbPredicates & servicePredicates,const vector<string> & columns)42 std::shared_ptr<NativeRdb::ResultSet> EnhancementDatabaseOperations::Query(MediaLibraryCommand &cmd,
43     RdbPredicates &servicePredicates, const vector<string> &columns)
44 {
45     RdbPredicates clientPredicates = RdbUtils::ToPredicates(cmd.GetDataSharePred(), PhotoColumn::PHOTOS_TABLE);
46     const vector<string> &whereUriArgs = clientPredicates.GetWhereArgs();
47     string uri = whereUriArgs.front();
48     if (!MediaFileUtils::StartsWith(uri, PhotoColumn::PHOTO_URI_PREFIX)) {
49         MEDIA_ERR_LOG("invalid URI: %{private}s", uri.c_str());
50         return nullptr;
51     }
52     string fileId = MediaFileUri::GetPhotoId(uri);
53     servicePredicates.EqualTo(MediaColumn::MEDIA_ID, fileId);
54     return MediaLibraryRdbStore::QueryWithFilter(servicePredicates, columns);
55 }
56 
BatchQuery(MediaLibraryCommand & cmd,const vector<string> & columns,unordered_map<int32_t,string> & fileId2Uri)57 std::shared_ptr<NativeRdb::ResultSet> EnhancementDatabaseOperations::BatchQuery(MediaLibraryCommand &cmd,
58     const vector<string> &columns, unordered_map<int32_t, string> &fileId2Uri)
59 {
60     RdbPredicates servicePredicates(PhotoColumn::PHOTOS_TABLE);
61     RdbPredicates clientPredicates = RdbUtils::ToPredicates(cmd.GetDataSharePred(), PhotoColumn::PHOTOS_TABLE);
62     const vector<string> &whereUriArgs = clientPredicates.GetWhereArgs();
63     vector<string> whereIdArgs;
64     whereIdArgs.reserve(whereUriArgs.size());
65     for (const auto &arg : whereUriArgs) {
66         if (!MediaFileUtils::StartsWith(arg, PhotoColumn::PHOTO_URI_PREFIX)) {
67             MEDIA_ERR_LOG("invalid URI: %{private}s", arg.c_str());
68             continue;
69         }
70         string fileId = MediaFileUri::GetPhotoId(arg);
71         fileId2Uri.emplace(stoi(fileId), arg);
72         whereIdArgs.push_back(fileId);
73     }
74     if (fileId2Uri.empty()) {
75         MEDIA_ERR_LOG("submit tasks are invalid");
76         return nullptr;
77     }
78     servicePredicates.In(MediaColumn::MEDIA_ID, whereIdArgs);
79     return MediaLibraryRdbStore::QueryWithFilter(servicePredicates, columns);
80 }
81 
Update(ValuesBucket & rdbValues,AbsRdbPredicates & predicates)82 int32_t EnhancementDatabaseOperations::Update(ValuesBucket &rdbValues, AbsRdbPredicates &predicates)
83 {
84     int32_t changedRows = -1;
85     for (int32_t i = 0; i < MAX_UPDATE_RETRY_TIMES; i++) {
86         changedRows = MediaLibraryRdbStore::UpdateWithDateTime(rdbValues, predicates);
87         if (changedRows >= 0) {
88             break;
89         }
90         MEDIA_ERR_LOG("Update DB failed! changedRows: %{public}d times: %{public}d", changedRows, i);
91     }
92     if (changedRows <= 0) {
93         MEDIA_INFO_LOG("Update DB failed! changedRows: %{public}d", changedRows);
94         return E_HAS_DB_ERROR;
95     }
96     return E_OK;
97 }
98 
HandleDateAdded(const int64_t dateAdded,const MediaType type,ValuesBucket & outValues)99 static void HandleDateAdded(const int64_t dateAdded, const MediaType type, ValuesBucket &outValues)
100 {
101     outValues.PutLong(MediaColumn::MEDIA_DATE_ADDED, dateAdded);
102     if (type != MEDIA_TYPE_PHOTO) {
103         return;
104     }
105     outValues.PutString(PhotoColumn::PHOTO_DATE_YEAR,
106         MediaFileUtils::StrCreateTimeByMilliseconds(PhotoColumn::PHOTO_DATE_YEAR_FORMAT, dateAdded));
107     outValues.PutString(PhotoColumn::PHOTO_DATE_MONTH,
108         MediaFileUtils::StrCreateTimeByMilliseconds(PhotoColumn::PHOTO_DATE_MONTH_FORMAT, dateAdded));
109     outValues.PutString(PhotoColumn::PHOTO_DATE_DAY,
110         MediaFileUtils::StrCreateTimeByMilliseconds(PhotoColumn::PHOTO_DATE_DAY_FORMAT, dateAdded));
111     outValues.PutLong(MediaColumn::MEDIA_DATE_TAKEN, dateAdded);
112 }
113 
SetOwnerAlbumId(ValuesBucket & assetInfo,shared_ptr<NativeRdb::ResultSet> resultSet)114 static void SetOwnerAlbumId(ValuesBucket &assetInfo, shared_ptr<NativeRdb::ResultSet> resultSet)
115 {
116     int32_t albumId = GetInt32Val(PhotoColumn::PHOTO_OWNER_ALBUM_ID, resultSet);
117     string ownerPackage = GetStringVal(MediaColumn::MEDIA_OWNER_PACKAGE, resultSet);
118     string ownerAppId = GetStringVal(MediaColumn::MEDIA_OWNER_APPID, resultSet);
119     string packageName = GetStringVal(MediaColumn::MEDIA_PACKAGE_NAME, resultSet);
120     assetInfo.PutInt(PhotoColumn::PHOTO_OWNER_ALBUM_ID, albumId);
121     assetInfo.PutString(MediaColumn::MEDIA_OWNER_PACKAGE, ownerPackage);
122     assetInfo.PutString(MediaColumn::MEDIA_OWNER_APPID, ownerAppId);
123     assetInfo.PutString(MediaColumn::MEDIA_PACKAGE_NAME, packageName);
124 }
125 
SetSupportedWatermarkType(int32_t sourceFileId,ValuesBucket & assetInfo,shared_ptr<NativeRdb::ResultSet> resultSet)126 static void SetSupportedWatermarkType(int32_t sourceFileId, ValuesBucket &assetInfo,
127     shared_ptr<NativeRdb::ResultSet> resultSet)
128 {
129     int32_t supportedWatermarkType = GetInt32Val(PhotoColumn::SUPPORTED_WATERMARK_TYPE, resultSet);
130     assetInfo.PutInt(PhotoColumn::SUPPORTED_WATERMARK_TYPE, supportedWatermarkType);
131 }
132 
InsertCloudEnhancementImageInDb(MediaLibraryCommand & cmd,const FileAsset & fileAsset,int32_t sourceFileId,shared_ptr<CloudEnhancementFileInfo> info,shared_ptr<NativeRdb::ResultSet> resultSet,std::shared_ptr<TransactionOperations> trans)133 int32_t EnhancementDatabaseOperations::InsertCloudEnhancementImageInDb(MediaLibraryCommand &cmd,
134     const FileAsset &fileAsset, int32_t sourceFileId, shared_ptr<CloudEnhancementFileInfo> info,
135     shared_ptr<NativeRdb::ResultSet> resultSet, std::shared_ptr<TransactionOperations> trans)
136 {
137     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
138     CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, E_HAS_DB_ERROR, "get rdb store failed");
139     CHECK_AND_RETURN_RET_LOG(fileAsset.GetPath().empty() || !MediaFileUtils::IsFileExists(fileAsset.GetPath()),
140         E_FILE_EXIST, "file %{private}s exists now", fileAsset.GetPath().c_str());
141     const string& displayName = fileAsset.GetDisplayName();
142     int64_t nowTime = MediaFileUtils::UTCTimeMilliSeconds();
143     ValuesBucket assetInfo;
144     assetInfo.PutInt(MediaColumn::MEDIA_TYPE, fileAsset.GetMediaType());
145     string extension = ScannerUtils::GetFileExtension(displayName);
146     assetInfo.PutString(MediaColumn::MEDIA_MIME_TYPE,
147         MimeTypeUtils::GetMimeTypeFromExtension(extension));
148     assetInfo.PutString(PhotoColumn::PHOTO_MEDIA_SUFFIX, extension);
149     assetInfo.PutString(MediaColumn::MEDIA_FILE_PATH, fileAsset.GetPath());
150     assetInfo.PutLong(MediaColumn::MEDIA_TIME_PENDING, fileAsset.GetTimePending());
151     assetInfo.PutString(MediaColumn::MEDIA_NAME, displayName);
152     assetInfo.PutString(MediaColumn::MEDIA_TITLE, MediaFileUtils::GetTitleFromDisplayName(displayName));
153     assetInfo.PutString(MediaColumn::MEDIA_DEVICE_NAME, cmd.GetDeviceName());
154     HandleDateAdded(nowTime, MEDIA_TYPE_PHOTO, assetInfo);
155     // Set subtype if source image is moving photo
156     assetInfo.PutInt(MediaColumn::MEDIA_HIDDEN, info->hidden);
157     if (info->subtype == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO)) {
158         assetInfo.PutInt(PhotoColumn::PHOTO_SUBTYPE, static_cast<int32_t>(PhotoSubType::MOVING_PHOTO));
159         int32_t dirty = GetInt32Val(PhotoColumn::PHOTO_DIRTY, resultSet);
160         if (dirty < 0) {
161             assetInfo.PutInt(PhotoColumn::PHOTO_DIRTY, dirty);
162         }
163     }
164     assetInfo.PutInt(PhotoColumn::PHOTO_CE_AVAILABLE,
165         static_cast<int32_t>(CloudEnhancementAvailableType::FINISH));
166     assetInfo.PutInt(PhotoColumn::PHOTO_STRONG_ASSOCIATION,
167         static_cast<int32_t>(StrongAssociationType::CLOUD_ENHANCEMENT));
168     assetInfo.PutInt(PhotoColumn::PHOTO_ASSOCIATE_FILE_ID, sourceFileId);
169     SetOwnerAlbumId(assetInfo, resultSet);
170     SetSupportedWatermarkType(sourceFileId, assetInfo, resultSet);
171     cmd.SetValueBucket(assetInfo);
172     cmd.SetTableName(PhotoColumn::PHOTOS_TABLE);
173     int64_t outRowId = -1;
174     int32_t errCode;
175     if (trans == nullptr) {
176         errCode = rdbStore->Insert(cmd, outRowId);
177     } else {
178         errCode = trans->Insert(cmd, outRowId);
179     }
180     CHECK_AND_RETURN_RET_LOG(errCode == NativeRdb::E_OK, E_HAS_DB_ERROR,
181         "Insert into db failed, errCode = %{public}d", errCode);
182     MEDIA_INFO_LOG("insert success, rowId = %{public}d", (int)outRowId);
183     return static_cast<int32_t>(outRowId);
184 }
185 
InsertCloudEnhancementPerm(int32_t sourceFileId,int32_t targetFileId)186 int64_t EnhancementDatabaseOperations::InsertCloudEnhancementPerm(int32_t sourceFileId,
187     int32_t targetFileId)
188 {
189     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
190     CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, E_HAS_DB_ERROR, "get rdb store failed");
191     RdbPredicates queryPredicates(AppUriPermissionColumn::APP_URI_PERMISSION_TABLE);
192     queryPredicates.EqualTo(AppUriPermissionColumn::FILE_ID, sourceFileId);
193     queryPredicates.And();
194     queryPredicates.EqualTo(AppUriPermissionColumn::PERMISSION_TYPE,
195         static_cast<int32_t>(PhotoPermissionType::PERSIST_READWRITE_IMAGEVIDEO));
196     vector<string> columns = { AppUriPermissionColumn::APP_ID,
197         AppUriPermissionColumn::URI_TYPE, AppUriPermissionColumn::DATE_MODIFIED,
198         AppUriPermissionColumn::SOURCE_TOKENID, AppUriPermissionColumn::TARGET_TOKENID };
199     auto resultSet = MediaLibraryRdbStore::StepQueryWithoutCheck(queryPredicates, columns);
200     CHECK_AND_RETURN_RET_LOG(resultSet != nullptr && resultSet->GoToFirstRow() == NativeRdb::E_OK,
201         E_HAS_DB_ERROR, "cannot get permission from origin photo: %{public}d", sourceFileId);
202     string appId = GetStringVal(AppUriPermissionColumn::APP_ID, resultSet);
203     int32_t uriType = GetInt32Val(AppUriPermissionColumn::URI_TYPE, resultSet);
204     int64_t dateModified = GetInt64Val(AppUriPermissionColumn::DATE_MODIFIED, resultSet);
205     int64_t sourceTokenId = GetInt64Val(AppUriPermissionColumn::SOURCE_TOKENID, resultSet);
206     int64_t targetTokenId = GetInt64Val(AppUriPermissionColumn::TARGET_TOKENID, resultSet);
207     resultSet->Close();
208 
209     int64_t outRowId = -1;
210     RdbPredicates checkPredicates(AppUriPermissionColumn::APP_URI_PERMISSION_TABLE);
211     checkPredicates.EqualTo(AppUriPermissionColumn::FILE_ID, targetFileId);
212     checkPredicates.EqualTo(AppUriPermissionColumn::URI_TYPE, uriType);
213     checkPredicates.EqualTo(AppUriPermissionColumn::PERMISSION_TYPE,
214         static_cast<int32_t>(PhotoPermissionType::PERSIST_READWRITE_IMAGEVIDEO));
215     checkPredicates.EqualTo(AppUriPermissionColumn::DATE_MODIFIED, dateModified);
216     checkPredicates.EqualTo(AppUriPermissionColumn::SOURCE_TOKENID, sourceTokenId);
217     checkPredicates.EqualTo(AppUriPermissionColumn::TARGET_TOKENID, targetTokenId);
218     resultSet = MediaLibraryRdbStore::StepQueryWithoutCheck(checkPredicates, columns);
219     if (resultSet != nullptr && resultSet->GoToFirstRow() == NativeRdb::E_OK) {
220         MEDIA_INFO_LOG("cloud enhancement permission record has already exists: %{public}d", targetFileId);
221         return outRowId;
222     }
223 
224     ValuesBucket valueBucket;
225     valueBucket.PutString(AppUriPermissionColumn::APP_ID, appId);
226     valueBucket.PutInt(AppUriPermissionColumn::FILE_ID, targetFileId);
227     valueBucket.PutInt(AppUriPermissionColumn::URI_TYPE, uriType);
228     valueBucket.PutInt(AppUriPermissionColumn::PERMISSION_TYPE,
229         static_cast<int32_t>(PhotoPermissionType::PERSIST_READWRITE_IMAGEVIDEO));
230     valueBucket.PutLong(AppUriPermissionColumn::DATE_MODIFIED, dateModified);
231     valueBucket.PutLong(AppUriPermissionColumn::SOURCE_TOKENID, sourceTokenId);
232     valueBucket.PutLong(AppUriPermissionColumn::TARGET_TOKENID, targetTokenId);
233 
234     int32_t errCode = MediaLibraryRdbStore::InsertInternal(outRowId,
235         AppUriPermissionColumn::APP_URI_PERMISSION_TABLE, valueBucket);
236     CHECK_AND_PRINT_LOG(errCode == E_OK, "insert permission failed: %{public}d", errCode);
237     MEDIA_INFO_LOG("Add permission for cloud enhancement photo success: %{public}d", targetFileId);
238     return outRowId;
239 }
240 
IsEditedTrashedHidden(const std::shared_ptr<NativeRdb::ResultSet> & ret)241 bool IsEditedTrashedHidden(const std::shared_ptr<NativeRdb::ResultSet> &ret)
242 {
243     if (ret == nullptr) {
244         MEDIA_ERR_LOG("resultset is null");
245         return false;
246     }
247     if (ret->GoToFirstRow() != NativeRdb::E_OK) {
248         MEDIA_ERR_LOG("go to first row failed");
249         return false;
250     }
251     int32_t colIndex = -1;
252     double editTime = -1;
253     double trashedTime = -1;
254     double hiddenTime = -1;
255     MEDIA_INFO_LOG("coIndex1 is %{public}d, editTime is %{public}d", colIndex, static_cast<int>(editTime));
256     ret->GetColumnIndex(PhotoColumn::PHOTO_EDIT_TIME, colIndex);
257     if (ret->GetDouble(colIndex, editTime) != NativeRdb::E_OK) {
258         MEDIA_ERR_LOG("Fail to query DB ");
259         MEDIA_INFO_LOG("coIndex2 is %{public}d, editTime is %{public}d", colIndex, static_cast<int>(editTime));
260         return false;
261     }
262     ret->GetColumnIndex(MediaColumn::MEDIA_DATE_TRASHED, colIndex);
263     if (ret->GetDouble(colIndex, trashedTime) != NativeRdb::E_OK) {
264         MEDIA_ERR_LOG("Fail to query DB ");
265         MEDIA_INFO_LOG("coIndex2 is %{public}d, trashedTime is %{public}d", colIndex, static_cast<int>(trashedTime));
266         return false;
267     }
268     ret->GetColumnIndex(PhotoColumn::PHOTO_HIDDEN_TIME, colIndex);
269     if (ret->GetDouble(colIndex, hiddenTime) != NativeRdb::E_OK) {
270         MEDIA_ERR_LOG("Fail to query DB ");
271         MEDIA_INFO_LOG("coIndex2 is %{public}d, hiddenTime is %{public}d", colIndex, static_cast<int>(hiddenTime));
272         return false;
273     }
274     MEDIA_INFO_LOG("editTime is %{public}d, trashedTime is %{public}d, hiddenTime is %{public}d",
275         static_cast<int>(editTime), static_cast<int>(trashedTime), static_cast<int>(hiddenTime));
276     return (editTime == 0 && trashedTime == 0 && hiddenTime == 0) ? true : false;
277 }
278 
GetPair(MediaLibraryCommand & cmd)279 std::shared_ptr<NativeRdb::ResultSet> EnhancementDatabaseOperations::GetPair(MediaLibraryCommand &cmd)
280 {
281     RdbPredicates firstServicePredicates(PhotoColumn::PHOTOS_TABLE);
282     RdbPredicates clientPredicates = RdbUtils::ToPredicates(cmd.GetDataSharePred(), PhotoColumn::PHOTOS_TABLE);
283     const vector<string> &whereUriArgs = clientPredicates.GetWhereArgs();
284     string UriArg = whereUriArgs.front();
285     if (!MediaFileUtils::StartsWith(UriArg, PhotoColumn::PHOTO_URI_PREFIX)) {
286         return nullptr;
287     }
288     string fileId = MediaFileUri::GetPhotoId(UriArg);
289     MEDIA_INFO_LOG("GetPair_build fileId: %{public}s", fileId.c_str());
290     vector<string> queryColumns = {PhotoColumn::PHOTO_EDIT_TIME, MediaColumn::MEDIA_DATE_TRASHED,
291         PhotoColumn::PHOTO_HIDDEN_TIME};
292     firstServicePredicates.EqualTo(MediaColumn::MEDIA_ID, fileId);
293     auto resultSet = MediaLibraryRdbStore::QueryWithFilter(firstServicePredicates, queryColumns);
294     if (IsEditedTrashedHidden(resultSet)) {
295         MEDIA_INFO_LOG("success into query stage after IsEditedTrashedHidden");
296         RdbPredicates secondServicePredicates(PhotoColumn::PHOTOS_TABLE);
297         secondServicePredicates.EqualTo(PhotoColumn::PHOTO_ASSOCIATE_FILE_ID, fileId);
298         vector<string> columns = {};
299         MEDIA_INFO_LOG("start query");
300         auto resultSetCallback = MediaLibraryRdbStore::QueryWithFilter(secondServicePredicates, columns);
301         if (IsEditedTrashedHidden(resultSetCallback)) {
302             return resultSetCallback;
303         }
304     }
305     MEDIA_INFO_LOG("PhotoAsset is edited or trashed or hidden");
306     return nullptr;
307 }
308 
QueryAndUpdatePhotos(const std::vector<std::string> & photoIds)309 int32_t EnhancementDatabaseOperations::QueryAndUpdatePhotos(const std::vector<std::string> &photoIds)
310 {
311     CHECK_AND_RETURN_RET_LOG(photoIds.size() > 0, NativeRdb::E_OK, "photoIds is emtpy");
312     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
313     CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, E_HAS_DB_ERROR, "rdbStore is nullptr");
314 
315     std::string photoIdList =
316         accumulate(photoIds.begin() + 1, photoIds.end(), photoIds[0], [](const string &a, const string &b) {
317             return a + ", " + b;
318         });
319     std::string querySql = "SELECT file_id FROM Photos WHERE (ce_available = 0 AND photo_id IN (" + photoIdList +
320                            ")) OR (ce_available = 1 AND photo_id NOT IN (" + photoIdList + "));";
321 
322     auto resultSet = rdbStore->QueryByStep(querySql);
323     CHECK_AND_RETURN_RET_LOG(resultSet != nullptr, NativeRdb::E_ERROR, "query by step failed");
324 
325     std::vector<std::string> needUpdateFileIds;
326     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
327         needUpdateFileIds.push_back(GetStringVal(PhotoColumn::MEDIA_ID, resultSet));
328     }
329     resultSet->Close();
330     auto needChange = needUpdateFileIds.size();
331     CHECK_AND_RETURN_RET_LOG(needChange > 0, NativeRdb::E_OK, "needUpdateFileIds is emtpy");
332 
333     int32_t ret = NativeRdb::E_OK;
334     int64_t totalChanged = 0;
335     for (size_t start = 0; start < needChange; start += UPDATE_BATCH_SIZE) {
336         size_t end = std::min(start + UPDATE_BATCH_SIZE, needChange);
337         std::stringstream updateSql;
338         updateSql << "UPDATE Photos SET ce_available = NOT ( ce_available ) WHERE file_id IN ( ";
339         for (size_t i = start; i < end; ++i) {
340             if (i != start) {
341                 updateSql << ", ";
342             }
343             updateSql << needUpdateFileIds[i];
344         }
345         updateSql << " );";
346         int64_t changedRowCount = 0;
347         auto errCode = rdbStore->ExecuteForChangedRowCount(changedRowCount, updateSql.str());
348         if (errCode != NativeRdb::E_OK) {
349             ret = errCode;
350             MEDIA_ERR_LOG("execute for changed row count failed, errCode: %{public}d", errCode);
351         } else {
352             totalChanged += changedRowCount;
353             MEDIA_INFO_LOG("changed row count: %{public}" PRId64, changedRowCount);
354         }
355     }
356 
357     MEDIA_INFO_LOG("need change: %{public}zu, total changed: %{public}" PRId64, needChange, totalChanged);
358     return ret;
359 }
360 } // namespace Media
361 } // namespace OHOS