# Proguard Rules, Notification, and Analytics Configurations

The documentation provides configuration and code snippets for integrating VPN capabilities into Android apps.

## Proguard Rules

### What is Proguard?

ProGuard is a tool used in Android development to optimize, shrink, and obfuscate the bytecode of Java and Kotlin applications. It's part of the Android build process and helps reduce the size of the APK, improve performance, and protect the code from reverse engineering by obfuscating class, method, and variable names. ProGuard processes the compiled bytecode before packaging the app for distribution.

### Benefits of using Proguard Rules

Developers use Proguard rules to optimize their application, protect sensitive code from reverse-engineering, and reduce the size of the final APK (Android package). By stripping away unnecessary code, Proguard improves app performance and reduces memory usage. It is especially important for production builds, where security and performance are critical.

### When to use Proguard Rules

Proguard rules should be used when preparing release builds of an Android app, especially if the app uses third-party libraries that rely on reflection (like JSON serialization libraries), or if the developer needs to optimize the app's size and security. They are less critical for debug builds but essential for production releases to ensure the app functions correctly while being as efficient and secure as possible.

### Usage

1. Copy the provided ProGuard rules into your project's `proguard-rules.pro` file.
2. By default, `minifyEnabled` is set to `false` within the `build.gradle (Module :app)`. You will need to go into `build.gradle (Module :app)` and set it as true:

Groovy

```groovy
android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}
```

<figure><img src="https://3437387582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M57yuAWriRSwZHosffd%2Fuploads%2Fgit-blob-ca471559a36d70f8e440b9a5439c8916304a7c67%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

Kotlin

By default, `isMinifyEnabled` is set to `false` within the `build.gradle (Module :app)` . You will need to go into `build.gradle.kts (Module :app)` and set it as = true

```kotlin
android {
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
        }
    }
}
```

### Proguard Rules Explanation

Proguard rules are included in sdk, but you can follow these steps if required:

* Located the `proguard-rules.pro` file on the left hand side of Android Studio.
* Double-click into this file
* Copy the rules from the grey box below and paste them into the `proguard-rules.pro` file

<figure><img src="https://3437387582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M57yuAWriRSwZHosffd%2Fuploads%2Fgit-blob-9fb10989ed7b618f0b5a5a6a4015d61911bf7fd7%2Fimage%20(111).png?alt=media" alt=""><figcaption></figcaption></figure>

```
-keep class * extends unified.vpn.sdk.ReportUrlProvider {
    public <init>(...);
}

-keep class * extends unified.vpn.sdk.BaseInfoCollector {
    public <init>(...);
}

-keep class * extends unified.vpn.sdk.TrackerTransportFactory {
    public <init>(...);
}

-keep class * extends unified.vpn.sdk.CredentialsSource {
    public <init>(...);
}

-keep class * extends unified.vpn.sdk.SdkTrackerDelegate {
    public <init>(...);
}
-keep enum unified.vpn.sdk.UnifiedSDKConfig$CallbackMode {
    *;
}

-keep class * extends unified.vpn.sdk.IStartConfigPatcher {
    public <init>(...);
}
-keep class * extends unified.vpn.sdk.ConfigPatcherFactory {
    public <init>(...);
}
-keep class * extends unified.vpn.sdk.IMiddleConfigPatcher {
    public <init>(...);
}

#DNSJava
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

-keepclassmembers class * implements javax.net.ssl.SSLSocketFactory {
    final javax.net.ssl.SSLSocketFactory delegate;
}

# https://stackoverflow.com/questions/56142150/fatal-exception-java-lang-nullpointerexception-in-release-build
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
-dontwarn org.conscrypt.Conscrypt$Version
-dontwarn org.conscrypt.Conscrypt
-dontwarn org.conscrypt.ConscryptHostnameVerifier
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE
-dontwarn unified.vpn.sdk.HydraConnectionState

-keep class * extends unified.vpn.sdk.Daemon {
     public <init>(...);
 }

-dontwarn unified.vpn.sdk.KeyValueStorageImpl

-keep public class * extends unified.vpn.sdk.NetworkProbeFactory {
     public <init>(...);
 }

-keep class unified.vpn.sdk.PingService { *; }
-keep class unified.vpn.sdk.PingResult { *; }

# This dnsjava class uses old Sun API
-dontnote org.xbill.DNS.spi.DNSJavaNameServiceDescriptor
-dontwarn org.xbill.DNS.spi.DNSJavaNameServiceDescriptor

-keepclassmembers class org.xbill.DNS.Lookup {
    java.util.Map defaultCaches;
    org.xbill.DNS.Name[] defaultSearchPath;
    int defaultNdots;
}

-keep class * extends unified.vpn.sdk.WireguardPingJobFactory {
    public <init>(...);
}
-keepclassmembers class com.wireguard.** { *; }

-keepclassmembers class unified.vpn.sdk.HydraTransport$ApiHeaderListener {
    *** protect(...);
    *** onHdr(...);
}

-keep class com.anchorfree.hdr.** {*;}

-keep public class * extends unified.vpn.sdk.VpnException {
    *;
}

-keep public class * extends unified.vpn.sdk.CaptivePortalChecker {
    public <init>(...);
}

-keep public class * extends unified.vpn.sdk.TransportFactory {
    public <init>(...);
}

-keep class * extends unified.vpn.sdk.ReconnectExceptionHandler {
    public <init>(...);
}

```

