Back-to-back trips are exclusive, independent trips that occur one right after the other, where the driver commits to picking up the next consumer before completing the current trip.
The main difference between a back-to-back trip and a single-destination trip, is that in with a back-to-back trip, the trip operator could assign a trip to a vehicle that has already been assigned trips.
This tutorial walks you through the process of creating a back-to-back trip. It also shows how you can then integrate that trip with your consumer-application so that your customers can visualize the trip progress from their phones. You do this integration using the Consumer SDK.
Prerequisites
To complete this tutorial, be sure to complete the following:
Set up the Fleet Engine. For more information, see Fleet Engine: Initial setup.
Integrate your app with the Driver SDK. For more information, see Initializing the Driver SDK for Android, and Integration Guide for the Driver SDK for iOS.
Integrate your consumer-facing app with the Consumer SDK. For more information, see Getting Started with the Consumer SDK for Android, and Getting Started with the Consumer SDK for iOS.
Set up authorization tokens. For more information about authorization tokens, see Creating a JSON Web Token for authorization in the Getting Started with Fleet Engine guide, and Authentication and authorization in the Consumer SDK documentation for Fleet Engine.
Step 1. Create a vehicle in Fleet Engine
Vehicles are objects that represent the vehicles in your fleet. You must create them in Fleet Engine in order to be able to track them in the consumer app.
You can create a vehicle using either of the following two approaches:
- gRPC
- Call the
CreateVehicle()
method with theCreateVehicleRequest
request message. You must have Fleet Engine Super User privileges to callCreateVehicle()
. - REST
- Call
https://fleetengine.googleapis.com/v1/providers.vehicles.create
.
Caveats
The following caveats apply when you create a vehicle.
Be sure to set the initial vehicle state to
OFFLINE
. This ensures that Fleet Engine can discover your vehicle for trip matching.The vehicle's
provider_id
must be the same as the project ID of the Google Cloud Project that contains the Service Accounts used for calling Fleet Engine. While multiple service accounts can access the Fleet Engine for the same rideshare provider, Fleet Engine doesn't currently support service accounts from different Google Cloud Projects accessing the same vehicles.The response returned from
CreateVehicle()
contains theVehicle
instance. The instance is deleted after seven days if it has not been updated usingUpdateVehicle()
. You should callGetVehicle()
before callingCreateVehicle()
just to confirm that the vehicle doesn't already exist. IfGetVehicle()
returns aNOT_FOUND
error, then you should proceed with callingCreateVehicle()
. For more information, see Vehicles and their lifecycle.
Example
The following provider code sample demonstrates how to create a vehicle in the Fleet Engine.
static final String PROJECT_ID = "my-rideshare-co-gcp-project";
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))
.build();
CreateVehicleRequest createVehicleRequest = CreateVehicleRequest.newBuilder()
.setParent(parent)
.setVehicleId("8241890") // Vehicle ID assigned by solution provider.
.setVehicle(vehicle) // Initial state.
.build();
// The Vehicle is created in the OFFLINE state, and no initial position is
// provided. When the driver app calls the rideshare provider, the state can be
// set to ONLINE, and the driver app updates 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;
}
To create a Vehicle
that supports back-to-back trips, you must set the backToBackEnabled
field in the Vehicle
object passed to CreateVehicleRequest
as true
.
Vehicle vehicle = Vehicle.newBuilder()
.setVehicleState(VehicleState.OFFLINE)
.addSupportedTripTypes(TripType.EXCLUSIVE)
.setMaximumCapacity(4)
.setVehicleType(VehicleType.newBuilder().setCategory(VehicleType.Category.AUTO))
.setBackToBackEnabled(true) // Set as 'true' so vehicle can be assigned back-to-back trips.
.build();
Step 2. Enable location tracking
Location tracking refers to tracking the vehicle's location during the trip, where the driver app sends telemetry to the Fleet Engine, which contains the vehicles current location. This constantly updated stream of position information is used to convey the vehicle's progress along the trip's route. When you enable location tracking, the driver app starts sending this telemetry, at a default frequency of once every five seconds.
You enable location tracking for Android and iOS as follows:
Call the Driver SDK for Android method
enableLocationTracking()
.Set the Driver SDK for iOS boolean property
locationTrackingEnabled
totrue
.
Example
The following code example demonstrates how to enable location tracking.
Java
RidesharingVehicleReporter vehicleReporter = ...;
vehicleReporter.enableLocationTracking();
Kotlin
val vehicleReporter = ...
vehicleReporter.enableLocationTracking()
Swift
vehicleReporter.locationTrackingEnabled = true
Objective-C
_vehicleReporter.locationTrackingEnabled = YES;
Step 3. Set the vehicle's state to online
You bring a vehicle into service (that is, to make it available for use) by setting its state to online, but you cannot do this until after you have enabled location tracking.
You set the vehicle's state to online for Android and iOS as follows:
Call the Driver SDK for Android method
setVehicleState(VehicleState.ONLINE)
.Call the Driver SDK for iOS method
vehicleReporter.update(.online)
.
Example
The following code example demonstrates how to set the vehicle's state to
ONLINE
.
Java
vehicleReporter.setVehicleState(VehicleState.ONLINE);
Kotlin
vehicleReporter.setVehicleState(VehicleState.ONLINE)
Swift
vehicleReporter.update(.online)
Objective-C
[_vehicleReporter updateVehicleState:GMTDVehicleStateOnline];
Step 4. Create a trip in Fleet Engine
In order to create a back-to-back trip, you create a Trip
object just like you would for a single-destination trip.
A trip is an object that represents a journey, which is a collection geocoordinate points, including origin, waypoints, and dropoff points.
You must create one Trip
object for each trip request so that the request can be matched to a vehicle and then tracked.
- Create a trip by calling the
CreateTrip()
method with theCreateTripRequest
request message.
Supply required attributes
To create a back-to-back trip, you must supply the following fields:
parent
- A string that includes the provider ID. This must be the same as the project ID of the Google Cloud Project that contains the Service Accounts used for calling Fleet Engine
trip_id
- A string that you create, that uniquely identifies this trip.
trip
Trip
object to create.
The following fields must be set in the Trip
object passed to CreateTripRequest
:
trip_type
TripType.EXCLUSIVE
pickup_point
- The trip's point of origin.
dropoff_point
- The trip's dropoff point. This field is not required at trip creation and can be set later by calling
UpdateTrip
.
Example
The following backend integration sample demonstrates how to create a trip and assign it to a vehicle.
// A vehicle with ID 'my-vehicle' is already created and it is assigned to a trip with ID 'current-trip'.
static final String PROJECT_ID = "my-rideshare-co-gcp-project";
static final String VEHICLE_ID =" my-vehicle";
static final String TRIP_ID = "back-to-back-trip");
TripServiceBlockingStub tripService = TripService.newBlockingStub(channel);
String parent = "providers/" + PROJECT_ID;
Trip trip = Trip.newBuilder()
.setTripType(TripType.EXCLUSIVE)
.setPickupPoint(
TerminalLocation.newBuilder().setPoint(
LatLng.newBuilder()
.setLatitude(-6.195139).setLongitude(106.820826)))
.setDropoffPoint(
TerminalLocation.newBuilder().setPoint(
LatLng.newBuilder()
.setLatitude(-6.1275).setLongitude(106.6537)))
.setVehicleId(VEHICLE_ID)
.build();
// Create trip request
CreateTripRequest createTripRequest = CreateTripRequest.newBuilder()
.setParent(parent)
.setTripId(TRIP_ID)
.setTrip(trip)
.build();
// Error handling.
try {
// Fleet Engine automatically assigns a 'waypoints' list to the trip containing
// the vehicle's current trip waypoints.
Trip createdTrip =
tripService.createTrip(createTripRequest);
} catch (StatusRuntimeException e) {
Status s = e.getStatus();
switch (s.getCode()) {
case ALREADY_EXISTS:
break;
case PERMISSION_DENIED:
break;
}
return;
}
Step 5. Update the trip with the vehicle ID and waypoints
You must configure the trip with a vehicle ID so that Fleet Engine can track the vehicle along its route. For back-to-back, a trip is assigned to a vehicle even if it has already been assigned one.
- You can update the trip with the vehicle ID by calling the
UpdateTrip
endpoint with anUpdateTripRequest
. Use theupdate_mask
field to specify that you are updating the vehicle ID.
After you assign the trip to a vehicle, Fleet Engine automatically adds the
waypoints associated with the back-to-back trips to the vehicle's waypoints
field. A trip's remainingWaypoints
field contains a list of all of the
waypoints, including those from concurrent trips, that will be visited before
the trip's drop-off.
For example, consider two back-to-back trips: Trip A and Trip B. The vehicle has picked up the consumer for Trip A, and while en route to the drop-off location, the driver receives a request to pickup another consumer for the next trip (Trip B).
- Calling
getVehicle()
returns a list of remaining waypoints (remainingWaypoints
) that contains
A Drop-off → B Pickup → B Drop-off. - Either
getTrip()
or theonTripRemainingWaypointsUpdated
callback for Trip A returns a list of remaining waypoints (remainingWaypoints
) that contains A Drop-off. - Either
getTrip()
or theonTripRemainingWaypointsUpdated
callback for Trip B returns a list of remaining waypoints (remainingWaypoints
) that contains A Drop-off → B Pickup → and B Drop-off.
Step 6. Listen for trip updates in the consumer app
For Android, you can listen for data updates from a trip by obtaining a
TripModel
object from theTripModelManager
and registering aTripModelCallback
listener.For iOS, you can listen for data updates from a trip by obtaining a
GMTCTripModel
object from theGMTCTripService
and registering aGMTCTripModelSubscriber
subscriber.
A TripModelCallback
listener and GMTCTripModelSubscriber
subscriber let your app receive periodic
trip progress updates on each refresh based on the auto refresh interval.
Only values that change can trigger the callback. Otherwise, the callback
remains silent.
The TripModelCallback.onTripUpdated()
and tripModel(_:didUpdate:updatedPropertyFields:)
methods are always called, regardless of any data changes.
Example 1
The following code sample demonstrates how to obtain a TripModel
from
TripModelManager
/GMTCTripService
and set a listener on it.
Java
// Start journey sharing after a trip has been created via Fleet Engine.
TripModelManager tripModelManager = consumerApi.getTripModelManager();
// Get a TripModel object.
TripModel tripModel = tripModelManager.getTripModel(tripName);
// Register a listener on the trip.
TripModelCallback tripCallback = new TripModelCallback() {
...
};
tripModel.registerTripCallback(tripCallback);
// Set the refresh interval.
TripModelOptions tripModelOptions = TripModelOptions.builder()
.setRefreshInterval(5000) // interval in milliseconds, so 5 seconds
.build();
tripModel.setTripModelOptions(tripModelOptions);
// The trip stops auto-refreshing when all listeners are unregistered.
tripModel.unregisterTripCallback(tripCallback);
Kotlin
// Start journey sharing after a trip has been created via Fleet Engine.
val tripModelManager = consumerApi.getTripModelManager()
// Get a TripModel object.
val tripModel = tripModelManager.getTripModel(tripName)
// Register a listener on the trip.
val tripCallback = TripModelCallback() {
...
}
tripModel.registerTripCallback(tripCallback)
// Set the refresh interval.
val tripModelOptions =
TripModelOptions.builder()
.setRefreshInterval(5000) // interval in milliseconds, so 5 seconds
.build()
tripModel.setTripModelOptions(tripModelOptions)
// The trip stops auto-refreshing when all listeners are unregistered.
tripModel.unregisterTripCallback(tripCallback)
Swift
let tripService = GMTCServices.shared().tripService
// Create a tripModel instance for listening for updates from the trip
// specified by the trip name.
let tripModel = tripService.tripModel(forTripName: tripName)
// Register for the trip update events.
tripModel.register(self)
// Set the refresh interval (in seconds).
tripModel.options.autoRefreshTimeInterval = 5
// Unregister for the trip update events.
tripModel.unregisterSubscriber(self)
Objective-C
GMTCTripService *tripService = [GMTCServices sharedServices].tripService;
// Create a tripModel instance for listening for updates from the trip
// specified by the trip name.
GMTCTripModel *tripModel = [tripService tripModelForTripName:tripName];
// Register for the trip update events.
[tripModel registerSubscriber:self];
// Set the refresh interval (in seconds).
tripModel.options.autoRefreshTimeInterval = 5;
// Unregister for the trip update events.
[tripModel unregisterSubscriber:self];
Example 2
The following code sample demonstrates how to set up a TripModelCallback
listener and GMTCTripModelSubscriber
subscriber.
Java
// Implements a callback for the trip model so your app can listen for trip
// updates from Fleet Engine.
TripModelCallback subscriber =
new TripModelCallback() {
@Override
public void onTripStatusUpdated(TripInfo tripInfo, @TripStatus int status) {
// ...
}
@Override
public void onTripActiveRouteUpdated(TripInfo tripInfo, List<LatLng> route) {
// ...
}
@Override
public void onTripVehicleLocationUpdated(
TripInfo tripInfo, @Nullable VehicleLocation vehicleLocation) {
// ...
}
@Override
public void onTripPickupLocationUpdated(
TripInfo tripInfo, @Nullable TerminalLocation pickup) {
// ...
}
@Override
public void onTripPickupTimeUpdated(TripInfo tripInfo, @Nullable Long timestampMillis) {
// ...
}
@Override
public void onTripDropoffLocationUpdated(
TripInfo tripInfo, @Nullable TerminalLocation dropoff) {
// ...
}
@Override
public void onTripDropoffTimeUpdated(TripInfo tripInfo, @Nullable Long timestampMillis) {
// ...
}
@Override
public void onTripETAToNextWaypointUpdated(
TripInfo tripInfo, @Nullable Long timestampMillis) {
// ...
}
@Override
public void onTripActiveRouteRemainingDistanceUpdated(
TripInfo tripInfo, @Nullable Integer distanceMeters) {
// ...
}
@Override
public void onTripUpdateError(TripInfo tripInfo, TripUpdateError error) {
// ...
}
@Override
public void onTripUpdated(TripInfo tripInfo) {
// ...
}
@Override
public void onTripRemainingWaypointsUpdated(
TripInfo tripInfo, List<TripWaypoint> waypointList) {
// ...
}
@Override
public void onTripIntermediateDestinationsUpdated(
TripInfo tripInfo, List<TerminalLocation> intermediateDestinations) {
// ...
}
@Override
public void onTripRemainingRouteDistanceUpdated(
TripInfo tripInfo, @Nullable Integer distanceMeters) {
// ...
}
@Override
public void onTripRemainingRouteUpdated(TripInfo tripInfo, List<LatLng> route) {
// ...
}
};
Kotlin
// Implements a callback for the trip model so your app can listen for trip
// updates from Fleet Engine.
val subscriber =
object : TripModelCallback() {
override fun onTripStatusUpdated(tripInfo: TripInfo, status: @TripStatus Int) {
// ...
}
override fun onTripActiveRouteUpdated(tripInfo: TripInfo, route: List<LatLng>) {
// ...
}
override fun onTripVehicleLocationUpdated(
tripInfo: TripInfo,
vehicleLocation: VehicleLocation?
) {
// ...
}
override fun onTripPickupLocationUpdated(tripInfo: TripInfo, pickup: TerminalLocation?) {
// ...
}
override fun onTripPickupTimeUpdated(tripInfo: TripInfo, timestampMillis: Long?) {
// ...
}
override fun onTripDropoffLocationUpdated(tripInfo: TripInfo, dropoff: TerminalLocation?) {
// ...
}
override fun onTripDropoffTimeUpdated(tripInfo: TripInfo, timestampMillis: Long?) {
// ...
}
override fun onTripETAToNextWaypointUpdated(tripInfo: TripInfo, timestampMillis: Long?) {
// ...
}
override fun onTripActiveRouteRemainingDistanceUpdated(
tripInfo: TripInfo,
distanceMeters: Int?
) {
// ...
}
override fun onTripUpdateError(tripInfo: TripInfo, error: TripUpdateError) {
// ...
}
override fun onTripUpdated(tripInfo: TripInfo) {
// ...
}
override fun onTripRemainingWaypointsUpdated(
tripInfo: TripInfo,
waypointList: List<TripWaypoint>
) {
// ...
}
override fun onTripIntermediateDestinationsUpdated(
tripInfo: TripInfo,
intermediateDestinations: List<TerminalLocation>
) {
// ...
}
override fun onTripRemainingRouteDistanceUpdated(tripInfo: TripInfo, distanceMeters: Int?) {
// ...
}
override fun onTripRemainingRouteUpdated(tripInfo: TripInfo, route: List<LatLng>) {
// ...
}
}
Swift
class TripModelSubscriber: NSObject, GMTCTripModelSubscriber {
func tripModel(_: GMTCTripModel, didUpdate trip: GMTSTrip?, updatedPropertyFields: GMTSTripPropertyFields) {
// Update the UI with the new `trip` data.
updateUI(with: trip)
...
}
func tripModel(_: GMTCTripModel, didUpdate tripStatus: GMTSTripStatus) {
// Handle trip status did change.
}
func tripModel(_: GMTCTripModel, didUpdateActiveRoute activeRoute: [GMTSLatLng]?) {
// Handle trip active route did update.
}
func tripModel(_: GMTCTripModel, didUpdate vehicleLocation: GMTSVehicleLocation?) {
// Handle vehicle location did update.
}
func tripModel(_: GMTCTripModel, didUpdatePickupLocation pickupLocation: GMTSTerminalLocation?) {
// Handle pickup location did update.
}
func tripModel(_: GMTCTripModel, didUpdateDropoffLocation dropoffLocation: GMTSTerminalLocation?) {
// Handle drop off location did update.
}
func tripModel(_: GMTCTripModel, didUpdatePickupETA pickupETA: TimeInterval) {
// Handle the pickup ETA did update.
}
func tripModel(_: GMTCTripModel, didUpdateDropoffETA dropoffETA: TimeInterval) {
// Handle the drop off ETA did update.
}
func tripModel(_: GMTCTripModel, didUpdateRemaining remainingWaypoints: [GMTSTripWaypoint]?) {
// Handle updates to the pickup, dropoff or intermediate destinations of the trip.
}
func tripModel(_: GMTCTripModel, didFailUpdateTripWithError error: Error?) {
// Handle the error.
}
func tripModel(_: GMTCTripModel, didUpdateIntermediateDestinations intermediateDestinations: [GMTSTerminalLocation]?) {
// Handle the intermediate destinations being updated.
}
...
}
Objective-C
@interface TripModelSubscriber : NSObject <GMTCTripModelSubscriber>
@end
@implementation TripModelSubscriber
- (void)tripModel:(GMTCTripModel *)tripModel
didUpdateTrip:(nullable GMTSTrip *)trip
updatedPropertyFields:(GMTSTripPropertyFields)updatedPropertyFields {
// Update the UI with the new `trip` data.
[self updateUIWithTrip:trip];
...
}
- (void)tripModel:(GMTCTripModel *)tripModel didUpdateTripStatus:(enum GMTSTripStatus)tripStatus {
// Handle trip status did change.
}
- (void)tripModel:(GMTCTripModel *)tripModel
didUpdateActiveRoute:(nullable NSArray<GMTSLatLng *> *)activeRoute {
// Handle trip route did update.
}
- (void)tripModel:(GMTCTripModel *)tripModel
didUpdateVehicleLocation:(nullable GMTSVehicleLocation *)vehicleLocation {
// Handle vehicle location did update.
}
- (void)tripModel:(GMTCTripModel *)tripModel
didUpdatePickupLocation:(nullable GMTSTerminalLocation *)pickupLocation {
// Handle pickup location did update.
}
- (void)tripModel:(GMTCTripModel *)tripModel
didUpdateDropoffLocation:(nullable GMTSTerminalLocation *)dropoffLocation {
// Handle drop off location did update.
}
- (void)tripModel:(GMTCTripModel *)tripModel didUpdatePickupETA:(NSTimeInterval)pickupETA {
// Handle the pickup ETA did update.
}
- (void)tripModel:(GMTCTripModel *)tripModel
didUpdateRemainingWaypoints:(nullable NSArray<GMTSTripWaypoint *> *)remainingWaypoints {
// Handle updates to the pickup, dropoff or intermediate destinations of the trip.
}
- (void)tripModel:(GMTCTripModel *)tripModel didUpdateDropoffETA:(NSTimeInterval)dropoffETA {
// Handle the drop off ETA did update.
}
- (void)tripModel:(GMTCTripModel *)tripModel didFailUpdateTripWithError:(nullable NSError *)error {
// Handle the error.
}
- (void)tripModel:(GMTCTripModel *)tripModel
didUpdateIntermediateDestinations:
(nullable NSArray<GMTSTerminalLocation *> *)intermediateDestinations {
// Handle the intermediate destinations being updated.
}
…
@end
You can access information for the trip any time as follows:
Call the Consumer SDK for Android method
TripModel.getTripInfo()
. Calling this method doesn't force a data refresh, although data still continues to be refreshed at the refresh frequency.Get the Consumer SDK for iOS property
GMTCTripModel.currentTrip
.
Step 7. Display the journey in the consumer app
You can access the Rides and Deliveries user interface element APIs as follows:
Get the Consumer SDK for Android
ConsumerController
object. For more information, see Using the user interface element APIs.Get the Consumer SDK for iOS
GMTCMapView
object. For more information, see Using the user interface element APIs.
Example
The following code example demonstrates how to start the journey sharing user interface.
Java
JourneySharingSession session = JourneySharingSession.createInstance(tripModel);
consumerController.showSession(session);
Kotlin
val session = JourneySharingSession.createInstance(tripModel)
consumerController.showSession(session)
Swift
let journeySharingSession = GMTCJourneySharingSession(tripModel: tripModel)
mapView.show(journeySharingSession)
Objective-C
GMTCJourneySharingSession *journeySharingSession =
[[GMTCJourneySharingSession alloc] initWithTripModel:tripModel];
[self.mapView showMapViewSession:journeySharingSession];
Example scenario
Consider a back-to-back trip in which the vehicle has already picked up the consumer for the first trip. The vehicle is en route to the drop-off point when the driver receives a request to pickup a different consumer upon completion of the current trip.
At this point in time, the consumer for the first trip sees the following.
At the same point in time, the consumer for the second trip sees the following.
By default, the Consumer SDK displays just the active leg in the route, but you have the option of displaying the remaining leg, which includes the destination.
If you want to display information about waypoints from other trips, you can access all of the waypoints relevant to a trip as follows:
Call the Consumer SDK for Android method
TripModel.getTripInfo()
. Then callTripInfo.getRemainingWaypoints()
to get theTripWaypoint
s. EachTripWaypoint
object contains the trip ID, waypoint location, and waypoint type.Get the Consumer SDK for iOS property
GMTCTripModel.currentTrip
. Then get theGMTSTrip.remainingWaypoints
array to access theGMTSTripWaypoint
s. EachGMTSTripWaypoint
object contains the trip ID, waypoint location, and waypoint type.
Step 8. Manage the trip state in the Fleet Engine
You specify the state of a trip using one of the
TripStatus
enumeration values. When a trip's state changes (for example, changing from
ENROUTE_TO_PICKUP
to ARRIVED_AT_PICKUP
) you must update the trip state via
the Fleet Engine. Trip state always begins with a value of NEW
, and ends with
a value of either COMPLETE
or CANCELED
. For more information, see
trip_status
.
Example
The following backend integration sample demonstrates how to update the trip state in Fleet Engine.
static final String PROJECT_ID = "my-rideshare-co-gcp-project";
static final String TRIP_ID = "trip-8241890";
String tripName = "providers/" + PROJECT_ID + "/trips/" + TRIP_ID;
TripServiceBlockingStub tripService = TripService.newBlockingStub(channel);
// Trip settings to be updated.
Trip trip = Trip.newBuilder()
.setTripStatus(TripStatus.ARRIVED_AT_PICKUP)
.build();
// Trip update request
UpdateTripRequest updateTripRequest = UpdateTripRequest.newBuilder()
.setName(tripName)
.setTrip(trip)
.setUpdateMask(FieldMask.newBuilder().addPaths("trip_status"))
.build();
// Error handling.
try {
Trip updatedTrip = tripService.updateTrip(updateTripRequest);
} catch (StatusRuntimeException e) {
Status s = e.getStatus();
switch (s.getCode()) {
case NOT_FOUND: // The trip doesn't exist.
break;
case FAILED_PRECONDITION: // The given trip status is invalid.
break;
case PERMISSION_DENIED:
break;
}
return;
}