Callbacks on Android

ExtendedObserver interface

The ExtendedObserver interface contains only one method: handleCallback(CallbackData data).

The CallbackData object contains all information supplied for a given callback.

CallbackData class

The CallbackData class serves as a base class for other classes that contain data for specific callbacks. These inherited classes reside in the no.promon.shield.callbacks package and have a ...Data suffix, for example RootingData or RepackagingData.

Each callback has a type, represented by enum CallbackType and is available via the CallbackData.getCallbackType() method call. Based on the return value of the CallbackData.getCallbackType() call, the callback handler can cast the CallbackData object to the correct descending type that contains the data for the callback.

If CallbackData.getCallbackType() returned CallbackType.ROOTING, the callback handler should cast the CallbackData object to the RootingData object and then, using methods of the RootingData class, retrieve information specific to the ROOTING callback.

Example code

Java

 

public class ExtendedObserverImplementation implements ExtendedObserver {
  public void handleCallback(CallbackData data) {
    switch (data.getCallbackType()) {
      case ROOTING: {
        RootingData callbackData = (RootingData) data;
        handleRootingCallback(
          callbackData.isDeviceCertainlyRooted(),
          callbackData.getRootingProbability());
        break;
      }
      case HOOKING_FRAMEWORKS: {
        HookingFrameworksData callbackData = (HookingFrameworksData) data;
        handleHookingFrameworksCallback(
          callbackData.areHookingFrameworksPresent(),
          callbackData.getLastUsedDetectionMethodCode());
        break;
      }
      ...
    }
  }
  ...
}
...
ExtendedObserver observer = new ExtendedObserverImplementation();
CallbackManager.addObserver(context, observer);

Kotlin

 

class ExtendedObserverImplementation : ExtendedObserver {
  override fun handleCallback(data: CallbackData) {
    when (data?.callbackType) {
      CallbackType.ROOTING -> {
        val callbackData = data as RootingData
        handleRootingCallback(
          callbackData.isDeviceCertainlyRooted,
          callbackData.rootingProbability
        )
      }
      CallbackType.HOOKING_FRAMEWORKS -> {
        val callbackData = data as HookingFrameworksData
        handleHookingFrameworksCallback(
          callbackData.areHookingFrameworksPresent()
          callbackData.getLastUsedDetectionMethodCode()
        )
      }
      ...
      else -> {
        ...
      }
    }
  }
  ...
}
...
val observer: ExtendedObserver = ExtendedObserverImplementation()
CallbackManager.addObserver (context, observer)

List of callback types: CallbackType enum

A list of callback types can be found in the CallbackType enum. Each callback type corresponds to its own callback data class, which extends the CallbackData class.

Callback types shows which callback type corresponds to which callback data class:

Callback types
Callback method Description Depends on enabling
ADB_STATUS AdbStatusData

check ADB Status

DEBUGGER DebuggerData Check Debugger
DEVELOPER_OPTIONS DeveloperOptionsData check Developer Options
EMULATED_INPUT EmulatedInputData Check Emulated Input
EMULATOR EmulatorData Check Emulator
FILESYSTEM_SCANNING FilesystemScanningData

Check RootingDeepScan, checkRooting

FILESYSTEM_WATCHING FilesystemWatchingData checkRooting
HOOKING_FRAMEWORKS HookingFrameworksData Check HookingFrameworks
KEYBOARD KeyboardData Check Trusted Keyboard
NATIVE_CODE_HOOKS NativeCodeHooksData Check NativeCodeHooks
REPACKAGING RepackagingData Check Repackaging
ROOTING RootingData checkRooting
SCREEN_MIRRORING ScreenMirroringData check Screen Mirroring
SCREENREADER ScreenreaderData Check Trusted Screenreaders
TASK_HIJACKING TaskHijackingData N/A: requires the import of ShieldSDK-activity-guard.aar
UNTRUSTED_SOURCE_APP UntrustedSourceAppData check Untrusted Installer
VIRTUAL_SPACE_APP VirtualSpaceAppData

Check App in Virtual Space

Callback data classes

The ExtendedObserver.handleCallback() method is called after App Shielding has performed security checks, with a callback-specific CallbackData object (see Callback types).

Callback data classes
Callback method Description
AdbStatusData

This class contains information about the detected Android Developer Bridge (ADB) status. If the boolean isAdbActive() return value is true, the device’s ADB is considered active.

DebuggerData

