# Welcome to Pushlink Docs

If you have any doubts or questions, please let us know at support\@pushlink.com


# Getting Started

Get up and running with Pushlink in 6 small steps

{% hint style="info" %}
Pushlink is currently working for all Android versions. Tested up to the **Android 14 - API 33**. See the [changelog](/changelog) to check compatibility.
{% endhint %}

## 1 Create an Account to obtain an API Key

Visit <https://www.pushlink.com/signup.xhtml> and create your free account.&#x20;

You'll be redirected to the dashboard. Copy your API key from the navigation bar.

## 2 Import Pushlink in your Android Project

### Option #1: Gradle

```groovy
...
  repositories {
    google()
    mavenCentral()
    maven { url "https://jitpack.io" }
  }
...
```

```groovy
//project/app/build.gradle
dependencies {
  implementation 'com.pushlink:pushlink-android:5.6.0'
  ...
}
```

### Option #2: Maven

```xml
<repositories>
  <repository>
    <id>jitpack.io</id>
    <url>https://jitpack.io</url>
  </repository>
</repositories>

<dependency>
  <groupId>com.pushlink</groupId>
  <artifactId>pushlink-android</artifactId>
  <version>5.6.0</version>
</dependency>
```

### Option #3: Download JAR File

[Download pushlink-android-5.6.0.jar ](https://jitpack.io/com/pushlink/pushlink-android/5.6.0/pushlink-android-5.6.0.jar)[\[s3 mirror\]](https://s3.amazonaws.com/bin.pushlink.com/pushlink-android-5.6.0.jar)

## 3 Set up permissions in your AndroidManifest.xml

```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application android:name=".PushlinkSetup" ... >
  <uses-library android:name="org.apache.http.legacy" android:required="false"/>
  <provider 
    android:name="com.pushlink.android.FileProvider" 
    android:authorities="your.package.name" <--- change it ---
    android:exported="true" />
</application>
```

## 4 Call `PushLink.start()` in the Main UI Thread

```java
public class PushlinkSetup extends android.app.Application {
  @Override
  public void onCreate() {
    super.onCreate();
    String yourDeviceID = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
    PushLink.start(this, R.mipmap.ic_launcher, "yourApiKey", yourDeviceID);
  }
}
```

{% hint style="warning" %}
**Don't call the start method inside an ordinary Activity**

Call this inside the class that extends `android.app.Application` precisely, as shown above.&#x20;
{% endhint %}

{% hint style="info" %}
**It's up to you to identify the device**

Make sure you've read the [Android Device ID Guide](/android-device-id-guide).
{% endhint %}

## 5 Build the APK and upload it to Pushlink

You can find the Upload APK button in the menu bar at the Pushlink dashboard.

{% hint style="danger" %}
**Don't forget, or your installation will fail:**

1 - Upload a fully signed release APK to Pushlink.

2 - Increment the [versionCode](/downgrade-an-apk)

3 - Give your app installation and notification permissions.

4 - Adapt your app to the [newer Android versions](/android-versions)
{% endhint %}

## 6 Enroll your devices (Install & run)

First installation options:

### Option #1: From the terminal

A tech person usually performs this before sending the device to the field.

```bash
curl -o yourApp.apk 'https://www.pushlink.com/download?package=some.package.name&api_key=yourApiKey'
adb install yourApp.apk
```

{% hint style="info" %}
You can also generate a **hashed download link** in the dashboard to hide your credentials.
{% endhint %}

### Option #2: From the device itself

Download the APK from **QRCode** and install it.

![](/files/-LLyeGHr1h8NyOusKLSV)

## Final words

That's it. Now, every time you upload a new app version to Pushlink and make that the current version, all the devices using your application will receive and apply the update.&#x20;

Look at all the documentation to understand how Pushlink can help you.


# Concerned about security?

Pushlink is meant for enterprise applications distribution and updates. Security is one of the main features from the beginning.

* APK traffic always occurs over SSL (download/upload).
* We use a renowned host service.
* APKs are safely stored and encrypted.
* Daily backups are performed on extra cloud-based servers.
* The APK will never be published or installed by others.

*"Pushlink has been in production since 2011, and we never missed a single update" - Célio Vasconcelos (CEO)*


# Update Strategies

Choose your update strategy according to your business model

## Status Bar (default)

![](/files/-LG6o8JX4Jt1h57j7cFX)

* `PushLink.setCurrentStrategy(StrategyEnum.STATUS_BAR);`
* Title message: "New version of \[App Name]"
* Description message: "Click to Update"
* Remains on the status bar until the installation happens.
* Clear the message if the admin changes back to an earlier version before the installation.

{% hint style="warning" %}
Check if your application has requested permission for notification or if you manually allowed it in Android settings.
{% endhint %}

## Friendly Popup (useful for full-screen apps)

![](/files/-LG6p85ZaWw3U_pdKHjY)

* `PushLink.setCurrentStrategy(StrategyEnum.FRIENDLY_POPUP);`
* Default message: "New version of \[App Name]. Do you want to update it?".
* Default buttons: \[Not Now]\[Update].
* It remains until the user presses \[Not Now] or \[Update] (The back button is blocked).
* Default reminder: 3 hours
* Hide the popup if the admin changes back to an earlier version before the installation.

{% hint style="info" %}
Don't forget to call `PushLink.setCurrentActivity(this);` inside `onResume`
{% endhint %}

```java
//Only for popups (FRIENDLY_POPUP or ANNOYING_POPUP)
//You MUST do this, otherwise popups will not work.
//Call it in the Activity you want to show the popup.
//You can show the popup in many screens by adding this in more than one Activity.
@Override
protected void onResume() {
  super.onResume();
  PushLink.setCurrentActivity(this);
}
```

## Annoying Popup (useful for full-screen when updates can't wait)

![](/files/-LG6pp5rP9vxQY-Ev7rz)

* `PushLink.setCurrentStrategy(StrategyEnum.ANNOYING_POPUP);`
* Default message: "For security reasons \[App Name] needs to be updated!"
* Default button: \[Update].
* Very insistent. It remains until the user presses \[Update] (The dismiss button is blocked).
* Hide the popup if the admin changes back to an earlier version before the installation.

{% hint style="info" %}
Don't forget to call `PushLink.setCurrentActivity(this);` inside `onResume`
{% endhint %}

```java
//Only for popups (FRIENDLY_POPUP or ANNOYING_POPUP)
//You MUST do this, otherwise popups will not work.
//Call it in the Activity you want to show the popup.
//You can show the popup in many screens by adding this in more than one Activity.
@Override
protected void onResume() {
  super.onResume();
  PushLink.setCurrentActivity(this);
}
```

## Custom (take complete control of your installation)

* `PushLink.setCurrentStrategy(StrategyEnum.CUSTOM);`
* When using the CUSTOM strategy, pushlink doesn't display anything and will broadcast two actions.
* `[your.package.name].pushlink.APPLY` is sent EVERY 30 seconds (more or less) when there is a downloaded but not applied new APK. The user has to handle those multiple broadcasts.
* `[your.package.name].pushlink.GIVEUP` is sent ONCE when the user gives up the update (in the web admin) before it has been applied.

{% hint style="info" %}
It is useful when the requirement is building a fancier and more modern notification, executing extra steps, or performing a silent background update.
{% endhint %}

```java
registerReceiver(new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
    Uri apkUri = (Uri) intent.getExtras().get("uri");
    //enjoy the apk uri
    //notice this will be called every 30s (more or less). You need to handle this.
  }
}, new IntentFilter(getPackageName() + ".pushlink.APPLY"));
```

{% hint style="warning" %}
Do NOT register this receiver in AndroidManifest.xml. If you do that, CUSTOM will not work in **Android Oreo.**

<https://developer.android.com/about/versions/oreo/background>
{% endhint %}

## Background

* Installing APK without user interaction
* Silent updates
* Zero-touch updates
* KIOSK apps
* POS - Point of sale apps
* Dedicated devices
* COSU - Corporate Owned Single Use devices

![](/files/-LG6qLKcqFr3_ZSSOxUs)

[**Learn how to update apps in the background with CUSTOM strategy.**](https://github.com/pushlink/background-device-owner)

## Idle

Sometimes, you want to avoid undesired popups, notifications, or restarts during user interaction.

By default `idle` is `true` . This means the installation will start after the new APK download.

If you call `PushLink.idle(false)` , Pushlink will never fire the installation process.

Knowing that you can switch this value to control updates.

{% hint style="danger" %}
**Manually setting up this property can be dangerous**. If, for some reason, the program doesn’t set `idle` back to `true`Pushlink will lose the ability to update the app, and you’ll need to update the device manually.
{% endhint %}

## Customization

You can customize the built-in strategies like this:

```java
//If you use StatusBarStrategy
StatusBarStrategy sbs =  (StatusBarStrategy) PushLink.getCurrentStrategy();
sbs.setStatusBarTitle("Hello, there is a new version");
sbs.setStatusBarDescription("Click to be happy");

//If you use FriendlyPopUpStrategy
FriendlyPopUpStrategy fps =  (FriendlyPopUpStrategy) PushLink.getCurrentStrategy();
fps.setPopUpMessage("New version available");
fps.setNotNowButton("No, thanks!");
fps.setUpdateButton("Ok, lets go");
fps.setReminderTimeInSeconds(60 * 60); //one hour

//If you use AnnoyingPopUpStrategy
AnnoyingPopUpStrategy fps =  (AnnoyingPopUpStrategy) PushLink.getCurrentStrategy();
fps.setPopUpMessage("New version available");
fps.setUpdateButton("Ok, lets go");

```


# Android Device Id Guide

Learn how to create a good device identifier

## Pushlink Start

As you may have noticed, the last Pushlink.start parameter is "**yourDeviceId**".

```java
PushLink.start(this, R.mipmap.ic_launcher, "yourApiKey", "yourDeviceID");
```

It's up to you to identify the device.

## **Disclaimer**

There is no default and 100% guaranteed way to retrieve the Device ID on Android. It depends on the OS version, device brand, etc. That's why Pushlink externalizes the id generation.

However, Pushlink is designed for enterprise apps, and the developers usually control which types of Devices and OS the application will run. Don't use an ID that is changed after every uninstall/install. Try to use a long-live ID. Think and create your strategy.

## Must read

* <https://developer.android.com/training/articles/user-data-ids.html>
* <https://proandroiddev.com/how-to-generate-android-unique-id-38362794e1a8>
* <http://stackoverflow.com/questions/2785485/is-there-a-unique-android-device-id>

## **What should I be alert?**

Be careful with non-certified ROMs.

* Fake IMEI like '004999010640000'.
* Fake Settings.Secure.ANDROID\_ID like '9774d56d682e549c'.

Be careful with Android releases. They frequently change permissions and behaviors related to device identifiers.

## **What happens if I set the same ID on different devices?**

Don't do that. You will not be able to manage your installations properly. This will create all sorts of problems.

## **What happens if my device ID changes frequently?**

One real-life installation will be registered many times, giving you an unreal number of managed devices.


# Metadata

Identify your devices in the best way adding tags to them

Add some metadata to identify the device/user:

```java
//This information will be shown in two places: "Installations" and "Exceptions" tabs of the web administration 
PushLink.addMetadata("Ip", getIp());
PushLink.addMetadata("Phone Number", getPhoneNumber());
PushLink.addMetadata("Brand", Build.BRAND);
PushLink.addMetadata("Model", Build.MODEL);
PushLink.addMetadata("OS Version", Build.VERSION.RELEASE);
PushLink.addMetadata("Logged in user", getUserName());
...

//This information will be shown ONLY in the "Exceptions" tab of the web administration 
PushLink.addExceptionMetadata("Battery Level", getBatteryLevel());
PushLink.addExceptionMetadata("Network Status", getNetworkStatus());
...
```


# Exception Notification

Be aware of your application exceptions

1. By default, Pushlink catches all unhandled exceptions in your app and doesn't block the natural life cycle of exceptions.
2. You can also send handled exceptions calling: `PushLink.sendAsyncException()`.
3. Pushlink always sends the exceptions asynchronously and guarantees its delivery.
4. If you also want to get the last 100 LogCat's lines, just add `android.permission.READ_LOGS` in AndroidManifest.xml

## Sending Limit

* Limits include handled and unhandled exceptions.
* PushLink was not designed to be a remote log system. Try not sending network issues or something like that.
* Each app has its sending limit.
* It's calculated using the last 24H, and it isn't cumulative.
* Formula: `sendingLimit == planQuantity * 2`
* Ex1: free plan (5 installations) can send 10 exceptions in the last 24H for each app.
* Ex2: 200 installations can send 400 exceptions in the last 24H for each app.

## Status

The messages you are going to see in the Android log system:

* REGISTERED
  * Exception sent.
* QUEUED
  * Exception not sent but queued due to: java.lang.Exception: Any reason.
* LOST
  * Exception sent but rejected: Invalid API Key. Probably caused by the wrong configuration.
  * Exception sent but rejected: Invalid Package. You probably never uploaded this app.
  * Exception sent but rejected: Invalid APK Hash. Probably you are in a development environment. You did not upload this version yet.
  * Exception sent but rejected: The application \[app name] has exceeded the 24H sending limit.

```java
//When sending exception manually, PushLink doesn't print it in logcat. You have to print it by yourself. 
try {
  ...
} catch (Throwable t) {
  Log.e("MyApp", "Some", t);
  PushLink.sendAsyncException(t);
  //or
  Map<String, String> moreInfo = ... 
  PushLink.sendAsyncException(t, moreInfo);
}
...
//You can also disable the automatic exception catch/send
PushLink.disableExceptionNotification();
...
//To receive LogCat last ones 100 lines just add <uses-permission android:name="android.permission.READ_LOGS" />
```


# Pushlink APIs

Simple but powerful API

## Metadata API

JSON output

```http
https://adm.pushlink.com/apps/api_list_json?packageName=some.package.name&apiKey=yourApiKey

#optional query string param --> &deviceId=mydeviceID
```

## Upload API

After uploading, you can manually set up the APK as current in the web administration

```bash
curl -F "apiKey=yourApiKey" -F "apk=@yourApp.apk" https://adm.pushlink.com/apps/api_upload
```

There are two optional parameters:

```bash
-F "current=true" -F "whatsnew=this is the fanciest release" 
```

### Raw text responses

> We understand the response should be better. In the next version of the API, the response will be JSON with error codes, and better English will be used in the text description.

* PushLink deploy sucessful.
* PushLink deploy fails. Invalid Api Key.
* PushLink deploy fails. Invalid APK file.
* PushLink deploy fails. This APK hash is the same of another APK you have uploaded.

## Download API

Download the current apk

```bash
curl -o yourApp.apk 'https://adm.pushlink.com/download?package=some.package.name&api_key=yourApiKey'
```

{% hint style="info" %}
You can also generate a **hashed download link** in the dashboard to hide your credentials.
{% endhint %}

![](/files/-LLyeGHr1h8NyOusKLSV)


# Integrations

.Net, Cordova and CI

## Official

Supported by Pushlink team.

### .Net

```csharp
using Com.Pushlink.Android;
...
PushLink.Start(this, Resource.Drawable.Icon, "yourApiKey", "yourDeviceID");
```

* [Download our official DLL wrapper](https://s3.amazonaws.com/bin.pushlink.com/PushLinkMono-5.6.0.dll)
* [Sample application project](https://github.com/pushlink/pushlink-mono-sample)
* [Java binding project](https://github.com/pushlink/pushlink-mono)

{% hint style="info" %}
**Updating the lib**

&#x20;After downloading a new version, rename it from `PushLinkMono-5.x.x.dll` to `PushLinkMono.dll` before adding it as a .Net Assembly reference.
{% endhint %}

### Cordova

```javascript
//this new start API was introduced in pushlink-cordova-6.0.1

var successCallback = function success() { console.log('PushLink started successfully'); };
var errorCallback = function error() { console.log('An error occurred.'); };

PushLink.start({
  apiKey: 'api-key',
  deviceId: 'device-id',
  successCallback: successCallback,
  errorCallback: errorCallback
});
```

[Pushlink official Cordova plugin on GitHub](https://github.com/pushlink/pushlink-cordova-plugin)

## Community

{% hint style="danger" %}
We don't provide support to community plugins.
{% endhint %}

### Flutter

<https://github.com/diogo-bruno/flutter-push-link> (Community)

### React Native

<https://github.com/diogo-bruno/react-native-push-link> (Community)

### Gradle

{% hint style="info" %}
Useful for CI/CD
{% endhint %}

<https://github.com/PuzzelSolutions/gradle-pushlink-plugin> (Community)


# Android changes

Stay aware of the new requirements as the Android platform evolves.

## Android 7 (Nougat)

Pushlink supports Android N `Build.VERSION.SDK_INT <= 24` since version **5.5.2** released in 2016.11.11.\
\
&#x20;It requires a FileProvider in the AndroidManifest.xml; otherwise, Pushlink will not work.

```xml
<provider 
  android:name="com.pushlink.android.FileProvider" 
  android:authorities="your.package.name" 
  android:exported="true" />
```

Don't forget to replace `your.package.name` it with your current application package name.

## Android 8 (Oreo)

Pushlink supports Android 8 `Build.VERSION.SDK_INT <= 26` since version **5.5.3** released in 2018.05.19.\
\
&#x20;It requires new permission in the AndroidManifest.xml; otherwise, Pushlink will not work.

```xml
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
```

{% hint style="info" %}
**Unknown sources**

&#x20;Note that app installations from unknown sources are disabled by default in Android Oreo. You have to authorize the specific source on the app's first installation. See: <https://developer.android.com/studio/publish/#publishing-unknown>
{% endhint %}

## Android 9 (Pie)

Pushlink **5.5.3** supports Android 9, but from now on, it requires to enable `org.apache.http.legacy`

```xml
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
```

More info: <https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p>

## Android 10

Pushlink **5.5.3** supports Android 10, and there are no tricks here.

## Android 11

Pushlink **5.5.3** supports Android 11. Don't forget to allow a V2 signature when building the APK.

<div align="left"><img src="/files/-MY6C9oN8Z9iZXuMAqli" alt=""></div>

If you forget this, the installation will fail with the following message:&#x20;

`com.android.server.pm.PackageManagerException: No signature found in package of version 2 or newer for package...`

## Android 12, 13 and 14

Pushlink supports Android 12, 13 and 14 `Build.VERSION.SDK_INT <= 34` since version **5.6.0** released in 2024.07.22.

It now requires the post notification permission.

```xml
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
```


# Network problems?

Sometimes you need to pass through a firewall

It's common to run Pushlink behind a firewall in a LAN environment. See the names we use:

* push.pushlink.com:443
* [www.pushlink.com:443](http://www.pushlink.com:443)


# Downgrade an APK?

Downgrading isn't allowed since Jelly Bean

Android has changed its behavior when installing a new APK file. Until ICS (ice cream sandwich), it didn't matter if the new APK had a lower or higher **android:versionCode** in AndroidManifest.xml. After that, it's mandatory a higher versionCode. We can't guarantee "when" the new behavior was introduced, but we noticed it was sometime around **Jelly Bean**.\
\
When trying to install a lower version code through ADB, you will get the message: INSTALL\_FAILED\_VERSION\_DOWNGRADE. In this case, you can force with the undocumented -d flag: **adb install -r -d my.apk**. Pushlink can't force it because it just launches the APK and the control is passed to the OS. So, the installation process will fail with no specific error message.\
\
&#x20;Remember that Pushlink can't perform downgrades when the OS does not allow it.


# Changelog

Know about Pushlink changes

**PushLinkAndroid-5.6.0.jar 2024.07.22**

* Now it works on Android 12, 13, and 14 \[Build.VERSION.SDK\_INT <= 34]. It requires new permission `<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />` in AndroidManifest.xml
* The NINJA strategy was removed
* [Fixed Path Traversal Vulnerability](https://support.google.com/faqs/answer/7496913)
* Pushlink.hasPengingUpdate with wrong spelling was removed.

**PushLinkAndroid-5.5.3.jar 2018.05.19**

* Now it works on Android 8 (Oreo) \[Build.VERSION.SDK\_INT <= 26]. It requires new permission `<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />` in AndroidManifest.xml

**PushLinkAndroid-5.5.2.jar 2016.11.11**

* Now it works on Android 7 (Nougat) \[Build.VERSION.SDK\_INT <= 24]. It requires `<provider android:name="com.pushlink.android.FileProvider" android:authorities="your.package.name" android:exported="true" />` in AndroidManifest.xml
* Now pushlink cleanup old downloaded apks.
* The new CUSTOM strategy is available. ([**NINJA is deprecated**](https://pushlink.gitbook.io/docs/strategies#background)**)**

**PushLinkAndroid-5.5.0.jar 2016.04.18**

* Fixed ninja NPE when using broadcast receiver

**PushLinkAndroid-5.4.5.jar 2016.03.13**

* Ninja fallback. (If the broadcast receiver is not present, it uses the classic ninja.)
* Commons network exceptions are now in debug level in logcat

**PushLinkAndroid-5.4.4.jar 2016.03.01**

* ~~Ninja now works in Android 5 or higher. Its requires the `<receiver android:name="com.pushlink.android.NinjaReceiver" />` in AndroidManifest.xml~~
  * **Wrong judgment. Ninja does not work in** [**Android 5**](https://pushlink.gitbook.io/docs/strategies#background)**.**
* `Ninja.setExtraShellCommand()` deprecated in favor of `Pushlink.setBeforeNinjaTaskShellCommand()`
* Available from Maven and Gradle by jitpack

**PushLinkAndroid-5.4.2.jar 2015.12.03**

* Bugfix: `Pushlink.hasPendingUpdate()` does not ignore the idle status anymore (Pushlink.idle method)

**PushLinkAndroid-5.4.1.jar 2015.11.25**

* Now Ninja is working in Android 64 bits
* Ninja does not execute the `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/system/lib` command anymore
* New method `Ninja.setExtraShellCommand()` for custom workarounds
* Now Pushlink logs the entire stack trace. (No more clipping)

**PushLinkAndroid-5.4.0.jar 2015.09.30**

* Fixed critical bug when using android:process
* New api method `Pushlink.setMaxExceptionQueueSize()` (to avoid huge cache)

**PushLinkAndroid-5.3.0.jar 2015.04.02**

* Download resume. (Very welcome!)
* Bugfix that would send old exception assigned with wrong version label.
* Bugfix that in `Pushlink.addMetadata()` and `Pushlink.addExceptionMetadata()` when key == null. Not allowed anymore.
* Now it prevents sending the very same exception twice or more.
* Less data transfer when using the metadata api.
* Now `Pushlink.setCurrentActivity()` and `Pushlink.setCurrentStrategy()` are independent of the start method.
* Some timeout adjustments

**PushLinkAndroid-5.1.0.jar 2013.12.14**

* NINJA strategy improved. Now working on a large variety of devices.
* Now PushLink uses only the 443 port. Goodbye 6670 port.

**PushLinkAndroid-5.0.4.jar 2013.11.12**

* Download method improved to avoid memory overflow.
* Popup strategies no more play notification sound.
* Now popup strategies ask for launching the app even in ICS or later.

**PushLinkAndroid-5.0.3.jar 2013.07.31**

* Now it sends the last 200 lines of logcat. (no more 100)
* From now on exceptions have a handled/unhandled label
* New method `PushLink.addExceptionMetadata()`. This metadata is going to appear ONLY in the "Exceptions" tab of the web administration

**PushLinkAndroid-5.0.2.jar 2013.06.17**

* Important bug fix. PushLink.addMetadata wasn't working fine.
* TCP client improvement. It was aborting connections sometimes.
* Installation checking for duplicated ID's improved.
* Method name changed! `PushLink.setCurrentPopUpTarget()` is now `PushLink.setCurrentActivity()`.
* Now PushLink grabs screenshots for unhandled exceptions.

**PushLinkAndroid-5.0.1.jar 2013.02.21**

* Logging improvements
* Bugfix. Calling `PushLink.setCurrentStrategy()` before `PushLink.start()` had no effect.

**PushLinkAndroid-5.0.0.jar 2013.02.06**

* Package change. Now all classes are under "com.pushlink.android"
* Critical bug fix. The heartbeat was not working when the devices were sleeping.
* The Device ID is back. Now the user MUST set the device id. Now the web administration shows off-line installations again.
* Api change to support device ID: Now `PushLink.start()` has a brand new fourth parameter. The device ID.
* Method name changed from `PushLink.sendAsyncNotification()` to `PushLink.sendAsyncException()`.
* Pushlink now has two lightweight threads. They were named  "PushLink-PushListener" and "PushLink-Heartbeat".

**PushLinkAndroid-4.2.1.jar 2012.12.25**

* Huge logging improvements

**PushLinkAndroid-4.2.0.jar 2012.12.17**

* Critical bug fix. Low socket/connection timeout was killing some APK downloads.
* Now, exceptions are always sent lazily with a delivery guarantee. PushLink holds the exception (sqlite persistence) until it can be sent to the server. `PushLink.sendSyncException()` was removed.
* New method `PushLink.hasPengingUpdate()`. This method checks if there is a downloaded but not applied update. It also notifies the user again.
* Log messages improved

**PushLinkAndroid-4.1.1.jar 2012.11.12**

* `PushLink.sendExceptionNotification()` has changed to `PushLink.sendAsyncException()` and `PushLink.sendSyncException()`
* Fixed NPE when `Pushlink.setCurrentPopUpTarget()` called before `PushLink.start()`
* When popup strategies were configured before PushLink.start, the popup title would appear "Application" instead of the real application name. Fixed!
* Log messages improved

**PushLinkAndroid-4.1.0.jar 2012.09.26**

* Lighter. HttpClient code improvement. Fewer data transferred between server and client.

**PushLinkAndroid-4.0.0.jar 2012.09.14**

* Popups strategies now support screen rotation
* It is ready to show online installations in the web administration
* Fixed some minor bugs that could happen in push notification
* `PushLink.addGlobalInfo()` has changed to `PushLink.addMetadata()`

**PushLinkAndroid-3.1.3.jar 2012.07.27**

* Fixed an NPE that would appear in some cases
* Fixed some confusing log information

**PushLinkAndroid-3.1.2.jar 2012.06.06**

* API method `PushLink.idle(boolean)` added. Especially useful for NINJA strategy to update your app only when it is idle. See the usage in JAVADOC.
* Now it sends device coordinates if the GPS is enabled. This is sent only once when the app is started.

**PushLinkAndroid-3.1.1.jar 2012.05.04**

* Bug fixed. It could happen to receive several notifications to the same version in some specific situations.

**PushLinkAndroid-3.1.0.jar 2012.04.23**

* NINJA strategy has been created. Only for rooted devices. BETA!
* Logging improvements.

**PushLinkAndroid-3.0.0.jar 2012.04.20**

* Full redesign to support premium features.
* `PushLink.checkForUpdates()` in no longer available. There is no sense in using the new push architecture.
* `PushLink.start()` method has changed. Now it has only three parameters
* Notification now play standard device sound
* Automatic exception catch/send a notification is the default.
* Added two methods. `PushLink.disableExceptionNotification()` and `PushLink.enableExceptionNotification`

**PushLinkAndroid-2.0.4.jar 2012.03.07**

* `Push.addUserInfoForExceptionNotification()` is now `Push.addGlobalInfo`. This information is going to appear in all exception notifications sent.
* `Push.sendExceptionNotification(Throable)` was overloaded with `Push.sendExceptionNotification(Throwable, Map)`. Now it's possible to send more information about the exception context in Map.
* Professional JAVADOC added

**PushLinkAndroid-2.0.3.jar 2012.03.04**

* `Push.requestUpdateRightNow` is now P`ush.checkForUpdates`
* Now `Push.checkForUpdates()` does not block the thread anymore.
* `Push.checkForUpdates` bug fix. It was rushing the download but not the notification.

**PushLinkAndroid-2.0.2.jar 2012.02.20**

* Now if exceptionNotification is set to false and you call `PushLink.sendExceptionNotification()` it will not throw an exception anymore. It only will log: "You must start pushlink enabling exception notification before using PushLink.sendExceptionNotification or PushLink.addUserInfoForExceptionNotification. Check PushLink.start method exceptionNotification parameter."

**PushLinkAndroid-2.0.1.jar 2012.02.01**

* Exception notification now sends LogCat (Need READ\_LOGS permission)
* Exception notification now sends the whole stack trace
* Fixed some exception notification bugs

**PushLinkAndroid-2.0.0.jar 2012.01.20**

* API Redesigned. All of PushLink's methods are static.
* There is no method `Pushlink.stop()` anymore.
* Now we have exception notification! Receive emails when your app crashes.
* New API method `Pushlink.requestUpdateRightNow()` to rush version check.

**PushLinkAndroid-1.1.3.jar 2011.12.01**

* Now popups strategies work nicely with `PushLink.setCurrentPopUpTarget()`

**PushLinkAndroid-1.1.2.jar 2011.11.22**

* Fixed java.lang.NoClassDefFoundError: com.pushlink.android.SSLHandler when runnig on ADT emulator

**PushLinkAndroid-1.1.1.jar 2011.11.17**

* Pop Up strategy support
* i18n support

**PushLinkAndroid-1.1.0.jar 2011.11.03**

* SSL Support
* Internal storage bug fix

**PushLinkAndroid-1.0.0.jar 2011.10.28**

* Beta test


# FAQ

Check the most frequent doubts and questions

## **Why am I getting the toast message&#x20;*****'Developer warning for package ... Failed to post notification on channel...'*****&#x20;?**

You are using the old pushlink version with [Android 8](/android-versions#android-8-oreo) which requires `>= 5.5.3`.

## **Why am I getting&#x20;*****'There was a problem parsing the package'*****&#x20;after initiating the installation process?**

List of possibilities:

* &#x20;You are using the old pushlink version with [Android 7](/android-versions#android-7-nougat) which requires `>= 5.5.2` + `FileProvider` In this case, you will see the error 'java.lang.SecurityException: MODE\_WORLD\_READABLE no longer supported' in logcat.
* &#x20;You are generating `testOnly` APK's. Take a look at [Android Studio 3.0 and FLAG\_TEST\_ONLY](https://commonsware.com/blog/2017/10/31/android-studio-3p0-flag-test-only.html).
* &#x20;You forgot to bump the [version code](/downgrade-an-apk).
* &#x20;You forgot to check the `V2 (Full APK Signature)` in Android Studio release process wizard.

## **Why am I getting&#x20;*****'PushLink-PushListener --> javax.net.ssl.SSLHandshakeException: com.android.org.bouncycastle.jce.exception...'*****&#x20;in logcat?**

Maybe your device clock is too late or ahead.

## **Why am I getting&#x20;*****'android.view\.WindowManager$BadTokenException: Unable to add window -- token null is not for an application'*****&#x20;in logcat?**

Maybe you forgot to call PushLink.setCurrentActivity when using popup strategies.

## **Why am I getting&#x20;*****'PushLink not initialized! java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()'*****&#x20;in logcat?**

You are starting PushLink inside your own thread. Start it inside the main UI thread.

## **What does "Installation" mean?**

Installation means a single Linux/android process using PushLink. If one device runs two applications using PushLink, there are two installations.

## **What does "plan overflow" mean?**

You have reached your plan limit, and Pushlink will register the new installations as disabled from now on. Disabled means that you can see them on administration but can't send updates or receive exceptions.


