Author Archives: Android Developers

Google Play’s Indie Games Contest is back in Europe. Enter now

Posted by Adriana Puchianu, Developer Marketing Google Play

Following last year's success, today we're announcing the second annual Google Play Indie Games Contest in Europe, expanding to more countries and bigger prizes. The contest rewards your passion, creativity and innovation, and provides support to help bring your game to more people.

Prizes for the finalists and winners

  • A trip to London to showcase your game at the Saatchi Gallery
  • Paid digital marketing campaigns worth up to 100,000 EUR
  • Influencer campaigns worth up to 50,000 EUR
  • Premium placements on Google Play
  • Promotion on Android and Google Play marketing channels
  • Tickets to Google I/O 2018 and other top industry events
  • Latest Google hardware
  • Special prizes for the best Unity games


How to enter the contest

If you're based in one of the 28 eligible countries, have 30 or less full time employees, and published a new game on Google Play after 1 January 2017, you may now be eligible to enter the contest. If you're planning on publishing a new game soon, you can also enter by submitting a private beta. Check out all the details in the terms and conditions. Submissions close on 31 December 2017.

Up to 20 finalists will showcase their games at an open event at the Saatchi Gallery in London on the 13th February 2018. At the event, the top 10 will be selected by the event attendees and the Google Play team. The top 10 will then pitch to the jury of industry experts, from which the final winner and runners up will be selected.

Come along to the final event

Anyone can register to attend the final showcase event at the Saatchi Gallery in London on 13 February 2018. Play some great indie games and have fun with indie developers,industry experts, and the Google Play team.

Enter now

Visit the contest site to find out more and enter the Indie Games Contest now.

How useful did you find this blogpost?

Fight global hunger with your favorite apps and games on Google Play

Editor's Note: Cross-post from The Keyword. If you’re a developer interested in supporting a fundraising cause within your title, or if you have a social impact app let us know

Posted by Maxim Mai, Partner Development Manager, Google Play

We grow enough food to feed everyone on the planet. Yet 815 million people–one in nine—still go to bed on an empty stomach every day.

On October 16, people from around the world come together for World Food Day, with the goal to promote awareness and action for those who suffer from hunger and to advocate for food security and nutritious diets for all.

To raise funds and awareness for this cause, Google Play has joined forces with 12 popular apps and games to create the Apps and Games Against Hunger collection available in North and Latin America.

From now until October 21, 100% of revenue from designated in-app purchases made in Google Play's Apps and Games Against Hunger collection will be donated to World Food Program USA.

World Food Program USA supports the mission of the UN World Food Programme, the leading agency fighting hunger, by mobilizing individuals, lawmakers and businesses in the U.S. to advance the global movement to end hunger, feeding families in need around the world.

These are the 12 global leading apps and games taking part in this special fundraising collection on Google Play:

ShareTheMeal–Help children,

Peak–Brain Games & Training,

Dragon City

Cooking Fever

Animation Throwdown: TQFC

Legendary: Game of Heroes

My Cafe: Recipes & Stories - World Cooking Game

TRANSFORMERS: Forged to Fight

Rodeo Stampede: Sky Zoo Safari

Jurassic World™: The Game

MARVEL Contest of Champions

Sling Kong

Thank you to all our users and developers for supporting World Food Day.

Effective phone number verification

Posted by Steven Soneff, Identity Product Manager

To build apps that make use of phone numbers, it's often crucial to verify that the user owns a number. Doing this can be tricky from a UX perspective, not least in understanding phone number formats in different locales, but also in providing a verification mechanism that isn't cumbersome or using intrusive device permissions, such as the ability to read all of a user's SMS.

There are many libraries for efficient pre-built phone authentication, such as Firebase Phone Auth, but if you are an advanced developer and need to build this functionality yourself, Google Play Services has two new APIs that help you obtain a user's phone number and verify it via SMS without device permissions: the Phone Selector and SMS Retriever. Apps like Flipkart have seen a 12% increase of success rates in phone number sign-up flows using these methods.

The steps for using these with your server can be seen here:

In this post we'll show the code that you need to provide a phone number selector to your users, and then use this with the SMS retriever API to request a verification code from your server that the Android device will automatically receive and parse with no input from the user.

Note: Before you begin you'll need to build and test this is a device with a phone number that can receive SMS and runs Google Play services 10.2.x and higher.

Using the Phone Selector to get the number

The first step is to have the user initiate SMS verification from within your app. Your app might prompt the user to enter a phone number, and you can use the Phone Selector to make this easier, using code like this:

// Construct a request for phone numbers and show the picker
private void requestHint() {
    HintRequest hintRequest = new HintRequest.Builder()
           .setPhoneNumberIdentifierSupported(true)
           .build();

    PendingIntent intent = Auth.CredentialsApi.getHintPickerIntent(
            apiClient, hintRequest);
    startIntentSenderForResult(intent.getIntentSender(),
            RESOLVE_HINT, null, 0, 0, 0);
}

The HintRequest builder tells Play Services that a phone number identifier is needed. This is then used to create and start an intent, which will show a Play Service dialog to the user allowing them to select their phone number to share with the app. This API does not require any permissions, and displays the number(s) available on the phone or Google Account for the user to select.

When the user selects a phone number it will be returned to the application in onActivityResult in E164 format on devices running the latest version of Play Services. Note that in some cases, depending on your phone, you may not get a phone number, so be sure to check if the credential is non-null. If you don't have a number, you'll need to provide a way for your user to type it in manually.

// Obtain the phone number from the result
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  if (requestCode == RESOLVE_HINT) {
      if (resultCode == RESULT_OK) {
          Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
          // credential.getId(); <-- E.164 format phone number on 10.2.+ devices
      }
  }
}

At this point you'll have a phone number string for your user. While this is useful, you'll likely want to verify that the user owns this particular number, for example to allow them to send or retrieve message with other users or identifying themselves with this number.

Using the SMS Verification API to verify the number

A simple way to verify phone number ownership is by sending an SMS to the number, containing a one time verification code, and having them enter that into your app. The SMS Verification API gives you the ability for the app to listen for an incoming SMS from which it can parse the code automatically.

To get started, your app will SmsRetrieverClient with code like this:

SmsRetrieverClient client = SmsRetriever.getClient(this /* context */);

Task<Void> task = client.startSmsRetriever();

task.addOnSuccessListener(new OnSuccessListener<Void>() {
  @Override
  public void onSuccess(Void aVoid) {
    // successfully started an SMS Retriever for one SMS message
  }
});

task.addOnFailureListener(new OnFailureListener() {
  @Override
  public void onFailure(@NonNull Exception e) {
  });
);

It's pretty simple -- you get an SMS Retriever client and then start a task for it. The task has an on Success listener as well as an on Failure one to override. After starting the SMS Retriever, you'd send the user's phone number to your server and start it's workflow for generating the message and sending it to that number.

The message needs to be constructed in a specific way. The message must fit in an SMS message, so it can't be longer than 140 bytes. It needs to start with a specific prefix: '<#>' or two consecutive zero-width space characters (U+200B). See the documentation for your more information. It must end with an 11-character hash that identifies your app, described below.

Example:

<#> Use 123456 as your verification code in Example App!

FA+9qCX9VSu

The one-time verification code can be any string: you can simply generate a random number. The message needs to end with a hash that is determined according to the procedures here. Google Play services will use this hash to determine which app the verification message is for. You only need to generate this hash once for your app package and signing certificate: it won't change and shouldn't be supplied by the client app.

Your server can then send the message to the phone using your existing SMS infrastructure or service. When this message is received, Google Play services broadcasts an intent which contains the text of the message. Here's the code:

public class MySMSBroadcastReceiver extends BroadcastReceiver {

  @Override
  public void onReceive(Context context, Intent intent) {
    if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
      Bundle extras = intent.getExtras();
      Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);

      switch(status.getStatusCode()) {
        case CommonStatusCodes.SUCCESS:
          String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
          break;
        case CommonStatusCodes.TIMEOUT:
          break;
      }
    }
  }
}

In the onReceive of the broadcast receiver you get the extras, and pull the status from there. If the status indicates that the message was successfully received, you can pull the message from the extras. From here you can parse out the verification code and send it back to your server to confirm phone number ownership.

For more information, check out the full documentation and this year's Google I/O talk.

Testimonies of Early Adopters

Our early partners who use this API love it. Here are some testimonials from them:

Twilio observed and blogged that Android SMS Verification has never been easier.

"If you're a developer building mobile apps on Android that use phone numbers to register and identify user accounts, you should be using Twilio Verification SDK for Android for the quickest way to solve the problem of providing a smooth, secure and easy sign-up flow." - Simon Thorpe, Product Owner at Twilio

Authy loved the fact that these APIs work with their existing SMS infrastructure without requiring many changes.

