Jak można programowo odbierać połączenia przychodzące w Androidzie 5.0 (Lollipop)?

Ponieważ próbuję stworzyć własny ekran dla połączeń przychodzących, próbuję programowo odbierać połączenia przychodzące. Używam poniższego kodu, ale nie działa on w Androidzie 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");
Author: Vadim Kotov, 2014-11-14

10 answers

Aktualizacja z Androidem 8.0 Oreo

Mimo, że pytanie było pierwotnie zadawane dla wsparcia Android L, ludzie nadal wydają się trafiać to pytanie i odpowiedzi, więc warto opisać ulepszenia wprowadzone w Androidzie 8.0 Oreo. Metody kompatybilne wstecz są nadal opisane poniżej.

Co się zmieniło?

Począwszy od Android 8.0 Oreo, PHONE permission group zawiera również ANSWER_PHONE_CALLS permission . Jak sugeruje nazwa uprawnienia, przytrzymanie go pozwala aplikacji programowo akceptować połączenia przychodzące za pośrednictwem właściwego połączenia API bez hakowania systemu za pomocą odbicia lub symulowania użytkownika.

Jak wykorzystać tę zmianę?

Powinieneś sprawdzić wersję systemu w czasie wykonywania, jeśli obsługujesz starsze wersje Androida, aby móc hermetyzować to nowe wywołanie API, zachowując wsparcie dla starsze wersje Androida. Należy postępować zgodnie z żądaniem uprawnień w czasie wykonywania , Aby uzyskać nowe uprawnienia w czasie wykonywania, jak to jest standardem w nowszych wersjach Androida.

Po uzyskaniu pozwolenia, Twoja aplikacja musi po prostu zadzwonić TelecomManager acceptringcall metoda. Podstawowe wywołanie wygląda wtedy następująco:

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();

Metoda 1: TelephonyManager.answeringingcall ()

gdy masz nieograniczona kontrola nad urządzeniem.

Co to jest?

Istnieje TelephonyManager.answeringingcall (), która jest ukrytą, wewnętrzną metodą. Działa jako pomost dla Itelefonii.answeringingcall (), który został omówiony na interwiki i wydaje się obiecujący na początku. Jest on nie dostępny na 4.4.2_r1 ponieważ został wprowadzony tylko w commit 83da75d dla Androida 4.4 KitKat (linia 1537 na 4.4.3_r1 ), a później "przywrócony" w commit f1e1e77 for Lollipop (line 3138 on 5.0. 0_r1) ze względu na strukturę drzewa Gita. Oznacza to, że o ile nie obsługujesz tylko urządzeń z lizakiem, co prawdopodobnie jest złą decyzją w oparciu o niewielki udział w rynku, nadal musisz zapewnić metody awaryjne, jeśli idziesz w dół tej trasy.

Jak to wykorzystamy?

Ponieważ dana metoda jest ukryta przed używanymi aplikacjami SDK, musisz użyć reflection , aby dynamicznie sprawdzaj i używaj metody podczas runtime. Jeśli nie jesteś zaznajomiony z refleksją, możesz szybko przeczytać czym jest refleksja i dlaczego jest przydatna?. Możesz również zagłębić się w szczegóły w Trail: The Reflection API jeśli jesteś zainteresowany.

I jak to wygląda w kodzie?

// 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);
}
To zbyt piękne, by było prawdziwe! Jest jeden mały problem. Metoda ta powinna być w pełni funkcjonalna, ale bezpieczeństwo menedżer chce, aby dzwoniący trzymali Androida.pozwolenie.MODIFY_PHONE_STATE . To uprawnienie jest w sferze tylko częściowo udokumentowanych funkcji systemu, ponieważ od stron trzecich nie oczekuje się, aby go dotknąć(jak widać z dokumentacji dla niego). Możesz spróbować dodać <uses-permission> dla niego, ale to nic nie da, ponieważ poziom ochrony dla tego uprawnienia to signature / system (zobacz linię 1201 core / AndroidManifest na 5.0. 0_r1 ).

