1. 程式人生 > >新增一個系統服務在後臺錄製視訊,並儲存到本地

新增一個系統服務在後臺錄製視訊,並儲存到本地

RecordVideoService.java

public class RecordVideoService extends Service implements SurfaceHolder.Callback {     private static final String TAG = "mylog";     private static final boolean DEBUG = true;     private Context mContext;     private static final String ACION_RECORD_START = "com.android.record.start";     private static final String ACION_RECORD_STOP = "com.android.record.stop";     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);         intentFilter.addAction(ACION_RECORD_START);         intentFilter.addAction(ACION_RECORD_STOP);         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);             } else if (ACION_RECORD_START.equals(action)) {                 mHandler.sendEmptyMessage(MSG_START_RECORD);             } else if (ACION_RECORD_STOP.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 = "mylog";     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看是出現了死鎖造成的。