Developer Console

Step 6: Report Your App's Dynamic Capabilities (VSK Fire TV)

With dynamic capabilities integrations, your app's capabilities depend on the user's state (such as login state or subscription level). As such, you will need to integrate with the VSK Agent to report the capabilities. The recommended approach for integrating with the VSK Agent is through the VSK Agent Client Library.

Sample App Notes

The sample app reports dynamic capabilities, so you do not need to do any coding in this step. In the sample app, see the DynamicCapabilityReporter class, AndroidManifest.xml, VSK Agent Client Library, and capability files (inside of res/raw) for details.

About the VSK Agent

The VSK Agent is an on-device routing agent on Fire OS. Your Fire TV app uses the VSKAgentClient class to report its dynamic capabilities to the VSK Agent. These capabilities let the VSK Agent know what kind of directives it can broadcast to your app. The VSK Agent communicates with Fire OS through the Android Interface Design Language API, or AIDL.

The VSK Agent will send only directives that your app is capable of supporting. For example, if you don't indicate that your app is capable of supporting the PlaybackController interface, Alexa won't send you PlaybackController directives.

Choose How to Integrate with the VSK Agent

You have two options for how you integrate with the VSK Agent:

Option 1: VSK Agent Client Library (recommended approach)

The recommended approach is to consume the Amazon-provided VSK Agent Client Library. The VSK Agent Client Library gives you the full functionality of VSK Agent while streamlining some of the Android interprocess communication (IPC) details. The VSK Agent Client Library reduces your need to write many lines of code for integrating with the Fire TV VSK Agent. In short, it handles the work of connecting to this service for you, presenting you with normal Java methods you can call.

When you consume the VSK Agent Client Library, you have the ability to dynamically add or remove capabilities selectively and at any time. If you plan to support more advanced ways of communicating your app's UI state or other dynamic state updates in the future, you should choose this option. Leveraging the VSK Agent Client Library to integrate with VSK Agent is the fastest and easiest route to enable VSK capabilities in your app. (Note that even with the VSK Agent Client Library, capabilities which are always supported can still be declared in a static resource by following the static capabilities integration.)

The VSK Agent provides two methods for reporting your app's capabilities in a dynamic way:

  • AddOrUpdateCapabilities() — a method for declaring the capabilities you support.
  • sendCapabilityTestDirective() — a method for testing your integration.

See Steps for Option 1: Integrate with the VSK Agent Service via the VSK Agent Client Library below for the implementation steps.

Option 2: VSKApplicationAgentAPI

Another option for connecting with the VSK Agent is for you to consume only the service definition and manage this connection code yourself. The VSK Agent Client Library communicates with a bound service under the hood. If you so choose, you can integrate with this under-the-hood service yourself by consuming the VSKApplicationAgentAPI package yourself.

This VSKApplicationAgentAPI package contains the necessary .aidl files (as part of the Android Interface Definition Library) and Java classes for communicating with this service. While not the recommended route, Amazon realizes that under certain circumstances, you may want to manage all code yourself. This option might be appealing if you do not want to have any Amazon libraries or code in your project, perhaps to avoid any dependencies or due to other requirements.

See Steps for Option 2: Integrate with VSK Agent through VSKApplicationAgentAPI Package for some brief steps. More details about how to consume the VSKApplicationAgentAPI package in detail are beyond the scope of this documentation.

Steps for Option 1: Integrate with the VSK Agent Service via the VSK Agent Client Library

To integrate with the VSK Agent Client Library to communicate with the VSK Agent (Option 1 above), perform the following steps:

Add the VSK Agent Client Library to Your Project

  1. Download the VSK Agent Client Library (an AAR file):

    Note that by downloading the VSK Agent Client Library, you agree to Amazon's Program Materials License Agreement.

    (Note that the "VSK Agent Client Library" is not the same as the "Alexa Client Library" used in cloudside integrations.)

  2. Add the VSK Agent Client Library to your project. In Android Studio, go to Find > New > Module. Then choose Import .JAR/.AAR Package and select the VSKApplicationAgentClientLibrary file.

  3. When you import the AAR file, Android Studio adds VSKApplicationAgentClientLibrary to settings.gradle (Project Settings) so that it is available to be added as a dependency in your app's build.gradle file. Expand Gradle Scripts and go to settings.gradle (Project Settings). Make sure the VSKApplicationAgentClientLibrary has been included:

    include ':VSKApplicationAgentClientLibrary'
    
  4. Expand Gradle Scripts, open build.gradle (Module: App), and add a dependency for the VSKApplicationAgentClientLibrary in the dependencies object:

    dependencies {
         implementation project(path: ':VSKApplicationAgentClientLibrary')
    }
    

    Note that the VSK Agent Client Library is not available on Maven or JCenter.

    Sample App Notes

    The sample app incorporates the VSK Agent Client Library. In Android Studio, switch to the Project view and look for the VSKApplicationAgentClientLibrary folder.