Możesz przeczytaj Issue 34785: Update Android:ProtectionLevel documentation , który został utworzony w 2012 roku, aby zobaczyć, że brakuje nam szczegółów na temat konkretnej "składni rur", ale z eksperymentowania wokół, wydaje się, że musi on działać jako 'AND', Co oznacza, że wszystkie podane flagi muszą być spełnione, aby uzyskać pozwolenie. Praca przy takim założeniu oznaczałaby, że musisz mieć swoją aplikację:

  1. Zainstalowany jako system podanie.

    Powinno to być w porządku i można to osiągnąć, prosząc użytkowników, aby zainstalować za pomocą ZIP w odzyskiwania, na przykład podczas rootowania lub instalowania Google apps Na niestandardowych ROM, które nie mają je już spakowane.

  2. Podpisane tym samym podpisem co Framework / base aka system, aka ROM.

    Tutaj pojawiają się problemy. Aby to zrobić, musisz mieć klucze używane do podpisywania frameworków/base. Ty byś nie tylko trzeba uzyskać dostęp do kluczy Google dla obrazów fabrycznych Nexus, ale trzeba również uzyskać dostęp do wszystkich innych OEM' i ROM deweloperów' klucze. Nie wydaje się to prawdopodobne, więc możesz mieć swoją aplikację podpisaną kluczami systemowymi, albo tworząc niestandardową pamięć ROM i prosząc użytkowników o przełączenie się na nią (co może być trudne), albo znajdując exploit, za pomocą którego można ominąć poziom ochrony uprawnień (co może być również trudne).

Dodatkowo, to zachowanie wydaje się być związane z Issue 34792: Android Jelly Bean / 4.1: android.pozwolenie.READ_LOGS nie działa już, który wykorzystuje ten sam poziom ochrony wraz z nieudokumentowaną flagą rozwoju.

Praca z menedżerem TelephonyManager brzmi dobrze, ale nie zadziała, chyba że uzyskasz odpowiednie pozwolenie, które nie jest takie łatwe w praktyce.

A co z używaniem aplikacji TelephonyManager w inny sposób?

Niestety, wydaje się, że / align = "left" / pozwolenie.MODIFY_PHONE_STATE , aby używać fajnych narzędzi, co z kolei oznacza, że będziesz miał trudności z uzyskaniem dostępu do tych metod.


Metoda 2: Kod usługi wywołania usługi

kiedy możesz sprawdzić, czy kompilacja uruchomiona na urządzeniu będzie działać z określonym kodem.

Bez możliwości interakcji z TelephonyManager, istnieje również możliwość interakcji z usługą poprzez plik wykonywalny service.

Jak to działa?

Jest to dość proste, ale jest jeszcze mniej dokumentacji na temat tej trasy niż inne. Wiemy na pewno, że plik wykonywalny przyjmuje dwa argumenty-nazwę usługi i Kod.

  • Nazwa usługi , której chcemy użyć, to telefon.

    Można to zobaczyć po uruchomieniu service list.

  • Kod, którego chcemy użyć, wydaje się być 6 ale wydaje się teraz bądź 5.

    Wygląda na to, że został oparty na Ibinderze.FIRST_CALL_TRANSACTION + 5 dla wielu wersji (od 1. 5_r4 do 4.4.4_r1), ale podczas testów lokalnych kod 5 działał, aby odpowiedzieć na połączenie przychodzące. Ponieważ Lollipo jest ogromną aktualizacją, jest zrozumiałe, że wewnętrzne zmiany również tutaj.

Wynika to z polecenia service call phone 5.

Jak wykorzystujemy to programowo?

Java

poniższy kod jest przybliżoną implementacją wykonaną, aby funkcjonować jako dowód koncepcji. Jeśli rzeczywiście chcesz skorzystać z tej metody, prawdopodobnie chcesz sprawdzić wytyczne dla bezproblemowego użycia su i ewentualnie przejść do bardziej w pełni rozwiniętego libsuperuserby 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();
}

