Minimum system requirements
The mobile device must be running Android 5.0 (API level 21) or later.
Maven configuration
Driver SDK versions 4.99 and later are available via the Google Maven repository.
Gradle
Add the following to your build.gradle
file:
repositories {
...
google()
}
Maven
Add the following to your pom.xml
file:
<project>
...
<repositories>
<repository>
<id>google-maven-repository</id>
<url>https://maven.google.com</url>
</repository>
</repositories>
...
</project>
Project configuration
To use the Driver SDK, your app must target
minSdkVersion
21 or higher. For more
information, see
Release Notes.
To run an app built with the Driver SDK, the Android device must have Google Play services installed.
Set up your development project
To set up your development project and get an API key for the project on the Google Cloud Console:
Create a new Google Cloud Console project, or select an existing project, for use with the Driver SDK. Wait a few minutes until the new project is visible on the Google Cloud Console.
In order to run the demo app, your project must have access to the Maps SDK for Android. In the Google Cloud Console, select APIs & Services > Library, then search for and enable the Maps SDK for Android.
Get an API key for the project by selecting APIs & Services > Credentials > Create credentials > API key. For more information about getting an API key, see Get an API key.
Add the Driver SDK to your app
The Driver SDK is available via a private Maven repository. The repository includes the SDK's Project Object Model (.pom) files and Javadocs. To add the Driver SDK to your app:
Add the following dependency to your Gradle or Maven configuration, substituting the
VERSION_NUMBER
placeholder for the desired version of the Driver SDK.Gradle
Add the following to your
build.gradle
:dependencies { ... implementation 'com.google.android.libraries.mapsplatform.transportation:transportation-driver:VERSION_NUMBER' }
Maven
Add the following to your
pom.xml
:<dependencies> ... <dependency> <groupId>com.google.android.libraries.mapsplatform.transportation</groupId> <artifactId>transportation-driver</artifactId> <version>VERSION_NUMBER</version> </dependency> </dependencies>
Update your app's manifest
Once you have added the Driver SDK to your app, you can
update your app's manifest by editing its AndroidManifest.xml
file.
Add your API key within the <application>
element. You must use the
project API key you obtained when you
set up your development project.
For example, replace PASTE_YOUR_API_KEY_HERE
with your API key
in the following application meta-data:
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="PASTE_YOUR_API_KEY_HERE"/>
The following example shows a complete manifest for a sample app:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.driverapidemo" >
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/_AppTheme" >
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="PASTE_YOUR_API_KEY_HERE"/>
<activity android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Include the required attributions in your app
If you use the Driver SDK in your app, you must include attribution text and open source licenses as part of your app's legal notices section. It's best to include the attributions as an independent menu item or as part of an About menu item.
You can find the required attribution text and open source licenses in the Driver SDK zip file:
NOTICE.txt
LICENSES.txt
Dependencies
The Driver SDK uses gRPC to communicate to the Fleet Engine server. If you don't already bring in gRPC, you may need to declare the following dependencies:
dependencies {
implementation 'io.grpc:grpc-android:1.12.0'
implementation 'io.grpc:grpc-okhttp:1.12.0'
}
Without these dependencies, the Driver SDK will experience errors at runtime while trying to communicate with the Fleet Engine server.
If you use ProGuard to optimize your builds, you may need to add the following lines to your ProGuard configuration file:
-dontwarn com.google.**
-dontwarn io.grpc.**
-dontwarn okio.**
The minimum API level supported is 21.
Initializing the SDK
A provider ID (usually the Google Cloud Project ID) is required in order to initialize the FleetEngine object. For more detail about setting up the Google Cloud Project, see Authentication and Authorization.
Before using the Driver SDK, you must first initialize the Navigation SDK. To initialize the SDK:
Obtain a
Navigator
object from theNavigationApi
.NavigationApi.getNavigator( this, // Activity new NavigationApi.NavigatorListener() { @Override public void onNavigatorReady(Navigator navigator) { // Keep a reference to the Navigator (used to configure and start nav) this.navigator = navigator; } } );
Create a
DriverContext
object, populating the required fields.DriverContext driverContext = DriverContext.builder(application) .setProviderId(providerId) .setVehicleId(vehicleId) .setAuthTokenFactory(authTokenFactory) .setNavigator(navigator) .setRoadSnappedLocationProvider( NavigationApi.getRoadSnappedLocationProvider(application)) .build()
Use the
DriverContext
object to initialize the*DriverApi
.DeliveryDriverApi driverApi = DeliveryDriverApi.createInstance(driverContext);
Obtain the
DeliveryVehicleReporter
from the API object. (DeliveryVehicleReporter
extendsNavigationVehicleReporter
.)DeliveryVehicleReporter vehicleReporter = driverApi.getDeliveryVehicleReporter();
Authenticating with AuthTokenFactory
When the Driver SDK generates location updates,
it must send these updates to
the Google Fleet Engine server. In order to authenticate these requests, the
Driver SDK will call out to a caller-provided
instance of AuthTokenFactory
.
The factory is responsible for generating authentication tokens at location
update time.
How exactly tokens are generated will be specific to each developer's situation. However, the implementation will probably need to:
- fetch an authentication token, possibly in JSON format, from an HTTPS server
- parse and cache the token
- refresh the token when it expires
For details of the tokens expected by the Fleet Engine server, see Creating a JSON Web Token (JWT) for authorization.
Here is a skeleton implementation of an AuthTokenFactory:
class JsonAuthTokenFactory implements AuthTokenFactory {
String vehicleServiceToken; // initially null
long expiryTimeMs = 0;
// This method is called on a thread whose only responsibility is to send
// location updates. Blocking is OK, but just know that no location updates
// can occur until this method returns.
@Override public String getToken(AuthTokenContext authTokenContext) {
if (System.currentTimeMillis() > expiryTimeMs) {
// The token has expired, go get a new one.
fetchNewToken(vehicleId);
}
if (ServiceType.VEHICLE.equals(authTokenContext.getServiceType)) {
return vehicleServiceToken;
} else {
throw new RuntimeException("Unsupported ServiceType: " + authTokenContext.getServiceType());
}
}
private void fetchNewToken(String vehicleId) {
String url = "https://yourauthserver.example/token/" + vehicleId;
try (Reader r = new URL(url).openStream()) {
com.google.gson.JsonObject obj
= new com.google.gson.JsonParser().parse(r);
vehicleServiceToken = obj.get("VehicleServiceToken").getAsString();
expiryTimeMs = obj.getAsLong("TokenExpiryMs");
// The expiry time could be an hour from now, but just to try and avoid
// passing expired tokens, we subtract 10 minutes from that time.
expiryTimeMs -= 10 * 60 * 1000;
} catch (IOException e) {
// It's OK to throw exceptions here, the StatusListener you passed to
// create the FleetEngine class will be notified and pass along the failed
// update warning.
throw new RuntimeException("Could not get auth token", e);
}
}
}
This particular implementation uses the built-in Java HTTP client to fetch a token in JSON format from the developer's authentication server. The token is saved for reuse. The token is re-fetched if the old token is within 10 minutes of its expiry time.
Your implementation may do things differently, such as using a background thread to refresh tokens.
Exceptions in AuthTokenFactory
will be treated as transient unless they happen
repeatedly. After a number of attempts, the Driver SDK
will assume that the
error is permanent and will stop trying to send updates.
Status and Error Reporting with StatusListener
Since the Driver SDK performs actions in the
background, use the StatusListener
to trigger notifications when certain
events occur, such as errors, warnings, or debug messages. Errors may be
transient in nature (such as BACKEND_CONNECTIVITY_ERROR
), or they may
cause location updates to be stopped permanently (such as VEHICLE_NOT_FOUND
,
indicating a configuration error).
You provide an optional StatusListener
implementation like the following:
class MyStatusListener implements StatusListener {
/** Called when background status is updated, during actions such as location reporting. */
@Override
public void updateStatus(
StatusLevel statusLevel,
StatusCode statusCode,
String statusMsg) {
// Status handling stuff goes here.
// StatusLevel may be DEBUG, INFO, WARNING, or ERROR.
// StatusCode may be DEFAULT, UNKNOWN_ERROR, VEHICLE_NOT_FOUND,
// BACKEND_CONNECTIVITY_ERROR, or PERMISSION_DENIED.
}
}
Notes on SSL/TLS
Internally, the Driver SDK implementation uses
SSL/TLS to communicate securely
with the Fleet Engine server. Older versions of Android (API versions 21 or
lower) may require a SecurityProvider
patch to be able to communicate with the
server. You should see this
article
for more information about working with SSL in Android. The article also
contains code samples for patching the security provider.
Enabling location updates
Once you have a *VehicleReporter
instance, enabling location updates is
straightforward:
DeliveryVehicleReporter reporter = ...;
reporter.enableLocationTracking();
Location updates will be sent at a regular interval, if possible. Each location update will also indicate that the vehicle is online.
By default, the reporting interval is 10 seconds, but the reporting interval can be changed with
FleetEngine.setLocationReportingInterval(long, TimeUnit)
. The minimum supported
update interval is 5 seconds. More frequent updates may result in slower requests and errors.
Disabling location updates
When the driver's shift is finished, location updates can be stopped by calling
DeliveryVehicleReporter.disableLocationTracking
.
Trusted model use cases
This section describes how to use the Driver SDK to implement common use cases when using the trusted model.
Create a vehicle
You can create a vehicle from the Driver SDK.
Before creating a vehicle, make sure to initialize the Delivery Driver API. The vehicle ID must be created with the vehicle and provider IDs used during Driver SDK initialization. Then create the vehicle as shown in the following example:
DeliveryDriverApi api = DeliveryDriverApi.getInstance();
DeliveryVehicleManager vehicleManager = api.getDeliveryVehicleManager();
try {
DeliveryVehicle vehicle = vehicleManager.createVehicle().get();
// Handle CreateVehicleRequest DeliveryVehicle response.
} catch (Exception e) {
// Handle CreateVehicleRequest error.
}
Create a shipment pickup task
You can create a shipment pickup task from the Driver SDK.
Before creating a task, make sure to initialize the Delivery Driver API. The task must be created using the provider ID specified during Driver SDK initialization. Then create the shipment pickup task as shown in the following example. For information about task IDs, see Task ID examples.
static final String TASK_ID = "task-8241890"; // Avoid auto-incrementing IDs.
DeliveryDriverApi api = DeliveryDriverApi.getInstance();
DeliveryTaskManager taskManager = api.getDeliveryTaskManager();
CreateDeliveryTaskRequest request = CreateDeliveryTaskRequest.builder(TASK_ID)
.setPlannedWaypoint(Waypoint.builder().setLatLng(-6.195139, 106.820826).build())
.setTaskDurationSeconds(2 * 60)
.setParentId("my-tracking-id")
.setTaskType(TaskType.DELIVERY_PICKUP)
.build();
try {
DeliveryTask task = taskManager.createTask(request).get();
// Handle CreateTaskRequest DeliveryTask response.
} catch (Exception e) {
// Handle CreateTaskRequest error.
}
Create a shipment delivery task
You can create a shipment delivery task from the Driver SDK.
Before creating a task, make sure to initialize the Delivery Driver API. Then create the shipment delivery task as shown in the following example. For information about task IDs, see Task ID examples.
static final String TASK_ID = "task-8241890"; // Avoid auto-incrementing IDs.
DeliveryDriverApi api = DeliveryDriverApi.getInstance();
DeliveryTaskManager taskManager = api.getDeliveryTaskManager();
CreateDeliveryTaskRequest request = CreateDeliveryTaskRequest.builder(TASK_ID)
.setPlannedWaypoint(Waypoint.builder().setLatLng(-6.195139, 106.820826).build())
.setTaskDurationSeconds(2 * 60)
.setParentId("my-tracking-id")
.setTaskType(TaskType.DELIVERY_DELIVERY)
.build();
try {
DeliveryTask task = taskManager.createTask(request).get();
// Handle CreateTaskRequest DeliveryTask response.
} catch (Exception e) {
// Handle CreateTaskRequest error.
}
Scheduled unavailability
You can create a task indicating unavailability (for example, for driver breaks or vehicle refueling) from the Driver SDK. A scheduled unavailability task must not include a tracking ID. You may optionally provide a location.
Before creating a task, make sure to initialize the Delivery Driver API. Then create the unavailability task as shown in the following example. For information about task IDs, see Task ID examples.
static final String TASK_ID = "task-8241890"; // Avoid auto-incrementing IDs.
DeliveryDriverApi api = DeliveryDriverApi.getInstance();
DeliveryTaskManager taskManager = api.getDeliveryTaskManager();
CreateDeliveryTaskRequest request = CreateDeliveryTaskRequest.builder(TASK_ID)
.setTaskDurationSeconds(2 * 60) // Duration or location (or both) must be provided for a BREAK task.
.setTaskType(TaskType.UNAVAILABLE)
.build();
try {
DeliveryTask task = taskManager.createTask(request).get();
// Handle CreateTaskRequest DeliveryTask response.
} catch (Exception e) {
// Handle CreateTaskRequest error.
}
Scheduled stops
You can create a scheduled stop task from the Driver SDK. A scheduled stop task cannot include a tracking ID.
Before creating a task, make sure to initialize the Delivery Driver API. Then create the scheduled stop task as shown in the following example. For information about task IDs, see Task ID examples.
static final String TASK_ID = "task-8241890"; // Avoid auto-incrementing IDs.
DeliveryDriverApi api = DeliveryDriverApi.getInstance();
DeliveryTaskManager taskManager = api.getDeliveryTaskManager();
CreateDeliveryTaskRequest request = CreateDeliveryTaskRequest.builder(TASK_ID)
.setPlannedWaypoint(Waypoint.builder().setLatLng(-6.195139, 106.820826).build())
.setTaskDurationSeconds(2 * 60)
.setTaskType(TaskType.DELIVERY_SCHEDULED_STOP)
.build();
try {
DeliveryTask task = taskManager.createTask(request).get();
// Handle CreateTaskRequest DeliveryTask response.
} catch (Exception e) {
// Handle CreateTaskRequest error.
}
Update task ordering
You can update the order of execution of tasks assigned to a vehicle from the Driver SDK.
Updating the task ordering also assigns tasks to a vehicle if they weren't previously assigned to a vehicle. It also closes tasks that were previously assigned to a vehicle and were left out of the updated ordering. Assigning a task to a different vehicle if it had previously been assigned to another vehicle generates an error. Before assigning a task to the new vehicle, close the existing task and then create a new task.
You can update task ordering at any time.
Before updating the task ordering for a vehicle, make sure that the vehicle and tasks have already been created in Fleet Engine. Then update the task ordering for the vehicle as shown in the following example.
DeliveryDriverApi api = DeliveryDriverApi.getInstance();
DeliveryVehicleReporter reporter = api.getDeliveryVehicleReporter();
try {
List<VehicleStop> stops = reporter.setVehicleStops(
ImmutableList.of(
VehicleStop.builder()
.setVehicleStopState(VehicleStopState.ARRIVED)
.setWaypoint(Waypoint.builder().setLatLng(37.1749, 122.412).build())
.setTasks(ImmutableList.of(task1)) // Previously created DeliveryTask in Fleet Engine.
.build(),
VehicleStop.builder()
.setVehicleStopState(VehicleStopState.NEW) // The current vehicle stop.
.setWaypoint(Waypoint.builder().setLatLng(37.7749, 122.4194).build())
.setTasks(ImmutableList.of(task2)) // Previously created DeliveryTask in Fleet Engine.
.build(),
VehicleStop.builder()
.setVehicleStopState(VehicleStopState.NEW)
.setWaypoint(Waypoint.builder().setLatLng(37.3382, 121.8863).build())
.setTasks(ImmutableList.of(task3, task4)) // Previously created DeliveryTasks in Fleet Engine.
.build())).get();
// Successfully updated vehicle stops in Fleet Engine. Returns the successfully set VehicleStops.
} catch (Exception e) {
// Failed to update vehicle stops in Fleet Engine. Setting VehicleStops must be attempted again after resolving
// errors.
}
An exception might occur that could prevent an update
to the Driver SDK's internal state. If this happens, resolve the issue and then
call setVehicleStops
again until the call is successful.
Potential issues can include:
The specified VehicleStops don't follow a valid pattern. Only the first VehicleStop can be in any of the VehicleStopStates: NEW, ENROUTE, or ARRIVED. VehicleStops after the current stop must be in the NEW VehicleStopState.
Tasks either don't exist, or they belong to a different vehicle.
The vehicle doesn't exist.
Vehicle is enroute to the next stop
Fleet Engine must be notified when a vehicle departs from a stop, and when it begins navigation. You can notify Fleet Engine from the Driver SDK.
Before notifying Fleet Engine that a vehicle departed from a stop, ensure that the vehicle stops have been created and set. Then notify Fleet Engine of the vehicle's departure as shown in the following example.
DeliveryDriverApi api = DeliveryDriverApi.getInstance();
DeliveryVehicleReporter reporter = api.getDeliveryVehicleReporter();
reporter.enableLocationTracking(); // Location tracking must be enabled.
// Create Vehicle, VehicleStops, and DeliveryTasks.
// Set VehicleStops on Vehicle.
navigator.setDestination(vehicleStop.getWaypoint());
try {
List<VehicleStop> updatedStops = reporter.enrouteToNextStop().get();
// Successfully updated vehicle stops in Fleet Engine. Returns the set VehicleStops, with the first
// VehicleStop updated to ENROUTE state.
} catch (Exception e) {
// Failed to update vehicle stops in Fleet Engine. Updating VehicleStops must be attempted again
// after resolving errors.
}
An exception might occur that could prevent an update to the Driver SDK's
internal state. If this happens, resolve the issue and then call
enrouteToNextStop
again until it is successful.
Potential issues can include:
- No remaining
VehicleStops
set in the Driver SDK.
Vehicle arrives at a stop
Fleet Engine must be notified when a vehicle arrives at a stop. You can notify Fleet Engine from the Driver SDK.
Before notifying Fleet Engine that a vehicle arrived at a stop, ensure that the vehicle stops have been set. Then notify Fleet Engine of the vehicle's arrival at the stop as shown in the following example.
DeliveryDriverApi api = DeliveryDriverApi.getInstance();
DeliveryVehicleReporter reporter = api.getDeliveryVehicleReporter();
reporter.enableLocationTracking(); // Location tracking must be enabled.
// Create Vehicle, VehicleStops, and DeliveryTasks.
// Set VehicleStops on Vehicle.
// Mark ENROUTE to VehicleStop and start guidance using Navigator.
try {
List<VehicleStop> updatedStopsArrived = reporter.arrivedAtStop().get();
// Successfully updated vehicle stops in Fleet Engine. Returns the set VehicleStops, with the first
// VehicleStop updated to ARRIVED state.
navigator.clearDestinations();
} catch (Exception e) {
// Failed to update vehicle stops in Fleet Engine. Updating VehicleStops must be attempted again
// after resolving errors.
}
An exception can occur that could prevent an update to the Driver SDK's internal
state. If this happens, resolve the issue and then call arrivedAtStop
again
until it is successful.
Potential issues might include:
- No remaining
VehicleStops
set in the Driver SDK.
Vehicle completes a stop
Fleet Engine must be notified when a vehicle completes a stop. This causes all tasks associated with the stop to be set to a CLOSED state. You can notify Fleet Engine from the Driver SDK.
Notify Fleet Engine that the vehicle has completed its VehicleStop as shown in the following example.
DeliveryDriverApi api = DeliveryDriverApi.getInstance();
DeliveryVehicleReporter reporter = api.getDeliveryVehicleReporter();
reporter.enableLocationTracking(); // Location tracking must be enabled.
// After completing the tasks at the VehicleStop, remove it from the
// the current list of VehicleStops.
try {
List<VehicleStop> updatedStopsCompleted = reporter.completedStop().get();
// Successfully updated vehicle stops in Fleet Engine. All tasks on the completed stop are set to CLOSED.
// Returns the set VehicleStops, with the completed VehicleStop removed from the remaining list.
} catch (Exception e) {
// Failed to update vehicle stops in Fleet Engine. Updating VehicleStops must be attempted again
// after resolving errors.
}
An exception can occur that could prevent an update to the Driver SDK's internal
state. If this happens, resolve the issue and then call completedStop
again
until it is successful.
Potential issues can include:
- No remaining
VehicleStops
set in the Driver SDK.
Close a task
To close a task that has been assigned to a vehicle, either notify Fleet Engine that the vehicle has completed the stop where the task takes place, or remove it from the list of vehicle stops. To do so, you can set the list of the remaining vehicle stops just as when updating the task ordering for a vehicle.
If a task was not yet assigned a vehicle, and it needs to be closed, update the task to a CLOSED state. However, you can not reopen a CLOSED task.
Closing a task does not indicate success or failure. It indicates that the task is no longer considered in progress. For shipment tracking, it is important to indicate the actual outcome of a task so a delivery outcome can be shown.
A task must be assigned to a vehicle in order to be able to use the Driver SDK to close the task. To close a task that has been assigned to a vehicle, notify Fleet Engine that the vehicle has completed the stop where the task takes place.
Alternatively, update the task ordering of the vehicle that the task is assigned to, and then remove the desired task from the list of stops.
Setting the task outcome and outcome location
Closing a task doesn't indicate success or failure. It indicates that the task is no longer considered in progress. For shipment tracking, it's important to indicate the actual outcome of a task so a delivery outcome can be shown, and so that there's proper billing for the services. Once set, you cannot change the task outcome. But you can modify task outcome time and task outcome location after you have set them.
Tasks that are in the CLOSED state can have their outcome set to either SUCCEEDED or FAILED. Fleet Engine charges only delivery tasks with a state of SUCCEEDED.
When marking the outcome of a task, Fleet Engine automatically fills-in the task outcome location with the last known vehicle location. You can override this behavior by calling Fleet Engine. Note that you cannot set task outcome location using the Driver SDK.
The following code example demonstrates how to use the Driver SDK to set a task outcome and timestamp.
static final String TASK_ID = "task-8241890";
DeliveryDriverApi api = DeliveryDriverApi.getInstance();
DeliveryTaskManager taskManager = api.getDeliveryTaskManager();
// Updating an existing DeliveryTask which is already CLOSED. Manually
// setting TaskOutcomeLocation with Driver SDK is not supported at this time.
UpdateDeliveryTaskRequest req = UpdateDeliveryTaskRequest.builder(TASK_ID)
.setTaskOutcome(TaskOutcome.SUCCEEDED)
.setTaskOutcomeTimestamp(now()) // Timestamp in milliseconds.
.build();
try {
DeliveryTask updatedTask = taskManager.updateTask(req);
// Handle UpdateTaskRequest DeliveryTask response.
} catch (Exception e) {
// Handle UpdateTaskRequest error.
}
Look up a vehicle
You can look up a vehicle from the Driver SDK. Before looking up a vehicle, ensure that you initialize the Delivery Driver API. You can then lookup the vehicle as shown in the following example.
DeliveryDriverApi api = DeliveryDriverApi.getInstance();
DeliveryVehicleManager vehicleManager = api.getDeliveryVehicleManager();
try {
DeliveryVehicle vehicle = vehicleManager.getVehicle().get();
// Handle GetVehicleRequest DeliveryVehicle response.
} catch (Exception e) {
// Handle GetVehicleRequest error.
}
The DeliveryVehicleManager
can only look up the DeliveryVehicle
for the vehicle ID that was provided during Delivery Driver API initialization.