return CameraServicesImpl.instance();
}
+ @Override
+ public FatalErrorHandler getFatalErrorHandler() {
+ return new FatalErrorHandlerImpl(this);
+ }
+
public List<String> getSupportedModeNames() {
List<Integer> indices = mModuleManager.getSupportedModeIndexList();
List<String> supported = new ArrayList<String>();
}
});
}
- });
+ }, mAppController.getFatalErrorHandler());
guard.stop("mCameraManager.open()");
}
--- /dev/null
+/*
+ * Copyright (C) 2015 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.camera;
+
+import android.hardware.camera2.CameraDevice;
+
+import com.android.camera2.R;
+
+/**
+ * Handles fatal application errors.
+ * <p>
+ * Usage:
+ *
+ * <pre>
+ * if (unrecoverableErrorDetected) {
+ * fatalErrorHandler.handleFatalError(Reason.CANNOT_CONNECT_TO_CAMERA);
+ * }
+ * </pre>
+ */
+public interface FatalErrorHandler {
+ public static enum Reason {
+ CANNOT_CONNECT_TO_CAMERA(
+ R.string.cannot_connect_camera,
+ R.string.feedback_description_camera_access,
+ true),
+ CAMERA_HAL_FAILED(
+ R.string.cannot_connect_camera,
+ R.string.feedback_description_camera_access,
+ true),
+ CAMERA_DISABLED_BY_SECURITY_POLICY(
+ R.string.camera_disabled,
+ R.string.feedback_description_camera_access,
+ true),
+ MEDIA_STORAGE_FAILURE(
+ R.string.media_storage_failure,
+ R.string.feedback_description_save_photo,
+ false);
+
+ private final int mDialogMsgId;
+ private final int mFeedbackMsgId;
+ private final boolean mFinishActivity;
+
+ /**
+ * @param dialogMsgId The resource ID of string to display in the fatal
+ * error dialog.
+ * @param feedbackMsgId The resource ID of default string to display in
+ * the feedback dialog, if the user chooses to submit
+ * feedback from the dialog.
+ * @param finishActivity Whether the activity should be finished as a
+ * result of this error.
+ */
+ Reason(int dialogMsgId, int feedbackMsgId, boolean finishActivity) {
+ mDialogMsgId = dialogMsgId;
+ mFeedbackMsgId = feedbackMsgId;
+ mFinishActivity = finishActivity;
+ }
+
+ /**
+ * @return The resource ID of the string to display in the fatal error
+ * dialog.
+ */
+ public int getFeedbackMsgId() {
+ return mFeedbackMsgId;
+ }
+
+ /**
+ * @return The resource ID of the default string to display in the
+ * feedback dialog, if the user chooses to submit feedback from
+ * the dialog.
+ */
+ public int getDialogMsgId() {
+ return mDialogMsgId;
+ }
+
+ /**
+ * @return Whether the activity should be finished as a result of this
+ * error.
+ */
+ public boolean doesFinishActivity() {
+ return mFinishActivity;
+ }
+
+ /**
+ * Creates a new Reason based on an error code for
+ * {@link CameraDevice.StateCallback#onError}.
+ *
+ * @param error The error code. One of
+ * CameraDevice.StateCallback.ERROR_*
+ * @return The appropriate Reason.
+ */
+ public static Reason fromCamera2CameraDeviceStateCallbackError(int error) {
+ // TODO Use a more descriptive reason to distinguish between
+ // different types of errors.
+ switch (error) {
+ case CameraDevice.StateCallback.ERROR_CAMERA_DEVICE:
+ case CameraDevice.StateCallback.ERROR_CAMERA_DISABLED:
+ case CameraDevice.StateCallback.ERROR_CAMERA_IN_USE:
+ case CameraDevice.StateCallback.ERROR_CAMERA_SERVICE:
+ case CameraDevice.StateCallback.ERROR_MAX_CAMERAS_IN_USE:
+ default:
+ return CANNOT_CONNECT_TO_CAMERA;
+ }
+ }
+ }
+
+ /**
+ * Handles a fatal error, e.g. by displaying the appropriate dialog and
+ * exiting the activity.
+ */
+ public void handleFatalError(Reason reason);
+}
--- /dev/null
+/*
+ * Copyright (C) 2015 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.camera;
+
+import android.app.Activity;
+
+import com.android.camera.debug.Log;
+import com.android.camera.util.CameraUtil;
+
+public final class FatalErrorHandlerImpl implements FatalErrorHandler {
+ private static final Log.Tag TAG = new Log.Tag("FatalErrorHandler");
+
+ private final Activity mActivity;
+
+ public FatalErrorHandlerImpl(Activity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public void handleFatalError(Reason reason) {
+ // Log a stack trace to be sure we can track the source.
+ Log.e(TAG, "Handling Fatal Error:", new Exception());
+
+ boolean finishActivity = reason.doesFinishActivity();
+ CameraUtil.showError(mActivity, reason.getDialogMsgId(), reason.getFeedbackMsgId(),
+ finishActivity);
+ }
+}
import android.widget.FrameLayout;
import com.android.camera.ButtonManager;
+import com.android.camera.FatalErrorHandler;
import com.android.camera.SoundPlayer;
import com.android.camera.module.ModuleController;
import com.android.camera.one.OneCameraManager;
public CameraServices getServices();
/**
+ * @return The error handler to invoke for fatal (app-killing) errors.
+ */
+ public FatalErrorHandler getFatalErrorHandler();
+
+ /**
* Returns the {@link com.android.camera.app.CameraAppUI}.
*
* @return {@code null} if not available yet.
package com.android.camera.captureintent.resource;
+import com.android.camera.FatalErrorHandler;
import com.android.camera.app.AppController;
import com.android.camera.app.LocationManager;
import com.android.camera.app.OrientationManager;
*/
@Deprecated
public AppController getAppController();
+
+ /**
+ * Obtains the fatal error handler.
+ *
+ * @return An {@link FatalErrorHandler} object.
+ */
+ public FatalErrorHandler getFatalErrorHandler();
}
package com.android.camera.captureintent.resource;
+import com.android.camera.FatalErrorHandler;
import com.android.camera.app.AppController;
import com.android.camera.app.LocationManager;
import com.android.camera.app.OrientationManager;
private final ResolutionSetting mResolutionSetting;
private final HandlerThread mCameraThread;
private final Handler mCameraHandler;
+ private final FatalErrorHandler mFatalErrorHandler;
// TODO: Hope one day we could get rid of AppController.
private final AppController mAppController;
OrientationManager orientationManager,
CameraFacingSetting cameraFacingSetting,
ResolutionSetting resolutionSetting,
- AppController appController) {
+ AppController appController,
+ FatalErrorHandler fatalErrorHandler) {
return new RefCountBase<ResourceConstructed>(new ResourceConstructedImpl(
intent, moduleUI, mainThread, context, cameraManager, locationManager,
- orientationManager, cameraFacingSetting, resolutionSetting, appController));
+ orientationManager, cameraFacingSetting, resolutionSetting,
+ appController, fatalErrorHandler));
}
private ResourceConstructedImpl(
OrientationManager orientationManager,
CameraFacingSetting cameraFacingSetting,
ResolutionSetting resolutionSetting,
- AppController appController) {
+ AppController appController,
+ FatalErrorHandler fatalErrorHandler) {
mIntent = intent;
mModuleUI = moduleUI;
mMainThread = mainThread;
mOrientationManager = orientationManager;
mCameraFacingSetting = cameraFacingSetting;
mResolutionSetting = resolutionSetting;
+ mFatalErrorHandler = fatalErrorHandler;
mAppController = appController;
mCameraThread = new HandlerThread("ImageCaptureIntentModule.CameraHandler");
public AppController getAppController() {
return mAppController;
}
+
+ @Override
+ public FatalErrorHandler getFatalErrorHandler() {
+ return mFatalErrorHandler;
+ }
}
super(stateMachine);
mResourceConstructed = ResourceConstructedImpl.create(
intent, moduleUI, mainThread, context, cameraManager, locationManager,
- orientationManager, cameraFacingSetting, resolutionSetting, appController);
+ orientationManager, cameraFacingSetting, resolutionSetting, appController,
+ appController.getFatalErrorHandler());
registerEventHandlers();
}
package com.android.camera.captureintent.state;
+import com.android.camera.FatalErrorHandlerImpl;
import com.google.common.base.Optional;
import com.android.camera.SoundPlayer;
imageRotationCalculator,
new BurstFacadeFactory.BurstFacadeStub(),
new SoundPlayer(mResourceConstructed.get().getContext()),
- mCameraOpenCallback);
+ mCameraOpenCallback,
+ mResourceConstructed.get().getFatalErrorHandler());
return Optional.absent();
}
import android.os.Handler;
import android.util.DisplayMetrics;
+import com.android.camera.FatalErrorHandler;
import com.android.camera.SoundPlayer;
import com.android.camera.async.MainThread;
import com.android.camera.burst.BurstFacade;
import com.android.camera.one.OneCamera.OpenCallback;
import com.android.camera.one.config.OneCameraFeatureConfig;
import com.android.camera.one.v2.photo.ImageRotationCalculator;
-import com.android.camera.settings.SettingsManager;
-import com.android.camera.util.Size;
import com.google.common.base.Optional;
/**
* <p>
* Exactly one call will always be made to a single method in the provided
* {@link OpenCallback}.
- *
- * @param captureSetting the related settings to configure the camera for capture.
+ *
+ * @param captureSetting the related settings to configure the camera for
+ * capture.
* @param handler the handler on which callback methods are invoked.
* @param mainThread Main thread executor
* @param imageRotationCalculator Image rotation calculator required for
* @param soundPlayer the sound player.
* @param openCallback this listener is called when the camera was opened or
* when it failed to open.
+ * @param fatalErrorHandler the fatal error handler to use for indicating
+ * fatal errors
*/
public abstract void open(
OneCameraCaptureSetting captureSetting,
ImageRotationCalculator imageRotationCalculator,
BurstFacade burstController,
SoundPlayer soundPlayer,
- OpenCallback openCallback);
+ OpenCallback openCallback,
+ FatalErrorHandler fatalErrorHandler);
// TODO: Move this to OneCameraCharacteristics class.
/**
Context context,
DisplayMetrics displayMetrics) throws OneCameraException {
Optional<OneCameraManager> manager = com.android.camera.one.v2.OneCameraManagerImpl.create(
- featureConfig, context, displayMetrics);
+ featureConfig, context, displayMetrics);
if (!manager.isPresent()) {
manager = com.android.camera.one.v1.OneCameraManagerImpl.create();
}
import android.hardware.Camera;
import android.os.Handler;
-import com.android.camera.CameraActivity;
+import com.android.camera.FatalErrorHandler;
import com.android.camera.SoundPlayer;
import com.android.camera.async.MainThread;
import com.android.camera.burst.BurstFacade;
import com.android.camera.one.OneCameraCharacteristics;
import com.android.camera.one.OneCameraManager;
import com.android.camera.one.v2.photo.ImageRotationCalculator;
-import com.android.camera.settings.SettingsManager;
-import com.android.camera.util.Size;
import com.google.common.base.Optional;
/**
* The {@link OneCameraManager} implementation on top of the Camera API 1.
ImageRotationCalculator imageRotationCalculator,
BurstFacade burstController,
SoundPlayer soundPlayer,
- OpenCallback openCallback) {
+ OpenCallback openCallback, FatalErrorHandler fatalErrorHandler) {
throw new RuntimeException("Not implemented yet.");
}
package com.android.camera.one.v2;
+import com.android.camera.FatalErrorHandler;
import com.android.camera.async.MainThread;
import com.android.camera.async.Observable;
import com.android.camera.burst.BurstFacade;
import com.android.camera.one.OneCamera;
import com.android.camera.one.OneCameraCharacteristics;
-import com.android.camera.one.config.OneCameraFeatureConfig;
import com.android.camera.one.config.OneCameraFeatureConfig.CaptureSupportLevel;
import com.android.camera.one.v2.camera2proxy.CameraDeviceProxy;
import com.android.camera.one.v2.imagesaver.ImageSaver;
Observable<OneCamera.PhotoCaptureParameters.Flash> flashSetting,
Observable<Integer> exposureSetting,
Observable<Boolean> hdrSceneSetting,
- BurstFacade burstController);
+ BurstFacade burstController, FatalErrorHandler fatalErrorHandler);
}
import android.os.Handler;
import android.util.DisplayMetrics;
+import com.android.camera.FatalErrorHandler;
import com.android.camera.SoundPlayer;
import com.android.camera.async.MainThread;
import com.android.camera.burst.BurstFacade;
final ImageRotationCalculator imageRotationCalculator,
final BurstFacade burstController,
final SoundPlayer soundPlayer,
- final OpenCallback openCallback) {
+ final OpenCallback openCallback,
+ final FatalErrorHandler fatalErrorHandler) {
try {
final String cameraId = getCameraId(captureSetting.getCameraFacing());
Log.i(TAG, "Opening Camera ID " + cameraId);
mainThread,
imageRotationCalculator,
burstController,
- soundPlayer);
+ soundPlayer, fatalErrorHandler);
openCallback.onCameraOpened(oneCamera);
} catch (CameraAccessException e) {
Log.d(TAG, "Could not get camera characteristics");
import android.os.Build;
import android.view.Surface;
-import com.android.camera.CaptureModule;
+import com.android.camera.FatalErrorHandler;
import com.android.camera.async.HandlerFactory;
import com.android.camera.async.Lifetime;
import com.android.camera.async.MainThread;
import com.android.camera.one.v2.sharedimagereader.SharedImageReaderFactory;
import com.android.camera.util.Provider;
import com.android.camera.util.Size;
-import com.google.common.base.Function;
import com.google.common.base.Supplier;
import java.util.ArrayList;
final Observable<OneCamera.PhotoCaptureParameters.Flash> flashSetting,
final Observable<Integer> exposureSetting,
final Observable<Boolean> hdrSceneSetting,
- final BurstFacade burstFacade) {
+ final BurstFacade burstFacade, FatalErrorHandler fatalErrorHandler) {
final Lifetime lifetime = new Lifetime();
final ImageReaderProxy imageReader = new CloseWhenDoneImageReader(new LoggingImageReader(
import android.util.Range;
import android.view.Surface;
+import com.android.camera.FatalErrorHandler;
import com.android.camera.async.HandlerFactory;
import com.android.camera.async.Lifetime;
import com.android.camera.async.MainThread;
import com.android.camera.one.v2.core.FrameServer;
import com.android.camera.one.v2.core.FrameServerFactory;
import com.android.camera.one.v2.core.RequestTemplate;
+import com.android.camera.one.v2.core.ResponseListener;
import com.android.camera.one.v2.core.ResponseListeners;
-import com.android.camera.one.v2.errorhandling.RepeatFailureDetector;
+import com.android.camera.one.v2.errorhandling.RepeatFailureHandlerComponent;
import com.android.camera.one.v2.imagesaver.ImageSaver;
import com.android.camera.one.v2.initialization.CameraStarter;
import com.android.camera.one.v2.initialization.InitializedOneCameraFactory;
import com.android.camera.util.ApiHelper;
import com.android.camera.util.Provider;
import com.android.camera.util.Size;
-import com.google.common.base.Function;
import com.google.common.base.Supplier;
import java.util.ArrayList;
final OneCameraCharacteristics characteristics,
CaptureSupportLevel featureConfig,
final MainThread mainThread,
- Size pictureSize, final ImageSaver.Builder imageSaverBuilder,
+ Size pictureSize,
+ final ImageSaver.Builder imageSaverBuilder,
final Observable<OneCamera.PhotoCaptureParameters.Flash> flashSetting,
final Observable<Integer> exposureSetting,
final Observable<Boolean> hdrSceneSetting,
- final BurstFacade burstFacade) {
+ final BurstFacade burstFacade,
+ final FatalErrorHandler fatalErrorHandler) {
final Lifetime lifetime = new Lifetime();
final ImageReaderProxy imageReader = new CloseWhenDoneImageReader(
sharedImageReaderFactory.provideMetadataPool(), flashSetting);
BurstTaker burstTaker = new BurstTakerImpl(cameraCommandExecutor, frameServer,
rootBuilder, sharedImageReaderFactory.provideSharedImageReader(),
- burstFacade.getInputSurface(),basicCameraFactory.providePreviewStarter(),
- // ImageReader#acquireLatestImage() requires two images as the margin so
- // specify that as the maximum number of images that can be used by burst.
+ burstFacade.getInputSurface(), basicCameraFactory.providePreviewStarter(),
+ // ImageReader#acquireLatestImage() requires two images
+ // as the margin so
+ // specify that as the maximum number of images that can
+ // be used by burst.
mMaxImageCount - 2);
burstFacade.setBurstTaker(burstTaker);
if (isBackCamera && ApiHelper.IS_NEXUS_5) {
// Workaround for bug: 19061883
- RepeatFailureDetector failureDetector = new RepeatFailureDetector(
- Loggers.tagFactory(), UsageStatistics.instance(),
- cameraCaptureSession, cameraCommandExecutor, basicCameraFactory
- .providePreviewStarter(), 10);
+ ResponseListener failureDetector = RepeatFailureHandlerComponent.create(
+ Loggers.tagFactory(),
+ fatalErrorHandler, cameraCaptureSession, cameraCommandExecutor,
+ basicCameraFactory.providePreviewStarter(),
+ UsageStatistics.instance(), 10).provideResponseListener();
rootBuilder.addResponseListener(failureDetector);
}
-
final Observable<Integer> availableImageCount = sharedImageReaderFactory
.provideAvailableImageCount();
final Observable<Boolean> frameServerAvailability = frameServerComponent
--- /dev/null
+/*
+ * Copyright (C) 2015 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.camera.one.v2.errorhandling;
+
+import android.hardware.camera2.CameraCaptureSession;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+/**
+ * Represents a strategy for dealing with repeat failure (multiple consecutive
+ * camera frames resulting in
+ * {@link CameraCaptureSession.CaptureCallback#onCaptureFailed}).
+ */
+@ParametersAreNonnullByDefault
+interface FailureHandler extends Runnable {
+}
--- /dev/null
+/*
+ * Copyright (C) 2015 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.camera.one.v2.errorhandling;
+
+import android.hardware.camera2.CameraAccessException;
+
+import com.android.camera.debug.Log;
+import com.android.camera.debug.Logger;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionProxy;
+import com.android.camera.one.v2.commands.CameraCommandExecutor;
+import com.android.camera.stats.UsageStatistics;
+import com.google.common.logging.eventprotos;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+/**
+ * Resets camera usage by calling abortCaptures(), flushing (interrupting) any
+ * currently-executing camera commands, and restarting the preview.
+ * <p>
+ * Workaround for Bug: 19061883
+ */
+@ParametersAreNonnullByDefault
+final class FastCameraReset implements FailureHandler {
+ private final Logger mLog;
+ private final CameraCaptureSessionProxy mCaptureSession;
+ private final CameraCommandExecutor mCommandExecutor;
+ private final Runnable mPreviewStarter;
+ private final UsageStatistics mUsageStats;
+
+ FastCameraReset(Logger.Factory logFactory, CameraCaptureSessionProxy captureSession,
+ CameraCommandExecutor commandExecutor, Runnable previewStarter,
+ UsageStatistics usageStats) {
+ mLog = logFactory.create(new Log.Tag("FastCameraReset"));
+ mCaptureSession = captureSession;
+ mCommandExecutor = commandExecutor;
+ mPreviewStarter = previewStarter;
+ mUsageStats = usageStats;
+ }
+
+ @Override
+ public void run() {
+ // TODO: Replace UNKNOWN_REASON with enum for this error.
+ mUsageStats.cameraFailure(eventprotos.CameraFailure.FailureReason.UNKNOWN_REASON,
+ "api2_repeated_failure", UsageStatistics.NONE, UsageStatistics.NONE);
+
+ mLog.w("beginning reset()");
+ try {
+ mLog.w("abortCaptures()");
+ mCaptureSession.abortCaptures();
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ } catch (CameraCaptureSessionClosedException e) {
+ e.printStackTrace();
+ }
+ mLog.w("flushing existing camera commands");
+ mCommandExecutor.flush();
+ mLog.w("restarting the preview");
+ mPreviewStarter.run();
+ mLog.w("finished reset()");
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2015 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.camera.one.v2.errorhandling;
+
+import com.android.camera.FatalErrorHandler;
+import com.android.camera.stats.UsageStatistics;
+import com.google.common.logging.eventprotos;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+/**
+ * Handles repeat failure by displaying the fatal error dialog (which also
+ * finishes the activity).
+ */
+@ParametersAreNonnullByDefault
+final class FatalErrorDialogFailureHandler implements FailureHandler {
+ private final FatalErrorHandler mFatalErrorHandler;
+ private final UsageStatistics mUsageStats;
+
+ FatalErrorDialogFailureHandler(FatalErrorHandler fatalErrorHandler,
+ UsageStatistics usageStats) {
+ mFatalErrorHandler = fatalErrorHandler;
+ mUsageStats = usageStats;
+ }
+
+ @Override
+ public void run() {
+ mUsageStats.cameraFailure(eventprotos.CameraFailure.FailureReason.UNKNOWN_REASON,
+ "api2_repeated_failure_2", UsageStatistics.NONE, UsageStatistics.NONE);
+ // TODO Add another {@link FatalErrorHandler.Reason} for this situation
+ mFatalErrorHandler.handleFatalError(FatalErrorHandler.Reason.CANNOT_CONNECT_TO_CAMERA);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2015 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.camera.one.v2.errorhandling;
+
+import com.android.camera.stats.UsageStatistics;
+import com.android.camera.util.Callback;
+import com.google.common.logging.eventprotos;
+
+import javax.annotation.Nonnull;
+import javax.annotation.ParametersAreNonnullByDefault;
+
+@ParametersAreNonnullByDefault
+final class RecoverySuccessCallback implements Callback<String> {
+ private final UsageStatistics mUsageStats;
+
+ RecoverySuccessCallback(UsageStatistics usageStats) {
+ mUsageStats = usageStats;
+ }
+
+ @Override
+ public void onCallback(@Nonnull String successfulRecoveryStrategyName) {
+ // Log Success
+ mUsageStats.cameraFailure(eventprotos.CameraFailure.FailureReason.UNKNOWN_REASON,
+ "api2_repeated_failure_recovery_success", UsageStatistics.NONE, UsageStatistics
+ .NONE);
+ }
+}
package com.android.camera.one.v2.errorhandling;
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.TotalCaptureResult;
import com.android.camera.debug.Log;
import com.android.camera.debug.Logger;
-import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
-import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionProxy;
-import com.android.camera.one.v2.commands.CameraCommandExecutor;
-import com.android.camera.one.v2.core.ResponseListener;
-import com.android.camera.stats.UsageStatistics;
-import com.google.common.logging.eventprotos;
+import com.android.camera.util.Callback;
-import java.net.UnknownServiceException;
+import java.util.List;
import javax.annotation.ParametersAreNonnullByDefault;
/**
- * Listens for repeated capture failure and resets the camera by calling
- * {@link CameraCaptureSession#abortCaptures()}, flushing/interrupting any
- * currently-running camera interactions, and restarting the preview.
+ * Listens for repeated capture failure and invokes recovery strategies,
+ * in-order as the repeated failures continue.
* <p>
* Workaround for bug: 19061883
*/
@ParametersAreNonnullByDefault
-public final class RepeatFailureDetector extends ResponseListener {
+final class RepeatFailureDetector extends com.android.camera.one.v2.core.ResponseListener {
private final Logger mLog;
- private final UsageStatistics mUsageStats;
- private final CameraCaptureSessionProxy mCaptureSession;
- private final CameraCommandExecutor mCommandExecutor;
- private final Runnable mPreviewStarter;
private final int mConsecutiveFailureThreshold;
+ private final List<FailureHandler> mRecoveryStrategies;
+ private final Callback<String> mRecoverySuccessCallback;
+ /**
+ * Indicates the number of consecutive times repeat failure has been
+ * detected.
+ * <p>
+ * 0 indicates normal operation
+ * <p>
+ * Positive values also indicate the index of the recovery strategy which
+ * has been used.
+ */
+ private int mFailureLevel;
+ /**
+ * The frame number of the failure which resulted in a recovery strategy
+ * being invoked. This is used to determine if a frame success corresponds
+ * to a frame from before or after the recovery strategy was run.
+ * <p>
+ * This
+ */
+ private long mFailureFrameNumber;
+ /**
+ * The number of consecutive
+ */
private int mConsecutiveErrorCount;
- private boolean mExecutedReset;
/**
* @param logFactory Used for logging.
- * @param usageStats Used for logging.
- * @param captureSession The camera capture session to abort captures upon
- * repeated failure.
- * @param commandExecutor The command executor to flush upon repeated
- * failure.
- * @param previewStarter Used for restarting the preview.
* @param consecutiveFailureThreshold The number of consecutive failures to
- * classify as a repeat-failure.
+ * consider a "repeat failure".
+ * @param recoveryStrategies A list of strategies to try to recover from or
+ * handle (in other ways) a repeat failure. Strategies are
+ * invoked in-order each time the number of consecutive failures
+ * reaches over the threshold. That is, the Nth strategy is
+ * invoked after N * consecutiveFailureThreshold consecutive
+ * failures are detected.
+ * @param recoverySuccessCallback Invoked upon success of a recovery
+ * strategy, with the string name of the recovery strategy which
+ * worked.
*/
public RepeatFailureDetector(Logger.Factory logFactory,
- UsageStatistics usageStats, CameraCaptureSessionProxy captureSession,
- CameraCommandExecutor commandExecutor, Runnable previewStarter,
- int consecutiveFailureThreshold) {
- mConsecutiveFailureThreshold = consecutiveFailureThreshold;
+ int consecutiveFailureThreshold, List<FailureHandler> recoveryStrategies,
+ Callback<String> recoverySuccessCallback) {
mLog = logFactory.create(new Log.Tag("RepeatFailureDtctr"));
- mUsageStats = usageStats;
- mCaptureSession = captureSession;
- mCommandExecutor = commandExecutor;
- mPreviewStarter = previewStarter;
- mConsecutiveErrorCount = 0;
- mExecutedReset = false;
- }
+ mConsecutiveFailureThreshold = consecutiveFailureThreshold;
+ mRecoveryStrategies = recoveryStrategies;
+ mRecoverySuccessCallback = recoverySuccessCallback;
- @Override
- public void onCompleted(TotalCaptureResult result) {
+ mFailureLevel = 0;
mConsecutiveErrorCount = 0;
+ mFailureFrameNumber = -1;
}
@Override
- public void onSequenceAborted(int sequenceId) {
- mExecutedReset = false;
+ public void onCompleted(TotalCaptureResult result) {
mConsecutiveErrorCount = 0;
- }
-
- @Override
- public void onSequenceCompleted(int sequenceId, long frameNumber) {
-
+ if (mFailureLevel > 0) {
+ if (result.getFrameNumber() > mFailureFrameNumber) {
+ // Success! Recovery worked, and a frame was completed
+ // successfully.
+ mRecoverySuccessCallback.onCallback(mRecoveryStrategies.get(mFailureLevel)
+ .toString());
+ mFailureLevel = 0;
+ mFailureFrameNumber = -1;
+ }
+ }
}
@Override
public void onFailed(CaptureFailure failure) {
if (failure.getReason() == CaptureFailure.REASON_ERROR) {
mConsecutiveErrorCount++;
- mLog.e("onCaptureFailed() REASON_ERROR: Consecutive error count = " +
- mConsecutiveErrorCount);
- if (mConsecutiveErrorCount >= mConsecutiveFailureThreshold && !
- mExecutedReset) {
- mLog.e("onCaptureFailed() REASON_ERROR: Running repeat error callback");
- // TODO: Replace UNKNOWN_REASON with enum for this error.
- mUsageStats.cameraFailure(eventprotos.CameraFailure.FailureReason.UNKNOWN_REASON,
- "api2_repeated_failure", UsageStatistics.NONE, UsageStatistics.NONE);
- mExecutedReset = true;
- reset();
+ mLog.e(String.format("onCaptureFailed() REASON_ERROR: Consecutive error count = %d x" +
+ " %d", mConsecutiveErrorCount, mFailureLevel));
+ if (mConsecutiveErrorCount >= mConsecutiveFailureThreshold) {
+ mConsecutiveErrorCount = 0;
+ mFailureFrameNumber = failure.getFrameNumber();
+ if (mFailureLevel < mRecoveryStrategies.size()) {
+ mLog.e(String.format("onCaptureFailed() REASON_ERROR: Repeat failure " +
+ "detected (x%d). Attempting recovery strategy: %s",
+ mConsecutiveErrorCount, mRecoveryStrategies.get(mFailureLevel)
+ .toString()));
+ mRecoveryStrategies.get(mFailureLevel).run();
+ }
+ mFailureLevel++;
}
}
}
-
- private void reset() {
- mLog.w("beginning reset()");
- try {
- mLog.w("abortCaptures()");
- mCaptureSession.abortCaptures();
- } catch (CameraAccessException e) {
- e.printStackTrace();
- } catch (CameraCaptureSessionClosedException e) {
- e.printStackTrace();
- }
- mLog.w("flushing existing camera commands");
- mCommandExecutor.flush();
- mLog.w("restarting the preview");
- mPreviewStarter.run();
- mLog.w("finished reset()");
- }
}
--- /dev/null
+/*
+ * Copyright (C) 2015 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.camera.one.v2.errorhandling;
+
+import com.android.camera.FatalErrorHandler;
+import com.android.camera.debug.Logger;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionProxy;
+import com.android.camera.one.v2.commands.CameraCommandExecutor;
+import com.android.camera.one.v2.core.ResponseListener;
+import com.android.camera.stats.UsageStatistics;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+/**
+ * Creates a ResponseListener which will handle repeat failures.
+ * <p>
+ * This is to workaround bug: 19061883
+ */
+@ParametersAreNonnullByDefault
+public final class RepeatFailureHandlerComponent {
+ private final RepeatFailureDetector mRepeatFailureHandler;
+
+ private RepeatFailureHandlerComponent(RepeatFailureDetector repeatFailureHandler) {
+ mRepeatFailureHandler = repeatFailureHandler;
+ }
+
+ public ResponseListener provideResponseListener() {
+ return mRepeatFailureHandler;
+ }
+
+ public static RepeatFailureHandlerComponent create(Logger.Factory logFactory,
+ FatalErrorHandler fatalErrorHandler, CameraCaptureSessionProxy captureSession,
+ CameraCommandExecutor commandExecutor, Runnable previewStarter,
+ UsageStatistics usageStats, int consecutiveFailureThreshold) {
+ FastCameraReset fastCameraReset = new FastCameraReset(logFactory, captureSession,
+ commandExecutor, previewStarter, usageStats);
+ FatalErrorDialogFailureHandler fatalErrorDialog = new FatalErrorDialogFailureHandler
+ (fatalErrorHandler, usageStats);
+
+ RecoverySuccessCallback recoverySuccessCallback = new RecoverySuccessCallback(usageStats);
+
+ List<FailureHandler> recoveryStrategies = Arrays.asList(fastCameraReset, fatalErrorDialog);
+ RepeatFailureDetector failureDetector = new RepeatFailureDetector(logFactory,
+ consecutiveFailureThreshold,
+ recoveryStrategies,
+ recoverySuccessCallback);
+ return new RepeatFailureHandlerComponent(failureDetector);
+ }
+}
import com.android.camera.CameraActivity;
import com.android.camera.CameraDisabledException;
+import com.android.camera.FatalErrorHandler;
import com.android.camera.debug.Log;
import com.android.camera2.R;
import com.android.ex.camera2.portability.CameraCapabilities;
/**
* Shows custom error dialog. Designed specifically
* for the scenario where the camera cannot be attached.
+ * @deprecated Use {@link FatalErrorHandler} instead.
*/
+ @Deprecated
public static void showError(final Activity activity, final int dialogMsgId, final int feedbackMsgId,
final boolean finishActivity) {
final DialogInterface.OnClickListener buttonListener =