Manifest

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

Czy to naprawdę wymaga dostępu roota?

Niestety, wydaje się więc. Możesz spróbować użyć Runtime.exec na nim, ale nie udało mi się uzyskać żadnego szczęścia z tej trasy.

Jak stabilne jest to?

Cieszę się, że pytasz. Ze względu na to, że nie jest to udokumentowane, może się to podzielić na różne wersje, co ilustruje powyższa pozorna różnica w kodzie. Nazwa usługi powinna prawdopodobnie pozostać phone w różnych kompilacjach, ale z tego, co wiemy, wartość kodu może się zmieniać w wielu kompilacjach tej samej wersji (wewnętrzne modyfikacje, powiedzmy, skóry OEM) z kolei łamiąc zastosowaną metodę. Warto więc wspomnieć, że testy miały miejsce na Nexusie 4 (mako / occam). Osobiście odradzałbym stosowanie tej metody, ale ponieważ nie jestem w stanie znaleźć bardziej stabilnej metody, uważam, że jest to najlepszy strzał.

Oryginalna metoda: headset keycode intents

na czas, kiedy trzeba się rozliczyć.

na poniższą sekcję silnie wpłynęła ta odpowiedź przez Riley C .

Symulowana metoda intencji zestawu słuchawkowego opublikowana w oryginalnym pytaniu wydaje się być transmitowana tak, jak można by się spodziewać, ale nie wydaje się, aby osiągnąć cel, jakim jest odpowiedź na połączenie. Chociaż wydaje się, że istnieje kod, który powinien obsługiwać te intencje, po prostu nie są one przejmowane, co musi oznaczać, że muszą istnieć jakieś nowe środki zaradcze przeciwko tej metodzie. Dziennik też nie pokazuje nic ciekawego, a ja nie osobiście uważam, że przekopanie się przez źródło Androida w tym celu będzie opłacalne tylko ze względu na możliwość wprowadzenia przez Google niewielkiej zmiany, która łatwo łamie stosowaną metodę.

Możemy coś teraz zrobić?

Zachowanie można konsekwentnie odtwarzać za pomocą wejściowego pliku wykonywalnego. Przyjmuje ona argument keycode, dla którego po prostu przekazujemy KeyEvent.KEYCODE_HEADSETHOOK . Metoda nie wymaga nawet dostępu roota, dzięki czemu nadaje się do metoda ta ma niewielką wadę - Zdarzenie naciśnięcia przycisku zestawu słuchawkowego nie może być określone, aby wymagało pozwolenia, co oznacza, że działa jak prawdziwe naciśnięcie przycisku i bąbelki przez cały łańcuch, co z kolei oznacza, że musisz być ostrożny, kiedy symulować naciśnięcie przycisku, ponieważ może to na przykład wywołać odtwarzacz muzyczny, aby rozpocząć odtwarzanie, jeśli nikt inny o wyższym priorytecie nie jest gotowy do obsługi przycisku. wydarzenie.

Kod?

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;dr

Istnieje ładne publiczne API dla Androida 8.0 Oreo i nowszych.

Nie ma publicznego API przed Androidem 8.0 Oreo. Wewnętrzne interfejsy API są niedostępne lub po prostu pozbawione dokumentacji. Postępuj ostrożnie.

 124
Author: Valter Jansons,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-28 20:27:37

W pełni działające rozwiązanie oparte jest na kodzie @ Valter Strods.

Aby go uruchomić, musisz wyświetlić (niewidoczną) aktywność na ekranie blokady, gdzie kod jest wykonywany.

AndroidManifest.xml
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />

<activity android:name="com.mysms.android.lib.activity.AcceptCallActivity"
        android:launchMode="singleTop"
        android:excludeFromRecents="true"
        android:taskAffinity=""
        android:configChanges="orientation|keyboardHidden|screenSize"
        android:theme="@style/Mysms.Invisible">
    </activity>

