Getting started with Fleet Engine

The Fleet Engine On-demand Rides and Deliveries API lets you manage trips and vehicle state for your Trip and Order Progress applications. It handles transactions between the Driver SDK, the Consumer SDK, and your backend service -- which can communicate with the Fleet Engine by making either gRPC or REST calls.

Prerequisites

For development, ensure that you install the Cloud SDK (gcloud) and are authenticated to your project.

shell

gcloud auth login

You should see a success message such as:

You are now logged in as [my-user@example.com].
Your current project is [project-id].  You ...

Check that On-demand Rides and Deliveries Solution Fleet Engine APIs are configured appropriately.

shell

gcloud --project=project-id services enable fleetengine.googleapis.com

If this command results in an error, check with your project administrator and your Google support representative to get access.

Logging

The Fleet Engine can write log messages about API calls it receives into Google Cloud platform logs. See Cloud Logging documentation for an overview of how to read and analyze logs.

Logging might not be enabled by default for projects created prior to Feb 10, 2022. See the logging documentation for more details.

Client Libraries

We publish client libraries in several common programming languages. These libraries will help provide a better developer experience over raw REST or gRPC. For instructions on how to obtain client libraries for your server application, see Client Libraries.

The Java examples in this documentation assume familiarity with gRPC.

Authentication and Authorization

You can configure capabilities provided by Trip and Order Progress through the Google Cloud Console. These APIs and SDKs require the use of JSON Web Tokens that have been signed using service accounts created from Cloud Console.

Cloud project setup

To set up your cloud project, first create your project and then create service accounts.

To create your Google Cloud project:

  1. Create a Google Cloud Project using the Google Cloud Console.
  2. Using the APIs and Services Dashboard, enable the Local Rides and Deliveries API.

Service accounts are associated with one or more roles. They are used to create JSON Web Tokens that grant different sets of permissions depending on the roles. Typically, to reduce the possibility of abuse you can create multiple service accounts, each with the minimum set of roles required.

Trip and Order Progress uses the following roles:

RoleDescription
Fleet Engine Consumer SDK User

roles/fleetengine.consumerSdkUser
Grants permission to search for vehicles and to retrieve information about vehicles and trips. Tokens created by a service account with this role are typically used from your ridesharing or delivery consumer app mobile devices.
Fleet Engine Driver SDK User

roles/fleetengine.driverSdkUser
Grants permission to update vehicle locations and routes and to retrieve information about vehicles and trips. Tokens created by a service account with this role are typically used from your ridesharing or delivery driver app mobile devices.
Fleet Engine Service Super User

roles/fleetengine.serviceSuperUser
Grants permission to all vehicles and trips APIs. Tokens created by a service account with this role are typically used from your backend servers.

For example, create a service account for each of the three roles and assign them their respective roles.

gcloud --project=project-id iam service-accounts create fleet-engine-consumer-sdk
gcloud projects add-iam-policy-binding project-id \
       --member=serviceAccount:fleet-engine-consumer-sdk@project-id.iam.gserviceaccount.com \
       --role=roles/fleetengine.consumerSdkUser

gcloud --project=project-id iam service-accounts create fleet-engine-driver-sdk
gcloud projects add-iam-policy-binding project-id \
       --member=serviceAccount:fleet-engine-driver-sdk@project-id.iam.gserviceaccount.com \
       --role=roles/fleetengine.driverSdkUser

gcloud --project=project-id iam service-accounts create fleet-engine-su
gcloud projects add-iam-policy-binding project-id \
       --member=serviceAccount:fleet-engine-su@project-id.iam.gserviceaccount.com \
       --role=roles/fleetengine.serviceSuperUser

The Driver and Consumer SDKs are built around these standard roles.

Alternatively, it is possible to create custom roles which allow an arbitrary set of permissions to be bundled together. The Driver and Consumer SDKs will show error messages whenever a required permission is missing. As a result, we strongly recommend using the standard set of roles presented above and not using custom roles.

For convenience, if you need to create JWT tokens for untrusted clients, adding users to the Service Account Token Creator Role lets them create tokens with gcloud command line tools.

gcloud projects add-iam-policy-binding project-id \
       --member=user:my-user@example.com \
       --role=roles/iam.serviceAccountTokenCreator

Where my-user@example.com is the email used to authenticate with gcloud (gcloud auth list --format='value(account)').

Fleet Engine Auth Library

Fleet Engine uses JSON Web Tokens (JWTs) to restrict access to Fleet Engine APIs. The new Fleet Engine Auth Library, available on Github, simplifies construction of Fleet Engine JWTs and securely signs them.

The library provides the following benefits:

  • Simplifies the process of creating Fleet Engine Tokens.
  • Provides token signing mechanisms other than using credential files (such as impersonating a service account.)
  • Attaches signed tokens to outbound requests made from either a gRPC stub or GAPIC client.

Creating a JSON Web Token (JWT) for authorization

When not using the Fleet Engine Auth Library, JSON Web Tokens (JWTs) need to be crafted directly within your codebase. This requires you to have both a deep understanding of JWTs and how they relate to Fleet Engine. This is why we HIGHLY recommend taking advantage of the Fleet Engine Auth Library.

Within Fleet Engine, JSON Web Tokens (JWTs) provide short-lived authentication and ensure that devices may only modify vehicles, trips, or tasks for which they are authorized. JWTs contain a header and a claim section. The header section contains information such as the private key to use (obtained from service accounts) and the encryption algorithm. The claim section contains information such as the token's create time, tokens time to live, services that it is claiming access to, and other authorization information to scope down access; for example, the vehicle ID.

A JWT header section contains the following fields:

FieldDescription
alg The algorithm to use. `RS256`.
typ The type of token. `JWT`.
kid Your service account's private key ID. You can find this value in the `private_key_id` field of your service account JSON file. Make sure to use a key from a service account with the correct level of permissions.

A JWT claims section contains the following fields:

FieldDescription
iss Your service account's email address.
sub Your service account's email address.
aud Your service account's SERVICE_NAME, in this case https://fleetengine.googleapis.com/
iat The timestamp when the token was created, specified in seconds elapsed since 00:00:00 UTC, January 1, 1970. Allow 10 minutes for skew. If the timestamp is too far in the past, or in the future, the server might report an error.
exp The timestamp when the token expires, specified in seconds elapsed since 00:00:00 UTC, January 1, 1970. The request fails if the timestamp is more than one hour in the future.
authorization Depending on the use case, may contain `vehicleid` or `tripid`.

Creating a JWT token refers to signing it. For instructions and code samples for creating and signing the JWT, see Service account authorization without OAuth. You can then attach a signed token to gRPC calls or other methods used to access Fleet Engine.

JWT Claims

When creating the JWT payload, add an additional claim in the authorization section with the key vehicleid or tripid set to the value of the vehicle ID or trip ID for which the call is being made.

The Driver SDK always uses the vehicleid claim, whether operating on a trip or vehicle. The Fleet Engine backend assures that the vehicle is associated with the requested trip before doing the modification.

The Consumer SDK always uses tripid claim.

The Rideshare or Delivery Provider should use the vehicleid or tripid with a "*" to match all Vehicles and Trips. Note that the JWT can contain both tokens, even if not required, which may simplify the token signing implementation.

JWT Use Cases

The following shows an example token for Provider server:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "private_key_id_of_provider_service_account"
}
.
{
  "iss": "provider@yourgcpproject.iam.gserviceaccount.com",
  "sub": "provider@yourgcpproject.iam.gserviceaccount.com",
  "aud": "https://fleetengine.googleapis.com/",
  "iat": 1511900000,
  "exp": 1511903600,
  "authorization": {
     "vehicleid": "*",
     "tripid": "*"
   }
}

The following shows an example token for Consumer app:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "private_key_id_of_consumer_service_account"
}
.
{
  "iss": "consumer@yourgcpproject.iam.gserviceaccount.com",
  "sub": "consumer@yourgcpproject.iam.gserviceaccount.com",
  "aud": "https://fleetengine.googleapis.com/",
  "iat": 1511900000,
  "exp": 1511903600,
  "authorization": {
     "tripid": "trip_54321"
   }
}

The following shows an example token for Driver app:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "private_key_id_of_driver_service_account"
}
.
{
  "iss": "driver@yourgcpproject.iam.gserviceaccount.com",
  "sub": "driver@yourgcpproject.iam.gserviceaccount.com",
  "aud": "https://fleetengine.googleapis.com/",
  "iat": 1511900000,
  "exp": 1511903600,
  "authorization": {
     "vehicleid": "driver_12345"
   }
}
  • For the kid field in the header, specify your service account's private key ID. You can find this value in the private_key_id field of your service account JSON file.
  • For the iss and sub fields, specify your service account's email address. You can find this value in the client_email field of your service account JSON file.
  • For the aud field, specify https://SERVICE_NAME/.
  • For the iat field, use the timestamp when the token was created, specified as seconds elapsed since 00:00:00 UTC, January 1, 1970. Allow 10 minutes for skew. If the timestamp is too far in the past, or in the future, the server might report an error.
  • For the exp field, use the timestamp when the token expires, specified as seconds since 00:00:00 UTC, January 1, 1970. The maximum allowed value is iat + 3600.

When signing the JWT to be passed to a mobile device, make sure to use the service account for the Driver or Consumer SDK role. Otherwise, the mobile device will have the ability to alter state it should not have.

Likewise, when signing the JWT to be used for privileged calls, make sure to use the service account with the Super User role. Otherwise, the operation will fail.

Generating a JWT for testing

Generating tokens from the terminal can be helpful when testing.

In order to follow these steps, your user account must have the Service Account Token Creator role:

gcloud projects add-iam-policy-binding project-id \
       --member=user:my-user@example.com \
       --role=roles/iam.serviceAccountTokenCreator

Create a new file called unsigned_token.json with the content below. The iat property is the current time in number of seconds after the epoch, which can be retrieved by running date +%s in your terminal. The exp property is the expiration time in number of seconds after the epoch, which can be calculated by adding 3600 to iat. The expiration time cannot be more than one hour in the future.

{
  "aud": "https://fleetengine.googleapis.com/",
  "iss": "super-user-service-account@project-id.iam.gserviceaccount.com",
  "sub": "super-user-service-account@project-id.iam.gserviceaccount.com",
  "iat": iat,
  "exp": exp,
  "authorization": {
     "vehicleid": "*",
     "tripid": "*"
   }
}

Then run the following gcloud command to sign the token on behalf of your Super User service account:

gcloud beta iam service-accounts sign-jwt --iam-account=super-user-service-account@project-id.iam.gserviceaccount.com unsigned_token.json signed_token.jwt

A signed Base64-encoded JWT should now be stored within the file signed_token.jwt. The token is valid for the next hour.

You can now test the token by running a curl command against the List Vehicles REST endpoint:

curl -X GET "https://fleetengine.googleapis.com/v1/providers/project-id/vehicles" -H "Authorization: Bearer $(cat signed_token.jwt)"

Vehicles and their lifecycle

The Vehicle is the entity representing a driver-vehicle pair. Currently, a Driver and Vehicle cannot be tracked separately. The Rideshare or Delivery Provider creates a Vehicle using a Provider ID (which must be the same as the Project ID of the Google Cloud Project which contains the service account used to call the Fleet Engine APIs) and a Rideshare or Delivery Provider-owned Vehicle ID.

A Vehicle which has not been updated via UpdateVehicle after seven days will be automatically deleted. It is an Error to call CreateVehicle with a Provider ID/Vehicle ID pair that already exists. The case of vehicles that are not updated frequently can be dealt with in two ways: frequently calling CreateVehicle with an expected Provider ID/Vehicle ID pair and discarding the error if the Vehicle already exists; or, calling CreateVehicle after an UpdateVehicle returns with a NOT_FOUND error.

Vehicle location updates

For the best performance with Fleet Engine, provide it with a stream of vehicle location updates. Use either of the following ways to provide these updates:

  1. Use the Driver SDK - Android, iOS -- simplest option.
  2. Use custom code -- useful if locations are relayed through your backend, or if you use devices other than Android or iOS.

Vehicle types

