31、通信卫士--拦截黑名单电话

当黑名单号码打进来时,自动挂断,并且在通话历史记录中删除该条记录。

挂断电话的操作可以通过PackageManager对象来实现,但是在android1.5以后,该方法没有暴露出来,需要通过AIDL来实现。

一个是ITelephony.aidl,从网上搜索下载:

/*
 * Copyright (C) 2007 The Android Open Source 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.internal.telephony;

import android.os.Bundle;
import java.util.List;
import android.telephony.NeighboringCellInfo;

/**
 * Interface used to interact with the phone.  Mostly this is used by the 
 * TelephonyManager class.  A few places are still using this directly.
 * Please clean them up if possible and use TelephonyManager insteadl.
 *
 * {@hide}
 */
interface ITelephony {

    /**
     * Dial a number. This doesn't place the call. It displays
     * the Dialer screen.
     * @param number the number to be dialed. If null, this
     * would display the Dialer screen with no number pre-filled.
     */
    void dial(String number);

    /**
     * Place a call to the specified number.
     * @param number the number to be called.
     */
    void call(String number);

    /**
     * If there is currently a call in progress, show the call screen.
     * The DTMF dialpad may or may not be visible initially, depending on
     * whether it was up when the user last exited the InCallScreen.
     *
     * @return true if the call screen was shown.
     */
    boolean showCallScreen();

    /**
     * Variation of showCallScreen() that also specifies whether the
     * DTMF dialpad should be initially visible when the InCallScreen
     * comes up.
     *
     * @param showDialpad if true, make the dialpad visible initially,
     *                    otherwise hide the dialpad initially.
     * @return true if the call screen was shown.
     *
     * @see showCallScreen
     */
    boolean showCallScreenWithDialpad(boolean showDialpad);

    /**
     * End call or go to the Home screen
     *
     * @return whether it hung up
     */
    boolean endCall();

    /**
     * Answer the currently-ringing call.
     *
     * If there's already a current active call, that call will be
     * automatically put on hold.  If both lines are currently in use, the
     * current active call will be ended.
     *
     * TODO: provide a flag to let the caller specify what policy to use
     * if both lines are in use.  (The current behavior is hardwired to
     * "answer incoming, end ongoing", which is how the CALL button
     * is specced to behave.)
     *
     * TODO: this should be a oneway call (especially since it's called
     * directly from the key queue thread).
     */
    void answerRingingCall();

    /**
     * Silence the ringer if an incoming call is currently ringing.
     * (If vibrating, stop the vibrator also.)
     *
     * It's safe to call this if the ringer has already been silenced, or
     * even if there's no incoming call.  (If so, this method will do nothing.)
     *
     * TODO: this should be a oneway call too (see above).
     *       (Actually *all* the methods here that return void can
     *       probably be oneway.)
     */
    void silenceRinger();

    /**
     * Check if we are in either an active or holding call
     * @return true if the phone state is OFFHOOK.
     */
    boolean isOffhook();

    /**
     * Check if an incoming phone call is ringing or call waiting.
     * @return true if the phone state is RINGING.
     */
    boolean isRinging();

    /**
     * Check if the phone is idle.
     * @return true if the phone state is IDLE.
     */
    boolean isIdle();

    /**
     * Check to see if the radio is on or not.
     * @return returns true if the radio is on.
     */
    boolean isRadioOn();

    /**
     * Check if the SIM pin lock is enabled.
     * @return true if the SIM pin lock is enabled.
     */
    boolean isSimPinEnabled();

    /**
     * Cancels the missed calls notification.
     */
    void cancelMissedCallsNotification(); 

    /**
     * Supply a pin to unlock the SIM.  Blocks until a result is determined.
     * @param pin The pin to check.
     * @return whether the operation was a success.
     */
    boolean supplyPin(String pin);

    /**
     * Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated
     * without SEND (so <code>dial</code> is not appropriate).
     * 
     * @param dialString the MMI command to be executed.
     * @return true if MMI command is executed.
     */
    boolean handlePinMmi(String dialString);

    /**
     * Toggles the radio on or off.
     */
    void toggleRadioOnOff();

    /**
     * Set the radio to on or off
     */
    boolean setRadio(boolean turnOn);

    /**
     * Request to update location information in service state
     */
    void updateServiceLocation();

    /**
     * Enable location update notifications.
     */
    void enableLocationUpdates();

    /**
     * Disable location update notifications.
     */
    void disableLocationUpdates();

    /**
     * Enable a specific APN type.
     */
    int enableApnType(String type);

    /**
     * Disable a specific APN type.
     */
    int disableApnType(String type);

    /**
     * Allow mobile data connections.
     */
    boolean enableDataConnectivity();

    /**
     * Disallow mobile data connections.
     */
    boolean disableDataConnectivity();

    /**
     * Report whether data connectivity is possible.
     */
    boolean isDataConnectivityPossible();

    Bundle getCellLocation();

    /**
     * Returns the neighboring cell information of the device.
     */
    List<NeighboringCellInfo> getNeighboringCellInfo();

     int getCallState();
     int getDataActivity();
     int getDataState();
}
此文件使用了NeighboringCellInfo.aidl:

package android.telephony;
parcelable NeighboringCellInfo;
将这两个文件按照包名目录放好。


创建服务CallFirewallService:

package com.example.mobilesafe.service;

