Merge "Camera: external camera fix for CtsVerifier" into pi-dev
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/PhotoCaptureActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/PhotoCaptureActivity.java
index 85d2a18..b042fed 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/PhotoCaptureActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/PhotoCaptureActivity.java
@@ -26,6 +26,9 @@
 import android.hardware.Camera;
 import android.hardware.Camera.PictureCallback;
 import android.hardware.Camera.ShutterCallback;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraManager;
 import android.os.Bundle;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
@@ -104,6 +107,13 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.camera_fov_calibration_photo_capture);
 
+        int cameraToBeTested = 0;
+        for (int cameraId = 0; cameraId < Camera.getNumberOfCameras(); ++cameraId) {
+            if (!isExternalCamera(cameraId)) {
+                cameraToBeTested++;
+            }
+        }
+
         mPreview = (SurfaceView) findViewById(R.id.camera_fov_camera_preview);
         mSurfaceHolder = mPreview.getHolder();
         mSurfaceHolder.addCallback(this);
@@ -139,7 +149,9 @@
                 mPreviewSizeCamerasToProcess.clear();
                 mPreviewSizes =  new Size[Camera.getNumberOfCameras()];
                 for (int cameraId = 0; cameraId < Camera.getNumberOfCameras(); ++cameraId) {
-                    mPreviewSizeCamerasToProcess.add(cameraId);
+                    if (!isExternalCamera(cameraId)) {
+                        mPreviewSizeCamerasToProcess.add(cameraId);
+                    }
                 }
                 showNextDialogToChoosePreviewSize();
             }
@@ -181,9 +193,19 @@
                 }
             }
 
-        @Override
-        public void onNothingSelected(AdapterView<?> arg0) {}
+            @Override
+            public void onNothingSelected(AdapterView<?> arg0) {}
         });
+
+        if (cameraToBeTested == 0) {
+            Log.i(TAG, "No cameras needs to be tested. Setting test pass.");
+            Toast.makeText(this, "No cameras needs to be tested. Test pass.",
+                    Toast.LENGTH_LONG).show();
+
+            TestResult.setPassedResult(this, getClass().getName(),
+                    "All cameras are external, test skipped!");
+            finish();
+        }
     }
 
     @Override
@@ -198,6 +220,10 @@
             mSupportedResolutions = new ArrayList<SelectableResolution>();
             int numCameras = Camera.getNumberOfCameras();
             for (int cameraId = 0; cameraId < numCameras; ++cameraId) {
+                if (isExternalCamera(cameraId)) {
+                    continue;
+                }
+
                 Camera camera = Camera.open(cameraId);
 
                 // Get the supported picture sizes and fill the spinner.
@@ -539,4 +565,23 @@
             mPreviewOrientation = mJpegOrientation;
         }
     }
+
+    private boolean isExternalCamera(int cameraId) {
+        CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
+        try {
+            String cameraIdStr = manager.getCameraIdList()[cameraId];
+            CameraCharacteristics characteristics =
+                    manager.getCameraCharacteristics(cameraIdStr);
+
+            if (characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) ==
+                            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL) {
+                // External camera doesn't support FOV informations
+                return true;
+            }
+        } catch (CameraAccessException e) {
+            Toast.makeText(this, "Could not access camera " + cameraId +
+                    ": " + e.getMessage(), Toast.LENGTH_LONG).show();
+        }
+        return false;
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
index 9024705..8007983 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
@@ -79,7 +79,7 @@
     private final ResultReceiver mResultsReceiver = new ResultReceiver();
 
     // Initialized in onCreate
