1 /*
2  * Copyright (c) 2021, 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 "BundleWrapper.h"
18 
19 #include "JniUtils.h"
20 #include "jni.h"
21 #include "nativehelper/scoped_local_ref.h"
22 
23 namespace com {
24 namespace android {
25 namespace car {
26 namespace scriptexecutor {
27 
28 using ::android::base::Error;
29 using ::android::base::Result;
30 
BundleWrapper(JNIEnv * env)31 BundleWrapper::BundleWrapper(JNIEnv* env) {
32     mJNIEnv = env;
33     ScopedLocalRef<jclass> localBundleClassRef(mJNIEnv,
34                                                mJNIEnv->FindClass("android/os/PersistableBundle"));
35     mBundleClass = static_cast<jclass>(mJNIEnv->NewGlobalRef(localBundleClassRef.get()));
36 
37     jmethodID bundleConstructor = mJNIEnv->GetMethodID(mBundleClass, "<init>", "()V");
38     ScopedLocalRef<jobject> localBundleObjectRef(mJNIEnv,
39                                                  mJNIEnv->NewObject(mBundleClass,
40                                                                     bundleConstructor));
41     mBundle = mJNIEnv->NewGlobalRef(localBundleObjectRef.get());
42 }
43 
~BundleWrapper()44 BundleWrapper::~BundleWrapper() {
45     // Delete global JNI references.
46     if (mBundle != NULL) {
47         mJNIEnv->DeleteGlobalRef(mBundle);
48     }
49     if (mBundleClass != NULL) {
50         mJNIEnv->DeleteGlobalRef(mBundleClass);
51     }
52 }
53 
putBoolean(const char * key,bool value)54 Result<void> BundleWrapper::putBoolean(const char* key, bool value) {
55     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
56     if (keyStringRef == nullptr) {
57         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
58     }
59 
60     // TODO(b/188832769): consider caching the references.
61     jmethodID putBooleanMethod =
62             mJNIEnv->GetMethodID(mBundleClass, "putBoolean", "(Ljava/lang/String;Z)V");
63     mJNIEnv->CallVoidMethod(mBundle, putBooleanMethod, keyStringRef.get(),
64                             static_cast<jboolean>(value));
65     return {};  // ok result
66 }
67 
putLong(const char * key,int64_t value)68 Result<void> BundleWrapper::putLong(const char* key, int64_t value) {
69     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
70     if (keyStringRef == nullptr) {
71         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
72     }
73 
74     jmethodID putLongMethod =
75             mJNIEnv->GetMethodID(mBundleClass, "putLong", "(Ljava/lang/String;J)V");
76     mJNIEnv->CallVoidMethod(mBundle, putLongMethod, keyStringRef.get(), static_cast<jlong>(value));
77     return {};  // ok result
78 }
79 
putDouble(const char * key,double value)80 Result<void> BundleWrapper::putDouble(const char* key, double value) {
81     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
82     if (keyStringRef == nullptr) {
83         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
84     }
85 
86     jmethodID putDoubleMethod =
87             mJNIEnv->GetMethodID(mBundleClass, "putDouble", "(Ljava/lang/String;D)V");
88     mJNIEnv->CallVoidMethod(mBundle, putDoubleMethod, keyStringRef.get(),
89                             static_cast<jdouble>(value));
90     return {};  // ok result
91 }
92 
putString(const char * key,const char * value)93 Result<void> BundleWrapper::putString(const char* key, const char* value) {
94     jmethodID putStringMethod = mJNIEnv->GetMethodID(mBundleClass, "putString",
95                                                      "(Ljava/lang/String;Ljava/lang/String;)V");
96     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
97     if (keyStringRef == nullptr) {
98         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
99     }
100 
101     ScopedLocalRef<jstring> valueStringRef(mJNIEnv, mJNIEnv->NewStringUTF(value));
102     if (valueStringRef == nullptr) {
103         return Error() << "Failed to create a string for the provided value due to OOM error";
104     }
105 
106     mJNIEnv->CallVoidMethod(mBundle, putStringMethod, keyStringRef.get(), valueStringRef.get());
107     return {};  // ok result
108 }
109 
putBooleanArray(const char * key,const std::vector<unsigned char> & value)110 Result<void> BundleWrapper::putBooleanArray(const char* key,
111                                             const std::vector<unsigned char>& value) {
112     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
113     if (keyStringRef == nullptr) {
114         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
115     }
116 
117     jmethodID putBooleanArrayMethod =
118             mJNIEnv->GetMethodID(mBundleClass, "putBooleanArray", "(Ljava/lang/String;[Z)V");
119 
120     ScopedLocalRef<jbooleanArray> arrayRef(mJNIEnv, mJNIEnv->NewBooleanArray(value.size()));
121     mJNIEnv->SetBooleanArrayRegion(arrayRef.get(), 0, value.size(), &value.at(0));
122     mJNIEnv->CallVoidMethod(mBundle, putBooleanArrayMethod, keyStringRef.get(), arrayRef.get());
123     return {};  // ok result
124 }
125 
putDoubleArray(const char * key,const std::vector<double> & value)126 Result<void> BundleWrapper::putDoubleArray(const char* key, const std::vector<double>& value) {
127     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
128     if (keyStringRef == nullptr) {
129         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
130     }
131 
132     jmethodID putDoubleArrayMethod =
133             mJNIEnv->GetMethodID(mBundleClass, "putDoubleArray", "(Ljava/lang/String;[D)V");
134 
135     ScopedLocalRef<jdoubleArray> arrayRef(mJNIEnv, mJNIEnv->NewDoubleArray(value.size()));
136     mJNIEnv->SetDoubleArrayRegion(arrayRef.get(), 0, value.size(), &value[0]);
137     mJNIEnv->CallVoidMethod(mBundle, putDoubleArrayMethod, keyStringRef.get(), arrayRef.get());
138     return {};  // ok result
139 }
140 
putLongArray(const char * key,const std::vector<int64_t> & value)141 Result<void> BundleWrapper::putLongArray(const char* key, const std::vector<int64_t>& value) {
142     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
143     if (keyStringRef == nullptr) {
144         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
145     }
146 
147     jmethodID putLongArrayMethod =
148             mJNIEnv->GetMethodID(mBundleClass, "putLongArray", "(Ljava/lang/String;[J)V");
149 
150     ScopedLocalRef<jlongArray> arrayRef(mJNIEnv, mJNIEnv->NewLongArray(value.size()));
151     mJNIEnv->SetLongArrayRegion(arrayRef.get(), 0, value.size(), &value[0]);
152     mJNIEnv->CallVoidMethod(mBundle, putLongArrayMethod, keyStringRef.get(), arrayRef.get());
153     return {};  // ok result
154 }
155 
putStringArray(const char * key,const std::vector<std::string> & value)156 Result<void> BundleWrapper::putStringArray(const char* key, const std::vector<std::string>& value) {
157     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
158     if (keyStringRef == nullptr) {
159         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
160     }
161 
162     jmethodID putStringArrayMethod =
163             mJNIEnv->GetMethodID(mBundleClass, "putStringArray",
164                                  "(Ljava/lang/String;[Ljava/lang/String;)V");
165 
166     ScopedLocalRef<jclass> stringClassRef(mJNIEnv, mJNIEnv->FindClass("java/lang/String"));
167     ScopedLocalRef<jobjectArray> arrayRef(mJNIEnv,
168                                           mJNIEnv->NewObjectArray(value.size(),
169                                                                   mJNIEnv->FindClass(
170                                                                           "java/lang/String"),
171                                                                   nullptr));
172     for (int i = 0; i < value.size(); i++) {
173         ScopedLocalRef<jstring> valueStringRef(mJNIEnv, mJNIEnv->NewStringUTF(value[i].c_str()));
174         if (valueStringRef == nullptr) {
175             return Error() << "Failed to create a string for provided value due to OOM error";
176         }
177         mJNIEnv->SetObjectArrayElement(arrayRef.get(), i, valueStringRef.get());
178     }
179     mJNIEnv->CallVoidMethod(mBundle, putStringArrayMethod, keyStringRef.get(), arrayRef.get());
180     return {};  // ok result
181 }
182 
putPersistableBundle(const char * key,const BundleWrapper & value)183 Result<void> BundleWrapper::putPersistableBundle(const char* key, const BundleWrapper& value) {
184     jmethodID putPersistableBundleMethod =
185             mJNIEnv->GetMethodID(mBundleClass, "putPersistableBundle",
186                                  "(Ljava/lang/String;Landroid/os/PersistableBundle;)V");
187     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
188     if (keyStringRef == nullptr) {
189         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
190     }
191     mJNIEnv->CallVoidMethod(mBundle, putPersistableBundleMethod, keyStringRef.get(),
192                             value.getBundle());
193     return {};  // ok result
194 }
195 
getBundle() const196 jobject BundleWrapper::getBundle() const {
197     return mBundle;
198 }
199 
200 }  // namespace scriptexecutor
201 }  // namespace car
202 }  // namespace android
203 }  // namespace com
204