summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/callrecord/impl/CallRecorderService.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/dialer/callrecord/impl/CallRecorderService.java')
-rw-r--r--java/com/android/dialer/callrecord/impl/CallRecorderService.java222
1 files changed, 222 insertions, 0 deletions
diff --git a/java/com/android/dialer/callrecord/impl/CallRecorderService.java b/java/com/android/dialer/callrecord/impl/CallRecorderService.java
new file mode 100644
index 000000000..298e8ade0
--- /dev/null
+++ b/java/com/android/dialer/callrecord/impl/CallRecorderService.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dialer.callrecord.impl;
+
+import android.app.Service;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.media.MediaRecorder;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.dialer.callrecord.CallRecording;
+import com.android.dialer.callrecord.ICallRecorderService;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import com.android.dialer.R;
+
+public class CallRecorderService extends Service {
+ private static final String TAG = "CallRecorderService";
+ private static final boolean DBG = false;
+
+ private MediaRecorder mMediaRecorder = null;
+ private CallRecording mCurrentRecording = null;
+
+ private SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyMMdd_HHmmssSSS");
+
+ private final ICallRecorderService.Stub mBinder = new ICallRecorderService.Stub() {
+ @Override
+ public CallRecording stopRecording() {
+ return stopRecordingInternal();
+ }
+
+ @Override
+ public boolean startRecording(String phoneNumber, long creationTime) throws RemoteException {
+ return startRecordingInternal(phoneNumber, creationTime);
+ }
+
+ @Override
+ public boolean isRecording() throws RemoteException {
+ return mMediaRecorder != null;
+ }
+
+ @Override
+ public CallRecording getActiveRecording() throws RemoteException {
+ return mCurrentRecording;
+ }
+ };
+
+ @Override
+ public void onCreate() {
+ if (DBG) Log.d(TAG, "Creating CallRecorderService");
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ private int getAudioSource() {
+ return getResources().getInteger(R.integer.call_recording_audio_source);
+ }
+
+ private int getAudioFormatChoice() {
+ // This replicates PreferenceManager.getDefaultSharedPreferences, except
+ // that we need multi process preferences, as the pref is written in a separate
+ // process (com.android.dialer vs. com.android.incallui)
+ final String prefName = getPackageName() + "_preferences";
+ final SharedPreferences prefs = getSharedPreferences(prefName, MODE_MULTI_PROCESS);
+
+ try {
+ String value = prefs.getString(getString(R.string.call_recording_format_key), null);
+ if (value != null) {
+ return Integer.parseInt(value);
+ }
+ } catch (NumberFormatException e) {
+ // ignore and fall through
+ }
+ return 0;
+ }
+
+ private synchronized boolean startRecordingInternal(String phoneNumber, long creationTime) {
+ if (mMediaRecorder != null) {
+ if (DBG) {
+ Log.d(TAG, "Start called with recording in progress, stopping current recording");
+ }
+ stopRecordingInternal();
+ }
+
+ if (checkSelfPermission(android.Manifest.permission.RECORD_AUDIO)
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.w(TAG, "Record audio permission not granted, can't record call");
+ return false;
+ }
+
+ if (DBG) Log.d(TAG, "Starting recording");
+
+ mMediaRecorder = new MediaRecorder();
+ try {
+ int audioSource = getAudioSource();
+ int formatChoice = getAudioFormatChoice();
+ if (DBG) Log.d(TAG, "Creating media recorder with audio source " + audioSource);
+ mMediaRecorder.setAudioSource(audioSource);
+ mMediaRecorder.setOutputFormat(formatChoice == 0
+ ? MediaRecorder.OutputFormat.AMR_WB : MediaRecorder.OutputFormat.MPEG_4);
+ mMediaRecorder.setAudioEncoder(formatChoice == 0
+ ? MediaRecorder.AudioEncoder.AMR_WB : MediaRecorder.AudioEncoder.AAC);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Error initializing media recorder", e);
+ mMediaRecorder.reset();
+ mMediaRecorder.release();
+ mMediaRecorder = null;
+ return false;
+ }
+
+ String fileName = generateFilename(phoneNumber);
+ Uri uri = getContentResolver().insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ CallRecording.generateMediaInsertValues(fileName, creationTime));
+
+ try {
+ ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, "w");
+ if (pfd == null) {
+ throw new IOException("Opening file for URI " + uri + " failed");
+ }
+ mMediaRecorder.setOutputFile(pfd.getFileDescriptor());
+ mMediaRecorder.prepare();
+ mMediaRecorder.start();
+
+ long mediaId = Long.parseLong(uri.getLastPathSegment());
+ mCurrentRecording = new CallRecording(phoneNumber, creationTime,
+ fileName, System.currentTimeMillis(), mediaId);
+ return true;
+ } catch (IOException | IllegalStateException e) {
+ Log.w(TAG, "Could not start recording", e);
+ getContentResolver().delete(uri, null, null);
+ } catch (RuntimeException e) {
+ getContentResolver().delete(uri, null, null);
+ // only catch exceptions thrown by the MediaRecorder JNI code
+ if (e.getMessage().indexOf("start failed") >= 0) {
+ Log.w(TAG, "Could not start recording", e);
+ } else {
+ throw e;
+ }
+ }
+
+ mMediaRecorder.reset();
+ mMediaRecorder.release();
+ mMediaRecorder = null;
+
+ return false;
+ }
+
+ private synchronized CallRecording stopRecordingInternal() {
+ CallRecording recording = mCurrentRecording;
+ if (DBG) Log.d(TAG, "Stopping current recording");
+ if (mMediaRecorder != null) {
+ try {
+ mMediaRecorder.stop();
+ mMediaRecorder.reset();
+ mMediaRecorder.release();
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Exception closing media recorder", e);
+ }
+
+ Uri uri = ContentUris.withAppendedId(
+ MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, mCurrentRecording.mediaId);
+ getContentResolver().update(uri, CallRecording.generateCompletedValues(), null, null);
+
+ mMediaRecorder = null;
+ mCurrentRecording = null;
+ }
+ return recording;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (DBG) Log.d(TAG, "Destroying CallRecorderService");
+ }
+
+ private String generateFilename(String number) {
+ String timestamp = DATE_FORMAT.format(new Date());
+
+ if (TextUtils.isEmpty(number)) {
+ number = "unknown";
+ }
+
+ int formatChoice = getAudioFormatChoice();
+ String extension = formatChoice == 0 ? ".amr" : ".m4a";
+ return number + "_" + timestamp + extension;
+ }
+
+ public static boolean isEnabled(Context context) {
+ return context.getResources().getBoolean(R.bool.call_recording_enabled);
+ }
+}