1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.dialer.simulator.impl;
18 
19 import android.content.Context;
20 import android.graphics.SurfaceTexture;
21 import android.hardware.camera2.CameraAccessException;
22 import android.hardware.camera2.CameraCaptureSession;
23 import android.hardware.camera2.CameraCharacteristics;
24 import android.hardware.camera2.CameraDevice;
25 import android.hardware.camera2.CameraManager;
26 import android.hardware.camera2.CameraMetadata;
27 import android.hardware.camera2.CaptureRequest;
28 import android.hardware.camera2.params.StreamConfigurationMap;
29 import android.support.annotation.NonNull;
30 import android.support.annotation.Nullable;
31 import android.telecom.VideoProfile.CameraCapabilities;
32 import android.util.Size;
33 import android.view.Surface;
34 import com.android.dialer.common.Assert;
35 import com.android.dialer.common.LogUtil;
36 import java.util.Arrays;
37 
38 /**
39  * Used by the video provider to draw the local camera. The in-call UI is responsible for setting
40  * the camera (front or back) and the view to draw to. The video provider then uses this class to
41  * capture frames from the given camera and draw to the given view.
42  */
43 final class SimulatorPreviewCamera {
44   @NonNull private final Context context;
45   @NonNull private final String cameraId;
46   @NonNull private final Surface surface;
47   @Nullable private CameraDevice camera;
48   private boolean isStopped;
49 
SimulatorPreviewCamera( @onNull Context context, @NonNull String cameraId, @NonNull Surface surface)50   SimulatorPreviewCamera(
51       @NonNull Context context, @NonNull String cameraId, @NonNull Surface surface) {
52     this.context = Assert.isNotNull(context);
53     this.cameraId = Assert.isNotNull(cameraId);
54     this.surface = Assert.isNotNull(surface);
55   }
56 
startCamera()57   void startCamera() {
58     LogUtil.enterBlock("SimulatorPreviewCamera.startCamera");
59     Assert.checkState(!isStopped);
60     try {
61       context
62           .getSystemService(CameraManager.class)
63           .openCamera(cameraId, new CameraListener(), null /* handler */);
64     } catch (CameraAccessException | SecurityException e) {
65       throw Assert.createIllegalStateFailException("camera error: " + e);
66     }
67   }
68 
stopCamera()69   void stopCamera() {
70     LogUtil.enterBlock("SimulatorPreviewCamera.stopCamera");
71     isStopped = true;
72     if (camera != null) {
73       camera.close();
74       camera = null;
75     }
76   }
77 
78   @Nullable
getCameraCapabilities( @onNull Context context, @Nullable String cameraId)79   static CameraCapabilities getCameraCapabilities(
80       @NonNull Context context, @Nullable String cameraId) {
81     if (cameraId == null) {
82       LogUtil.e("SimulatorPreviewCamera.getCameraCapabilities", "null camera ID");
83       return null;
84     }
85 
86     CameraManager cameraManager = context.getSystemService(CameraManager.class);
87     CameraCharacteristics characteristics;
88     try {
89       characteristics = cameraManager.getCameraCharacteristics(cameraId);
90     } catch (CameraAccessException e) {
91       throw Assert.createIllegalStateFailException("camera error: " + e);
92     }
93 
94     StreamConfigurationMap map =
95         characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
96     Size previewSize = map.getOutputSizes(SurfaceTexture.class)[0];
97     LogUtil.i("SimulatorPreviewCamera.getCameraCapabilities", "preview size: " + previewSize);
98     return new CameraCapabilities(previewSize.getWidth(), previewSize.getHeight());
99   }
100 
101   private final class CameraListener extends CameraDevice.StateCallback {
102     @Override
onOpened(CameraDevice camera)103     public void onOpened(CameraDevice camera) {
104       LogUtil.enterBlock("SimulatorPreviewCamera.CameraListener.onOpened");
105       SimulatorPreviewCamera.this.camera = camera;
106       if (isStopped) {
107         LogUtil.i("SimulatorPreviewCamera.CameraListener.onOpened", "stopped");
108         stopCamera();
109         return;
110       }
111 
112       try {
113         camera.createCaptureSession(
114             Arrays.asList(Assert.isNotNull(surface)),
115             new CaptureSessionCallback(),
116             null /* handler */);
117       } catch (CameraAccessException e) {
118         throw Assert.createIllegalStateFailException("camera error: " + e);
119       }
120     }
121 
122     @Override
onError(CameraDevice camera, int error)123     public void onError(CameraDevice camera, int error) {
124       LogUtil.i("SimulatorPreviewCamera.CameraListener.onError", "error: " + error);
125       stopCamera();
126     }
127 
128     @Override
onDisconnected(CameraDevice camera)129     public void onDisconnected(CameraDevice camera) {
130       LogUtil.enterBlock("SimulatorPreviewCamera.CameraListener.onDisconnected");
131       stopCamera();
132     }
133 
134     @Override
onClosed(CameraDevice camera)135     public void onClosed(CameraDevice camera) {
136       LogUtil.enterBlock("SimulatorPreviewCamera.CameraListener.onCLosed");
137     }
138   }
139 
140   private final class CaptureSessionCallback extends CameraCaptureSession.StateCallback {
141     @Override
onConfigured(@onNull CameraCaptureSession session)142     public void onConfigured(@NonNull CameraCaptureSession session) {
143       LogUtil.enterBlock("SimulatorPreviewCamera.CaptureSessionCallback.onConfigured");
144 
145       if (isStopped) {
146         LogUtil.i("SimulatorPreviewCamera.CaptureSessionCallback.onConfigured", "stopped");
147         stopCamera();
148         return;
149       }
150       try {
151         CaptureRequest.Builder builder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
152         builder.addTarget(surface);
153         builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
154         session.setRepeatingRequest(
155             builder.build(), null /* captureCallback */, null /* handler */);
156       } catch (CameraAccessException e) {
157         throw Assert.createIllegalStateFailException("camera error: " + e);
158       }
159     }
160 
161     @Override
onConfigureFailed(@onNull CameraCaptureSession cameraCaptureSession)162     public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
163       LogUtil.enterBlock("SimulatorPreviewCamera.CaptureSessionCallback.onConfigureFailed");
164     }
165   }
166 }
167