1 /*
2  * Copyright (C) 2018 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 // JNI wrapper for actions.
18 
19 #include "actions/actions_jni.h"
20 
21 #include <jni.h>
22 
23 #include <map>
24 #include <type_traits>
25 #include <vector>
26 
27 #include "actions/actions-suggestions.h"
28 #include "annotator/annotator.h"
29 #include "annotator/annotator_jni_common.h"
30 #include "utils/base/integral_types.h"
31 #include "utils/base/status_macros.h"
32 #include "utils/base/statusor.h"
33 #include "utils/intents/intent-generator.h"
34 #include "utils/intents/jni.h"
35 #include "utils/intents/remote-action-template.h"
36 #include "utils/java/jni-base.h"
37 #include "utils/java/jni-cache.h"
38 #include "utils/java/jni-helper.h"
39 #include "utils/memory/mmap.h"
40 
41 using libtextclassifier3::ActionsSuggestions;
42 using libtextclassifier3::ActionsSuggestionsResponse;
43 using libtextclassifier3::ActionSuggestionOptions;
44 using libtextclassifier3::Annotator;
45 using libtextclassifier3::Conversation;
46 using libtextclassifier3::IntentGenerator;
47 using libtextclassifier3::JStringToUtf8String;
48 using libtextclassifier3::ScopedLocalRef;
49 using libtextclassifier3::StatusOr;
50 
51 // When using the Java's ICU, UniLib needs to be instantiated with a JavaVM
52 // pointer from JNI. When using a standard ICU the pointer is not needed and the
53 // objects are instantiated implicitly.
54 #ifdef TC3_UNILIB_JAVAICU
55 using libtextclassifier3::UniLib;
56 #endif
57 
58 namespace libtextclassifier3 {
59 
60 namespace {
61 
62 // Cached state for model inference.
63 // Keeps a jni cache, intent generator and model instance so that they don't
64 // have to be recreated for each call.
65 class ActionsSuggestionsJniContext {
66  public:
Create(const std::shared_ptr<libtextclassifier3::JniCache> & jni_cache,std::unique_ptr<ActionsSuggestions> model)67   static ActionsSuggestionsJniContext* Create(
68       const std::shared_ptr<libtextclassifier3::JniCache>& jni_cache,
69       std::unique_ptr<ActionsSuggestions> model) {
70     if (jni_cache == nullptr || model == nullptr) {
71       return nullptr;
72     }
73     std::unique_ptr<IntentGenerator> intent_generator =
74         IntentGenerator::Create(model->model()->android_intent_options(),
75                                 model->model()->resources(), jni_cache);
76     if (intent_generator == nullptr) {
77       return nullptr;
78     }
79 
80     TC3_ASSIGN_OR_RETURN_NULL(
81         std::unique_ptr<RemoteActionTemplatesHandler> template_handler,
82         libtextclassifier3::RemoteActionTemplatesHandler::Create(jni_cache));
83 
84     return new ActionsSuggestionsJniContext(jni_cache, std::move(model),
85                                             std::move(intent_generator),
86                                             std::move(template_handler));
87   }
88 
jni_cache() const89   std::shared_ptr<libtextclassifier3::JniCache> jni_cache() const {
90     return jni_cache_;
91   }
92 
model() const93   ActionsSuggestions* model() const { return model_.get(); }
94 
intent_generator() const95   IntentGenerator* intent_generator() const { return intent_generator_.get(); }
96 
template_handler() const97   RemoteActionTemplatesHandler* template_handler() const {
98     return template_handler_.get();
99   }
100 
101  private:
ActionsSuggestionsJniContext(const std::shared_ptr<libtextclassifier3::JniCache> & jni_cache,std::unique_ptr<ActionsSuggestions> model,std::unique_ptr<IntentGenerator> intent_generator,std::unique_ptr<RemoteActionTemplatesHandler> template_handler)102   ActionsSuggestionsJniContext(
103       const std::shared_ptr<libtextclassifier3::JniCache>& jni_cache,
104       std::unique_ptr<ActionsSuggestions> model,
105       std::unique_ptr<IntentGenerator> intent_generator,
106       std::unique_ptr<RemoteActionTemplatesHandler> template_handler)
107       : jni_cache_(jni_cache),
108         model_(std::move(model)),
109         intent_generator_(std::move(intent_generator)),
110         template_handler_(std::move(template_handler)) {}
111 
112   std::shared_ptr<libtextclassifier3::JniCache> jni_cache_;
113   std::unique_ptr<ActionsSuggestions> model_;
114   std::unique_ptr<IntentGenerator> intent_generator_;
115   std::unique_ptr<RemoteActionTemplatesHandler> template_handler_;
116 };
117 
FromJavaActionSuggestionOptions(JNIEnv * env,jobject joptions)118 ActionSuggestionOptions FromJavaActionSuggestionOptions(JNIEnv* env,
119                                                         jobject joptions) {
120   ActionSuggestionOptions options = ActionSuggestionOptions::Default();
121   return options;
122 }
123 
ActionSuggestionsToJObject(JNIEnv * env,const ActionsSuggestionsJniContext * context,jobject app_context,const reflection::Schema * annotations_entity_data_schema,const ActionsSuggestionsResponse & action_response,const Conversation & conversation,const jstring device_locales,const bool generate_intents)124 StatusOr<ScopedLocalRef<jobject>> ActionSuggestionsToJObject(
125     JNIEnv* env, const ActionsSuggestionsJniContext* context,
126     jobject app_context,
127     const reflection::Schema* annotations_entity_data_schema,
128     const ActionsSuggestionsResponse& action_response,
129     const Conversation& conversation, const jstring device_locales,
130     const bool generate_intents) {
131   // Find the class ActionSuggestion.
132   auto status_or_action_class = JniHelper::FindClass(
133       env, TC3_PACKAGE_PATH TC3_ACTIONS_CLASS_NAME_STR "$ActionSuggestion");
134   if (!status_or_action_class.ok()) {
135     TC3_LOG(ERROR) << "Couldn't find ActionSuggestion class.";
136     return status_or_action_class.status();
137   }
138   ScopedLocalRef<jclass> action_class =
139       std::move(status_or_action_class.ValueOrDie());
140 
141   // Find the class ActionSuggestions
142   auto status_or_result_class = JniHelper::FindClass(
143       env, TC3_PACKAGE_PATH TC3_ACTIONS_CLASS_NAME_STR "$ActionSuggestions");
144   if (!status_or_result_class.ok()) {
145     TC3_LOG(ERROR) << "Couldn't find ActionSuggestions class.";
146     return status_or_result_class.status();
147   }
148   ScopedLocalRef<jclass> result_class =
149       std::move(status_or_result_class.ValueOrDie());
150 
151   // Find the class Slot.
152   auto status_or_slot_class = JniHelper::FindClass(
153       env, TC3_PACKAGE_PATH TC3_ACTIONS_CLASS_NAME_STR "$Slot");
154   if (!status_or_slot_class.ok()) {
155     TC3_LOG(ERROR) << "Couldn't find Slot class.";
156     return status_or_slot_class.status();
157   }
158   ScopedLocalRef<jclass> slot_class =
159       std::move(status_or_slot_class.ValueOrDie());
160 
161   TC3_ASSIGN_OR_RETURN(
162       const jmethodID action_class_constructor,
163       JniHelper::GetMethodID(
164           env, action_class.get(), "<init>",
165           "(Ljava/lang/String;Ljava/lang/String;F[L" TC3_PACKAGE_PATH
166               TC3_NAMED_VARIANT_CLASS_NAME_STR
167           ";[B[L" TC3_PACKAGE_PATH TC3_REMOTE_ACTION_TEMPLATE_CLASS_NAME_STR
168           ";[L" TC3_PACKAGE_PATH TC3_ACTIONS_CLASS_NAME_STR "$Slot;)V"));
169   TC3_ASSIGN_OR_RETURN(const jmethodID slot_class_constructor,
170                        JniHelper::GetMethodID(env, slot_class.get(), "<init>",
171                                               "(Ljava/lang/String;IIIF)V"));
172   TC3_ASSIGN_OR_RETURN(
173       ScopedLocalRef<jobjectArray> actions,
174       JniHelper::NewObjectArray(env, action_response.actions.size(),
175                                 action_class.get(), nullptr));
176   for (int i = 0; i < action_response.actions.size(); i++) {
177     ScopedLocalRef<jobjectArray> extras;
178     const reflection::Schema* actions_entity_data_schema =
179         context->model()->entity_data_schema();
180     if (actions_entity_data_schema != nullptr &&
181         !action_response.actions[i].serialized_entity_data.empty()) {
182       TC3_ASSIGN_OR_RETURN(
183           extras, context->template_handler()->EntityDataAsNamedVariantArray(
184                       actions_entity_data_schema,
185                       action_response.actions[i].serialized_entity_data));
186     }
187 
188     ScopedLocalRef<jbyteArray> serialized_entity_data;
189     if (!action_response.actions[i].serialized_entity_data.empty()) {
190       TC3_ASSIGN_OR_RETURN(
191           serialized_entity_data,
192           JniHelper::NewByteArray(
193               env, action_response.actions[i].serialized_entity_data.size()));
194       TC3_RETURN_IF_ERROR(JniHelper::SetByteArrayRegion(
195           env, serialized_entity_data.get(), 0,
196           action_response.actions[i].serialized_entity_data.size(),
197           reinterpret_cast<const jbyte*>(
198               action_response.actions[i].serialized_entity_data.data())));
199     }
200 
201     ScopedLocalRef<jobjectArray> remote_action_templates_result;
202     if (generate_intents) {
203       std::vector<RemoteActionTemplate> remote_action_templates;
204       if (context->intent_generator()->GenerateIntents(
205               device_locales, action_response.actions[i], conversation,
206               app_context,
207               /*annotations_entity_data_schema=*/annotations_entity_data_schema,
208               /*actions_entity_data_schema=*/actions_entity_data_schema,
209               &remote_action_templates)) {
210         TC3_ASSIGN_OR_RETURN(
211             remote_action_templates_result,
212             context->template_handler()->RemoteActionTemplatesToJObjectArray(
213                 remote_action_templates));
214       }
215     }
216 
217     TC3_ASSIGN_OR_RETURN(ScopedLocalRef<jstring> reply,
218                          context->jni_cache()->ConvertToJavaString(
219                              action_response.actions[i].response_text));
220 
221     TC3_ASSIGN_OR_RETURN(
222         ScopedLocalRef<jstring> action_type,
223         JniHelper::NewStringUTF(env, action_response.actions[i].type.c_str()));
224 
225     ScopedLocalRef<jobjectArray> slots;
226     if (!action_response.actions[i].slots.empty()) {
227       TC3_ASSIGN_OR_RETURN(slots,
228                            JniHelper::NewObjectArray(
229                                env, action_response.actions[i].slots.size(),
230                                slot_class.get(), nullptr));
231       for (int j = 0; j < action_response.actions[i].slots.size(); j++) {
232         const Slot& slot_c = action_response.actions[i].slots[j];
233         TC3_ASSIGN_OR_RETURN(ScopedLocalRef<jstring> slot_type,
234                              JniHelper::NewStringUTF(env, slot_c.type.c_str()));
235 
236         TC3_ASSIGN_OR_RETURN(
237             ScopedLocalRef<jobject> slot,
238             JniHelper::NewObject(
239                 env, slot_class.get(), slot_class_constructor, slot_type.get(),
240                 slot_c.span.message_index, slot_c.span.span.first,
241                 slot_c.span.span.second, slot_c.confidence_score));
242 
243         TC3_RETURN_IF_ERROR(
244             JniHelper::SetObjectArrayElement(env, slots.get(), j, slot.get()));
245       }
246     }
247 
248     TC3_ASSIGN_OR_RETURN(
249         ScopedLocalRef<jobject> action,
250         JniHelper::NewObject(
251             env, action_class.get(), action_class_constructor, reply.get(),
252             action_type.get(),
253             static_cast<jfloat>(action_response.actions[i].score), extras.get(),
254             serialized_entity_data.get(), remote_action_templates_result.get(),
255             slots.get()));
256     TC3_RETURN_IF_ERROR(
257         JniHelper::SetObjectArrayElement(env, actions.get(), i, action.get()));
258   }
259 
260   // Create the ActionSuggestions object.
261   TC3_ASSIGN_OR_RETURN(
262       const jmethodID result_class_constructor,
263       JniHelper::GetMethodID(env, result_class.get(), "<init>",
264                              "([L" TC3_PACKAGE_PATH TC3_ACTIONS_CLASS_NAME_STR
265                              "$ActionSuggestion;Z)V"));
266   TC3_ASSIGN_OR_RETURN(
267       ScopedLocalRef<jobject> result,
268       JniHelper::NewObject(env, result_class.get(), result_class_constructor,
269                            actions.get(), action_response.is_sensitive));
270   return result;
271 }
272 
FromJavaConversationMessage(JNIEnv * env,jobject jmessage)273 StatusOr<ConversationMessage> FromJavaConversationMessage(JNIEnv* env,
274                                                           jobject jmessage) {
275   if (!jmessage) {
276     return {};
277   }
278 
279   TC3_ASSIGN_OR_RETURN(
280       ScopedLocalRef<jclass> message_class,
281       JniHelper::FindClass(env, TC3_PACKAGE_PATH TC3_ACTIONS_CLASS_NAME_STR
282                            "$ConversationMessage"));
283   // .getText()
284   TC3_ASSIGN_OR_RETURN(
285       jmethodID get_text_method,
286       JniHelper::GetMethodID(env, message_class.get(), "getText",
287                              "()Ljava/lang/String;"));
288   TC3_ASSIGN_OR_RETURN(
289       ScopedLocalRef<jstring> text,
290       JniHelper::CallObjectMethod<jstring>(env, jmessage, get_text_method));
291 
292   // .getUserId()
293   TC3_ASSIGN_OR_RETURN(
294       jmethodID get_user_id_method,
295       JniHelper::GetMethodID(env, message_class.get(), "getUserId", "()I"));
296   TC3_ASSIGN_OR_RETURN(int32 user_id, JniHelper::CallIntMethod(
297                                           env, jmessage, get_user_id_method));
298 
299   // .getReferenceTimeMsUtc()
300   TC3_ASSIGN_OR_RETURN(jmethodID get_reference_time_method,
301                        JniHelper::GetMethodID(env, message_class.get(),
302                                               "getReferenceTimeMsUtc", "()J"));
303   TC3_ASSIGN_OR_RETURN(
304       int64 reference_time,
305       JniHelper::CallLongMethod(env, jmessage, get_reference_time_method));
306 
307   // .getReferenceTimezone()
308   TC3_ASSIGN_OR_RETURN(
309       jmethodID get_reference_timezone_method,
310       JniHelper::GetMethodID(env, message_class.get(), "getReferenceTimezone",
311                              "()Ljava/lang/String;"));
312   TC3_ASSIGN_OR_RETURN(ScopedLocalRef<jstring> reference_timezone,
313                        JniHelper::CallObjectMethod<jstring>(
314                            env, jmessage, get_reference_timezone_method));
315 
316   // .getDetectedTextLanguageTags()
317   TC3_ASSIGN_OR_RETURN(jmethodID get_detected_text_language_tags_method,
318                        JniHelper::GetMethodID(env, message_class.get(),
319                                               "getDetectedTextLanguageTags",
320                                               "()Ljava/lang/String;"));
321   TC3_ASSIGN_OR_RETURN(
322       ScopedLocalRef<jstring> detected_text_language_tags,
323       JniHelper::CallObjectMethod<jstring>(
324           env, jmessage, get_detected_text_language_tags_method));
325 
326   ConversationMessage message;
327   TC3_ASSIGN_OR_RETURN(message.text, JStringToUtf8String(env, text.get()));
328   message.user_id = user_id;
329   message.reference_time_ms_utc = reference_time;
330   TC3_ASSIGN_OR_RETURN(message.reference_timezone,
331                        JStringToUtf8String(env, reference_timezone.get()));
332   TC3_ASSIGN_OR_RETURN(
333       message.detected_text_language_tags,
334       JStringToUtf8String(env, detected_text_language_tags.get()));
335   return message;
336 }
337 
FromJavaConversation(JNIEnv * env,jobject jconversation)338 StatusOr<Conversation> FromJavaConversation(JNIEnv* env,
339                                             jobject jconversation) {
340   if (!jconversation) {
341     return {Status::UNKNOWN};
342   }
343 
344   TC3_ASSIGN_OR_RETURN(
345       ScopedLocalRef<jclass> conversation_class,
346       JniHelper::FindClass(
347           env, TC3_PACKAGE_PATH TC3_ACTIONS_CLASS_NAME_STR "$Conversation"));
348 
349   TC3_ASSIGN_OR_RETURN(
350       jmethodID get_conversation_messages_method,
351       JniHelper::GetMethodID(env, conversation_class.get(),
352                              "getConversationMessages",
353                              "()[L" TC3_PACKAGE_PATH TC3_ACTIONS_CLASS_NAME_STR
354                              "$ConversationMessage;"));
355   TC3_ASSIGN_OR_RETURN(
356       ScopedLocalRef<jobjectArray> jmessages,
357       JniHelper::CallObjectMethod<jobjectArray>(
358           env, jconversation, get_conversation_messages_method));
359 
360   std::vector<ConversationMessage> messages;
361   TC3_ASSIGN_OR_RETURN(const int size,
362                        JniHelper::GetArrayLength(env, jmessages.get()));
363   for (int i = 0; i < size; i++) {
364     TC3_ASSIGN_OR_RETURN(
365         ScopedLocalRef<jobject> jmessage,
366         JniHelper::GetObjectArrayElement<jobject>(env, jmessages.get(), i));
367     TC3_ASSIGN_OR_RETURN(ConversationMessage message,
368                          FromJavaConversationMessage(env, jmessage.get()));
369     messages.push_back(message);
370   }
371   Conversation conversation;
372   conversation.messages = messages;
373   return conversation;
374 }
375 
GetLocalesFromMmap(JNIEnv * env,libtextclassifier3::ScopedMmap * mmap)376 StatusOr<ScopedLocalRef<jstring>> GetLocalesFromMmap(
377     JNIEnv* env, libtextclassifier3::ScopedMmap* mmap) {
378   if (!mmap->handle().ok()) {
379     return JniHelper::NewStringUTF(env, "");
380   }
381   const ActionsModel* model = libtextclassifier3::ViewActionsModel(
382       mmap->handle().start(), mmap->handle().num_bytes());
383   if (!model || !model->locales()) {
384     return JniHelper::NewStringUTF(env, "");
385   }
386   return JniHelper::NewStringUTF(env, model->locales()->c_str());
387 }
388 
GetVersionFromMmap(JNIEnv * env,libtextclassifier3::ScopedMmap * mmap)389 jint GetVersionFromMmap(JNIEnv* env, libtextclassifier3::ScopedMmap* mmap) {
390   if (!mmap->handle().ok()) {
391     return 0;
392   }
393   const ActionsModel* model = libtextclassifier3::ViewActionsModel(
394       mmap->handle().start(), mmap->handle().num_bytes());
395   if (!model) {
396     return 0;
397   }
398   return model->version();
399 }
400 
GetNameFromMmap(JNIEnv * env,libtextclassifier3::ScopedMmap * mmap)401 StatusOr<ScopedLocalRef<jstring>> GetNameFromMmap(
402     JNIEnv* env, libtextclassifier3::ScopedMmap* mmap) {
403   if (!mmap->handle().ok()) {
404     return JniHelper::NewStringUTF(env, "");
405   }
406   const ActionsModel* model = libtextclassifier3::ViewActionsModel(
407       mmap->handle().start(), mmap->handle().num_bytes());
408   if (!model || !model->name()) {
409     return JniHelper::NewStringUTF(env, "");
410   }
411   return JniHelper::NewStringUTF(env, model->name()->c_str());
412 }
413 }  // namespace
414 }  // namespace libtextclassifier3
415 
416 using libtextclassifier3::ActionsSuggestionsJniContext;
417 using libtextclassifier3::ActionSuggestionsToJObject;
418 using libtextclassifier3::FromJavaActionSuggestionOptions;
419 using libtextclassifier3::FromJavaConversation;
420 using libtextclassifier3::JByteArrayToString;
421 
TC3_JNI_METHOD(jlong,TC3_ACTIONS_CLASS_NAME,nativeNewActionsModel)422 TC3_JNI_METHOD(jlong, TC3_ACTIONS_CLASS_NAME, nativeNewActionsModel)
423 (JNIEnv* env, jobject clazz, jint fd, jbyteArray jserialized_preconditions) {
424   std::shared_ptr<libtextclassifier3::JniCache> jni_cache =
425       libtextclassifier3::JniCache::Create(env);
426   std::string serialized_preconditions;
427   if (jserialized_preconditions != nullptr) {
428     TC3_ASSIGN_OR_RETURN_0(
429         serialized_preconditions,
430         JByteArrayToString(env, jserialized_preconditions),
431         TC3_LOG(ERROR) << "Could not convert serialized preconditions.");
432   }
433 
434 #ifdef TC3_UNILIB_JAVAICU
435   return reinterpret_cast<jlong>(ActionsSuggestionsJniContext::Create(
436       jni_cache, ActionsSuggestions::FromFileDescriptor(
437                      fd, std::unique_ptr<UniLib>(new UniLib(jni_cache)),
438                      serialized_preconditions)));
439 #else
440   return reinterpret_cast<jlong>(ActionsSuggestionsJniContext::Create(
441       jni_cache, ActionsSuggestions::FromFileDescriptor(
442                      fd, /*unilib=*/nullptr, serialized_preconditions)));
443 #endif  // TC3_UNILIB_JAVAICU
444 }
445 
TC3_JNI_METHOD(jlong,TC3_ACTIONS_CLASS_NAME,nativeNewActionsModelFromPath)446 TC3_JNI_METHOD(jlong, TC3_ACTIONS_CLASS_NAME, nativeNewActionsModelFromPath)
447 (JNIEnv* env, jobject clazz, jstring path,
448  jbyteArray jserialized_preconditions) {
449   std::shared_ptr<libtextclassifier3::JniCache> jni_cache =
450       libtextclassifier3::JniCache::Create(env);
451   TC3_ASSIGN_OR_RETURN_0(const std::string path_str,
452                          JStringToUtf8String(env, path));
453   std::string serialized_preconditions;
454   if (jserialized_preconditions != nullptr) {
455     TC3_ASSIGN_OR_RETURN_0(
456         serialized_preconditions,
457         JByteArrayToString(env, jserialized_preconditions),
458         TC3_LOG(ERROR) << "Could not convert serialized preconditions.");
459   }
460 #ifdef TC3_UNILIB_JAVAICU
461   return reinterpret_cast<jlong>(ActionsSuggestionsJniContext::Create(
462       jni_cache, ActionsSuggestions::FromPath(
463                      path_str, std::unique_ptr<UniLib>(new UniLib(jni_cache)),
464                      serialized_preconditions)));
465 #else
466   return reinterpret_cast<jlong>(ActionsSuggestionsJniContext::Create(
467       jni_cache, ActionsSuggestions::FromPath(path_str, /*unilib=*/nullptr,
468                                               serialized_preconditions)));
469 #endif  // TC3_UNILIB_JAVAICU
470 }
471 
TC3_JNI_METHOD(jlong,TC3_ACTIONS_CLASS_NAME,nativeNewActionsModelWithOffset)472 TC3_JNI_METHOD(jlong, TC3_ACTIONS_CLASS_NAME, nativeNewActionsModelWithOffset)
473 (JNIEnv* env, jobject clazz, jint fd, jlong offset, jlong size,
474  jbyteArray jserialized_preconditions) {
475   std::shared_ptr<libtextclassifier3::JniCache> jni_cache =
476       libtextclassifier3::JniCache::Create(env);
477   std::string serialized_preconditions;
478   if (jserialized_preconditions != nullptr) {
479     TC3_ASSIGN_OR_RETURN_0(
480         serialized_preconditions,
481         JByteArrayToString(env, jserialized_preconditions),
482         TC3_LOG(ERROR) << "Could not convert serialized preconditions.");
483   }
484 #ifdef TC3_UNILIB_JAVAICU
485   return reinterpret_cast<jlong>(ActionsSuggestionsJniContext::Create(
486       jni_cache,
487       ActionsSuggestions::FromFileDescriptor(
488           fd, offset, size, std::unique_ptr<UniLib>(new UniLib(jni_cache)),
489           serialized_preconditions)));
490 #else
491   return reinterpret_cast<jlong>(ActionsSuggestionsJniContext::Create(
492       jni_cache,
493       ActionsSuggestions::FromFileDescriptor(
494           fd, offset, size, /*unilib=*/nullptr, serialized_preconditions)));
495 #endif  // TC3_UNILIB_JAVAICU
496 }
497 
TC3_JNI_METHOD(jobject,TC3_ACTIONS_CLASS_NAME,nativeSuggestActions)498 TC3_JNI_METHOD(jobject, TC3_ACTIONS_CLASS_NAME, nativeSuggestActions)
499 (JNIEnv* env, jobject thiz, jlong ptr, jobject jconversation, jobject joptions,
500  jlong annotatorPtr, jobject app_context, jstring device_locales,
501  jboolean generate_intents) {
502   if (!ptr) {
503     return nullptr;
504   }
505   TC3_ASSIGN_OR_RETURN_NULL(const Conversation conversation,
506                             FromJavaConversation(env, jconversation));
507   const ActionSuggestionOptions options =
508       FromJavaActionSuggestionOptions(env, joptions);
509   const ActionsSuggestionsJniContext* context =
510       reinterpret_cast<ActionsSuggestionsJniContext*>(ptr);
511   const Annotator* annotator = reinterpret_cast<Annotator*>(annotatorPtr);
512 
513   const ActionsSuggestionsResponse response =
514       context->model()->SuggestActions(conversation, annotator, options);
515 
516   const reflection::Schema* anntotations_entity_data_schema =
517       annotator ? annotator->entity_data_schema() : nullptr;
518 
519   TC3_ASSIGN_OR_RETURN_NULL(
520       ScopedLocalRef<jobject> result,
521       ActionSuggestionsToJObject(
522           env, context, app_context, anntotations_entity_data_schema, response,
523           conversation, device_locales, generate_intents));
524   return result.release();
525 }
526 
TC3_JNI_METHOD(void,TC3_ACTIONS_CLASS_NAME,nativeCloseActionsModel)527 TC3_JNI_METHOD(void, TC3_ACTIONS_CLASS_NAME, nativeCloseActionsModel)
528 (JNIEnv* env, jobject thiz, jlong model_ptr) {
529   const ActionsSuggestionsJniContext* context =
530       reinterpret_cast<ActionsSuggestionsJniContext*>(model_ptr);
531   delete context;
532 }
533 
TC3_JNI_METHOD(jstring,TC3_ACTIONS_CLASS_NAME,nativeGetLocales)534 TC3_JNI_METHOD(jstring, TC3_ACTIONS_CLASS_NAME, nativeGetLocales)
535 (JNIEnv* env, jobject clazz, jint fd) {
536   const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
537       new libtextclassifier3::ScopedMmap(fd));
538   TC3_ASSIGN_OR_RETURN_NULL(
539       ScopedLocalRef<jstring> result,
540       libtextclassifier3::GetLocalesFromMmap(env, mmap.get()));
541   return result.release();
542 }
543 
TC3_JNI_METHOD(jstring,TC3_ACTIONS_CLASS_NAME,nativeGetLocalesWithOffset)544 TC3_JNI_METHOD(jstring, TC3_ACTIONS_CLASS_NAME, nativeGetLocalesWithOffset)
545 (JNIEnv* env, jobject clazz, jint fd, jlong offset, jlong size) {
546   const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
547       new libtextclassifier3::ScopedMmap(fd, offset, size));
548   TC3_ASSIGN_OR_RETURN_NULL(
549       ScopedLocalRef<jstring> result,
550       libtextclassifier3::GetLocalesFromMmap(env, mmap.get()));
551   return result.release();
552 }
553 
TC3_JNI_METHOD(jstring,TC3_ACTIONS_CLASS_NAME,nativeGetName)554 TC3_JNI_METHOD(jstring, TC3_ACTIONS_CLASS_NAME, nativeGetName)
555 (JNIEnv* env, jobject clazz, jint fd) {
556   const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
557       new libtextclassifier3::ScopedMmap(fd));
558   TC3_ASSIGN_OR_RETURN_NULL(
559       ScopedLocalRef<jstring> result,
560       libtextclassifier3::GetNameFromMmap(env, mmap.get()));
561   return result.release();
562 }
563 
TC3_JNI_METHOD(jstring,TC3_ACTIONS_CLASS_NAME,nativeGetNameWithOffset)564 TC3_JNI_METHOD(jstring, TC3_ACTIONS_CLASS_NAME, nativeGetNameWithOffset)
565 (JNIEnv* env, jobject clazz, jint fd, jlong offset, jlong size) {
566   const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
567       new libtextclassifier3::ScopedMmap(fd, offset, size));
568   TC3_ASSIGN_OR_RETURN_NULL(
569       ScopedLocalRef<jstring> result,
570       libtextclassifier3::GetNameFromMmap(env, mmap.get()));
571   return result.release();
572 }
573 
TC3_JNI_METHOD(jint,TC3_ACTIONS_CLASS_NAME,nativeGetVersion)574 TC3_JNI_METHOD(jint, TC3_ACTIONS_CLASS_NAME, nativeGetVersion)
575 (JNIEnv* env, jobject clazz, jint fd) {
576   const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
577       new libtextclassifier3::ScopedMmap(fd));
578   return libtextclassifier3::GetVersionFromMmap(env, mmap.get());
579 }
580 
TC3_JNI_METHOD(jint,TC3_ACTIONS_CLASS_NAME,nativeGetVersionWithOffset)581 TC3_JNI_METHOD(jint, TC3_ACTIONS_CLASS_NAME, nativeGetVersionWithOffset)
582 (JNIEnv* env, jobject clazz, jint fd, jlong offset, jlong size) {
583   const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
584       new libtextclassifier3::ScopedMmap(fd, offset, size));
585   return libtextclassifier3::GetVersionFromMmap(env, mmap.get());
586 }
587 
TC3_JNI_METHOD(jlong,TC3_ACTIONS_CLASS_NAME,nativeGetNativeModelPtr)588 TC3_JNI_METHOD(jlong, TC3_ACTIONS_CLASS_NAME, nativeGetNativeModelPtr)
589 (JNIEnv* env, jobject thiz, jlong ptr) {
590   if (!ptr) {
591     return 0L;
592   }
593   return reinterpret_cast<jlong>(
594       reinterpret_cast<ActionsSuggestionsJniContext*>(ptr)->model());
595 }
596 
TC3_JNI_METHOD(jboolean,TC3_ACTIONS_CLASS_NAME,nativeInitializeConversationIntentDetection)597 TC3_JNI_METHOD(jboolean, TC3_ACTIONS_CLASS_NAME,
598                nativeInitializeConversationIntentDetection)
599 (JNIEnv* env, jobject thiz, jlong ptr, jbyteArray jserialized_config) {
600   if (!ptr) {
601     return false;
602   }
603 
604   ActionsSuggestions* model =
605       reinterpret_cast<ActionsSuggestionsJniContext*>(ptr)->model();
606 
607   std::string serialized_config;
608   TC3_ASSIGN_OR_RETURN_0(
609       serialized_config, JByteArrayToString(env, jserialized_config),
610       TC3_LOG(ERROR) << "Could not convert serialized conversation intent "
611                         "detection config.");
612   return model->InitializeConversationIntentDetection(serialized_config);
613 }
614