1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <sstream>
18 #include <string>
19 #include <vector>
20 
21 #include <nativehelper/JNIHelp.h>
22 #include "jni.h"
23 #include "core_jni_helpers.h"
24 
25 #include "android-base/logging.h"
26 #include "androidfw/PosixUtils.h"
27 
28 using ::android::util::ExecuteBinary;
29 
30 static jclass g_stringClass = nullptr;
31 
createIdmap(JNIEnv * env,jclass,jstring targetPath,jobjectArray overlayPath,jobjectArray policies,jboolean enforceOverlayable)32 static jobjectArray createIdmap(JNIEnv* env, jclass /*clazz*/, jstring targetPath,
33                            jobjectArray overlayPath, jobjectArray policies,
34                            jboolean enforceOverlayable) {
35   if (access("/system/bin/idmap2", X_OK) == -1) {
36     PLOG(WARNING) << "unable to execute idmap2";
37     return nullptr;
38   }
39 
40   const char* targetApkPath = env->GetStringUTFChars(targetPath, NULL /* isCopy */);
41   std::vector<std::string> argv{"/system/bin/idmap2",
42     "create-multiple",
43     "--target-apk-path", targetApkPath,
44   };
45   env->ReleaseStringUTFChars(targetPath, targetApkPath);
46 
47   // Add the overlays for which to generate idmap files to the idmap arguments.
48   for (size_t i = 0, count = env->GetArrayLength(overlayPath); i < count; ++i) {
49     jstring element = (jstring) env->GetObjectArrayElement(overlayPath, i);
50     const char* overlayApkPath = env->GetStringUTFChars(element, NULL /* isCopy */);
51     argv.emplace_back("--overlay-apk-path");
52     argv.emplace_back(overlayApkPath);
53     env->ReleaseStringUTFChars(element, overlayApkPath);
54   }
55 
56   // Add the policies the overlays fulfill to the idmap arguments.
57   for (size_t i = 0, count = env->GetArrayLength(policies); i < count; ++i) {
58     jstring element = (jstring)env->GetObjectArrayElement(policies, i);
59     const char* policy = env->GetStringUTFChars(element, NULL /* isCopy */);
60     argv.emplace_back("--policy");
61     argv.emplace_back(policy);
62     env->ReleaseStringUTFChars(element, policy);
63   }
64 
65   if (!enforceOverlayable) {
66     argv.emplace_back("--ignore-overlayable");
67   }
68 
69   auto result = ExecuteBinary(argv);
70   if (!result) {
71       LOG(ERROR) << "failed to execute idmap2";
72       return nullptr;
73   }
74 
75   if (result.status != 0) {
76       LOG(ERROR) << "idmap2: " << result.stderr_str;
77       return nullptr;
78   }
79 
80   // Return the paths of the idmaps created or updated during the idmap invocation.
81   std::vector<std::string> idmap_paths;
82   std::istringstream input(std::move(result.stdout_str));
83   std::string path;
84   while (std::getline(input, path)) {
85       idmap_paths.push_back(std::move(path));
86   }
87 
88   jobjectArray array = env->NewObjectArray(idmap_paths.size(), g_stringClass, nullptr);
89   if (array == nullptr) {
90     return nullptr;
91   }
92   for (size_t i = 0; i < idmap_paths.size(); i++) {
93       const std::string& path = idmap_paths[i];
94       jstring java_string = env->NewStringUTF(path.c_str());
95       if (env->ExceptionCheck()) {
96           return nullptr;
97       }
98     env->SetObjectArrayElement(array, i, java_string);
99     env->DeleteLocalRef(java_string);
100   }
101 
102   return array;
103 }
104 
105 static const JNINativeMethod g_methods[] = {
106     { "createIdmap",
107       "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Z)[Ljava/lang/String;",
108       (void *)createIdmap },
109 };
110 
111 static const char* const kOverlayConfigPathName = "com/android/internal/content/om/OverlayConfig";
112 
113 namespace android {
114 
register_com_android_internal_content_om_OverlayConfig(JNIEnv * env)115 int register_com_android_internal_content_om_OverlayConfig(JNIEnv* env) {
116   jclass stringClass = FindClassOrDie(env, "java/lang/String");
117   g_stringClass = MakeGlobalRefOrDie(env, stringClass);
118 
119   return RegisterMethodsOrDie(env, kOverlayConfigPathName, g_methods, NELEM(g_methods));
120 }
121 
122 } // namespace android
123