JavaScript Samples

The following JavaScript samples are available as examples of how you can use the JavaScript engine:

See Android Plus JavaScript Scripts for more information.
Note: Some script editors (largely those accessed from the legacy console, such as within File Sync rules, and specifically SOTI MobiControl package scripts) do not include the switch that specifies the script type. Start your script with the following shebang line: #!/usr/bin/env js to ensure the Android device agent recognizes the script as a JavaScript script.

Simple log

Demonstrates simple logging to the console.

#!/usr/bin/env js
mobicontrol.log.info('Hello world!');

Executing a simple command on the device

Enters admin mode on the device.

#!/usr/bin/env js
mobicontrol.agent.enterAdminMode();

Log real-time device information to the console

Logs device battery charge level to the console.

#!/usr/bin/env js
mobicontrol.log.info('The current battery level is: ' + mobicontrol.battery.level);

Conditioning on agent's values

Logs camera permission status.

#!/usr/bin/env js
while (mobicontrol.featureControl.camera.isDisabled()) {
    mobicontrol.featureControl.camera.allow();
}
mobicontrol.log.info('Camera feature is allowed');

if loop and string operations

Demonstrates some JavaScript standard features: if loop and string operations.

#!/usr/bin/env js
if (mobicontrol.device.name.startsWith('Headquarters')) {
    mobicontrol.featureControl.camera.disable();
}

Arrays

Demonstrates a JavaScript standard feature: arrays.

#!/usr/bin/env js
// Stop clock app if it runs in the foreground
var clockApp = 'clock';
var foregroundActivities = mobicontrol.app.foregroundActivities;
foregroundActivities.forEach(function(activity) {
    if (activity.packageName.includes(clockApp)) {
         mobicontrol.app.stop(activity.packageName);
    }
});

Simple asynchronous callback execution

Installs an app, then starts the app.

#!/usr/bin/env js
// Install an app and start it as soon as it has been installed
mobicontrol.app.install('/sdcard/example.apk', onFinish);

function onFinish(result) {
    if (result.isSuccessful) {
        mobicontrol.app.start('net.soti.example');
    }
}

Complex asynchronous callback execution

Creates an asynchronous callback including an invocation timeout parameter and failure scenario.

#!/usr/bin/env js

mobicontrol.agent.disconnect()
pause(3000)
mobicontrol.agent.connect(onFinish, 3000)

function onFinish(result) {
    if (result.isTimedOut) {
        mobicontrol.log.info('Timeout')
    } else {
        mobicontrol.log.info('Failure')
    }
    if (result.statusCode == mobicontrol.agent.ConnectResult.NO_NETWORK) {
        mobicontrol.log.info('No network')
    } else {
        mobicontrol.log.info(result.statusCode)
    }
}

function pause(numberMillis) {
    var now = new Date()
    var exitTime = now.getTime() + numberMillis
    while (true) {
       now = new Date()
       if (now.getTime() > exitTime)
           return;
    }
}

Randomize code execution over time

Randomizes device reboots over 30 minutes, so the DS (which might serve up to 100-200K devices) does not become unresponsive.

#!/usr/bin/env js
setTimeout(reboot, Math.random() * 30 * 60 * 1000);

function reboot() {
   mobicontrol.device.reboot();
}

Verify agent version

Verifies that the version of the agent is correct.

#!/usr/bin/env js
var CORRECT_VERSION = '14.3.3 Build 1234';
if (mobicontrol.agent.version != CORRECT_VERSION) {
    mobicontrol.log.error("The agent has incorrect version: " + mobicontrol.agent.version);
}

Check minimum OS version

Checks the minimum Android operating system version on a device.

#!/usr/bin/env js
var MINIMUM_ANDROID_OS_LEVEL = 26;
if (mobicontrol.os.apiLevel < MINIMUM_ANDROID_OS_LEVEL) {
    mobicontrol.log.error("Device must be upgraded to Oreo or higher");
}

Check SIM card status

Checks if a SIM card is inserted in the device.

#!/usr/bin/env js
if (mobicontrol.cellular.signalStrength == null) {
    mobicontrol.log.error("SIM card is removed.");
}

Force agent connection

Forces the agent to connect if it is not already online, and provides error messages in case the connection fails.

#!/usr/bin/env js
mobicontrol.agent.connect(onFinish);

