先看實現(xiàn)后效果
這是來電效果如下:

這是接聽來電效果如下:

所有來電都會自動在通話記錄里留下數(shù)據(jù)

實際效果可以在沒有SIM卡的機器上也能接收來電,實現(xiàn)思路來源于系統(tǒng)源碼 packages/services/Telecomm/testapps, 這個模塊是用來測試通話功能的,基于這個測試代碼我們稍加修改即可達到以上效果.以下大部分操作需要在有完整的系統(tǒng)源碼和機器的root權限的前提下執(zhí)行.
1. 單獨編譯testapps,即可在out目錄下得到TelecomTestApps.apk
mmm packages/services/Telecomm/testapps
2. 將TelecomTestApps.apk push到機器的/system/app目錄下,該操作需要root權限以及掛載system目錄,然后再重啟機器.
adb push TelecomTestApps.apk /system/app
3. 重啟機器后在桌面可以看到多出很多APP,我們只需要啟動其中的Test Connection Service App即可.
該界面啟動后添加了兩個通知交互UI,效果如下:

展開第一個通知可以選擇添加PhoneAccount,這一步也是必須.
展開第二個通知可以選擇Add Call,點擊后即可在上方顯示一個虛擬的來電.
在操作第一步添加PhoneAccount的時候可能在logcat上出現(xiàn)如下錯誤:

這說明系統(tǒng)中缺少android.software.connectionservice feature,在系統(tǒng)源碼中找到frameworks/native/data/etc/android.software.connectionservice.xml文件,導入到機器的/vendor/etc/permissions目錄下然后重啟下系統(tǒng)即可.
4.修改testapps2源碼,以契合自己的業(yè)務需求.
目前業(yè)務需求是
1.不顯示任何多余的通知
2.在桌面不顯示任何testapps相關的APP
3.使用廣播來啟動來電
我們先在packages/services/Telecomm目錄下復制一份testapps代碼命名為testapps2,z這樣可以保留一份原始的系統(tǒng)源碼.
我們現(xiàn)在查看下程序入口com.android.server.telecom.testapps.TestCallActivity內容.
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Intent intent = getIntent();
final String action = intent != null ? intent.getAction() : null;
final Uri data = intent != null ? intent.getData() : null;
if (ACTION_NEW_INCOMING_CALL.equals(action) && data != null) {
CallNotificationReceiver.sendIncomingCallIntent(this, data,
VideoProfile.STATE_AUDIO_ONLY);
} else if (ACTION_NEW_UNKNOWN_CALL.equals(action) && data != null) {
CallNotificationReceiver.addNewUnknownCall(this, data, intent.getExtras());
} else if (ACTION_HANGUP_CALLS.equals(action)) {
CallNotificationReceiver.hangupCalls(this);
} else if (ACTION_RTT_CALL.equals(action)) {
CallNotificationReceiver.sendIncomingRttCallIntent(
this, data, VideoProfile.STATE_AUDIO_ONLY);
} else if (ACTION_SEND_UPGRADE_REQUEST.equals(action)) {
CallNotificationReceiver.sendUpgradeRequest(this, data);
} else if (ACTION_REMOTE_RTT_UPGRADE.equals(action)) {
CallNotificationReceiver.remoteRttUpgrade(this);
} else {
CallServiceNotifier.getInstance().updateNotification(this);
}
finish();
}
這部分代碼是顯示通知以及響應通知回調,這塊不用注重查看.關鍵位置在com.android.server.telecom.testapps.CallNotificationReceiver中,查看下核心代碼:
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_CALL_SERVICE_EXIT.equals(action)) {
CallServiceNotifier.getInstance().cancelNotifications(context);
} else if (ACTION_REGISTER_PHONE_ACCOUNT.equals(action)) {
CallServiceNotifier.getInstance().registerPhoneAccount(context);
} else if (ACTION_SHOW_ALL_PHONE_ACCOUNTS.equals(action)) {
CallServiceNotifier.getInstance().showAllPhoneAccounts(context);
} else if (ACTION_ONE_WAY_VIDEO_CALL.equals(action)) {
sendIncomingCallIntent(context, null, VideoProfile.STATE_RX_ENABLED);
} else if (ACTION_TWO_WAY_VIDEO_CALL.equals(action)) {
sendIncomingCallIntent(context, null, VideoProfile.STATE_BIDIRECTIONAL);
} else if (ACTION_RTT_CALL.equals(action)) {
sendIncomingRttCallIntent(context, null, VideoProfile.STATE_AUDIO_ONLY);
} else if (ACTION_AUDIO_CALL.equals(action)) {
sendIncomingCallIntent(context, null, VideoProfile.STATE_AUDIO_ONLY);
}
}
public static void sendIncomingCallIntent(Context context, Uri handle, int videoState) {
PhoneAccountHandle phoneAccount = new PhoneAccountHandle(
new ComponentName(context, TestConnectionService.class),
CallServiceNotifier.SIM_SUBSCRIPTION_ID);
// For the purposes of testing, indicate whether the incoming call is a video call by
// stashing an indicator in the EXTRA_INCOMING_CALL_EXTRAS.
Bundle extras = new Bundle();
extras.putInt(TestConnectionService.EXTRA_START_VIDEO_STATE, videoState);
if (handle != null) {
extras.putParcelable(TestConnectionService.EXTRA_HANDLE, handle);
}
TelecomManager.from(context).addNewIncomingCall(phoneAccount, extras);
}
以上只貼出了SIM卡的語音通話響應,其他類型可自行研究,由以上代碼可知添加PhoneAccount和響應Add Call是由
CallServiceNotifier.getInstance().registerPhoneAccount(context);
sendIncomingCallIntent(context, null, VideoProfile.STATE_AUDIO_ONLY);
這兩個函數(shù)實現(xiàn)的,那么事情就簡單了,我們可以在testapps2中添加一個廣播接收器MyBroadcastReceiver.java
用于接收我們自定義的廣播,當接受到自定義廣播后調用以上兩個函數(shù)即可觸發(fā)來電顯示.
關于如何處理去掉多余的桌面APP和移除通知,我們只需將APP的清單文件中包含<action android:name="android.intent.action.MAIN"/>的組件全部注釋掉即可.
如何在系統(tǒng)源碼全量編譯時自動將我們修改的APP編譯進鏡像中:
1.修改testapps/android.bp 中的android_test { .... } 為 android_app { .... },并且注釋掉APP清單文件中的<uses-library android:name="android.test.runner"/>,修改android_app {} 中的模塊名稱為TelecomTestApps2,這是為了防止編譯時與之前備份的testapp出現(xiàn)同名模塊沖突.
2.在device/廠商/廠商.mk中添加如下
PRODUCT_PACKAGES += \
TelecomTestApps2
PRODUCT_COPY_FILES += \
frameworks/native/data/etc/android.software.connectionservice.xml:vendor/etc/permissions/android.software.connectionservice.xml
完成以上步驟后進行全量編譯即可.