michael@0: /* michael@0: * Copyright (C) 2013-2014 Mozilla Foundation michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: #include "TestGonkCameraHardware.h" michael@0: michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: using namespace android; michael@0: using namespace mozilla; michael@0: michael@0: TestGonkCameraHardware::TestGonkCameraHardware(nsGonkCameraControl* aTarget, michael@0: uint32_t aCameraId, michael@0: const sp& aCamera) michael@0: : GonkCameraHardware(aTarget, aCameraId, aCamera) michael@0: { michael@0: DOM_CAMERA_LOGA("v===== Created TestGonkCameraHardware =====v\n"); michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p (aTarget=%p)\n", michael@0: __func__, __LINE__, this, aTarget); michael@0: MOZ_COUNT_CTOR(TestGonkCameraHardware); michael@0: } michael@0: michael@0: TestGonkCameraHardware::~TestGonkCameraHardware() michael@0: { michael@0: MOZ_COUNT_DTOR(TestGonkCameraHardware); michael@0: DOM_CAMERA_LOGA("^===== Destroyed TestGonkCameraHardware =====^\n"); michael@0: } michael@0: michael@0: nsresult michael@0: TestGonkCameraHardware::Init() michael@0: { michael@0: if (IsTestCase("init-failure")) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return GonkCameraHardware::Init(); michael@0: } michael@0: michael@0: const nsCString michael@0: TestGonkCameraHardware::TestCase() michael@0: { michael@0: const nsCString test = Preferences::GetCString("camera.control.test.hardware"); michael@0: return test; michael@0: } michael@0: michael@0: const nsCString michael@0: TestGonkCameraHardware::GetExtraParameters() michael@0: { michael@0: /** michael@0: * The contents of this pref are appended to the flattened string of michael@0: * parameters stuffed into GonkCameraParameters by the camera library. michael@0: * It consists of semicolon-delimited key=value pairs, e.g. michael@0: * michael@0: * focus-mode=auto;flash-mode=auto;preview-size=1024x768 michael@0: * michael@0: * The unflattening process breaks this string up on semicolon boundaries michael@0: * and sets an entry in a hashtable of strings with the token before michael@0: * the equals sign as the key, and the token after as the value. Because michael@0: * the string is parsed in order, key=value pairs occuring later in the michael@0: * string will replace value pairs appearing earlier, making it easy to michael@0: * inject fake, testable values into the parameters table. michael@0: * michael@0: * One constraint of this approach is that neither the key nor the value michael@0: * may contain equals signs or semicolons. We don't enforce that here michael@0: * so that we can also test correct handling of improperly-formatted values. michael@0: */ michael@0: const nsCString parameters = Preferences::GetCString("camera.control.test.hardware.gonk.parameters"); michael@0: DOM_CAMERA_LOGA("TestGonkCameraHardware : extra-parameters '%s'\n", michael@0: parameters.get()); michael@0: return parameters; michael@0: } michael@0: michael@0: bool michael@0: TestGonkCameraHardware::IsTestCaseInternal(const char* aTest, const char* aFile, int aLine) michael@0: { michael@0: if (TestCase().EqualsASCII(aTest)) { michael@0: DOM_CAMERA_LOGA("TestGonkCameraHardware : test-case '%s' (%s:%d)\n", michael@0: aTest, aFile, aLine); michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: int michael@0: TestGonkCameraHardware::TestCaseError(int aDefaultError) michael@0: { michael@0: // for now, just return the default error michael@0: return aDefaultError; michael@0: } michael@0: michael@0: int michael@0: TestGonkCameraHardware::AutoFocus() michael@0: { michael@0: class AutoFocusFailure : public nsRunnable michael@0: { michael@0: public: michael@0: AutoFocusFailure(nsGonkCameraControl* aTarget) michael@0: : mTarget(aTarget) michael@0: { } michael@0: michael@0: NS_IMETHODIMP michael@0: Run() michael@0: { michael@0: OnAutoFocusComplete(mTarget, false); michael@0: return NS_OK; michael@0: } michael@0: michael@0: protected: michael@0: nsGonkCameraControl* mTarget; michael@0: }; michael@0: michael@0: if (IsTestCase("auto-focus-failure")) { michael@0: return TestCaseError(UNKNOWN_ERROR); michael@0: } michael@0: if (IsTestCase("auto-focus-process-failure")) { michael@0: nsresult rv = NS_DispatchToCurrentThread(new AutoFocusFailure(mTarget)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: return OK; michael@0: } michael@0: DOM_CAMERA_LOGE("Failed to dispatch AutoFocusFailure runnable (0x%08x)\n", rv); michael@0: return UNKNOWN_ERROR; michael@0: } michael@0: michael@0: return GonkCameraHardware::AutoFocus(); michael@0: } michael@0: michael@0: // These classes have to be external to StartFaceDetection(), at least michael@0: // until we pick up gcc 4.5, which supports local classes as template michael@0: // arguments. michael@0: class FaceDetected : public nsRunnable michael@0: { michael@0: public: michael@0: FaceDetected(nsGonkCameraControl* aTarget) michael@0: : mTarget(aTarget) michael@0: { } michael@0: michael@0: ~FaceDetected() michael@0: { michael@0: ReleaseFacesArray(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Run() michael@0: { michael@0: InitMetaData(); michael@0: OnFacesDetected(mTarget, &mMetaData); michael@0: return NS_OK; michael@0: } michael@0: michael@0: protected: michael@0: virtual nsresult InitMetaData() = 0; michael@0: michael@0: nsresult michael@0: AllocateFacesArray(uint32_t num) michael@0: { michael@0: mMetaData.faces = new camera_face_t[num]; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ReleaseFacesArray() michael@0: { michael@0: delete [] mMetaData.faces; michael@0: mMetaData.faces = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr mTarget; michael@0: camera_frame_metadata_t mMetaData; michael@0: }; michael@0: michael@0: class OneFaceDetected : public FaceDetected michael@0: { michael@0: public: michael@0: OneFaceDetected(nsGonkCameraControl* aTarget) michael@0: : FaceDetected(aTarget) michael@0: { } michael@0: michael@0: nsresult michael@0: InitMetaData() MOZ_OVERRIDE michael@0: { michael@0: mMetaData.number_of_faces = 1; michael@0: AllocateFacesArray(1); michael@0: mMetaData.faces[0].id = 1; michael@0: mMetaData.faces[0].score = 2; michael@0: mMetaData.faces[0].rect[0] = 3; michael@0: mMetaData.faces[0].rect[1] = 4; michael@0: mMetaData.faces[0].rect[2] = 5; michael@0: mMetaData.faces[0].rect[3] = 6; michael@0: mMetaData.faces[0].left_eye[0] = 7; michael@0: mMetaData.faces[0].left_eye[1] = 8; michael@0: mMetaData.faces[0].right_eye[0] = 9; michael@0: mMetaData.faces[0].right_eye[1] = 10; michael@0: mMetaData.faces[0].mouth[0] = 11; michael@0: mMetaData.faces[0].mouth[1] = 12; michael@0: michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: class TwoFacesDetected : public FaceDetected michael@0: { michael@0: public: michael@0: TwoFacesDetected(nsGonkCameraControl* aTarget) michael@0: : FaceDetected(aTarget) michael@0: { } michael@0: michael@0: nsresult michael@0: InitMetaData() MOZ_OVERRIDE michael@0: { michael@0: mMetaData.number_of_faces = 2; michael@0: AllocateFacesArray(2); michael@0: mMetaData.faces[0].id = 1; michael@0: mMetaData.faces[0].score = 2; michael@0: mMetaData.faces[0].rect[0] = 3; michael@0: mMetaData.faces[0].rect[1] = 4; michael@0: mMetaData.faces[0].rect[2] = 5; michael@0: mMetaData.faces[0].rect[3] = 6; michael@0: mMetaData.faces[0].left_eye[0] = 7; michael@0: mMetaData.faces[0].left_eye[1] = 8; michael@0: mMetaData.faces[0].right_eye[0] = 9; michael@0: mMetaData.faces[0].right_eye[1] = 10; michael@0: mMetaData.faces[0].mouth[0] = 11; michael@0: mMetaData.faces[0].mouth[1] = 12; michael@0: mMetaData.faces[1].id = 13; michael@0: mMetaData.faces[1].score = 14; michael@0: mMetaData.faces[1].rect[0] = 15; michael@0: mMetaData.faces[1].rect[1] = 16; michael@0: mMetaData.faces[1].rect[2] = 17; michael@0: mMetaData.faces[1].rect[3] = 18; michael@0: mMetaData.faces[1].left_eye[0] = 19; michael@0: mMetaData.faces[1].left_eye[1] = 20; michael@0: mMetaData.faces[1].right_eye[0] = 21; michael@0: mMetaData.faces[1].right_eye[1] = 22; michael@0: mMetaData.faces[1].mouth[0] = 23; michael@0: mMetaData.faces[1].mouth[1] = 24; michael@0: michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: class OneFaceNoFeaturesDetected : public FaceDetected michael@0: { michael@0: public: michael@0: OneFaceNoFeaturesDetected(nsGonkCameraControl* aTarget) michael@0: : FaceDetected(aTarget) michael@0: { } michael@0: michael@0: nsresult michael@0: InitMetaData() MOZ_OVERRIDE michael@0: { michael@0: mMetaData.number_of_faces = 1; michael@0: AllocateFacesArray(1); michael@0: mMetaData.faces[0].id = 1; michael@0: // Test clamping 'score' to 100. michael@0: mMetaData.faces[0].score = 1000; michael@0: mMetaData.faces[0].rect[0] = 3; michael@0: mMetaData.faces[0].rect[1] = 4; michael@0: mMetaData.faces[0].rect[2] = 5; michael@0: mMetaData.faces[0].rect[3] = 6; michael@0: // Nullable values set to 'not-supported' specific values michael@0: mMetaData.faces[0].left_eye[0] = -2000; michael@0: mMetaData.faces[0].left_eye[1] = -2000; michael@0: // Test other 'not-supported' values as well. We treat michael@0: // anything outside the range [-1000, 1000] as invalid. michael@0: mMetaData.faces[0].right_eye[0] = 1001; michael@0: mMetaData.faces[0].right_eye[1] = -1001; michael@0: mMetaData.faces[0].mouth[0] = -2000; michael@0: mMetaData.faces[0].mouth[1] = 2000; michael@0: michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: class NoFacesDetected : public FaceDetected michael@0: { michael@0: public: michael@0: NoFacesDetected(nsGonkCameraControl* aTarget) michael@0: : FaceDetected(aTarget) michael@0: { } michael@0: michael@0: nsresult michael@0: InitMetaData() MOZ_OVERRIDE michael@0: { michael@0: mMetaData.number_of_faces = 0; michael@0: mMetaData.faces = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: int michael@0: TestGonkCameraHardware::StartFaceDetection() michael@0: { michael@0: nsRefPtr faceDetected; michael@0: michael@0: if (IsTestCase("face-detection-detected-one-face")) { michael@0: faceDetected = new OneFaceDetected(mTarget); michael@0: } else if (IsTestCase("face-detection-detected-two-faces")) { michael@0: faceDetected = new TwoFacesDetected(mTarget); michael@0: } else if (IsTestCase("face-detection-detected-one-face-no-features")) { michael@0: faceDetected = new OneFaceNoFeaturesDetected(mTarget); michael@0: } else if (IsTestCase("face-detection-no-faces-detected")) { michael@0: faceDetected = new NoFacesDetected(mTarget); michael@0: } michael@0: michael@0: if (!faceDetected) { michael@0: return GonkCameraHardware::StartFaceDetection(); michael@0: } michael@0: michael@0: nsresult rv = NS_DispatchToCurrentThread(faceDetected); michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGE("Failed to dispatch FaceDetected runnable (0x%08x)\n", rv); michael@0: return UNKNOWN_ERROR; michael@0: } michael@0: michael@0: return OK; michael@0: } michael@0: michael@0: int michael@0: TestGonkCameraHardware::StopFaceDetection() michael@0: { michael@0: if (IsTestCase("face-detection-detected-one-face") || michael@0: IsTestCase("face-detection-detected-two-faces") || michael@0: IsTestCase("face-detection-detected-one-face-no-features") || michael@0: IsTestCase("face-detection-no-faces-detected")) michael@0: { michael@0: return OK; michael@0: } michael@0: michael@0: return GonkCameraHardware::StopFaceDetection(); michael@0: } michael@0: michael@0: int michael@0: TestGonkCameraHardware::TakePicture() michael@0: { michael@0: class TakePictureFailure : public nsRunnable michael@0: { michael@0: public: michael@0: TakePictureFailure(nsGonkCameraControl* aTarget) michael@0: : mTarget(aTarget) michael@0: { } michael@0: michael@0: NS_IMETHODIMP michael@0: Run() michael@0: { michael@0: OnTakePictureError(mTarget); michael@0: return NS_OK; michael@0: } michael@0: michael@0: protected: michael@0: nsGonkCameraControl* mTarget; michael@0: }; michael@0: michael@0: if (IsTestCase("take-picture-failure")) { michael@0: return TestCaseError(UNKNOWN_ERROR); michael@0: } michael@0: if (IsTestCase("take-picture-process-failure")) { michael@0: nsresult rv = NS_DispatchToCurrentThread(new TakePictureFailure(mTarget)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: return OK; michael@0: } michael@0: DOM_CAMERA_LOGE("Failed to dispatch TakePictureFailure runnable (0x%08x)\n", rv); michael@0: return UNKNOWN_ERROR; michael@0: } michael@0: michael@0: return GonkCameraHardware::TakePicture(); michael@0: } michael@0: michael@0: int michael@0: TestGonkCameraHardware::StartPreview() michael@0: { michael@0: if (IsTestCase("start-preview-failure")) { michael@0: return TestCaseError(UNKNOWN_ERROR); michael@0: } michael@0: michael@0: return GonkCameraHardware::StartPreview(); michael@0: } michael@0: michael@0: int michael@0: TestGonkCameraHardware::StartAutoFocusMoving(bool aIsMoving) michael@0: { michael@0: class AutoFocusMoving : public nsRunnable michael@0: { michael@0: public: michael@0: AutoFocusMoving(nsGonkCameraControl* aTarget, bool aIsMoving) michael@0: : mTarget(aTarget) michael@0: , mIsMoving(aIsMoving) michael@0: { } michael@0: michael@0: NS_IMETHODIMP michael@0: Run() michael@0: { michael@0: OnAutoFocusMoving(mTarget, mIsMoving); michael@0: return NS_OK; michael@0: } michael@0: michael@0: protected: michael@0: nsGonkCameraControl* mTarget; michael@0: bool mIsMoving; michael@0: }; michael@0: michael@0: nsresult rv = NS_DispatchToCurrentThread(new AutoFocusMoving(mTarget, aIsMoving)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: return OK; michael@0: } michael@0: DOM_CAMERA_LOGE("Failed to dispatch AutoFocusMoving runnable (0x%08x)\n", rv); michael@0: return UNKNOWN_ERROR; michael@0: } michael@0: michael@0: int michael@0: TestGonkCameraHardware::PushParameters(const GonkCameraParameters& aParams) michael@0: { michael@0: if (IsTestCase("push-parameters-failure")) { michael@0: return TestCaseError(UNKNOWN_ERROR); michael@0: } michael@0: michael@0: nsString focusMode; michael@0: GonkCameraParameters& params = const_cast(aParams); michael@0: params.Get(CAMERA_PARAM_FOCUSMODE, focusMode); michael@0: if (focusMode.EqualsASCII("continuous-picture") || michael@0: focusMode.EqualsASCII("continuous-video")) michael@0: { michael@0: if (IsTestCase("autofocus-moving-true")) { michael@0: return StartAutoFocusMoving(true); michael@0: } else if (IsTestCase("autofocus-moving-false")) { michael@0: return StartAutoFocusMoving(false); michael@0: } michael@0: } michael@0: michael@0: return GonkCameraHardware::PushParameters(aParams); michael@0: } michael@0: michael@0: nsresult michael@0: TestGonkCameraHardware::PullParameters(GonkCameraParameters& aParams) michael@0: { michael@0: if (IsTestCase("pull-parameters-failure")) { michael@0: return static_cast(TestCaseError(UNKNOWN_ERROR)); michael@0: } michael@0: michael@0: String8 s = mCamera->getParameters(); michael@0: nsCString extra = GetExtraParameters(); michael@0: if (!extra.IsEmpty()) { michael@0: s += ";"; michael@0: s += extra.get(); michael@0: } michael@0: michael@0: return aParams.Unflatten(s); michael@0: } michael@0: michael@0: int michael@0: TestGonkCameraHardware::StartRecording() michael@0: { michael@0: if (IsTestCase("start-recording-failure")) { michael@0: return TestCaseError(UNKNOWN_ERROR); michael@0: } michael@0: michael@0: return GonkCameraHardware::StartRecording(); michael@0: } michael@0: michael@0: int michael@0: TestGonkCameraHardware::StopRecording() michael@0: { michael@0: if (IsTestCase("stop-recording-failure")) { michael@0: return TestCaseError(UNKNOWN_ERROR); michael@0: } michael@0: michael@0: return GonkCameraHardware::StopRecording(); michael@0: } michael@0: michael@0: int michael@0: TestGonkCameraHardware::SetListener(const sp& aListener) michael@0: { michael@0: if (IsTestCase("set-listener-failure")) { michael@0: return TestCaseError(UNKNOWN_ERROR); michael@0: } michael@0: michael@0: return GonkCameraHardware::SetListener(aListener); michael@0: } michael@0: michael@0: int michael@0: TestGonkCameraHardware::StoreMetaDataInBuffers(bool aEnabled) michael@0: { michael@0: if (IsTestCase("store-metadata-in-buffers-failure")) { michael@0: return TestCaseError(UNKNOWN_ERROR); michael@0: } michael@0: michael@0: return GonkCameraHardware::StoreMetaDataInBuffers(aEnabled); michael@0: }