Wywołanie Accept Activity

package com.mysms.android.lib.activity;

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 org.apache.log4j.Logger;

import java.io.IOException;

public class AcceptCallActivity extends Activity {

     private static Logger logger = Logger.getLogger(AcceptCallActivity.class);

     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() {

         // for HTC devices we need to broadcast a connected headset
         boolean broadcastConnected = MANUFACTURER_HTC.equalsIgnoreCase(Build.MANUFACTURER)
                  && !audioManager.isWiredHeadsetOn();

         if (broadcastConnected) {
              broadcastHeadsetConnected(false);
         }

         try {
              try {
                  logger.debug("execute input keycode headset hook");
                  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
                  logger.debug("send keycode headset hook intents");
                  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) {
         }
     }

     private class CallStateReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
              finish();
         }
     }
}

Style

<style name="Mysms.Invisible">
    <item name="android:windowFrame">@null</item>
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowAnimationStyle">@null</item>
</style>

Wreszcie wywołaj magię!

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);
 32
Author: notz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-26 18:38:20

Poniżej znajduje się alternatywne podejście, które zadziałało dla mnie. Wysyła kluczowe Zdarzenie do serwera telekomunikacyjnego bezpośrednio przy użyciu API MediaController. Wymaga to, aby aplikacja miała BIND_NOTIFICATION_LISTENER_SERVICE pozwolenie i jest wyraźne przyznanie dostępu powiadomienia od użytkownika:

@TargetApi(Build.VERSION_CODES.LOLLIPOP) 
void sendHeadsetHookLollipop() {
    MediaSessionManager mediaSessionManager =  (MediaSessionManager) getApplicationContext().getSystemService(Context.MEDIA_SESSION_SERVICE);

    try {
        List<MediaController> mediaControllerList = mediaSessionManager.getActiveSessions 
                     (new ComponentName(getApplicationContext(), NotificationReceiverService.class));

        for (MediaController m : mediaControllerList) {
             if ("com.android.server.telecom".equals(m.getPackageName())) {
                 m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
                 log.info("HEADSETHOOK sent to telecom server");
                 break;
             }
        }
    } catch (SecurityException e) {
        log.error("Permission error. Access to notification not granted to the app.");      
    }  
}

NotificationReceiverService.class w powyższym kodzie może być tylko pusta Klasa.

import android.service.notification.NotificationListenerService;

public class NotificationReceiverService extends NotificationListenerService{
     public NotificationReceiverService() {
     }
}

Z odpowiednią sekcją w manifest:

    <service android:name=".NotificationReceiverService" android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
        android:enabled="true" android:exported="true">
    <intent-filter>
         <action android:name="android.service.notification.NotificationListenerService" />
    </intent-filter>

Ponieważ cel zdarzenia jest wyraźny, powinno to prawdopodobnie uniknąć efektu ubocznego wywołania odtwarzacza multimedialnego.

Uwaga: serwer telekomunikacyjny może nie być natychmiast aktywny po zdarzeniu dzwonienia. Aby to działało niezawodnie, może być przydatne dla aplikacji zaimplementowanie MediaSessionManager.Onactivesessionschangedlistener do monitorowania, kiedy serwer telekomunikacyjny staje się aktywny, przed wysłaniem zdarzenia.

Aktualizacja:

W Android O , trzeba symulować ACTION_DOWN przed ACTION_UP, w przeciwnym razie powyższe nie ma wpływu. tzn. potrzebne jest:

m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));

Ale ponieważ oficjalne wezwanie do odebrania połączenia jest dostępne od Androida O( Zobacz odpowiedź na górze), może nie być już potrzeby tego włamania, chyba że ktoś utknie ze starym poziomem API kompilacji przed Androidem o.

 11
Author: headuck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-22 13:48:50

