1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.dialer.telecom;
18 
19 import android.Manifest;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.PackageManager;
23 import android.net.Uri;
24 import android.provider.CallLog.Calls;
25 import android.support.annotation.Nullable;
26 import android.support.annotation.VisibleForTesting;
27 import android.support.v4.content.ContextCompat;
28 import android.telecom.PhoneAccount;
29 import android.telecom.PhoneAccountHandle;
30 import android.telecom.TelecomManager;
31 import android.text.TextUtils;
32 import com.android.dialer.common.LogUtil;
33 import java.util.ArrayList;
34 import java.util.List;
35 
36 /**
37  * Performs permission checks before calling into TelecomManager. Each method is self-explanatory -
38  * perform the required check and return the fallback default if the permission is missing,
39  * otherwise return the value from TelecomManager.
40  */
41 public class TelecomUtil {
42 
43   private static final String TAG = "TelecomUtil";
44   private static boolean sWarningLogged = false;
45   private static Boolean isDefaultDialerForTesting;
46   private static Boolean hasPermissionForTesting;
47 
showInCallScreen(Context context, boolean showDialpad)48   public static void showInCallScreen(Context context, boolean showDialpad) {
49     if (hasReadPhoneStatePermission(context)) {
50       try {
51         getTelecomManager(context).showInCallScreen(showDialpad);
52       } catch (SecurityException e) {
53         // Just in case
54         LogUtil.w(TAG, "TelecomManager.showInCallScreen called without permission.");
55       }
56     }
57   }
58 
silenceRinger(Context context)59   public static void silenceRinger(Context context) {
60     if (hasModifyPhoneStatePermission(context)) {
61       try {
62         getTelecomManager(context).silenceRinger();
63       } catch (SecurityException e) {
64         // Just in case
65         LogUtil.w(TAG, "TelecomManager.silenceRinger called without permission.");
66       }
67     }
68   }
69 
cancelMissedCallsNotification(Context context)70   public static void cancelMissedCallsNotification(Context context) {
71     if (hasModifyPhoneStatePermission(context)) {
72       try {
73         getTelecomManager(context).cancelMissedCallsNotification();
74       } catch (SecurityException e) {
75         LogUtil.w(TAG, "TelecomManager.cancelMissedCalls called without permission.");
76       }
77     }
78   }
79 
getAdnUriForPhoneAccount(Context context, PhoneAccountHandle handle)80   public static Uri getAdnUriForPhoneAccount(Context context, PhoneAccountHandle handle) {
81     if (hasModifyPhoneStatePermission(context)) {
82       try {
83         return getTelecomManager(context).getAdnUriForPhoneAccount(handle);
84       } catch (SecurityException e) {
85         LogUtil.w(TAG, "TelecomManager.getAdnUriForPhoneAccount called without permission.");
86       }
87     }
88     return null;
89   }
90 
handleMmi( Context context, String dialString, @Nullable PhoneAccountHandle handle)91   public static boolean handleMmi(
92       Context context, String dialString, @Nullable PhoneAccountHandle handle) {
93     if (hasModifyPhoneStatePermission(context)) {
94       try {
95         if (handle == null) {
96           return getTelecomManager(context).handleMmi(dialString);
97         } else {
98           return getTelecomManager(context).handleMmi(dialString, handle);
99         }
100       } catch (SecurityException e) {
101         LogUtil.w(TAG, "TelecomManager.handleMmi called without permission.");
102       }
103     }
104     return false;
105   }
106 
107   @Nullable
getDefaultOutgoingPhoneAccount( Context context, String uriScheme)108   public static PhoneAccountHandle getDefaultOutgoingPhoneAccount(
109       Context context, String uriScheme) {
110     if (hasReadPhoneStatePermission(context)) {
111       return getTelecomManager(context).getDefaultOutgoingPhoneAccount(uriScheme);
112     }
113     return null;
114   }
115 
getPhoneAccount(Context context, PhoneAccountHandle handle)116   public static PhoneAccount getPhoneAccount(Context context, PhoneAccountHandle handle) {
117     return getTelecomManager(context).getPhoneAccount(handle);
118   }
119 
getCallCapablePhoneAccounts(Context context)120   public static List<PhoneAccountHandle> getCallCapablePhoneAccounts(Context context) {
121     if (hasReadPhoneStatePermission(context)) {
122       return getTelecomManager(context).getCallCapablePhoneAccounts();
123     }
124     return new ArrayList<>();
125   }
126 
isInCall(Context context)127   public static boolean isInCall(Context context) {
128     if (hasReadPhoneStatePermission(context)) {
129       return getTelecomManager(context).isInCall();
130     }
131     return false;
132   }
133 
isVoicemailNumber( Context context, PhoneAccountHandle accountHandle, String number)134   public static boolean isVoicemailNumber(
135       Context context, PhoneAccountHandle accountHandle, String number) {
136     if (hasReadPhoneStatePermission(context)) {
137       return getTelecomManager(context).isVoiceMailNumber(accountHandle, number);
138     }
139     return false;
140   }
141 
142   @Nullable
getVoicemailNumber(Context context, PhoneAccountHandle accountHandle)143   public static String getVoicemailNumber(Context context, PhoneAccountHandle accountHandle) {
144     if (hasReadPhoneStatePermission(context)) {
145       return getTelecomManager(context).getVoiceMailNumber(accountHandle);
146     }
147     return null;
148   }
149 
150   /**
151    * Tries to place a call using the {@link TelecomManager}.
152    *
153    * @param context context.
154    * @param intent the call intent.
155    * @return {@code true} if we successfully attempted to place the call, {@code false} if it failed
156    *     due to a permission check.
157    */
placeCall(Context context, Intent intent)158   public static boolean placeCall(Context context, Intent intent) {
159     if (hasCallPhonePermission(context)) {
160       getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());
161       return true;
162     }
163     return false;
164   }
165 
getCallLogUri(Context context)166   public static Uri getCallLogUri(Context context) {
167     return hasReadWriteVoicemailPermissions(context)
168         ? Calls.CONTENT_URI_WITH_VOICEMAIL
169         : Calls.CONTENT_URI;
170   }
171 
hasReadWriteVoicemailPermissions(Context context)172   public static boolean hasReadWriteVoicemailPermissions(Context context) {
173     return isDefaultDialer(context)
174         || (hasPermission(context, Manifest.permission.READ_VOICEMAIL)
175             && hasPermission(context, Manifest.permission.WRITE_VOICEMAIL));
176   }
177 
hasModifyPhoneStatePermission(Context context)178   public static boolean hasModifyPhoneStatePermission(Context context) {
179     return isDefaultDialer(context)
180         || hasPermission(context, Manifest.permission.MODIFY_PHONE_STATE);
181   }
182 
hasReadPhoneStatePermission(Context context)183   public static boolean hasReadPhoneStatePermission(Context context) {
184     return isDefaultDialer(context) || hasPermission(context, Manifest.permission.READ_PHONE_STATE);
185   }
186 
hasCallPhonePermission(Context context)187   public static boolean hasCallPhonePermission(Context context) {
188     return isDefaultDialer(context) || hasPermission(context, Manifest.permission.CALL_PHONE);
189   }
190 
hasPermission(Context context, String permission)191   private static boolean hasPermission(Context context, String permission) {
192     if (hasPermissionForTesting != null) {
193       return hasPermissionForTesting;
194     }
195     return ContextCompat.checkSelfPermission(context, permission)
196         == PackageManager.PERMISSION_GRANTED;
197   }
198 
isDefaultDialer(Context context)199   public static boolean isDefaultDialer(Context context) {
200     if (isDefaultDialerForTesting != null) {
201       return isDefaultDialerForTesting;
202     }
203     final boolean result =
204         TextUtils.equals(
205             context.getPackageName(), getTelecomManager(context).getDefaultDialerPackage());
206     if (result) {
207       sWarningLogged = false;
208     } else {
209       if (!sWarningLogged) {
210         // Log only once to prevent spam.
211         LogUtil.w(TAG, "Dialer is not currently set to be default dialer");
212         sWarningLogged = true;
213       }
214     }
215     return result;
216   }
217 
getTelecomManager(Context context)218   private static TelecomManager getTelecomManager(Context context) {
219     return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
220   }
221 
222   @VisibleForTesting(otherwise = VisibleForTesting.NONE)
setIsDefaultDialerForTesting(Boolean defaultDialer)223   public static void setIsDefaultDialerForTesting(Boolean defaultDialer) {
224     isDefaultDialerForTesting = defaultDialer;
225   }
226 
227   @VisibleForTesting(otherwise = VisibleForTesting.NONE)
setHasPermissionForTesting(Boolean hasPermission)228   public static void setHasPermissionForTesting(Boolean hasPermission) {
229     hasPermissionForTesting = hasPermission;
230   }
231 }
232