"Adding Phone Selector + SMS Retriever into Authy 2FA app delivers magical UX for users while retaining the high security our application requires." -- Serge Kruppa, Head of Authy Engineering

Telesign observed better UX, increased security and higher conversion rates with the same backend framework.

"One significant advantage of this verification mode with lower friction is that customers might be able to see increased conversion rates for user sign-up and registration scenarios.

Enhanced security is also a benefit as Google Play Services only provides access to the SMS message to the targeted application based on the application hash inside the message." -- Priyesh Jain (Post author)

Introducing Android Instant Apps SDK 1.1

Jichao Li, Software Engineer; Shobana Ravi, Software Engineer

Since our public launch at Google I/O, we've been working hard to improve the developer experience of building instant apps. Today, we're excited to announce availability of the Android Instant Apps SDK 1.1 with some highly-requested features such as improved NDK support, configuration APKs for binary size reduction, and a new API to maintain user's context when they transition from an instant app to the installed app.

Introducing configuration APKs

For a great instant app experience, app binaries need to be lean and well structured. That's why we're introducing configuration APKs.

Configuration APKs allow developers to isolate device-specific resources and native libraries into independent APKs. For an application that uses configuration APKs, the Android Instant Apps framework will only load the resources and native libraries relevant to the user's device, thereby reducing the total size of the instant app on the device.

We currently support configuration APKs for display density, CPU architecture (ABI), and language. With these, we have seen an average reduction of 10% in the size of the binaries loaded. Actual savings for a given app depend on the number of resource files and native libraries that can be configured.

As an example, a user on an ARM device with LDPI screen density and language set to Chinese would then receive device-agnostic code and resources, and then only get the configuration APKs that have ARM native libraries, the Chinese language, and LDPI resources. They would not receive any of the other configuration APKs such as the x86 libraries, Spanish language strings, or HDPI resources.

Setting up configuration APKs for your app is a simple change to your gradle setup. Just follow the steps in our public documentation.

Persistent user context after installation

On Android Oreo, the internal storage of the instant version of the app is directly available to the installed version of the app. With this release of the SDK, we are enabling this functionality on older versions of the Android Framework, including Lollipop, Marshmallow, and Nougat devices.

To extract the internal storage of the instant app, installed apps can now call InstantAppsClient.getInstantAppData() using the Instant Apps Google Play Services API and get a ZIP file of the instant app's internal storage.

Check out our code sample and documentation for more details on how to use this API.

Start building your Android Instant App

It's simple to start building your instant app on the latest SDK. Just open the SDK Manager in Android Studio and update your Instant Apps Development SDK to 1.1.0. We can't wait to see what instant app experiences you build with these new features.

The Google Assistant can help control your Android media apps

Posted by Nazmul Idris, Android Devices and Media Developer Advocate

The Google Assistant is available across phones, speakers, Android TV and more. And it can help users get more done throughout their day—where they need to add something to a to-do list, turn on the lights, or play a song.

With music specifically, the Assistant has a lot to offer. It can understand media commands across devices ("play rock music") and supports media controls (like pause, skip, fast forward, thumbs up). And users can also control Android media playback through the Google Assistant. For example, you can pause playback by telling the Google Assistant to "pause playback" without lifting a finger. Or play music by saying "play rock on Universal Music Player".

In order for the user to control playback in your Android Media app via the Google Assistant, you can use the MediaSession APIs to make this happen. We recommend that you use MediaSession over intents as you craft your app's integration with the Google Assistant.

How to use MediaSession in your app?

Your app must implement a MediaSession that handles a prescribed set of actions as described in Interacting with the Google Assistant.

Here are some sample projects that can help you get started using MediaSession in your media apps:

  1. MediaBrowserService sample
  2. Universal Music Player sample
  3. Assistant Integration with Leanback sample

To learn more about MediaSession here are some good resources:

  1. Understanding MediaSession
  2. Working with a MediaSession
  3. Video Playback with the Google Assistant on Android TV

How does the Google Assistant use MediaSession?

The Google Assistant uses MediaSession in the same way as other external controllers such as Android Wear do. Each of these controllers cause state changes inside your app, and there needs to be a way to respond to these changes. This is where MediaSession comes into play.

By using MediaSession, the Google Assistant can control your app's media playback, as well as stay synced with its current state.

How do I test my app with MediaSession?

Once you implement MediaSession you will probably want to test how playback can be controlled outside of your app. The Media Controller Test tool was created to emulate external control of your media playback. You can verify that when other apps (like Google Assistant) interact with your app, that it works as expected.

