16

I am trying to get a phone object so that I can call and conference two numbers from within my application.

I have tried using the static PhoneFactory.makeDefaultPhones((Context)this) but have not had any luck.

String phoneFactoryName = "com.android.internal.telephony.PhoneFactory";
String phoneName = "com.android.internal.telephony.Phone";
Class phoneFactoryClass = Class.forName(phoneFactoryName);
Class phoneClass = Class.forName(phoneName);
Method getDefaultPhone = phoneFactoryClass.getMethod("getDefaultPhone");
Object phoneObject = getDefaultPhone.invoke(null);

Error - Caused by java.lang.RuntimeException: PhoneFactory.getDefaultPhone must be called from Looper thread

The Vee
  • 11,420
  • 5
  • 27
  • 60
Ty Smith
  • 2,594
  • 2
  • 23
  • 29
  • This is probably obvious but, since `PhoneFactory` isn't in the SDK, you probably don't want to use it :) – Roman Nurik Jan 27 '10 at 02:38
  • 1
    When running this code, I get an `InvocationTargetException` on `getDefaultPhone.invoke(null)`. I've also tried preceding it with `getDefaultPhone.setAccessible(true)`, but that has no effect. – Paul Lammertsma Jan 25 '11 at 13:06
  • @Tyler i have to launch conference call from my application. if you found any solution .pls answer here – Mohit Aug 27 '11 at 09:16
  • Unfortunately we didn't find a way to do that, and for other reasons the project was scrapped. I've since fully implemented my own version of the telephony class from the AOSP in an internal package for some low level sms/mms manipulation. I would assume this same route could be taken for more advanced calling control. – Ty Smith Sep 01 '11 at 19:07
  • @tsmith Does your implementation of telephony for sms/mms manipulation work on non-rooted device? if yes, could you share the result? – iSun Jan 07 '17 at 01:35

6 Answers6

11

Yes it can be instantiated. But you have to overcome a couple of hurdles:

  • In your AndroidManifest.xml set

    android:sharedUserId="android.uid.phone"

    within the <manifest> tag. This is required to prevent a SecurityException from being thrown when protected Intents are sent by the methods you may invoke (like android.intent.action.SIM_STATE_CHANGED).

  • Set

    android:process="com.android.phone"

    in your <application> tag. This is required to allow the invocation of getDefaultPhone() / makeDefaultPhone().

  • To do all this your app must be signed with the system signature key.

The Vee
  • 11,420
  • 5
  • 27
  • 60
icyerasor
  • 4,973
  • 1
  • 43
  • 52
  • Your answer most correct but I can't use sharedUserId –  May 14 '18 at 15:24
  • @СергейГрушин - yep that is one of the "hurdles". Regular apps are not allowed to do it, unless they are signed with the system signature key.. – icyerasor May 14 '18 at 17:02
  • system signature key is unique for ROM :) Okay. Thanks. But how i can use it for regular app? My application is aimed at working with different systems –  May 14 '18 at 17:30
4

Al least we can answer or ignore calls =) let me copy-paste my post

OMG!!! YES, WE CAN DO THAT!!!
I was going to kill myself after severe 24 hours of investigating and discovering... But I've found a "fresh" solution!

// "cheat" with Java reflection to gain access
// to TelephonyManager's ITelephony getter
Class c = Class.forName(tm.getClass().getName());
Method m = c.getDeclaredMethod("getITelephony");
m.setAccessible(true);
telephonyService = (ITelephony)m.invoke(tm);

People who want to develop their call-control software visit this start point: http://www.google.com/codesearch/p?hl=en#zvQ8rp58BUs/trunk/phone/src/i4nc4mp/myLock/phone/CallPrompt.java&q=itelephony%20package:http://mylockforandroid%5C.googlecode%5C.com&d=0

There is a project. and there are important comments (and credits).

In short: copy AIDL file, add permissions to manifest, copy-paste source for telephony management.

