添加一个系统服务在后台录制视频,并保存到本地

RecordVideoService.java

public class RecordVideoService extends Service implements SurfaceHolder.Callback {
    private static final String TAG = "RecordVideoService";
    private static final boolean DEBUG = true;
    private Context mContext;
    private RecordVideoServiceImp mRecordVideoServiceImp;
    private RecordVideoMode mRecordVideoMode;
    LinearLayout mLinearLayout;
    private WindowManager mWindowManager;
    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;
    private static final int MSG_START_RECORD = 1 << 0;
    private static final int MSG_STOP_RECORD = 1 << 1;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            int what = msg.what;
            if (DEBUG) Log.d(TAG, "handleMessage what : " + what);
            switch (what) {
                case MSG_START_RECORD:
                    startRecordVideo(true);
                    break;
                case MSG_STOP_RECORD:
                    startRecordVideo(false);
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;
        mRecordVideoServiceImp = new RecordVideoServiceImp();
        publish();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_SHUTDOWN);
        mContext.registerReceiver(mReceiver, intentFilter);
        initView();
        mRecordVideoMode = new RecordVideoMode(mContext, mSurfaceHolder);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mRecordVideoServiceImp;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (null != mRecordVideoMode)
            mRecordVideoMode.releaseVideoRecorder();
        unregisterReceiver(mReceiver);
        releaseView();
    }

    private void publish() {
        Log.d(TAG, "publish: " + mRecordVideoServiceImp);
        ServiceManager.addService("recordvideo", mRecordVideoServiceImp);
    }


    private final class RecordVideoServiceImp extends IRecordVideoService.Stub {
        @Override
        public void startRecordVideo() throws RemoteException {
            if (DEBUG) Log.w(TAG, "startRecordVideo");
            mHandler.sendEmptyMessage(MSG_START_RECORD);
        }

        @Override
        public void stopRecordVideo() throws RemoteException {
            if (DEBUG) Log.w(TAG, "stopRecordVideo");
            mHandler.sendEmptyMessage(MSG_STOP_RECORD);
        }
    }

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (DEBUG) Log.w(TAG, "BroadcastReceiver action : " + action);
            if (Intent.ACTION_SHUTDOWN.equals(action)) {
                mHandler.sendEmptyMessage(MSG_STOP_RECORD);
            }
        }
    };

    private void initView() {
        Log.d(TAG, "initView");

        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
        mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
        // 设置图片格式,效果为背景透明 //wmParams.format = PixelFormat.RGBA_8888;
        mLayoutParams.format = 1;
        mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mLayoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
        // 以屏幕左上角为原点,设置x、y初始值
        mLayoutParams.x = 0;
        mLayoutParams.y = 0;

        mSurfaceView = new SurfaceView(this);
        mSurfaceHolder = mSurfaceView.getHolder();
        WindowManager.LayoutParams mLayoutParamsSur = new WindowManager.LayoutParams();
        mLayoutParamsSur.width = 1;
        mLayoutParamsSur.height = 1;
        mLayoutParamsSur.alpha = 255;
        mSurfaceView.setLayoutParams(mLayoutParamsSur);
        mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        mSurfaceView.getHolder().addCallback(this);

        mLinearLayout = new LinearLayout(this);
        WindowManager.LayoutParams mLayoutParamsLin = new WindowManager.LayoutParams();
        mLayoutParamsLin.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mLayoutParamsLin.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mLinearLayout.setLayoutParams(mLayoutParamsLin);
        mLinearLayout.addView(mSurfaceView);

        mWindowManager.addView(mLinearLayout, mLayoutParams); // 创建View
    }

    private void releaseView() {
        if (mWindowManager != null) {
            mWindowManager.removeView(mLinearLayout);
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.d(TAG, "surfaceCreated");
        mSurfaceHolder = holder;
        if (null == mRecordVideoMode) {
            mRecordVideoMode = new RecordVideoMode(mContext, mSurfaceHolder);
        }
        mRecordVideoMode.updateSurfaceHoler(mSurfaceHolder);
        mHandler.sendEmptyMessage(MSG_START_RECORD);

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.d(TAG, "surfaceChanged");
        mSurfaceHolder = holder;
        if (null == mRecordVideoMode) {
            mRecordVideoMode = new RecordVideoMode(mContext, mSurfaceHolder);
        }
        mRecordVideoMode.updateSurfaceHoler(mSurfaceHolder);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.d(TAG, "surfaceDestroyed");
    }

    private Runnable mStartRecordRunable = new Runnable() {
        @Override
        public void run() {
            if (null != mRecordVideoMode) {
                mRecordVideoMode.startVideoRecording();
            }
        }
    };

    private Runnable mStopRecordRunable = new Runnable() {
        @Override
        public void run() {
            if (null != mRecordVideoMode) {
                mRecordVideoMode.releaseVideoRecorder();
            }
        }
    };

    private void startRecordVideo(boolean enable) {
        if (DEBUG) Log.d(TAG, "startRecordVideo enable : " + enable);
        if (enable) {
            mHandler.removeCallbacks(mStartRecordRunable);
            mHandler.post(mStartRecordRunable);
        } else {
            mHandler.removeCallbacks(mStopRecordRunable);
            mHandler.post(mStopRecordRunable);
        }
    }
}