Working with Multiple JobServices

Posted by Isai Damier, Software Engineer, Android DA

Working with Multiple JobServices

In its continuous effort to improve user experience, the Android platform has introduced strict limitations on background services starting in API level 26. Basically, unless your app is running in the foreground, the system will stop all of your app's background services within minutes.

As a result of these restrictions on background services, JobScheduler jobs have become the de facto solution for performing background tasks. For people familiar with services, JobScheduler is generally straightforward to use: except in a few cases, one of which we shall explore presently.

Imagine you are building an Android TV app. Since channels are very important to TV Apps, your app should be able to perform at least five different background operations on channels: publish a channel, add programs to a channel, send logs about a channel to your remote server, update a channel's metadata, and delete a channel. Prior to Android 8.0 (Oreo) each of these five operations could be implemented within background services. Starting in API 26, however, you must be judicious in deciding which should be plain old background Services and which should be JobServices.

In the case of a TV app, of the five operations mentioned above, only channel publication can be a plain old background service. For some context, channel publication involves three steps: first the user clicks on a button to start the process; second the app starts a background operation to create and submit the publication; and third, the user gets a UI to confirm subscription. So as you can see, publishing channels requires user interactions and therefore a visible Activity. Hence, ChannelPublisherService could be an IntentService that handles the background portion. The reason you should not use a JobService here is because JobService will introduce a delay in execution, whereas user interaction usually requires immediate response from your app.

For the other four operations, however, you should use JobServices; that's because all of them may execute while your app is in the background. So respectively, you should have ChannelProgramsJobService, ChannelLoggerJobService, ChannelMetadataJobService, and ChannelDeletionJobService.

Avoiding JobId Collisions

Since all the four JobServices above deal with Channel objects, it should be convenient to use the channelId as the jobId for each one of them. But because of the way JobServices are designed in the Android Framework, you can't. The following is the official description of jobId

Application-provided id for this job. Subsequent calls to cancel, 
or jobs created with the same jobId, will update the pre-existing 
job with the same id. This ID must be unique across all clients 
of the same uid (not just the same package). You will want to 
make sure this is a stable id across app updates, so probably not 
based on a resource ID.

What the description is telling you is that even though you are using 4 different Java objects (i.e. -JobServices), you still cannot use the same channelId as their jobIds. You don't get credit for class-level namespace.

This indeed is a real problem. You need a stable and scalable way to relate a channelId to its set of jobIds. The last thing you want is to have different channels overwriting each other's operations because of jobId collisions. Were jobId of type String instead of Integer, the solution would be easy: jobId= "ChannelPrograms" + channelId for ChannelProgramsJobService, jobId= "ChannelLogs" + channelId for ChannelLoggerJobService, etc. But since jobId is an Integer and not a String, you have to devise a clever system for generating reusable jobIds for your jobs. And for that, you can use something like the following JobIdManager.

JobIdManager is a class that you tweak according to your app's needs. For this present TV app, the basic idea is to use a single channelId over all jobs dealing with Channels. To expedite clarification: let's first look at the code for this sample JobIdManager class, and then we'll discuss.

public class JobIdManager {

   public static final int JOB_TYPE_CHANNEL_PROGRAMS = 1;
   public static final int JOB_TYPE_CHANNEL_METADATA = 2;
   public static final int JOB_TYPE_CHANNEL_DELETION = 3;
   public static final int JOB_TYPE_CHANNEL_LOGGER = 4;

   public static final int JOB_TYPE_USER_PREFS = 11;
   public static final int JOB_TYPE_USER_BEHAVIOR = 21;

   @IntDef(value = {
           JOB_TYPE_CHANNEL_PROGRAMS,
           JOB_TYPE_CHANNEL_METADATA,
           JOB_TYPE_CHANNEL_DELETION,
           JOB_TYPE_CHANNEL_LOGGER,
           JOB_TYPE_USER_PREFS,
           JOB_TYPE_USER_BEHAVIOR
   })
   @Retention(RetentionPolicy.SOURCE)
   public @interface JobType {
   }

   //16-1 for short. Adjust per your needs
   private static final int JOB_TYPE_SHIFTS = 15;

   public static int getJobId(@JobType int jobType, int objectId) {
       if ( 0 < objectId && objectId < (1<< JOB_TYPE_SHIFTS) ) {
           return (jobType << JOB_TYPE_SHIFTS) + objectId;
       } else {
           String err = String.format("objectId %s must be between %s and %s",
                   objectId,0,(1<<JOB_TYPE_SHIFTS));
           throw new IllegalArgumentException(err);
       }
   }
}

