blob: 5b806a6e57da860305a38379b68d3f681cd25acd [file] [log] [blame]
Yorke Lee014de022015-04-21 17:15:47 -07001/*
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
15package android.telecom;
16
Yorke Lee6526f672015-05-04 17:07:32 -070017import android.app.ActivityManager;
Eugene Susla92b88c72019-01-11 13:31:20 -080018import android.app.role.RoleManager;
Yorke Lee014de022015-04-21 17:15:47 -070019import android.content.Context;
20import android.content.Intent;
21import android.content.pm.ActivityInfo;
22import android.content.pm.PackageManager;
23import android.content.pm.ResolveInfo;
Yorke Lee856a5ac2015-04-28 15:45:42 -070024import android.net.Uri;
Eugene Susla92b88c72019-01-11 13:31:20 -080025import android.os.AsyncTask;
26import android.os.Binder;
Tony Makb401a942016-01-19 19:05:18 +000027import android.os.Process;
Tony Mak39198fa2017-09-15 17:41:43 +010028import android.os.UserHandle;
Yorke Lee014de022015-04-21 17:15:47 -070029import android.text.TextUtils;
Eugene Susla98a872d2019-01-31 18:30:47 -080030import android.util.Slog;
Yorke Lee014de022015-04-21 17:15:47 -070031
Eugene Susla92b88c72019-01-11 13:31:20 -080032import com.android.internal.util.CollectionUtils;
33
Yorke Lee014de022015-04-21 17:15:47 -070034import java.util.ArrayList;
35import java.util.List;
Hai Zhangf5e8ccd2019-03-06 20:12:24 -080036import java.util.concurrent.CompletableFuture;
Eugene Susla98a872d2019-01-31 18:30:47 -080037import java.util.concurrent.ExecutionException;
38import java.util.concurrent.TimeUnit;
39import java.util.concurrent.TimeoutException;
Hai Zhangf5e8ccd2019-03-06 20:12:24 -080040import java.util.function.Consumer;
Yorke Lee014de022015-04-21 17:15:47 -070041
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 */
48public class DefaultDialerManager {
49 private static final String TAG = "DefaultDialerManager";
50
51 /**
Yorke Lee6526f672015-05-04 17:07:32 -070052 * 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.
Yorke Lee014de022015-04-21 17:15:47 -070055 *
Yorke Leedb6da482015-06-02 13:55:25 -070056 * @return {@code true} if the default dialer application was successfully changed,
57 * {@code false} otherwise.
58 *
Yorke Lee014de022015-04-21 17:15:47 -070059 * @hide
60 * */
Yorke Leedb6da482015-06-02 13:55:25 -070061 public static boolean setDefaultDialerApplication(Context context, String packageName) {
62 return setDefaultDialerApplication(context, packageName, ActivityManager.getCurrentUser());
Yorke Lee6526f672015-05-04 17:07:32 -070063 }
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 *
Yorke Leedb6da482015-06-02 13:55:25 -070070 * @return {@code true} if the default dialer application was successfully changed,
71 * {@code false} otherwise.
72 *
Yorke Lee6526f672015-05-04 17:07:32 -070073 * @hide
74 * */
Yorke Leedb6da482015-06-02 13:55:25 -070075 public static boolean setDefaultDialerApplication(Context context, String packageName,
76 int user) {
Eugene Susla92b88c72019-01-11 13:31:20 -080077 long identity = Binder.clearCallingIdentity();
78 try {
Hai Zhangf5e8ccd2019-03-06 20:12:24 -080079 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 };
Eugene Susla92b88c72019-01-11 13:31:20 -080087 context.getSystemService(RoleManager.class).addRoleHolderAsUser(
Hai Zhang71d70362019-02-04 16:17:38 -080088 RoleManager.ROLE_DIALER, packageName, 0, UserHandle.of(user),
Hai Zhangf5e8ccd2019-03-06 20:12:24 -080089 AsyncTask.THREAD_POOL_EXECUTOR, callback);
90 future.get(5, TimeUnit.SECONDS);
Yorke Leedb6da482015-06-02 13:55:25 -070091 return true;
Eugene Susla98a872d2019-01-31 18:30:47 -080092 } catch (InterruptedException | ExecutionException | TimeoutException e) {
93 Slog.e(TAG, "Failed to set default dialer to " + packageName + " for user " + user, e);
94 return false;
Eugene Susla92b88c72019-01-11 13:31:20 -080095 } finally {
96 Binder.restoreCallingIdentity(identity);
Yorke Lee014de022015-04-21 17:15:47 -070097 }
98 }
99
100 /**
Yorke Lee6526f672015-05-04 17:07:32 -0700101 * 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.
Yorke Lee014de022015-04-21 17:15:47 -0700103 *
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 *
Yorke Lee6526f672015-05-04 17:07:32 -0700109 * The caller of this method needs to have permission to manage users on the device.
110 *
Yorke Lee014de022015-04-21 17:15:47 -0700111 * @hide
112 * */
Yorke Lee8e0207b2015-04-28 09:39:20 -0700113 public static String getDefaultDialerApplication(Context context) {
Svet Ganov52153f42015-08-11 08:59:12 -0700114 return getDefaultDialerApplication(context, context.getUserId());
Yorke Lee6526f672015-05-04 17:07:32 -0700115 }
Yorke Lee014de022015-04-21 17:15:47 -0700116
Yorke Lee6526f672015-05-04 17:07:32 -0700117 /**
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 * */
130 public static String getDefaultDialerApplication(Context context, int user) {
Eugene Susla92b88c72019-01-11 13:31:20 -0800131 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);
Yorke Lee8e0207b2015-04-28 09:39:20 -0700137 }
Yorke Lee014de022015-04-21 17:15:47 -0700138 }
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 **/
Tony Makb401a942016-01-19 19:05:18 +0000151 public static List<String> getInstalledDialerApplications(Context context, int userId) {
Yorke Lee014de022015-04-21 17:15:47 -0700152 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);
Tony Makb401a942016-01-19 19:05:18 +0000156 List<ResolveInfo> resolveInfoList =
157 packageManager.queryIntentActivitiesAsUser(intent, 0, userId);
Yorke Lee014de022015-04-21 17:15:47 -0700158
Yorke Lee8e0207b2015-04-28 09:39:20 -0700159 List<String> packageNames = new ArrayList<>();
Yorke Lee014de022015-04-21 17:15:47 -0700160
161 for (ResolveInfo resolveInfo : resolveInfoList) {
162 final ActivityInfo activityInfo = resolveInfo.activityInfo;
Tony Mak39198fa2017-09-15 17:41:43 +0100163 if (activityInfo != null
164 && !packageNames.contains(activityInfo.packageName)
165 // ignore cross profile intent handler
166 && resolveInfo.targetUserId == UserHandle.USER_CURRENT) {
Yorke Lee856a5ac2015-04-28 15:45:42 -0700167 packageNames.add(activityInfo.packageName);
Yorke Lee014de022015-04-21 17:15:47 -0700168 }
Yorke Lee014de022015-04-21 17:15:47 -0700169 }
170
Yorke Lee856a5ac2015-04-28 15:45:42 -0700171 final Intent dialIntentWithTelScheme = new Intent(Intent.ACTION_DIAL);
172 dialIntentWithTelScheme.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, "", null));
Hai Zhang7c8d2e72019-02-11 19:07:30 +0000173 return filterByIntent(context, packageNames, dialIntentWithTelScheme, userId);
Yorke Lee014de022015-04-21 17:15:47 -0700174 }
175
Tony Makb401a942016-01-19 19:05:18 +0000176 public static List<String> getInstalledDialerApplications(Context context) {
177 return getInstalledDialerApplications(context, Process.myUserHandle().getIdentifier());
178 }
179
Yorke Lee014de022015-04-21 17:15:47 -0700180 /**
Yorke Lee61043822015-04-27 11:18:38 -0700181 * 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 */
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
Yorke Lee856a5ac2015-04-28 15:45:42 -0700201 /**
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.
Tyler Gunn0907bd62017-09-01 15:17:05 -0700207 * @param userId The UserId
Yorke Lee856a5ac2015-04-28 15:45:42 -0700208 * @return The filtered list.
209 */
210 private static List<String> filterByIntent(Context context, List<String> packageNames,
Tyler Gunn0907bd62017-09-01 15:17:05 -0700211 Intent intent, int userId) {
Yorke Lee856a5ac2015-04-28 15:45:42 -0700212 if (packageNames == null || packageNames.isEmpty()) {
213 return new ArrayList<>();
214 }
215
216 final List<String> result = new ArrayList<>();
Jeff Sharkey2a9e3f82015-12-18 10:57:58 -0700217 final List<ResolveInfo> resolveInfoList = context.getPackageManager()
Tyler Gunn0907bd62017-09-01 15:17:05 -0700218 .queryIntentActivitiesAsUser(intent, 0, userId);
Yorke Lee856a5ac2015-04-28 15:45:42 -0700219 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
Yorke Lee014de022015-04-21 17:15:47 -0700232 private static TelecomManager getTelecomManager(Context context) {
233 return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
234 }
235}