---------------------------------------------------------------------------------------

RecordVideoMode.java

public class RecordVideoMode {
    private static final String TAG = "RecordVideoMode";
    private static final boolean DEBUG = true;
    private static final Long VIDEO_4G_SIZE = 4 * 1024 * 1024 * 1024L;
    private static final int NOT_FAT_FILE_SYSTEM = 0;
    private MediaRecorder mMediaRecorder;
    private Camera mCamera;
    protected boolean mIsRecorderCameraReleased = true;
    private Context mContext;
    private static final long RECORD_LOW_STORAGE_THRESHOLD = 9600000;
    private static StorageManager sStorageManager;
    private static final long UNAVAILABLE = -1L;
    private static final long PREPARING = -2L;
    private static final long UNKNOWN_SIZE = -3L;
    private static final long FULL_SDCARD = -4L;
    private static final String FOLDER_PATH = "/RecordVideo";
    private static final String OUTPUT_FORMAT = ".3gp";
    private static String sMountPoint;
    protected static final int MEDIA_RECORDER_INFO_BITRATE_ADJUSTED = 898;
    protected static final int MEDIA_RECORDER_INFO_RECORDING_SIZE = 895;
    protected static final int MEDIA_RECORDER_INFO_FPS_ADJUSTED = 897;
    protected static final int MEDIA_RECORDER_INFO_START_TIMER = 1998;
    protected static final int MEDIA_RECORDER_INFO_WRITE_SLOW = 899;
    protected static final int MEDIA_RECORDER_INFO_CAMERA_RELEASE = 1999;
    protected static final int MEDIA_RECORDER_ENCODER_ERROR = -1103;
    private static final String RECORDER_INFO_SUFFIX = "media-recorder-info=";
    SurfaceHolder mSurfaceHolder;
    private Toast mToast;
    private boolean mRelease = false;
    private boolean mStartRecording = false;
    private int mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;

    public RecordVideoMode(Context context, SurfaceHolder surfaceHolder) {
        mContext = context;
        mSurfaceHolder = surfaceHolder;
        sMountPoint = StorageManagerEx.getDefaultPath();
    }

    public void updateSurfaceHoler(SurfaceHolder surfaceHolder) {
        mSurfaceHolder = surfaceHolder;
    }

    public int startVideoRecording() {
        if (DEBUG) Log.d(TAG, "startVideoRecording");
        if (!mIsRecorderCameraReleased) {
            if (DEBUG) Log.d(TAG, "startVideoRecording videoRecord has been start");
            return -1;
        }

        if (mStartRecording) {
            if (DEBUG) Log.d(TAG, "startVideoRecording videoRecord is opening");
            showToast("videoRecord is opening");
            return -1;
        }

        mStartRecording = true;
        if (null == mCamera) {
            if (DEBUG) Log.d(TAG, "startVideoRecording mCamera open");
            try {
                int numCameras = Camera.getNumberOfCameras();
                if (numCameras > 1) {
                    mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
                } else if (numCameras == 1){
                    mCamera = Camera.open();
                } else {
                    if (DEBUG) Log.i(TAG, "startVideoRecording numberOfCameras : " + numCameras);
                    showToast("Camera not supported");
                    mStartRecording = false;
                    return -1;
                }

            } catch (Exception e) {
                Log.e(TAG, "Camera open failed, exception : " + e);
                showToast("Camera open failed");
            }
        }

        if (null == mCamera) {
            if (DEBUG) Log.d(TAG, "startVideoRecording mCamera is null");
            mStartRecording = false;
            return -1;
        }


        Camera.Parameters parameters = mCamera.getParameters();
        List<String> focusModesList = parameters.getSupportedFocusModes();

        if (focusModesList.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
        } else if (focusModesList.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
        }

        mCamera.setParameters(parameters);
        mCamera.setDisplayOrientation(90);
        if (mMediaRecorder == null) {
            mMediaRecorder = new MediaRecorder();
        }
        mCamera.stopPreview();
        mCamera.unlock();
        mMediaRecorder.setCamera(mCamera);

        CamcorderProfile mProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW);
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        mMediaRecorder.setOutputFormat(mProfile.fileFormat);
        mMediaRecorder.setVideoEncodingBitRate(mProfile.videoBitRate);
        mMediaRecorder.setVideoEncoder(mProfile.videoCodec);
        mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
        mMediaRecorder.setVideoFrameRate(mProfile.videoFrameRate);
        mMediaRecorder.setAudioEncodingBitRate(mProfile.audioBitRate);
        mMediaRecorder.setAudioChannels(mProfile.audioChannels);
        mMediaRecorder.setAudioSamplingRate(mProfile.audioSampleRate);
        mMediaRecorder.setAudioEncoder(mProfile.audioCodec);
        mMediaRecorder.setMaxDuration(0);//disables the duration limit

