You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
TermApp/app/src/main/cpp/PhoneDevice.cpp

711 lines
19 KiB
C++

#include "TerminalDevice.h"
/*
* Copyright 2018 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 "CameraTestHelpers"
#include <AndroidHelper.h>
#include "PhoneDevice.h"
#include <Client/Terminal.h>
#include <Utils.h>
2 years ago
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
// #include <opencv2/objdetect.hpp>
// #include <opencv2/features2d.hpp>
2 years ago
#include <opencv2/core/types.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <android/log.h>
#include <sys/system_properties.h>
extern bool GetJniEnv(JavaVM *vm, JNIEnv **env, bool& didAttachThread);
#define WAKELOCK_NAME "NDK_WK_"
2 years ago
// This value is 2 ^ 18 - 1, and is used to clamp the RGB values before their
// ranges
// are normalized to eight bits.
static const int kMaxChannelValue = 262143;
static long getFreeMemoryImpl(const char* const sums[], const size_t sumsLen[], size_t num)
{
int fd = open("/proc/meminfo", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
ALOGW("Unable to open /proc/meminfo");
return -1;
}
char buffer[2048];
const int len = read(fd, buffer, sizeof(buffer)-1);
close(fd);
if (len < 0) {
ALOGW("Unable to read /proc/meminfo");
return -1;
}
buffer[len] = 0;
size_t numFound = 0;
jlong mem = 0;
char* p = buffer;
while (*p && numFound < num) {
int i = 0;
while (sums[i]) {
if (strncmp(p, sums[i], sumsLen[i]) == 0) {
p += sumsLen[i];
while (*p == ' ') p++;
char* num = p;
while (*p >= '0' && *p <= '9') p++;
if (*p != 0) {
*p = 0;
p++;
if (*p == 0) p--;
}
mem += atoll(num) * 1024;
numFound++;
break;
}
i++;
}
p++;
}
return numFound > 0 ? mem : -1;
}
static jlong android_os_Process_getFreeMemory()
{
static const char* const sums[] = { "MemFree:", "Cached:", NULL };
static const size_t sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), 0 };
return getFreeMemoryImpl(sums, sumsLen, 2);
}
static jlong android_os_Process_getTotalMemory()
{
static const char* const sums[] = { "MemTotal:", NULL };
static const size_t sumsLen[] = { strlen("MemTotal:"), 0 };
return getFreeMemoryImpl(sums, sumsLen, 1);
}
2 years ago
static inline uint32_t YUV2RGB(int nY, int nU, int nV) {
nY -= 16;
nU -= 128;
nV -= 128;
if (nY < 0) nY = 0;
// This is the floating point equivalent. We do the conversion in integer
// because some Android devices do not have floating point in hardware.
// nR = (int)(1.164 * nY + 1.596 * nV);
// nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU);
// nB = (int)(1.164 * nY + 2.018 * nU);
int nR = (int)(1192 * nY + 1634 * nV);
int nG = (int)(1192 * nY - 833 * nV - 400 * nU);
int nB = (int)(1192 * nY + 2066 * nU);
nR = std::min(kMaxChannelValue, std::max(0, nR));
nG = std::min(kMaxChannelValue, std::max(0, nG));
nB = std::min(kMaxChannelValue, std::max(0, nB));
nR = (nR >> 10) & 0xff;
nG = (nG >> 10) & 0xff;
nB = (nB >> 10) & 0xff;
return 0xff000000 | (nR << 16) | (nG << 8) | nB;
}
CPhoneDevice::CPhoneCamera::CPhoneCamera(CPhoneDevice* dev, int32_t width, int32_t height) : NdkCamera(width, height), m_dev(dev)
{
}
bool CPhoneDevice::CPhoneCamera::on_image(const cv::Mat& rgb)
{
if (m_dev != NULL)
{
return m_dev->OnImageReady(rgb);
}
return false;
}
void CPhoneDevice::CPhoneCamera::on_error(const std::string& msg)
{
if (m_dev != NULL)
{
m_dev->onError(msg);
}
}
CPhoneDevice::CPhoneDevice(JavaVM* vm, jobject service)
{
mCamera = NULL;
m_listener = NULL;
mHeartbeatStartTime = 0;
mHeartbeatDuration = 0;
m_javaService = NULL;
m_vm = vm;
JNIEnv* env = NULL;
bool didAttachThread = false;
bool res = GetJniEnv(m_vm, &env, didAttachThread);
if (!res)
{
ALOGE("Failed to get JNI Env");
}
m_javaService = env->NewGlobalRef(service);
jclass classService = env->GetObjectClass(m_javaService);
mRegisterTimerMid = env->GetMethodID(classService, "registerTimer", "(JIJ)Z");
2 years ago
mRegisterHeartbeatMid = env->GetMethodID(classService, "registerHeartbeatTimer", "(I)V");
mUnregisterTimerMid = env->GetMethodID(classService, "unregisterTimer", "(J)Z");
mUpdateTimeMid = env->GetMethodID(classService, "updateTime", "(J)Z");
mRequestWakelockMid = env->GetMethodID(classService, "requestWakelock", "(Ljava/lang/String;J)V");
mReleaseWakelockMid = env->GetMethodID(classService, "releaseWakelock", "(Ljava/lang/String;)V");
mGetPowerInfoMid = env->GetMethodID(classService, "getPowerInfo", "()Ljava/lang/String;");
mRebootMid = env->GetMethodID(classService, "reboot", "()V");
mEnableGpsMid = env->GetMethodID(classService, "enableGps", "(Z)V");
mRequestPositionMid = env->GetMethodID(classService, "requestPosition", "()Z");
env->DeleteLocalRef(classService);
if (didAttachThread)
{
vm->DetachCurrentThread();
}
m_timerUidFeed = time(NULL) * 1000;
m_wakelockIdFeed = (unsigned long)m_timerUidFeed;
}
CPhoneDevice::~CPhoneDevice()
{
JNIEnv* env = NULL;
bool didAttachThread = false;
bool res = GetJniEnv(m_vm, &env, didAttachThread);
if (!res)
{
ALOGE("Failed to get JNI Env");
}
env->DeleteGlobalRef(m_javaService);
if (didAttachThread)
{
m_vm->DetachCurrentThread();
}
m_javaService = NULL;
}
void CPhoneDevice::SetListener(IListener* listener)
{
m_listener = listener;
}
bool CPhoneDevice::UpdateTime(time_t ts)
{
JNIEnv* env = NULL;
jboolean ret = JNI_FALSE;
bool didAttachThread = false;
bool res = GetJniEnv(m_vm, &env, didAttachThread);
if (!res)
{
ALOGE("Failed to get JNI Env");
}
jlong timeInMillis = ((jlong)ts) * 1000;
ret = env->CallBooleanMethod(m_javaService, mUpdateTimeMid, timeInMillis);
if (didAttachThread)
{
m_vm->DetachCurrentThread();
}
return (ret == JNI_TRUE);
}
bool CPhoneDevice::UpdateSchedules()
{
return true;
}
bool CPhoneDevice::QuerySystemProperties(std::map<std::string, std::string>& properties)
{
2 years ago
char value[PROP_VALUE_MAX] = { 0 };
std::map<std::string, std::string> powerInfo;
for (std::map<std::string, std::string>::iterator it = properties.begin(); it != properties.end(); ++it)
{
if (it->first == PROP_EQUIP_NAME)
{
__system_property_get("ro.product.name", value);
it->second = value;
}
else if (it->first == PROP_MODEL)
{
__system_property_get("ro.product.model", value);
it->second = value;
}
else if (it->first == PROP_BS_MANU)
{
__system_property_get("ro.product.manufacturer", value);
it->second = value;
}
else if (it->first == PROP_VERSION) {
__system_property_get("ro.build.version.release", value);
it->second = value;
}
else if (it->first == PROP_PROD_DATE) {
__system_property_get("ro.build.date.utc", value);
it->second = value;
}
else if (it->first == PROP_SN)
{
__system_property_get("ro.serialno", value);
it->second = value;
}
else if (it->first == PROP_IMEI)
{
__system_property_get("phone.imei", value);
it->second = value;
}
2 years ago
properties[PROP_BS_ID] = "SHXY";
2 years ago
}
2 years ago
__system_property_get("ro.telephony.default_network", value);
2 years ago
return true;
}
void CPhoneDevice::QueryPowerInfo(std::map<std::string, std::string>& powerInfo)
{
JNIEnv* env = NULL;
jboolean ret = JNI_FALSE;
bool didAttachThread = false;
bool res = GetJniEnv(m_vm, &env, didAttachThread);
if (!res)
{
ALOGE("Failed to get JNI Env");
}
jobject jobj = env->CallObjectMethod(m_javaService, mGetPowerInfoMid);
std::string str = jstring2string(env, (jstring)jobj);
if (didAttachThread)
{
m_vm->DetachCurrentThread();
}
if (!str.empty())
{
std::map<std::string, std::string> queries = parseQuery(str);
powerInfo.swap(queries);
}
}
bool CPhoneDevice::GetNextScheduleItem(uint32_t tsBasedZero, uint32_t scheduleTime, vector<uint32_t>& items)
{
return false;
}
2 years ago
bool CPhoneDevice::Reboot()
{
JNIEnv* env = NULL;
bool didAttachThread = false;
bool res = GetJniEnv(m_vm, &env, didAttachThread);
if (!res)
{
ALOGE("Failed to get JNI Env");
}
env->CallVoidMethod(m_javaService, mRebootMid);
if (didAttachThread)
{
m_vm->DetachCurrentThread();
}
return true;
}
bool CPhoneDevice::EnableGPS(bool enabled)
{
JNIEnv* env = NULL;
bool didAttachThread = false;
bool res = GetJniEnv(m_vm, &env, didAttachThread);
if (!res)
{
ALOGE("Failed to get JNI Env");
return false;
}
jboolean jenabled = enabled ? JNI_TRUE : JNI_FALSE;
env->CallVoidMethod(m_javaService, mEnableGpsMid, jenabled);
if (didAttachThread)
{
m_vm->DetachCurrentThread();
}
return true;
}
bool CPhoneDevice::RequestPosition()
{
JNIEnv* env = NULL;
bool didAttachThread = false;
bool res = GetJniEnv(m_vm, &env, didAttachThread);
if (!res)
{
ALOGE("Failed to get JNI Env");
return false;
}
jboolean ret = env->CallBooleanMethod(m_javaService, mRequestPositionMid);
if (didAttachThread)
{
m_vm->DetachCurrentThread();
}
return (ret == JNI_TRUE);
2 years ago
}
IDevice::timer_uid_t CPhoneDevice::RegisterTimer(unsigned int timerType, unsigned int timeout, unsigned long times/* = 0*/)
{
IDevice::timer_uid_t uid = m_timerUidFeed.fetch_add(1);
ALOGI("NDK RegTimer: uid=%lld Type=%u timeout=%u", uid, timerType, timeout);
JNIEnv* env = NULL;
bool didAttachThread = false;
bool res = GetJniEnv(m_vm, &env, didAttachThread);
if (!res)
{
ALOGE("Failed to get JNI Env");
return 0;
}
jboolean ret = env->CallBooleanMethod(m_javaService, mRegisterTimerMid, (jlong)uid, (jint)timeout, (jlong)times);
if (didAttachThread)
{
m_vm->DetachCurrentThread();
}
if (ret == JNI_TRUE)
{
unsigned long val = timerType;
mTimers.insert(mTimers.end(), std::pair<IDevice::timer_uid_t, unsigned long>(uid, val));
return uid;
}
return 0;
}
bool CPhoneDevice::UnregisterTimer(IDevice::timer_uid_t uid)
{
jboolean ret = JNI_FALSE;
JNIEnv* env = NULL;
bool didAttachThread = false;
bool res = GetJniEnv(m_vm, &env, didAttachThread);
if (!res)
{
ALOGE("Failed to get JNI Env");
return false;
}
ret = env->CallBooleanMethod(m_javaService, mUnregisterTimerMid, (jlong)uid);
if (didAttachThread)
{
m_vm->DetachCurrentThread();
}
if (ret == JNI_TRUE)
{
mTimers.erase(uid);
return true;
}
return false;
}
unsigned long CPhoneDevice::RequestWakelock(unsigned long timeout)
{
unsigned long wakelockId = m_wakelockIdFeed.fetch_add(1);
std::string name = WAKELOCK_NAME;
name += to_string(wakelockId);
// ALOGI("RequestWakelock=%lld",wakelockId);
jboolean ret = JNI_FALSE;
JNIEnv* env = NULL;
bool didAttachThread = false;
bool res = GetJniEnv(m_vm, &env, didAttachThread);
if (!res)
{
ALOGE("Failed to get JNI Env");
return 0;
}
jstring jname = env->NewStringUTF(name.c_str());
jlong jtimeout = (jlong)timeout;
env->CallVoidMethod(m_javaService, mRequestWakelockMid, jname, jtimeout);
// env->ReleaseStringUTFChars(jname, name.c_str());
env->DeleteLocalRef(jname);
if (didAttachThread)
{
m_vm->DetachCurrentThread();
}
return wakelockId;
}
bool CPhoneDevice::ReleaseWakelock(unsigned long wakelock)
{
// ALOGI("ReleaseWakelock=%lld", wakelock);
std::string name = WAKELOCK_NAME;
name += to_string(wakelock);
jboolean ret = JNI_FALSE;
JNIEnv* env = NULL;
bool didAttachThread = false;
bool res = GetJniEnv(m_vm, &env, didAttachThread);
if (!res)
{
ALOGE("Failed to get JNI Env");
return false;
}
jstring jname = env->NewStringUTF(name.c_str());
env->CallVoidMethod(m_javaService, mReleaseWakelockMid, jname);
env->DeleteLocalRef(jname);
// env->ReleaseStringUTFChars(jname, name.c_str());
if (didAttachThread)
{
m_vm->DetachCurrentThread();
}
return true;
}
bool CPhoneDevice::FireTimer(timer_uid_t uid, unsigned long times)
{
std::map<IDevice::timer_uid_t, unsigned long>::iterator it = mTimers.find(uid);
if (it == mTimers.end())
{
return false;
}
unsigned long timerType = it->second & 0xFFFFFFFF;
unsigned long ntimes = (it->second & 0xFFFFFFFF00000000) >> 32;
ntimes++;
if (timerType != 100)
{
int aa = 0;
}
it->second = timerType | (ntimes << 32);
if (m_listener == NULL)
{
return false;
}
m_listener->OnTimeout(uid, timerType, ntimes);
return true;
}
IDevice::timer_uid_t CPhoneDevice::RegisterHeartbeat(unsigned int timerType, unsigned int timeout)
{
mHeartbeatStartTime = time(NULL);
mHeartbeatDuration = timeout;
2 years ago
IDevice::timer_uid_t uid = m_timerUidFeed.fetch_add(1);
jboolean ret = JNI_FALSE;
JNIEnv* env = NULL;
bool didAttachThread = false;
bool res = GetJniEnv(m_vm, &env, didAttachThread);
if (!res)
{
ALOGE("Failed to get JNI Env");
return 0;
}
if (didAttachThread)
2 years ago
{
env->CallVoidMethod(m_javaService, mRegisterHeartbeatMid, (jint)timeout);
m_vm->DetachCurrentThread();
}
return uid;
}
bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector<OSD_INFO>& osds, const std::string& path)
{
if (mCamera != NULL)
{
delete mCamera;
mCamera = NULL;
}
ALOGI("TAKE_PHOTO: CH=%u PR=%u\n", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset);
mPhotoInfo = photoInfo;
mPath = path;
mOsds = osds;
mCamera = new CPhoneCamera(this, photoInfo.width, photoInfo.height);
if (mCamera->open(to_string(photoInfo.channel - 1).c_str()) == 0)
{
}
else
{
delete mCamera;
mCamera = NULL;
return false;
}
return true;
}
bool CPhoneDevice::CloseCamera()
{
if (mCamera != NULL)
{
auto camera = mCamera;
mCamera = NULL;
camera->close();
delete camera;
}
return true;
}
void CPhoneDevice::CloseCamera2(CPhoneDevice::CPhoneCamera* camera)
{
std::this_thread::sleep_for(std::chrono::milliseconds(16));
camera->close();
delete camera;
}
bool CPhoneDevice::OnImageReady(const cv::Mat& mat)
{
mPhotoInfo.photoTime = time(NULL);
int baseline = 0;
cv::Size textSize, textSize2;
double fontScale = 1; // base 1024
double height = mat.size().height;
double width = mat.size().width;
double ratio = std::min(height / 1024, width / 1920);
fontScale = fontScale * ratio;
int thickness2 = 1 * ratio;
if (thickness2 == 0) thickness2 = 1;
int thickness1 = thickness2 + 1;
cv::Scalar scalar1(0, 0, 0); // black
cv::Scalar scalar2(255, 255, 255); // white
cv::Point pt1, pt2;
for (vector<OSD_INFO>::const_iterator it = mOsds.cbegin(); it != mOsds.cend(); ++it)
{
textSize = cv::getTextSize(it->text, cv::FONT_HERSHEY_COMPLEX, fontScale, thickness1, &baseline);
textSize2 = cv::getTextSize(it->text, cv::FONT_HERSHEY_COMPLEX, fontScale, thickness2, &baseline);
ALOGE("putText: w1=%d w2=%d tn=%d/%d", textSize.width, textSize2.width, thickness1, thickness2);
if (it->alignment == OSD_ALIGNMENT_TOP_LEFT)
{
pt1.x = it->x * ratio;
pt1.y = it->y * ratio + textSize.height;
}
else if (it->alignment == OSD_ALIGNMENT_TOP_RIGHT)
{
pt1.x = width - textSize.width - it->x * ratio;
pt1.y= it->y * ratio + textSize.height;
}
else if (it->alignment == OSD_ALIGNMENT_BOTTOM_RIGHT)
{
pt1.x = width - textSize.width - it->x * ratio;
pt1.y = height - it->y * ratio;
}
else if (it->alignment == OSD_ALIGNMENT_BOTTOM_LEFT)
{
pt1.x = it->x * ratio;
pt1.y = height - it->y * ratio;
}
cv::putText(mat, it->text, pt1, cv::FONT_HERSHEY_COMPLEX, fontScale, scalar1, thickness1,cv::LINE_AA);
cv::putText(mat, it->text, pt1, cv::FONT_HERSHEY_COMPLEX, fontScale, scalar2, thickness2,cv::LINE_AA);
}
vector <int> compression_params;
compression_params.push_back(cv::IMWRITE_JPEG_QUALITY);
compression_params.push_back(mPhotoInfo.quality);
bool res = false;
std::string fullPath = mPath + CTerminal::BuildPhotoFileName(mPhotoInfo);
if (!std::filesystem::exists(std::filesystem::path(fullPath)))
{
bool res = cv::imwrite(fullPath.c_str(), mat, compression_params);
if (!res)
{
ALOGE("Failed to write photo: %s", fullPath.c_str());
}
else
{
ALOGI("Succeeded to write photo: %s", fullPath.c_str());
}
TakePhotoCb(res, mPhotoInfo, fullPath, time(NULL));
}
else
{
ALOGI("Photo file exists: %s", mPath.c_str());
}
CPhoneCamera* pCamera = mCamera;
mCamera = NULL;
std::thread closeThread(&CPhoneDevice::CloseCamera2, pCamera);
closeThread.detach();
return res;
}
void CPhoneDevice::onError(const std::string& msg)
{
// XFLOG(XFLOG_SEVERITY_ERROR, "Failed to Take Photo: %s", msg.c_str());
ALOGE("Failed to Take Photo: %s", msg.c_str());
CPhoneCamera* pCamera = mCamera;
mCamera = NULL;
TakePhotoCb(false, mPhotoInfo, mPath, 0);
std::thread closeThread(&CPhoneDevice::CloseCamera2, pCamera);
closeThread.detach();
}
std::string CPhoneDevice::GetFileName() const
{
return mPath;
}
2 years ago
void CPhoneDevice::UpdatePosition(double lon, double lat, time_t ts)
{
if (m_listener != NULL)
{
return m_listener->OnPositionDataArrived(lon, lat, ts);
}
}