## Setting VPN Process Name

To set a custom process name for the VPN, add the following string resource to your source file:

1. On the left hand side of Android Studio, click the folder `res`
2. Then click on `values`
3. Double click on `</> strings.xml`
4. Paste the text below into that file:

<figure><img src="https://3437387582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M57yuAWriRSwZHosffd%2Fuploads%2Fgit-blob-0d51f62dcdc6f69c917f726ce81556e8ffaf0ce8%2Fimage%20(113).png?alt=media" alt=""><figcaption></figcaption></figure>

```xml
<string name="vpn_process_name" translatable="false">:vpn</string>
```

## Enabling Java 8 Support

If you are using Android Studio and selected a minimum SDK version of API 26 (Oreo; Android 8.0), Java 8 will be configured by default for you. If for some reason it is not, please follow the steps listed below

The minimum version of Java that our SDK works with is Java 8. You can use Java 8 or higher (up to Java 21) to build your application.

To add Java 8 support to your project:

1. On the left hand side of Android Studio, select `build.gradle (Module: app)` for Gradle projects or `build.gradle.kts (Module: app)` for Kotlin projects.

<figure><img src="https://3437387582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M57yuAWriRSwZHosffd%2Fuploads%2Fgit-blob-9074d1357d2cee490df8de6581f2af176f03a9e3%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

2. update your `build.gradle` file with the following:

Kotlin

```kotlin
compileOptions {
   sourceCompatibility = JavaVersion.VERSION_1_8
   targetCompatibility = JavaVersion.VERSION_1_8
}
```

Groovy

```groovy
compileOptions {
   sourceCompatibility = JavaVersion.VERSION_1_8
   targetCompatibility = JavaVersion.VERSION_1_8
}
```

3. Paste the code snippet within the Android configuration block

<figure><img src="https://3437387582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M57yuAWriRSwZHosffd%2Fuploads%2Fgit-blob-c3b1c09d2012377b2c51bf55a416553798383216%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