        /*try {
            mMediaRecorder.setMaxFileSize(getRecorderMaxSize(1));
        } catch (RuntimeException exception) {
            if (DEBUG) Slog.w(TAG, "initializeNormalRecorder()", exception);
        }*/
        mMediaRecorder.setPreviewDisplay( mSurfaceHolder.getSurface() ;//必须要设,并且不能设null,否则在mMediaRecorder.start()是抛异常
       mMediaRecorder.setOutputFile(getFileDirectory() + File.separator + createVideoName() + OUTPUT_FORMAT);
        setMediaRecorderParameters(mMediaRecorder);
        int orientation = mContext.getResources().getConfiguration().orientation;
        mMediaRecorder.setOrientationHint(getRecordingRotation(orientation, mCameraId));

        try {
            mMediaRecorder.prepare();
            mMediaRecorder.start();
            mIsRecorderCameraReleased = false;
        } catch (Exception e) {
            if (DEBUG) Log.e(TAG, "startVideoRecording exception " + e);
            showToast("MediaRecorder start failed");
            releaseVideoRecorder();
            mStartRecording = false;
            return -1;
        }
        mMediaRecorder.setOnErrorListener(mRecorderErrorListener);
        mMediaRecorder.setOnInfoListener(mRecorderOnInfoListener);
        mMediaRecorder.setOnCameraReleasedListener(mRecorderOnInfoListener);
        mStartRecording = false;

        return 1;
    }

    public void releaseVideoRecorder() {
        if (DEBUG) Log.d(TAG, "releaseMediaRecorder mIsRecorderCameraReleased : " + mIsRecorderCameraReleased);
        if (mIsRecorderCameraReleased) {
            if (DEBUG) Log.d(TAG, "releaseMediaRecorder return when camera&mediaRecorder has been released");
            return;
        }

        if (mRelease) {
            if (DEBUG) Log.d(TAG, "releaseMediaRecorder Return when MediaRecorder is being released");
            return;
        }

        if (mMediaRecorder != null) {
            mRelease = true;
            try {
                if (!mIsRecorderCameraReleased) {
                    mMediaRecorder.stop();
                }
                mMediaRecorder.reset();
                mMediaRecorder.release();
                if (null != mCamera) {
                    mCamera.release();
                }
            } catch (Exception e) {
                if (DEBUG) Log.e(TAG, "releaseMediaRecorder exception : " + e);
            }
            mIsRecorderCameraReleased = true;
            mMediaRecorder.setOnInfoListener(null);
            mMediaRecorder.setOnErrorListener(null);
            mMediaRecorder.setOnCameraReleasedListener(null);
            mRelease = false;
            mMediaRecorder = null;
            mCamera = null;
        }
        mStartRecording = false;
    }

    private long getRecorderMaxSize(long limitSize) {

        long maxFileSize = getAvailableSpace() - RECORD_LOW_STORAGE_THRESHOLD;
        if (DEBUG) Log.w(TAG, "getRecorderMaxSize spaceSize : " + maxFileSize);
        if (limitSize > 0 && limitSize < maxFileSize) {
            maxFileSize = limitSize;
        } else if (maxFileSize >= VIDEO_4G_SIZE
                && NOT_FAT_FILE_SYSTEM != getStorageCapbility()) {
            maxFileSize = VIDEO_4G_SIZE;
        }
        if (DEBUG) Log.w(TAG, "getRecorderMaxSize maxFileSize : " + maxFileSize);
        return maxFileSize;
    }

