gpt4 book ai didi

android - 如何在 Android 5.0 (Lollipop) 中以编程方式接听来电?

转载 作者:行者123 更新时间:2023-12-01 23:58:40 25 4
gpt4 key购买 nike

当我尝试为来电创建自定义屏幕时,我正在尝试以编程方式接听来电。我正在使用以下代码,但它在 Android 5.0 中不起作用。

// Simulate a press of the headset button to pick up the call
Intent buttonDown = new Intent(Intent.ACTION_MEDIA_BUTTON);
buttonDown.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(buttonDown, "android.permission.CALL_PRIVILEGED");

// froyo and beyond trigger on buttonUp instead of buttonDown
Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);
buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");

最佳答案

使用 Android 8.0 Oreo 更新

尽管这个问题最初是为了 Android L 支持而提出的,但人们似乎仍然在回答这个问题,因此值得描述 Android 8.0 Oreo 中引入的改进。下面仍然描述向后兼容的方法。

发生了什么变化?

Android 8.0 Oreo 开头, PHONE permission group还包含 ANSWER_PHONE_CALLS permission .顾名思义,持有它允许您的应用程序通过适当的 API 调用以编程方式接受传入调用,而无需使用反射或模拟用户对系统进行任何黑客攻击。

我们如何利用这种变化?

您应该check system version at runtime如果您支持较旧的 Android 版本,以便您可以封装这个新的 API 调用,同时保持对那些较旧的 Android 版本的支持。您应该关注 requesting permissions at run time在运行时获得新的权限,这是较新的 Android 版本的标准。

获得许可后,您的应用程序只需调用 TelecomManager's acceptRingingCall方法。一个基本的调用如下所示:

TelecomManager tm = (TelecomManager) mContext
.getSystemService(Context.TELECOM_SERVICE);

if (tm == null) {
// whether you want to handle this is up to you really
throw new NullPointerException("tm == null");
}

tm.acceptRingingCall();

方法一:TelephonyManager.answerRingingCall()

当您可以无限制地控制设备时。

这是什么?

TelephonyManager.answerRingingCall() 是一个隐藏的内部方法。它作为 ITelephony.answerRingingCall() 的桥梁,已经在互联网上进行了讨论,一开始似乎很有希望。是 不是 可在 4.4.2_r1 上获得因为它仅在提交 83da75d 中引入适用于 Android 4.4 KitKat ( line 1537 on 4.4.3_r1 ) 及更高版本在提交 f1e1e77 中“重新引入” Lollipop ( line 3138 on 5.0.0_r1 ) 由于 Git 树的结构。这意味着除非你只支持 Lollipop 设备,鉴于目前它的市场份额很小,这可能是一个错误的决定,否则如果沿着这条路线走,你仍然需要提供后备方法。

我们将如何使用它?

由于该方法对 SDK 应用程序使用是隐藏的,您需要使用 reflection在运行时动态检查和使用该方法。如果你对反射不熟悉,可以快速阅读 What is reflection, and why is it useful? .您还可以在 Trail: The Reflection API 上更深入地了解细节。如果你有兴趣这样做。

这在代码中看起来如何?
// set the logging tag constant; you probably want to change this
final String LOG_TAG = "TelephonyAnswer";

TelephonyManager tm = (TelephonyManager) mContext
.getSystemService(Context.TELEPHONY_SERVICE);

try {
if (tm == null) {
// this will be easier for debugging later on
throw new NullPointerException("tm == null");
}

// do reflection magic
tm.getClass().getMethod("answerRingingCall").invoke(tm);
} catch (Exception e) {
// we catch it all as the following things could happen:
// NoSuchMethodException, if the answerRingingCall() is missing
// SecurityException, if the security manager is not happy
// IllegalAccessException, if the method is not accessible
// IllegalArgumentException, if the method expected other arguments
// InvocationTargetException, if the method threw itself
// NullPointerException, if something was a null value along the way
// ExceptionInInitializerError, if initialization failed
// something more crazy, if anything else breaks

// TODO decide how to handle this state
// you probably want to set some failure state/go to fallback
Log.e(LOG_TAG, "Unable to use the Telephony Manager directly.", e);
}

这真是太好了!

实际上,有一个小问题。这个方法应该是全功能的,但是安全管理器希望调用者保持 android.permission.MODIFY_PHONE_STATE .
此权限仅属于系统部分记录功能的领域,因为预计 3rd 方不会接触它(正如您从文档中看到的那样)。您可以尝试添加 <uses-permission> 为它,但这不会有什么好处,因为此权限的保护级别是签名|系统( see line 1201 of core/AndroidManifest on 5.0.0_r1 )。

