53

私は、着信に応答し、いくつかの作業を行ってから通話を終了する必要がある Android アプリを作成しています。グーグル検索の結果、これを達成するための 2 つの異なる方法を見つけることができましたが、どちらも Android の最近のバージョン、特に 4.1 以降の Jelly Bean では機能しません。

I.) 「android.intent.action.PHONE_STATE」のブロードキャスト レシーバーで Java リフレクションを使用して「com.android.internal.telephony.ITelephony」にアクセスします。以下のサンプル コードは、何百もの関連記事で見つけることができます。

public class PhoneCallReceiver extends BroadcastReceiver {
 Context context = null;
 private static final String TAG = "Phone call";
 private ITelephony telephonyService;

@Override
 public void onReceive(Context context, Intent intent) {
  if (!intent.getAction().equals("android.intent.action.PHONE_STATE")) 
    return;

  Log.v(TAG, "Receving....");
  TelephonyManager telephony = (TelephonyManager) 
  context.getSystemService(Context.TELEPHONY_SERVICE);  
  try {
      Log.v(TAG, "Get getTeleService...");
      Class c = Class.forName(telephony.getClass().getName());
      Method m = c.getDeclaredMethod("getITelephony");
      m.setAccessible(true);
      telephonyService = (ITelephony) m.invoke(telephony);
      telephonyService.silenceRinger();
      Log.v(TAG, "Answering Call now...");
      telephonyService.answerRingingCall();
      Log.v(TAG, "Call answered...");
      //telephonyService.endCall();
  } catch (Exception e) {
   e.printStackTrace();
   Log.e(TAG,
           "FATAL ERROR: could not connect to telephony subsystem");
   Log.e(TAG, "Exception object: " + e);
  }
 }
}

このコードの問題は、

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

このメソッドが機能するには が必要であり、この権限は Android v 2.3 から「システム アプリのみ」として定義されています。つまり、通常のユーザー アプリは、マニフェスト ファイルでこのアクセス許可を定義できなくなりました。

II.) もう 1 つの方法は、Android が通話に応答するようにするヘッドセット フックのプッシュをシミュレートすることです。これは、以下のコードに示すように「Intent.ACTION_MEDIA_BUTTON」をブロードキャストすることによって行われます。

public class PhoneCallReceiver extends BroadcastReceiver {
 Context context = null;
 private static final String TAG = "Phone call";

 @Override
 public void onReceive(Context context, Intent intent) {
     if (!intent.getAction().equals("android.intent.action.PHONE_STATE")) 
         return;

     String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
     if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
         String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);

         Intent answer = new Intent(Intent.ACTION_MEDIA_BUTTON);
         answer.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
         context.sendOrderedBroadcast(answer, null);
         Log.d(TAG, "Answered incoming call from: " + number);
     }   
     return;
 } 
}

このメソッドは Android 4.1 まで機能し、その後、Android はユーザー アプリを "Intent.ACTION_MEDIA_BUTTON" のブロードキャストから制限しました。

したがって、Android 4.1 以降でこれを実現する方法は現在のところないというのが私の結論です。

他の誰かがこの問題に対する他の解決策または回避策を見つけましたか?

4

9 に答える 9

8

プログラムを使用して通話を終了するには、この回答を試してください。私にとってはうまくいきます。

try {

    String serviceManagerName = "android.os.ServiceManager";
    String serviceManagerNativeName = "android.os.ServiceManagerNative";
    String telephonyName = "com.android.internal.telephony.ITelephony";

    Class telephonyClass;
    Class telephonyStubClass;
    Class serviceManagerClass;
    Class serviceManagerStubClass;
    Class serviceManagerNativeClass;
    Class serviceManagerNativeStubClass;

    Method telephonyCall;
    Method telephonyEndCall;
    Method telephonyAnswerCall;
    Method getDefault;

    Method[] temps;
    Constructor[] serviceManagerConstructor;

    // Method getService;
    Object telephonyObject;
    Object serviceManagerObject;

    telephonyClass = Class.forName(telephonyName);
    telephonyStubClass = telephonyClass.getClasses()[0];
    serviceManagerClass = Class.forName(serviceManagerName);
    serviceManagerNativeClass = Class.forName(serviceManagerNativeName);

    Method getService = // getDefaults[29];
            serviceManagerClass.getMethod("getService", String.class);

    Method tempInterfaceMethod = serviceManagerNativeClass.getMethod(
            "asInterface", IBinder.class);

    Binder tmpBinder = new Binder();
    tmpBinder.attachInterface(null, "fake");

    serviceManagerObject = tempInterfaceMethod.invoke(null, tmpBinder);
    IBinder retbinder = (IBinder) getService.invoke(serviceManagerObject, "phone");
    Method serviceMethod = telephonyStubClass.getMethod("asInterface", IBinder.class);

    telephonyObject = serviceMethod.invoke(null, retbinder);
    //telephonyCall = telephonyClass.getMethod("call", String.class);
    telephonyEndCall = telephonyClass.getMethod("endCall");
    //telephonyAnswerCall = telephonyClass.getMethod("answerRingingCall");

    telephonyEndCall.invoke(telephonyObject);

} catch (Exception e) {
    e.printStackTrace();
    Log.error(DialerActivity.this,
            "FATAL ERROR: could not connect to telephony subsystem");
    Log.error(DialerActivity.this, "Exception object: " + e);
}
于 2013-11-28T13:44:09.433 に答える
7

このスレッドの結論として、Android 4.2.2 で動作するコードを次に示します。