This class contains information about whether a Java debugger is attached to the host application. If the boolean isRunningUnderDebugger() method returns true, a Java debugger is attached.

If App Shielding is configured to block Java debuggers this callback is never called. Low-level debuggers, such as gdb are blocked altogether, and no callback will be called if anyone tries to attach such a debugger to the application.

DeveloperOptionsData

This class contains information about the Developer Options status. If the return value of boolean isDeveloperOptionsEnabled() is true, the device’s Developer Options are enabled.

EmulatedInputData

This class contains information about a detected emulated input. If the booleanisInputEmulated() return value is true, an emulated input was detected. The input can be both touch and swipe events.

EmulatorData

This class contains information about whether or not App Shielding detects that the application is executed inside an emulator. If the return value of boolean isRunningOnEmulator() is true, the application is running inside an emulator.

Release versions of App Shielding do not permit executing on emulators, so this callback can only be used when using debug versions of App Shielding on an emulator.

FilesystemScanningData

This class contains information about whether suspicious files have been detected in the file system.

The following are the public methods for this class:

  • boolean isSuidOrSgidDetected
    Indicates if a file with suid or sgid bit is detected.
  • boolean isSuDetected
    Indicates if su executable file is detected.
  • boolean isSuspiciousFileDetected
    Indicates if any suspicious file is detected, either a file with an suid or sgid bit, or a file which is su executable. In other words, isSuspiciousFileDetected() returns true if isSuidOrSgidDetected() or isSuDetected() (or both) return true.

The file system scanning callback is usually invoked 5-10 seconds after application startup because file system scanning takes time.

FilesystemWatchingData

This class contains information about suspicious activity on the file system. This check depends on root detection being enabled. Currently, the following scenarios are considered suspicious activity:

  • An Suid or sgid bit is set on an executable in one of the bin paths.
  • An su executable is copied into one of the bin paths.

The following public methods are available for this class:

  • String getSuspiciousFileName

    Returns name of the suspicious file.

  • boolean isSuidOrSgidDetected

    Indicates if the suspicious file has suid or sgid bit.

  • boolean isSuDetected

    Indicates if the suspicious file is su executable.

The file system watching callback is only invoked if suspicious activity happens on the file system during application runtime. If suspicious files are already present on the file system before the application starts, their presence will be reported via the file system scanning callback, not via the file system watching callback.

HookingFrameworksData

This class contains information about code hooking frameworks in the process.

The following public methods are available for this class:

  • boolean areHookingFrameworksPresent

    Returns true if code hooking frameworks are present.

  • int getLastUsedDetectionMethodCode

    Returns the last used detection method code. Provide this code to OneSpan if you are trying to diagnose a problem with hooking framework detection.

OneSpan recommends to configure App Shielding to exit the application if code hooking frameworks are detected. This callback should not be used to make security decisions because such frameworks can modify the behavior of the callback.

KeyboardData

This class contains information about whether the active keyboard on the system is trusted.

The following public methods are available for this class:

  • boolean isKeyboardUntrusted

    Returns true if the active keyboard is untrusted.

    App Shielding treats keyboards that are part of the original firmware as trusted. Otherwise, the signer of the keyboard is compared to a list of trusted signers that can be configured in the App Shielding configuration (see Additional trusted keyboard signatures in OneSpan Customer Portal).

  • String getKeyboardPackageName

    Returns the keyboard APK package name.

  • String getKeyboardVersionName
    Returns the keyboard APK package's human-readable version name, which is usually specified in AndroidManifest.xmlby the versionName attribute of the <manifest> tag.

  • String getKeyboardAppLabel
    Returns the keyboard app's human-readable name (i.e., label), which is usually specified in AndroidManifest.xml by the label attribute of the <application> tag.

  • String getKeyboardSignerName

    Returns the Common Name (CN) of the keyboard APK signer’s X.509 certificate subject. If it is impossible to determine the CN field, this method returns an empty string.

NativeCodeHooksData

This class contains information about whether native code hooks are present. If the boolean areNativeCodeHooksPresent() return value is true, native code hooks are present. If false, no native code hooks are detected.

RepackagingData

Provides a boolean that indicates whether the application has been repackaged. If the application is running in an emulator, this method is not called. If the boolean isRepackaged() return value is true, the application has been repackaged.

This callback is only triggered if the exitOnRepackaging option is set to false. exitOnRepackaging cannot be disabled in a release version of App Shielding.

RootingData