Aby rozwinąć trochę odpowiedź @Muzikant i zmodyfikować ją trochę, aby działała trochę czystsza na moim urządzeniu, spróbuj input keyevent 79, stała dla KeyEvent.KEYCODE_HEADSETHOOK . bardzo mniej więcej:

    new Thread(new Runnable() {

        @Override
        public void run() {

            try {

                Runtime.getRuntime().exec( "input keyevent " + KeyEvent.KEYCODE_HEADSETHOOK );
            }
            catch (Throwable t) {

                // do something proper here.
            }
        }
    }).start();

Wybacz dość złe konwencje kodowania, nie jestem zbyt dobrze obeznany w uruchomieniu.wywołanie exec (). Zauważ, że moje urządzenie nie jest zakorzenione, ani nie żądam uprawnień roota.

Problem z tym podejściem polega na tym, że działa tylko pod pewnymi warunkami (dla mnie). Że jest, jeśli uruchomić powyższy wątek z opcji menu, które użytkownik wybiera podczas połączenia dzwoni, połączenie odpowiada dobrze. Jeśli uruchomię go z odbiornika, który monitoruje status połączenia przychodzącego, zostanie całkowicie zignorowany.

Więc na moim Nexus 5 działa dobrze dla użytkownika kierowane odpowiedzi i powinien pasować do niestandardowego ekranu połączenia celu. To po prostu nie będzie działać dla wszelkiego rodzaju automatycznych aplikacji typu call-control.

Należy również zwrócić uwagę na wszystkie możliwe zastrzeżenia, w tym, że to również będzie prawdopodobnie przestanie działać w aktualizacji lub dwóch.

 9
Author: Riley C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-25 21:52:56

Za pomocą poleceń adb Jak odebrać połączenie przez adb

Pamiętaj, że Android to Linux z potężnym JVM na przodzie. Możesz pobrać aplikację wiersza poleceń i wykorzenić telefon, a teraz masz zwykły komputer z Linuksem i wiersz poleceń, który robi wszystkie normalne rzeczy. Uruchom Skrypty, możesz nawet ssh do niego (sztuczka OpenVPN)

 0
Author: user1544207,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:16

Dzięki @ notz odpowiedź działa dla mnie na Lolillop. Aby ten kod działał ze starym zestawem SDK Androida, możesz wykonać ten kod:

if (Build.VERSION.SDK_INT >= 21) {  
    Intent answerCalintent = new Intent(context, AcceptCallActivity.class);  
    answerCalintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 
                             Intent.FLAG_ACTIVITY_CLEAR_TASK  | 
                             Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
    context.startActivity(answerCalintent);  
}  
else {  
  if (telephonyService != null) {  
    try {  
        telephonyService.answerRingingCall();  
    }  
    catch (Exception e) {  
        answerPhoneHeadsethook();  
    }  
  }  
}  
 0
Author: Khac Quyet Dang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-11 15:58:26

Jak włączyć telefon głośnikowy po automatycznym odbieraniu połączeń.

Rozwiązałem powyższy problem za pomocą setSpeakerphoneOn. Myślę, że warto tutaj opublikować, ponieważ przypadek użycia automatycznego odbierania rozmowy telefonicznej często wymagałby również użycia zestawu głośnomówiącego. Jeszcze raz dziękuję wszystkim w tym wątku, co za niesamowita praca.

To działa dla mnie na Androidzie 5.1.1 na moim Nexusie 4 bez roota. ;)

Wymagane pozwolenie:

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

Kod Javy:

// this means the phone has answered
if(state==TelephonyManager.CALL_STATE_OFFHOOK)
{
    // try and turn on speaker phone
    final Handler mHandler = new Handler();
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            AudioManager audioManager = (AudioManager) localContext.getSystemService(Context.AUDIO_SERVICE);

            // this doesnt work without android.permission.MODIFY_PHONE_STATE
            // audioManager.setMode(AudioManager.MODE_IN_CALL);

            // weirdly this works
            audioManager.setMode(AudioManager.MODE_NORMAL); // this is important
            audioManager.setSpeakerphoneOn(true);

            // note the phone interface won't show speaker phone is enabled
            // but the phone speaker will be on
            // remember to turn it back off when your done ;)
        }
    }, 500); // half a second delay is important or it might fail
}
 0