function onFinish(result) {
    if (result.isSuccessful) {
        mobicontrol.log.info('Success');
    } else if (result.isTimedOut) {
        mobicontrol.log.info('Timeout');
    } else {
        switch (result.statusCode) {
            case mobicontrol.agent.ConnectResult.NO_NETWORK:
                mobicontrol.log.info('No available networks');
                break;
            case mobicontrol.agent.ConnectResult.SERVER_UNREACHABLE:
                mobicontrol.log.info('The agent cannot establish network connection to the deployment server');
                break;
            case mobicontrol.agent.ConnectResult.SERVER_BUSY:
                mobicontrol.log.info('The network connection to the deployment server was established, but the server didn't acknowledge connection');
                break;
            default:
                mobicontrol.log.info('Other error occurred: ' + result.statusCode);
        }
    }
}

Device property check and action

Sends a message to the screen when certain device properties are identified. Here, it is checking for signal strength and suggesting a remedy.

#!/usr/bin/env js
if (mobicontrol.cellular.signalStrength == null) {
    mobicontrol.message.createInfoDialog("Check that your SIM card is properly inserted.").show();
}

Request user acknowledgement before action

Sends a custom dialog with multiple options to allow users to control app install time. The package will be pushed to the device, and the pre-install script will check if the app is running in the foreground and if it is, it will ask the user if we can install the app now, in 5 minutes, or in 1 hour.

#!/usr/bin/env js
var FIVE_MINUTES = 5 * 60 * 1000;
var ONE_HOUR = 60 * 60 * 1000;
var buttonLabels = ['Install now', 'Install in 5 minutes', 'Install in 1 hour'];

if (isAppRunningInForeground('com.acme.importantapp')) {
    mobicontrol.message.createInfoDialog('Would you like to install ImportantApp?')
        .withButtons(buttonLabels[0], buttonLabels[1], buttonLabels[2])
        .withCallback(onConfirm).show();
}

function onConfirm(result) {
    if (result.buttonIndex != null) {
        switch (buttonLabels[result.buttonIndex]) {
            case 'Install now':
                installImportantPcg();
                break;
            case 'Install in 5 minutes':
                setTimeout(installImportantPcg, FIVE_MINUTES);
                break;
            case 'Install in 1 hour':
                setTimeout(installImportantPcg, ONE_HOUR);
                break;
         }
    }
}

function installImportantPcg() {
    // Do nothing - the normal return from the script will launch the package installation
}

function isAppRunningInForeground(app) {
    return mobicontrol.app.foregroundActivities.filter(activity => activity.packageName == app).length > 0;
}

Device self-service scripts

The following simple scripts could be organized as icons users could click to perform self-help actions that are based on condtional logic:

Restarts the agent if the battery is charging.

#!/usr/bin/env js
if (mobicontrol.battery.isCharging) {
    mobicontrol.agent.restart();
}

Performs a check-in if the device is not currently roaming.

#!/usr/bin/env js
if (!mobicontrol.cellullar.isRoaming) {
    mobicontrol.agent.checkIn();
}

Reboots the device if the clock app is not running in the foreground.

#!/usr/bin/env js
if (isAppRunningInForeground('com.android.deskclock')) {
    mobicontrol.device.reboot()
}

function isAppRunningInForeground(app) {
    return mobicontrol.app.foregroundActivities.filter(activity => activity.packageName == app).length > 0;
}

Closes an app if a low memory state is detected.

#!/usr/bin/env js
var ONE_HUNDRED_KB = 100 * 1024;
if (mobicontrol.memory.availableSpace < ONE_HUNDRED_KB) {
    mobicontrol.app.stop('com.example');
}

Device compliance support

Promotes device compliance by running periodic checks to ensure the device is operating within accepted parameters. In this case, a persistent pop-up window is displayed for user action if the device battery is low.

#!/usr/bin/env js
if (mobicontrol.battery.level < 0.20 && !mobicontrol.battery.isCharging) {
    mobicontrol.message.createWarnDialog('The battery is low, charge your device!').show();
}

Package deployment support

Controls the flow and execution of package installation. In this case, installation is aborted on devices running earlier than Android Oreo OS.

#!/usr/bin/env js
if (mobicontrol.os.level < 26) {
    mobicontrol.log.warn('Aborting package installation, as device has below-Oreo OS');
    throw(mobicontrol.packages.Termination.ABORTED);
}

Device relocation by custom data, attribute, or properties

Controls the deployment progress or ensures the proper configurations are pushed based on certain values. The following examples feature SSID and geofencing scenarios.

If a device's SSID is abc, move the device to ABC folder (device group). To set this up, perform the following steps:

  1. Create the ABC folder (device group) and configure it with a certain payload (for example, Disable Camera) and INI custom data ssid.ini, with section info and key ssidIsAbc.
  2. Create a relocation rule from the original folder to the ABC folder, when ssidIsAbc becomes true.
  3. Create a relocation rule from ABC to the original folder, when ssidIsAbc becomes false.
  4. Run the following JavaScript on schedule to ensure that when the device connects and disconnects to/from abc SSID - the device is successfully relocated:
#!/usr/bin/env js
var ini = require('ini');

var iniFile = new mobicontrol.io.File(mobicontrol.storage.internal.dataDirectory + '/ssid.ini');
var config = ini.parse(iniFile.readText());
var currentSsidIsAbc = mobicontrol.network.ssid == 'abc';
if (config.info.ssidIsAbc != currentSsidIsAbc) {
    config.info.ssidIsAbc = currentSsidIsAbc;
    iniFile.writeText(ini.stringify(config));
    mobicontrol.agent.checkIn();
}

If device with a name starting with 'Headquarters' enters geofence abc, move the device to the ABC folder. To set this up, perform the following steps:

  1. Create the ABC folder (device group), and configure it with a certain payload (e.g Disable Camera) and XML custom data geofence.xml, with the name isInAbc and XPath expression /isInAbc.
  2. Create a relocation rule from the original folder to the ABC folder, when isInAbc becomes true.
  3. Create a relocation rule from ABC to the original folder, when isInAbc becomes false.
  4. Create a rule for entering the abc geofence with the following script:
    #!/usr/bin/env js
    if (mobicontrol.device.name.startsWith('Headquarters')) {
        var xmlFile = new mobicontrol.io.File(mobicontrol.storage.internal.dataDirectory + '/geofence.xml');
        var config = new XML(xmlFile.readText());
        config.isInAbc = true;
        xmlFile.writeText(config.toString());
        mobicontrol.agent.checkIn();
    }
  5. Create a rule for exiting abc geofence with the same script as above, but with false instead of true.
  6. Ensure that when device enters or exits the abc geofence it is successfully relocated.

Here is the same script with some custom troubleshooting code:

#!/usr/bin/env js
if (mobicontrol.device.name.startsWith('Headquarters')) {
    try {
        var xmlFile = new mobicontrol.io.File(mobicontrol.storage.internal.dataDirectory + '/geofence.xml');
        var config = new XML(xmlFile.readText());
        config.isInAbc = true;
        xmlFile.writeText(config.toString());
        mobicontrol.agent.checkIn();
    } catch(err) {
        if (err.statusCode == mobicontrol.io.IoError.FILE_NOT_FOUND) {
            mobicontrol.log.err("XML file is missing");
        } else if (err.statusCode == mobicontrol.io.IoError.NOT_ACCESSIBLE) {
            mobicontrol.log.err("XML file is not accessible");
        }
    }
}

Upgrade application if it is not runnning in the foreground

Installs an application if it is not currently running in the foreground. The device battery must also be sufficiently charged and currently charging before the upgrade can begin. When installation is complete, the agent automatically checks in so that the WebConsole immediately reflects the change.

#!/usr/bin/env js

function isBatteryFullOrCharging() {
    return mobicontrol.battery.level > 0.75 || mobicontrol.battery.isCharging;
}

function isForegroundApp(app) {
    return mobicontrol.app.foregroundActivities.some(function(activity) {
        return activity.packageName == app;
    });
}

if (!isForegroundApp('com.example.bundleid') && isBatteryFullOrCharging()) {
    mobicontrol.app.install('sdcard/PathTo/ExampleApp.apk', onFinish);
} else {
    mobicontrol.log.info('Either Secure web is running or the battery is low and not charging')
}

function onFinish(result) {
    if (result.isSuccessful) {
        mobicontrol.agent.checkIn();
        mobicontrol.log.info('Application ' + result.apkFileName + ' successfully installed');
    } else {
        mobicontrol.log.error('Application ' + result.apkFileName + ' installation failure: ' + result.statusCode);
    }
}