Camera: new buffer management HIDL APIs

No actual implementations yet.

Test: compile, new VTS to be written
Bug: 109829698
Change-Id: Ibe509dd743a84b147fdfed6599d8f066adb8793b
This commit is contained in:
Yin-Chia Yeh 2018-09-06 15:38:34 -07:00
parent 9ee5d7103c
commit 6a6fe0f47b
16 changed files with 543 additions and 7 deletions

View file

@ -148,7 +148,9 @@ interface ICameraDevice {
* session handle for active operations.
*
* @param callback Interface to invoke by the HAL for device asynchronous
* events.
* events. For HALs newer than version 3.2, HAL must use castFrom
* method to check the exact version of callback sent by camera service.
*
* @return status Status code for the operation, one of:
* OK:
* On a successful open of the camera device.

View file

@ -149,9 +149,8 @@ interface ICameraDeviceSession {
* - Including too many output streams of a certain format.
* - Unsupported rotation configuration
* - Stream sizes/formats don't satisfy the
* camera3_stream_configuration_t->operation_mode requirements
* for non-NORMAL mode, or the requested operation_mode is not
* supported by the HAL.
* StreamConfigurationMode requirements for non-NORMAL mode, or
* the requested operation_mode is not supported by the HAL.
* - Unsupported usage flag
* The camera service cannot filter out all possible illegal stream
* configurations, since some devices may support more simultaneous

View file

@ -54,7 +54,7 @@ interface ICameraDeviceSession extends @3.3::ICameraDeviceSession {
* - Including too many output streams of a certain format.
* - Unsupported rotation configuration
* - Stream sizes/formats don't satisfy the
* camera3_stream_configuration_t->operation_mode requirements
* StreamConfigurationMode requirements
* for non-NORMAL mode, or the requested operation_mode is not
* supported by the HAL.
* - Unsupported usage flag

View file

@ -7,7 +7,9 @@ hidl_interface {
enabled: true,
},
srcs: [
"types.hal",
"ICameraDevice.hal",
"ICameraDeviceCallback.hal",
"ICameraDeviceSession.hal",
],
interfaces: [
@ -18,6 +20,14 @@ hidl_interface {
"android.hardware.graphics.common@1.0",
"android.hidl.base@1.0",
],
types: [
"BufferRequest",
"BufferRequestStatus",
"StreamBufferRequestError",
"StreamBufferRet",
"StreamBuffersVal",
"StreamConfiguration",
],
gen_java: false,
}

View file

@ -0,0 +1,70 @@
/*
* Copyright (C) 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.
*/
package android.hardware.camera.device@3.5;
import @3.2::StreamBuffer;
import @3.4::ICameraDeviceCallback;
/**
* Callback methods for the HAL to call into the framework.
*/
interface ICameraDeviceCallback extends @3.4::ICameraDeviceCallback {
/**
* requestStreamBuffers:
*
* Synchronous callback for HAL to ask for output buffers from camera service.
*
* This call may be serialized in camera service so it is strongly
* recommended to only call this method from one thread.
*
* When camera device advertises
* (CameraMetadataEnumAndroidInfoSupportedBufferManagementVersion ==
* ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5), HAL
* can use this method to request buffers from camera service.
*
* @return status Status code for the operation, one of:
* OK: all requested buffers are returned
* FAILED_PARTIAL: some streams failed while some succeeds. Check
* individual StreamBufferRet for details.
* FAILED_CONFIGURING: the request failed because camera servicve is
* performing configureStreams and no buffers are returned.
* FAILED_UNKNOWN: the request failed for unknown reason and no buffers
* are returned.
*
* Performance requirements:
* This is a blocking call that takes more time with more buffers requested.
* HAL must not request large amount of buffers on a latency critical code
* path. It is highly recommended to use a dedicated thread to perform
* all requestStreamBuffers calls, and adjust the thread priority and/or
* timing of making the call in order for buffers to arrive before HAL is
* ready to fill the buffer.
*/
requestStreamBuffers(vec<BufferRequest> bufReqs)
generates (BufferRequestStatus st, vec<StreamBufferRet> buffers);
/**
* returnStreamBuffers:
*
* Synchronous callback for HAL to return output buffers to camera service.
*
* If this method is called during a configureStreams call, it must be blocked
* until camera service finishes the ongoing configureStreams call.
*/
returnStreamBuffers(vec<StreamBuffer> buffers);
};

View file

@ -18,6 +18,7 @@ package android.hardware.camera.device@3.5;
import android.hardware.camera.common@1.0::Status;
import @3.4::ICameraDeviceSession;
import @3.4::HalStreamConfiguration;
/**
* Camera device active session interface.
@ -26,4 +27,76 @@ import @3.4::ICameraDeviceSession;
* configure and request captures from an active camera device.
*/
interface ICameraDeviceSession extends @3.4::ICameraDeviceSession {
/**
* configureStreams_3_5:
*
* Identical to @3.4::ICameraDeviceSession.configureStreams, except that:
*
* - a streamConfigCounter counter is provided to check for race condition
* between configureStreams_3_5 and signalStreamFlush call.
*
* @return status Status code for the operation, one of:
* OK:
* On successful stream configuration.
* INTERNAL_ERROR:
* If there has been a fatal error and the device is no longer
* operational. Only close() can be called successfully by the
* framework after this error is returned.
* ILLEGAL_ARGUMENT:
* If the requested stream configuration is invalid. Some examples
* of invalid stream configurations include:
* - Including more than 1 INPUT stream
* - Not including any OUTPUT streams
* - Including streams with unsupported formats, or an unsupported
* size for that format.
* - Including too many output streams of a certain format.
* - Unsupported rotation configuration
* - Stream sizes/formats don't satisfy the
* StreamConfigurationMode requirements
* for non-NORMAL mode, or the requested operation_mode is not
* supported by the HAL.
* - Unsupported usage flag
* The camera service cannot filter out all possible illegal stream
* configurations, since some devices may support more simultaneous
* streams or larger stream resolutions than the minimum required
* for a given camera device hardware level. The HAL must return an
* ILLEGAL_ARGUMENT for any unsupported stream set, and then be
* ready to accept a future valid stream configuration in a later
* configureStreams call.
* @return halConfiguration The stream parameters desired by the HAL for
* each stream, including maximum buffers, the usage flags, and the
* override format.
*/
configureStreams_3_5(@3.5::StreamConfiguration requestedConfiguration)
generates (Status status,
@3.4::HalStreamConfiguration halConfiguration);
/**
* signalStreamFlush:
*
* Signaling HAL camera service is about to perform configureStreams_3_5 and
* HAL must return all buffers of designated streams. HAL must finish
* inflight requests normally and return all buffers that belongs to the
* designated streams through processCaptureResult or returnStreamBuffer
* API in a timely manner, or camera service will run into a fatal error.
*
* Note that this call serves as an optional hint and camera service may
* skip sending this call if all buffers are already returned.
*
* @param streamIds The ID of streams camera service need all of its
* buffers returned.
*
* @param streamConfigCounter Note that due to concurrency nature, it is
* possible the signalStreamFlush call arrives later than the
* corresponding configureStreams_3_5 call, HAL must check
* streamConfigCounter for such race condition. If the counter is less
* than the counter in the last configureStreams_3_5 call HAL last
* received, the call is stale and HAL should just return this call.
*/
oneway signalStreamFlush(
vec<int32_t> streamIds,
uint32_t streamConfigCounter
);
};

View file

@ -27,6 +27,7 @@ cc_library_shared {
vendor: true,
srcs: [
"CameraDevice.cpp",
"CameraDeviceSession.cpp",
],
shared_libs: [
"libhidlbase",

View file

@ -39,6 +39,22 @@ CameraDevice::CameraDevice(sp<CameraModule> module, const std::string& cameraId,
CameraDevice::~CameraDevice() {
}
sp<V3_2::implementation::CameraDeviceSession> CameraDevice::createSession(camera3_device_t* device,
const camera_metadata_t* deviceInfo,
const sp<V3_2::ICameraDeviceCallback>& callback) {
sp<CameraDeviceSession> session = new CameraDeviceSession(device, deviceInfo, callback);
IF_ALOGV() {
session->getInterface()->interfaceChain([](
::android::hardware::hidl_vec<::android::hardware::hidl_string> interfaceChain) {
ALOGV("Session interface chain:");
for (auto iface : interfaceChain) {
ALOGV(" %s", iface.c_str());
}
});
}
return session;
}
Return<void> CameraDevice::getPhysicalCameraCharacteristics(const hidl_string& physicalCameraId,
V3_5::ICameraDevice::getPhysicalCameraCharacteristics_cb _hidl_cb) {
Status status = initStatus();

View file

@ -0,0 +1,68 @@
/*
* Copyright (C) 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 "CamDevSession@3.5-impl"
#include <android/log.h>
#include <utils/Trace.h>
#include "CameraDeviceSession.h"
namespace android {
namespace hardware {
namespace camera {
namespace device {
namespace V3_5 {
namespace implementation {
CameraDeviceSession::CameraDeviceSession(
camera3_device_t* device,
const camera_metadata_t* deviceInfo,
const sp<V3_2::ICameraDeviceCallback>& callback) :
V3_4::implementation::CameraDeviceSession(device, deviceInfo, callback) {
mHasCallback_3_5 = false;
auto castResult = ICameraDeviceCallback::castFrom(callback);
if (castResult.isOk()) {
sp<ICameraDeviceCallback> callback3_5 = castResult;
if (callback3_5 != nullptr) {
mHasCallback_3_5 = true;
}
}
}
CameraDeviceSession::~CameraDeviceSession() {
}
Return<void> CameraDeviceSession::configureStreams_3_5(
const StreamConfiguration& /*requestedConfiguration*/,
ICameraDeviceSession::configureStreams_3_5_cb _hidl_cb) {
HalStreamConfiguration outStreams;
_hidl_cb(Status::OPERATION_NOT_SUPPORTED, outStreams);
return Void();
}
Return<void> CameraDeviceSession::signalStreamFlush(
const hidl_vec<int32_t>& /*requests*/, uint32_t /*streamConfigCounter*/) {
return Void();
}
} // namespace implementation
} // namespace V3_5
} // namespace device
} // namespace camera
} // namespace hardware
} // namespace android