This class contains information about whether App Shielding has detected that the device is rooted or not.

The following public methods are available for this class:

  • boolean isDeviceCertainlyRooted()

    Indicates if the device is rooted or not. Returns true if App Shielding is certain that the device is rooted.

  • int getRootingProbability()

    Returns a percentage value that indicates the probability that a device is rooted, based on heuristic checks. A value of 0 indicates that the device is most likely not rooted, while a value of 100 indicates that the device is most likely rooted. This value is created independently of the value returned by isDeviceCertainlyRooted, so they are not connected in any way and might contradict each other in certain cases.

  • int getLastUsedDetectionMethodCode

    Returns the last used detection method code. Supply this code to OneSpan if you are trying to diagnose a problem with the rooting detection.

  • int getLastUsedHeuristicMethodCode()

    Returns the last used heuristic method code. Supply this code to OneSpan if you are trying to diagnose a problem with the rooting detection.

ScreenMirroringData

This class contains information about active screen mirrors. Such screen readers might include the device projecting the screen to a Chrome Cast device, MiraCast, or other type of screen casting device.

The following public methods are available for this class:

  • boolean isScreenMirroringDetected()

    Returns true if screen mirroring is detected.

  • boolean isScreenMirroringBlocked()

    Returns true if the screen mirroring is currently being blocked by App Shielding.

ScreenreaderData

This class contains information about whether an untrusted screen reader is active.

The following public methods are available for this class:

  • boolean isUntrustedScreenreaderPresent()

    Returns true if an active screen reader that is not included in the allowlist has been found. Trusted screen readers can be configured with the addTrustedScreenreaderSigner option.

  • int getUntrustedScreenreaderCount()

    Returns the number of untrusted active screen readers.

  • String getUntrustedScreenreaderPackageName(int index)

    Returns the package name of the untrusted screen reader APK with the specified index. The index must be in the range of 0 to getUntrustedScreenreaderCount() - 1. If no screen readers are active or the index is out of bounds, this method returns an empty string.

  • String getUntrustedScreenreaderVersionName(int index)

    Returns the human-readable version name with the specified index, which is usually specified in AndroidManifest.xml by the versionName attribute of the manifest tag. The index must be in the range of 0 to getUntrustedScreenreaderCount() - 1. If no screen readers are active or the index is out of bounds, this method returns an empty string.

  • String getUntrustedScreenreaderAppLabel(int index)

    Returns the human-readable name (label) of the untrusted screen reader APK with the specified index, which is usually specified in AndroidManifest.xml by the label attribute of the application tag. The index must be in the range of 0 to getUntrustedScreenreaderCount() - 1. If no screen readers are active or the index is out of bounds, this method returns an empty string.

  • String getUntrustedScreenreaderSignerName(int index)

    Returns Common Name (CN) of the signer’s X.509 certificate subject for the untrusted screen reader APK with the specified index. The index must be in the range of 0 to getUntrustedScreenreaderCount() - 1. If no screen readers are active or the index is out of bounds, this method returns an empty string.

  • String getUntrustedScreenreaderSignature(int index)

    Returns the screen reader signature of the untrusted screen reader APK with the specified index. The index must be in the range of 0 to getUntrustedScreenreaderCount() - 1. If no screen readers are active or the index is out of bounds, this method returns an empty string.

TaskHijackingData

This class contains information about a task that was hijacked by an external application targeting this app through taskAffinity.

The following public methods are available for this class:

  • ComponentName getOffendingAppActivity()

    Returns ComponentName (app-packageName + activity class name) of the app/activity that hijacked this app’s task.

  • String getOffendingTaskAffinity()

    Returns the taskAffinity set by the offending activity.

  • String getIntentData()

    Returns the intent used to start the task.

The task hijacking callback is only called if task hijacking protection is enabled by including the ShieldSDK-activity-guard package in the application.

UntrustedSourceAppData

This class contains information about detected untrusted source or sideloaded application installed on the device. If an untrusted application is installed or uninstalled from the device while the application is running, the observer is notified with updated callback data.

UntrustedSourceAppData returns a list of all untrusted applications installed to the device. If a new untrusted application is installed, the installed application will not necessarily be added as the last item on the untrusted applications list.