The Vehicle entity contains a required field of VehicleType, which contains a Category enum that can be specified as either AUTO, TAXI, TRUCK, TWO_WHEELER, BICYCLE, or PEDESTRIAN. The vehicle type can serve as a filter criteria in SearchVehicles and ListVehicles.

All routing for vehicles will use the corresponding RouteTravelMode if the category is set to either AUTO, TWO_WHEELER, BICYCLE, or PEDESTRIAN. If the category is set to TAXI or TRUCK, the routing is treated the same as the AUTO mode.

Vehicle attributes

The Vehicle entity contains a repeated field of VehicleAttribute. These attributes are not interpreted by the Fleet Engine. The SearchVehicles API includes a field to require that matched Vehicles must contain all the included attributes set to the specified value.

Note that the attribute field is in addition to several other supported fields in the Vehicle message, such as vehicle_type and supported_trip_types.

Vehicle remaining waypoints

The Vehicle entity contains a repeated field of TripWaypoint (RPC | REST), called waypoints(RPC | REST). This field includes the remaining waypoints in the trips, in the order that the vehicle reaches them. Fleet Engine computes this field as trips are assigned to the vehicle, and updates it as trips change their status. These waypoints can be identified by TripId field and WaypointType field.

Expanding a vehicle's eligibility for matches

Typically, the services of Rideshare or Delivery Provider are responsible for matching trip requests to vehicles. The service can use the vehicle attributes to include a vehicle in a larger number of searches. For example, the provider can implement a set to attributes corresponding to levels of perks or capabilities provided by a vehicle. For example, three levels could be a set of attributes with boolean values: is_bronze_level, is_silver_level, and is_gold_level. A vehicle can be eligible for all three. When the Fleet Engine receives a request for a trip that requires silver level capabilities, the search includes that vehicle. Using attributes this way includes vehicles that are offering a variety of capabilities.

There are two ways to update vehicle attributes. One is the UpdateVehicle API. When using this API, the entire set of Vehicle Attributes is set to the value. It's not possible to just update a single attribute. The other method is the UpdateVehicleAttributes API. This method takes just the attributes to be updated. Attributes included in the request will be set to the new value or added; unspecified attributes will not be altered.

HOW-TO: Create a Vehicle

A Vehicle entity must be created for each Vehicle to be tracked in the fleet.

Use the CreateVehicle endpoint with the CreateVehicleRequest to create a Vehicle.

The provider_id of the Vehicle must be the Project ID (e.g. my-on-demand-project) of the Google Cloud Project that contains the Service Accounts that will be used to call the Fleet Engine. Note that while multiple service accounts may access the Fleet Engine for the same Rideshare or Delivery Provider, the Fleet Engine does not currently support service accounts from multiple Google Cloud Projects accessing the same Vehicles.

The Vehicle can be created in the OFFLINE or ONLINE state. If created ONLINE it may be immediately returned in response to SearchVehicles queries.

An initial last_location may be included in the CreateVehicle call. While permitted, a Vehicle should not be created in the ONLINE state without a last_location.

See the Vehicle Types for details on the vehicle type field.

See the Vehicle Attributes for details on the attributes field.

The value returned from CreateVehicle is the created Vehicle entity.

Example

shell

curl -X POST \
  "https://fleetengine.googleapis.com/v1/providers/project-id/vehicles?vehicleId=vid-8241890" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data-binary @- << EOM
{
    "vehicleState": "OFFLINE",
    "supportedTripTypes": ["EXCLUSIVE"],
    "maximumCapacity": 4,
    "vehicleType": {"category": "AUTO"},
    "attributes": [{"key": "on_trip", "value": "false"}]
}
EOM

See providers.vehicles.create reference.

Java

static final String PROJECT_ID = "project-id";

VehicleServiceBlockingStub vehicleService =
    VehicleService.newBlockingStub(channel);

String parent = "providers/" + PROJECT_ID;
Vehicle vehicle = Vehicle.newBuilder()
    .setVehicleState(VehicleState.OFFLINE)  // Initial state
    .addSupportedTripTypes(TripType.EXCLUSIVE)
    .setMaximumCapacity(4)
    .setVehicleType(VehicleType.newBuilder().setCategory(VehicleType.Category.AUTO))
    .addAttributes(VehicleAttribute.newBuilder()
        .setKey("on_trip").setValue("false"))  // Opaque to the Fleet Engine
    // Add .setBackToBackEnabled(true) to make this vehicle eligible for trip
    // matching while even if it is on a trip.  By default this is disabled.
    .build();

CreateVehicleRequest createVehicleRequest =
    CreateVehicleRequest.newBuilder()  // no need for the header
        .setParent(parent)
        .setVehicleId("vid-8241890")  // Vehicle ID assigned by Rideshare or Delivery Provider
        .setVehicle(vehicle)  // Initial state
        .build();

// In this case, the Vehicle is being created in the OFFLINE state and
// no initial position is being provided.  When the Driver App checks
// in with the Rideshare or Delivery Provider, the state can be set to ONLINE and
// the Driver App will update the Vehicle Location.

try {
  Vehicle createdVehicle =
      vehicleService.createVehicle(createVehicleRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
    case ALREADY_EXISTS:
      break;
    case PERMISSION_DENIED:
      break;
  }
  return;
}
// If no Exception, Vehicle created successfully.

Google Cloud platform logs for Vehicle creation

The Fleet Engine API writes a log entry via Google Cloud platform logs when a call to the CreateVehicle endpoint is received. The log entry includes information about the values in the CreateVehicle request. If the call succeeds, it will also include information about the Vehicle that was returned.

shell

gcloud --project=project-id logging read --freshness=1h '
  jsonPayload.request.vehicleId="vid-8241890"
  jsonPayload.@type="type.googleapis.com/maps.fleetengine.v1.CreateVehicleLog"
'

Should print a record similar to the following:

---
insertId: c2cf4d3a180251c1bdb892137c14f022
jsonPayload:
  '@type': type.googleapis.com/maps.fleetengine.v1.CreateVehicleLog
  request:
    vehicle:
      attributes:
      - key: on_trip
        value: 'false'
      maximumCapacity: 4
      state: VEHICLE_STATE_OFFLINE
      supportedTrips:
      - EXCLUSIVE_TRIP
      vehicleType:
        vehicleCategory: AUTO
    vehicleId: vid-8241890
  response:
    attributes:
    - key: on_trip
      value: 'false'
    availableCapacity: 4
    currentRouteSegmentHandle: AdSiwAwCO9gZ7Pw5UZZimOXOo41cJTjg/r3SuwVPQmuuaV0sU3+3UCY+z53Cl9i6mWHLoCKbBt9Vsj5PMRgOJ8zX
    maximumCapacity: 4
    name: providers/project-id/vehicles/vid-8241890
    state: VEHICLE_STATE_OFFLINE
    supportedTrips:
    - EXCLUSIVE_TRIP
    vehicleType:
      vehicleCategory: AUTO
labels:
  vehicle_id: vid-8241890
logName: projects/project-id/logs/fleetengine.googleapis.com%2Fcreate_vehicle
receiveTimestamp: '2021-09-22T03:25:16.361159871Z'
resource:
  labels:
    location: global
    resource_container: projects/project-id
  type: fleetengine.googleapis.com/Fleet
timestamp: '2021-09-22T03:25:15.724998Z'

Cloud Pub/Sub notifications for Vehicle creation

The Fleet Engine API publishes a notification via Cloud Pub/Sub when a new vehicle is created. In order to receive these notifications, please follow the instructions here.

HOW-TO: Update a Vehicle's location

If not using the Driver SDK to update the vehicle's location, you can make a direct call to Fleet Engine with the vehicle's location. For any active vehicle, Fleet Engine expects a location update at least once every minute and at most once every 5 seconds. These updates require only Fleet Engine Driver SDK User privileges.

Example

shell

curl -X PUT \
  "https://fleetengine.googleapis.com/v1/providers/project-id/vehicles/vid-8241890?updateMask=last_location" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data-binary @- << EOM
{
    "supplementalLocation": {"latitude": 12.1, "longitude": 14.5},
    "supplementalLocationTime": "$(date -u --iso-8601=seconds)",
    "supplementalLocationSensor": "CUSTOMER_SUPPLIED_LOCATION",
    "supplementalLocationAccuracy": 15
}
EOM

See providers.vehicles.update reference.

Java

static final String PROJECT_ID = "project-id";
static final String VEHICLE_ID = "vid-8241890";

VehicleServiceBlockingStub vehicleService = VehicleService.newBlockingStub(channel);

String vehicleName = "providers/" + PROJECT_ID + "/vehicles/" + VEHICLE_ID;
Vehicle updatedVehicle = Vehicle.newBuilder()
    .setLastLocation(VehicleLocation.newBuilder()
        .setSupplementalLocation(LatLng.newBuilder()
            .setLatitude(37.3382)
            .setLongitude(121.8863))
        .setSupplementalLocationTime(now())
        .setSupplementalLocationSensor(LocationSensor.CUSTOMER_SUPPLIED_LOCATION)
        .setSupplementalLocationAccuracy(DoubleValue.of(15.0)))  // Optional)
    .build();

UpdateVehicleRequest updateVehicleRequest = UpdateVehicleRequest.newBuilder()
    .setName(vehicleName)
    .setVehicle(updatedVehicle)
    .setUpdateMask(FieldMask.newBuilder()
        .addPaths("last_location"))
    .build();

try {
  Vehicle updatedVehicle =
      vehicleService.updateVehicle(updateVehicleRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
    case NOT_FOUND:
      // Most implementations will call CreateVehicle in this case
      break;
    case PERMISSION_DENIED:
      break;
  }
  return;
}
// If no Exception, Vehicle updated successfully.

HOW-TO: Update other Vehicle fields

Updates to other attributes of the Vehicle state occur less frequently than position updates. Updates to attributes other than last_location require Fleet Engine Super User privileges.

The UpdateVehicleRequest includes an update_mask to indicate which fields to update. The behavior of the field is as in the Protobuf documentation for field masks.

As noted in Vehicle Attributes, updating the attributes field requires writing all of the attributes to be preserved. It is not possible to just update the value of one key-value pair in an UpdateVehicle call. To update values of specific attributes, the UpdateVehicleAttributes API can be used.

Example

This example enables back_to_back.

shell

curl -X PUT \
  "https://fleetengine.googleapis.com/v1/providers/project-id/vehicles/vid-8241890?updateMask=vehicle_state,attributes,back_to_back_enabled" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data-binary @- << EOM
{
    "vehicleState": "ONLINE",
    "attributes": [
      {"key": "on_trip", "value": "true"},
      {"key": "cash_only", "value": "false"}
    ],
    "backToBackEnabled": true
}
EOM

See providers.vehicles.update reference.

Java

static final String PROJECT_ID = "project-id";
static final String VEHICLE_ID = "vid-8241890";

VehicleServiceBlockingStub vehicleService = VehicleService.newBlockingStub(channel);

String vehicleName = "providers/" + PROJECT_ID + "/vehicles/" + VEHICLE_ID;
Vehicle updatedVehicle = Vehicle.newBuilder()
    .setVehicleState(VehicleState.ONLINE)
    .addAllAttributes(ImmutableList.of(
        VehicleAttribute.newBuilder().setKey("on_trip").setValue("true").build(),
        VehicleAttribute.newBuilder().setKey("cash_only").setValue("false").build()))
    .setBackToBackEnabled(true)
    .build();

UpdateVehicleRequest updateVehicleRequest = UpdateVehicleRequest.newBuilder()
    .setName(vehicleName)
    .setVehicle(updatedVehicle)
    .setUpdateMask(FieldMask.newBuilder()
        .addPaths("vehicle_state")
        .addPaths("attributes")
        .addPaths("back_to_back_enabled"))
    .build();

// Attributes and vehicle state are being updated, so both are
// included in the field mask.  Note that of on_trip were
// not being updated, but rather cash_only was being changed,
// the desired value of "on_trip" would still need to be written
// as the attributes are completely replaced in an update operation.