--> 上記のスレッドで @PravinDodia が述べているように、ヘッドセット フックのプッシュをシミュレートし、ブロードキャストを try-catch に保持することで、通話に応答します。(例外がスローされ、catch で処理され、とにかく呼び出しが応答されることに注意してください。したがって、この例外を無視して、何も起こらなかったかのように生活を続けることができると思います!)

--> ITelephony を使用して通話が切断されます。

public class PhoneCallReceiver extends BroadcastReceiver {
 Context context = null;
 private static final String TAG = "Phone call";

 @Override
 public void onReceive(Context context, Intent intent) {
     if (!intent.getAction().equals("android.intent.action.PHONE_STATE")) 
         return;
     else {
         String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);

         if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
             answerPhoneHeadsethook(context, intent);
             return;
         }
         else if(state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
             Log.d(TAG, "CALL ANSWERED NOW!!");
             try {
                    synchronized(this) {
                        Log.d(TAG, "Waiting for 10 sec ");
                        this.wait(10000);
                    }
                }
                catch(Exception e) {
                    Log.d(TAG, "Exception while waiting !!");
                    e.printStackTrace();
                }
             disconnectPhoneItelephony(context);
             return;
         }
         else {
             Log.d(TAG, "ALL DONE ...... !!");
         }
     }  
 }

 public void answerPhoneHeadsethook(Context context, Intent intent) {
     String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
     if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
         String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
         Log.d(TAG, "Incoming call from: " + number);
         Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);             
         buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
         try {
             context.sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");
             Log.d(TAG, "ACTION_MEDIA_BUTTON broadcasted...");
         }
         catch (Exception e) {
             Log.d(TAG, "Catch block of ACTION_MEDIA_BUTTON broadcast !");
         }

         Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG);
         headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
         headSetUnPluggedintent.putExtra("state", 1); // 0 = unplugged  1 = Headset with microphone 2 = Headset without microphone
         headSetUnPluggedintent.putExtra("name", "Headset");
         // TODO: Should we require a permission?
         try {
             context.sendOrderedBroadcast(headSetUnPluggedintent, null);
             Log.d(TAG, "ACTION_HEADSET_PLUG broadcasted ...");
         }
         catch (Exception e) {
                // TODO Auto-generated catch block
                //e.printStackTrace();
                Log.d(TAG, "Catch block of ACTION_HEADSET_PLUG broadcast");
                Log.d(TAG, "Call Answered From Catch Block !!");
         }
         Log.d(TAG, "Answered incoming call from: " + number);  
    }
    Log.d(TAG, "Call Answered using headsethook");
 }

 public static void disconnectPhoneItelephony(Context context) {
     ITelephony telephonyService;
     Log.v(TAG, "Now disconnecting using ITelephony....");
      TelephonyManager telephony = (TelephonyManager) 
      context.getSystemService(Context.TELEPHONY_SERVICE);  
      try {
          Log.v(TAG, "Get getTeleService...");
          Class c = Class.forName(telephony.getClass().getName());
          Method m = c.getDeclaredMethod("getITelephony");
          m.setAccessible(true);
          telephonyService = (ITelephony) m.invoke(telephony);
          //telephonyService.silenceRinger();
          Log.v(TAG, "Disconnecting Call now...");
          //telephonyService.answerRingingCall();
          //telephonyService.endcall();
          Log.v(TAG, "Call disconnected...");
          telephonyService.endCall();
      } catch (Exception e) {
       e.printStackTrace();
       Log.e(TAG,
               "FATAL ERROR: could not connect to telephony subsystem");
       Log.e(TAG, "Exception object: " + e);
      }
 }
}

少なくとも切断機能は機能し、その仕組みはわかっています。そのため、Call Barring アプリケーションを開発したい人は先に進むことができます。私のように電話に出たい人は、今のところこれを使用できると思いますが、次のバージョンで機能しなくなることはありません.

于 2013-07-05T12:03:26.327 に答える
3

Iテレフォニー メソッドは 4.4 では機能しません。また、ヘッドセット/メディア ボタン メソッドでも、電話を切る前にかなり長い呼び出し音を鳴らすことができることがわかりました。

この chaps ブログ投稿では、4.4.2 Galaxy s4 と HTC one mini で動作するようにテストした新しい方法を紹介しています。

http://aprogrammersday.blogspot.co.uk/2014/05/disconnect-block-drop-calls-android-4.html

この手法では、以下のようにランタイム exec を使用します。デバイスによっては別の番号を使用する必要があるようです。

public class HangupPhoneCallReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        if (TelephonyManager.EXTRA_STATE_RINGING.equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE))) {

            Executor eS = Executors.newSingleThreadExecutor();
            eS.execute(new Runnable() {
                @Override
                public void run() {
                    Runtime runtime = Runtime.getRuntime();
                    try {
                        Log.d(TAG, "service call phone 5 \n");
                        runtime.exec("service call phone 5 \n");
                    } catch (Exception exc) {
                        Log.e(TAG, exc.getMessage());
                    }
                }
            });

            return;
        }
    }
}
于 2014-09-17T11:22:21.070 に答える
3

これを試して :

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

AndroidManifest.xmlファイルに次の権限を追加します。

<uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
于 2013-11-25T10:25:21.590 に答える
2

IT テレフォニーを使用した通話の切断は、Samsung S Duos などの一部のデバイスでは機能しません。ただし、着信音をサイレントにすることはできます:)

于 2013-10-01T05:27:57.607 に答える