您可以阅读 Issue 34785: Update android:protectionLevel documentation它是在 2012 年创建的,目的是为了看到我们缺少有关特定“管道语法”的详细信息,但是从周围的实验来看,它似乎必须用作“AND”,这意味着必须满足所有指定的标志才能获得许可授予。在该假设下工作,这意味着您必须拥有您的应用程序:
  • 作为系统应用程序安装。

    这应该没问题,可以通过要求用户在恢复中使用 ZIP 进行安装来实现,例如在尚未打包的自定义 ROM 上生根或安装 Google 应用程序时。
  • 使用与框架/基础(又名系统)(又名 ROM)相同的签名进行签名。

    这就是问题出现的地方。为此,您需要掌握用于签署框架/基础的 key 。您不仅需要访问 Google 的 Nexus 工厂镜像 key ,还必须访问所有其他 OEM 和 ROM 开发人员的 key 。这似乎不合理,因此您可以通过制作自定义 ROM 并要求您的用户切换到它(这可能很难)或通过找到可以绕过权限保护级别的漏洞来使用系统 key 对您的应用程序进行签名(这也可能很难)。

  • 此外,此行为似乎与 Issue 34792: Android Jelly Bean / 4.1: android.permission.READ_LOGS no longer works 有关。它使用相同的保护级别以及未记录的开发标志。

    使用 TelephonyManager 听起来不错,但除非您获得适当的许可,否则将无法工作,这在实践中并不容易。

    以其他方式使用 TelephonyManager 怎么样?

    可悲的是,它似乎要求您持有 android.permission.MODIFY_PHONE_STATE使用很酷的工具,这反过来意味着您将很难访问这些方法。

    方法二:服务调用SERVICE CODE

    什么时候可以测试在设备上运行的构建是否可以使用指定的代码。

    在无法与 TelephonyManager 交互的情况下,也有可能通过 service 与服务交互。可执行。

    这是如何工作的?

    它相当简单,但关于这条路线的文档比其他路线更少。我们确信可执行文件有两个参数——服务名称和代码。
  • 服务名称 我们要使用的是电话。

    这可以通过运行 service list 看到.
  • 代码我们要使用的似乎是 6 但现在似乎是 5。

    看起来它已经基于 IBinder.FIRST_CALL_TRANSACTION + 5 现在很多版本(从 1.5_r44.4.4_r1 )但在本地测试期间,代码 5 可以接听来电。由于 Lollipo 是一个全面的大规模更新,因此内部结构也发生了变化,这是可以理解的。

  • 结果是命令 service call phone 5 .

    我们如何以编程方式利用它?

    java

    以下代码是一个粗略的实现,用作概念证明。如果你真的想继续使用这种方法,你可能想查看 guidelines for problem-free su usage并可能切换到更完善的 libsuperuser来自 Chainfire .
    try {
    Process proc = Runtime.getRuntime().exec("su");
    DataOutputStream os = new DataOutputStream(proc.getOutputStream());

    os.writeBytes("service call phone 5\n");
    os.flush();

    os.writeBytes("exit\n");
    os.flush();

    if (proc.waitFor() == 255) {
    // TODO handle being declined root access
    // 255 is the standard code for being declined root for SU
    }
    } catch (IOException e) {
    // TODO handle I/O going wrong
    // this probably means that the device isn't rooted
    } catch (InterruptedException e) {
    // don't swallow interruptions
    Thread.currentThread().interrupt();
    }

    舱单
    <!-- Inform the user we want them root accesses. -->
    <uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>

    这真的需要root访问权限吗?

    可悲的是,似乎是这样。您可以尝试使用 Runtime.exec在它上面,但我无法在这条路线上获得任何运气。

    这有多稳定?

    我很高兴你问。由于没有记录在案,这可能会跨越各种版本,如上面看似代码差异所示。服务名称可能应该在各种版本中保持不变,但就我们所知,代码值可能会在同一版本的多个版本中发生变化(例如,OEM 皮肤的内部修改),从而破坏所使用的方法。因此值得一提的是,测试是在 Nexus 4 (mako/occam) 上进行的。我个人建议你不要使用这种方法,但由于我找不到更稳定的方法,我相信这是最好的方法。

    原始方法:耳机键码 Intent

    在您必须安顿下来的时候。

    以下部分受到 this answer 的强烈影响来自 Riley C .

    原始问题中发布的模拟耳机 Intent 方法似乎正如人们所期望的那样广播,但它似乎并没有实现接听电话的目标。虽然似乎有代码来处理这些 Intent ,但它们根本没有被关心,这意味着必须有某种新的对策来对抗这种方法。日志也没有显示任何有趣的内容,我个人不认为为此挖掘 Android 源代码是值得的,因为 Google 可能会引入一些细微的更改,这很容易破坏所使用的方法。

    我们现在有什么可以做的吗?

    可以使用输入可执行文件一致地重现该行为。它接受一个 keycode 参数,我们只需传入 KeyEvent.KEYCODE_HEADSETHOOK .该方法甚至不需要 root 访问权限,使其适用于普通大众的常见用例,但该方法有一个小缺点 - 耳机按钮按下事件无法指定为需要许可,这意味着它像真实的一样工作按钮按下并在整个链中冒泡,这反过来意味着您必须谨慎选择何时模拟按钮按下,因为它可能,例如,如果没有其他更高优先级的人准备好处理,则触发音乐播放器开始播放事件。

    代码?
    new Thread(new Runnable() {

    @Override
    public void run() {
    try {
    Runtime.getRuntime().exec("input keyevent " +
    Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK));
    } catch (IOException e) {
    // Runtime.exec(String) had an I/O problem, try to fall back
    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));

    mContext.sendOrderedBroadcast(btnDown, enforcedPerm);
    mContext.sendOrderedBroadcast(btnUp, enforcedPerm);
    }
    }

    }).start();

    tl;博士

    Android 8.0 Oreo 及更高版本有一个很好的公共(public) API。

    在 Android 8.0 Oreo 之前没有公共(public) API。内部 API 是禁止使用的,或者根本没有文档。您应该谨慎行事。

    关于android - 如何在 Android 5.0 (Lollipop) 中以编程方式接听来电?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26924618/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com