As you can see, JobIdManager simply combines a prefix with a channelId to get a jobId. This elegant simplicity, however, is just the tip of the iceberg. Let's consider the assumptions and caveats beneath.

First insight: you must be able to coerce channelId into a Short, so that when you combine channelId with a prefix you still end up with a valid Java Integer. Now of course, strictly speaking, it does not have to be a Short. As long as your prefix and channelId combine into a non-overflowing Integer, it will work. But margin is essential to sound engineering. So unless you truly have no choice, go with a Short coercion. One way you can do this in practice, for objects with large IDs on your remote server, is to define a key in your local database or content provider and use that key to generate your jobIds.

Second insight: your entire app ought to have only one JobIdManager class. That class should generate jobIds for all your app's jobs: whether those jobs have to do with Channels, Users, or Cats and Dogs. The sample JobIdManager class points this out: not all JOB_TYPEs have to do with Channel operations. One job type has to do with user prefs and one with user behavior. The JobIdManager accounts for them all by assigning a different prefix to each job type.

Third insight: for each -JobService in your app, you must have a unique and final JOB_TYPE_ prefix. Again, this must be an exhaustive one-to-one relationship.

Using JobIdManager

The following code snippet from ChannelProgramsJobService demonstrates how to use a JobIdManager in your project. Whenever you need to schedule a new job, you generate the jobId using JobIdManager.getJobId(...).

import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
import android.os.PersistableBundle;

public class ChannelProgramsJobService extends JobService {
  
   private static final String CHANNEL_ID = "channelId";
   . . .

   public static void schedulePeriodicJob(Context context,
                                      final int channelId,
                                      String channelName,
                                      long intervalMillis,
                                      long flexMillis)
{
   JobInfo.Builder builder = scheduleJob(context, channelId);
   builder.setPeriodic(intervalMillis, flexMillis);

   JobScheduler scheduler = 
            (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
   if (JobScheduler.RESULT_SUCCESS != scheduler.schedule(builder.build())) {
       //todo what? log to server as analytics maybe?
       Log.d(TAG, "could not schedule program updates for channel " + channelName);
   }
}

private static JobInfo.Builder scheduleJob(Context context,final int channelId){
   ComponentName componentName =
           new ComponentName(context, ChannelProgramsJobService.class);
   final int jobId = JobIdManager
             .getJobId(JobIdManager.JOB_TYPE_CHANNEL_PROGRAMS, channelId);
   PersistableBundle bundle = new PersistableBundle();
   bundle.putInt(CHANNEL_ID, channelId);
   JobInfo.Builder builder = new JobInfo.Builder(jobId, componentName);
   builder.setPersisted(true);
   builder.setExtras(bundle);
   builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
   return builder;
}

   ...
}

Footnote: Thanks to Christopher Tate and Trevor Johns for their invaluable feedback

Video Playback with the Google Assistant on Android TV

Posted by Benjamin Baxter, Developer Programs Engineer

How to integrate the Google Assistant in a TV app

Earlier this year, we announced that the Google Assistant will be coming to Android TV and it has arrived. The Google Assistant on Android TV will allow users to discover, launch and control media content, control smart devices like light bulbs, and much more. Your Assistant also understands that you're interacting on a TV, so you'll get the best experience possible while watching your favorite movies and TV shows.

The Google Assistant has a built-in capability to understand commands like "Watch The Incredibles", and media controls, like pause, fast forward, etc. This article will walk through how to integrate the Google Assistant into your application.

There are no new APIs needed to integrate with the Google Assistant. You just need to follow the pattern that the Google Assistant expects from your app. If you want to experiment and play with the APIs and the Assistant, you can download this sample from github.

Discovery

The Google Assistant has made some changes to improve finding information on Android TV.

There are a few ways to expose your content to users through the Google Assisant.

Server side integration. (Requires registration and onboarding)

You need to provide your content catalog to Google. This data is ingested and available to the Google Assistant outside of your app.

This is not specific for Google Assistant. It will also enable other Google services such as search and discovery on Google Search, Google Play, Google Home App, and Android TV.

Client side integration. (Available to all apps)

If your app is already searchable, then you only need to handle the EXTRA_START_PLAYBACK flag, which we go into more detail later. Content will auto-play if the app name is explicitly specified in the search results or if the user is already in your app.

Once your app is searchable, you can test by asking the Assistant or, if you are in a loud area, test quietly by running the following adb command:

adb shell am start -a "android.search.action.GLOBAL_SEARCH" --es query \"The Incredibles\" 

Each app that responds to the search query will have a row displaying their search results. Notice that YouTube and the sample app, Assistant Playback, each receive their own rows for content that match the search query.

For specific searches such as "Play Big Buck Bunny", the Assistant will present a card with a button for each app that exactly matched the search query. In the screenshot below, you can see the sample app, Assistant Playback, shows up as an option to watch Big Buck Bunny.

There are times when the Google Assistant will launch an app directly to start playing content. An example of when this occurs is when content is exclusive to the app; "Play the Netflix original House of Cards".

Launching

When the user selects a video from search results, an intent is sent to your app. The priority order for the intent actions are as follows:

  1. Intent specified in the cursor returned from the search (SUGGEST_COLUMN_INTENT_ACTION).
  2. Intent specific in the searchable.xml file with the searchSuggestIntentAction value.
  3. Defaults to ACTION_VIEW.

In addition, the Assistant will also pass an extra to signal if playback should begin immediately. You app should be able to handle the intent and expect a boolean extra called EXTRA_START_PLAYBACK.

import static android.support.v4.content.IntentCompat.EXTRA_START_PLAYBACK;

public class SearchableActivity extends Activity {

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       if (getIntent() != null) {
           // Retrieve video from getIntent().getData().

           boolean startPlayback = getIntent().getBooleanExtra(EXTRA_START_PLAYBACK, false);
           Log.d(TAG, "Should start playback? " + (startPlayback ? "yes" : "no"));

           if (startPlayback) {
               // Start playback.
               startActivity(...);
           } else {
               // Show details for movie.
               startActivity(...);
           }
       }
       finish();
   }
}

You can test this by modifying and running the following adb command. If your app has a custom action, then replace android.intent.action.VIEW with the custom action. Replace the value of the -d argument with the URI you return from the Assistant's query.

adb shell 'am start -a android.intent.action.VIEW --ez
android.intent.extra.START_PLAYBACK true -d <URI> -f 0x14000000'

The -f argument is the logical OR value from FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP. This will force your activity to be freshly launched.

For example, in the sample app, you can run the following command to launch playback of "Big Buck Bunny" as if the assistant had launched it.

adb shell 'am start -a android.intent.action.VIEW --ez
android.intent.extra.START_PLAYBACK true -d 
content://com.example.android.assistantplayback/video/2 -n
com.example.android.assistantplayback/.SearchableActivity -f 0x14000000'

