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 "nativehelper/scoped_local_ref.h"
21 
22 namespace com {
23 namespace android {
24 namespace car {
25 namespace scriptexecutor {
26 
27 using ::android::base::Error;
28 using ::android::base::Result;
29 
BundleWrapper(JNIEnv * env)30 BundleWrapper::BundleWrapper(JNIEnv* env) {
31     mJNIEnv = env;
32     ScopedLocalRef<jclass> localBundleClassRef(mJNIEnv,
33                                                mJNIEnv->FindClass("android/os/PersistableBundle"));
34     mBundleClass = static_cast<jclass>(mJNIEnv->NewGlobalRef(localBundleClassRef.get()));
35 
36     jmethodID bundleConstructor = mJNIEnv->GetMethodID(mBundleClass, "<init>", "()V");
37     ScopedLocalRef<jobject> localBundleObjectRef(mJNIEnv,
38                                                  mJNIEnv->NewObject(mBundleClass,
39                                                                     bundleConstructor));
40     mBundle = mJNIEnv->NewGlobalRef(localBundleObjectRef.get());
41 }
42 
~BundleWrapper()43 BundleWrapper::~BundleWrapper() {
44     // Delete global JNI references.
45     if (mBundle != NULL) {
46         mJNIEnv->DeleteGlobalRef(mBundle);
47     }
48     if (mBundleClass != NULL) {
49         mJNIEnv->DeleteGlobalRef(mBundleClass);
50     }
51 }
52 
putBoolean(const char * key,bool value)53 Result<void> BundleWrapper::putBoolean(const char* key, bool value) {
54     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
55     if (keyStringRef == nullptr) {
56         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
57     }
58 
59     // TODO(b/188832769): consider caching the references.
60     jmethodID putBooleanMethod =
61             mJNIEnv->GetMethodID(mBundleClass, "putBoolean", "(Ljava/lang/String;Z)V");
62     mJNIEnv->CallVoidMethod(mBundle, putBooleanMethod, keyStringRef.get(),
63                             static_cast<jboolean>(value));
64     return {};  // ok result
65 }
66 
putLong(const char * key,int64_t value)67 Result<void> BundleWrapper::putLong(const char* key, int64_t value) {
68     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
69     if (keyStringRef == nullptr) {
70         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
71     }
72 
73     jmethodID putLongMethod =
74             mJNIEnv->GetMethodID(mBundleClass, "putLong", "(Ljava/lang/String;J)V");
75     mJNIEnv->CallVoidMethod(mBundle, putLongMethod, keyStringRef.get(), static_cast<jlong>(value));
76     return {};  // ok result
77 }
78 
putDouble(const char * key,double value)79 Result<void> BundleWrapper::putDouble(const char* key, double value) {
80     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
81     if (keyStringRef == nullptr) {
82         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
83     }
84 
85     jmethodID putDoubleMethod =
86             mJNIEnv->GetMethodID(mBundleClass, "putDouble", "(Ljava/lang/String;D)V");
87     mJNIEnv->CallVoidMethod(mBundle, putDoubleMethod, keyStringRef.get(),
88                             static_cast<jdouble>(value));
89     return {};  // ok result
90 }
91 
putString(const char * key,const char * value)92 Result<void> BundleWrapper::putString(const char* key, const char* value) {
93     jmethodID putStringMethod = mJNIEnv->GetMethodID(mBundleClass, "putString",
94                                                      "(Ljava/lang/String;Ljava/lang/String;)V");
95     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
96     if (keyStringRef == nullptr) {
97         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
98     }
99 
100     ScopedLocalRef<jstring> valueStringRef(mJNIEnv, mJNIEnv->NewStringUTF(value));
101     if (valueStringRef == nullptr) {
102         return Error() << "Failed to create a string for the provided value due to OOM error";
103     }
104 
105     mJNIEnv->CallVoidMethod(mBundle, putStringMethod, keyStringRef.get(), valueStringRef.get());
106     return {};  // ok result
107 }
108 
putLongArray(const char * key,const std::vector<int64_t> & value)109 Result<void> BundleWrapper::putLongArray(const char* key, const std::vector<int64_t>& value) {
110     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
111     if (keyStringRef == nullptr) {
112         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
113     }
114 
115     jmethodID putLongArrayMethod =
116             mJNIEnv->GetMethodID(mBundleClass, "putLongArray", "(Ljava/lang/String;[J)V");
117 
118     ScopedLocalRef<jlongArray> arrayRef(mJNIEnv, mJNIEnv->NewLongArray(value.size()));
119     mJNIEnv->SetLongArrayRegion(arrayRef.get(), 0, value.size(), &value[0]);
120     mJNIEnv->CallVoidMethod(mBundle, putLongArrayMethod, keyStringRef.get(), arrayRef.get());
121     return {};  // ok result
122 }
123 
putStringArray(const char * key,const std::vector<std::string> & value)124 Result<void> BundleWrapper::putStringArray(const char* key, const std::vector<std::string>& value) {
125     ScopedLocalRef<jstring> keyStringRef(mJNIEnv, mJNIEnv->NewStringUTF(key));
126     if (keyStringRef == nullptr) {
127         return Error() << "Failed to create a string for a key=" << key << " due to OOM error";
128     }
129 
130     jmethodID putStringArrayMethod =
131             mJNIEnv->GetMethodID(mBundleClass, "putStringArray",
132                                  "(Ljava/lang/String;[Ljava/lang/String;)V");
133 
134     ScopedLocalRef<jclass> stringClassRef(mJNIEnv, mJNIEnv->FindClass("java/lang/String"));
135     ScopedLocalRef<jobjectArray> arrayRef(mJNIEnv,
136                                           mJNIEnv->NewObjectArray(value.size(),
137                                                                   mJNIEnv->FindClass(
138                                                                           "java/lang/String"),
139                                                                   nullptr));
140     for (int i = 0; i < value.size(); i++) {
141         ScopedLocalRef<jstring> valueStringRef(mJNIEnv, mJNIEnv->NewStringUTF(value[i].c_str()));
142         if (valueStringRef == nullptr) {
143             return Error() << "Failed to create a string for provided value due to OOM error";
144         }
145         mJNIEnv->SetObjectArrayElement(arrayRef.get(), i, valueStringRef.get());
146     }
147     mJNIEnv->CallVoidMethod(mBundle, putStringArrayMethod, keyStringRef.get(), arrayRef.get());
148     return {};  // ok result
149 }
150 
getBundle()151 jobject BundleWrapper::getBundle() {
152     return mBundle;
153 }
154 
155 }  // namespace scriptexecutor
156 }  // namespace car
157 }  // namespace android
158 }  // namespace com
159