try {
  Vehicle updatedVehicle =
      vehicleService.updateVehicle(updateVehicleRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
    case NOT_FOUND:
      // Most implementations will call CreateVehicle in this case
      break;
    case PERMISSION_DENIED:
      break;
  }
  return;
}
// If no Exception, Vehicle updated successfully.

Google Cloud platform logs for Vehicle Updates

The Fleet Engine API writes a log entry via Google Cloud platform logs when a call to the UpdateVehicle endpoint is received. The log entry includes information about the values in the UpdateVehicle request. If the call succeeds, it will also include information about the Vehicle that was returned.

shell

gcloud --project=project-id logging read --freshness=1h '
  jsonPayload.request.vehicleId="vid-8241890"
  jsonPayload.@type="type.googleapis.com/maps.fleetengine.v1.UpdateVehicleLog"
'

Cloud Pub/Sub notifications for Vehicle updates

The Fleet Engine API publishes a notification via Cloud Pub/Sub when an existing vehicle is updated. In order to receive these notifications, please follow the instructions here.

HOW-TO: Search vehicles

The Fleet Engine supports searching for vehicles. The SearchVehicles API lets you find available nearby drivers who are best suited to a task such as servicing a ride or a delivery request. The SearchVehicles API returns a ranked list of drivers matching task attributes with attributes of vehicles in your fleet. For more information, see Finding nearby drivers.

Example

When searching for available vehicles, the Fleet Engine excludes vehicles on active trips by default. The services of the Rideshare or Delivery Provider need to explicitly include them in search requests. The following example shows how to include those vehicles in a search for vehicles matching a trip from the Grand Indonesia East Mall to the Balai Sidang Jakarta Convention Center.

shell

First update the location of the vehicle we created in prior steps so that it is eligible. In the real world, this would be done by the Driver SDK running on an Android or iOS device in the vehicle.

curl -X PUT \
  "https://fleetengine.googleapis.com/v1/providers/project-id/vehicles/vid-8241890?updateMask=last_location,attributes" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data-binary @- << EOM
{
  "lastLocation": {
    "updateTime": "$( date -u +"%Y-%m-%dT%H:%M:%SZ" )",
    "location": {
      "latitude": "-6.195139",
      "longitude": "106.820826"
    }
  },
  "attributes": [{"key": "on_trip", "value": "false"}]
}
EOM

Performing the search should yield at least that vehicle.

curl -X POST \
  "https://fleetengine.googleapis.com/v1/providers/project-id/vehicles:search" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data-binary @- << EOM
{
  "pickupPoint": {
    "point": {"latitude": "-6.195139", "longitude": "106.820826"}
  },
  "dropoffPoint": {
    "point": {"latitude": "-6.1275", "longitude": "106.6537"}
  },
  "pickupRadiusMeters": 2000,
  "count": 10,
  "minimumCapacity": 2,
  "tripTypes": ["EXCLUSIVE"],
  "vehicleTypes": [{"category": "AUTO"}],
  "filter": "attributes.on_trip=\"false\"",
  "orderBy": "PICKUP_POINT_ETA",
  "includeBackToBack": true
}
EOM

See providers.vehicles.search reference.

Java

static final String PROJECT_ID = "project-id";

VehicleServiceBlockingStub vehicleService = VehicleService.newBlockingStub(channel);

String parent = "providers/" + PROJECT_ID;
SearchVehiclesRequest searchVehiclesRequest = SearchVehiclesRequest.newBuilder()
    .setParent(parent)
    .setPickupPoint( // Grand Indonesia East Mall
        TerminalLocation.newBuilder().setPoint(
            LatLng.newBuilder().setLatitude(-6.195139).setLongitude(106.820826)))
    .setDropoffPoint( // Balai Sidang Jakarta Convention Center
        TerminalLocation.newBuilder().setPoint(
            LatLng.newBuilder().setLatitude(-6.213796).setLongitude(106.807195)))
    .setPickupRadiusMeters(2000)
    .setCount(10)
    .setMinimumCapacity(2)
    .addTripTypes(TripType.EXCLUSIVE)
    .addVehicleTypes(VehicleType.newBuilder().setCategory(VehicleType.Category.AUTO))
    .setFilter("attributes.on_trip=\"false\"")
    .setOrderBy(VehicleMatchOrder.PICKUP_POINT_ETA)
    .setIncludeBackToBack(true) // Fleet Engine includes vehicles that are en route.
    .build();

// Error handling
// If matches are returned and the authentication passed, the request completed
// successfully

try {
  SearchVehiclesResponse searchVehiclesResponse =
      vehicleService.searchVehicles(searchVehiclesRequest);

  // Search results: Each vehicle match contains a vehicle entity and information
  // about the distance and ETA to the pickup point and dropoff point.
  List<VehicleMatch> vehicleMatches = searchVehiclesResponse.getMatchesList();
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
    case NOT_FOUND:
      break;
    case PERMISSION_DENIED:
      break;
  }
  return;
}

Vehicle filtering query

SearchVehicles and ListVehicles support filtering on vehicle attributes using a filter query. For filter query syntax, see AIP-160 for examples.

Note that filter queries ONLY support filtering on vehicle attributes, and cannot be used for other fields. The filter query functions as an AND clause with other constraints, such as minimum_capacity or vehicle_types in SearchVehiclesRequest.

HOW-TO: List vehicles

SearchVehicles is optimized for finding a small number of vehicles in ranked order very quickly and is mainly used to find nearby drivers who are best suited to a task. However, sometimes you want to find all vehicles that meet some criteria even if paging through the results is necessary. ListVehicles is designed for that use case.

The ListVehicles API lets you find all vehicles that satisfy some specific request options. The ListVehicles API returns a paginated list of vehicles in the project that matches some requirements.

To filter on vehicle attributes, please refer to Vehicle filtering query.

Example

This example performs filtering on vehicle_type and attributes using the filter string.

shell

curl -X POST \
  "https://fleetengine.googleapis.com/v1/providers/project-id/vehicles:list" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data-binary @- << EOM
{
  "vehicleTypes": [{"category": "AUTO"}],
  "filter": "attributes.on_trip=\"false\"",
}
EOM

See providers.vehicles.list reference.

Java

static final String PROJECT_ID = "project-id";

