Track down the source of android toast

1 minute read

There are many ways to tell which app sends a toast, the most quickest way is to install third-party app such as Toast Source or Toaster, if you don’t want to install apps, then the following method can do the job.

Use dumpsys

AppOps service of dumpsys can show app operations such as READ/WRITE external storage, it can also show which app sends an toast with specific time when it sends it and its duration, like this:

$ dumpsys appops
...
  Uid u0a75:
    state=top
    startNesting=1
    Package com.sdsmdg.demoexample:
      TOAST_WINDOW (allow):
          Access: top   = 2019-08-03 17:21:42.853 (-3s933ms)
          Running start at: +3s933ms
          startNesting=1
      READ_EXTERNAL_STORAGE (allow):
          Access: cch   = 2019-08-03 17:21:20.630 (-26s156ms)
      WRITE_EXTERNAL_STORAGE (allow):
          Access: cch   = 2019-08-03 17:21:20.630 (-26s156ms)
...

The above example was generated by TastyToast, before build, commit 1c7bd7 needs to be applied to avoid build failure.

export JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64"
echo 'sdk.dir = /opt/sdk' > local.properties
./gradlew assemble

This method can be used to find which package sends a toast after you see it.

Use uiautomator

Another way is via uiautomator, but you need to issue command before the toast was sent, if a toast shows up, an event with type of TYPE_WINDOW_CONTENT_CHANGED will be shown in the log, the toast message can also be seen in the log:

$ uiautomator events

toast

Use Accessibility service

There is an answer in stackoverflow mentioned AccessibilityService can be extented by overriding method onAccessibilityEvent() like this to show which package sends a toast or notification:

public void onAccessibilityEvent(AccessibilityEvent event) {
    if (event.getEventType() != AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)
        return; // event is not a notification

    String sourcePackageName = (String) event.getPackageName();

    Parcelable parcelable = event.getParcelableData();
    if (parcelable instanceof Notification) {
        // Statusbar Notification
    }
    else {
        // something else, e.g. a Toast message
        String log = "Message: " + event.getText().get(0)
                   + " [Source: " + sourcePackageName + "]";
        // write `log` to file...
    }
}

This should work, but not tested.