-    ArrayList<String> mNonLegacyCameraIds = null;
+    ArrayList<String> mToBeTestedCameraIds = null;
 
     // Scenes
     private static final ArrayList<String> mSceneIds = new ArrayList<String> () { {
@@ -164,7 +164,7 @@
                     return;
                 }
 
-                if (!mNonLegacyCameraIds.contains(cameraId)) {
+                if (!mToBeTestedCameraIds.contains(cameraId)) {
                     Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS");
                     return;
                 }
@@ -214,7 +214,7 @@
                         mExecutedScenes.clear();
                         mAllScenes.clear();
                         for (String scene : scenes) {
-                            for (String c : mNonLegacyCameraIds) {
+                            for (String c : mToBeTestedCameraIds) {
                                 mAllScenes.add(new ResultKey(c, scene));
                             }
                         }
@@ -333,20 +333,22 @@
         CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
         try {
             String[] cameraIds = manager.getCameraIdList();
-            mNonLegacyCameraIds = new ArrayList<String>();
-            boolean allCamerasAreLegacy = true;
+            mToBeTestedCameraIds = new ArrayList<String>();
             for (String id : cameraIds) {
                 CameraCharacteristics characteristics = manager.getCameraCharacteristics(id);
-                if (characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
-                        != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
-                    mNonLegacyCameraIds.add(id);
-                    allCamerasAreLegacy = false;
+                int hwLevel = characteristics.get(
+                        CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+                if (hwLevel
+                        != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY &&
+                        hwLevel
+                        != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL) {
+                    mToBeTestedCameraIds.add(id);
                 }
             }
-            if (allCamerasAreLegacy) {
+            if (mToBeTestedCameraIds.size() == 0) {
                 showToast(R.string.all_legacy_devices);
                 ItsTestActivity.this.getReportLog().setSummary(
-                        "PASS: all cameras on this device are LEGACY"
+                        "PASS: all cameras on this device are LEGACY or EXTERNAL"
                         , 1.0, ResultType.NEUTRAL, ResultUnit.NONE);
                 setTestResultAndFinish(true);
             }
@@ -375,7 +377,7 @@
     }
 
     protected void setupItsTests(ArrayTestListAdapter adapter) {
-        for (String cam : mNonLegacyCameraIds) {
+        for (String cam : mToBeTestedCameraIds) {
             for (String scene : mSceneIds) {
                 adapter.add(new DialogTestListItem(this,
                 testTitle(cam, scene),
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
index 0a397e8..1f17cdf 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
@@ -16,12 +16,16 @@
 package com.android.cts.verifier.camera.video;
 
 import android.app.AlertDialog;
+import android.content.Context;
 import android.content.DialogInterface;
 import android.graphics.Matrix;
 import android.graphics.SurfaceTexture;
 import android.hardware.Camera;
 import android.hardware.Camera.CameraInfo;
 import android.hardware.Camera.Size;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraManager;
 import android.media.CamcorderProfile;
 import android.media.MediaPlayer;
 import android.media.MediaRecorder;
@@ -38,6 +42,7 @@
 import android.widget.ImageButton;
 import android.widget.Spinner;
 import android.widget.TextView;
+import android.widget.Toast;
 import android.widget.VideoView;
 
 import com.android.cts.verifier.PassFailButtons;
@@ -79,6 +84,7 @@
 
     private int mCurrentCameraId = -1;
     private Camera mCamera;
+    private boolean mIsExternalCamera;
 
     private MediaRecorder mMediaRecorder;
 
@@ -141,6 +147,19 @@
         return mediaFile;
     }
 
+    private static final int BIT_RATE_720P = 8000000;
+    private static final int BIT_RATE_MIN = 64000;
+    private static final int BIT_RATE_MAX = BIT_RATE_720P;
+
+    private int getVideoBitRate(Camera.Size sz) {
+        int rate = BIT_RATE_720P;
+        float scaleFactor = sz.height * sz.width / (float)(1280 * 720);
+        rate = (int)(rate * scaleFactor);
+
+        // Clamp to the MIN, MAX range.
+        return Math.max(BIT_RATE_MIN, Math.min(BIT_RATE_MAX, rate));
+    }
+
     private boolean prepareVideoRecorder() {
 
         mMediaRecorder = new MediaRecorder();
@@ -154,7 +173,39 @@
         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
 
         // Step 3: set a CamcorderProfile
-        mMediaRecorder.setProfile(CamcorderProfile.get(mCurrentCameraId, mCurrentVideoSizeId));
+        if (mIsExternalCamera) {
+            Camera.Size recordSize = null;
+            switch (mCurrentVideoSizeId) {
+                case CamcorderProfile.QUALITY_QCIF:
+                    recordSize = mCamera.new Size(176, 144);
+                break;
+                case CamcorderProfile.QUALITY_QVGA:
+                    recordSize = mCamera.new Size(320, 240);
+                break;
+                case CamcorderProfile.QUALITY_CIF:
+                    recordSize = mCamera.new Size(352, 288);
+                break;
+                case CamcorderProfile.QUALITY_480P:
+                    recordSize = mCamera.new Size(720, 480);
+                break;
+                case CamcorderProfile.QUALITY_720P:
+                    recordSize = mCamera.new Size(1280, 720);
+                break;
+                default:
+                    String msg = "Unknown CamcorderProfile: " + mCurrentVideoSizeId;
+                    Log.e(TAG, msg);
+                    releaseMediaRecorder();
+                    throw new AssertionError(msg);
+            }
+
+            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
+            mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
+            mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
+            mMediaRecorder.setVideoEncodingBitRate(getVideoBitRate(recordSize));
+            mMediaRecorder.setVideoSize(recordSize.width, recordSize.height);
+        } else {
+            mMediaRecorder.setProfile(CamcorderProfile.get(mCurrentCameraId, mCurrentVideoSizeId));
+        }
 
         // Step 4: set output file
         outputVideoFile = getOutputMediaFile(MEDIA_TYPE_VIDEO);
@@ -450,15 +501,28 @@
         int[] qualityArray = {
                 CamcorderProfile.QUALITY_LOW,
                 CamcorderProfile.QUALITY_HIGH,
-                CamcorderProfile.QUALITY_QCIF,
-                CamcorderProfile.QUALITY_QVGA,
-                CamcorderProfile.QUALITY_CIF,
-                CamcorderProfile.QUALITY_480P,
-                CamcorderProfile.QUALITY_720P,
-                CamcorderProfile.QUALITY_1080P,
+                CamcorderProfile.QUALITY_QCIF,  // 176x144
+                CamcorderProfile.QUALITY_QVGA,  // 320x240
+                CamcorderProfile.QUALITY_CIF,   // 352x288
+                CamcorderProfile.QUALITY_480P,  // 720x480
+                CamcorderProfile.QUALITY_720P,  // 1280x720
+                CamcorderProfile.QUALITY_1080P, // 1920x1080 or 1920x1088
                 CamcorderProfile.QUALITY_2160P
         };
 
+        final Camera.Size skip = mCamera.new Size(-1, -1);
+        Camera.Size[] videoSizeArray = {
+                skip,
+                skip,
+                mCamera.new Size(176, 144),
+                mCamera.new Size(320, 240),
+                mCamera.new Size(352, 288),
+                mCamera.new Size(720, 480),
+                mCamera.new Size(1280, 720),
+                skip,
+                skip
+        };
+
         String[] nameArray = {
                 "LOW",
                 "HIGH",
@@ -474,10 +538,23 @@
         ArrayList<VideoSizeNamePair> availableSizes =
                 new ArrayList<VideoSizeNamePair> ();
 
+        Camera.Parameters p = mCamera.getParameters();
+        List<Camera.Size> supportedVideoSizes = p.getSupportedVideoSizes();
         for (int i = 0; i < qualityArray.length; i++) {
-            if (CamcorderProfile.hasProfile(cameraId, qualityArray[i])) {
-                VideoSizeNamePair pair = new VideoSizeNamePair(qualityArray[i], nameArray[i]);
-                availableSizes.add(pair);
+            if (mIsExternalCamera) {
+                Camera.Size videoSz = videoSizeArray[i];
+                if (videoSz.equals(skip)) {
+                    continue;
+                }
+                if (supportedVideoSizes.contains(videoSz)) {
+                    VideoSizeNamePair pair = new VideoSizeNamePair(qualityArray[i], nameArray[i]);
+                    availableSizes.add(pair);
+                }
+            } else {
+                if (CamcorderProfile.hasProfile(cameraId, qualityArray[i])) {
+                    VideoSizeNamePair pair = new VideoSizeNamePair(qualityArray[i], nameArray[i]);
+                    availableSizes.add(pair);
+                }
             }
         }
         return availableSizes;
@@ -511,12 +588,38 @@
                 CamcorderProfile.QUALITY_2160P
         };
 
+        final Camera.Size skip = mCamera.new Size(-1, -1);
+        Camera.Size[] videoSizeArray = {
+                skip,
+                skip,
+                mCamera.new Size(176, 144),
+                mCamera.new Size(320, 240),
+                mCamera.new Size(352, 288),
+                mCamera.new Size(720, 480),
+                mCamera.new Size(1280, 720),
+                skip,
+                skip
+        };
+
         ArrayList<ResolutionQuality> qualityList = new ArrayList<ResolutionQuality>();
+        Camera.Parameters p = mCamera.getParameters();
+        List<Camera.Size> supportedVideoSizes = p.getSupportedVideoSizes();
         for (int i = 0; i < possibleQuality.length; i++) {
-            if (CamcorderProfile.hasProfile(cameraId, possibleQuality[i])) {
-                CamcorderProfile profile = CamcorderProfile.get(cameraId, possibleQuality[i]);
-                qualityList.add(new ResolutionQuality(possibleQuality[i],
-                        profile.videoFrameWidth, profile.videoFrameHeight));
+            if (mIsExternalCamera) {
+                Camera.Size videoSz = videoSizeArray[i];
+                if (videoSz.equals(skip)) {
+                    continue;
+                }
+                if (supportedVideoSizes.contains(videoSz)) {
+                    qualityList.add(new ResolutionQuality(possibleQuality[i],
+                            videoSz.width, videoSz.height));
+                }
+            } else {
+                if (CamcorderProfile.hasProfile(cameraId, possibleQuality[i])) {
+                    CamcorderProfile profile = CamcorderProfile.get(cameraId, possibleQuality[i]);
+                    qualityList.add(new ResolutionQuality(possibleQuality[i],
+                            profile.videoFrameWidth, profile.videoFrameHeight));
+                }
             }
         }
 
@@ -595,6 +698,7 @@
             failTest("camera not available" + e.getMessage());
             return;
         }
+        mIsExternalCamera = isExternalCamera(id);
 
         Camera.Parameters p = mCamera.getParameters();
         if (VERBOSE) {
@@ -767,4 +871,23 @@
                 .show();
     }
 
+    private boolean isExternalCamera(int cameraId) {
+        CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
+        try {
+            String cameraIdStr = manager.getCameraIdList()[cameraId];
+            CameraCharacteristics characteristics =
+                    manager.getCameraCharacteristics(cameraIdStr);
+
+            if (characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) ==
+                            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL) {
+                // External camera doesn't support FOV informations
+                return true;
+            }
+        } catch (CameraAccessException e) {
+            Toast.makeText(this, "Could not access camera " + cameraId +
+                    ": " + e.getMessage(), Toast.LENGTH_LONG).show();
+        }
+        return false;
+    }
+
 }