import java.lang.reflect.Method;

import android.app.Service;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.provider.CallLog;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;

import com.android.internal.telephony.ITelephony;
import com.example.mobilesafe.db.BlackNumberDao;

public class CallFirewallService extends Service {
    public static final String TAG = "CallFirewallService";

    public static final int STOP_SMS = 1;
    public static final int STOP_CALL = 2;
    public static final int STOP_SMSCALL = 4;

    private TelephonyManager tm;
    private MyPhoneListener listener;
    private BlackNumberDao dao;

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

    /**
     * 当服务第一次被创建的时候 调用
     */
    @Override
    public void onCreate() {
        super.onCreate();
        dao = new BlackNumberDao(this);
        // 注册系统的电话状态改变的监听器.
        listener = new MyPhoneListener();
        tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
        // 系统的电话服务 就监听了 电话状态的变化,
        tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
    }

    private class MyPhoneListener extends PhoneStateListener {
        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            switch (state) {
                case TelephonyManager.CALL_STATE_RINGING:// 手机铃声正在响.
                    //starttime = System.currentTimeMillis();
                    // 判断 incomingNumber 是否是黑名单号码
                    int mode = dao.findNumberMode(incomingNumber);
                    if ((mode & STOP_CALL) != 0) {
                        // 黑名单号码
                        Log.i(TAG, "挂断电话");
                        //挂断电话
                        endcall(incomingNumber);
                    }
                    break;

                case TelephonyManager.CALL_STATE_IDLE: // 手机的空闲状态
                    break;
                case TelephonyManager.CALL_STATE_OFFHOOK:// 手机接通通话的状态
                    break;
            }

            super.onCallStateChanged(state, incomingNumber);
        }

    }

    /**
     * 取消电话状态的监听.
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
        tm.listen(listener, PhoneStateListener.LISTEN_NONE);
        listener = null;
    }

    /**
     * 显示添加黑名单号码的notification
     * @param incomingNumber
     *//*
	public void showNotification(String incomingNumber) {
		//1.创建一个notification的管理者
		String ns = Context.NOTIFICATION_SERVICE;
		NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
		//2.创建一个notification 
		int icon = R.drawable.notification;
		CharSequence tickerText = "拦截到一个一声响号码";
		long when = System.currentTimeMillis();

		Notification notification = new Notification(icon, tickerText, when);
		//3.定义notification的具体内容 和点击事件
		Context context = getApplicationContext();
		CharSequence contentTitle = "发现响一声号码";
		CharSequence contentText = "号码为:"+incomingNumber;
		notification.flags = Notification.FLAG_AUTO_CANCEL;
		
		Intent notificationIntent = new Intent(this, CallSmsSafeActivity.class);
		notificationIntent.putExtra("blacknumber", incomingNumber);
		notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT );
		notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
		
		//4.利用notification的manager 显示一个notification
		mNotificationManager.notify(0, notification);
		
	}*/

    /**
     * 挂断电话
     * 需要拷贝两个aidl文件
     * 添加权限<uses-permission android:name="android.permission.CALL_PHONE" />
     *
     * @param incomingNumber
     */
    public void endcall(String incomingNumber) {
        try {
            //使用反射获取系统的service方法
            Method method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);
            IBinder binder = (IBinder) method.invoke(null, new Object[]{TELEPHONY_SERVICE});
            //通过aidl实现方法的调用
            ITelephony telephony = ITelephony.Stub.asInterface(binder);
            telephony.endCall();//该方法是一个异步方法,他会新开启一个线程将呼入的号码存入数据库中

            //deleteCallLog(incomingNumber);

            // 注册一个内容观察者 观察uri数据的变化
            getContentResolver().registerContentObserver(CallLog.Calls.CONTENT_URI, true, new MyObserver(new Handler(), incomingNumber));

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

    /**
     * 定义自己的内容观察者 ,
     * 在构造方法里面传递 观察的号码
     *
     * @author
     */
    private class MyObserver extends ContentObserver {
        private String incomingNumber;

        public MyObserver(Handler handler, String incomingNumber) {
            super(handler);
            this.incomingNumber = incomingNumber;
        }

        /**
         * 数据库内容发生改变的时候调用的方法
         */
        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            //立即执行删除操作
            deleteCallLog(incomingNumber);
            //停止数据的观察
            getContentResolver().unregisterContentObserver(this);
        }
    }

    /**
     * 删除呼叫记录
     *
     * @param incomingNumber
     */
    private void deleteCallLog(String incomingNumber) {
        // 呼叫记录内容提供者对应的uri
        Uri uri = Uri.parse("content://call_log/calls");
        // CallLog.Calls.CONTENT_URI;
        Cursor cursor = getContentResolver().query(uri, new String[]{"_id"}, "number=?", new String[]{incomingNumber}, null);
        while (cursor.moveToNext()) {
            String id = cursor.getString(0);
            getContentResolver().delete(uri, "_id=?", new String[]{id});
        }
        cursor.close();
    }
}

其中showNotification是显示拦截的黑名单通知。

endcall注册了一个内容观察者,当拦截成功后,由于历史记录中会有此次拨打记录。

这样内容观察者就可以接收到数据的变化,进而将此次号码的通话记录删除掉。


清单文件需要添加权限:

    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
    <uses-permission android:name="android.permission.CALL_PHONE" />

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

asmcvc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值