xref: /aosp_15_r20/external/libtextclassifier/native/utils/intents/jni-lua.cc (revision 993b0882672172b81d12fad7a7ac0c3e5c824a12)
1*993b0882SAndroid Build Coastguard Worker /*
2*993b0882SAndroid Build Coastguard Worker  * Copyright (C) 2018 The Android Open Source Project
3*993b0882SAndroid Build Coastguard Worker  *
4*993b0882SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*993b0882SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*993b0882SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*993b0882SAndroid Build Coastguard Worker  *
8*993b0882SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*993b0882SAndroid Build Coastguard Worker  *
10*993b0882SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*993b0882SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*993b0882SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*993b0882SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*993b0882SAndroid Build Coastguard Worker  * limitations under the License.
15*993b0882SAndroid Build Coastguard Worker  */
16*993b0882SAndroid Build Coastguard Worker 
17*993b0882SAndroid Build Coastguard Worker #include "utils/intents/jni-lua.h"
18*993b0882SAndroid Build Coastguard Worker 
19*993b0882SAndroid Build Coastguard Worker #include "utils/hash/farmhash.h"
20*993b0882SAndroid Build Coastguard Worker #include "utils/java/jni-helper.h"
21*993b0882SAndroid Build Coastguard Worker #include "utils/strings/substitute.h"
22*993b0882SAndroid Build Coastguard Worker 
23*993b0882SAndroid Build Coastguard Worker #ifdef __cplusplus
24*993b0882SAndroid Build Coastguard Worker extern "C" {
25*993b0882SAndroid Build Coastguard Worker #endif
26*993b0882SAndroid Build Coastguard Worker #include "lauxlib.h"
27*993b0882SAndroid Build Coastguard Worker #include "lua.h"
28*993b0882SAndroid Build Coastguard Worker #ifdef __cplusplus
29*993b0882SAndroid Build Coastguard Worker }
30*993b0882SAndroid Build Coastguard Worker #endif
31*993b0882SAndroid Build Coastguard Worker 
32*993b0882SAndroid Build Coastguard Worker namespace libtextclassifier3 {
33*993b0882SAndroid Build Coastguard Worker namespace {
34*993b0882SAndroid Build Coastguard Worker 
35*993b0882SAndroid Build Coastguard Worker static constexpr const char* kHashKey = "hash";
36*993b0882SAndroid Build Coastguard Worker static constexpr const char* kUrlSchemaKey = "url_schema";
37*993b0882SAndroid Build Coastguard Worker static constexpr const char* kUrlHostKey = "url_host";
38*993b0882SAndroid Build Coastguard Worker static constexpr const char* kUrlEncodeKey = "urlencode";
39*993b0882SAndroid Build Coastguard Worker static constexpr const char* kPackageNameKey = "package_name";
40*993b0882SAndroid Build Coastguard Worker static constexpr const char* kDeviceLocaleKey = "device_locales";
41*993b0882SAndroid Build Coastguard Worker static constexpr const char* kFormatKey = "format";
42*993b0882SAndroid Build Coastguard Worker 
43*993b0882SAndroid Build Coastguard Worker }  // namespace
44*993b0882SAndroid Build Coastguard Worker 
JniLuaEnvironment(const Resources & resources,const JniCache * jni_cache,const jobject context,const std::vector<Locale> & device_locales)45*993b0882SAndroid Build Coastguard Worker JniLuaEnvironment::JniLuaEnvironment(const Resources& resources,
46*993b0882SAndroid Build Coastguard Worker                                      const JniCache* jni_cache,
47*993b0882SAndroid Build Coastguard Worker                                      const jobject context,
48*993b0882SAndroid Build Coastguard Worker                                      const std::vector<Locale>& device_locales)
49*993b0882SAndroid Build Coastguard Worker     : LuaEnvironment(),
50*993b0882SAndroid Build Coastguard Worker       resources_(resources),
51*993b0882SAndroid Build Coastguard Worker       jenv_(jni_cache ? jni_cache->GetEnv() : nullptr),
52*993b0882SAndroid Build Coastguard Worker       jni_cache_(jni_cache),
53*993b0882SAndroid Build Coastguard Worker       context_(context),
54*993b0882SAndroid Build Coastguard Worker       device_locales_(device_locales),
55*993b0882SAndroid Build Coastguard Worker       usermanager_(/*object=*/nullptr,
56*993b0882SAndroid Build Coastguard Worker                    /*jvm=*/(jni_cache ? jni_cache->jvm : nullptr)),
57*993b0882SAndroid Build Coastguard Worker       usermanager_retrieved_(false),
58*993b0882SAndroid Build Coastguard Worker       system_resources_(/*object=*/nullptr,
59*993b0882SAndroid Build Coastguard Worker                         /*jvm=*/(jni_cache ? jni_cache->jvm : nullptr)),
60*993b0882SAndroid Build Coastguard Worker       system_resources_resources_retrieved_(false),
61*993b0882SAndroid Build Coastguard Worker       string_(/*object=*/nullptr,
62*993b0882SAndroid Build Coastguard Worker               /*jvm=*/(jni_cache ? jni_cache->jvm : nullptr)),
63*993b0882SAndroid Build Coastguard Worker       android_(/*object=*/nullptr,
64*993b0882SAndroid Build Coastguard Worker                /*jvm=*/(jni_cache ? jni_cache->jvm : nullptr)) {}
65*993b0882SAndroid Build Coastguard Worker 
PreallocateConstantJniStrings()66*993b0882SAndroid Build Coastguard Worker bool JniLuaEnvironment::PreallocateConstantJniStrings() {
67*993b0882SAndroid Build Coastguard Worker   TC3_ASSIGN_OR_RETURN_FALSE(ScopedLocalRef<jstring> string_value,
68*993b0882SAndroid Build Coastguard Worker                              JniHelper::NewStringUTF(jenv_, "string"));
69*993b0882SAndroid Build Coastguard Worker   string_ = MakeGlobalRef(string_value.get(), jenv_, jni_cache_->jvm);
70*993b0882SAndroid Build Coastguard Worker   TC3_ASSIGN_OR_RETURN_FALSE(ScopedLocalRef<jstring> android_value,
71*993b0882SAndroid Build Coastguard Worker                              JniHelper::NewStringUTF(jenv_, "android"));
72*993b0882SAndroid Build Coastguard Worker   android_ = MakeGlobalRef(android_value.get(), jenv_, jni_cache_->jvm);
73*993b0882SAndroid Build Coastguard Worker   if (string_ == nullptr || android_ == nullptr) {
74*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Could not allocate constant strings references.";
75*993b0882SAndroid Build Coastguard Worker     return false;
76*993b0882SAndroid Build Coastguard Worker   }
77*993b0882SAndroid Build Coastguard Worker   return true;
78*993b0882SAndroid Build Coastguard Worker }
79*993b0882SAndroid Build Coastguard Worker 
Initialize()80*993b0882SAndroid Build Coastguard Worker bool JniLuaEnvironment::Initialize() {
81*993b0882SAndroid Build Coastguard Worker   if (!PreallocateConstantJniStrings()) {
82*993b0882SAndroid Build Coastguard Worker     return false;
83*993b0882SAndroid Build Coastguard Worker   }
84*993b0882SAndroid Build Coastguard Worker   return (RunProtected([this] {
85*993b0882SAndroid Build Coastguard Worker             LoadDefaultLibraries();
86*993b0882SAndroid Build Coastguard Worker             SetupExternalHook();
87*993b0882SAndroid Build Coastguard Worker             lua_setglobal(state_, "external");
88*993b0882SAndroid Build Coastguard Worker             return LUA_OK;
89*993b0882SAndroid Build Coastguard Worker           }) == LUA_OK);
90*993b0882SAndroid Build Coastguard Worker }
91*993b0882SAndroid Build Coastguard Worker 
SetupExternalHook()92*993b0882SAndroid Build Coastguard Worker void JniLuaEnvironment::SetupExternalHook() {
93*993b0882SAndroid Build Coastguard Worker   // This exposes an `external` object with the following fields:
94*993b0882SAndroid Build Coastguard Worker   //   * entity: the bundle with all information about a classification.
95*993b0882SAndroid Build Coastguard Worker   //   * android: callbacks into specific android provided methods.
96*993b0882SAndroid Build Coastguard Worker   //   * android.user_restrictions: callbacks to check user permissions.
97*993b0882SAndroid Build Coastguard Worker   //   * android.R: callbacks to retrieve string resources.
98*993b0882SAndroid Build Coastguard Worker   PushLazyObject(&JniLuaEnvironment::HandleExternalCallback);
99*993b0882SAndroid Build Coastguard Worker 
100*993b0882SAndroid Build Coastguard Worker   // android
101*993b0882SAndroid Build Coastguard Worker   PushLazyObject(&JniLuaEnvironment::HandleAndroidCallback);
102*993b0882SAndroid Build Coastguard Worker   {
103*993b0882SAndroid Build Coastguard Worker     // android.user_restrictions
104*993b0882SAndroid Build Coastguard Worker     PushLazyObject(&JniLuaEnvironment::HandleUserRestrictionsCallback);
105*993b0882SAndroid Build Coastguard Worker     lua_setfield(state_, /*idx=*/-2, "user_restrictions");
106*993b0882SAndroid Build Coastguard Worker 
107*993b0882SAndroid Build Coastguard Worker     // android.R
108*993b0882SAndroid Build Coastguard Worker     // Callback to access android string resources.
109*993b0882SAndroid Build Coastguard Worker     PushLazyObject(&JniLuaEnvironment::HandleAndroidStringResources);
110*993b0882SAndroid Build Coastguard Worker     lua_setfield(state_, /*idx=*/-2, "R");
111*993b0882SAndroid Build Coastguard Worker   }
112*993b0882SAndroid Build Coastguard Worker   lua_setfield(state_, /*idx=*/-2, "android");
113*993b0882SAndroid Build Coastguard Worker }
114*993b0882SAndroid Build Coastguard Worker 
HandleExternalCallback()115*993b0882SAndroid Build Coastguard Worker int JniLuaEnvironment::HandleExternalCallback() {
116*993b0882SAndroid Build Coastguard Worker   const StringPiece key = ReadString(kIndexStackTop);
117*993b0882SAndroid Build Coastguard Worker   if (key.Equals(kHashKey)) {
118*993b0882SAndroid Build Coastguard Worker     PushFunction(&JniLuaEnvironment::HandleHash);
119*993b0882SAndroid Build Coastguard Worker     return 1;
120*993b0882SAndroid Build Coastguard Worker   } else if (key.Equals(kFormatKey)) {
121*993b0882SAndroid Build Coastguard Worker     PushFunction(&JniLuaEnvironment::HandleFormat);
122*993b0882SAndroid Build Coastguard Worker     return 1;
123*993b0882SAndroid Build Coastguard Worker   } else {
124*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Undefined external access " << key;
125*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
126*993b0882SAndroid Build Coastguard Worker     return 0;
127*993b0882SAndroid Build Coastguard Worker   }
128*993b0882SAndroid Build Coastguard Worker }
129*993b0882SAndroid Build Coastguard Worker 
HandleAndroidCallback()130*993b0882SAndroid Build Coastguard Worker int JniLuaEnvironment::HandleAndroidCallback() {
131*993b0882SAndroid Build Coastguard Worker   const StringPiece key = ReadString(kIndexStackTop);
132*993b0882SAndroid Build Coastguard Worker   if (key.Equals(kDeviceLocaleKey)) {
133*993b0882SAndroid Build Coastguard Worker     // Provide the locale as table with the individual fields set.
134*993b0882SAndroid Build Coastguard Worker     lua_newtable(state_);
135*993b0882SAndroid Build Coastguard Worker     for (int i = 0; i < device_locales_.size(); i++) {
136*993b0882SAndroid Build Coastguard Worker       // Adjust index to 1-based indexing for Lua.
137*993b0882SAndroid Build Coastguard Worker       lua_pushinteger(state_, i + 1);
138*993b0882SAndroid Build Coastguard Worker       lua_newtable(state_);
139*993b0882SAndroid Build Coastguard Worker       PushString(device_locales_[i].Language());
140*993b0882SAndroid Build Coastguard Worker       lua_setfield(state_, -2, "language");
141*993b0882SAndroid Build Coastguard Worker       PushString(device_locales_[i].Region());
142*993b0882SAndroid Build Coastguard Worker       lua_setfield(state_, -2, "region");
143*993b0882SAndroid Build Coastguard Worker       PushString(device_locales_[i].Script());
144*993b0882SAndroid Build Coastguard Worker       lua_setfield(state_, -2, "script");
145*993b0882SAndroid Build Coastguard Worker       lua_settable(state_, /*idx=*/-3);
146*993b0882SAndroid Build Coastguard Worker     }
147*993b0882SAndroid Build Coastguard Worker     return 1;
148*993b0882SAndroid Build Coastguard Worker   } else if (key.Equals(kPackageNameKey)) {
149*993b0882SAndroid Build Coastguard Worker     if (context_ == nullptr) {
150*993b0882SAndroid Build Coastguard Worker       TC3_LOG(ERROR) << "Context invalid.";
151*993b0882SAndroid Build Coastguard Worker       lua_error(state_);
152*993b0882SAndroid Build Coastguard Worker       return 0;
153*993b0882SAndroid Build Coastguard Worker     }
154*993b0882SAndroid Build Coastguard Worker 
155*993b0882SAndroid Build Coastguard Worker     StatusOr<ScopedLocalRef<jstring>> status_or_package_name_str =
156*993b0882SAndroid Build Coastguard Worker         JniHelper::CallObjectMethod<jstring>(
157*993b0882SAndroid Build Coastguard Worker             jenv_, context_, jni_cache_->context_get_package_name);
158*993b0882SAndroid Build Coastguard Worker 
159*993b0882SAndroid Build Coastguard Worker     if (!status_or_package_name_str.ok()) {
160*993b0882SAndroid Build Coastguard Worker       TC3_LOG(ERROR) << "Error calling Context.getPackageName";
161*993b0882SAndroid Build Coastguard Worker       lua_error(state_);
162*993b0882SAndroid Build Coastguard Worker       return 0;
163*993b0882SAndroid Build Coastguard Worker     }
164*993b0882SAndroid Build Coastguard Worker     StatusOr<std::string> status_or_package_name_std_str = JStringToUtf8String(
165*993b0882SAndroid Build Coastguard Worker         jenv_, status_or_package_name_str.ValueOrDie().get());
166*993b0882SAndroid Build Coastguard Worker     if (!status_or_package_name_std_str.ok()) {
167*993b0882SAndroid Build Coastguard Worker       lua_error(state_);
168*993b0882SAndroid Build Coastguard Worker       return 0;
169*993b0882SAndroid Build Coastguard Worker     }
170*993b0882SAndroid Build Coastguard Worker     PushString(status_or_package_name_std_str.ValueOrDie());
171*993b0882SAndroid Build Coastguard Worker     return 1;
172*993b0882SAndroid Build Coastguard Worker   } else if (key.Equals(kUrlEncodeKey)) {
173*993b0882SAndroid Build Coastguard Worker     PushFunction(&JniLuaEnvironment::HandleUrlEncode);
174*993b0882SAndroid Build Coastguard Worker     return 1;
175*993b0882SAndroid Build Coastguard Worker   } else if (key.Equals(kUrlHostKey)) {
176*993b0882SAndroid Build Coastguard Worker     PushFunction(&JniLuaEnvironment::HandleUrlHost);
177*993b0882SAndroid Build Coastguard Worker     return 1;
178*993b0882SAndroid Build Coastguard Worker   } else if (key.Equals(kUrlSchemaKey)) {
179*993b0882SAndroid Build Coastguard Worker     PushFunction(&JniLuaEnvironment::HandleUrlSchema);
180*993b0882SAndroid Build Coastguard Worker     return 1;
181*993b0882SAndroid Build Coastguard Worker   } else {
182*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Undefined android reference " << key;
183*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
184*993b0882SAndroid Build Coastguard Worker     return 0;
185*993b0882SAndroid Build Coastguard Worker   }
186*993b0882SAndroid Build Coastguard Worker }
187*993b0882SAndroid Build Coastguard Worker 
HandleUserRestrictionsCallback()188*993b0882SAndroid Build Coastguard Worker int JniLuaEnvironment::HandleUserRestrictionsCallback() {
189*993b0882SAndroid Build Coastguard Worker   if (jni_cache_->usermanager_class == nullptr ||
190*993b0882SAndroid Build Coastguard Worker       jni_cache_->usermanager_get_user_restrictions == nullptr) {
191*993b0882SAndroid Build Coastguard Worker     // UserManager is only available for API level >= 17 and
192*993b0882SAndroid Build Coastguard Worker     // getUserRestrictions only for API level >= 18, so we just return false
193*993b0882SAndroid Build Coastguard Worker     // normally here.
194*993b0882SAndroid Build Coastguard Worker     lua_pushboolean(state_, false);
195*993b0882SAndroid Build Coastguard Worker     return 1;
196*993b0882SAndroid Build Coastguard Worker   }
197*993b0882SAndroid Build Coastguard Worker 
198*993b0882SAndroid Build Coastguard Worker   // Get user manager if not previously retrieved.
199*993b0882SAndroid Build Coastguard Worker   if (!RetrieveUserManager()) {
200*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Error retrieving user manager.";
201*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
202*993b0882SAndroid Build Coastguard Worker     return 0;
203*993b0882SAndroid Build Coastguard Worker   }
204*993b0882SAndroid Build Coastguard Worker 
205*993b0882SAndroid Build Coastguard Worker   StatusOr<ScopedLocalRef<jobject>> status_or_bundle =
206*993b0882SAndroid Build Coastguard Worker       JniHelper::CallObjectMethod(
207*993b0882SAndroid Build Coastguard Worker           jenv_, usermanager_.get(),
208*993b0882SAndroid Build Coastguard Worker           jni_cache_->usermanager_get_user_restrictions);
209*993b0882SAndroid Build Coastguard Worker   if (!status_or_bundle.ok() || status_or_bundle.ValueOrDie() == nullptr) {
210*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Error calling getUserRestrictions";
211*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
212*993b0882SAndroid Build Coastguard Worker     return 0;
213*993b0882SAndroid Build Coastguard Worker   }
214*993b0882SAndroid Build Coastguard Worker 
215*993b0882SAndroid Build Coastguard Worker   const StringPiece key_str = ReadString(kIndexStackTop);
216*993b0882SAndroid Build Coastguard Worker   if (key_str.empty()) {
217*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Expected string, got null.";
218*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
219*993b0882SAndroid Build Coastguard Worker     return 0;
220*993b0882SAndroid Build Coastguard Worker   }
221*993b0882SAndroid Build Coastguard Worker 
222*993b0882SAndroid Build Coastguard Worker   const StatusOr<ScopedLocalRef<jstring>> status_or_key =
223*993b0882SAndroid Build Coastguard Worker       jni_cache_->ConvertToJavaString(key_str);
224*993b0882SAndroid Build Coastguard Worker   if (!status_or_key.ok()) {
225*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
226*993b0882SAndroid Build Coastguard Worker     return 0;
227*993b0882SAndroid Build Coastguard Worker   }
228*993b0882SAndroid Build Coastguard Worker   const StatusOr<bool> status_or_permission = JniHelper::CallBooleanMethod(
229*993b0882SAndroid Build Coastguard Worker       jenv_, status_or_bundle.ValueOrDie().get(),
230*993b0882SAndroid Build Coastguard Worker       jni_cache_->bundle_get_boolean, status_or_key.ValueOrDie().get());
231*993b0882SAndroid Build Coastguard Worker   if (!status_or_permission.ok()) {
232*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Error getting bundle value";
233*993b0882SAndroid Build Coastguard Worker     lua_pushboolean(state_, false);
234*993b0882SAndroid Build Coastguard Worker   } else {
235*993b0882SAndroid Build Coastguard Worker     lua_pushboolean(state_, status_or_permission.ValueOrDie());
236*993b0882SAndroid Build Coastguard Worker   }
237*993b0882SAndroid Build Coastguard Worker   return 1;
238*993b0882SAndroid Build Coastguard Worker }
239*993b0882SAndroid Build Coastguard Worker 
HandleUrlEncode()240*993b0882SAndroid Build Coastguard Worker int JniLuaEnvironment::HandleUrlEncode() {
241*993b0882SAndroid Build Coastguard Worker   const StringPiece input = ReadString(/*index=*/1);
242*993b0882SAndroid Build Coastguard Worker   if (input.empty()) {
243*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Expected string, got null.";
244*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
245*993b0882SAndroid Build Coastguard Worker     return 0;
246*993b0882SAndroid Build Coastguard Worker   }
247*993b0882SAndroid Build Coastguard Worker 
248*993b0882SAndroid Build Coastguard Worker   // Call Java Uri encode.
249*993b0882SAndroid Build Coastguard Worker   const StatusOr<ScopedLocalRef<jstring>> status_or_input_str =
250*993b0882SAndroid Build Coastguard Worker       jni_cache_->ConvertToJavaString(input);
251*993b0882SAndroid Build Coastguard Worker   if (!status_or_input_str.ok()) {
252*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
253*993b0882SAndroid Build Coastguard Worker     return 0;
254*993b0882SAndroid Build Coastguard Worker   }
255*993b0882SAndroid Build Coastguard Worker   StatusOr<ScopedLocalRef<jstring>> status_or_encoded_str =
256*993b0882SAndroid Build Coastguard Worker       JniHelper::CallStaticObjectMethod<jstring>(
257*993b0882SAndroid Build Coastguard Worker           jenv_, jni_cache_->uri_class.get(), jni_cache_->uri_encode,
258*993b0882SAndroid Build Coastguard Worker           status_or_input_str.ValueOrDie().get());
259*993b0882SAndroid Build Coastguard Worker 
260*993b0882SAndroid Build Coastguard Worker   if (!status_or_encoded_str.ok()) {
261*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Error calling Uri.encode";
262*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
263*993b0882SAndroid Build Coastguard Worker     return 0;
264*993b0882SAndroid Build Coastguard Worker   }
265*993b0882SAndroid Build Coastguard Worker   const StatusOr<std::string> status_or_encoded_std_str =
266*993b0882SAndroid Build Coastguard Worker       JStringToUtf8String(jenv_, status_or_encoded_str.ValueOrDie().get());
267*993b0882SAndroid Build Coastguard Worker   if (!status_or_encoded_std_str.ok()) {
268*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
269*993b0882SAndroid Build Coastguard Worker     return 0;
270*993b0882SAndroid Build Coastguard Worker   }
271*993b0882SAndroid Build Coastguard Worker   PushString(status_or_encoded_std_str.ValueOrDie());
272*993b0882SAndroid Build Coastguard Worker   return 1;
273*993b0882SAndroid Build Coastguard Worker }
274*993b0882SAndroid Build Coastguard Worker 
ParseUri(StringPiece url) const275*993b0882SAndroid Build Coastguard Worker StatusOr<ScopedLocalRef<jobject>> JniLuaEnvironment::ParseUri(
276*993b0882SAndroid Build Coastguard Worker     StringPiece url) const {
277*993b0882SAndroid Build Coastguard Worker   if (url.empty()) {
278*993b0882SAndroid Build Coastguard Worker     return {Status::UNKNOWN};
279*993b0882SAndroid Build Coastguard Worker   }
280*993b0882SAndroid Build Coastguard Worker 
281*993b0882SAndroid Build Coastguard Worker   // Call to Java URI parser.
282*993b0882SAndroid Build Coastguard Worker   TC3_ASSIGN_OR_RETURN(
283*993b0882SAndroid Build Coastguard Worker       const StatusOr<ScopedLocalRef<jstring>> status_or_url_str,
284*993b0882SAndroid Build Coastguard Worker       jni_cache_->ConvertToJavaString(url));
285*993b0882SAndroid Build Coastguard Worker 
286*993b0882SAndroid Build Coastguard Worker   // Try to parse uri and get scheme.
287*993b0882SAndroid Build Coastguard Worker   TC3_ASSIGN_OR_RETURN(
288*993b0882SAndroid Build Coastguard Worker       ScopedLocalRef<jobject> uri,
289*993b0882SAndroid Build Coastguard Worker       JniHelper::CallStaticObjectMethod(jenv_, jni_cache_->uri_class.get(),
290*993b0882SAndroid Build Coastguard Worker                                         jni_cache_->uri_parse,
291*993b0882SAndroid Build Coastguard Worker                                         status_or_url_str.ValueOrDie().get()));
292*993b0882SAndroid Build Coastguard Worker   if (uri == nullptr) {
293*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Error calling Uri.parse";
294*993b0882SAndroid Build Coastguard Worker     return {Status::UNKNOWN};
295*993b0882SAndroid Build Coastguard Worker   }
296*993b0882SAndroid Build Coastguard Worker   return uri;
297*993b0882SAndroid Build Coastguard Worker }
298*993b0882SAndroid Build Coastguard Worker 
HandleUrlSchema()299*993b0882SAndroid Build Coastguard Worker int JniLuaEnvironment::HandleUrlSchema() {
300*993b0882SAndroid Build Coastguard Worker   StringPiece url = ReadString(/*index=*/1);
301*993b0882SAndroid Build Coastguard Worker 
302*993b0882SAndroid Build Coastguard Worker   const StatusOr<ScopedLocalRef<jobject>> status_or_parsed_uri = ParseUri(url);
303*993b0882SAndroid Build Coastguard Worker   if (!status_or_parsed_uri.ok()) {
304*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
305*993b0882SAndroid Build Coastguard Worker     return 0;
306*993b0882SAndroid Build Coastguard Worker   }
307*993b0882SAndroid Build Coastguard Worker 
308*993b0882SAndroid Build Coastguard Worker   const StatusOr<ScopedLocalRef<jstring>> status_or_scheme_str =
309*993b0882SAndroid Build Coastguard Worker       JniHelper::CallObjectMethod<jstring>(
310*993b0882SAndroid Build Coastguard Worker           jenv_, status_or_parsed_uri.ValueOrDie().get(),
311*993b0882SAndroid Build Coastguard Worker           jni_cache_->uri_get_scheme);
312*993b0882SAndroid Build Coastguard Worker   if (!status_or_scheme_str.ok()) {
313*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Error calling Uri.getScheme";
314*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
315*993b0882SAndroid Build Coastguard Worker     return 0;
316*993b0882SAndroid Build Coastguard Worker   }
317*993b0882SAndroid Build Coastguard Worker   if (status_or_scheme_str.ValueOrDie() == nullptr) {
318*993b0882SAndroid Build Coastguard Worker     lua_pushnil(state_);
319*993b0882SAndroid Build Coastguard Worker   } else {
320*993b0882SAndroid Build Coastguard Worker     const StatusOr<std::string> status_or_scheme_std_str =
321*993b0882SAndroid Build Coastguard Worker         JStringToUtf8String(jenv_, status_or_scheme_str.ValueOrDie().get());
322*993b0882SAndroid Build Coastguard Worker     if (!status_or_scheme_std_str.ok()) {
323*993b0882SAndroid Build Coastguard Worker       lua_error(state_);
324*993b0882SAndroid Build Coastguard Worker       return 0;
325*993b0882SAndroid Build Coastguard Worker     }
326*993b0882SAndroid Build Coastguard Worker     PushString(status_or_scheme_std_str.ValueOrDie());
327*993b0882SAndroid Build Coastguard Worker   }
328*993b0882SAndroid Build Coastguard Worker   return 1;
329*993b0882SAndroid Build Coastguard Worker }
330*993b0882SAndroid Build Coastguard Worker 
HandleUrlHost()331*993b0882SAndroid Build Coastguard Worker int JniLuaEnvironment::HandleUrlHost() {
332*993b0882SAndroid Build Coastguard Worker   const StringPiece url = ReadString(kIndexStackTop);
333*993b0882SAndroid Build Coastguard Worker 
334*993b0882SAndroid Build Coastguard Worker   const StatusOr<ScopedLocalRef<jobject>> status_or_parsed_uri = ParseUri(url);
335*993b0882SAndroid Build Coastguard Worker   if (!status_or_parsed_uri.ok()) {
336*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
337*993b0882SAndroid Build Coastguard Worker     return 0;
338*993b0882SAndroid Build Coastguard Worker   }
339*993b0882SAndroid Build Coastguard Worker 
340*993b0882SAndroid Build Coastguard Worker   const StatusOr<ScopedLocalRef<jstring>> status_or_host_str =
341*993b0882SAndroid Build Coastguard Worker       JniHelper::CallObjectMethod<jstring>(
342*993b0882SAndroid Build Coastguard Worker           jenv_, status_or_parsed_uri.ValueOrDie().get(),
343*993b0882SAndroid Build Coastguard Worker           jni_cache_->uri_get_host);
344*993b0882SAndroid Build Coastguard Worker   if (!status_or_host_str.ok()) {
345*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Error calling Uri.getHost";
346*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
347*993b0882SAndroid Build Coastguard Worker     return 0;
348*993b0882SAndroid Build Coastguard Worker   }
349*993b0882SAndroid Build Coastguard Worker 
350*993b0882SAndroid Build Coastguard Worker   if (status_or_host_str.ValueOrDie() == nullptr) {
351*993b0882SAndroid Build Coastguard Worker     lua_pushnil(state_);
352*993b0882SAndroid Build Coastguard Worker   } else {
353*993b0882SAndroid Build Coastguard Worker     const StatusOr<std::string> status_or_host_std_str =
354*993b0882SAndroid Build Coastguard Worker         JStringToUtf8String(jenv_, status_or_host_str.ValueOrDie().get());
355*993b0882SAndroid Build Coastguard Worker     if (!status_or_host_std_str.ok()) {
356*993b0882SAndroid Build Coastguard Worker       lua_error(state_);
357*993b0882SAndroid Build Coastguard Worker       return 0;
358*993b0882SAndroid Build Coastguard Worker     }
359*993b0882SAndroid Build Coastguard Worker     PushString(status_or_host_std_str.ValueOrDie());
360*993b0882SAndroid Build Coastguard Worker   }
361*993b0882SAndroid Build Coastguard Worker   return 1;
362*993b0882SAndroid Build Coastguard Worker }
363*993b0882SAndroid Build Coastguard Worker 
HandleHash()364*993b0882SAndroid Build Coastguard Worker int JniLuaEnvironment::HandleHash() {
365*993b0882SAndroid Build Coastguard Worker   const StringPiece input = ReadString(kIndexStackTop);
366*993b0882SAndroid Build Coastguard Worker   lua_pushinteger(state_, tc3farmhash::Hash32(input.data(), input.length()));
367*993b0882SAndroid Build Coastguard Worker   return 1;
368*993b0882SAndroid Build Coastguard Worker }
369*993b0882SAndroid Build Coastguard Worker 
HandleFormat()370*993b0882SAndroid Build Coastguard Worker int JniLuaEnvironment::HandleFormat() {
371*993b0882SAndroid Build Coastguard Worker   const int num_args = lua_gettop(state_);
372*993b0882SAndroid Build Coastguard Worker   std::vector<StringPiece> args(num_args - 1);
373*993b0882SAndroid Build Coastguard Worker   for (int i = 0; i < num_args - 1; i++) {
374*993b0882SAndroid Build Coastguard Worker     args[i] = ReadString(/*index=*/i + 2);
375*993b0882SAndroid Build Coastguard Worker   }
376*993b0882SAndroid Build Coastguard Worker   PushString(strings::Substitute(ReadString(/*index=*/1), args));
377*993b0882SAndroid Build Coastguard Worker   return 1;
378*993b0882SAndroid Build Coastguard Worker }
379*993b0882SAndroid Build Coastguard Worker 
LookupModelStringResource() const380*993b0882SAndroid Build Coastguard Worker bool JniLuaEnvironment::LookupModelStringResource() const {
381*993b0882SAndroid Build Coastguard Worker   // Handle only lookup by name.
382*993b0882SAndroid Build Coastguard Worker   if (lua_type(state_, kIndexStackTop) != LUA_TSTRING) {
383*993b0882SAndroid Build Coastguard Worker     return false;
384*993b0882SAndroid Build Coastguard Worker   }
385*993b0882SAndroid Build Coastguard Worker 
386*993b0882SAndroid Build Coastguard Worker   const StringPiece resource_name = ReadString(kIndexStackTop);
387*993b0882SAndroid Build Coastguard Worker   std::string resource_content;
388*993b0882SAndroid Build Coastguard Worker   if (!resources_.GetResourceContent(device_locales_, resource_name,
389*993b0882SAndroid Build Coastguard Worker                                      &resource_content)) {
390*993b0882SAndroid Build Coastguard Worker     // Resource cannot be provided by the model.
391*993b0882SAndroid Build Coastguard Worker     return false;
392*993b0882SAndroid Build Coastguard Worker   }
393*993b0882SAndroid Build Coastguard Worker 
394*993b0882SAndroid Build Coastguard Worker   PushString(resource_content);
395*993b0882SAndroid Build Coastguard Worker   return true;
396*993b0882SAndroid Build Coastguard Worker }
397*993b0882SAndroid Build Coastguard Worker 
HandleAndroidStringResources()398*993b0882SAndroid Build Coastguard Worker int JniLuaEnvironment::HandleAndroidStringResources() {
399*993b0882SAndroid Build Coastguard Worker   // Check whether the requested resource can be served from the model data.
400*993b0882SAndroid Build Coastguard Worker   if (LookupModelStringResource()) {
401*993b0882SAndroid Build Coastguard Worker     return 1;
402*993b0882SAndroid Build Coastguard Worker   }
403*993b0882SAndroid Build Coastguard Worker 
404*993b0882SAndroid Build Coastguard Worker   // Get system resources if not previously retrieved.
405*993b0882SAndroid Build Coastguard Worker   if (!RetrieveSystemResources()) {
406*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Error retrieving system resources.";
407*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
408*993b0882SAndroid Build Coastguard Worker     return 0;
409*993b0882SAndroid Build Coastguard Worker   }
410*993b0882SAndroid Build Coastguard Worker 
411*993b0882SAndroid Build Coastguard Worker   int resource_id;
412*993b0882SAndroid Build Coastguard Worker   switch (lua_type(state_, kIndexStackTop)) {
413*993b0882SAndroid Build Coastguard Worker     case LUA_TNUMBER:
414*993b0882SAndroid Build Coastguard Worker       resource_id = Read<int>(/*index=*/kIndexStackTop);
415*993b0882SAndroid Build Coastguard Worker       break;
416*993b0882SAndroid Build Coastguard Worker     case LUA_TSTRING: {
417*993b0882SAndroid Build Coastguard Worker       const StringPiece resource_name_str = ReadString(kIndexStackTop);
418*993b0882SAndroid Build Coastguard Worker       if (resource_name_str.empty()) {
419*993b0882SAndroid Build Coastguard Worker         TC3_LOG(ERROR) << "No resource name provided.";
420*993b0882SAndroid Build Coastguard Worker         lua_error(state_);
421*993b0882SAndroid Build Coastguard Worker         return 0;
422*993b0882SAndroid Build Coastguard Worker       }
423*993b0882SAndroid Build Coastguard Worker       const StatusOr<ScopedLocalRef<jstring>> status_or_resource_name =
424*993b0882SAndroid Build Coastguard Worker           jni_cache_->ConvertToJavaString(resource_name_str);
425*993b0882SAndroid Build Coastguard Worker       if (!status_or_resource_name.ok()) {
426*993b0882SAndroid Build Coastguard Worker         TC3_LOG(ERROR) << "Invalid resource name.";
427*993b0882SAndroid Build Coastguard Worker         lua_error(state_);
428*993b0882SAndroid Build Coastguard Worker         return 0;
429*993b0882SAndroid Build Coastguard Worker       }
430*993b0882SAndroid Build Coastguard Worker       StatusOr<int> status_or_resource_id = JniHelper::CallIntMethod(
431*993b0882SAndroid Build Coastguard Worker           jenv_, system_resources_.get(), jni_cache_->resources_get_identifier,
432*993b0882SAndroid Build Coastguard Worker           status_or_resource_name.ValueOrDie().get(), string_.get(),
433*993b0882SAndroid Build Coastguard Worker           android_.get());
434*993b0882SAndroid Build Coastguard Worker       if (!status_or_resource_id.ok()) {
435*993b0882SAndroid Build Coastguard Worker         TC3_LOG(ERROR) << "Error calling getIdentifier.";
436*993b0882SAndroid Build Coastguard Worker         lua_error(state_);
437*993b0882SAndroid Build Coastguard Worker         return 0;
438*993b0882SAndroid Build Coastguard Worker       }
439*993b0882SAndroid Build Coastguard Worker       resource_id = status_or_resource_id.ValueOrDie();
440*993b0882SAndroid Build Coastguard Worker       break;
441*993b0882SAndroid Build Coastguard Worker     }
442*993b0882SAndroid Build Coastguard Worker     default:
443*993b0882SAndroid Build Coastguard Worker       TC3_LOG(ERROR) << "Unexpected type for resource lookup.";
444*993b0882SAndroid Build Coastguard Worker       lua_error(state_);
445*993b0882SAndroid Build Coastguard Worker       return 0;
446*993b0882SAndroid Build Coastguard Worker   }
447*993b0882SAndroid Build Coastguard Worker   if (resource_id == 0) {
448*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Resource not found.";
449*993b0882SAndroid Build Coastguard Worker     lua_pushnil(state_);
450*993b0882SAndroid Build Coastguard Worker     return 1;
451*993b0882SAndroid Build Coastguard Worker   }
452*993b0882SAndroid Build Coastguard Worker   StatusOr<ScopedLocalRef<jstring>> status_or_resource_str =
453*993b0882SAndroid Build Coastguard Worker       JniHelper::CallObjectMethod<jstring>(jenv_, system_resources_.get(),
454*993b0882SAndroid Build Coastguard Worker                                            jni_cache_->resources_get_string,
455*993b0882SAndroid Build Coastguard Worker                                            resource_id);
456*993b0882SAndroid Build Coastguard Worker   if (!status_or_resource_str.ok()) {
457*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Error calling getString.";
458*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
459*993b0882SAndroid Build Coastguard Worker     return 0;
460*993b0882SAndroid Build Coastguard Worker   }
461*993b0882SAndroid Build Coastguard Worker 
462*993b0882SAndroid Build Coastguard Worker   if (status_or_resource_str.ValueOrDie() == nullptr) {
463*993b0882SAndroid Build Coastguard Worker     lua_pushnil(state_);
464*993b0882SAndroid Build Coastguard Worker   } else {
465*993b0882SAndroid Build Coastguard Worker     StatusOr<std::string> status_or_resource_std_str =
466*993b0882SAndroid Build Coastguard Worker         JStringToUtf8String(jenv_, status_or_resource_str.ValueOrDie().get());
467*993b0882SAndroid Build Coastguard Worker     if (!status_or_resource_std_str.ok()) {
468*993b0882SAndroid Build Coastguard Worker       lua_error(state_);
469*993b0882SAndroid Build Coastguard Worker       return 0;
470*993b0882SAndroid Build Coastguard Worker     }
471*993b0882SAndroid Build Coastguard Worker     PushString(status_or_resource_std_str.ValueOrDie());
472*993b0882SAndroid Build Coastguard Worker   }
473*993b0882SAndroid Build Coastguard Worker   return 1;
474*993b0882SAndroid Build Coastguard Worker }
475*993b0882SAndroid Build Coastguard Worker 
RetrieveSystemResources()476*993b0882SAndroid Build Coastguard Worker bool JniLuaEnvironment::RetrieveSystemResources() {
477*993b0882SAndroid Build Coastguard Worker   if (system_resources_resources_retrieved_) {
478*993b0882SAndroid Build Coastguard Worker     return (system_resources_ != nullptr);
479*993b0882SAndroid Build Coastguard Worker   }
480*993b0882SAndroid Build Coastguard Worker   system_resources_resources_retrieved_ = true;
481*993b0882SAndroid Build Coastguard Worker   TC3_ASSIGN_OR_RETURN_FALSE(ScopedLocalRef<jobject> system_resources_ref,
482*993b0882SAndroid Build Coastguard Worker                              JniHelper::CallStaticObjectMethod(
483*993b0882SAndroid Build Coastguard Worker                                  jenv_, jni_cache_->resources_class.get(),
484*993b0882SAndroid Build Coastguard Worker                                  jni_cache_->resources_get_system));
485*993b0882SAndroid Build Coastguard Worker   system_resources_ =
486*993b0882SAndroid Build Coastguard Worker       MakeGlobalRef(system_resources_ref.get(), jenv_, jni_cache_->jvm);
487*993b0882SAndroid Build Coastguard Worker   return (system_resources_ != nullptr);
488*993b0882SAndroid Build Coastguard Worker }
489*993b0882SAndroid Build Coastguard Worker 
RetrieveUserManager()490*993b0882SAndroid Build Coastguard Worker bool JniLuaEnvironment::RetrieveUserManager() {
491*993b0882SAndroid Build Coastguard Worker   if (context_ == nullptr) {
492*993b0882SAndroid Build Coastguard Worker     return false;
493*993b0882SAndroid Build Coastguard Worker   }
494*993b0882SAndroid Build Coastguard Worker   if (usermanager_retrieved_) {
495*993b0882SAndroid Build Coastguard Worker     return (usermanager_ != nullptr);
496*993b0882SAndroid Build Coastguard Worker   }
497*993b0882SAndroid Build Coastguard Worker   usermanager_retrieved_ = true;
498*993b0882SAndroid Build Coastguard Worker   TC3_ASSIGN_OR_RETURN_FALSE(const ScopedLocalRef<jstring> service,
499*993b0882SAndroid Build Coastguard Worker                              JniHelper::NewStringUTF(jenv_, "user"));
500*993b0882SAndroid Build Coastguard Worker   TC3_ASSIGN_OR_RETURN_FALSE(
501*993b0882SAndroid Build Coastguard Worker       const ScopedLocalRef<jobject> usermanager_ref,
502*993b0882SAndroid Build Coastguard Worker       JniHelper::CallObjectMethod(jenv_, context_,
503*993b0882SAndroid Build Coastguard Worker                                   jni_cache_->context_get_system_service,
504*993b0882SAndroid Build Coastguard Worker                                   service.get()));
505*993b0882SAndroid Build Coastguard Worker 
506*993b0882SAndroid Build Coastguard Worker   usermanager_ = MakeGlobalRef(usermanager_ref.get(), jenv_, jni_cache_->jvm);
507*993b0882SAndroid Build Coastguard Worker   return (usermanager_ != nullptr);
508*993b0882SAndroid Build Coastguard Worker }
509*993b0882SAndroid Build Coastguard Worker 
ReadRemoteActionTemplateResult() const510*993b0882SAndroid Build Coastguard Worker RemoteActionTemplate JniLuaEnvironment::ReadRemoteActionTemplateResult() const {
511*993b0882SAndroid Build Coastguard Worker   RemoteActionTemplate result;
512*993b0882SAndroid Build Coastguard Worker   // Read intent template.
513*993b0882SAndroid Build Coastguard Worker   lua_pushnil(state_);
514*993b0882SAndroid Build Coastguard Worker   while (Next(/*index=*/-2)) {
515*993b0882SAndroid Build Coastguard Worker     const StringPiece key = ReadString(/*index=*/-2);
516*993b0882SAndroid Build Coastguard Worker     if (key.Equals("title_without_entity")) {
517*993b0882SAndroid Build Coastguard Worker       result.title_without_entity = Read<std::string>(/*index=*/kIndexStackTop);
518*993b0882SAndroid Build Coastguard Worker     } else if (key.Equals("title_with_entity")) {
519*993b0882SAndroid Build Coastguard Worker       result.title_with_entity = Read<std::string>(/*index=*/kIndexStackTop);
520*993b0882SAndroid Build Coastguard Worker     } else if (key.Equals("description")) {
521*993b0882SAndroid Build Coastguard Worker       result.description = Read<std::string>(/*index=*/kIndexStackTop);
522*993b0882SAndroid Build Coastguard Worker     } else if (key.Equals("description_with_app_name")) {
523*993b0882SAndroid Build Coastguard Worker       result.description_with_app_name =
524*993b0882SAndroid Build Coastguard Worker           Read<std::string>(/*index=*/kIndexStackTop);
525*993b0882SAndroid Build Coastguard Worker     } else if (key.Equals("action")) {
526*993b0882SAndroid Build Coastguard Worker       result.action = Read<std::string>(/*index=*/kIndexStackTop);
527*993b0882SAndroid Build Coastguard Worker     } else if (key.Equals("data")) {
528*993b0882SAndroid Build Coastguard Worker       result.data = Read<std::string>(/*index=*/kIndexStackTop);
529*993b0882SAndroid Build Coastguard Worker     } else if (key.Equals("type")) {
530*993b0882SAndroid Build Coastguard Worker       result.type = Read<std::string>(/*index=*/kIndexStackTop);
531*993b0882SAndroid Build Coastguard Worker     } else if (key.Equals("flags")) {
532*993b0882SAndroid Build Coastguard Worker       result.flags = Read<int>(/*index=*/kIndexStackTop);
533*993b0882SAndroid Build Coastguard Worker     } else if (key.Equals("package_name")) {
534*993b0882SAndroid Build Coastguard Worker       result.package_name = Read<std::string>(/*index=*/kIndexStackTop);
535*993b0882SAndroid Build Coastguard Worker     } else if (key.Equals("request_code")) {
536*993b0882SAndroid Build Coastguard Worker       result.request_code = Read<int>(/*index=*/kIndexStackTop);
537*993b0882SAndroid Build Coastguard Worker     } else if (key.Equals("category")) {
538*993b0882SAndroid Build Coastguard Worker       result.category = ReadVector<std::string>(/*index=*/kIndexStackTop);
539*993b0882SAndroid Build Coastguard Worker     } else if (key.Equals("extra")) {
540*993b0882SAndroid Build Coastguard Worker       result.extra = ReadExtras();
541*993b0882SAndroid Build Coastguard Worker     } else {
542*993b0882SAndroid Build Coastguard Worker       TC3_LOG(INFO) << "Unknown entry: " << key;
543*993b0882SAndroid Build Coastguard Worker     }
544*993b0882SAndroid Build Coastguard Worker     lua_pop(state_, 1);
545*993b0882SAndroid Build Coastguard Worker   }
546*993b0882SAndroid Build Coastguard Worker   lua_pop(state_, 1);
547*993b0882SAndroid Build Coastguard Worker   return result;
548*993b0882SAndroid Build Coastguard Worker }
549*993b0882SAndroid Build Coastguard Worker 
ReadExtras() const550*993b0882SAndroid Build Coastguard Worker std::map<std::string, Variant> JniLuaEnvironment::ReadExtras() const {
551*993b0882SAndroid Build Coastguard Worker   if (lua_type(state_, kIndexStackTop) != LUA_TTABLE) {
552*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Expected extras table, got: "
553*993b0882SAndroid Build Coastguard Worker                    << lua_type(state_, kIndexStackTop);
554*993b0882SAndroid Build Coastguard Worker     lua_pop(state_, 1);
555*993b0882SAndroid Build Coastguard Worker     return {};
556*993b0882SAndroid Build Coastguard Worker   }
557*993b0882SAndroid Build Coastguard Worker   std::map<std::string, Variant> extras;
558*993b0882SAndroid Build Coastguard Worker   lua_pushnil(state_);
559*993b0882SAndroid Build Coastguard Worker   while (Next(/*index=*/-2)) {
560*993b0882SAndroid Build Coastguard Worker     // Each entry is a table specifying name and value.
561*993b0882SAndroid Build Coastguard Worker     // The value is specified via a type specific field as Lua doesn't allow
562*993b0882SAndroid Build Coastguard Worker     // to easily distinguish between different number types.
563*993b0882SAndroid Build Coastguard Worker     if (lua_type(state_, kIndexStackTop) != LUA_TTABLE) {
564*993b0882SAndroid Build Coastguard Worker       TC3_LOG(ERROR) << "Expected a table for an extra, got: "
565*993b0882SAndroid Build Coastguard Worker                      << lua_type(state_, kIndexStackTop);
566*993b0882SAndroid Build Coastguard Worker       lua_pop(state_, 1);
567*993b0882SAndroid Build Coastguard Worker       return {};
568*993b0882SAndroid Build Coastguard Worker     }
569*993b0882SAndroid Build Coastguard Worker     std::string name;
570*993b0882SAndroid Build Coastguard Worker     Variant value;
571*993b0882SAndroid Build Coastguard Worker 
572*993b0882SAndroid Build Coastguard Worker     lua_pushnil(state_);
573*993b0882SAndroid Build Coastguard Worker     while (Next(/*index=*/-2)) {
574*993b0882SAndroid Build Coastguard Worker       const StringPiece key = ReadString(/*index=*/-2);
575*993b0882SAndroid Build Coastguard Worker       if (key.Equals("name")) {
576*993b0882SAndroid Build Coastguard Worker         name = Read<std::string>(/*index=*/kIndexStackTop);
577*993b0882SAndroid Build Coastguard Worker       } else if (key.Equals("int_value")) {
578*993b0882SAndroid Build Coastguard Worker         value = Variant(Read<int>(/*index=*/kIndexStackTop));
579*993b0882SAndroid Build Coastguard Worker       } else if (key.Equals("long_value")) {
580*993b0882SAndroid Build Coastguard Worker         value = Variant(Read<int64>(/*index=*/kIndexStackTop));
581*993b0882SAndroid Build Coastguard Worker       } else if (key.Equals("float_value")) {
582*993b0882SAndroid Build Coastguard Worker         value = Variant(Read<float>(/*index=*/kIndexStackTop));
583*993b0882SAndroid Build Coastguard Worker       } else if (key.Equals("bool_value")) {
584*993b0882SAndroid Build Coastguard Worker         value = Variant(Read<bool>(/*index=*/kIndexStackTop));
585*993b0882SAndroid Build Coastguard Worker       } else if (key.Equals("string_value")) {
586*993b0882SAndroid Build Coastguard Worker         value = Variant(Read<std::string>(/*index=*/kIndexStackTop));
587*993b0882SAndroid Build Coastguard Worker       } else if (key.Equals("string_array_value")) {
588*993b0882SAndroid Build Coastguard Worker         value = Variant(ReadVector<std::string>(/*index=*/kIndexStackTop));
589*993b0882SAndroid Build Coastguard Worker       } else if (key.Equals("float_array_value")) {
590*993b0882SAndroid Build Coastguard Worker         value = Variant(ReadVector<float>(/*index=*/kIndexStackTop));
591*993b0882SAndroid Build Coastguard Worker       } else if (key.Equals("int_array_value")) {
592*993b0882SAndroid Build Coastguard Worker         value = Variant(ReadVector<int>(/*index=*/kIndexStackTop));
593*993b0882SAndroid Build Coastguard Worker       } else if (key.Equals("named_variant_array_value")) {
594*993b0882SAndroid Build Coastguard Worker         value = Variant(ReadExtras());
595*993b0882SAndroid Build Coastguard Worker       } else {
596*993b0882SAndroid Build Coastguard Worker         TC3_LOG(INFO) << "Unknown extra field: " << key;
597*993b0882SAndroid Build Coastguard Worker       }
598*993b0882SAndroid Build Coastguard Worker       lua_pop(state_, 1);
599*993b0882SAndroid Build Coastguard Worker     }
600*993b0882SAndroid Build Coastguard Worker     if (!name.empty()) {
601*993b0882SAndroid Build Coastguard Worker       extras[name] = value;
602*993b0882SAndroid Build Coastguard Worker     } else {
603*993b0882SAndroid Build Coastguard Worker       TC3_LOG(ERROR) << "Unnamed extra entry. Skipping.";
604*993b0882SAndroid Build Coastguard Worker     }
605*993b0882SAndroid Build Coastguard Worker     lua_pop(state_, 1);
606*993b0882SAndroid Build Coastguard Worker   }
607*993b0882SAndroid Build Coastguard Worker   return extras;
608*993b0882SAndroid Build Coastguard Worker }
609*993b0882SAndroid Build Coastguard Worker 
ReadRemoteActionTemplates(std::vector<RemoteActionTemplate> * result)610*993b0882SAndroid Build Coastguard Worker int JniLuaEnvironment::ReadRemoteActionTemplates(
611*993b0882SAndroid Build Coastguard Worker     std::vector<RemoteActionTemplate>* result) {
612*993b0882SAndroid Build Coastguard Worker   // Read result.
613*993b0882SAndroid Build Coastguard Worker   if (lua_type(state_, kIndexStackTop) != LUA_TTABLE) {
614*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Unexpected result for snippet: "
615*993b0882SAndroid Build Coastguard Worker                    << lua_type(state_, kIndexStackTop);
616*993b0882SAndroid Build Coastguard Worker     lua_error(state_);
617*993b0882SAndroid Build Coastguard Worker     return LUA_ERRRUN;
618*993b0882SAndroid Build Coastguard Worker   }
619*993b0882SAndroid Build Coastguard Worker 
620*993b0882SAndroid Build Coastguard Worker   // Read remote action templates array.
621*993b0882SAndroid Build Coastguard Worker   lua_pushnil(state_);
622*993b0882SAndroid Build Coastguard Worker   while (Next(/*index=*/-2)) {
623*993b0882SAndroid Build Coastguard Worker     if (lua_type(state_, kIndexStackTop) != LUA_TTABLE) {
624*993b0882SAndroid Build Coastguard Worker       TC3_LOG(ERROR) << "Expected intent table, got: "
625*993b0882SAndroid Build Coastguard Worker                      << lua_type(state_, kIndexStackTop);
626*993b0882SAndroid Build Coastguard Worker       lua_pop(state_, 1);
627*993b0882SAndroid Build Coastguard Worker       continue;
628*993b0882SAndroid Build Coastguard Worker     }
629*993b0882SAndroid Build Coastguard Worker     result->push_back(ReadRemoteActionTemplateResult());
630*993b0882SAndroid Build Coastguard Worker   }
631*993b0882SAndroid Build Coastguard Worker   lua_pop(state_, /*n=*/1);
632*993b0882SAndroid Build Coastguard Worker   return LUA_OK;
633*993b0882SAndroid Build Coastguard Worker }
634*993b0882SAndroid Build Coastguard Worker 
RunIntentGenerator(const std::string & generator_snippet,std::vector<RemoteActionTemplate> * remote_actions)635*993b0882SAndroid Build Coastguard Worker bool JniLuaEnvironment::RunIntentGenerator(
636*993b0882SAndroid Build Coastguard Worker     const std::string& generator_snippet,
637*993b0882SAndroid Build Coastguard Worker     std::vector<RemoteActionTemplate>* remote_actions) {
638*993b0882SAndroid Build Coastguard Worker   int status;
639*993b0882SAndroid Build Coastguard Worker   status = luaL_loadbuffer(state_, generator_snippet.data(),
640*993b0882SAndroid Build Coastguard Worker                            generator_snippet.size(),
641*993b0882SAndroid Build Coastguard Worker                            /*name=*/nullptr);
642*993b0882SAndroid Build Coastguard Worker   if (status != LUA_OK) {
643*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Couldn't load generator snippet: " << status;
644*993b0882SAndroid Build Coastguard Worker     return false;
645*993b0882SAndroid Build Coastguard Worker   }
646*993b0882SAndroid Build Coastguard Worker   status = lua_pcall(state_, /*nargs=*/0, /*nresults=*/1, /*errfunc=*/0);
647*993b0882SAndroid Build Coastguard Worker   if (status != LUA_OK) {
648*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Couldn't run generator snippet: " << status;
649*993b0882SAndroid Build Coastguard Worker     return false;
650*993b0882SAndroid Build Coastguard Worker   }
651*993b0882SAndroid Build Coastguard Worker   if (RunProtected(
652*993b0882SAndroid Build Coastguard Worker           [this, remote_actions] {
653*993b0882SAndroid Build Coastguard Worker             return ReadRemoteActionTemplates(remote_actions);
654*993b0882SAndroid Build Coastguard Worker           },
655*993b0882SAndroid Build Coastguard Worker           /*num_args=*/1) != LUA_OK) {
656*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Could not read results.";
657*993b0882SAndroid Build Coastguard Worker     return false;
658*993b0882SAndroid Build Coastguard Worker   }
659*993b0882SAndroid Build Coastguard Worker   // Check that we correctly cleaned-up the state.
660*993b0882SAndroid Build Coastguard Worker   const int stack_size = lua_gettop(state_);
661*993b0882SAndroid Build Coastguard Worker   if (stack_size > 0) {
662*993b0882SAndroid Build Coastguard Worker     TC3_LOG(ERROR) << "Unexpected stack size.";
663*993b0882SAndroid Build Coastguard Worker     lua_settop(state_, 0);
664*993b0882SAndroid Build Coastguard Worker     return false;
665*993b0882SAndroid Build Coastguard Worker   }
666*993b0882SAndroid Build Coastguard Worker   return true;
667*993b0882SAndroid Build Coastguard Worker }
668*993b0882SAndroid Build Coastguard Worker 
669*993b0882SAndroid Build Coastguard Worker }  // namespace libtextclassifier3
670