View file

@ -0,0 +1,157 @@
/*
* Copyright (C) 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.
*/
#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_CAMERADEVICE3SESSION_H
#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_CAMERADEVICE3SESSION_H
#include <android/hardware/camera/device/3.5/ICameraDevice.h>
#include <android/hardware/camera/device/3.5/ICameraDeviceSession.h>
#include <android/hardware/camera/device/3.5/ICameraDeviceCallback.h>
#include <../../3.4/default/include/device_v3_4_impl/CameraDeviceSession.h>
namespace android {
namespace hardware {
namespace camera {
namespace device {
namespace V3_5 {
namespace implementation {
using namespace ::android::hardware::camera::device;
using ::android::hardware::camera::device::V3_2::CaptureRequest;
using ::android::hardware::camera::device::V3_5::StreamConfiguration;
using ::android::hardware::camera::device::V3_4::HalStreamConfiguration;
using ::android::hardware::camera::device::V3_5::ICameraDeviceSession;
using ::android::hardware::camera::device::V3_5::ICameraDeviceCallback;
using ::android::hardware::camera::common::V1_0::Status;
using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::hidl_vec;
using ::android::hardware::hidl_string;
using ::android::sp;
using ::android::Mutex;
struct CameraDeviceSession : public V3_4::implementation::CameraDeviceSession {
CameraDeviceSession(camera3_device_t*,
const camera_metadata_t* deviceInfo,
const sp<V3_2::ICameraDeviceCallback>&);
virtual ~CameraDeviceSession();
virtual sp<V3_2::ICameraDeviceSession> getInterface() override {
return new TrampolineSessionInterface_3_5(this);
}
protected:
// Methods from v3.4 and earlier will trampoline to inherited implementation
Return<void> configureStreams_3_5(
const StreamConfiguration& requestedConfiguration,
ICameraDeviceSession::configureStreams_3_5_cb _hidl_cb);
Return<void> signalStreamFlush(
const hidl_vec<int32_t>& requests,
uint32_t streamConfigCounter);
// Whether this camera device session is created with version 3.5 callback.
bool mHasCallback_3_5;
private:
struct TrampolineSessionInterface_3_5 : public ICameraDeviceSession {
TrampolineSessionInterface_3_5(sp<CameraDeviceSession> parent) :
mParent(parent) {}
virtual Return<void> constructDefaultRequestSettings(
V3_2::RequestTemplate type,
V3_3::ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb) override {
return mParent->constructDefaultRequestSettings(type, _hidl_cb);
}
virtual Return<void> configureStreams(
const V3_2::StreamConfiguration& requestedConfiguration,
V3_3::ICameraDeviceSession::configureStreams_cb _hidl_cb) override {
return mParent->configureStreams(requestedConfiguration, _hidl_cb);
}
virtual Return<void> processCaptureRequest_3_4(
const hidl_vec<V3_4::CaptureRequest>& requests,
const hidl_vec<V3_2::BufferCache>& cachesToRemove,
ICameraDeviceSession::processCaptureRequest_3_4_cb _hidl_cb) override {
return mParent->processCaptureRequest_3_4(requests, cachesToRemove, _hidl_cb);
}
virtual Return<void> processCaptureRequest(const hidl_vec<V3_2::CaptureRequest>& requests,
const hidl_vec<V3_2::BufferCache>& cachesToRemove,
V3_3::ICameraDeviceSession::processCaptureRequest_cb _hidl_cb) override {
return mParent->processCaptureRequest(requests, cachesToRemove, _hidl_cb);
}
virtual Return<void> getCaptureRequestMetadataQueue(
V3_3::ICameraDeviceSession::getCaptureRequestMetadataQueue_cb _hidl_cb) override {
return mParent->getCaptureRequestMetadataQueue(_hidl_cb);
}
virtual Return<void> getCaptureResultMetadataQueue(
V3_3::ICameraDeviceSession::getCaptureResultMetadataQueue_cb _hidl_cb) override {
return mParent->getCaptureResultMetadataQueue(_hidl_cb);
}
virtual Return<Status> flush() override {
return mParent->flush();
}
virtual Return<void> close() override {
return mParent->close();
}
virtual Return<void> configureStreams_3_3(
const V3_2::StreamConfiguration& requestedConfiguration,
configureStreams_3_3_cb _hidl_cb) override {
return mParent->configureStreams_3_3(requestedConfiguration, _hidl_cb);
}
virtual Return<void> configureStreams_3_4(
const V3_4::StreamConfiguration& requestedConfiguration,
configureStreams_3_4_cb _hidl_cb) override {
return mParent->configureStreams_3_4(requestedConfiguration, _hidl_cb);
}
virtual Return<void> configureStreams_3_5(
const StreamConfiguration& requestedConfiguration,
configureStreams_3_5_cb _hidl_cb) override {
return mParent->configureStreams_3_5(requestedConfiguration, _hidl_cb);
}
virtual Return<void> signalStreamFlush(
const hidl_vec<int32_t>& requests,
uint32_t streamConfigCounter) override {
return mParent->signalStreamFlush(requests, streamConfigCounter);
}
private:
sp<CameraDeviceSession> mParent;
};
};
} // namespace implementation
} // namespace V3_5
} // namespace device
} // namespace camera
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_CAMERADEVICE3SESSION_H

View file

@ -17,7 +17,7 @@
#ifndef ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_CAMERADEVICE_H
#define ANDROID_HARDWARE_CAMERA_DEVICE_V3_5_CAMERADEVICE_H
#include "CameraModule.h"
#include "CameraDeviceSession.h"
#include <../../../../3.4/default/include/device_v3_4_impl/CameraDevice_3_4.h>
#include <android/hardware/camera/device/3.5/ICameraDevice.h>
@ -57,6 +57,10 @@ struct CameraDevice : public V3_4::implementation::CameraDevice {
}
protected:
virtual sp<V3_2::implementation::CameraDeviceSession> createSession(camera3_device_t*,
const camera_metadata_t* deviceInfo,
const sp<V3_2::ICameraDeviceCallback>&) override;
Return<void> getPhysicalCameraCharacteristics(const hidl_string& physicalCameraId,
V3_5::ICameraDevice::getPhysicalCameraCharacteristics_cb _hidl_cb);

131
camera/device/3.5/types.hal Normal file
View file

@ -0,0 +1,131 @@
/*
* Copyright (C) 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.
*/
package android.hardware.camera.device@3.5;
import @3.2::StreamBuffer;
import @3.4::StreamConfiguration;
/**
* StreamConfiguration:
*
* Identical to @3.4::StreamConfiguration, except that it contains streamConfigCounter
*/
struct StreamConfiguration {
@3.4::StreamConfiguration v3_4;
/**
* An incrementing counter used for HAL to keep track of the stream
* configuration and the paired oneway signalStreamFlush call. When the
* counter in signalStreamFlush call is less than the counter here, that
* signalStreamFlush call is stale.
*/
uint32_t streamConfigCounter;
};
enum StreamBufferRequestError : uint32_t {
/**
* Get buffer failed due to timeout waiting for an available buffer. This is
* likely due to the client application holding too many buffers, or the
* system is under memory pressure.
* This is not a fatal error. HAL may try to request buffer for this stream
* later. If HAL cannot get a buffer for certain capture request in time
* due to this error, HAL can send an ERROR_REQUEST to camera service and
* drop processing that request.
*/
NO_BUFFER_AVAILABLE = 1,
/**
* Get buffer failed due to HAL has reached its maxBuffer count. This is not
* a fatal error. HAL may try to request buffer for this stream again after
* it returns at least one buffer of that stream to camera service.
*/
MAX_BUFFER_EXCEEDED = 2,
/**
* Get buffer failed due to the stream is disconnected by client
* application, has been removed, or not recognized by camera service.
* This means application is no longer interested in this stream.
* Requesting buffer for this stream must never succeed after this error is
* returned. HAL must safely return all buffers of this stream after
* getting this error. If HAL gets another capture request later targeting
* a disconnected stream, HAL must send an ERROR_REQUEST to camera service
* and drop processing that request.
*/
STREAM_DISCONNECTED = 3,
/**
* Get buffer failed for unknown reasons. This is a fatal error and HAL must
* send ERROR_DEVICE to camera service and be ready to be closed.
*/
UNKNOWN_ERROR = 4
};
/**
* Per-stream return value for requestStreamBuffers.
* For each stream, either an StreamBufferRequestError error code, or all
* requested buffers for this stream is returned, so buffers.size() must be
* equal to BufferRequest::numBuffersRequested of corresponding stream.
*/
safe_union StreamBuffersVal {
StreamBufferRequestError error;
vec<@3.2::StreamBuffer> buffers;
};
struct StreamBufferRet {
int32_t streamId;
StreamBuffersVal val;
};
enum BufferRequestStatus : uint32_t {
/**
* Method call succeeded and all requested buffers are returned.
*/
OK = 0,
/**
* Method call failed for some streams. Check per stream status for each
* returned StreamBufferRet.
*/
FAILED_PARTIAL = 1,
/**
* Method call failed for all streams and no buffers are returned at all.
* Camera service is about to or is performing configureStreams. HAL must
* wait until next configureStreams call is finished before requesting
* buffers again.
*/
FAILED_CONFIGURING = 2,
/**
* Method call failed for all streams and no buffers are returned at all.
* Failure due to bad BufferRequest input, eg: unknown streamId or repeated
* streamId.
*/
FAILED_ILLEGAL_ARGUMENTS = 3,
/**
* Method call failed for all streams and no buffers are returned at all.
* Failure due to unknown reason.
*/
FAILED_UNKNOWN = 4,
};
struct BufferRequest {
int32_t streamId;
uint32_t numBuffersRequested;
};

View file

@ -14,6 +14,7 @@ hidl_interface {
"android.hardware.camera.metadata@3.3",
],
types: [
"CameraMetadataEnumAndroidInfoSupportedBufferManagementVersion",
"CameraMetadataTag",
],
gen_java: true,

View file

@ -14,7 +14,6 @@ hidl_interface {
"android.hardware.camera.common@1.0",
"android.hardware.camera.device@1.0",
"android.hardware.camera.device@3.2",
"android.hardware.camera.device@3.5",
"android.hidl.base@1.0",
],
gen_java: false,

View file

@ -552,6 +552,7 @@ Return<void> CameraProvider::getCameraDeviceInterface_V3_x(
return Void();
}
// ICameraDevice 3.4 or upper
sp<android::hardware::camera::device::V3_2::implementation::CameraDevice> deviceImpl;
if (deviceVersion >= kHAL3_4) {
ALOGV("Constructing v3.4 camera device");
@ -580,6 +581,7 @@ Return<void> CameraProvider::getCameraDeviceInterface_V3_x(
return Void();
}
// ICameraDevice 3.2 and 3.3
// Since some Treble HAL revisions can map to the same legacy HAL version(s), we default
// to the newest possible Treble HAL revision, but allow for override if needed via
// system property.

View file

@ -385,6 +385,9 @@ cd4330c3196bda1d642a32abfe23a7d64ebfbda721940643af6867af3b3f0aa9 android.hardwar
10ff2fae516346b86121368ce5790d5accdfcb73983246b813f3d488b66db45a android.hardware.wifi.supplicant@1.1::ISupplicantStaNetwork
# ABI preserving changes to HALs during Android Q
2a55e224aa9bc62c0387cd85ad3c97e33f0c33a4e1489cbae86b2523e6f9df35 android.hardware.camera.device@3.2::ICameraDevice
f61b616732d8f374e030f90575d7eba3ecc99d209a05b945949ba892bcb81e1d android.hardware.camera.device@3.2::ICameraDeviceSession
684702a60deef03a1e8093961dc0a18c555c857ad5a77ba7340b0635ae01eb70 android.hardware.camera.device@3.4::ICameraDeviceSession
a95745bbf76aea16a76518bd7efe70cabc5886d09eaeffc993c2e1787a22ed23 android.hardware.camera.metadata@3.3::types
5f936a5befde7af8d2a683670f80a836b4741e94d84b7b39026da3ed78be9906 android.hardware.configstore@1.0::ISurfaceFlingerConfigs
574e8f1499436fb4075894dcae0b36682427956ecb114f17f1fe22d116a83c6b android.hardware.neuralnetworks@1.0::IPreparedModel