steve y
steve y

Reputation: 463

Android Device phone call ability

How can I tell whether a given device has the ability to make phone calls?

For example, my Galaxy Tablet cannot initiate call, its not a phone. I want to detect that before doing a call to isIntentAvailable(context, Intent.ACTION_DIAL). I've tried checking the isIntentAvailable for this, but that doesn't seem to be the way to go.

Upvotes: 36

Views: 25308

Answers (7)

Daniel
Daniel

Reputation: 2605

You can check only TELEPHONY feature or GSM and CDMA separately:

private boolean hasTelephony() {
    return getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
}

or

private boolean hasGsmOrCdma() {
    PackageManager pm = getPackageManager();

    boolean gsmSupported = pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_GSM);
    boolean cdmaSupported = pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA);
//if necessary write somehow what exactly the devise supports
return gsmSupported || cdmaSupported;
    }

works well!

Upvotes: 4

Thunderstick
Thunderstick

Reputation: 1243

I have not checked this yet but here is a solution I came up with that seems like it should work fine. I don't think this requires any special permissions since it is just looking for bools set by the manufacture.

static boolean isRunningOnPhone(Context context){
    UiModeManager uiModeManager = (UiModeManager) context.getSystemService(UI_MODE_SERVICE);
    PackageManager packageManager = context.getPackageManager();
    boolean a = packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
    boolean b = packageManager.hasSystemFeature(PackageManager.FEATURE_SIP_VOIP) || context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA) || context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA);
    boolean c = packageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
    boolean d;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        d = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
    }else {
        d = !(uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR);
    }
    boolean e = !(uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION);
    boolean f = uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_APPLIANCE;
    boolean g = true;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
        g = !(uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_WATCH);
    }

    return a && b && c && d && e && f && g;
}

Upvotes: 0

Automatico
Automatico

Reputation: 12916

Most/all of these solutions here work in some cases, but not all.

As @DanJ indicates. There are no easy way of achieving this.

I had issues checking the PackageManager.FEATURE_TELEPHONY and the getPhoneType, because both appear to check if the device is capable in theory, and assume it can make calls if this is the case. This breaks if you have a phone with no SIM card, and possibly if you have no coverage/out of range. The same goes with the approach of checking which applications can handle a call, because the phone app is still installed, but it will break (at least on my galaxy s3) when there is no SIM.

However, there is once check you can do which appears to me to work for most cases, and that is to check your telephone's subscriberId, basically "what is the name/id of your network provider".

You can do this:

if(((TelephonyManager)getContext()
   .getSystemService(Context.TELEPHONY_SERVICE))
   .getSubscriberId() == null) {
  //No network id ~= can't make calls
}

Here we check if we have no network, thus can "safely" assume that the device can't make a call. This approach gives a false negatives in the case where you have a VoIP phone, or have something like Skype installed.

This approach requires you to add a permission:

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

@TedHopp's solution requires the READ_SMS permission which seems more intrusive to me.


Ohh how I wish phone.canMakeCallsWithoutCrashing() was a thing in the android api.

Upvotes: 2

Will Tate
Will Tate

Reputation: 33509

Steve,

Check out TelephonyManager. It has the getPhoneType() function which will return PHONE_TYPE_NONE in your case.

Upvotes: 4

chutcher
chutcher

Reputation: 586

I think a better approach would be to query the PackageManager to detect if Telephony features are even available on the device. A device should only have Telephony features if it has a phone. I tested this on a Nexus 7, which does not have a phone, and it works. I do not have a device with a phone to test the opposite case.

PackageManager pm = getPackageManager();

if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)){
    //has Telephony features.
}

Upvotes: 25

Ted Hopp
Ted Hopp

Reputation: 234795

if (((TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE)).getPhoneType()
    == TelephonyManager.PHONE_TYPE_NONE)
{
    // no phone
}

EDIT I'm surprised it comes back PHONE_TYPE_CDMA. Here's another possibility:

if (((TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE)).getLine1Number()
    == null)
{
    // no phone
}

This will require the READ_PHONE_STATE permission.

Upvotes: 50

Dan J
Dan J

Reputation: 25673

Devices do not necessarily need Context.TELEPHONY_SERVICE to make phone calls. Consider what happens if you install Skype:

  • Enter a phone number on the native Dialer/Phone app and press "Call".
  • A popup appears titled "Complete action using" and offering "Dialer" or "Skype" applications (it could list other applications too).

So, I believe Skype would work on a wifi-only device with no phone capabilities (according to Context.TELEPHONY_SERVICE).

I think you were correct with your original idea, but you need to check what applications are registered to handle Intent.ACTION_CALL instead of Intent.ACTION_DIAL, e.g.

Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:5551231234"));
List<ResolveInfo> callAppsList = 
  context.getPackageManager().queryIntentActivities(callIntent, 0);

However, I am not aware of any reliable, forward-proof ways of figuring out if those applications will be able to handle the phone call. Consider these cases:

1) A wifi-only Xoom with Skype installed. It needs a valid wifi connection, and the user must have configured Skype to use their account, otherwise the call won't succeed.

2) A telephony enabled device with no SIM card in, or a SIM card that is locked or has run out. The device thinks it can handle telephony, but the call results in a "Not registered on network" error.

3) A telephony enabled device with no wifi or mobile connection (or because it is in Airplane/Flight mode). The device thinks it can handle telephony, but the call will fail.

There are ways you could detect some of these scenarios (e.g. inspecting getSystemService(Context.TELEPHONY_SERVICE).getSimState()), but I think this would probably lead to fragile code that may break when things change in future. For example, could you always reliably detect which application in the list is the default Dialer/Phone app? What if Android changed the package name for it in a subsequent release.

Hopefully that gave you some useful information - I wanted to show this is more tricky that it might appear at a first glance!

Upvotes: 31

Related Questions