From 762b357ec94aef3e7ee6f991c553aa950840a8bc Mon Sep 17 00:00:00 2001 From: Yorke Lee Date: Fri, 18 Sep 2015 12:54:59 -0700 Subject: Protect against more Telecom privileged operations Make sure that all invocations of TelecomManager methods that could possibly require a permission are protected by a permission check. Some of these are overcautious - for example, the UI should never show the option to return to a call (READ_PHONE_STATE) if we didn't detect an active call (READ_PHONE_STATE) in the first place, so it is not strictly necessary to protect against the former. But not crashing is the most preferable of all options. Bug: 20266292 Change-Id: Id91dd16e34320a5e607f91dbce9a4296025eeaaf --- src/com/android/dialer/DialtactsActivity.java | 8 +-- src/com/android/dialer/SpecialCharSequenceMgr.java | 24 ++++----- .../android/dialer/calllog/PhoneAccountUtils.java | 6 ++- .../android/dialer/dialpad/DialpadFragment.java | 16 ++---- src/com/android/dialer/util/DialerUtils.java | 9 ++-- src/com/android/dialer/util/PhoneNumberUtil.java | 6 +-- src/com/android/dialer/util/TelecomUtil.java | 59 ++++++++++++++++++++++ 7 files changed, 85 insertions(+), 43 deletions(-) diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java index f8fb17f6b..fe828a3bb 100644 --- a/src/com/android/dialer/DialtactsActivity.java +++ b/src/com/android/dialer/DialtactsActivity.java @@ -34,7 +34,6 @@ import android.support.design.widget.CoordinatorLayout; import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBar; import android.telecom.PhoneAccount; -import android.telecom.TelecomManager; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; @@ -370,7 +369,6 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY()); } return super.dispatchTouchEvent(ev); - } @Override @@ -929,7 +927,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O final boolean callKey = Intent.ACTION_CALL_BUTTON.equals(intent.getAction()); if (callKey) { - getTelecomManager().showInCallScreen(false); + TelecomUtil.showInCallScreen(this, false); return true; } @@ -1340,10 +1338,6 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O public void onPageScrollStateChanged(int state) { } - private TelecomManager getTelecomManager() { - return (TelecomManager) getSystemService(Context.TELECOM_SERVICE); - } - @Override public boolean isActionBarShowing() { return mActionBarController.isActionBarShowing(); diff --git a/src/com/android/dialer/SpecialCharSequenceMgr.java b/src/com/android/dialer/SpecialCharSequenceMgr.java index 31aa5c3c7..994829a50 100644 --- a/src/com/android/dialer/SpecialCharSequenceMgr.java +++ b/src/com/android/dialer/SpecialCharSequenceMgr.java @@ -32,7 +32,6 @@ import android.os.Looper; import android.provider.Settings; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; -import android.telecom.TelecomManager; import android.telephony.PhoneNumberUtils; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -48,7 +47,6 @@ import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.Selec import com.android.dialer.calllog.PhoneAccountUtils; import com.android.dialer.util.TelecomUtil; -import java.util.Arrays; import java.util.ArrayList; import java.util.List; @@ -91,13 +89,13 @@ public class SpecialCharSequenceMgr { private static QueryHandler sPreviousAdnQueryHandler; public static class HandleAdnEntryAccountSelectedCallback extends SelectPhoneAccountListener{ - final private TelecomManager mTelecomManager; + final private Context mContext; final private QueryHandler mQueryHandler; final private SimContactQueryCookie mCookie; - public HandleAdnEntryAccountSelectedCallback(TelecomManager telecomManager, + public HandleAdnEntryAccountSelectedCallback(Context context, QueryHandler queryHandler, SimContactQueryCookie cookie) { - mTelecomManager = telecomManager; + mContext = context; mQueryHandler = queryHandler; mCookie = cookie; } @@ -105,7 +103,7 @@ public class SpecialCharSequenceMgr { @Override public void onPhoneAccountSelected(PhoneAccountHandle selectedAccountHandle, boolean setDefault) { - Uri uri = mTelecomManager.getAdnUriForPhoneAccount(selectedAccountHandle); + Uri uri = TelecomUtil.getAdnUriForPhoneAccount(mContext, selectedAccountHandle); handleAdnQuery(mQueryHandler, mCookie, uri); // TODO: Show error dialog if result isn't valid. } @@ -193,6 +191,7 @@ public class SpecialCharSequenceMgr { * and query cancel handler implemented in {@link SimContactQueryCookie}. */ static boolean handleAdnEntry(Context context, String input, EditText textField) { + context = context.getApplicationContext(); /* ADN entries are of the form "N(N)(N)#" */ TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); @@ -245,20 +244,19 @@ public class SpecialCharSequenceMgr { sc.progressDialog.getWindow().addFlags( WindowManager.LayoutParams.FLAG_BLUR_BEHIND); - final TelecomManager telecomManager = - (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); List subscriptionAccountHandles = PhoneAccountUtils.getSubscriptionPhoneAccounts(context); boolean hasUserSelectedDefault = subscriptionAccountHandles.contains( - telecomManager.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL)); + TelecomUtil.getDefaultOutgoingPhoneAccount(context, + PhoneAccount.SCHEME_TEL)); if (subscriptionAccountHandles.size() == 1 || hasUserSelectedDefault) { - Uri uri = telecomManager.getAdnUriForPhoneAccount(null); + Uri uri = TelecomUtil.getAdnUriForPhoneAccount(context, null); handleAdnQuery(handler, sc, uri); } else if (subscriptionAccountHandles.size() > 1){ SelectPhoneAccountListener callback = - new HandleAdnEntryAccountSelectedCallback(telecomManager, handler, sc); + new HandleAdnEntryAccountSelectedCallback(context, handler, sc); DialogFragment dialogFragment = SelectPhoneAccountDialogFragment.newInstance( subscriptionAccountHandles, callback); @@ -299,12 +297,10 @@ public class SpecialCharSequenceMgr { static boolean handlePinEntry(final Context context, final String input) { if ((input.startsWith("**04") || input.startsWith("**05")) && input.endsWith("#")) { - final TelecomManager telecomManager = - (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); List subscriptionAccountHandles = PhoneAccountUtils.getSubscriptionPhoneAccounts(context); boolean hasUserSelectedDefault = subscriptionAccountHandles.contains( - telecomManager.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL)); + TelecomUtil.getDefaultOutgoingPhoneAccount(context, PhoneAccount.SCHEME_TEL)); if (subscriptionAccountHandles.size() == 1 || hasUserSelectedDefault) { // Don't bring up the dialog for single-SIM or if the default outgoing account is diff --git a/src/com/android/dialer/calllog/PhoneAccountUtils.java b/src/com/android/dialer/calllog/PhoneAccountUtils.java index 143d13e86..ceadabce2 100644 --- a/src/com/android/dialer/calllog/PhoneAccountUtils.java +++ b/src/com/android/dialer/calllog/PhoneAccountUtils.java @@ -23,6 +23,9 @@ import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.text.TextUtils; +import com.android.contacts.common.util.PermissionsUtil; +import com.android.dialer.util.TelecomUtil; + import java.util.ArrayList; import java.util.List; @@ -38,7 +41,8 @@ public class PhoneAccountUtils { (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); List subscriptionAccountHandles = new ArrayList(); - List accountHandles = telecomManager.getCallCapablePhoneAccounts(); + final List accountHandles = + TelecomUtil.getCallCapablePhoneAccounts(context); for (PhoneAccountHandle accountHandle : accountHandles) { PhoneAccount account = telecomManager.getPhoneAccount(accountHandle); if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) { diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java index 54e4b8946..9c77f30c5 100644 --- a/src/com/android/dialer/dialpad/DialpadFragment.java +++ b/src/com/android/dialer/dialpad/DialpadFragment.java @@ -42,7 +42,6 @@ import android.provider.Contacts.PhonesColumns; import android.provider.Settings; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; -import android.telecom.TelecomManager; import android.telephony.PhoneNumberUtils; import android.telephony.TelephonyManager; import android.text.Editable; @@ -274,10 +273,6 @@ public class DialpadFragment extends Fragment return (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE); } - private TelecomManager getTelecomManager() { - return (TelecomManager) getActivity().getSystemService(Context.TELECOM_SERVICE); - } - @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { mWasEmptyBeforeTextChange = TextUtils.isEmpty(s); @@ -980,7 +975,7 @@ public class DialpadFragment extends Fragment List subscriptionAccountHandles = PhoneAccountUtils.getSubscriptionPhoneAccounts(getActivity()); boolean hasUserSelectedDefault = subscriptionAccountHandles.contains( - getTelecomManager().getDefaultOutgoingPhoneAccount( + TelecomUtil.getDefaultOutgoingPhoneAccount(getActivity(), PhoneAccount.SCHEME_VOICEMAIL)); boolean needsAccountDisambiguation = subscriptionAccountHandles.size() > 1 && !hasUserSelectedDefault; @@ -1462,7 +1457,7 @@ public class DialpadFragment extends Fragment * or "return to call" from the dialpad chooser. */ private void returnToInCallScreen(boolean showDialpad) { - getTelecomManager().showInCallScreen(showDialpad); + TelecomUtil.showInCallScreen(getActivity(), showDialpad); // Finally, finish() ourselves so that we don't stay on the // activity stack. @@ -1579,20 +1574,19 @@ public class DialpadFragment extends Fragment * * @return true if voicemail is enabled and accessible. Note that this can be false * "temporarily" after the app boot. - * @see TelecomManager#getVoiceMailNumber(PhoneAccountHandle) */ private boolean isVoicemailAvailable() { try { PhoneAccountHandle defaultUserSelectedAccount = - getTelecomManager().getDefaultOutgoingPhoneAccount( + TelecomUtil.getDefaultOutgoingPhoneAccount(getActivity(), PhoneAccount.SCHEME_VOICEMAIL); if (defaultUserSelectedAccount == null) { // In a single-SIM phone, there is no default outgoing phone account selected by // the user, so just call TelephonyManager#getVoicemailNumber directly. return !TextUtils.isEmpty(getTelephonyManager().getVoiceMailNumber()); } else { - return !TextUtils.isEmpty( - getTelecomManager().getVoiceMailNumber(defaultUserSelectedAccount)); + return !TextUtils.isEmpty(TelecomUtil.getVoicemailNumber(getActivity(), + defaultUserSelectedAccount)); } } catch (SecurityException se) { // Possibly no READ_PHONE_STATE privilege. diff --git a/src/com/android/dialer/util/DialerUtils.java b/src/com/android/dialer/util/DialerUtils.java index 8870f76e4..3d5c257ea 100644 --- a/src/com/android/dialer/util/DialerUtils.java +++ b/src/com/android/dialer/util/DialerUtils.java @@ -86,11 +86,10 @@ public class DialerUtils { extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint); intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras); } - final TelecomManager tm = - (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); - if (TelecomUtil.hasCallPhonePermission(context)) { - tm.placeCall(intent.getData(), intent.getExtras()); - } else { + + final boolean hasCallPermission = TelecomUtil.placeCall(context, intent.getData(), + intent.getExtras()); + if (!hasCallPermission) { // TODO: Make calling activity show request permission dialog and handle // callback results appropriately. Toast.makeText(context, "Cannot place call without Phone permission", diff --git a/src/com/android/dialer/util/PhoneNumberUtil.java b/src/com/android/dialer/util/PhoneNumberUtil.java index 539d8b9ba..26998037d 100644 --- a/src/com/android/dialer/util/PhoneNumberUtil.java +++ b/src/com/android/dialer/util/PhoneNumberUtil.java @@ -19,7 +19,6 @@ package com.android.dialer.util; import android.content.Context; import android.provider.CallLog; import android.telecom.PhoneAccountHandle; -import android.telecom.TelecomManager; import android.text.TextUtils; import android.util.Log; import android.util.Pair; @@ -55,10 +54,7 @@ public class PhoneNumberUtil { if (TextUtils.isEmpty(number)) { return false; } - - final TelecomManager telecomManager = - (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); - return telecomManager.isVoiceMailNumber(accountHandle, number.toString()); + return TelecomUtil.isVoicemailNumber(context, accountHandle, number.toString()); } /** diff --git a/src/com/android/dialer/util/TelecomUtil.java b/src/com/android/dialer/util/TelecomUtil.java index 43b9a72a3..bab1adeea 100644 --- a/src/com/android/dialer/util/TelecomUtil.java +++ b/src/com/android/dialer/util/TelecomUtil.java @@ -20,6 +20,7 @@ import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; import android.net.Uri; +import android.os.Bundle; import android.provider.CallLog.Calls; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; @@ -29,10 +30,27 @@ import android.util.Log; import java.util.ArrayList; import java.util.List; +/** + * Performs permission checks before calling into TelecomManager. Each method is self-explanatory - + * perform the required check and return the fallback default if the permission is missing, + * otherwise return the value from TelecomManager. + * + */ public class TelecomUtil { private static final String TAG = "TelecomUtil"; private static boolean sWarningLogged = false; + public static void showInCallScreen(Context context, boolean showDialpad) { + if (hasReadPhoneStatePermission(context)) { + try { + getTelecomManager(context).showInCallScreen(showDialpad); + } catch (SecurityException e) { + // Just in case + Log.w(TAG, "TelecomManager.showInCallScreen called without permission."); + } + } + } + public static void silenceRinger(Context context) { if (hasModifyPhoneStatePermission(context)) { try { @@ -81,6 +99,14 @@ public class TelecomUtil { return false; } + public static PhoneAccountHandle getDefaultOutgoingPhoneAccount(Context context, + String uriScheme) { + if (hasReadPhoneStatePermission(context)) { + return getTelecomManager(context).getDefaultOutgoingPhoneAccount(uriScheme); + } + return null; + } + public static List getCallCapablePhoneAccounts(Context context) { if (hasReadPhoneStatePermission(context)) { return getTelecomManager(context).getCallCapablePhoneAccounts(); @@ -95,6 +121,39 @@ public class TelecomUtil { return false; } + public static boolean isVoicemailNumber(Context context, PhoneAccountHandle accountHandle, + String number) { + if (hasReadPhoneStatePermission(context)) { + return getTelecomManager(context).isVoiceMailNumber(accountHandle, number); + } + return false; + } + + public static String getVoicemailNumber(Context context, PhoneAccountHandle accountHandle) { + if (hasReadPhoneStatePermission(context)) { + return getTelecomManager(context).getVoiceMailNumber(accountHandle); + } + return null; + } + + /** + * Tries to place a call using the {@link TelecomManager}. + * + * @param context a valid context. + * @param address Handle to call. + * @param extras Bundle of extras to attach to the call intent. + * + * @return {@code true} if we successfully attempted to place the call, {@code false} if it + * failed due to a permission check. + */ + public static boolean placeCall(Context context, Uri address, Bundle extras) { + if (hasCallPhonePermission(context)) { + getTelecomManager(context).placeCall(address, extras); + return true; + } + return false; + } + public static Uri getCallLogUri(Context context) { return hasReadWriteVoicemailPermissions(context) ? Calls.CONTENT_URI_WITH_VOICEMAIL : Calls.CONTENT_URI; -- cgit v1.2.3