The URI above is defined by the value of android:searchSuggestIntentData in searchable.xml (content://com.example.android.assistantplayback/video/) in addition to the value of SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID (2) returned from the query.

Note that intents may be cached by the Google Assistant up to 7 days. Your app could receive a request to play content that is no longer available. The intent handler should be designed to be stateless and not rely on any previously knowledge to handle the deep link. Your app should gracefully handle this situation. One solution would be to show an error message and let the user land on your main activity or another relevant activity.

Playback

If your app implements MediaSession correctly, then your app should work right away with no changes.

The Google Assistant assumes that your app handles transport controls. The Assistant uses the TransportControls to send media commands to your app's MediaSession. Video apps must support the following controls wherever possible:

  • Play/Pause/Stop
  • Previous/Next
  • Rewind/Fast Forward (implemented with seekTo())

You can easily get a hook for these controls by implementing a MediaSession.Callback. If you play videos using PlaybackTransportControlGlue, then all your callback needs to do it sync the glue and the MediaSession. Otherwise use this callback to sync your player.

public class MyMediaSessionCallback extends MediaSessionCompat.Callback {

   private final PlaybackTransportControlGlue<?> mGlue;

   public MediaSessionCallback(PlaybackTransportControlGlue<?> glue) {
       mGlue = glue;
   }

   @Override
   public void onPlay() {
       Log.d(TAG, "MediaSessionCallback: onPlay()");
       mGlue.play();
       updateMediaSessionState(...);
   }

   @Override
   public void onPause() {
       Log.d(TAG, "MediaSessionCallback: onPause()");
       mGlue.pause();
       updateMediaSessionState(...);
   }

   @Override
   public void onSeekTo(long position) {
       Log.d(TAG, "MediaSessionCallback: onSeekTo()");
       mGlue.seekTo(position);
       updateMediaSessionState(...);
   }

   @Override
   public void onStop() {
       Log.d(TAG, "MediaSessionCallback: onStop()");
       // Handle differently based on your use case.
   }

   @Override
   public void onSkipToNext() {
       Log.d(TAG, "MediaSessionCallback: onSkipToNext()");
       playAndUpdateMediaSession(...);
   }

   @Override
   public void onSkipToPrevious() {
       Log.d(TAG, "MediaSessionCallback: onSkipToPrevious()");
       playAndUpdateMediaSession(...);
   }
}

Continue learning

Check out the following articles and training documents to continue learning about MediaSession and Video apps.

To play around with the Google Assistant on Android TV, download the sample app and run it on Nvidia Shield running Android M or above.

If you would like to continue the discussion, leave a response or talk to me on Twitter.

Android Excellence: congratulations to the new apps and games for Fall 2017

Posted by Kacey Fahey, Developer Marketing, Google Play

Android Excellence recognizes some of the highest quality apps and games on Google Play. With a strong focus on great design, an engaging user experience, and strong app performance, this set of apps and games show the diversity of content on Google Play. Whether you're trying to better manage personal finances with Money Lover or want to experience the thrill of stunt-racing with stunning graphics and real-time challenges in Asphalt 8, there's something for everyone to enjoy.

One new awardee is Bring!, a simple-to-use app that helps manage your grocery lists. Use the existing catalog of items or add your own product photos, then share your lists and message in-app to let others know when it's time to shop. If you're looking for a new game to play, Karma. Incarnation 1. is a "wonderfully weird, puzzle-filled indie adventure game." With beautiful hand-drawn art, you guide the story's hero through moments of humor and challenge to be reunited with his love.

Congratulations to the new Android Excellence apps and games for Fall 2017.

New Android Excellence apps New Android Excellence games
Agoda Asphalt 8
AlarmMon Bubble Witch 3 Saga
Bring! Castle Creeps
CastBox Crab War
Email by Edison Crash of Cars
Eve Dan the Man
Fotor Dawn of Titans
Mint Dream Defense
Money Lover Iron Marines
Onefootball Karma. Incarnation 1.
Robinhood Postknight
Viki Sky Force Reloaded
Zombie Age 3

Explore other great apps and games in the Editors’ Choice section on Google Play.

How useful did you find this blogpost?

Android Wear Beta

Posted by Hoi Lam, Lead Developer Advocate, Android Wear
LG Watch Sport

Today, we are launching the beta of the next Android Wear update. As we mentioned at Google I/O, this will mainly be a technical upgrade to API 26 with enhancements to background limits and notification channels. LG Watch Sport users can go to this webpage to sign up and the factory image will automatically be downloaded to the watch you enroll. As this is a beta, please be sure to review the known issues before enrolling. If you don't have a watch to test on, you can use the Android emulator. For developers working with Android Wear for China, an updated emulator image is also available.

Notification Channels

In this update, users can choose the types of notifications they receive via an app through notification channels. This gives users finer-grained control than muting all notifications from the app. For notifications generated locally by Android Wear apps, users will be able to customise the notifications channel they want to see, right on their watch. Please refer to the Wear notification sample for more details. For notifications bridged from the phone, the phone notifications channel settings will dictate what is shown on the watch.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    mNotificationManager.createNotificationChannel(
        NotificationChannel("1001", "New Follower",
            NotificationManager.IMPORTANCE_DEFAULT))

    mNotificationManager.createNotificationChannel(
        NotificationChannel("1002", "Likes",
            NotificationManager.IMPORTANCE_LOW))
}

Background Limits

There are increased restrictions on background services. Developers should assume services can no longer run in the background without a visible notification. In addition, the background location update frequency will be reduced. Battery-saving best practices such as using JobScheduler should be adopted to ensure your app is battery-efficient and able to perform background tasks when possible.

Please give us your feedback

We expect this to be the only beta release before the final production release. Thank you for your feedback so far. Please submit any bugs you find via the Android Wear issue tracker. The earlier you submit them, the higher the likelihood that we can include the fixes in the final release.

Keystore Key Attestation

Posted by Shawn Willden, Software Engineer