VehicleServiceBlockingStub vehicleService = VehicleService.newBlockingStub(channel);

String parent = "providers/" + PROJECT_ID;
ListVehiclesRequest listVehiclesRequest = ListVehiclesRequest.newBuilder()
    .setParent(parent)
    .addTripTypes(TripType.EXCLUSIVE)
    .addVehicleTypes(VehicleType.newBuilder().setCategory(VehicleType.Category.AUTO))
    .setFilter("attributes.on_trip=\"false\"")
    .setIncludeBackToBack(true) // Fleet Engine includes vehicles that are en route.
    .build();

// Error handling
// If matches are returned and the authentication passed, the request completed
// successfully

try {
  ListVehiclesResponse listVehiclesResponse =
      vehicleService.listVehicles(listVehiclesRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
    case NOT_FOUND:
      break;
    case PERMISSION_DENIED:
      break;
  }
  return;
}

Trips and their lifecycle

The Trip API and lifecycle are similar to the Vehicle API and lifecycle. The Rideshare Provider is responsible for creating trips using the Fleet Engine interfaces. The Fleet Engine provides both an RPC service, TripService , and REST resources, provider.trips . These interfaces enable Trip entity creation, information requests, search functionality, and update capability.

A Trip has a status field to track its progression through the lifecycle. The values move from NEW to COMPLETE plus CANCELED and UNKNOWN_TRIP_STATUS . Refer to trip_status for RPC or TripStatus for REST.

  • NEW
  • ENROUTE_TO_PICKUP
  • ARRIVED_AT_PICKUP
  • ENROUTE_TO_INTERMEDIATE_DESTINATION
  • ARRIVED_AT_INTERMEDIATE_DESTINATION
  • ENROUTE_TO_DROPOFF
  • COMPLETE

Your service can update the trip to CANCELED from any of these statuses. When your service creates a trip, the engine sets the status as NEW. A vehicle_id is optional. As with vehicles, the services delete trips automatically after seven days without an update. If your service attempts to create a trip with an ID that already exists, an error is returned. A trip is considered ‘active’ if it is in a status other than COMPLETE or CANCELED. This distinction is important in the active_trips field in the Vehicle entity and SearchTripsRequest.

The service can only change the vehicle_id assigned to a Trip when the status is NEW or CANCELED. If a Driver cancels a Trip while en route, the Trip status must be set to NEW or CANCELED before the vehicle_id is changed or cleared.

The status is important when implementing back-to-back trip support. This support enables the Provider to assign a new trip to a Vehicle while that Vehicle is on an active Trip. The code for creating a back-to-back Trip is the same as a single trip and uses the same vehicle ID. The Fleet Engine adds the origin and destination of the new trip to the vehicle's waypoints. For more information about back-to-back trips, see Create Multi-waypoint trips.

Trip remaining waypoints

The Trip entity contains a repeated field of TripWaypoint (RPC | REST), called remainingWaypoints(RPC | REST). This field includes all waypoints that vehicle will need to travel in order before this trip's final drop off point. It computes from Vehicle's remaining waypoints. In the Back-to-back and Carpool use cases, this list contains waypoints from other trips that will be traversed before this trip, but excludes any waypoints after this trip. The waypoint in the list can be identified by its TripId and WaypointType.

The relation between trip status and Vehicle remaining waypoints

The Vehicle's remaining waypoints (RPC | REST) will be updated when Fleet Engine receives a trip status change request. The previous waypoint will be removed from Vehicle's remaining waypoints list when the tripStatus(RPC | REST) is changed from other status to ENROUTE_TO_XXX. That is, when trip status is changed from ENROUTE_TO_PICKUP to ARRIVED_AT_PICKUP, the trip's pickup point will still be in Vehicle's remaining waypoint list, but when trip status is changed to ENROUTE_TO_INTERMEDIATE_DESTINATION or ENROUTE_TO_DROPOFF, its pickup point then will be removed from the vehicle's remaining waypoints.

This is the same for ARRIVED_AT_INTERMEDIATE_DESTINATION and ENROUTE_TO_INTERMDEDIATE_DESTINATION. When ARRIVED_AT_INTERMEDIATE_DESTINATION, the current intermediate destination won't be removed from Vehicle's remaining waypoint list until the vehicle reports that it enroutes to the next waypoint.

When trip status is changed to COMPLETED, no waypoints from this trip will be in Vehicle's remaining waypoint list.

HOW-TO: Create a trip

A Trip entity must be created in order for each trip request to be tracked and matched to Vehicles in the fleet. Use the CreateTrip endpoint with CreateTripRequest to create a Trip.

The following attributes are required to create a trip:

  • parent - A string that includes the Provider ID created when the Google Cloud Project was created.
  • trip_id - A string created by the Rideshare Provider.
  • trip - Container with basic metadata describing the trip.
    • trip_type - Enum representing whether the trip might have other riders from a different origin and destination in the same vehicle (SHARED) or single party only (EXCLUSIVE).
    • pickup_point - TerminalLocation representing the origin point for the trip. Refer to RPC reference or REST reference

When you create a trip, you can provide thenumber_of_passengers, dropoff_point and vehicle_id. Although these fields are not required, if you provide them, they are retained. All other Trip fields are ignored. For example, all trips start with a trip_status of NEW even if you pass in a trip_status of CANCELED in the creation request.

Example

The following example creates a trip to the Grand Indonesia East Mall. The trip is for two passengers and is exclusive. The provider_id of the Trip must be the same as the Project ID. In the example, the Rideshare Provider created the Google Cloud Project, project-id. This project must have the Service Accounts used to call the Fleet Engine. The trip's status is NEW.

Later, after the service matches the trip to a vehicle, the service can call UpdateTrip and change the vehicle_id when the trip is assigned to a vehicle.

shell

curl -X POST \
  "https://fleetengine.googleapis.com/v1/providers/project-id/trips?tripId=tid-1f97" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data-binary @- << EOM
{
  "tripType": "EXCLUSIVE",
  "numberOfPassengers": 2,
  "pickupPoint": {
    "point": {"latitude": "-6.195139", "longitude": "106.820826"}
  },
  "dropoffPoint": {
    "point": {"latitude": "-6.1275", "longitude": "106.6537"}
  }
}
EOM