If you cannot enable Java 8 or higher (up to Java 21) support in your project, please contact your account manager for assistance. If you do not have an account manager, please contact us [here](https://www.pango.co/contact-us/).

## Configuring Notifications

The SDK provides the ability to customize the notifications that appear during various stages of the VPN lifecycle. By utilizing the `NotificationConfig.Builder` class, you can configure how these notifications behave, what information they display, and how users can interact with them.

**Important**: Notifications are not shown by default. You must create a notification channel and configure the SDK to use it.

### **Creating a Notification Channel**

Before the SDK can display notifications, you need to create a notification channel and provide its ID to the SDK configuration:

Kotlin

```kotlin
val notificationsConfig = SdkNotificationConfig.newBuilder()
    .channelId("createdId") // Set the ID of your created notification channel
    .inConnected("VPN Connected", "Your VPN is now connected.")
    .build()

UnifiedSdk.update(notificationsConfig)
```

Java

```java
SdkNotificationConfig notificationsConfig = SdkNotificationConfig.newBuilder()
    .channelId("createdId") // Set the ID of your created notification channel
    .inConnected("VPN Connected", "Your VPN is now connected.")
    .build();

UnifiedSdk.update(notificationsConfig);
```

### **Notifications for Different States**

The SDK allows you to customize the notification messages for various VPN states. This provides flexibility in messaging and helps keep users informed about what is happening with the VPN connection.

1. VPN Connected: When the VPN successfully connects, a notification is displayed.

   ```java
   builder.inConnected("Connected", "VPN is successfully connected.");
   ```
2. VPN Idle: When the VPN is not connected (idle state), display a different notification.

   ```java
   builder.inIdle("Not Connected", "VPN is currently disconnected.");
   ```
3. VPN Connecting: During the VPN connection process, you can show a "connecting" notification.

   ```java
   builder.inConnecting("Connecting", "Attempting to establish a VPN connection...");
   ```
4. VPN Paused (Waiting for Network Connection): This state occurs when the VPN is paused while waiting for a network connection (e.g., when switching from Wi-Fi to cellular data).

   ```java
   builder.inPause("Paused", "VPN is paused, waiting for network connection.");
   ```
5. Client Network List (CNL) Feature: If the Client Network List feature is used, you can provide a notification to inform users.

   ```java
   builder.inCnl("Network Feature", "Client Network List feature is active.");
   ```

### **Notification Message and Title Fallback**

The SDK provides notifications to users during different VPN states (such as "connected" or "paused"). When notifications are enabled (by providing a notification channel), if you do not explicitly define custom notifications for these states, the SDK has a fallback process in place to ensure that meaningful information is still displayed to the user. This fallback process guarantees that the user is kept informed, even when you haven't manually set up specific notifications.

When notifications are configured, the SDK will show notifications for the `CONNECTED` and `PAUSED` states. The fallback process kicks in when specific customization is missing.

Here’s a step-by-step breakdown of how the SDK handles the fallback for notification messages and titles:

1. If **inConnected** was not called

The SDK allows you to customize notifications for different VPN states using methods like inConnected(). For example, you can specify the title and message when the VPN is connected:

```
builder.inConnected("VPN Connected", "Your VPN is now connected.") 
```

**What happens if you don’t call inConnected()?**

If you don’t explicitly set a notification for the `CONNECTED` state using `inConnected()`, the SDK will try to fall back to a general title that is set in the `NotificationConfig.Builder`. This means that the SDK will search for any custom title provided in the configuration and use that instead.

2. If No Custom Title is set using `NotificationConfig.Builder.title()`

If you haven’t called `inConnected()` and also haven’t set a custom title for notifications using `NotificationConfig.Builder.title()`, the SDK will move to the next fallback option: the app’s name.

The method `builder.title()` allows you to set a more generic title that can be used across multiple states (such as connected, paused, or disconnected). For example:

```
builder.title("VPN Status");
```

This generic title will be used for notifications unless specific messages are set for different VPN states.

**What happens if you don’t set a title with builder.title() either?**

If no title is provided with `builder.title()`, the SDK will then use the app’s name as defined in your `AndroidManifest.xml` file.

3. If no title is set, the SDK uses the App Name from AndroidManifest.xml

At this stage, if neither `inConnected()` nor `builder.title()` was called, the SDK will use the name of your app as a fallback. This app name is the name that appears in the app launcher (on the user’s home screen) and is defined in the `AndroidManifest.xml` file.

Here’s where the app’s name is set in the manifest:

```xml
<application
    android:label="@string/app_name">
</application>
```

The `android:label` points to a string resource that contains the name of your app. This string is usually stored in a file called strings.xml in the res/values folder:

```xml
<resources>
    <string name="app_name">My VPN App</string>
</resources>
```

In this case, if no custom title or message is set, the notification title will simply show “My VPN App” (or any name you’ve defined) as the fallback title when the VPN is connected or paused.

4. If No Custom Message Is Set, the SDK Uses Default String Resources

In addition to the title, the SDK can display default messages based on the VPN’s state. If you haven’t provided custom messages for these states (like connected or paused), the SDK will search for predefined string resources to use as the message.

For example:

If no message for the `CONNECTED` state is provided using `inConnected()`, the SDK will look for a default string resource called `default_notification_connected_message`.

Example of the default string resource in `strings.xml`:

```xml
<string name="default_notification_connected_message">VPN is connected</string>
```

Similarly, for the `PAUSED` state, if no message is provided using `inPause()`, the SDK will use a default string resource called `default_notification_paused_message`. Example:

```xml
<string name="default_notification_paused_message">VPN is paused</string>
```

**Why is this fallback important?**

Even if you don’t customize the messages, the SDK ensures that meaningful text is shown to users. This prevents the app from showing blank or empty notifications, ensuring a consistent user experience.

#### Fallback Flow Recap

Here’s the order in which the SDK decides what to show for the title and message in notifications:

1. First priority: The title and message you explicitly set using methods like inConnected() and inPause() (for specific states).
2. Second priority: A generic title you set using builder.title() (if state-specific titles aren’t provided).
3. Third priority: The app’s name from the AndroidManifest.xml file is used as the title if no custom titles are set.
4. Fourth priority: Default string resources like default\_notification\_connected\_message and default\_notification\_paused\_message are used for the message if no custom messages are set.

#### Summary

* When the VPN is in a certain state (like connected or paused), the SDK shows notifications to keep the user informed.
* If you don’t set a custom title and message for these notifications, the SDK will use a fallback process to ensure something meaningful is still displayed.
* The fallback process goes from your custom notifications, to general titles, to the app name, and finally to default string resources. This ensures that users always see a notification, even if you haven’t explicitly set one.

This fallback mechanism is designed to prevent your app from showing incomplete or confusing notifications, ensuring a smooth user experience even when certain settings are not customized.

### Notification Click Intent

#### clickAction Method

* The `clickAction` method is used to specify what should happen when a user clicks on the notification that is generated by the VPN SDK. This is typically handled through Intents in Android.
* Intents are used to trigger activities or services. If you don't define a `clickAction`, the SDK will default to opening the app’s main launch activity.

Intent Extras

* The SDK will include a specific extra in the Intent when the notification is clicked. The extra is a boolean value with the key `UnifiedSDKNotificationManager.EXTRA_NOTIFICATION`, which is set to `true`.
* Your activity can check for this extra to know that the user has clicked a VPN-related notification.

Activity Handling

* To ensure that a specific **Activity** (which is the screen or part of the app that you want to display) is launched when the notification is clicked, you need to define an Intent Filter in the `AndroidManifest.xml` file.
* The `intent-filter` helps the system recognize which activity to open when the notification is clicked. The `<action>` tag is where you define the custom action, and `<category>` is where you declare that this Intent belongs to the "default" category.

To handle the click action, register `intent-filter` with your activity:

```xml
<intent-filter>
    <action android:name="com.sdk.notification.action"/>
    <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
```

This means:

* When the user clicks on the notification, the SDK will send an Intent with the action `"com.sdk.notification.action"`.
* Android will look for an activity that has registered to handle this action (through the `intent-filter`) and open that activity.

### Updating Notification Preferences

**Purpose**: The UnifiedSDK.update(createNotificationConfig()) call allows you to update the notification settings for the SDK dynamically at runtime. The states were defined previously [here](#notifications-for-different-states).

**Usage**: You create a new configuration (e.g., what notifications should display), and then use the update() method to apply those changes immediately.

Notification config can be updated by calling the SDK method:

```java
UnifiedSDK.update(createNotificationConfig());
```

### Connected State Notification Placeholders

In the `CONNECTED` state, you can use placeholders in notifications to display real-time information about your VPN connection. These placeholders are special codes that will automatically update with live data while the VPN is connected.

Here are the available placeholders and what they do:

* `{dS}` – Shows the current download speed. This tells the user how fast data is being downloaded while the VPN is on.
* `{uS}` – Shows the current upload speed. This tells the user how fast data is being uploaded while the VPN is on.
* `{dT}` – Shows the total amount of downloaded data. This tells the user how much data has been downloaded in total since the VPN connected.
* `{uT}` – Shows the total amount of uploaded data. This tells the user how much data has been uploaded in total since the VPN connected.

Kotlin

```kotlin
val notificationConfig = SdkNotificationConfig.newBuilder() 
 .inConnected("VPN Connected", "Download Speed: {dS}, Upload Speed: {uS}") 
 .build()

 UnifiedSDK.update(notificationConfig)
```

Groovy

```groovy
def notificationConfig = SdkNotificationConfig.newBuilder()
    .inConnected("VPN Connected", "Download Speed: {dS}, Upload Speed: {uS}")
    .build()

UnifiedSDK.update(notificationConfig)
```

In this example:

* The title of the notification will be "VPN Connected".
* The message will show the actual download and upload speeds, which will be updated automatically as the connection changes.

These placeholders give your users more useful information about their VPN connection without needing to open the app.

### **Disabling Notifications**

If you prefer to completely disable notifications within your app, you can do so by calling the `disabled()` method on the `SdkNotificationConfig.Builder`. This can be useful if you are managing notifications in your own way or if notifications are not required for your specific use case.

To disable notifications:

```java
SdkNotificationConfig.Builder builder = SdkNotificationConfig.newBuilder();
builder = builder.disabled()
```

Once disabled, no notifications related to VPN status will be shown to the user.

## Handling Missing Notification Permissions

### Overview

Starting with Android 13 (API level 33), apps must request the `POST_NOTIFICATIONS` permission to display notifications. Additionally, Android requires proper foreground service permissions for VPN services. The SDK handles these permission scenarios gracefully to ensure VPN functionality continues even when notification permissions are not granted.

### Permission Behavior

The SDK implements the following behavior when handling notification permissions:

#### When VPN Starts

1. **If notification permissions are granted**: The SDK maintains full notification functionality as configured
2. **If notification permissions are not granted**: The SDK automatically disables the notification feature entirely without retry attempts

#### When VPN Stops and Restarts

1. The SDK makes one additional attempt to show/update notifications
2. **If permissions become available**: The SDK resumes normal notification functionality
3. **If permissions remain unavailable**: The SDK completely disables notifications until the next VPN session begins

## Analytics

SDK sends internal analytics events. They can be disabled by:

```java
UnifiedSdk.setAnalyticsEnabled(false);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://pango.gitbook.io/paas/sdk/vpn-sdk-for-android/setup/configuration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
