Программно принять вызов в Nougat

С одного года я работаю над продуктом IOT, и приложение, прикрепленное к нему, работает нормально. Теперь я не могу принимать звонки программно в более высоких версиях Android. Особенность очень важна для продукта. Любая помощь высоко ценится.

До обновления исправления для системы безопасности ноябрь 2016 г. Runtime.getRunTime.exec("Command") нормально работал, чтобы принимать звонки программно.

Runtime.getRuntime().exec("input keyevent " +Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK));

Как сделать это возможным в версии Nougat для Android.

Ищу любой взлом.

Я открыл тему для улучшений.

https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Status%20Priority%20Owner%20Summary%20Stars%20Reporter%20Opened&groupby=&sort=&id=231938

Примечание* Если кто-то из вас столкнулся с такой же проблемой, попросите Android Dev Team разобраться с ней и предоставить возможность получить разрешение пользователя во время выполнения. Следуйте указанному выше URL-адресу для запроса.


person AndroidHacker    schedule 09.01.2017    source источник
comment
stackoverflow.com/q/40673922/5996106. Тот же вопрос без решения.   -  person karanatwal.github.io    schedule 09.01.2017
comment
Автор вопроса связал это с устройством, а не с версией.   -  person AndroidHacker    schedule 09.01.2017
comment
вы читали это stackoverflow.com/a/27084305/5996106   -  person karanatwal.github.io    schedule 09.01.2017
comment
Уже попробовал, а также интерфейс ITelephony.Aidl   -  person AndroidHacker    schedule 09.01.2017
comment
Надеюсь, это не проблема с разрешением.   -  person maheshsgr    schedule 09.01.2017
comment
Неа. Поверх обновления ОС от ноября 2016 г. применено некоторое исправление безопасности.   -  person AndroidHacker    schedule 09.01.2017
comment
Вам удалось найти в источниках, о чем именно был патч?   -  person kar    schedule 12.01.2017
comment
Я только что нашел ссылку, в которой говорится, что команда exec во время выполнения не будет использоваться для приема вызова.   -  person AndroidHacker    schedule 12.01.2017
comment
source.android.com/security/bulletin/2016-11-01. html   -  person AndroidHacker    schedule 12.01.2017
comment
Как насчет этого. Нет команд нажатия клавиш. stackoverflow.com/a/36566071/3503855   -  person Anis LOUNIS    schedule 31.01.2017
comment
Спасибо @AnixPasBesoin   -  person AndroidHacker    schedule 31.01.2017
comment
@AndroidHacker Пожалуйста, дайте мне знать, помогло ли это. Я могу столкнуться с этой проблемой, когда попытаюсь поддерживать Android N в некоторых своих приложениях.   -  person Anis LOUNIS    schedule 31.01.2017
comment
Конечно.. Ну, я пробовал это решение. Собираюсь попробовать и с задержкой. Это может сработать. В любом случае я дам вам знать.   -  person AndroidHacker    schedule 31.01.2017
comment
Для работы требуется разрешение MODIFY_PHONE_STATE. Это разрешение доступно только для системных приложений.   -  person AndroidHacker    schedule 31.01.2017
comment
начиная с зефира, вы можете создать приложение для телефона, и после того, как пользователь выберет ваше приложение в качестве телефонного приложения по умолчанию, вы можете использовать общедоступные API для совершения и приема вызовов и т.п.   -  person nandsito    schedule 10.04.2017


Ответы (4)


Поскольку я также работаю над продуктом IOT, это была одна из самых больших проблем, с которыми я столкнулся, но после некоторых исследований я думаю, что нашел какое-то решение этой проблемы, или, можно сказать, простой хак. Я проверил этот хак на нескольких устройствах с несколькими версиями и обнаружил, что большинство устройств отвечают. Не отвечают только устройства Samsung, некоторые устройства Huawei и некоторые устройства Oppo также не отвечают. (Я все еще ищу что-то для этих устройств).

Я заметил, что Android предоставляет одну функцию доступа к уведомлениям. Вы можете использовать NotificationListenerService для чтения уведомлений и выполнения над ними некоторых действий. Он предоставляет некоторые методы переопределения:

 onNotificationPosted()
    onNotificationRemoved()
    getActiveNotifications()

... так далее