Configure Your Manifest

Your app's manifest (AndroidManifest.xml) contains declarations of the activities that your app performs. In your app's manifest, add the following permission to allow your app to integrate with VSK Agent:

 <uses-permission android:name="com.amazon.alexa.vsk_app_agent_api.permission.BIND_SERVICE_PERMISSION" />
Sample App Notes

See the AndroidManifest.xml file (in app/manifests) in the sample app for a full example of the app manifest.

Initialize the VSK Agent Client Library

The next step is to initialize the VSK Agent Client Library within your code base.

Sample App Notes

In the sample app, this initialization takes place within the DynamicCapabilityReporter class (see src/java/com/example/vskfiretv/reporter). DynamicCapabilityReporter is the class where you report what voice capabilities your app supports.
  1. Create a new instance of the VSK Agent client:

    public DynamicCapabilityReporter(final Context context) {
        this.context = context;
        // Create an instance of VSKAgentClient
        client = new VSKAgentClient(context);
    }
    
  2. Declare your capabilities in the reportDynamicCapabilities() method, and then make a capability reporting request with the AddOrUpdateCapabilities() method. Call this method in your app after the user has logged in.

      public void reportDynamicCapabilities() {
          // Create a list of supported capabilities in your app.
          final List<AlexaCapability> supportedCapabilities = new ArrayList<>();
          supportedCapabilities.add(getAlexaCapability(R.raw.remote_video_player_capability)); // RemoteVideoPlayer Capability
          // supportedCapabilities.add(getAlexaCapability(R.raw.play_back_controller_capability)); // PlaybackController Capability -- commented out here because Media Session is recommended over PlaybackController
          //supportedCapabilities.add(getAlexaCapability(R.raw.channel_controller_capability)); // ChannelController Capability (not yet supported in app-only integrations)
          //supportedCapabilities.add(getAlexaCapability(R.raw.seek_controller_capability)); // SeekController Capability
          //supportedCapabilities.add(getAlexaCapability(R.raw.keypad_controller_capability)); // KeypadController Capability (not yet supported in app-only integrations)
    
          // Make the capability reporting request
          final AddOrUpdateCapabilitiesRequest request = new AddOrUpdateCapabilitiesRequest(supportedCapabilities);
        }            
    

    Alexa will send directives related to these capabilities listed here. For example, if you indicate remote_video_player_capability, Alexa will send directives related to the RemoteVideoPlayer interface. If you don't have the capability declared, Alexa won't send directives related to the capability.

  3. The previous code declares capabilities by referencing files with code such as Utils.getTextForRawResource(R.raw.remote_video_player). The references in the sample code above are as follows:

    In app/res/raw, create the necessary files with these names and capabilities declarations based on the interfaces your app supports. The details of the JSON objects for each of the capabilities are described in Supported Capabilities with App-only Integration.

    Sample App Notes

    In the sample app, you can see these capabilities in app/res/raw. You can copy these sample JSON objects into your own app as appropriate.
  4. Using the the addOrUpdateCapabilities() method, report your app's dynamic capabilities to the VSK Agent:

    Log.i(TAG, "Reporting dynamic capabilities to the VSK Agent...");
    final boolean reportingStatus = client.addOrUpdateCapabilities(request);
    
    if(reportingStatus) {
        Log.i(TAG, "Successfully reported dynamic capabilities to the VSK Agent");
        return;
    }
    Log.e(TAG, "Failed reporting dynamic capabilities to the VSK Agent");
    }
    

    Now the VSK Agent will know that your app supports those capabilities. When you later run your app on Fire TV and look at the logs (in Step 10: Test Utterances and Observe Logs), you will see these capabilities reported to the VSK Agent by the DynamicCapabilityReporter class.

    Looking Ahead: Specifying Different Catalogs Based on User State

    If desired, in the future you'll be able to specify a different catalog based on the user's subscription or status. For example, you could offer free content on one catalog ID (e.g., acme_free) and premium content with another catalog (e.g., acme_premium). To do this, you would report different capabilities based on different user states.

    For example, for users with a premium subscription level, you could report capabilities that contain a different catalogs property in the capability file. For these users, you could point to a capabilities file with acme_premium. For users who are not logged in, you could point to a capabilities file with a catalogs property containing acme_free. See the RemoteVideoPlayer Capabilities section in "Supported Capabilities with App-only Integration" for more details about the catalogs property. Note that this functionality isn't currently supported but will be in the future.

Full Code Example for Reporting Dynamic Capabilities

Here's a full boilerplate code example for reporting dynamic capabilities.

Sample App Notes

See also the DynamicCapabilityReporter class example in the sample app for a more real-world example in context.
// -------------------------------\
// MyApplication.java
// -------------------------------/
public class MyApplication extends Application {

  private static MyApplication sTheApp;
  private VSKAgentClient client;
  private HandlerThread vskAgentThread;
  private Handler vskAgentHandler;...

