Handle borked Android cameras gracefully.
It turns out that Camera.getCameraInfo can throw an exception if the camera does not work.

TESTED=added a throw before all calls to Camera.open and Camera.getCameraInfo and made sure APPRtcDemo does not crash.

BUG=4371
R=glaznev@webrtc.org, magjed@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/44909004

Cr-Commit-Position: refs/heads/master@{#8876}
diff --git a/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java b/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java
index ede2549..f8b03f9 100644
--- a/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java
+++ b/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java
@@ -103,31 +103,51 @@
     return Camera.getNumberOfCameras();
   }
 
+  // Returns the name of the camera with camera index. Returns null if the
+  // camera can not be used.
   public static String getDeviceName(int index) {
     Camera.CameraInfo info = new Camera.CameraInfo();
-    Camera.getCameraInfo(index, info);
+    try {
+      Camera.getCameraInfo(index, info);
+    } catch (Exception e) {
+      Log.e(TAG, "getCameraInfo failed on index " + index,e);
+      return null;
+    }
+
     String facing =
         (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) ? "front" : "back";
     return "Camera " + index + ", Facing " + facing
         + ", Orientation " + info.orientation;
   }
 
+  // Returns the name of the front facing camera. Returns null if the
+  // camera can not be used or does not exist.
   public static String getNameOfFrontFacingDevice() {
     for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
       Camera.CameraInfo info = new Camera.CameraInfo();
-      Camera.getCameraInfo(i, info);
-      if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
-        return getDeviceName(i);
+      try {
+        Camera.getCameraInfo(i, info);
+        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
+          return getDeviceName(i);
+      } catch (Exception e) {
+        Log.e(TAG, "getCameraInfo failed on index " + i, e);
+      }
     }
     return null;
   }
 
+  // Returns the name of the back facing camera. Returns null if the
+  // camera can not be used or does not exist.
   public static String getNameOfBackFacingDevice() {
     for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
       Camera.CameraInfo info = new Camera.CameraInfo();
-      Camera.getCameraInfo(i, info);
-      if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK)
-        return getDeviceName(i);
+      try {
+        Camera.getCameraInfo(i, info);
+        if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK)
+          return getDeviceName(i);
+      } catch (Exception e) {
+        Log.e(TAG, "getCameraInfo failed on index " + i, e);
+      }
     }
     return null;
   }
@@ -152,10 +172,10 @@
       return false;
     }
 
-    id = ++id % Camera.getNumberOfCameras();
+    int new_id = (id + 1) % Camera.getNumberOfCameras();
 
     CaptureFormat formatToUse  = null;
-    List<CaptureFormat> formats = supportedFormats.get(id);
+    List<CaptureFormat> formats = supportedFormats.get(new_id);
     for (CaptureFormat format : formats) {
       if (format.width == width && format.height == height) {
         formatToUse = format;
@@ -168,6 +188,7 @@
       return false;
     }
 
+    id = new_id;
     cameraThreadHandler.post(new Runnable() {
       @Override public void run() {
         switchCameraOnCameraThread();
@@ -188,7 +209,7 @@
   // compatible with the generic VideoCapturer class.
   boolean init(String deviceName) {
     Log.d(TAG, "init " + deviceName);
-    if (!initStatics())
+    if (deviceName == null || !initStatics())
       return false;
 
     boolean foundDevice = false;
@@ -197,7 +218,8 @@
       foundDevice = true;
     } else {
       for (int i = 0; i < Camera.getNumberOfCameras(); ++i) {
-        if (deviceName.equals(getDeviceName(i))) {
+        String existing_device = getDeviceName(i);
+        if (existing_device != null && deviceName.equals(existing_device)) {
           this.id = i;
           foundDevice = true;
         }
@@ -259,32 +281,42 @@
 
   // Returns a list of CaptureFormat for the camera with index id.
   static ArrayList<CaptureFormat> getSupportedFormats(int id) {
-    Camera camera;
-    camera = Camera.open(id);
-    Camera.Parameters parameters;
-    parameters = camera.getParameters();
-
     ArrayList<CaptureFormat> formatList = new ArrayList<CaptureFormat>();
-    // getSupportedPreviewFpsRange returns a sorted list.
-    List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange();
-    int[] range = {0, 0};
-    if (listFpsRange != null)
-      range = listFpsRange.get(listFpsRange.size() -1);
 
-    List<Camera.Size> supportedSizes = parameters.getSupportedPreviewSizes();
-    for (Camera.Size size : supportedSizes) {
-      if (size.width % 16 != 0) {
-        // If the width is not a multiple of 16, the frames received from the
-        // camera will have a stride != width when YV12 is used. Since we
-        // currently only support tightly packed images, we simply ignore those
-        // resolutions.
-        continue;
-      }
-      formatList.add(new CaptureFormat(size.width, size.height,
-          range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
-          range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]));
+    Camera camera;
+    try {
+      camera = Camera.open(id);
+    } catch (Exception e) {
+      Log.e(TAG, "Open camera failed on id " + id, e);
+      return formatList;
     }
-    camera.release();
+
+    try {
+      Camera.Parameters parameters;
+      parameters = camera.getParameters();
+      // getSupportedPreviewFpsRange returns a sorted list.
+      List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange();
+      int[] range = {0, 0};
+      if (listFpsRange != null)
+        range = listFpsRange.get(listFpsRange.size() -1);
+
+      List<Camera.Size> supportedSizes = parameters.getSupportedPreviewSizes();
+      for (Camera.Size size : supportedSizes) {
+        if (size.width % 16 != 0) {
+          // If the width is not a multiple of 16, the frames received from the
+          // camera will have a stride != width when YV12 is used. Since we
+          // currently only support tightly packed images, we simply ignore
+          // those resolutions.
+          continue;
+        }
+        formatList.add(new CaptureFormat(size.width, size.height,
+            range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+            range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]));
+      }
+      camera.release();
+    } catch (Exception e) {
+      Log.e(TAG, "getSupportedFormats failed on id " + id, e);
+    }
     return formatList;
   }