Some more info for you. AT commands you can send only if you are rooted. Than you can kill system process and send commands but you will need a reboot to allow your phone to receive and send calls.

I'm very happy! =) Now my Shake2MuteCall will get an update !

Paul Lammertsma
  • 37,593
  • 16
  • 136
  • 187
foryou
  • 1,799
  • 2
  • 11
  • 4
  • I can confirm that the `myLock` project works (you can check it out from SVN from http://mylockforandroid.googlecode.com/svn/trunk/phone/), but it doesn't provide access to `PhoneFactory`. Is there any way to modify this approach to do so, or at the very least get the active `Call`? – Paul Lammertsma Jan 25 '11 at 13:04
  • Do notice that you are accessing internals of the Android framework. And you have no guarantees what so ever. It will probably not work on all devices and it might suddenly stop working on some devices after an update. – Bjarke Freund-Hansen Feb 20 '12 at 13:33
  • Link goes to 404 ;[ – r1si Feb 08 '17 at 06:53
2

Hy. I was able to retrieve an ProxyPhone trought this class ( and a little bit of reflection ). You may use the (Reflected)PhoneFactory below:

package your.package;

import java.lang.reflect.Method;

import android.content.Context;
import android.util.Log;

public class ReflectedPhoneFactory {

public static final String TAG = "PHONE";

public static void makeDefaultPhones(Context context) throws IllegalArgumentException {

    try{

      ClassLoader cl = context.getClassLoader(); 
      @SuppressWarnings("rawtypes")
      Class PhoneFactory = cl.loadClass("com.android.internal.telephony.PhoneFactory");

      //Parameters Types
      @SuppressWarnings("rawtypes")
      Class[] paramTypes= new Class[1];
      paramTypes[0]= Context.class;

      Method get = PhoneFactory.getMethod("makeDefaultPhone",  paramTypes);

      //Parameters
      Object[] params= new Object[1];
      params[0]= context;

      get.invoke(null, params);

    }catch( IllegalArgumentException iAE ){
        throw iAE;
    }catch( Exception e ){
        Log.e(TAG, "makeDefaultPhones", e);
    }

}

public static void makeDefaultPhone(Context context) throws IllegalArgumentException {

    try{

      ClassLoader cl = context.getClassLoader(); 
      @SuppressWarnings("rawtypes")
      Class PhoneFactory = cl.loadClass("com.android.internal.telephony.PhoneFactory");

      //Parameters Types
      @SuppressWarnings("rawtypes")
      Class[] paramTypes= new Class[1];
      paramTypes[0]= Context.class;

      Method get = PhoneFactory.getMethod("makeDefaultPhone",  paramTypes);

      //Parameters
      Object[] params= new Object[1];
      params[0]= context;

      get.invoke(null, params);

    }catch( IllegalArgumentException iAE ){
        throw iAE;
    }catch( Exception e ){
        Log.e(TAG, "makeDefaultPhone", e);
    }

}

/*
 * This function returns the type of the phone, depending
 * on the network mode.
 *
 * @param network mode
 * @return Phone Type
 */
public static Integer getPhoneType(Context context, int networkMode) throws IllegalArgumentException {

    Integer ret= -1;

    try{

      ClassLoader cl = context.getClassLoader(); 
      @SuppressWarnings("rawtypes")
      Class PhoneFactory = cl.loadClass("com.android.internal.telephony.PhoneFactory");

      //Parameters Types
      @SuppressWarnings("rawtypes")
      Class[] paramTypes= new Class[1];
      paramTypes[0]= Integer.class;

      Method get = PhoneFactory.getMethod("getPhoneType", paramTypes);

      //Parameters
      Object[] params= new Object[1];
      params[0]= new Integer(networkMode);

      ret= (Integer) get.invoke(PhoneFactory, params);

    }catch( IllegalArgumentException iAE ){
        throw iAE;
    }catch( Exception e ){
        ret= -1;
    }

    return ret;

}

public static Object getDefaultPhone(Context context) throws IllegalArgumentException {

    Object ret= null;

    try{

        ClassLoader cl = context.getClassLoader(); 
        @SuppressWarnings("rawtypes")
        Class PhoneFactory = cl.loadClass("com.android.internal.telephony.PhoneFactory");

        Method get = PhoneFactory.getMethod("getDefaultPhone",  (Class[]) null);
        ret= (Object)get.invoke(null, (Object[]) null);

    }catch( IllegalArgumentException iAE ){
        throw iAE;
    }catch( Exception e ){
        Log.e(TAG, "getDefaultPhone", e);
    }

    return ret;

}

public static Phone getCdmaPhone(Context context) throws IllegalArgumentException {

    Phone ret= null;

    try{

      ClassLoader cl = context.getClassLoader(); 
      @SuppressWarnings("rawtypes")
      Class PhoneFactory = cl.loadClass("com.android.internal.telephony.PhoneFactory");

      Method get = PhoneFactory.getMethod("getCdmaPhone",  (Class[]) null);
      ret= (Phone)get.invoke(null, (Object[]) null);

    }catch( IllegalArgumentException iAE ){
        throw iAE;
    }catch( Exception e ){
        //
    }

    return ret;

}

public static Phone getGsmPhone(Context context) throws IllegalArgumentException {

    Phone ret= null;

    try{

      ClassLoader cl = context.getClassLoader(); 
      @SuppressWarnings("rawtypes")
      Class PhoneFactory = cl.loadClass("com.android.internal.telephony.PhoneFactory");

      Method get = PhoneFactory.getMethod("getGsmPhone",  (Class[]) null);
      ret= (Phone)get.invoke(null, (Object[]) null);

    }catch( IllegalArgumentException iAE ){
        throw iAE;
    }catch( Exception e ){
        //
    }

    return ret;

}
}

With it, use the code:

        ReflectedPhoneFactory.makeDefaultPhone(yourContext);
        Object phoneProxy= ReflectedPhoneFactory.getDefaultPhone(yourContext);

Note that the "makeDefaultPhone" call will update the value of the static member "static private Looper sLooper;" and i did not yet tested for collateral effects.

With the received "phoneProxy" object you may make the PhoneProxy call´s trought reflection. ( I am currently implementing this class and may post it if considered useful.

Void
  • 969
  • 1
  • 11
  • 19
0

Fyi, the internal classes Phone, CallManager and some more are moved from /system/framework/framework.jar to /system/framework/telephony-common.jar in Jelly bean.

slash33
  • 899
  • 1
  • 11
  • 17
0

I am trying to get a phone object so that I can call and conference two numbers from within my application.

That is not possible from the SDK.

I have tried using the static PhoneFactory.makeDefaultPhones((Context)this) but have not had any luck.

That is not in the SDK. Please do not go past the bounds of the SDK.

Error - Caused by java.lang.RuntimeException: PhoneFactory.getDefaultPhone must be called from Looper thread

That is because you are trying to do the thing-you're-not-supposed-to-be-doing from a background thread.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 4
    I realize that I am not supposed to, however to do the advanced call managing from within my application, none of the public methods will suffice. I want to call two numbers, conference them, and hang up the call when appropriate. Do you have any recommendations on how to do that outside of getting an internal Phone object. – Ty Smith Jan 27 '10 at 17:21
0

I called it from Activity.onCreate and it crashed several lines after your problem with the following error:

Default phones haven't been made yet!

See the Android sources:

public static Phone getDefaultPhone() {
    if (sLooper != Looper.myLooper()) {
        throw new RuntimeException(
            "PhoneFactory.getDefaultPhone must be called from Looper thread");
    }

    if (!sMadeDefaults) {
        throw new IllegalStateException("Default phones haven't been made yet!");
    }
    return sProxyPhone;
}
Paul Lammertsma
  • 37,593
  • 16
  • 136
  • 187
Honza
  • 1