  public static MyApplication getInstance() {
    return sTheApp;
  }

  @Override
  public void onCreate() {

    super.onCreate();
    //...
    vskAgentThread = new HandlerThread("VSKAgentThread");
    vskAgentThread.start()
    vskAgentHandler = new Handler(vskAgentThread.getLooper);
    client = new VSKAgentClient(this);
    reportCapabilities();
    //...
  }
  //...

  public void reportCapabilities() {
    // api calls should be made on a background thread
    vskAgentHandler.post(new Runnable() {
      public void run() {
        reportCapabilitiesInternal()
      }
    });
  }

  private void reportCapabilitiesInternal() {
    // Determine currently supported Alexa capabilities based on
    // current application state. You can dynamically construct
    // the capability json, or just reference json in resource files (as we do here)
    List < AlexaCapability > supportedCapabilities = new ArrayList < >();
    if (accountManager.isLoggedIn()) supportedCapabilities.add(new AlexaCapability(Utils.getTextForRawResource(R.raw.remote_video_player)));
    //supportedCapabilities.add(new AlexaCapability(Utils.getTextForRawResource(R.raw.playback_controller)));
    if (accountManager.geteCurrentCustomer().hasSubscription(Subscription.LIVE_TV) {
      supportedCapabilities.add(new AlexaCapability(Utils.getTextForRawResource(R.raw.channel_controller)));
    }
  }

  AddOrUpdateCapabilitiesRequest request = new AddOrUpdateCapabilitiesRequest(supportedCapabilities);
  boolean success = client.addOrUpdateCapabilities(request);
  if (!success) {
    //handle error
  }
}

private String getTextForRawResource(int resId) {
  InputStream inputStream = getResources().openRawResource(resId);
  String line;
  StringBuilder result = new StringBuilder();

  try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
    while ((line = reader.readLine()) != null) {
      result.append(line).append('\n');
    }
    return result.toString();
  } catch(IOException e) {
    //error
  }

  return null;
}

Steps for Option 2: Integrate with VSK Agent through VSKApplicationAgentAPI Package

If you prefer to integrate with the VSK Agent yourself using the VSKApplicationAgentAPI package (without relying on the VSK Agent Client Library), do the following:

  1. Download the VSKApplicationAgentAPI (an AAR file).

    Note that by downloading the VSKApplicationAgentAPI, you agree to Amazon's Program Materials License Agreement.

  2. Add the VSKApplicationAgentAPI to your project. In Android Studio, go to Find > New > Module. Then choose Import .JAR/.AAR Package and select the VSKApplicationAgentAPI file.

  3. When you import the AAR file, Android Studio adds the file to settings.gradle (Project Settings) so that it is available to be added as a dependency in your app's build.gradle file. Expand Gradle Scripts and open the settings.gradle (Project Settings) file. Make sure the VSKApplicationAgentAPI has been included:

    include ':VSKApplicationAgentAPI'
    
  4. Expand Gradle Scripts and go to build.gradle (Module: App) and add a dependency for the VSKApplicationAgentAPI in the dependencies object:

    dependencies {
         implementation project(path: ':VSKApplicationAgentAPI')
    }
    
  5. Declare the necessary permissions in your app manifest.
  6. On application create, report the currently supported capabilities via the addOrUpdateCapabilities() method.
  7. Whenever an app state change would cause your supported capabilities to change, also call addOrUpdateCapabilities().

Best Practices: Dynamic Capability Reporting

When you report dynamic capabilities to the Alexa App Agent, it determines if your app's capabilities have changed since the previous report, and then reports any changes to the Alexa cloud. For that reason, it's a good idea to report your dynamic capabilities on every app start. However, these capabilities should not change across reports. Changes in dynamic capabilities should be reported when there are long term changes in the application or the customer state that impacts the ability to handle customer requests.

For example, you may remove the capability to play content when the customer logs out of your app. Reporting changes in your dynamic capabilities causes unnecessary churn in the capabilities reported to Alexa, and may impact your customers. This is especially true if your app is throttled due to excessive discovery traffic. To avoid this, we highly recommend you follow the guidelines below when reporting dynamic capabilities:

  1. Don't report capabilities based on an unknown state. For instance, if the dynamic capabilities your app reports depend on if the user is logged in and that is determined asynchronously, wait until that state is known before reporting. Don't report dynamic capabilities as if the customer was logged out only to report again a few moments later.
  2. Don't report capabilities based on an ephemeral state that may change frequently. For example, you don't need to add or remove PlaybackController/SeekController based on if content is playing or not. It is enough that your app is capable of the actions declared by the capability interface.
  3. Don't add or remove capabilities based on the state outside your application logic. For instance, it's not necessary to change your app's capabilities based on if your app is in the foreground, or if a customer switched inputs on FireTV.

Next Steps

Go to the next step: Step 7: Add a BroadcastReceiver.


Last updated: Feb 15, 2023