    public static long getAvailableSpace() {
        String state;
        StorageManager storageManager = getStorageManager();
        state = storageManager.getVolumeState(sMountPoint);
        // Log.d(TAG, "External storage state=" + state + ", mount point = " +
        // sMountPoint);
        if (Environment.MEDIA_CHECKING.equals(state)) {
            return PREPARING;
        }
        if (!Environment.MEDIA_MOUNTED.equals(state)) {
            return UNAVAILABLE;
        }

        File dir = new File(getFileDirectory());
        dir.mkdirs();
        boolean isDirectory = dir.isDirectory();
        boolean canWrite = dir.canWrite();
        if (!isDirectory || !canWrite) {
            if (DEBUG)
                Slog.d(TAG, "getAvailableSpace() isDirectory=" + isDirectory + ", canWrite=" + canWrite);
            return FULL_SDCARD;
        }

        try {
            // Here just use one directory to stat fs.
            StatFs stat = new StatFs(getFileDirectory());
            return stat.getAvailableBlocks() * (long) stat.getBlockSize();
        } catch (Exception e) {
            if (DEBUG) Slog.e(TAG, "Fail to access external storage", e);
        }
        return UNKNOWN_SIZE;
    }

    private static StorageManager getStorageManager() {
        if (sStorageManager == null) {
            try {
                sStorageManager = new StorageManager(null, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return sStorageManager;
    }

    public static String getFileDirectory() {
        if (TextUtils.isEmpty(sMountPoint)) {
            sMountPoint = "/sdcard";
        }
        String path = sMountPoint + FOLDER_PATH;
        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdir();
        }
        if (DEBUG) Log.d(TAG, "getFileDirectory path : " + path);
        return path;
    }

    public static Long getStorageCapbility() {
        StorageManager storageManager = getStorageManager();
        String storagePath = sMountPoint;// storageManager.getDefaultPath();
        StorageVolume[] volumes = storageManager.getVolumeList();
        int nVolume = -1;
        if (volumes != null) {
            for (int i = 0; i < volumes.length; i++) {
                if (volumes[i].getPath().equals(storagePath)) {
                    nVolume = i;
                    break;
                }
            }
            Long maxFileSize = 0l;
            if (nVolume != -1) {
                maxFileSize = volumes[nVolume].getMaxFileSize();
                if (DEBUG)
                    Slog.i(TAG, "getStorageCapbility maxFileSize = " + maxFileSize + ",nVolume = "
                            + nVolume);
            }
            return maxFileSize;
        } else {
            return 0l;
        }
    }

    private String createVideoName() {
        String nameStr;
        SimpleDateFormat mFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
        Date date = new Date();
        nameStr = mFormat.format(date);
        if (DEBUG) Log.d(TAG, "createVideoName nameStr : " + nameStr);
        return nameStr;
    }

    private MediaRecorder.OnErrorListener mRecorderErrorListener = new MediaRecorder.OnErrorListener() {
        @Override
        public void onError(MediaRecorder mr, int what, int extra) {
            if (DEBUG) Log.d(TAG, "OnErrorListener what : " + what + " extra : " + extra);
            if (MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN == what
                    || MEDIA_RECORDER_ENCODER_ERROR == extra) {
                // We may have run out of space on the SD card.
                releaseVideoRecorder();
            }
        }
    };

    private MediaRecorder.OnInfoListener mRecorderOnInfoListener = new MediaRecorder.OnInfoListener() {
        @Override
        public void onInfo(MediaRecorder mr, int what, int extra) {
            if (DEBUG) Log.d(TAG, "OnInfoListener what : " + what);
            switch (what) {
                case MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED:
                    showToast("max duration reached");
                    if (DEBUG) Log.d(TAG, "OnInfoListener MAX_DURATION_REACHED");
                    releaseVideoRecorder();
                    break;
                case MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED:
                    showToast("Size limit reached");
                    if (DEBUG) Log.d(TAG, "OnInfoListener MAX_FILESIZE_REACHED");
                    releaseVideoRecorder();
                    break;
                case MEDIA_RECORDER_INFO_CAMERA_RELEASE:
                    if (DEBUG) Log.d(TAG, "OnInfoListener CAMERA_RELEASE");
                    releaseVideoRecorder();
                    break;
                case MEDIA_RECORDER_INFO_START_TIMER:
                    break;
                case MEDIA_RECORDER_INFO_FPS_ADJUSTED:
                case MEDIA_RECORDER_INFO_BITRATE_ADJUSTED:
                    showToast("Low memory,auto change quality");
                    if (DEBUG) Log.d(TAG, "OnInfoListener auto change quality");
                    break;
                case MEDIA_RECORDER_INFO_WRITE_SLOW:
                    showToast("Low memory,auto stop recording");
                    if (DEBUG) Log.d(TAG, "OnInfoListener MEDIA_RECORDER_INFO_WRITE_SLOW");
                    releaseVideoRecorder();
                    break;
                case MEDIA_RECORDER_INFO_RECORDING_SIZE:
                    break;
                default:
                    break;
            }
        }
    };

    private void setMediaRecorderParameters(MediaRecorder mediaRecorder) {
        try {
            mediaRecorder.setParametersExtra(RECORDER_INFO_SUFFIX
                    + MEDIA_RECORDER_INFO_BITRATE_ADJUSTED);
            mediaRecorder.setParametersExtra(RECORDER_INFO_SUFFIX
                    + MEDIA_RECORDER_INFO_FPS_ADJUSTED);
            mediaRecorder.setParametersExtra(RECORDER_INFO_SUFFIX
                    + MEDIA_RECORDER_INFO_START_TIMER);
            mediaRecorder.setParametersExtra(RECORDER_INFO_SUFFIX
                    + MEDIA_RECORDER_INFO_WRITE_SLOW);
            mediaRecorder.setParametersExtra(RECORDER_INFO_SUFFIX
                    + MEDIA_RECORDER_INFO_CAMERA_RELEASE);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public void showToast(String text) {
        if (null != mToast) {
            mToast.cancel();
        }
        mToast = Toast.makeText(mContext, text, Toast.LENGTH_SHORT);
        mToast.show();
    }

    public static int getRecordingRotation(int orientation, int cameraId) {
        int rotation = 90;
        if (DEBUG) Log.d(TAG, "getRecordingRotation orientation : " + orientation);
        boolean backCamera = cameraId == Camera.CameraInfo.CAMERA_FACING_BACK;
        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
            if (backCamera) {
                rotation = 0;
            } else {
                rotation = 180;
            }
        } else if (orientation == Configuration.ORIENTATION_PORTRAIT){
            if (backCamera) {
                rotation = 90;
            } else {
                rotation = 270;
            }
        }
        if (DEBUG) Log.d(TAG, "getRecordingRotation rotation : " + rotation);
        return rotation;
    }
}

--------------------------------------------------------------------------------------------------

IRecordVideoService.aidl

interface IRecordVideoService {
    void startRecordVideo();
    void stopRecordVideo();
}

--------------------------------------------------------------------------------------------

android7.0后台添加服务到系统需要添加如下selinux权限

移植该部分功能需添加到selinux权限如下所示:
device/mediatek/common/sepolicy/basic/service.te
+type record_video_service, service_manager_type;

device/mediatek/common/sepolicy/basic/service_contexts
+recordvideo         u:object_r:record_video_service:s0

device/mediatek/common/sepolicy/basic/system_app.te
+allow system_app record_video_service:service_manager {add find};

device/mediatek/common/sepolicy/basic/system_server.te
+allow system_server record_video_service:service_manager { add find };

------------------------------------------------------------------------------------------------

将aidl添加到apk中编译Android.mk
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+            src/com/android/recordvideo/IRecordVideoService.aidl

----------------------------------------------------------------------------------------------------

AndroidManifest.xml中添加到配置如下:
+    <uses-permission android:name="android.permission.CAMERA"/>
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <uses-feature android:name="android.hardware.camera" android:required="true"/>
+    <uses-feature android:name="android.hardware.camera.autofocus" android:required="true" />
+
 

+        <service android:name="com.android.recordvideo.RecordVideoService">
+            <intent-filter>
+                <action android:name="com.android.action.RECORD_VIDEO"/>
+            </intent-filter>
+        </service>
+
+        <receiver android:name="com.android.recordvideo.RecordVideoBootReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+        </receiver>

注:添加一个系统服务,没有按照系统服务那样直接extends SystemService,是因为直接在SystemService中调用Camera.ope()时不能正常打开,从log看是出现了死锁造成的。

猜你喜欢

转载自blog.csdn.net/u010867436/article/details/83185282
今日推荐