Author: Madhava Jay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-18 12:56:15

Uruchom następujące polecenie jako root:

input keyevent 5

Więcej szczegółów na temat symulacji keyevents tutaj .

Możesz użyć tej podstawowej klasy, którą stworzyłem do uruchamiania poleceń jako root z Twojej aplikacji.

 -1
Author: Muzikant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-23 11:17:17

Przetestuj to: najpierw dodaj uprawnienia, a następnie użyj killCall (), aby się rozłączyć użyj answerCall (), aby odebrać wywołanie

<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"></uses-permission>


public void killCall() {
    try {
        TelephonyManager telephonyManager =
                (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);

        Class classTelephony = Class.forName(telephonyManager.getClass().getName());
        Method methodGetITelephony = classTelephony.getDeclaredMethod("getITelephony");

        methodGetITelephony.setAccessible(true);

        Object telephonyInterface = methodGetITelephony.invoke(telephonyManager);

        Class telephonyInterfaceClass =
                Class.forName(telephonyInterface.getClass().getName());
        Method methodEndCall = telephonyInterfaceClass.getDeclaredMethod("endCall");

        methodEndCall.invoke(telephonyInterface);

    } catch (Exception ex) {
        Log.d(TAG, "PhoneStateReceiver **" + ex.toString());
    }
}

public void answerCall() {
    try {
        Runtime.getRuntime().exec("input keyevent " +
                Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK));

    } catch (IOException e) {
        answerRingingCallWithIntent();
    }
}

public void answerRingingCallWithIntent() {
    try {
        Intent localIntent1 = new Intent(Intent.ACTION_HEADSET_PLUG);
        localIntent1.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
        localIntent1.putExtra("state", 1);
        localIntent1.putExtra("microphone", 1);
        localIntent1.putExtra("name", "Headset");
        getContext().sendOrderedBroadcast(localIntent1, "android.permission.CALL_PRIVILEGED");

        Intent localIntent2 = new Intent(Intent.ACTION_MEDIA_BUTTON);
        KeyEvent localKeyEvent1 = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK);
        localIntent2.putExtra(Intent.EXTRA_KEY_EVENT, localKeyEvent1);
        getContext().sendOrderedBroadcast(localIntent2, "android.permission.CALL_PRIVILEGED");

        Intent localIntent3 = new Intent(Intent.ACTION_MEDIA_BUTTON);
        KeyEvent localKeyEvent2 = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK);
        localIntent3.putExtra(Intent.EXTRA_KEY_EVENT, localKeyEvent2);
        getContext().sendOrderedBroadcast(localIntent3, "android.permission.CALL_PRIVILEGED");

        Intent localIntent4 = new Intent(Intent.ACTION_HEADSET_PLUG);
        localIntent4.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
        localIntent4.putExtra("state", 0);
        localIntent4.putExtra("microphone", 1);
        localIntent4.putExtra("name", "Headset");
        getContext().sendOrderedBroadcast(localIntent4, "android.permission.CALL_PRIVILEGED");
    } catch (Exception e2) {
        e2.printStackTrace();
    }
}
 -2
Author: Michael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-21 07:16:51

FYI jeśli jesteś zainteresowany tym, jak zakończyć trwające połączenie na Androidzie O, Valter ' s Method 1: TelephonyManager.answerRingingCall() działa, jeśli zmienisz wywołaną metodę na endCall.

Wymaga tylko pozwolenia android.permission.CALL_PHONE.

Oto kod:

// set the logging tag constant; you probably want to change this
final String LOG_TAG = "TelephonyAnswer";

public void endCall() {
    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("endCall").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);
    }
}
 -2
Author: Francois Dermu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-24 18:10:31