Android Device phone call ability
Asked Answered
S

7

36

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.

Sociality answered 4/3, 2011 at 16:59 Comment(0)
H
50
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.

Houseless answered 4/3, 2011 at 17:8 Comment(11)
Actaully, it comes back with PHONE_TYPE_CDMA. I know that the device doesn't actually allow calls because it passes back an alert when I try to place a call that says "Cannot make or take a call using Galaxy-Tab".Sociality
Thanks, as I test various devices, the first solution works for the Xoom which has no phone built in, but apparently the Galaxy-Tab is available with phone capabilities in Europe, hence the CDMA response.Sociality
This method can also sometimes return an empty String. I am currently working on an app where in one instance an empty String is returned, and in another null is returned. The simplest fix for this is to test the returned value with TextUtils.isEmpty(String).Lade
@Lade - That's interesting. What device returned an empty string? According to the docs, it's supposed to return null when a phone number is not available. But I suppose manufacturers introduce all sorts of crap when they customize Android. (Perils of an open system.)Houseless
It's an Asus Transformer TF300, Android 4.2.1. Could the docs have been updated to reflect the latest APIs?Lade
@Lade - That version of the docs for getLine1Number() was introduced in Eclair (Android 2.0; API level 5); the phrase about returning null was not present in the Donut release. I'm not familiar with what Asus might have done when it adapted Android to that device.Houseless
getLine1Number() is NOT the answer. Try it on the Nexus 10 emulator, you'll see it comes back non-null when it should be null. Nexus 10 can't make phone calls! Also, Nexus 10 has comes back as PHONE_TYPE_GSM. This is ridiculous.Sotos
@Sotos - On a Nexus 10, does getLine1Number() return a non-null string? What is in the string? (For that matter, what's in the string when you try this on a Nexus 10 emulator?)Houseless
@TedHopp I refactored out the code, but it was definitely non-null when I debugged. Looked to be 12 digits or so.Sotos
@Sotos - Weird. So it seems that so far there is no foolproof technique that works on all platforms. I wonder who would answer if you called that number? :)Houseless
Just for information getLine1Number() returns null even if there is a SIM card (on a Nexus4) and if the telephony is capable. So you should not rely on that information.Intentional
A
31

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!

Ammoniate answered 11/10, 2011 at 21:12 Comment(2)
Thanks for the details and example cases; it illuminates how the seemingly simple question "Can it make a call?" is in fact mired in minutia. Your advice worked well for my implementation.Landwaiter
Turns out ACTION_CALL isn't foolproof. I tried querying this intent on my out-of-the-box ASUS Eee Pad Transformer TF101 16GB running Android 3.2.1, one of the most popular Android tablets on the market at the moment. This device doesn't have phone capabilities. Unexpectedly, it returns a single intent, identified by the class com.android.phone.PhoneApp. I was expecting 0. I surmise PhoneApp is what handles dial attempts by showing the "Phone number: [XXX]XXX-XXXX" [Add To Contacts] [Close] dlg on this device (similarly to what iOS Touch devices do).Landwaiter
B
25

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.
}
Brana answered 7/10, 2014 at 17:42 Comment(1)
This works great! (Tested it by disabling Phone app on tablets). Works on smartphones like intended. You don't even need the READ_PHONE_STATE permission.Jacobba
R
4

Steve,

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

Rochellrochella answered 4/3, 2011 at 17:4 Comment(0)
M
4

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!

Mitre answered 24/2, 2017 at 12:42 Comment(0)
T
2

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.

Timbre answered 18/3, 2016 at 20:33 Comment(0)
D
0

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;
}
Denitadenitrate answered 30/12, 2016 at 20:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.