Вот код: Создайте службу, которая расширяет NotificationListenerService.

 class NLService extends NotificationListenerService {

     @Override
     public void onNotificationPosted(StatusBarNotification sbn) {
       ....
     }

     @Override
     public void onNotificationRemoved(StatusBarNotification sbn) {
       ....
     }

В AndroidMenifest добавьте эту службу как:

 <service
        android:name=".NLService"
        android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
        <intent-filter>
            <action 
android:name="android.service.notification.NotificationListenerService" />
        </intent-filter>
    </service>

Это позволит вашему приложению читать любое полученное уведомление.

А теперь основной код:

В onNotificationPosted(StatusBarNotification sbn) добавьте этот код:

 @Override
    public void onNotificationPosted(StatusBarNotification sbn) {
        try {
            if (sbn.getNotification().actions != null) {
                for (Notification.Action action : sbn.getNotification().actions) 
                  {
                    Log.e(TAG, "" + action.title);
                    if (action.title.toString().equalsIgnoreCase("Answer")) {
                        Log.e(TAG, "" + true);
                        PendingIntent intent = action.actionIntent;

                        try {
                            intent.send();
                        } catch (PendingIntent.CanceledException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
          } catch (Exception e) {
              e.printStackTrace();
          }
     }

Вот и все!

Все настроено, запустите приложение и устройства, кроме Samsung, в зависимости от того, какое из них показывает уведомление о входящем вызове с кнопками «Ответить» и «Отклонить / отклонить действие», позволит вам ответить на вызов.

Чтобы открыть настройки доступа к уведомлениям и разрешить вашему приложению читать уведомления, используйте:

 Intent intent = new 
    Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
        startActivity(intent); 

Просто создайте POC для этого и дайте мне знать, как это работает.

Пожалуйста, отметьте мой ответ, если это поможет.

Кроме того, если вы можете предоставить какое-либо решение для устройств Samsung, пожалуйста, обновите.

Спасибо

person Vishal Sharma    schedule 06.04.2017
comment
работает безупречно на моем Samsung Note 2 Android v7.1.1! Спасибо ! - person ProllyGeek; 09.04.2017
comment
работает отлично, но что, если я хочу ответить на него кнопкой? не автоответ - person ; 28.06.2017
comment
Для этого можно использовать простой сценарий. Пожалуйста, просмотрите мой новый ответ. - person Vishal Sharma; 29.06.2017
comment
Привет, вы решили проблемы с Samsung? Я думаю, что Samsung использует свой SDK с богатыми уведомлениями. Но я понятия не имею, что с этим делать. - person EricZhao; 03.07.2018

Это своего рода хак, вы можете использовать службу специальных возможностей для получения вызова. Чтобы включить службу специальных возможностей, вы должны включить свою службу в Настройках — Специальные возможности — Ваша служба.

Во-первых, добавьте typeWindowContentChanged в accessibilityEventTypes.

<accessibility-service
     android:accessibilityEventTypes="typeViewClicked|typeViewFocused|typeViewScrolled|typeWindowContentChanged|typeWindowStateChanged"
     android:packageNames="com.example.android.myFirstApp, com.example.android.mySecondApp"
     android:accessibilityFeedbackType="feedbackSpoken"
     android:notificationTimeout="100"
     android:settingsActivity="com.example.android.apis.accessibility.TestBackActivity"
     android:canRetrieveWindowContent="true"
/>

И сделайте что-нибудь с событием или "отображаемым текстом" или "описанием содержимого".

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {

    // Do something with Click or Focused event
    final int eventType = event.getEventType();
    String eventText = null;
    switch(eventType) {
        case AccessibilityEvent.TYPE_VIEW_CLICKED:
            eventText = "Focused: ";
            break;
        case AccessibilityEvent.TYPE_VIEW_FOCUSED:
            eventText = "Focused: ";
            break;
    }
    eventText = eventText + event.getContentDescription();


    // Traverse all items in screen. 
    // Do something with text.

    AccessibilityNodeInfo info = getRootInActiveWindow();

    int index;
    int count = info.getChildCount();
    AccessibilityNodeInfo child;

    for (index = 0; index < count; index++) {
        child = info.getChild(index);
        if (child.getText() != null)
            Log.d(TAG, "text: " + child.getText().toString() + " " + child.getContentDescription());

        // perform Click
        //if (child.isClickable());
            //child.performAction(AccessibilityNodeInfo.ACTION_CLICK);
    }
}

Да, я знаю, что это не изящный способ решить вашу проблему. Это своего рода хак.

person Stanley Ko    schedule 21.02.2017
comment
@Stanely Я пытаюсь сделать то же самое, что вы написали в своем посте, но я не получаю никаких событий в onAccessibilityEvent при поступлении телефонного звонка. Не могли бы вы показать свой полный пример. - person user565; 01.07.2019
comment
@ user565 Вы включили параметр «Специальные возможности» в меню настроек? Вы можете проверить developer.android.com/guide/topics/ui/accessibility/service чтобы узнать о Службе специальных возможностей. А также, вы должны использовать Android версии 1.6 или выше. - person Stanley Ko; 01.07.2019
comment
Служба специальных возможностей может делать что угодно, но использовать ее для ответа на вызов — это хак. Так что я не предлагаю вам делать это. - person Stanley Ko; 01.07.2019
comment
Что ж, на самом деле я тестировал с помощью NotificationListenerService, и он работает нормально, но единственная проблема, с которой я сталкиваюсь при использовании NotificationListenerService, заключается в том, что пользователю необходимо зарегистрировать прослушиватель вручную, и я хочу сделать это с помощью приложения (у меня есть системное приложение). Итак, затем я начинаю изучать ваш путь с помощью службы специальных возможностей, и здесь интересно то, что я могу зарегистрировать свое приложение программно, используя этот способ: - person user565; 01.07.2019
comment
Settings.Secure.putString(getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, myPackage/myService); Settings.Secure.putString(getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 1); - person user565; 01.07.2019

Для ответа на вызов с помощью кнопки установите флаг при обнаружении входящего вызова:

 if(state==TelephonyManager.CALL_STATE_RINGING){
           shouldAnswerCallViaNotification = true;
        } else {
            shouldAnswerCallViaNotification = false;
        }

Теперь создайте список в своем классе NSLogService,

static ArrayList<StatusBarNotification> statusBarNotifications;

и в вашем onNotificationPosted() добавьте StatusBarNotification в список,

   @Override
public void onNotificationPosted(StatusBarNotification sbn) {

    if (HomeScreen.shouldAnswerCallViaNotification) {
        if (statusBarNotifications == null) {
            updateNotificationList();
        }
       statusBarNotifications.add(sbn);

    } else {
        updateNotificationList();
    }

}
 public static ArrayList<StatusBarNotification> getAllNotifications() {
    return statusBarNotifications;
}

public static void updateNotificationList() {

    if (statusBarNotifications != null)
        statusBarNotifications = null;
    statusBarNotifications = new ArrayList<StatusBarNotification>();
}

На главном экране при нажатии кнопки вызовите performNotificationOperation(NLService.getAllNotifications());

вот определение этого метода:

 private void performNotificationOperation(ArrayList<StatusBarNotification> activeNotifications) {

    if (activeNotifications.size()> 0) {
        main_Loop:
        for (StatusBarNotification notification : activeNotifications) {
            try {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    if (notification.getNotification().actions != null) {
                        for (Notification.Action action : notification.getNotification().actions) {
                            //            Log.e(TAG, "" + action);

                            Log.e(TAG, "" + action.title);
                            if (action.title.toString().equalsIgnoreCase("Answer")) {
                                Log.e(TAG, "" + true);
                                PendingIntent intent = action.actionIntent;

                                try {
                                    intent.send();
                                } catch (PendingIntent.CanceledException e) {
                                    e.printStackTrace();
                                }
                                break main_Loop;
                            }


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

        }
    }
    try {
        NLService.updateNotificationList();
    } catch (Exception e) {
        e.printStackTrace();
    }

}
person Vishal Sharma    schedule 29.06.2017

Согласно ответу «Vishal Sharma», мы можем динамически получить заголовок кнопки действия для поддержки других языков:

  @Override
  public void onNotificationPosted(StatusBarNotification sbn) {
    try {
      if (sbn.getNotification().actions != null) {
        Notification.Action[] notificationAction=sbn.getNotification().actions;
        //Log.e(G.TAG, "" +notificationAction  + " =>"+notificationAction.length);
           String rejectTitle=""+notificationAction[0].title;
           String acceptTitle=""+notificationAction[1].title;

        for (Notification.Action action : notificationAction){
          if (action.title.toString().equalsIgnoreCase(acceptTitle)) {
            PendingIntent intent = action.actionIntent;
            try {
              intent.send();
            } catch (PendingIntent.CanceledException e) {
              e.printStackTrace();
            }
          }
        }


      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
person Mikael    schedule 02.07.2018