7Answers
  • 15
name

A PHP Error was encountered

Severity: Notice

Message: Undefined index: userid

Filename: views/question.php

Line Number: 191

Backtrace:

File: /home/prodcxja/public_html/questions/application/views/question.php
Line: 191
Function: _error_handler

File: /home/prodcxja/public_html/questions/application/controllers/Questions.php
Line: 433
Function: view

File: /home/prodcxja/public_html/questions/index.php
Line: 315
Function: require_once

name Punditsdkoslkdosdkoskdo

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.

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.

  • 50
Reply Report
      • 1
    • It's an Asus Transformer TF300, Android 4.2.1. Could the docs have been updated to reflect the latest APIs?
      • 2
    • 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".
    • 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.
    • 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).
      • 2
    • @Phil - 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.)

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!

  • 30
Reply Report
    • 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.
      • 1
    • 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).

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.
}
  • 24
Reply Report
    • 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.

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!

  • 3
Reply Report

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.

  • 2
Reply Report

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;
}
  • 0
Reply Report