1 /*
2 * Copyright (c) 2025 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 #include "ani_file_share.h"
17
18 #include <ani.h>
19 #include <iostream>
20 #include <vector>
21 #include "ability.h"
22 #include "datashare_helper.h"
23 #include "datashare_values_bucket.h"
24 #include "ipc_skeleton.h"
25 #include "log.h"
26 #include "n_error.h"
27 #include "remote_uri.h"
28 #include "tokenid_kit.h"
29 #include "uri.h"
30 #include "uri_permission_manager_client.h"
31 #include "want.h"
32
33 using namespace OHOS::DataShare;
34 using namespace OHOS::DistributedFS::ModuleRemoteUri;
35 using namespace OHOS::FileManagement::LibN;
36
37 namespace OHOS {
38 namespace AppFileService {
39 namespace ModuleFileShare {
40
ParseObjToStr(ani_env * env,ani_string stringObj)41 static std::string ParseObjToStr(ani_env *env, ani_string stringObj)
42 {
43 ani_size strSize;
44 env->String_GetUTF8Size(stringObj, &strSize);
45 std::vector<char> buffer(strSize + 1);
46 char* utf8Buffer = buffer.data();
47
48 ani_size byteswritten = 0;
49 env->String_GetUTF8(stringObj, utf8Buffer, strSize + 1, &byteswritten);
50
51 if (byteswritten <= strSize) {
52 utf8Buffer[byteswritten] = '\0';
53 } else {
54 utf8Buffer[strSize] = '\0';
55 }
56 std::string path = std::string(utf8Buffer);
57 return path;
58 }
59
ParseEnumToInt(ani_env * env,ani_enum_item enumItem)60 static ani_int ParseEnumToInt(ani_env *env, ani_enum_item enumItem)
61 {
62 ani_int intValue = -1;
63 if (ANI_OK != env->EnumItem_GetValue_Int(enumItem, &intValue)) {
64 LOGE("%{public}s: EnumItem_GetValue_Int FAILD.", __func__);
65 return -1;
66 }
67 LOGD("%{public}s: Enum Value: %{public}d.", __func__, intValue);
68 return intValue;
69 }
70
ThrowBusinessError(ani_env * env,int errCode,std::string && errMsg)71 static void ThrowBusinessError(ani_env *env, int errCode, std::string&& errMsg)
72 {
73 LOGD("Begin ThrowBusinessError.");
74 static const char *errorClsName = "L@ohos/base/BusinessError;";
75 ani_class cls {};
76 if (ANI_OK != env->FindClass(errorClsName, &cls)) {
77 LOGE("find class BusinessError %{public}s failed", errorClsName);
78 return;
79 }
80 ani_method ctor;
81 if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", ":V", &ctor)) {
82 LOGE("find method BusinessError.constructor failed");
83 return;
84 }
85 ani_object errorObject;
86 if (ANI_OK != env->Object_New(cls, ctor, &errorObject)) {
87 LOGE("create BusinessError object failed");
88 return;
89 }
90 ani_double aniErrCode = static_cast<ani_double>(errCode);
91 ani_string errMsgStr;
92 if (ANI_OK != env->String_NewUTF8(errMsg.c_str(), errMsg.size(), &errMsgStr)) {
93 LOGE("convert errMsg to ani_string failed");
94 return;
95 }
96 if (ANI_OK != env->Object_SetFieldByName_Double(errorObject, "code", aniErrCode)) {
97 LOGE("set error code failed");
98 return;
99 }
100 if (ANI_OK != env->Object_SetPropertyByName_Ref(errorObject, "message", errMsgStr)) {
101 LOGE("set error message failed");
102 return;
103 }
104 env->ThrowError(static_cast<ani_error>(errorObject));
105 return;
106 }
107
IsSystemApp()108 static bool IsSystemApp()
109 {
110 uint64_t fullTokenId = OHOS::IPCSkeleton::GetCallingFullTokenID();
111 return OHOS::Security::AccessToken::TokenIdKit::IsSystemAppByFullTokenID(fullTokenId);
112 }
113
GetIdFromUri(string uri)114 static int32_t GetIdFromUri(string uri)
115 {
116 std::replace(uri.begin(), uri.end(), '/', ' ');
117 std::stringstream ss(uri);
118 std::string tmp;
119 int fileId = -1;
120 ss >> tmp >> tmp >> tmp >> fileId;
121 return fileId;
122 }
123
CheckValidPublicUri(const std::string & inputUri)124 static bool CheckValidPublicUri(const std::string &inputUri)
125 {
126 Uri uri(inputUri);
127 std::string scheme = uri.GetScheme();
128 if (scheme != FILE_SCHEME) {
129 return false;
130 }
131
132 std::string authority = uri.GetAuthority();
133 if (authority != MEDIA_AUTHORITY && authority != FILE_MANAGER_AUTHORITY) {
134 return false;
135 }
136
137 return true;
138 }
139
GetModeFromFlag(unsigned int flag)140 static std::string GetModeFromFlag(unsigned int flag)
141 {
142 std::string mode = "";
143 if (flag & OHOS::AAFwk::Want::FLAG_AUTH_READ_URI_PERMISSION) {
144 mode += "r";
145 }
146 if (flag & OHOS::AAFwk::Want::FLAG_AUTH_WRITE_URI_PERMISSION) {
147 mode += "w";
148 }
149 return mode;
150 }
151
GetMediaTypeAndApiFromUri(const std::string & uri,bool & isApi10)152 static int32_t GetMediaTypeAndApiFromUri(const std::string &uri, bool &isApi10)
153 {
154 if (uri.find(MEDIA_FILE_URI_PHOTO_PREFIX) == 0) {
155 isApi10 = true;
156 return MediaFileTable::PHOTO_TABLE;
157 } else if (uri.find(MEDIA_FILE_URI_VIDEO_PREFIX) == 0 ||
158 uri.find(MEDIA_FILE_URI_IMAGE_PREFIX) == 0) {
159 return MediaFileTable::PHOTO_TABLE;
160 } else if (uri.find(MEDIA_FILE_URI_AUDIO_PREFIX) == 0) {
161 isApi10 = true;
162 return MediaFileTable::AUDIO_TABLE;
163 } else if (uri.find(MEDIA_FILE_URI_AUDIO_PREFIX_L) == 0) {
164 return MediaFileTable::AUDIO_TABLE;
165 } else if (uri.find(MEDIA_FILE_URI_FILE_PREFIX) == 0) {
166 return MediaFileTable::FILE_TABLE;
167 }
168
169 return MediaFileTable::FILE_TABLE;
170 }
171
InitValuesBucket(const UriPermissionInfo & uriPermInfo,Uri & uri,bool & isApi10,DataShareValuesBucket & valuesBucket)172 static int InitValuesBucket(const UriPermissionInfo &uriPermInfo, Uri &uri, bool &isApi10,
173 DataShareValuesBucket &valuesBucket)
174 {
175 int32_t fileId = GetIdFromUri(uriPermInfo.uri);
176 if (fileId == -1) {
177 LOGE("%{public}s: get fileId parameter failed!", __func__);
178 return -EINVAL;
179 }
180
181 int32_t filesType = GetMediaTypeAndApiFromUri(uri.ToString(), isApi10);
182 valuesBucket.Put(PERMISSION_FILE_ID, fileId);
183 valuesBucket.Put(PERMISSION_BUNDLE_NAME, uriPermInfo.bundleName);
184 valuesBucket.Put(PERMISSION_MODE, uriPermInfo.mode);
185 valuesBucket.Put(PERMISSION_TABLE_TYPE, filesType);
186 return 0;
187 }
188
InsertByDatashare(const DataShareValuesBucket & valuesBucket,bool isApi10)189 static int InsertByDatashare(const DataShareValuesBucket &valuesBucket, bool isApi10)
190 {
191 int ret = -1;
192 std::shared_ptr<DataShare::DataShareHelper> dataShareHelper = nullptr;
193 sptr<FileShareGrantToken> remote = new (std::nothrow) IRemoteStub<FileShareGrantToken>();
194 if (remote == nullptr) {
195 LOGE("%{public}s: get remoteObject failed!", __func__);
196 return -ret;
197 }
198
199 dataShareHelper = DataShare::DataShareHelper::Creator(remote->AsObject(), MEDIALIBRARY_DATA_URI);
200 if (!dataShareHelper) {
201 LOGE("%{public}s: connect to datashare failed!", __func__);
202 return ret;
203 }
204 string uriStr = MEDIA_GRANT_URI_PERMISSION;
205 if (isApi10) {
206 uriStr += MEDIA_API_VERSION_10;
207 }
208
209 Uri uri(uriStr);
210 ret = dataShareHelper->Insert(uri, valuesBucket);
211 if (ret < 0) {
212 LOGE("%{public}s: insert failed with error code %{public}d!", __func__, ret);
213 return ret;
214 }
215 return ret;
216 }
217
GrantInMediaLibrary(const UriPermissionInfo & uriPermInfo,Uri & uri)218 static int GrantInMediaLibrary(const UriPermissionInfo &uriPermInfo, Uri &uri)
219 {
220 bool isApi10 = false;
221 DataShareValuesBucket valuesBucket;
222 int ret = InitValuesBucket(uriPermInfo, uri, isApi10, valuesBucket);
223 if (ret < 0) {
224 LOGE("%{public}s: InitValuesBucket failed!", __func__);
225 return ret;
226 }
227
228 ret = InsertByDatashare(valuesBucket, isApi10);
229 if (ret < 0) {
230 LOGE("%{public}s: InsertByDatashare failed!", __func__);
231 return ret;
232 }
233 return 0;
234 }
235
DoGrantUriPermission(const UriPermissionInfo & uriPermInfo)236 static int DoGrantUriPermission(const UriPermissionInfo &uriPermInfo)
237 {
238 Uri uri(uriPermInfo.uri);
239 std::string authority = uri.GetAuthority();
240 std::string path = uri.GetPath();
241 if (authority == MEDIA_AUTHORITY && path.find(".") == string::npos) {
242 return GrantInMediaLibrary(uriPermInfo, uri);
243 } else {
244 auto& uriPermissionClient = OHOS::AAFwk::UriPermissionManagerClient::GetInstance();
245 int ret = uriPermissionClient.GrantUriPermission(uri, uriPermInfo.flag, uriPermInfo.bundleName);
246 if (ret != 0) {
247 LOGD("%{public}s: uriPermissionClient.GrantUriPermission by uri permission client failed!", __func__);
248 return GrantInMediaLibrary(uriPermInfo, uri);
249 }
250 }
251
252 return 0;
253 }
254
GrantUriPermission(ani_env * env,ani_string uri,ani_string bundleName,ani_object enumIndex)255 static void GrantUriPermission(ani_env *env, ani_string uri, ani_string bundleName, ani_object enumIndex)
256 {
257 LOGD("Enter GrantUriPermission.");
258 if (!IsSystemApp()) {
259 LOGE("%{public}s: GrantUriPermission is not System App!", __func__);
260 ThrowBusinessError(env, E_PERMISSION_SYS, "FileShare::GrantUriPermission is not System App!");
261 return;
262 }
263
264 UriPermissionInfo uriPermInfo;
265 std::string uriStr = ParseObjToStr(env, uri);
266 if (!CheckValidPublicUri(uriStr)) {
267 LOGE("%{public}s: GrantUriPermission uri is not valid!", __func__);
268 ThrowBusinessError(env, EINVAL, "GrantUriPermission uri is not valid!");
269 return;
270 }
271 uriPermInfo.uri = uriStr;
272 std::string bundleNameStr = ParseObjToStr(env, bundleName);
273 uriPermInfo.bundleName = bundleNameStr;
274
275 ani_int wantConstantFlag = ParseEnumToInt(env, static_cast<ani_enum_item>(enumIndex));
276 if (wantConstantFlag < 0) {
277 LOGE("%{public}s: GrantUriPermission ParseEnumToInt Faild!", __func__);
278 ThrowBusinessError(env, EINVAL, "GrantUriPermission is not System App!");
279 return;
280 }
281 uriPermInfo.flag = static_cast<uint32_t>(wantConstantFlag);
282 uriPermInfo.mode = GetModeFromFlag(wantConstantFlag);
283 LOGD("GrantUriPermission uri: %{public}s, bundleName: %{public}s, flag: %{public}d, mode: %{public}s",
284 uriStr.c_str(), bundleNameStr.c_str(), wantConstantFlag, uriPermInfo.mode.c_str());
285
286 int ret = DoGrantUriPermission(uriPermInfo);
287 if (ret < 0) {
288 LOGE("FileShare::GrantUriPermission DoGrantUriPermission failed with %{public}d", ret);
289 ThrowBusinessError(env, -ret, "GrantUriPermission failed");
290 return;
291 }
292 LOGD("FileShare::GrantUriPermission DoGrantUriPermission successfully!");
293 }
294 } // namespace ModuleFileShare
295 } // namespace AppFileService
296 } // namespace OHOS
297
ANI_Constructor(ani_vm * vm,uint32_t * result)298 ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
299 {
300 LOGD("Enter ANI_Constructor.");
301 ani_env *env;
302 if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
303 LOGE("Unsupported ANI_VERSION_1");
304 return ANI_ERROR;
305 }
306
307 static const char *nsName = "L@ohos/fileshare/fileShare;";
308 ani_namespace ns;
309 if (ANI_OK != env->FindNamespace(nsName, &ns)) {
310 LOGE("Not found namespace %{public}s.", nsName);
311 return ANI_NOT_FOUND;
312 }
313 std::array nsMethods = {
314 ani_native_function {"grantUriPermissionInner", nullptr,
315 reinterpret_cast<void *>(OHOS::AppFileService::ModuleFileShare::GrantUriPermission)},
316 };
317 if (ANI_OK != env->Namespace_BindNativeFunctions(ns, nsMethods.data(), nsMethods.size())) {
318 LOGE("Cannot bind native methods to namespace %{public}s.", nsName);
319 return ANI_ERROR;
320 };
321
322 *result = ANI_VERSION_1;
323 return ANI_OK;
324 }
325