See providers.trips.create reference.

Java

static final String PROJECT_ID = "project-id";

TripServiceBlockingStub tripService = TripService.newBlockingStub(channel);

String parent = "providers/" + PROJECT_ID;
Trip trip = Trip.newBuilder()
    .setTripType(TripType.EXCLUSIVE) // Use TripType.SHARED for carpooling
    .setPickupPoint(                 // Grand Indonesia East Mall
        TerminalLocation.newBuilder().setPoint(
            LatLng.newBuilder().setLatitude(-6.195139).setLongitude(106.820826)))
    // Provide the number of passengers if available.
    .setNumberOfPassengers(2)
    // Provide the drop-off point if available.
    .setDropoffPoint(
        TerminalLocation.newBuilder().setPoint(
            LatLng.newBuilder().setLatitude(-6.1275).setLongitude(106.6537)))
    .build();

CreateTripRequest createTripRequest =
    CreateTripRequest.newBuilder()  // no need for the header
        .setParent(parent)
        .setTripId("tid-1f97")  // Trip ID assigned by the Provider
        .setTrip(trip)              // Initial state
        .build();

// Error handling
// If Fleet Engine does not have trip with that id and the credentials of the
// requestor pass, the service creates the trip successfully.

try {
  Trip createdTrip =
      tripService.createTrip(createTripRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
    case ALREADY_EXISTS:
      break;
    case PERMISSION_DENIED:
      break;
  }
  return;
}

Google Cloud platform logs for Trip Creation

The Fleet Engine API writes a log entry using Google Cloud platform logs when a call to the CreateTrip endpoint is received. The log entry includes information about the values in the CreateTrip request. If the call succeeds, it will also include information about the Trip that was returned.

HOW-TO: Update a trip

The Trip entity contains fields that enable tracking by the service and for reporting the trip's progression by the Driver SDK and to the Consumer SDK. To update the properties, use UpdateTripRequest message. This updates the Trip fields according to the request’s field_mask. Refer to UpdateTripRequest.

The Rideshare Provider is responsible for updating the following attributes:

  • Trip status.
  • Vehicle ID. Either at the time of creation, or after matching the vehicle to a trip.
  • Changes to pickup, dropoff, or waypoints.

The Fleet Engine automatically updates the following fields when using the Journey Sharing feature through the Driver SDK or Consumer SDK:

  • Routes
  • ETA
  • Remaining distance
  • Vehicle location
  • Remaining waypoints

Refer to Tripin RPC or Resource.Trip in REST.

Google Cloud platform logs for Trip Updates

The Fleet Engine API writes a log entry using Google Cloud platform logs when a call to the UpdateTrip endpoint is received. The log entry includes information about the values in the UpdateTrip request. If the call succeeds, it will also include information about the Trip that was returned.

HOW-TO: Search trips

The Fleet Engine supports searching for trips. As previously noted, a Trip is automatically deleted after seven days, so SearchTrips does not expose a complete history of all Trips.

While SearchTrips is a flexible API, the list below considers two use cases.

  • Determining a Vehicle’s Active Trips -- The Provider can determine a vehicle’s currently active trips. Within the SearchTripsRequest, the vehicle_id is set to the vehicle under consideration and active_trips_only should be set to true.

  • Reconciling the Provider and Fleet Engine State -- The Provider can use SearchTrips to ensure their Trip state and that of Fleet Engine match. This is especially important for TripStatus. If the state of a trip assigned to a Vehicle is not properly set to COMPLETE or CANCELED, the Vehicle is not included by SearchVehicles.

To use SearchTrips in this way, leave vehicle_id empty, set active_trips_only to true, and set minimum_staleness to a time greater than most trip durations. For example, you might use one hour. The results include Trips that are not COMPLETE nor CANCELED, and have not been updated in over an hour. The Provider should examine these Trips to ensure that their status in the Fleet Engine is properly updated.

Troubleshooting

In the case of a DEADLINE_EXCEEDED Error, the state of the Fleet Engine is unknown. The Provider should call CreateTrip again, which either returns a 201 (CREATED) or 409 (CONFLICT). In the latter case, the previous request succeeded before DEADLINE_EXCEEDED. See the Consumer API guides for more information about handling trip errors: Android or iOS.

Carpool ride support

You can assign multiple SHARED trips to a vehicle that supports TripType.SHARED. You need to specify the order of all unpassed waypoints for all Trips assigned to the Vehicle in this shared ride via Trip.vehicle_waypoints when you assign the vehicle_id for a shared trip (in a CreateTrip or UpdateTrip request). Refer to vehicle_waypoints for RPC or vehicleWaypoints for REST.

Multiple destinations support

Identify an intermediate destination

The field intermediateDestinations and field intermediateDestinationIndex in Trip (RPC | REST) are combined to be used to indicate the destination.

Update intermediate destination

You can update the intermediate destinations via UpdateTrip. When updating intermediate destinations, you must provide a complete list of intermediate destinations, including those that have been visited, not just the one newly added or to-be-modified. When the intermediateDestinationIndex points to an index after the position of newly added/modified intermediate destination, the new/updated intermediate destination will not be added to Vehicle’s waypoints or Trip’s remainingWaypoints. The reason is that any intermediate destinations before intermediateDestinationIndex are treated as already visited.

Trip status changes

The field intermediateDestinationsVersion in (RPC | REST) is required in the Trip status update request sent to Fleet Engine to indicate an intermediate destination has passed. The targeted intermediate destination is specified via the field intermediateDestinationIndex. When tripStatus (RPC | REST) is ENROUTE_TO_INTERMEDIATE_DESTINATION, a number between [0..N-1] indicates which intermediate destination the vehicle will cross next. When tripStatus is ARRIVED_AT_INTERMEDIATE_DESTINATION, a number between [0..N-1] indicates which intermediate destination the vehicle is at.

Example

The following code example demonstrates how to update a trip's status to enroute to its first intermediate destination, assuming that you have created a multi-destination trip and the trip has passed its pickup point.

Java

static final String PROJECT_ID = "project-id";
static final String TRIP_ID = "multi-destination-trip-A";

