Merge "Add a notification channel for car services."
diff --git a/car_product/build/car_base.mk b/car_product/build/car_base.mk
index 7da9477..e73603c 100644
--- a/car_product/build/car_base.mk
+++ b/car_product/build/car_base.mk
@@ -88,7 +88,8 @@
PRODUCT_PACKAGES += evs_app
PRODUCT_COPY_FILES += \
packages/services/Car/evs/app/config.json:system/etc/automotive/evs/config.json \
- packages/services/Car/evs/app/CarFromTop.png:system/etc/automotive/evs/CarFromTop.png
+ packages/services/Car/evs/app/CarFromTop.png:system/etc/automotive/evs/CarFromTop.png \
+ packages/services/Car/evs/app/LabeledChecker.png:system/etc/automotive/evs/LabeledChecker.png
PRODUCT_PACKAGES += android.hardware.automotive.vehicle@2.1-service
diff --git a/evs/app/Android.mk b/evs/app/Android.mk
index 5cf6eec..784068d 100644
--- a/evs/app/Android.mk
+++ b/evs/app/Android.mk
@@ -8,7 +8,7 @@
EvsStateControl.cpp \
RenderBase.cpp \
RenderDirectView.cpp \
- RenderPixelCopy.cpp \
+ RenderTopView.cpp \
ConfigManager.cpp \
glError.cpp \
shader.cpp \
diff --git a/evs/app/ConfigManager.h b/evs/app/ConfigManager.h
index cf1c4df..0d24919 100644
--- a/evs/app/ConfigManager.h
+++ b/evs/app/ConfigManager.h
@@ -56,7 +56,7 @@
};
float getDisplayRightLocation(float aspectRatio) const {
// Given the display aspect ratio (width over height), how far can we see to the right?
- return (getDisplayTopLocation() - getDisplayBottomLocation()) * 0.5f / aspectRatio;
+ return (getDisplayTopLocation() - getDisplayBottomLocation()) * 0.5f * aspectRatio;
};
float getDisplayLeftLocation(float aspectRatio) const {
// Given the display aspect ratio (width over height), how far can we see to the left?
diff --git a/evs/app/EvsStateControl.cpp b/evs/app/EvsStateControl.cpp
index eb3c698..44986c0 100644
--- a/evs/app/EvsStateControl.cpp
+++ b/evs/app/EvsStateControl.cpp
@@ -15,7 +15,7 @@
*/
#include "EvsStateControl.h"
#include "RenderDirectView.h"
-#include "RenderPixelCopy.h"
+#include "RenderTopView.h"
#include <stdio.h>
#include <string.h>
@@ -38,6 +38,7 @@
mVehicle(pVnet),
mEvs(pEvs),
mDisplay(pDisplay),
+ mConfig(config),
mCurrentState(OFF) {
// Initialize the property value containers we'll be updating (they'll be zeroed by default)
@@ -49,10 +50,10 @@
mGearValue.prop = static_cast<int32_t>(VehicleProperty::GEAR_SELECTION);
mTurnSignalValue.prop = static_cast<int32_t>(VehicleProperty::TURN_SIGNAL_STATE);
+#if 0 // This way we only ever deal with cameras which exist in the system
// Build our set of cameras for the states we support
ALOGD("Requesting camera list");
- mEvs->getCameraList([this, &config]
- (hidl_vec<CameraDesc> cameraList) {
+ mEvs->getCameraList([this, &config](hidl_vec<CameraDesc> cameraList) {
ALOGI("Camera list callback received %zu cameras",
cameraList.size());
for (auto&& cam: cameraList) {
@@ -62,17 +63,23 @@
// Check our configuration for information about this camera
// Note that a camera can have a compound function string
// such that a camera can be "right/reverse" and be used for both.
+ // If more than one camera is listed for a given function, we'll
+ // list all of them and let the UX/rendering logic use one, some
+ // or all of them as appropriate.
for (auto&& info: config.getCameras()) {
if (cam.cameraId == info.cameraId) {
// We found a match!
if (info.function.find("reverse") != std::string::npos) {
- mCameraInfo[State::REVERSE] = info;
+ mCameraList[State::REVERSE].push_back(info);
}
if (info.function.find("right") != std::string::npos) {
- mCameraInfo[State::RIGHT] = info;
+ mCameraList[State::RIGHT].push_back(info);
}
if (info.function.find("left") != std::string::npos) {
- mCameraInfo[State::LEFT] = info;
+ mCameraList[State::LEFT].push_back(info);
+ }
+ if (info.function.find("park") != std::string::npos) {
+ mCameraList[State::PARKING].push_back(info);
}
cameraConfigFound = true;
break;
@@ -85,6 +92,25 @@
}
}
);
+#else // This way we use placeholders for cameras in the configuration but not reported by EVS
+ // Build our set of cameras for the states we support
+ ALOGD("Requesting camera list");
+ for (auto&& info: config.getCameras()) {
+ if (info.function.find("reverse") != std::string::npos) {
+ mCameraList[State::REVERSE].push_back(info);
+ }
+ if (info.function.find("right") != std::string::npos) {
+ mCameraList[State::RIGHT].push_back(info);
+ }
+ if (info.function.find("left") != std::string::npos) {
+ mCameraList[State::LEFT].push_back(info);
+ }
+ if (info.function.find("park") != std::string::npos) {
+ mCameraList[State::PARKING].push_back(info);
+ }
+ }
+#endif
+
ALOGD("State controller ready");
}
@@ -210,6 +236,7 @@
}
// Choose our desired EVS state based on the current car state
+ // TODO: Update this logic, and include user input when choosing if a view should be presented
State desiredState = OFF;
if (mGearValue.value.int32Values[0] == int32_t(VehicleGear::GEAR_REVERSE)) {
desiredState = REVERSE;
@@ -217,6 +244,8 @@
desiredState = RIGHT;
} else if (mTurnSignalValue.value.int32Values[0] == int32_t(VehicleTurnSignal::LEFT)) {
desiredState = LEFT;
+ } else if (mGearValue.value.int32Values[0] == int32_t(VehicleGear::GEAR_PARK)) {
+ desiredState = PARKING;
}
ALOGV("Selected state %d.", desiredState);
@@ -252,10 +281,10 @@
return true;
}
- ALOGD(" Current state (%d) = %s", mCurrentState,
- mCameraInfo[mCurrentState].cameraId.c_str());
- ALOGD(" Desired state (%d) = %s", desiredState,
- mCameraInfo[desiredState].cameraId.c_str());
+ ALOGD(" Current state %d has %zu cameras", mCurrentState,
+ mCameraList[mCurrentState].size());
+ ALOGD(" Desired state %d has %zu cameras", desiredState,
+ mCameraList[desiredState].size());
// Since we're changing states, shut down the current renderer
if (mCurrentRenderer != nullptr) {
@@ -264,15 +293,21 @@
}
// Do we need a new direct view renderer?
- if (desiredState == PARKING) {
- // We need a new top view renderer
- // TODO: Implement this by instantiating a new RenderTopView
- } else if (!mCameraInfo[desiredState].cameraId.empty()) {
- // We have a camera assigned to this state for direct view
- mCurrentRenderer = std::make_unique<RenderDirectView>(mEvs, mCameraInfo[desiredState]);
-// mCurrentRenderer = std::make_unique<RenderPixelCopy>(mEvs, mCameraInfo[desiredState]);
+ if (mCameraList[desiredState].size() > 1 || desiredState == PARKING) {
+ // TODO: DO we want other kinds of compound view or else sequentially selected views?
+ mCurrentRenderer = std::make_unique<RenderTopView>(mEvs,
+ mCameraList[desiredState],
+ mConfig);
if (!mCurrentRenderer) {
- ALOGE("Failed to construct renderer. Skipping state change.");
+ ALOGE("Failed to construct top view renderer. Skipping state change.");
+ return false;
+ }
+ } else if (mCameraList[desiredState].size() == 1) {
+ // We have a camera assigned to this state for direct view
+ mCurrentRenderer = std::make_unique<RenderDirectView>(mEvs,
+ mCameraList[desiredState][0]);
+ if (!mCurrentRenderer) {
+ ALOGE("Failed to construct direct renderer. Skipping state change.");
return false;
}
}
diff --git a/evs/app/EvsStateControl.h b/evs/app/EvsStateControl.h
index 3a830cf..cfb6833 100644
--- a/evs/app/EvsStateControl.h
+++ b/evs/app/EvsStateControl.h
@@ -86,13 +86,14 @@
sp<IVehicle> mVehicle;
sp<IEvsEnumerator> mEvs;
sp<IEvsDisplay> mDisplay;
+ const ConfigManager& mConfig;
VehiclePropValue mGearValue;
VehiclePropValue mTurnSignalValue;
State mCurrentState = OFF;
- ConfigManager::CameraInfo mCameraInfo[NUM_STATES] = {};
+ std::vector<ConfigManager::CameraInfo> mCameraList[NUM_STATES];
std::unique_ptr<RenderBase> mCurrentRenderer;
std::thread mRenderThread; // The thread that runs the main rendering loop
diff --git a/evs/app/LabeledChecker.png b/evs/app/LabeledChecker.png
new file mode 100644
index 0000000..02da85d
--- /dev/null
+++ b/evs/app/LabeledChecker.png
Binary files differ
diff --git a/evs/app/RenderBase.cpp b/evs/app/RenderBase.cpp
index d8bb7f0..cb6aa93 100644
--- a/evs/app/RenderBase.cpp
+++ b/evs/app/RenderBase.cpp
@@ -36,7 +36,7 @@
EGLImageKHR RenderBase::sKHRimage = EGL_NO_IMAGE_KHR;
unsigned RenderBase::sWidth = 0;
unsigned RenderBase::sHeight = 0;
-
+float RenderBase::sAspectRatio = 0.0f;
bool RenderBase::prepareGL() {
@@ -182,21 +182,6 @@
return false;
}
-#if 0 // Do we need a z-buffer? Does this code work correctly?
- // Request a (local) depth buffer so we can z-test while drawing
- glBindRenderbuffer(GL_RENDERBUFFER, sDepthBuffer);
- if ((sWidth != tgtBuffer.width) || (sHeight != tgtBuffer.height)) {
- // We can't reuse the depth buffer, so make a new one
- sWidth = tgtBuffer.width;
- sHeight = tgtBuffer.height;
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, sWidth, sHeight);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER,
- GL_DEPTH_ATTACHMENT,
- GL_RENDERBUFFER,
- sDepthBuffer);
- }
-#endif
-
GLenum checkResult = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (checkResult != GL_FRAMEBUFFER_COMPLETE) {
ALOGE("Offscreen framebuffer not configured successfully (%d: %s)",
@@ -204,6 +189,21 @@
return false;
}
+ // Store the size of our target buffer
+ sWidth = tgtBuffer.width;
+ sHeight = tgtBuffer.height;
+ sAspectRatio = (float)sWidth / sHeight;
+
+ // Set the viewport
+ glViewport(0, 0, sWidth, sHeight);
+
+#if 1 // We don't actually need the clear if we're going to cover the whole screen anyway
+ // Clear the color buffer
+ glClearColor(0.8f, 0.1f, 0.2f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+#endif
+
+
return true;
}
diff --git a/evs/app/RenderBase.h b/evs/app/RenderBase.h
index df34639..25474d5 100644
--- a/evs/app/RenderBase.h
+++ b/evs/app/RenderBase.h
@@ -61,6 +61,7 @@
static unsigned sWidth;
static unsigned sHeight;
+ static float sAspectRatio;
};
diff --git a/evs/app/RenderDirectView.cpp b/evs/app/RenderDirectView.cpp
index a50b264..24eb485 100644
--- a/evs/app/RenderDirectView.cpp
+++ b/evs/app/RenderDirectView.cpp
@@ -19,10 +19,8 @@
#include "glError.h"
#include "shader.h"
#include "shader_simpleTex.h"
-#include "shader_testColors.h"
#include <log/log.h>
-
#include <math/mat4.h>
@@ -51,23 +49,13 @@
}
}
- // TODO: Remove this once we're done testing with it
- if (!mShaderTestColors) {
- mShaderTestColors = buildShaderProgram(vtxShader_testColors,
- pixShader_testColors,
- "testColors");
- if (!mShaderTestColors) {
- ALOGE("Error building shader program");
- return false;
- }
- }
-
// Construct our video texture
mTexture.reset(createVideoTexture(mEnumerator, mCameraInfo.cameraId.c_str(), sDisplay));
if (!mTexture) {
ALOGE("Failed to set up video texture for %s (%s)",
mCameraInfo.cameraId.c_str(), mCameraInfo.function.c_str());
- return false;
+// TODO: For production use, we may actually want to fail in this case, but not yet...
+// return false;
}
return true;
@@ -89,15 +77,6 @@
return false;
}
- // Set the viewport
- glViewport(0, 0, tgtBuffer.width, tgtBuffer.height);
-
-#if 0 // We don't actually need the clear if we're going to cover the whole screen anyway
- // Clear the color buffer
- glClearColor(0.8f, 0.1f, 0.2f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
-#endif
-
// Select our screen space simple texture shader
glUseProgram(mShaderProgram);
@@ -108,7 +87,7 @@
return false;
} else {
const android::mat4 identityMatrix;
- glUniformMatrix4fv(loc, 1, false, &identityMatrix[0][0]);
+ glUniformMatrix4fv(loc, 1, false, identityMatrix.asArray());
}
@@ -118,20 +97,6 @@
glBindTexture(GL_TEXTURE_2D, mTexture->glId());
-#if 0
- static TexWrapper* sTestTexture = createTextureFromPng("/system/etc/automotive/evs/CarFromTop.png");
- if (sTestTexture) {
- static int tickTock = 0;
- tickTock =~tickTock;
- if (tickTock) {
- printf("tick...");
- glBindTexture(GL_TEXTURE_2D, sTestTexture->glId());
- } else {
- printf("tock\n");
- }
- }
-#endif
-
GLint sampler = glGetUniformLocation(mShaderProgram, "tex");
if (sampler < 0) {
ALOGE("Couldn't set shader parameter 'tex'");
@@ -146,20 +111,11 @@
// Draw a rectangle on the screen
- // TODO: We pulled in from the edges for now for diagnostic purposes...
-#if 1
GLfloat vertsCarPos[] = { -1.0, 1.0, 0.0f, // left top in window space
1.0, 1.0, 0.0f, // right top
-1.0, -1.0, 0.0f, // left bottom
1.0, -1.0, 0.0f // right bottom
};
-#else
- GLfloat vertsCarPos[] = { -0.8, 0.8, 0.0f, // left top in window space
- 0.8, 0.8, 0.0f, // right top
- -0.8, -0.8, 0.0f, // left bottom
- 0.8, -0.8, 0.0f // right bottom
- };
-#endif
// TODO: We're flipping horizontally here, but should do it only for specified cameras!
GLfloat vertsCarTex[] = { 1.0f, 1.0f, // left top
0.0f, 1.0f, // right top
diff --git a/evs/app/RenderDirectView.h b/evs/app/RenderDirectView.h
index c452297..1543fce 100644
--- a/evs/app/RenderDirectView.h
+++ b/evs/app/RenderDirectView.h
@@ -35,8 +35,8 @@
public:
RenderDirectView(sp<IEvsEnumerator> enumerator, const ConfigManager::CameraInfo& cam);
- virtual bool activate();
- virtual void deactivate();
+ virtual bool activate() override;
+ virtual void deactivate() override;
virtual bool drawFrame(const BufferDesc& tgtBuffer);
@@ -47,7 +47,6 @@
std::unique_ptr<VideoTex> mTexture;
GLuint mShaderProgram = 0;
- GLuint mShaderTestColors = 0;
};
diff --git a/evs/app/RenderPixelCopy.h b/evs/app/RenderPixelCopy.h
index ff3917a..ee6eede 100644
--- a/evs/app/RenderPixelCopy.h
+++ b/evs/app/RenderPixelCopy.h
@@ -35,8 +35,8 @@
public:
RenderPixelCopy(sp<IEvsEnumerator> enumerator, const ConfigManager::CameraInfo& cam);
- virtual bool activate();
- virtual void deactivate();
+ virtual bool activate() override;
+ virtual void deactivate() override;
virtual bool drawFrame(const BufferDesc& tgtBuffer);
diff --git a/evs/app/RenderTopView.cpp b/evs/app/RenderTopView.cpp
new file mode 100644
index 0000000..a44f2af
--- /dev/null
+++ b/evs/app/RenderTopView.cpp
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "RenderTopView.h"
+#include "VideoTex.h"
+#include "glError.h"
+#include "shader.h"
+#include "shader_simpleTex.h"
+#include "shader_projectedTex.h"
+
+#include <log/log.h>
+#include <math/mat4.h>
+#include <math/vec3.h>
+
+
+// Simple aliases to make geometric math using vectors more readable
+static const unsigned X = 0;
+static const unsigned Y = 1;
+static const unsigned Z = 2;
+//static const unsigned W = 3;
+
+
+// Since we assume no roll in these views, we can simplify the required math
+static android::vec3 unitVectorFromPitchAndYaw(float pitch, float yaw) {
+ float sinPitch, cosPitch;
+ sincosf(pitch, &sinPitch, &cosPitch);
+ float sinYaw, cosYaw;
+ sincosf(yaw, &sinYaw, &cosYaw);
+ return android::vec3(cosPitch * -sinYaw,
+ cosPitch * cosYaw,
+ sinPitch);
+}
+
+
+// Helper function to set up a perspective matrix with independent horizontal and vertical
+// angles of view.
+static android::mat4 perspective(float hfov, float vfov, float near, float far) {
+ const float tanHalfFovX = tanf(hfov * 0.5f);
+ const float tanHalfFovY = tanf(vfov * 0.5f);
+
+ android::mat4 p(0.0f);
+ p[0][0] = 1.0f / tanHalfFovX;
+ p[1][1] = 1.0f / tanHalfFovY;
+ p[2][2] = - (far + near) / (far - near);
+ p[2][3] = -1.0f;
+ p[3][2] = - (2.0f * far * near) / (far - near);
+ return p;
+}
+
+
+// Helper function to set up a view matrix for a camera given it's yaw & pitch & location
+// Yes, with a bit of work, we could use lookAt, but it does a lot of extra work
+// internally that we can short cut.
+static android::mat4 cameraLookMatrix(const ConfigManager::CameraInfo& cam) {
+ float sinYaw, cosYaw;
+ sincosf(cam.yaw, &sinYaw, &cosYaw);
+
+ // Construct principal unit vectors
+ android::vec3 vAt = unitVectorFromPitchAndYaw(cam.pitch, cam.yaw);
+ android::vec3 vRt = android::vec3(cosYaw, sinYaw, 0.0f);
+ android::vec3 vUp = -cross(vAt, vRt);
+ android::vec3 eye = android::vec3(cam.position[X], cam.position[Y], cam.position[Z]);
+
+ android::mat4 Result(1.0f);
+ Result[0][0] = vRt.x;
+ Result[1][0] = vRt.y;
+ Result[2][0] = vRt.z;
+ Result[0][1] = vUp.x;
+ Result[1][1] = vUp.y;
+ Result[2][1] = vUp.z;
+ Result[0][2] =-vAt.x;
+ Result[1][2] =-vAt.y;
+ Result[2][2] =-vAt.z;
+ Result[3][0] =-dot(vRt, eye);
+ Result[3][1] =-dot(vUp, eye);
+ Result[3][2] = dot(vAt, eye);
+ return Result;
+}
+
+
+RenderTopView::RenderTopView(sp<IEvsEnumerator> enumerator,
+ const std::vector<ConfigManager::CameraInfo>& camList,
+ const ConfigManager& mConfig) :
+ mEnumerator(enumerator),
+ mConfig(mConfig) {
+
+ // Copy the list of cameras we're to employ into our local storage. We'll create and
+ // associate a streaming video texture when we are activated.
+ mActiveCameras.reserve(camList.size());
+ for (unsigned i=0; i<camList.size(); i++) {
+ mActiveCameras.emplace_back(camList[i]);
+ }
+}
+
+
+bool RenderTopView::activate() {
+ // Ensure GL is ready to go...
+ if (!prepareGL()) {
+ ALOGE("Error initializing GL");
+ return false;
+ }
+
+ // Load our shader programs
+ mPgmAssets.simpleTexture = buildShaderProgram(vtxShader_simpleTexture,
+ pixShader_simpleTexture,
+ "simpleTexture");
+ if (!mPgmAssets.simpleTexture) {
+ ALOGE("Failed to build shader program");
+ return false;
+ }
+ mPgmAssets.projectedTexture = buildShaderProgram(vtxShader_projectedTexture,
+ pixShader_projectedTexture,
+ "projectedTexture");
+ if (!mPgmAssets.projectedTexture) {
+ ALOGE("Failed to build shader program");
+ return false;
+ }
+
+
+ // Load the checkerboard text image
+ mTexAssets.checkerBoard.reset(createTextureFromPng(
+ "/system/etc/automotive/evs/LabeledChecker.png"));
+ if (!mTexAssets.checkerBoard->glId()) {
+ ALOGE("Failed to load checkerboard texture");
+ return false;
+ }
+
+ // Load the car image
+ mTexAssets.carTopView.reset(createTextureFromPng(
+ "/system/etc/automotive/evs/CarFromTop.png"));
+ if (!mTexAssets.carTopView->glId()) {
+ ALOGE("Failed to load carTopView texture");
+ return false;
+ }
+
+
+ // Set up streaming video textures for our associated cameras
+ for (auto&& cam: mActiveCameras) {
+ cam.tex.reset(createVideoTexture(mEnumerator, cam.info.cameraId.c_str(), sDisplay));
+ if (!cam.tex) {
+ ALOGE("Failed to set up video texture for %s (%s)",
+ cam.info.cameraId.c_str(), cam.info.function.c_str());
+// TODO: For production use, we may actually want to fail in this case, but not yet...
+// return false;
+ }
+ }
+
+ return true;
+}
+
+
+void RenderTopView::deactivate() {
+ // Release our video textures
+ // We can't hold onto it because some other Render object might need the same camera
+ // TODO: If start/stop costs become a problem, we could share video textures
+ for (auto&& cam: mActiveCameras) {
+ cam.tex = nullptr;
+ }
+}
+
+
+bool RenderTopView::drawFrame(const BufferDesc& tgtBuffer) {
+ // Tell GL to render to the given buffer
+ if (!attachRenderTarget(tgtBuffer)) {
+ ALOGE("Failed to attached render target");
+ return false;
+ }
+
+ // Set up our top down projection matrix from car space (world units, Xfwd, Yright, Zup)
+ // to view space (-1 to 1)
+ const float top = mConfig.getDisplayTopLocation();
+ const float bottom = mConfig.getDisplayBottomLocation();
+ const float right = mConfig.getDisplayRightLocation(sAspectRatio);
+ const float left = mConfig.getDisplayLeftLocation(sAspectRatio);
+
+ const float near = 10.0f; // arbitrary top of view volume
+ const float far = 0.0f; // ground plane is at zero
+
+ // We can use a simple, unrotated ortho view since the screen and car space axis are
+ // naturally aligned in the top down view.
+ // TODO: Not sure if flipping top/bottom here is "correct" or a double reverse...
+// orthoMatrix = android::mat4::ortho(left, right, bottom, top, near, far);
+ orthoMatrix = android::mat4::ortho(left, right, top, bottom, near, far);
+
+
+ // Refresh our video texture contents. We do it all at once in hopes of getting
+ // better coherence among images. This does not guarantee synchronization, of course...
+ for (auto&& cam: mActiveCameras) {
+ if (cam.tex) {
+ cam.tex->refresh();
+ }
+ }
+
+ // Iterate over all the cameras and project their images onto the ground plane
+ for (auto&& cam: mActiveCameras) {
+ renderCameraOntoGroundPlane(cam);
+ }
+
+ // Draw the car image
+ renderCarTopView();
+
+ // Wait for the rendering to finish
+ glFinish();
+
+ return true;
+}
+
+
+//
+// Responsible for drawing the car's self image in the top down view.
+// Draws in car model space (units of meters with origin at center of rear axel)
+// NOTE: We probably want to eventually switch to using a VertexArray based model system.
+//
+void RenderTopView::renderCarTopView() {
+ // Compute the corners of our image footprint in car space
+ const float carLengthInTexels = mConfig.carGraphicRearPixel() - mConfig.carGraphicFrontPixel();
+ const float carSpaceUnitsPerTexel = mConfig.getCarLength() / carLengthInTexels;
+ const float textureHeightInCarSpace = mTexAssets.carTopView->height() * carSpaceUnitsPerTexel;
+ const float textureAspectRatio = (float)mTexAssets.carTopView->width() /
+ mTexAssets.carTopView->height();
+ const float pixelsBehindCarInImage = mTexAssets.carTopView->height() -
+ mConfig.carGraphicRearPixel();
+ const float textureExtentBehindCarInCarSpace = pixelsBehindCarInImage * carSpaceUnitsPerTexel;
+
+ const float btCS = mConfig.getRearLocation() - textureExtentBehindCarInCarSpace;
+ const float tpCS = textureHeightInCarSpace + btCS;
+ const float ltCS = 0.5f * textureHeightInCarSpace * textureAspectRatio;
+ const float rtCS = -ltCS;
+
+ GLfloat vertsCarPos[] = { ltCS, tpCS, 0.0f, // left top in car space
+ rtCS, tpCS, 0.0f, // right top
+ ltCS, btCS, 0.0f, // left bottom
+ rtCS, btCS, 0.0f // right bottom
+ };
+ // NOTE: We didn't flip the image in the texture, so V=0 is actually the top of the image
+ GLfloat vertsCarTex[] = { 0.0f, 0.0f, // left top
+ 1.0f, 0.0f, // right top
+ 0.0f, 1.0f, // left bottom
+ 1.0f, 1.0f // right bottom
+ };
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsCarPos);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertsCarTex);
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glUseProgram(mPgmAssets.simpleTexture);
+ GLint loc = glGetUniformLocation(mPgmAssets.simpleTexture, "cameraMat");
+ glUniformMatrix4fv(loc, 1, false, orthoMatrix.asArray());
+ glBindTexture(GL_TEXTURE_2D, mTexAssets.carTopView->glId());
+
+ printf("top view tex=%u\n", mTexAssets.carTopView->glId());
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+
+ glDisable(GL_BLEND);
+
+ glDisableVertexAttribArray(0);
+ glDisableVertexAttribArray(1);
+}
+
+
+// NOTE: Might be worth reviewing the ideas at
+// http://math.stackexchange.com/questions/1691895/inverse-of-perspective-matrix
+// to see if that simplifies the math, although we'll still want to compute the actual ground
+// interception points taking into account the pitchLimit as below.
+void RenderTopView::renderCameraOntoGroundPlane(const ActiveCamera& cam) {
+ // How far is the farthest any camera should even consider projecting it's image?
+ const float visibleSizeV = mConfig.getDisplayTopLocation() - mConfig.getDisplayBottomLocation();
+ const float visibleSizeH = visibleSizeV * sAspectRatio;
+ const float maxRange = (visibleSizeH > visibleSizeV) ? visibleSizeH : visibleSizeV;
+
+ // Construct the projection matrix (View + Projection) associated with this sensor
+ // TODO: Consider just hard coding the far plane distance as it likely doesn't matter
+ const android::mat4 V = cameraLookMatrix(cam.info);
+ const android::mat4 P = perspective(cam.info.hfov, cam.info.vfov, cam.info.position[Z], maxRange);
+ const android::mat4 projectionMatix = P*V;
+
+ // Just draw the whole darn ground plane for now -- we're wasting fill rate, but so what?
+ // A 2x optimization would be to draw only the 1/2 space of the window in the direction
+ // the sensor is facing. A more complex solution would be to construct the intersection
+ // of the sensor volume with the ground plane and render only that geometry.
+ const float top = mConfig.getDisplayTopLocation();
+ const float bottom = mConfig.getDisplayBottomLocation();
+ const float wsHeight = top - bottom;
+ const float wsWidth = wsHeight * sAspectRatio;
+ const float right = wsWidth * 0.5f;
+ const float left = -right;
+
+ const android::vec3 topLeft(left, top, 0.0f);
+ const android::vec3 topRight(right, top, 0.0f);
+ const android::vec3 botLeft(left, bottom, 0.0f);
+ const android::vec3 botRight(right, bottom, 0.0f);
+
+ GLfloat vertsPos[] = { topLeft[X], topLeft[Y], topLeft[Z],
+ topRight[X], topRight[Y], topRight[Z],
+ botLeft[X], botLeft[Y], botLeft[Z],
+ botRight[X], botRight[Y], botRight[Z],
+ };
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertsPos);
+ glEnableVertexAttribArray(0);
+
+
+ glDisable(GL_BLEND);
+
+ glUseProgram(mPgmAssets.projectedTexture);
+ GLint locCam = glGetUniformLocation(mPgmAssets.projectedTexture, "cameraMat");
+ glUniformMatrix4fv(locCam, 1, false, orthoMatrix.asArray());
+ GLint locProj = glGetUniformLocation(mPgmAssets.projectedTexture, "projectionMat");
+ glUniformMatrix4fv(locProj, 1, false, projectionMatix.asArray());
+
+ GLuint texId;
+ if (cam.tex) {
+ texId = cam.tex->glId();
+ } else {
+ texId = mTexAssets.checkerBoard->glId();
+ }
+ printf("projected tex=%u\n", texId);
+ glBindTexture(GL_TEXTURE_2D, texId);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ // glDrawArrays(GL_LINE_STRIP, 0, 4);
+
+
+ glDisableVertexAttribArray(0);
+}
diff --git a/evs/app/RenderTopView.h b/evs/app/RenderTopView.h
new file mode 100644
index 0000000..570718f
--- /dev/null
+++ b/evs/app/RenderTopView.h
@@ -0,0 +1,76 @@
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef CAR_EVS_APP_RENDERTOPVIEW_H
+#define CAR_EVS_APP_RENDERTOPVIEW_H
+
+
+#include "RenderBase.h"
+
+#include <android/hardware/automotive/evs/1.0/IEvsEnumerator.h>
+#include "ConfigManager.h"
+#include "VideoTex.h"
+#include <math/mat4.h>
+
+
+using namespace ::android::hardware::automotive::evs::V1_0;
+
+
+/*
+ * Combines the views from all available cameras into one reprojected top down view.
+ */
+class RenderTopView: public RenderBase {
+public:
+ RenderTopView(sp<IEvsEnumerator> enumerator,
+ const std::vector<ConfigManager::CameraInfo>& camList,
+ const ConfigManager& config);
+
+ virtual bool activate() override;
+ virtual void deactivate() override;
+
+ virtual bool drawFrame(const BufferDesc& tgtBuffer);
+
+protected:
+ struct ActiveCamera {
+ const ConfigManager::CameraInfo& info;
+ std::unique_ptr<VideoTex> tex;
+
+ ActiveCamera(const ConfigManager::CameraInfo& c) : info(c) {};
+ };
+
+ void renderCarTopView();
+ void renderCameraOntoGroundPlane(const ActiveCamera& cam);
+
+ sp<IEvsEnumerator> mEnumerator;
+ const ConfigManager& mConfig;
+ std::vector<ActiveCamera> mActiveCameras;
+
+ struct {
+ std::unique_ptr<TexWrapper> checkerBoard;
+ std::unique_ptr<TexWrapper> carTopView;
+ } mTexAssets;
+
+ struct {
+ GLuint simpleTexture;
+ GLuint projectedTexture;
+ } mPgmAssets;
+
+ android::mat4 orthoMatrix;
+};
+
+
+#endif //CAR_EVS_APP_RENDERTOPVIEW_H
diff --git a/evs/app/config.json b/evs/app/config.json
index 7ba447d..dedac44 100644
--- a/evs/app/config.json
+++ b/evs/app/config.json
@@ -16,18 +16,18 @@
"cameras" : [
{
"cameraId" : "/dev/video45",
- "function" : "reverse",
+ "function" : "reverse,park",
"x" : 0.0,
"y" : -40.0,
"z" : 48,
"yaw" : 180,
- "pitch" : -10,
- "hfov" : 60,
- "vfov" : 42
+ "pitch" : -30,
+ "hfov" : 90,
+ "vfov" : 60
},
{
"cameraId" : "/dev/video1",
- "function" : "front",
+ "function" : "front,park",
"x" : 0.0,
"y" : 100.0,
"z" : 48,
@@ -37,13 +37,13 @@
"vfov" : 42
},
{
- "cameraId" : "backup",
- "function" : "reverse",
- "x" : 0.0,
- "y" : 100.0,
- "z" : 48,
- "yaw" : 0,
- "pitch" : -10,
+ "cameraId" : "/dev/video0",
+ "function" : "right,park",
+ "x" : 36.0,
+ "y" : 60.0,
+ "z" : 32,
+ "yaw" : -90,
+ "pitch" : -30,
"hfov" : 60,
"vfov" : 42
}
diff --git a/evs/app/shader_testColors.h b/evs/app/shader_testColors.h
deleted file mode 100644
index 20d73be..0000000
--- a/evs/app/shader_testColors.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#ifndef SHADER_TESTCOLORS_H
-#define SHADER_TESTCOLORS_H
-
-const char vtxShader_testColors[] =
- "#version 300 es \n"
- "layout(location = 0) in vec4 pos; \n"
- "out vec2 uv; \n"
- "void main() \n"
- "{ \n"
- " gl_Position = pos; \n"
- " // using the screen space position as the UV coordinates\n"
- " uv = pos.xy * 0.5f + 0.5f; \n"
- "} \n";
-
-const char pixShader_testColors[] =
- "#version 300 es \n"
- "precision mediump float; \n"
- "uniform sampler2D tex; \n"
- "in vec2 uv; \n"
- "out vec4 color; \n"
- "void main() \n"
- "{ \n"
- " // R, G, B, A \n"
- " color = vec4(0.5, 1.0, 0.5, 1.0); \n"
- " color.r = uv.x; \n"
- " color.b = uv.y; \n"
- "} \n";
-
-#endif // SHADER_TESTCOLORS_H
\ No newline at end of file
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index 83d023d..263c83a 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -139,6 +139,7 @@
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
+ <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index c24e58e..fd4a841 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -77,4 +77,8 @@
<!-- The com.android.car.VmsPublisherService will bind to this list of clients -->
<string-array translatable="false" name="vmsPublisherClients">
</string-array>
+ <!-- Permissions that the com.android.car.VmsPublisherService is allowed to grant to publishers -->
+ <string-array translatable="false" name="vmsSafePermissions">
+ <item>"android.permission.ACCESS_FINE_LOCATION"</item>
+ </string-array>
</resources>
diff --git a/service/src/com/android/car/VmsPublisherService.java b/service/src/com/android/car/VmsPublisherService.java
index 8bb0167..683c1c8 100644
--- a/service/src/com/android/car/VmsPublisherService.java
+++ b/service/src/com/android/car/VmsPublisherService.java
@@ -27,9 +27,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import com.android.car.hal.VmsHalService;
@@ -37,7 +40,9 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -56,6 +61,7 @@
private final Context mContext;
private final VmsHalService mHal;
private final VmsPublisherManager mPublisherManager;
+ private Set<String> mSafePermissions;
public VmsPublisherService(Context context, VmsHalService hal) {
mContext = context;
@@ -67,6 +73,9 @@
@Override
public void init() {
mHal.addPublisherListener(this);
+ // Load permissions that can be granted to publishers.
+ mSafePermissions = new HashSet<>(
+ Arrays.asList(mContext.getResources().getStringArray(R.array.vmsSafePermissions)));
// Launch publishers.
String[] publisherNames = mContext.getResources().getStringArray(
R.array.vmsPublisherClients);
@@ -187,11 +196,12 @@
// Already registered, nothing to do.
return;
}
+ grantPermissions(name);
Intent intent = new Intent();
intent.setComponent(name);
PublisherConnection connection = new PublisherConnection();
- if (publisherService.mContext.bindService(intent, connection,
- Context.BIND_AUTO_CREATE)) {
+ if (publisherService.mContext.bindServiceAsUser(intent, connection,
+ Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
mPublisherConnectionMap.put(publisherName, connection);
} else {
Log.e(TAG, "unable to bind to: " + publisherName);
@@ -244,6 +254,39 @@
mPublisherMap.clear();
}
+ private void grantPermissions(ComponentName component) {
+ VmsPublisherService publisherService = mPublisherService.get();
+ if (publisherService == null) return;
+ final PackageManager packageManager = publisherService.mContext.getPackageManager();
+ final String packageName = component.getPackageName();
+ PackageInfo packageInfo;
+ try {
+ packageInfo = packageManager.getPackageInfo(packageName,
+ PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Error getting package info for " + packageName, e);
+ return;
+ }
+ if (packageInfo.requestedPermissions == null) return;
+ for (String permission : packageInfo.requestedPermissions) {
+ if (!publisherService.mSafePermissions.contains(permission)) {
+ continue;
+ }
+ if (packageManager.checkPermission(permission, packageName)
+ == PackageManager.PERMISSION_GRANTED) {
+ continue;
+ }
+ try {
+ packageManager.grantRuntimePermission(packageName, permission,
+ UserHandle.SYSTEM);
+ Log.d(TAG, "Permission " + permission + " granted to " + packageName);
+ } catch (SecurityException | IllegalArgumentException e) {
+ Log.e(TAG, "Error while trying to grant " + permission + " to " + packageName,
+ e);
+ }
+ }
+ }
+
class PublisherConnection implements ServiceConnection {
private final IBinder mToken = new Binder();
diff --git a/tests/EmbeddedKitchenSinkApp/Android.mk b/tests/EmbeddedKitchenSinkApp/Android.mk
index 57a0620..d661902 100644
--- a/tests/EmbeddedKitchenSinkApp/Android.mk
+++ b/tests/EmbeddedKitchenSinkApp/Android.mk
@@ -14,6 +14,9 @@
#
#
+#disble build in PDK, missing ui-lib breaks build
+ifneq ($(TARGET_BUILD_PDK),true)
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
@@ -47,3 +50,5 @@
include packages/services/Car/car-support-lib/car-support.mk
include $(BUILD_PACKAGE)
+
+endif #TARGET_BUILD_PDK
diff --git a/tests/EmbeddedKitchenSinkApp/res/xml/automotive_app_desc.xml b/tests/EmbeddedKitchenSinkApp/res/xml/automotive_app_desc.xml
index 9bb6ae7..20d5d81 100644
--- a/tests/EmbeddedKitchenSinkApp/res/xml/automotive_app_desc.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/xml/automotive_app_desc.xml
@@ -16,5 +16,5 @@
<automotiveApp>
<uses name="service" />
<uses name="projection" />
- <uses name="activity" class="com.google.android.car.kitchensink.KitchenSinkProxyActivity" />
+ <uses name="activity" class="com.google.android.car.kitchensink.KitchenSinkActivity" />
</automotiveApp>
diff --git a/tests/VmsPublisherClientSample/Android.mk b/tests/VmsPublisherClientSample/Android.mk
index 6bb5bf7..2aa6c40 100644
--- a/tests/VmsPublisherClientSample/Android.mk
+++ b/tests/VmsPublisherClientSample/Android.mk
@@ -28,7 +28,7 @@
LOCAL_PRIVILEGED_MODULE := true
-LOCAL_CERTIFICATE := platform
+LOCAL_CERTIFICATE := testkey
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/tests/VmsPublisherClientSample/AndroidManifest.xml b/tests/VmsPublisherClientSample/AndroidManifest.xml
index d3ac195..fdc1a31 100644
--- a/tests/VmsPublisherClientSample/AndroidManifest.xml
+++ b/tests/VmsPublisherClientSample/AndroidManifest.xml
@@ -15,8 +15,11 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.android.car.vms.publisher"
- android:sharedUserId="android.uid.system">
+ package="com.google.android.car.vms.publisher">
+
+ <uses-permission android:name="android.car.permission.VMS_PUBLISHER" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.CAMERA"/>
<uses-sdk android:minSdkVersion="25" android:targetSdkVersion='25'/>
@@ -24,7 +27,8 @@
android:icon="@mipmap/ic_launcher"
android:directBootAware="true">
<service android:name=".VmsPublisherClientSampleService"
- android:exported="false">
+ android:exported="true"
+ android:singleUser="true">
</service>
</application>
</manifest>
diff --git a/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java b/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
index c310464..08d37cd 100644
--- a/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
+++ b/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
@@ -22,7 +22,6 @@
import android.os.Handler;
import android.os.Message;
-import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -31,7 +30,7 @@
*/
public class VmsPublisherClientSampleService extends VmsPublisherClientService {
public static final int PUBLISH_EVENT = 0;
- public static final VmsLayer TEST_LAYER = new VmsLayer(0,0);
+ public static final VmsLayer TEST_LAYER = new VmsLayer(0, 0);
private byte mCounter = 0;
private AtomicBoolean mInitialized = new AtomicBoolean(false);
@@ -39,7 +38,7 @@
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- if (msg.what == PUBLISH_EVENT) {
+ if (msg.what == PUBLISH_EVENT && mInitialized.get()) {
periodicPublish();
}
}
@@ -51,6 +50,8 @@
*/
@Override
public void onVmsPublisherServiceReady() {
+ VmsSubscriptionState subscriptionState = getSubscriptions();
+ onVmsSubscriptionChange(subscriptionState);
}
@Override
@@ -64,6 +65,13 @@
}
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mInitialized.set(false);
+ mHandler.removeMessages(PUBLISH_EVENT);
+ }
+
private void periodicPublish() {
publish(TEST_LAYER, new byte[]{mCounter});
++mCounter;
diff --git a/tests/VmsSubscriberClientSample/res/xml/automotive_app_desc.xml b/tests/VmsSubscriberClientSample/res/xml/automotive_app_desc.xml
index 0d0a3fb..b10ddd0 100644
--- a/tests/VmsSubscriberClientSample/res/xml/automotive_app_desc.xml
+++ b/tests/VmsSubscriberClientSample/res/xml/automotive_app_desc.xml
@@ -16,5 +16,5 @@
<automotiveApp>
<uses name="service" />
<uses name="projection" />
- <uses name="activity" class="com.google.android.car.vms.VmsSubscriberClientSampleActivity" />
+ <uses name="activity" class="com.google.android.car.vms.subscriber.VmsSubscriberClientSampleActivity" />
</automotiveApp>
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java
new file mode 100644
index 0000000..739f5d0
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package com.android.car.test;
+
+import android.annotation.ArrayRes;
+import android.car.VehicleAreaType;
+import android.car.annotation.FutureFeature;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
+import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_1.VmsBaseMessageIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
+
+import com.android.car.R;
+import com.android.car.vehiclehal.VehiclePropValueBuilder;
+import com.android.car.vehiclehal.test.MockedVehicleHal;
+import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
+
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@FutureFeature
+public class VmsPublisherPermissionsTest extends MockedCarTestBase {
+ private static final String TAG = "VmsPublisherTest";
+ private static final int MOCK_PUBLISHER_LAYER_ID = 0;
+ private static final int MOCK_PUBLISHER_LAYER_VERSION = 0;
+
+ private HalHandler mHalHandler;
+ // Used to block until the HAL property is updated in HalHandler.onPropertySet.
+ private Semaphore mHalHandlerSemaphore;
+
+ @Override
+ protected synchronized void configureMockedHal() {
+ mHalHandler = new HalHandler();
+ addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
+ .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
+ .setAccess(VehiclePropertyAccess.READ_WRITE)
+ .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
+ }
+
+ /**
+ * Creates a context with the resource vmsPublisherClients overridden. The overridden value
+ * contains the name of the test service defined also in this test package.
+ */
+ @Override
+ protected Context getCarServiceContext() throws PackageManager.NameNotFoundException {
+ Context context = getContext()
+ .createPackageContext("com.android.car", Context.CONTEXT_IGNORE_SECURITY);
+ Resources resources = new Resources(context.getAssets(),
+ context.getResources().getDisplayMetrics(),
+ context.getResources().getConfiguration()) {
+ @Override
+ public String[] getStringArray(@ArrayRes int id) throws NotFoundException {
+ if (id == R.array.vmsPublisherClients) {
+ return new String[]{
+ "com.google.android.car.vms.publisher/"
+ + ".VmsPublisherClientSampleService"};
+ } else if (id == R.array.vmsSafePermissions) {
+ return new String[]{"android.permission.ACCESS_FINE_LOCATION"};
+ }
+ return super.getStringArray(id);
+ }
+ };
+ ContextWrapper wrapper = new ContextWrapper(context) {
+ @Override
+ public Resources getResources() {
+ return resources;
+ }
+ };
+ return wrapper;
+ }
+
+ private VehiclePropValue getHalSubscriptionRequest() {
+ return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .addIntValue(VmsMessageType.SUBSCRIBE)
+ .addIntValue(MOCK_PUBLISHER_LAYER_ID)
+ .addIntValue(MOCK_PUBLISHER_LAYER_VERSION)
+ .build();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ /**
+ * First init the semaphore, setUp will start a series of events that will ultimately
+ * update the HAL layer and release this semaphore.
+ */
+ mHalHandlerSemaphore = new Semaphore(0);
+ super.setUp();
+
+ // Inject a subscribe event which simulates the HAL is subscribed to the Sample Publisher.
+ MockedVehicleHal mHal = getMockedVehicleHal();
+ mHal.injectEvent(getHalSubscriptionRequest());
+ }
+
+ @Override
+ protected synchronized void tearDown() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ super.tearDown();
+ }
+
+ /**
+ * The method setUp initializes all the Car services, including the VmsPublisherService.
+ * The VmsPublisherService will start and configure its list of clients. This list was
+ * overridden in the method getCarServiceContext.
+ * Therefore, only VmsPublisherClientSampleService will be started.
+ * The service VmsPublisherClientSampleService will publish one message, which is validated in
+ * this test.
+ */
+ public void testPermissions() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ // At this point the client initialization finished. Let's validate the permissions.
+ // The VMS service is only allowed to grant ACCESS_FINE_LOCATION but not CAMERA.
+ assertTrue(
+ getContext().getPackageManager().checkPermission(
+ "android.permission.ACCESS_FINE_LOCATION",
+ "com.google.android.car.vms.publisher")
+ == PackageManager.PERMISSION_GRANTED);
+ assertFalse(getContext().getPackageManager().checkPermission(
+ "android.permission.CAMERA", "com.google.android.car.vms.publisher")
+ == PackageManager.PERMISSION_GRANTED);
+ }
+
+ private class HalHandler implements VehicleHalPropertyHandler {
+ @Override
+ public synchronized void onPropertySet(VehiclePropValue value) {
+ // If this is the data message release the semaphore so the test can continue.
+ ArrayList<Integer> int32Values = value.value.int32Values;
+ if (int32Values.get(VmsBaseMessageIntegerValuesIndex.VMS_MESSAGE_TYPE) ==
+ VmsMessageType.DATA) {
+ mHalHandlerSemaphore.release();
+ }
+ }
+ }
+}
diff --git a/tools/bootanalyze/bootanalyze.py b/tools/bootanalyze/bootanalyze.py
index e50627a..4b814c7 100755
--- a/tools/bootanalyze/bootanalyze.py
+++ b/tools/bootanalyze/bootanalyze.py
@@ -48,6 +48,7 @@
ADB_CMD = "adb"
TIMING_THRESHOLD = 5.0
BOOT_PROP = "\[ro\.boottime\.([^\]]+)\]:\s+\[(\d+)\]"
+BOOTLOADER_TIME_PROP = "\[ro\.boot\.boottime\]:\s+\[([^\]]+)\]"
max_wait_time = BOOT_TIME_TOO_BIG
@@ -278,7 +279,7 @@
if not logcat_event_time.get(KERNEL_TIME_KEY):
print "kernel time not captured in logcat, cannot get time diff"
- return None, None, None
+ return None, None, None, None
diffs = []
diffs.append((logcat_event_time[KERNEL_TIME_KEY], logcat_event_time[KERNEL_TIME_KEY]))
if logcat_event_time.get(BOOT_ANIM_END_TIME_KEY) and dmesg_event_time.get(BOOT_ANIM_END_TIME_KEY):
@@ -288,7 +289,7 @@
if not dmesg_event_time.get(KERNEL_BOOT_COMPLETE):
print "BootAnimEnd time or BootComplete-kernel not captured in both log" +\
", cannot get time diff"
- return None, None, None
+ return None, None, None, None
diffs.append((logcat_event_time[KERNEL_BOOT_COMPLETE],\
logcat_event_time[KERNEL_BOOT_COMPLETE] - dmesg_event_time[KERNEL_BOOT_COMPLETE]))
@@ -316,7 +317,7 @@
else:
events[k] = 0.0
- data_points = {}
+ data_points = collections.OrderedDict()
print "-----------------"
print "ro.boottime.*: time"
@@ -337,9 +338,24 @@
'from_dmesg': item[0] in replaced_from_dmesg,
'logcat_value': logcat_original_time[item[0]]
}
+ # add times with bootloader
+ if events.get("BootComplete") and boottime_events.get("bootloader"):
+ total = events["BootComplete"] + boottime_events["bootloader"]
+ data_points["*BootComplete+Bootloader"] = {
+ 'value': total,
+ 'from_dmesg': False,
+ 'logcat_value': 0.0
+ }
+ if events.get("LauncherStart") and boottime_events.get("bootloader"):
+ total = events["LauncherStart"] + boottime_events["bootloader"]
+ data_points["*LauncherStart+Bootloader"] = {
+ 'value': total,
+ 'from_dmesg': False,
+ 'logcat_value': 0.0
+ }
+ for k, v in data_points.iteritems():
print '{0:30}: {1:<7.5} {2:1} ({3})'.format(
- item[0], item[1], '*' if item[0] in replaced_from_dmesg else '',
- logcat_original_time[item[0]])
+ k, v['value'], '*' if v['from_dmesg'] else '', v['logcat_value'])
print '\n* - event time was obtained from dmesg log\n'
@@ -520,6 +536,8 @@
stdout=subprocess.PIPE);
out = process.stdout
pattern = re.compile(BOOT_PROP)
+ pattern_bootloader = re.compile(BOOTLOADER_TIME_PROP)
+ bootloader_time = 0.0
for line in out:
match = pattern.match(line)
if match:
@@ -527,7 +545,18 @@
events[match.group(1)] = float(match.group(2)) / 1000.0 #ms to s
else:
events[match.group(1)] = float(match.group(2)) / 1000000000.0 #ns to s
+ match = pattern_bootloader.match(line)
+ if match:
+ items = match.group(1).split(",")
+ for item in items:
+ entry_pair = item.split(":")
+ entry_name = entry_pair[0]
+ time_spent = float(entry_pair[1]) / 1000 #ms to s
+ if entry_name != "SW":
+ bootloader_time = bootloader_time + time_spent
ordered_event = collections.OrderedDict()
+ if bootloader_time != 0.0:
+ ordered_event["bootloader"] = bootloader_time;
for item in sorted(events.items(), key=operator.itemgetter(1)):
ordered_event[item[0]] = item[1]
return ordered_event
diff --git a/tools/bootanalyze/config.yaml b/tools/bootanalyze/config.yaml
index 46b2d21..923ca5d 100644
--- a/tools/bootanalyze/config.yaml
+++ b/tools/bootanalyze/config.yaml
@@ -50,6 +50,6 @@
KeyguardShown: KeyguardServiceDelegate.*\*\*\*\* SHOWN CALLED \*\*\*\*
BootComplete: Starting phase 1000
BootComplete_kernel: processing action \(sys\.boot_completed=1\)
- LauncherStart: START.*HOME.*(NexusLauncherActivity|GEL|LensPickerTrampolineActivity)
+ LauncherStart: START.*HOME.*(NexusLauncherActivity|GEL|LensPickerTrampolineActivity|SetupWizardActivity)
FsStat: fs_stat, partition:userdata stat:(0x\S+)