Android's keystore has been available for many years, providing app developers with a way to use cryptographic keys for authentication and encryption. Keystore keeps the key material out of the app's process space, so that the app cannot inadvertently reveal it to the user where it could be phished, leak it through some other channel, or have it compromised in the event of a compromise of the app. Many devices also provide hardware-based security for keystore keys in secure hardware, which keeps the key material out of the Android system entirely, so that the key material cannot be leaked even by a Linux kernel compromise. In the vast majority of Android devices, secure hardware is a special mode of the main CPU, with hardware-enforced isolation from the Linux kernel and Android userspace. Alternatively, some devices use a separate secure microprocessor.

Android provides APIs that allow the app to determine whether a given keystore key is in secure hardware, but these APIs could be unreliable if the operating system has been compromised. Key attestation provides a way for a device's secure hardware to verify that an asymmetric key is in secure hardware, protected against compromise of the Android OS.

History of Keystore

Keystore was originally introduced in Android 4.0 and keys were encrypted with the user's passcode. In Android 4.1 the infrastructure to use device secure hardware was added.

Up until Android 6.0, Keystore supported RSA and ECDSA. In Android 6.0, Keystore was significantly enhanced, adding support for AES and HMAC. Also, other crucial elements of cryptographic operations, such as RSA padding1 and AES block chaining2 modes were moved into secure hardware.

In Android 6.0, Keystore also gained the ability to restrict the ways in which a particular key could be used. The most obviously useful restriction that can be applied is user authentication binding. This allows a key's usage to be "bound" to the user's passcode—their PIN, pattern, or password—or fingerprint. For passcode authentication binding, the app developer can specify a timeout in seconds. If more than the specified time has elapsed since the user last entered their passcode, the secure hardware refuses any requests to use the key. Fingerprint-bound keys require a new user authentication each time the key is used.

Other, more technical, restrictions can be applied to Android 6.0+ keys as well. In particular, at point of key creation or import, it is necessary to specify the cryptographic purposes (encrypt, decrypt, sign, or verify) for which the key may be used, as well as padding and block modes, digests, source of entropy for initialization vectors or nonces, and other details of the cryptographic operation. Because the specified information is permanently and cryptographically bound to the key material, Keystore won't allow the key to be used in any other way. Therefore, an attacker who gains control of the app or the system can't misuse the key. To help prevent attacks, developers should specify the narrowest possible range of uses for a given key.

One of the most important changes to Android Keystore was introduced in Android 7.0. New devices that launch with Android 7.0+ with a secure lock screen must have secure hardware and support hardware-based passcode authentication and keystore keys. Prior to Android 7.0, secure hardware support was widespread, but over the next few years it will become universal.

In Android 8.0, key attestation was made mandatory for all new devices that ship with Google Play installed.

Why use key attestation?

Suppose you're developing an app to provide a bank's customers with access to their bank balance, transaction history, and bill pay system. Security is important; you don't want anyone who picks up the user's phone to have access to their the bank account. One approach would be to use the user's web site password. But that's often inconvenient for the user because web sites often demand long, complex passwords, which are inconvenient on a small touchscreen.

With Android Keystore, you can generate an asymmetric authentication key, such as a 256-bit ECDSA key, and have each user sign in with their complex web password once, then register the public key in the bank's customer account database. Each time they open the app, you can execute a challenge-response authentication protocol using that ECDSA key. Further, if you make the key authentication-bound, the user can authenticate with their lock screen passcode or fingerprint each time they open the app. That allows them to use the simpler and more convenient authentication mechanism on their phone.

If an attacker compromises Android and attempts to extract the key, they shouldn't be able to because the key is in secure hardware.

As an app developer, key attestation allows you to verify on your server that the ECDSA key your app requested actually lives in secure hardware. Note that there's little point in using the attestation in your app itself; if the Android OS is uncompromised and trustworthy, then you can just use the KeyInfo class introduced in 6.0 to discover whether the key is in secure hardware. If it is compromised, then that API and any attempt you make to validate the attestation on device are both unreliable.

Note that key attestation is distinct from SafetyNet attestation. They're the same concept, but attest to different things and come from different places. Keystore key attestation affirms that a crypto key lives in secure hardware and has specific characteristics. SafetyNet attestation affirms that a device is real (not an emulator) and that it's running known software. SafetyNet uses Keystore key attestation under the covers, so if you want to know about device integrity use that. If you want to confirm that your key is in secure hardware, use key attestation.

For details and sample code, see the key attestation training article on developer.android.com.

Notes


  1. Keystore supports the recommended OAEP and PSS padding modes for RSA encryption and signing, respectively, as well as the older PKCS#1 v1.5 modes. 

  2. Keystore supports GCM, CBC and ECB block chaining modes.