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