This guide describes how to use the Android API library wrapper. The library
wrapper command-line tool generates C-language wrapper code for Java Android
APIs, enabling you to integrate Java libraries into native C/C++ Android apps.
For more details on the library wrapper, see
[Library wrapper for Android APIs](https://developer.android.com/games/develop/custom/wrapper).

This step-by-step guide demonstrates how to use the wrapper tool to integrate a
Java library into a native Android app. For example purposes, this guide covers
integrating the [notification library](https://developer.android.com/reference/androidx/core/app/NotificationCompat) of the `androidx.core.app` package.
See [Create a Notification](https://developer.android.com/develop/ui/views/notifications/build-notification) to learn more about this library.

## Prerequisites

This guide assumes you have an existing native Android project. It also
uses the [Gradle](https://developer.android.com/studio/build) build system. If you don't have an existing project, create a
new one in Android Studio using the **Native C++** template.

The example code in this guide uses the directory root `my_project/`. Native
code is located in `my_project/app/src/main/cpp/`, the default directory for
Android Studio projects.

If you don't already have the library wrapper tool, [download](https://dl.google.com/developers/android/agdk/library_wrapper_1.0.0-beta1.zip) and unzip the
package to the directory of your choice. This CLI tool requires the Java Runtime
Environment (JRE).

## Generate native code

When integrating a Java library, use the wrapper tool to
generate a native code wrapper. The first step is to configure the wrapper.

### Create the wrapper config

You create library wrapper [configuration files](https://developer.android.com/games/develop/custom/wrapper#config) to control the
output of the native code generator. One feature of this file lets you specify
the classes and methods to generate wrapper code.

Since there aren't many methods to wrap for the notifications library, you can
define them directly in the `custom_classes` section. Create a new
`config.json` resource anywhere in your project to define the methods. For example,
you can create `my_project/library_wrapper/config.json` and paste the following
sample configuration:  

    {
      "custom_classes": [
        {
          "class_name": "class java.lang.CharSequence"
        },
        {
          "class_name": "class java.lang.Object",
          "methods": [
            "java.lang.String toString()"
          ]
        },
        {
          "class_name": "class java.lang.String"
        },
        {
          "class_name": "class android.content.Context",
          "methods": [
            "java.lang.Object getSystemService(java.lang.String name)"
          ]
        },
        {
          "class_name": "class android.app.Notification"
        },
        {
          "class_name": "class android.app.NotificationManager",
          "methods": [
            "void createNotificationChannel(android.app.NotificationChannel channel)"
          ]
        },
        {
          "class_name": "class android.app.NotificationChannel",
          "methods": [
            "NotificationChannel(java.lang.String id, java.lang.CharSequence name, int importance)",
            "void setDescription(java.lang.String description)"
          ]
        },
        {
          "class_name": "class androidx.core.app.NotificationCompat"
        },
        {
          "class_name": "class androidx.core.app.NotificationCompat$Builder",
          "methods": [
            "Builder(android.content.Context context, java.lang.String channelId)",
            "androidx.core.app.NotificationCompat$Builder setContentText(java.lang.CharSequence text)",
            "androidx.core.app.NotificationCompat$Builder setContentTitle(java.lang.CharSequence title)",
            "androidx.core.app.NotificationCompat$Builder setSmallIcon(int icon)",
            "androidx.core.app.NotificationCompat$Builder setPriority(int pri)",
            "android.app.Notification build()"
          ]
        },
        {
          "class_name": "class androidx.core.app.NotificationManagerCompat",
          "methods": [
            "static androidx.core.app.NotificationManagerCompat from(android.content.Context context)",
            "void notify(int id, android.app.Notification notification)"
          ]
        }
      ]
    }

In the preceding sample, you directly declare the Java classes and methods that
require native wrapper code.
| **Note:** If an entire Java library needs to be wrapped, it's more efficient to provide a JAR file to the library wrapper instead of defining classes and methods in the configuration file. For an example, see [Generate wrappers from JARs](https://developer.android.com/games/develop/custom/wrapper-guide#jar-wrapper).

### Run the library wrapper

With your wrapper config file defined, you're ready to use the tool to generate
native wrapper code. Open a terminal to where you extracted the library wrapper
and run the following command:  

    java -jar lw.jar \
      -o "my_project/app/src/main/cpp/native_wrappers" \
      -c "my_project/library_wrapper/config.json"

| **Note:** You may need to update the directory paths to reflect your project structure.

In the preceding sample you use the `-c` parameter to specify your wrapper
config location, and the `-o` parameter to define the generated code directory.
After running the tool, you should now have the generated code needed to call the
Java-based notifications API from your native app.

## Implement native notifications

In this section, you integrate the Android notifications library into your
native app using your generated wrapper code. The first step is to update your
project's app-level `gradle.build` resource (`my_project/app/gradle.build`).

### Update `gradle.build`

1. GNI is a support library required by the generated wrapper code. All projects
   using generated code should reference this library. To reference this library,
   add the following line to the `dependencies` section of `build.gradle`:

       implementation 'com.google.android.gms:play-services-gni-native-c:1.0.0-beta2'

2. To enable [prefab](https://developer.android.com/studio/build/dependencies#using-native-dependencies) support, add the following code to the `android` section:

       buildFeatures {
         prefab true
       }

3. To configure `cmake`, use the following `cmake` configuration in the
   `android/defaultConfig` section:

       externalNativeBuild {
         cmake {
             arguments '-DANDROID_STL=c++_shared'
         }
       }

Your completed `build.gradle` configuration should resemble the following:  

    android {
        ...

        buildFeatures {
            prefab true
        }

        defaultConfig {
            ...

            externalNativeBuild {
                cmake {
                    arguments '-DANDROID_STL=c++_shared'
                }
            }
        }
    }

    dependencies {
        ...
        implementation 'com.google.android.gms:play-services-gni-native-c:1.0.0-beta2'
        ...
    }

### Modify `CMakeLists`

1. Add the GNI library to your project's `CMakeLists.txt`
   (`my_project/app/src/main/cpp/CMakeLists.txt`) by adding the following line at
   the top level of the file:

       find_package(com.google.android.gms.gni.c REQUIRED CONFIG)

2. Add the following line to the `target_link_libraries` section:

       PUBLIC com.google.android.gms.gni.c::gni_shared

3. Add a reference to the generated code by adding the following line at the
   top level of the file:

       file(GLOB_RECURSE native_wrappers CONFIGURE_DEPENDS "native_wrappers/*.cpp" "native_wrappers/*.cc")

4. Add these lines near the end of the file:

       include_directories(./native_wrappers/c)
       include_directories(./native_wrappers/cpp)

Your updated `CMakeLists.txt` resource should resemble the following sample:  

    cmake_minimum_required(VERSION 3.18.1)

    project("my_project")

    file(GLOB_RECURSE native_wrappers CONFIGURE_DEPENDS "native_wrappers/*.cpp" "native_wrappers/*.cc")

    add_library(
            my_project
            SHARED
            native-lib.cpp
            ${native_wrappers}
            )

    find_library(
            log-lib
            log)

    find_package(com.google.android.gms.gni.c REQUIRED CONFIG)

    target_link_libraries(
            my_project
            PUBLIC com.google.android.gms.gni.c::gni_shared
            ${log-lib})

    include_directories(./native_wrappers/c)
    include_directories(./native_wrappers/cpp)

### Implement notification logic

1. Open or create the source file where you would like to implement notification
   capabilities. In this file, include the header file `gni.h` and define a
   new `ShowNativeNotification()` function:

       #include "gni/gni.h"

       void ShowNativeNotification(JNIEnv *env, jobject main_activity, int icon_id) {
         // Get the JavaVM from the JNIEnv.
         JavaVM *java_vm;
         env->GetJavaVM(&java_vm);

         // Initialize the GNI runtime. This function needs to be called before any
         // call to the generated code.
         GniCore_init(java_vm, main_activity);
       }

2. Define notification-specific constant values, and the notification
   handler functions `CharSequenceFromCString()` and `CreateNotification()`:

   **Caution:** C++ code generation is a work in progress. Use the generated C code instead.  

   ### C

   ```c
   const int32_t IMPORTANCE_HIGH = 4;  // NotificationManager.IMPORTANCE_HIGH
   const int32_t PRIORITY_MAX = 2;  // NotificationCompat.PRIORITY_MAX
   const int32_t NOTIFICATION_ID = 123;  // User defined notification id.

   // Convert a C string into CharSequence.
   CharSequence *CharSequenceFromCString(const char *text) {
      String *string = String_fromCString(text);
      // Cast String to CharSequence. In Java, a String implements CharSequence.
      CharSequence *result = GNI_CAST(CharSequence, String, string);
      // Casting creates a new object, so it needs to be destroyed as normal.
      String_destroy(string);
      return result;
   }

   // Create a notification.
   Notification *
   CreateNotification(Context *context, String *channel_id,
                      const char *title, const char *content,
                      int32_t icon_id) {
      // Convert C strings to CharSequence.
      CharSequence *title_chars = CharSequenceFromCString(title);
      CharSequence *content_chars = CharSequenceFromCString(content);

      // Create a NotificationCompat.Builder and set all required properties.
      NotificationCompat_Builder *notification_builder =
          NotificationCompat_Builder_construct(context, channel_id);
      NotificationCompat_Builder_setContentTitle(notification_builder,
                                                 title_chars);
      NotificationCompat_Builder_setContentText(notification_builder,
                                                content_chars);
      NotificationCompat_Builder_setSmallIcon(notification_builder, icon_id);
      NotificationCompat_Builder_setPriority(notification_builder,
                                             PRIORITY_MAX);

      // Build a notification.
      Notification *notification =
          NotificationCompat_Builder_build(notification_builder);

      // Clean up allocated objects.
      NotificationCompat_Builder_destroy(notification_builder);
      CharSequence_destroy(title_chars);
      CharSequence_destroy(content_chars);

      return notification;
   }
   ```

   ### C++

   ```c++
   const int32_t IMPORTANCE_HIGH = 4;  // NotificationManager.IMPORTANCE_HIGH
   const int32_t PRIORITY_MAX = 2;  // NotificationCompat.PRIORITY_MAX
   const int32_t NOTIFICATION_ID = 123;  // User defined notification id.

   // Convert a C string into CharSequence.
   CharSequence *CharSequenceFromCString(const char *text) {
      String *string = String_fromCString(text);
      // Cast String to CharSequence. In Java, a String implements CharSequence.
      CharSequence *result = new CharSequence(string->GetImpl());
      // Casting creates a new object, so it needs to be destroyed as normal.
      String::destroy(string);
      return result;
   }

   // Create a notification.
   Notification&
   CreateNotification(Context *context, String *channel_id, const char *title,
                      const char *content, int32_t icon_id) {
      // Convert C strings to CharSequence.
      CharSequence *title_chars = CharSequenceFromCString(title);
      CharSequence *content_chars = CharSequenceFromCString(content);

      // Create a NotificationCompat.Builder and set all required properties.

      NotificationCompat::Builder *notification_builder = new NotificationCompat::Builder(*context, *channel_id);
      notification_builder->setContentTitle(*title_chars);
      notification_builder->setContentText(*content_chars);
      notification_builder->setSmallIcon(icon_id);
      notification_builder->setPriority(PRIORITY_MAX);

      // Build a notification.
      Notification& notification = notification_builder->build();

      // Clean up allocated objects.
      NotificationCompat::Builder::destroy(notification_builder);
      CharSequence::destroy(title_chars);
      CharSequence::destroy(content_chars);

      return notification;
   }
   ```

   Some functions of the notifications library take the `CharSequence` instead of
   `String`. The `CharSequenceFromCString()` function enables conversion between
   these objects. The function `CreateNotification()` uses the wrapped version of
   Java [`NotificationCompat.Builder`](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder) to create a notification.
3. Add logic to create a notification channel by pasting in the following
   function, `CreateNotificationChannel()`:

   ### C

   ```c
   void CreateNotificationChannel(Context *context, String *channel_id) {
      CharSequence *channel_name = CharSequenceFromCString("channel name");
      String *channel_description = String_fromCString("channel description");
      String *system_service_name = String_fromCString("notification");
      NotificationChannel *channel =
          NotificationChannel_construct(channel_id, channel_name,
                                        IMPORTANCE_HIGH);
      NotificationChannel_setDescription(channel, channel_description);

      Object *notification_manager_as_object =
          Context_getSystemService(context, system_service_name);
      NotificationManager *notification_manager =
          GNI_CAST(NotificationManager, Object,
                   notification_manager_as_object);

      NotificationManager_createNotificationChannel(notification_manager,
                                                    channel);

      CharSequence_destroy(channel_name);
      String_destroy(channel_description);
      String_destroy(system_service_name);
      NotificationChannel_destroy(channel);
      Object_destroy(notification_manager_as_object);
      NotificationManager_destroy(notification_manager);
   }
   ```

   ### C++

   ```c++
   void CreateNotificationChannel(Context *context, String *channel_id) {
      CharSequence *channel_name = CharSequenceFromCString("channel name");
      String *channel_description = String_fromCString("channel description");
      String *system_service_name = String_fromCString("notification");
      NotificationChannel *channel =
          new NotificationChannel(*channel_id, *channel_name, IMPORTANCE_HIGH);
      channel->setDescription(*channel_description);

      Object& notification_manager_as_object =
          context->getSystemService(*system_service_name);
      NotificationManager *notification_manager =
          new NotificationManager(notification_manager_as_object.GetImpl());

      notification_manager->createNotificationChannel(*channel);

      CharSequence::destroy(channel_name);
      String::destroy(channel_description);
      String::destroy(system_service_name);
      NotificationChannel::destroy(channel);
      Object::destroy(&notification_manager_as_object);
      NotificationManager::destroy(notification_manager);
   }
   ```
   | **Note:** You must eventually destroy every object that's created or returned from a function. For details, see [Object lifecycle](https://developer.android.com/games/develop/custom/wrapper#object-lifecycle).
4. Update the `ShowNativeNotification()` function you created earlier to
   call `CreateNotificationChannel()`. Add the following code to the end of
   `ShowNativeNotification()`:

   ### C

   ```c
   void ShowNativeNotification(JNIEnv *env, jobject main_activity, int icon_id) {
    // ...

    // Create a Context object by wrapping an existing JNI reference.
    Context *context = Context_wrapJniReference(main_activity);

    // Create a String object.
    String *channel_id = String_fromCString("new_messages");

    // Create a notification channel.
    CreateNotificationChannel(context, channel_id);

    // Create a notification with a given title, content, and icon.
    Notification *notification =
        CreateNotification(context, channel_id, "My Native Notification",
                           "Hello!", icon_id);

    // Create a notification manager and use it to show the notification.
    NotificationManagerCompat *notification_manager =
        NotificationManagerCompat_from(context);
    NotificationManagerCompat_notify(notification_manager, NOTIFICATION_ID,
                                     notification);

    // Destroy all objects.
    Context_destroy(context);
    String_destroy(channel_id);
    Notification_destroy(notification);
    NotificationManagerCompat_destroy(notification_manager);
   }
   ```

   ### C++

   ```c++
   void ShowNativeNotification(JNIEnv *env, jobject main_activity, int icon_id) {
      // Get the JavaVM from the JNIEnv.
      JavaVM *java_vm;
      env->GetJavaVM(&java_vm);

      // Initialize the GNI runtime. This function needs to be called before any
      // call to the generated code.
      GniCore::Init(java_vm, main_activity);

      // Create a Context object by wrapping an existing JNI reference.
      Context *context = new Context(main_activity);

      // Create a String object.
      String *channel_id = String_fromCString("new_messages");

      // Create a notification channel.
      CreateNotificationChannel(context, channel_id);

      // Create a notification with a given title, content, and icon.
      Notification& notification =
          CreateNotification(context, channel_id, "My Native Notification",
                             "Hello!", icon_id);

      // Create a notification manager and use it to show the notification.
      NotificationManagerCompat& notification_manager =
          NotificationManagerCompat::from(*context);
      notification_manager.notify(NOTIFICATION_ID, notification);

      // Destroy all objects.
      Context::destroy(context);
      String::destroy(channel_id);
      Notification::destroy(&notification);
      NotificationManagerCompat::destroy(&notification_manager);
   }   
   ```
5. With your logic defined, trigger a notification by calling
   `ShowNativeNotification()` at an appropriate location in your project.

### Run the app

Compile and run the code that calls `ShowNativeNotification()`. A simple
notification should appear at the top of the screen of your test device.

## Generate wrappers from JARs

In the previous example, you manually defined Java classes and
methods requiring native code in a wrapper config file. For scenarios where you
need to access large sections of an API, its more efficient to provide one or
more library JARs to the wrapper tool. The wrapper then generates wrappers for
all public symbols it finds in the JAR.

The following example wraps the entire Notifications API by providing a library
JAR.

### Get the required JARs

The Notification API is a part of the `androidx.core` package, available from
the [Google Maven](https://maven.google.com/web/index.html?q=androidx#androidx.core:core:1.9.0) repository. Download the library [aar](https://dl.google.com/android/maven2/androidx/core/core/1.9.0/core-1.9.0.aar) file and unpack it to
a directory of your choice. Locate the `classes.jar` file.

The `classes.jar` file contains many classes beyond our required notifications
library. If you provide the library wrapper with just `classes.jar`, the tool
generates native code for every class in the JAR, which is inefficient and
unnecessary for our project. To solve this, provide a filter file to the
wrapper configuration to restrict code generation to the JAR's notification
classes.

### Define an allow filter

[Filter files](https://developer.android.com/games/develop/custom/wrapper#filters) are plain text files you provide to your library wrapper
configuration. They allow you to define which classes to include (or exclude)
from JAR files provided to the library wrapper.

In your project, create a file titled `allowed-symbols.txt` and paste in the
following line:  

    androidx.core.app.NotificationCompat*

When used as an allow filter, the preceding code specifies that only symbols
whose name starts with `androidx.core.app.NotificationCompat` are wrapped.

### Run the library wrapper

Open a terminal to the JAR directory and run the following command:  

    java -jar lw.jar \
     -i classes.jar \
     -o "./generated-jar" \
     -c "./config.json" \
     -fa allowed-symbols.txt \
     --skip_deprecated_symbols

The preceding sample command generates wrapper code for your filtered classes
to the directory `generated-jar/`.

## Support

If you find an issue with the library wrapper, please let us know.

|                                  Browse bugs                                   |                                 File a bug                                  |
|--------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
| [Engineering](http://issuetracker.google.com/issues?q=componentid:778598%2B)   | [*bug_report*](https://issuetracker.google.com/issues/new?component=778598) |
| [Documentation](http://issuetracker.google.com/issues?q=componentid:778285%2B) | [*bug_report*](https://issuetracker.google.com/issues/new?component=778285) |

*** ** * ** ***