Giới thiệu
Ví dụ này minh họa cách cố gắng tự động trả lời cuộc gọi đến (auto-answer) trong Android. Lưu ý quan trọng: Từ Android 8.0 (API 26), Google đã hạn chế chặt chẽ quyền can thiệp vào cuộc gọi. Ứng dụng thông thường KHÔNG THỂ tự động nghe máy mà không có quyền đặc biệt (system app, hoặc ứng dụng được người dùng đặt làm "Phone App" mặc định). Code dưới đây chỉ mang tính tham khảo học tập.
Các bước thực hiện
Bước 1: Tạo dự án mới
Mở Android Studio, chọn File → New Project.
Bước 2: Giao diện (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
xmlns:app="https://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Auto Answer Call Demo"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Bước 3: MainActivity (src/MainActivity.java)
package com.app.sample;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void acceptCall() {
Context context = this; // Sửa: dùng this thay vì null
if (Build.VERSION.SDK_INT >= 21) {
Intent answerCallIntent = new Intent(context, AcceptCallActivity.class);
answerCallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(answerCallIntent);
} else {
Intent intent = new Intent(context, AcceptCallActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(intent);
}
}
}
Bước 4: AcceptCallActivity (src/AcceptCallActivity.java)
package com.app.sample;
import android.app.Activity;
import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.view.KeyEvent;
import android.view.WindowManager;
import java.io.IOException;
import java.util.logging.Logger;
public class AcceptCallActivity extends Activity {
private static final Logger logger = Logger.getLogger(AcceptCallActivity.class.getName());
private static final String MANUFACTURER_HTC = "HTC";
private KeyguardManager keyguardManager;
private AudioManager audioManager;
private CallStateReceiver callStateReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
}
@Override
protected void onResume() {
super.onResume();
registerCallStateReceiver();
updateWindowFlags();
acceptCall();
}
@Override
protected void onPause() {
super.onPause();
if (callStateReceiver != null) {
unregisterReceiver(callStateReceiver);
callStateReceiver = null;
}
}
private void registerCallStateReceiver() {
callStateReceiver = new CallStateReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
registerReceiver(callStateReceiver, intentFilter);
}
private void updateWindowFlags() {
if (keyguardManager.inKeyguardRestrictedInputMode()) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
}
}
private void acceptCall() {
boolean broadcastConnected = MANUFACTURER_HTC.equalsIgnoreCase(Build.MANUFACTURER)
&& !audioManager.isWiredHeadsetOn();
if (broadcastConnected) {
broadcastHeadsetConnected(false);
}
try {
try {
Runtime.getRuntime().exec("input keyevent " + KeyEvent.KEYCODE_HEADSETHOOK);
} catch (IOException e) {
String enforcedPerm = "android.permission.CALL_PRIVILEGED";
Intent btnDown = new Intent(Intent.ACTION_MEDIA_BUTTON)
.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
Intent btnUp = new Intent(Intent.ACTION_MEDIA_BUTTON)
.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
sendOrderedBroadcast(btnDown, enforcedPerm);
sendOrderedBroadcast(btnUp, enforcedPerm);
}
} finally {
if (broadcastConnected) {
broadcastHeadsetConnected(false);
}
}
}
private void broadcastHeadsetConnected(boolean connected) {
Intent i = new Intent(Intent.ACTION_HEADSET_PLUG);
i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
i.putExtra("state", connected ? 1 : 0);
i.putExtra("name", "mysms");
try {
sendOrderedBroadcast(i, null);
} catch (Exception e) {
// Bỏ qua
}
}
private class CallStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
finish(); // Đóng Activity khi trạng thái cuộc gọi thay đổi
}
}
}
Bước 5: Manifest (Manifests/AndroidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.app.sample">
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".AcceptCallActivity"
android:excludeFromRecents="true"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
</application>
</manifest>
Chạy ứng dụng
Kết nối thiết bị, nhấn Run. Lưu ý: Trên Android 8+, code này sẽ KHÔNG hoạt động do hạn chế bảo mật.

Giải pháp hiện đại (Android 8.0+ / API 26+)
Để tự động trả lời cuộc gọi trên Android hiện đại, ứng dụng PHẢI:
- Được người dùng chọn làm Default Phone App (Dialer) trong Settings → Apps → Default Apps → Phone App.
- Khai báo
android:role="android.app.role.DIALER"trong Manifest. - Sử dụng
TelecomManagervàConnectionServiceđể quản lý cuộc gọi.
Ví dụ với TelecomManager (chỉ hoạt động nếu là Default Phone App)
TelecomManager telecomManager = (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
if (telecomManager != null) {
// Chỉ có thể accept call nếu app là default dialer
// telecomManager.acceptCall(callId); // Cần callId từ InCallService
}
Kết luận
Việc tự động nghe máy hiện nay chỉ khả thi cho ứng dụng hệ thống hoặc ứng dụng Dialer mặc định do người dùng chọn. Các ứng dụng thường không thể can thiệp vào cuộc gọi đến vì lý do bảo mật và quyền riêng tư.