The following public methods are available for this class:

  • boolean isUntrustedSourceAppPresent()

    Returns true if there are one or more untrusted source applications installed on the device.

    App Shielding trusts all system applications. For any other application, App Shielding compares the signer of the installer package with an allowlist of trusted signers that may be configured in the App Shielding configuration by addTrustedInstallerSigner. In addition to this option, it is possible to allowlist a single application installed via untrusted app store, or sideloaded by addTrustedApplicationSigner

  • int getUntrustedSourceAppCount()

    Returns the number of applications that were sideloaded or installed via untrusted sources.

  • String getUntrustedSourceAppPackageName(int index)

    Returns the package name of the untrusted source application at the specified index.

  • String getUntrustedSourceAppVersionName(int index)

    Returns the APK package’s human-readable version name of the untrusted source app at the specified index, which is usually specified in AndroidManifest.xml by the versionName attribute of the <manifest> tag .

  • String getUntrustedSourceAppLabel(int index)

    Returns the human-readable name (i.e., label) of the untrusted source app at the specified index, which is usually specified in AndroidManifest.xml by the label attribute of the <application> tag.

  • String getUntrustedSourceAppSignerName(int index)

    Returns the Common Name (CN) of the APK signer’s X.509 certificate subject for the untrusted source app at the specified index. If it is not possible to determine the CN field, this method returns an empty string.

  • String getUntrustedSourceAppSignature(int index)

    Returns the APK signature of the untrusted source app APK at index. If it is not possible to determine the signature, this method returns an empty string.

The following methods return information about the corresponding installer for an untrusted source app:

  • String getUntrustedSourceAppInstallerPackageName(int index)

    Returns the package name of the installer of the untrusted source application at the given index.

  • String getUntrustedSourceAppInstallerSignerName(int index)

    Returns the Common Name (CN) of the installer APK signer’s X.509 certificate subject for the untrusted source app at the specified index. If it is not possible to determine the CN field, this method returns an empty string.

  • String getUntrustedSourceAppInstallerSignature(int index)

    Returns the installer APK signature of the untrusted source app at the provided index. If it is not possible to determine the signature, this method returns an empty string.

    If the installer is removed from the device, the affected apps are still marked as having an "untrusted source", but the getUntrustedSourceAppInstaller* methods will then return empty values for those installers.

VIRTUAL_SPACE_APP

This callback contains information about whether the application was launched via a virtual space app.

The following public methods are available for this class:

  • boolean isAppInVirtualSpace()

    Returns true if the application is launched via a virtual space app. Returns false if the application is not in a virtual space or is in the virtual space of any application included in the allowlist by addTrustedVirtualSpaceAppSigner.

  • String getVirtualSpaceAppPackageName()

    Returns the virtual space app’s APK package name. If the application is launched outside of a virtual space, this method returns an empty string.

  • String getVirtualSpaceAppVersionName()

    Returns the virtual space app’s APK package human- readable version name, which is usually specified in AndroidManifest.xml by the versionName attribute of the <manifest> tag. If the application is launched outside of a virtual space, this method returns an empty string.

  • String getVirtualSpaceAppAppLabel()

    Returns the virtual space app’s human-readable name (i.e., label), which is usually specified in AndroidManifest.xml by the label attribute of the <application> tag. If the application is launched outside of a virtual space, this method returns an empty string.

  • String getVirtualSpaceAppSignerName()

    Returns the Subject’s Common Name (CN) of the APK signer’s X.509 certificate subject of the virtual space app. If the application is launched outside of a virtual space, this method returns an empty string.

  • String getVirtualSpaceAppSignature()

    Returns the virtual space app’s signature. If the application is launched outside of a virtual space, this method returns an empty string.

Due to the limitations with some virtual spaces like Samsung Secure Folder, Xiaomi Dual Apps, and Work Profile applications, the get methods return empty values regardless.

The following callbacks can only be used when the app is shielded in Debug mode:

  • RepackagingData: In case of a repackaging attack, the application will exit immediately.
  • EmulatorData: If an emulator is detected, the application will exit immediately.
  • DebuggerData: Java debuggers are blocked by default. If blocking is not possible, the application will exit immediately. Low-level debuggers, such as gdb are simply blocked.
  • NativeCodeHooksData: If a native code hook is detected, the application will exit immediately.

The anti-debugging feature is only operational when the App Shielding native library is loaded. However, only the Android OS code can be debugged during application startup, the application code cannot be debugged. After the library has been loaded, the debugger is killed.

Security and priority are equally important in App Shielding. The EXIT reaction is safer than a callback and has therefore precedence over a callback. To reach the place in the code with the callback, the EXIT reaction that corresponds to this detection should be turned off.