String tripName = "providers/" + PROJECT_ID + "/trips/" + TRIP_ID;
Trip trip = …; // Fetch trip object from FleetEngine or your storage.

TripServiceBlockingStub tripService = TripService.newBlockingStub(channel);

// Trip settings to update.
Trip trip = Trip.newBuilder()
    // Trip status cannot go back to a previous status once it is passed
    .setTripStatus(TripStatus.ENROUTE_TO_INTERMEDIATE_DESTINATION)
    // Enrouting to the first intermediate destination.
    .setIntermediateDestinationIndex(0)
    // intermediate_destinations_version MUST be provided to ensure you
    // have the same picture on intermediate destinations list as FleetEngine has.
    .setIntermediateDestinationsVersion(
        trip.getIntermediateDestinationsVersion())
    .build();

// Trip update request
UpdateTripRequest updateTripRequest =
    UpdateTripRequest.newBuilder()
        .setName(tripName)
        .setTrip(trip)
        .setUpdateMask(
            FieldMask.newBuilder()
                .addPaths("trip_status")
                .addPaths("intermediate_destination_index")
                // intermediate_destinations_version must not be in the
                // update mask.
                .build())
        .build();

// Error handling
try {
  Trip updatedTrip = tripService.updateTrip(updateTripRequest);
} catch (StatusRuntimeException e) {
  Status s = e.getStatus();
  switch (s.getCode()) {
    case NOT_FOUND:  // Trip does not exist.
      break;
    case FAILED_PRECONDITION:  // The given trip status is invalid, or the
                                // intermediate_destinations_version
                                // doesn’t match FleetEngine’s.
      break;
    case PERMISSION_DENIED:
      break;
  }
  return;
}

HOW-TO: Subscribe to notification messages from the Fleet Engine API

The Fleet Engine API uses Google Cloud Pub/Sub to publish notifications on the topic created by the consumer Google Cloud Project. Pub/Sub is not enabled by default for Fleet Engine on your Google Cloud project. Please file a support case or contact your Customer Engineer to enable Pub/Sub.

In order to create a topic on your Cloud Project, follow these instructions. The topic ID must be 'fleet_engine_notifications'.

The topic must be created in the same Cloud project that is calling Fleet Engine APIs.

Once the topic is created, you will need to grant the Fleet Engine API permission to publish on the topic. In order to do so, click on the topic you just created and add a new permission. You might have to click on SHOW INFO PANEL to open the permissions editor. The principal should be geo-fleet-engine@system.gserviceaccount.com and the role should be Pub/Sub publisher.

In order to setup your Cloud Project to subscribe to notifications, follow these instructions

The Fleet Engine API will publish each notification in two different data formats, protobuf and json. The data format for each notification is denoted in the PubsubMessage attributes with the key as data_format and value as protobuf or json.

Notification schema:

Protobuf

// A batch of notifications that is published by the Fleet Engine service using
// Cloud Pub/Sub in a single PubsubMessage.
message BatchNotification {
  // Required. At least one notification must exist.
  // List of notifications containing information related to changes in
  // Fleet Engine data.
  repeated Notification notifications = 1;
}

// A notification related to changes in Fleet Engine data.
// The data provides additional information specific to the type of the
// notification.
message Notification {
  // Required. At least one type must exist.
  // Type of notification.
  oneof type {
    // Notification related to changes in vehicle data.
    VehicleNotification vehicle_notification = 1;
  }
}

// Notification sent when a new vehicle was created.
message CreateVehicleNotification {
  // Required.
  // Vehicle must contain all fields that were set when it was created.
  Vehicle vehicle = 1;
}

// Notification sent when an existing vehicle is updated.
message UpdateVehicleNotification {
  // Required.
  // Vehicle must only contain name and fields that are present in the
  // field_mask field below.
  Vehicle vehicle = 1;

  // Required.
  // Contains vehicle field paths that were specifically requested
  // by the Provider.
  google.protobuf.FieldMask field_mask = 2;
}

// Notification related to changes in vehicle data.
message VehicleNotification {
  // Required. At least one type must be set.
  // Type of notification.
  oneof type {
    // Notification sent when a new vehicle was created.
    CreateVehicleNotification create_notification = 1;
    // Notification sent when an existing vehicle is updated.
    UpdateVehicleNotification update_notification = 2;
  }
}

JSON

BatchNotification: {
  "description": "A batch of notifications that is published by the Fleet Engine service using Cloud Pub/Sub in a single PubsubMessage.",
  "type": "object",
  "required": ["notifications"],
  "properties": {
    "notifications": {
      "description": "At least one notification must exist. List of notifications containing information related to changes in Fleet Engine data.",
      "type": "Notification[]"
    }
  }
}

Notification: {
  "description": "A notification related to changes in Fleet Engine data. The data provides additional information specific to the type of the notification.",
  "type": "object",
  "properties": {
    "vehicleNotification": {
      "description": "Notification related to changes in vehicle data.",
      "type": "VehicleNotification"
    }
  }
}

VehicleNotification: {
  "description": "Notification related to changes in vehicle data.",
  "type": "object",
  "properties": {
    "createNotification": {
      "description": "Notification sent when a new vehicle was created.",
      "type": "CreateVehicleNotification"
    },
    "updateNotification": {
      "description": "Notification sent when an existing vehicle is updated.",
      "type": "UpdateVehicleNotification"
    }
  }
}

CreateVehicleNotification: {
  "description": "Notification sent when a new vehicle was created.",
  "type": "object",
  "required": ["vehicle"],
  "properties": {
    "vehicle": {
      "description": "Vehicle must contain all fields that were set when it was created.",
      "type": "Vehicle"
    }
  }
}

UpdateVehicleNotification: {
  "description": "Notification sent when an existing vehicle is updated.",
  "type": "object",
  "required": ["vehicle", "fieldMask"],
  "properties": {
    "vehicle": {
      "description": "Vehicle must only contain name and fields that are present in the fieldMask field below.",
      "type": "Vehicle"
    },
    "fieldMask": {
      "description": "Contains vehicle field paths that were specifically requested by the Provider.",
      "type": "FieldMask"
    }
  }
}