1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package android.telecom; 16 17 import android.app.ActivityManager; 18 import android.app.role.RoleManager; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.pm.ActivityInfo; 22 import android.content.pm.PackageManager; 23 import android.content.pm.ResolveInfo; 24 import android.net.Uri; 25 import android.os.AsyncTask; 26 import android.os.Binder; 27 import android.os.Process; 28 import android.os.UserHandle; 29 import android.text.TextUtils; 30 import android.util.Slog; 31 32 import com.android.internal.util.CollectionUtils; 33 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.concurrent.CompletableFuture; 37 import java.util.concurrent.ExecutionException; 38 import java.util.concurrent.TimeUnit; 39 import java.util.concurrent.TimeoutException; 40 import java.util.function.Consumer; 41 42 /** 43 * Class for managing the default dialer application that will receive incoming calls, and be 44 * allowed to make emergency outgoing calls. 45 * 46 * @hide 47 */ 48 public class DefaultDialerManager { 49 private static final String TAG = "DefaultDialerManager"; 50 51 /** 52 * Sets the specified package name as the default dialer application for the current user. 53 * The caller of this method needs to have permission to write to secure settings and 54 * manage users on the device. 55 * 56 * @return {@code true} if the default dialer application was successfully changed, 57 * {@code false} otherwise. 58 * 59 * @hide 60 * */ setDefaultDialerApplication(Context context, String packageName)61 public static boolean setDefaultDialerApplication(Context context, String packageName) { 62 return setDefaultDialerApplication(context, packageName, ActivityManager.getCurrentUser()); 63 } 64 65 /** 66 * Sets the specified package name as the default dialer application for the specified user. 67 * The caller of this method needs to have permission to write to secure settings and 68 * manage users on the device. 69 * 70 * @return {@code true} if the default dialer application was successfully changed, 71 * {@code false} otherwise. 72 * 73 * @hide 74 * */ setDefaultDialerApplication(Context context, String packageName, int user)75 public static boolean setDefaultDialerApplication(Context context, String packageName, 76 int user) { 77 long identity = Binder.clearCallingIdentity(); 78 try { 79 CompletableFuture<Void> future = new CompletableFuture<>(); 80 Consumer<Boolean> callback = successful -> { 81 if (successful) { 82 future.complete(null); 83 } else { 84 future.completeExceptionally(new RuntimeException()); 85 } 86 }; 87 context.getSystemService(RoleManager.class).addRoleHolderAsUser( 88 RoleManager.ROLE_DIALER, packageName, 0, UserHandle.of(user), 89 AsyncTask.THREAD_POOL_EXECUTOR, callback); 90 future.get(5, TimeUnit.SECONDS); 91 return true; 92 } catch (InterruptedException | ExecutionException | TimeoutException e) { 93 Slog.e(TAG, "Failed to set default dialer to " + packageName + " for user " + user, e); 94 return false; 95 } finally { 96 Binder.restoreCallingIdentity(identity); 97 } 98 } 99 100 /** 101 * Returns the installed dialer application for the current user that will be used to receive 102 * incoming calls, and is allowed to make emergency calls. 103 * 104 * The application will be returned in order of preference: 105 * 1) User selected phone application (if still installed) 106 * 2) Pre-installed system dialer (if not disabled) 107 * 3) Null 108 * 109 * The caller of this method needs to have permission to manage users on the device. 110 * 111 * @hide 112 * */ getDefaultDialerApplication(Context context)113 public static String getDefaultDialerApplication(Context context) { 114 return getDefaultDialerApplication(context, context.getUserId()); 115 } 116 117 /** 118 * Returns the installed dialer application for the specified user that will be used to receive 119 * incoming calls, and is allowed to make emergency calls. 120 * 121 * The application will be returned in order of preference: 122 * 1) User selected phone application (if still installed) 123 * 2) Pre-installed system dialer (if not disabled) 124 * 3) Null 125 * 126 * The caller of this method needs to have permission to manage users on the device. 127 * 128 * @hide 129 * */ getDefaultDialerApplication(Context context, int user)130 public static String getDefaultDialerApplication(Context context, int user) { 131 long identity = Binder.clearCallingIdentity(); 132 try { 133 return CollectionUtils.firstOrNull(context.getSystemService(RoleManager.class) 134 .getRoleHoldersAsUser(RoleManager.ROLE_DIALER, UserHandle.of(user))); 135 } finally { 136 Binder.restoreCallingIdentity(identity); 137 } 138 } 139 140 /** 141 * Returns a list of installed and available dialer applications. 142 * 143 * In order to appear in the list, a dialer application must implement an intent-filter with 144 * the DIAL intent for the following schemes: 145 * 146 * 1) Empty scheme 147 * 2) tel Uri scheme 148 * 149 * @hide 150 **/ getInstalledDialerApplications(Context context, int userId)151 public static List<String> getInstalledDialerApplications(Context context, int userId) { 152 PackageManager packageManager = context.getPackageManager(); 153 154 // Get the list of apps registered for the DIAL intent with empty scheme 155 Intent intent = new Intent(Intent.ACTION_DIAL); 156 List<ResolveInfo> resolveInfoList = 157 packageManager.queryIntentActivitiesAsUser(intent, 0, userId); 158 159 List<String> packageNames = new ArrayList<>(); 160 161 for (ResolveInfo resolveInfo : resolveInfoList) { 162 final ActivityInfo activityInfo = resolveInfo.activityInfo; 163 if (activityInfo != null 164 && !packageNames.contains(activityInfo.packageName) 165 // ignore cross profile intent handler 166 && resolveInfo.targetUserId == UserHandle.USER_CURRENT) { 167 packageNames.add(activityInfo.packageName); 168 } 169 } 170 171 final Intent dialIntentWithTelScheme = new Intent(Intent.ACTION_DIAL); 172 dialIntentWithTelScheme.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, "", null)); 173 return filterByIntent(context, packageNames, dialIntentWithTelScheme, userId); 174 } 175 getInstalledDialerApplications(Context context)176 public static List<String> getInstalledDialerApplications(Context context) { 177 return getInstalledDialerApplications(context, Process.myUserHandle().getIdentifier()); 178 } 179 180 /** 181 * Determines if the package name belongs to the user-selected default dialer or the preloaded 182 * system dialer, and thus should be allowed to perform certain privileged operations. 183 * 184 * @param context A valid context. 185 * @param packageName of the package to check for. 186 * 187 * @return {@code true} if the provided package name corresponds to the user-selected default 188 * dialer or the preloaded system dialer, {@code false} otherwise. 189 * 190 * @hide 191 */ isDefaultOrSystemDialer(Context context, String packageName)192 public static boolean isDefaultOrSystemDialer(Context context, String packageName) { 193 if (TextUtils.isEmpty(packageName)) { 194 return false; 195 } 196 final TelecomManager tm = getTelecomManager(context); 197 return packageName.equals(tm.getDefaultDialerPackage()) 198 || packageName.equals(tm.getSystemDialerPackage()); 199 } 200 201 /** 202 * Filter a given list of package names for those packages that contain an activity that has 203 * an intent filter for a given intent. 204 * 205 * @param context A valid context 206 * @param packageNames List of package names to filter. 207 * @param userId The UserId 208 * @return The filtered list. 209 */ filterByIntent(Context context, List<String> packageNames, Intent intent, int userId)210 private static List<String> filterByIntent(Context context, List<String> packageNames, 211 Intent intent, int userId) { 212 if (packageNames == null || packageNames.isEmpty()) { 213 return new ArrayList<>(); 214 } 215 216 final List<String> result = new ArrayList<>(); 217 final List<ResolveInfo> resolveInfoList = context.getPackageManager() 218 .queryIntentActivitiesAsUser(intent, 0, userId); 219 final int length = resolveInfoList.size(); 220 for (int i = 0; i < length; i++) { 221 final ActivityInfo info = resolveInfoList.get(i).activityInfo; 222 if (info != null && packageNames.contains(info.packageName) 223 && !result.contains(info.packageName)) { 224 result.add(info.packageName); 225 } 226 } 227 228 return result; 229 } 230 231 getTelecomManager(Context context)232 private static TelecomManager getTelecomManager(Context context) { 233 return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); 234 } 235 } 236