/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "GnssHAL_GnssBatchingInterface"
#include "GnssBatching.h"
#include <Gnss.h> // for wakelock consolidation
#include <GnssUtils.h>
#include <android/log.h> // for ALOGE
#include <vector>
namespace android {
namespace hardware {
namespace gnss {
namespace V1_0 {
namespace implementation {
sp<IGnssBatchingCallback> GnssBatching::sGnssBatchingCbIface = nullptr;
bool GnssBatching::sFlpSupportsBatching = false;
FlpCallbacks GnssBatching::sFlpCb = {
.size = sizeof(FlpCallbacks),
.location_cb = locationCb,
.acquire_wakelock_cb = acquireWakelockCb,
.release_wakelock_cb = releaseWakelockCb,
.set_thread_event_cb = setThreadEventCb,
.flp_capabilities_cb = flpCapabilitiesCb,
.flp_status_cb = flpStatusCb,
};
GnssBatching::GnssBatching(const FlpLocationInterface* flpLocationIface) :
mFlpLocationIface(flpLocationIface) {
}
/*
* This enum is used locally by various methods below. It is only used by the default
* implementation and is not part of the GNSS interface.
*/
enum BatchingValues : uint16_t {
// Numbers 0-3 were used in earlier implementations - using 4 to be distinct to the HAL
FLP_GNSS_BATCHING_CLIENT_ID = 4,
// Tech. mask of GNSS, and sensor aiding, for legacy HAL to fit with GnssBatching API
FLP_TECH_MASK_GNSS_AND_SENSORS = FLP_TECH_MASK_GNSS | FLP_TECH_MASK_SENSORS,
// Putting a cap to avoid possible memory issues. Unlikely values this high are supported.
MAX_LOCATIONS_PER_BATCH = 1000
};
void GnssBatching::locationCb(int32_t locationsCount, FlpLocation** locations) {
if (sGnssBatchingCbIface == nullptr) {
ALOGE("%s: GNSS Batching Callback Interface configured incorrectly", __func__);
return;
}
if (locations == nullptr) {
ALOGE("%s: Invalid locations from GNSS HAL", __func__);
return;
}
if (locationsCount < 0) {
ALOGE("%s: Negative location count: %d set to 0", __func__, locationsCount);
locationsCount = 0;
} else if (locationsCount > MAX_LOCATIONS_PER_BATCH) {
ALOGW("%s: Unexpected high location count: %d set to %d", __func__, locationsCount,
MAX_LOCATIONS_PER_BATCH);
locationsCount = MAX_LOCATIONS_PER_BATCH;
}
/**
* Note:
* Some existing implementations may drop duplicate locations. These could be expanded here
* but as there's ambiguity between no-GPS-fix vs. dropped duplicates in that implementation,
* and that's not specified by the fused_location.h, that isn't safe to do here.
* Fortunately, this shouldn't be a major issue in cases where GNSS batching is typically
* used (e.g. when user is likely in vehicle/bicycle.)
*/
std::vector<android::hardware::gnss::V1_0::GnssLocation> gnssLocations;
for (int iLocation = 0; iLocation < locationsCount; iLocation++) {
if (locations[iLocation] == nullptr) {
ALOGE("%s: Null location at slot: %d of %d, skipping", __func__, iLocation,
locationsCount);
continue;
}
if ((locations[iLocation]->sources_used & ~FLP_TECH_MASK_GNSS_AND_SENSORS) != 0)
{
ALOGE("%s: Unrequested location type %d at slot: %d of %d, skipping", __func__,
locations[iLocation]->sources_used, iLocation, locationsCount);
continue;
}
gnssLocations.push_back(convertToGnssLocation(locations[iLocation]));
}
auto ret = sGnssBatchingCbIface->gnssLocationBatchCb(gnssLocations);
if (!ret.isOk()) {
ALOGE("%s: Unable to invoke callback", __func__);
}
}
void GnssBatching::acquireWakelockCb() {
Gnss::acquireWakelockFused();
}
void GnssBatching::releaseWakelockCb() {
Gnss::releaseWakelockFused();
}
// this can just return success, because threads are now set up on demand in the jni layer
int32_t GnssBatching::setThreadEventCb(ThreadEvent /*event*/) {
return FLP_RESULT_SUCCESS;
}
void GnssBatching::flpCapabilitiesCb(int32_t capabilities) {
ALOGD("%s capabilities %d", __func__, capabilities);
if (capabilities & CAPABILITY_GNSS) {
// once callback is received and capabilities high enough, we know version is
// high enough for flush()
sFlpSupportsBatching = true;
}
}
void GnssBatching::flpStatusCb(int32_t status) {
ALOGD("%s (default implementation) not forwarding status: %d", __func__, status);
}
// Methods from ::android::hardware::gnss::V1_0::IGnssBatching follow.
Return<bool> GnssBatching::init(const sp<IGnssBatchingCallback>& callback) {
if (mFlpLocationIface == nullptr) {
ALOGE("%s: Flp batching is unavailable", __func__);
return false;
}
sGnssBatchingCbIface = callback;
return (mFlpLocationIface->init(&sFlpCb) == 0);
}
Return<uint16_t> GnssBatching::getBatchSize() {
if (mFlpLocationIface == nullptr) {
ALOGE("%s: Flp batching interface is unavailable", __func__);
return 0;
}
return mFlpLocationIface->get_batch_size();
}
Return<bool> GnssBatching::start(const IGnssBatching::Options& options) {
if (mFlpLocationIface == nullptr) {
ALOGE("%s: Flp batching interface is unavailable", __func__);
return false;
}
if (!sFlpSupportsBatching) {
ALOGE("%s: Flp batching interface not supported, no capabilities callback received",
__func__);
return false;
}
FlpBatchOptions optionsHw;
// Legacy code used 9999 mW for High accuracy, and 21 mW for balanced.
// New GNSS API just expects reasonable GNSS chipset behavior - do something efficient
// given the interval. This 100 mW limit should be quite sufficient (esp. given legacy code
// implementations may not even use this value.)
optionsHw.max_power_allocation_mW = 100;
optionsHw.sources_to_use = FLP_TECH_MASK_GNSS_AND_SENSORS;
optionsHw.flags = 0;
if (options.flags & Flag::WAKEUP_ON_FIFO_FULL) {
optionsHw.flags |= FLP_BATCH_WAKEUP_ON_FIFO_FULL;
}
optionsHw.period_ns = options.periodNanos;
optionsHw.smallest_displacement_meters = 0; // Zero offset - just use time interval
return (mFlpLocationIface->start_batching(FLP_GNSS_BATCHING_CLIENT_ID, &optionsHw)
== FLP_RESULT_SUCCESS);
}
Return<void> GnssBatching::flush() {
if (mFlpLocationIface == nullptr) {
ALOGE("%s: Flp batching interface is unavailable", __func__);
return Void();
}
mFlpLocationIface->flush_batched_locations();
return Void();
}
Return<bool> GnssBatching::stop() {
if (mFlpLocationIface == nullptr) {
ALOGE("%s: Flp batching interface is unavailable", __func__);
return false;
}
return (mFlpLocationIface->stop_batching(FLP_GNSS_BATCHING_CLIENT_ID) == FLP_RESULT_SUCCESS);
}
Return<void> GnssBatching::cleanup() {
if (mFlpLocationIface == nullptr) {
ALOGE("%s: Flp batching interface is unavailable", __func__);
return Void();
}
mFlpLocationIface->cleanup();
return Void();
}
} // namespace implementation
} // namespace V1_0
} // namespace gnss
} // namespace hardware
} // namespace android