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
15package android.telecom;
16
17import android.app.ActivityManager;
18import android.content.Context;
19import android.content.Intent;
20import android.content.pm.ActivityInfo;
21import android.content.pm.PackageManager;
22import android.content.pm.ResolveInfo;
23import android.net.Uri;
24import android.provider.Settings;
25import android.text.TextUtils;
26
27import java.util.ArrayList;
28import java.util.List;
29
30/**
31 * Class for managing the default dialer application that will receive incoming calls, and be
32 * allowed to make emergency outgoing calls.
33 *
34 * @hide
35 */
36public class DefaultDialerManager {
37    private static final String TAG = "DefaultDialerManager";
38
39    /**
40     * Sets the specified package name as the default dialer application for the current user.
41     * The caller of this method needs to have permission to write to secure settings and
42     * manage users on the device.
43     *
44     * @return {@code true} if the default dialer application was successfully changed,
45     *         {@code false} otherwise.
46     *
47     * @hide
48     * */
49    public static boolean setDefaultDialerApplication(Context context, String packageName) {
50        return setDefaultDialerApplication(context, packageName, ActivityManager.getCurrentUser());
51    }
52
53    /**
54     * Sets the specified package name as the default dialer application for the specified user.
55     * The caller of this method needs to have permission to write to secure settings and
56     * manage users on the device.
57     *
58     * @return {@code true} if the default dialer application was successfully changed,
59     *         {@code false} otherwise.
60     *
61     * @hide
62     * */
63    public static boolean setDefaultDialerApplication(Context context, String packageName,
64            int user) {
65        // Get old package name
66        String oldPackageName = Settings.Secure.getStringForUser(context.getContentResolver(),
67                Settings.Secure.DIALER_DEFAULT_APPLICATION, user);
68
69        if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
70            // No change
71            return false;
72        }
73
74        // Only make the change if the new package belongs to a valid phone application
75        List<String> packageNames = getInstalledDialerApplications(context);
76
77        if (packageNames.contains(packageName)) {
78            // Update the secure setting.
79            Settings.Secure.putStringForUser(context.getContentResolver(),
80                    Settings.Secure.DIALER_DEFAULT_APPLICATION, packageName, user);
81            return true;
82        }
83        return false;
84    }
85
86    /**
87     * Returns the installed dialer application for the current user that will be used to receive
88     * incoming calls, and is allowed to make emergency calls.
89     *
90     * The application will be returned in order of preference:
91     * 1) User selected phone application (if still installed)
92     * 2) Pre-installed system dialer (if not disabled)
93     * 3) Null
94     *
95     * The caller of this method needs to have permission to manage users on the device.
96     *
97     * @hide
98     * */
99    public static String getDefaultDialerApplication(Context context) {
100        return getDefaultDialerApplication(context, context.getUserId());
101    }
102
103    /**
104     * Returns the installed dialer application for the specified user that will be used to receive
105     * incoming calls, and is allowed to make emergency calls.
106     *
107     * The application will be returned in order of preference:
108     * 1) User selected phone application (if still installed)
109     * 2) Pre-installed system dialer (if not disabled)
110     * 3) Null
111     *
112     * The caller of this method needs to have permission to manage users on the device.
113     *
114     * @hide
115     * */
116    public static String getDefaultDialerApplication(Context context, int user) {
117        String defaultPackageName = Settings.Secure.getStringForUser(context.getContentResolver(),
118                Settings.Secure.DIALER_DEFAULT_APPLICATION, user);
119
120        final List<String> packageNames = getInstalledDialerApplications(context);
121
122        // Verify that the default dialer has not been disabled or uninstalled.
123        if (packageNames.contains(defaultPackageName)) {
124            return defaultPackageName;
125        }
126
127        // No user-set dialer found, fallback to system dialer
128        String systemDialerPackageName = getTelecomManager(context).getSystemDialerPackage();
129
130        if (TextUtils.isEmpty(systemDialerPackageName)) {
131            // No system dialer configured at build time
132            return null;
133        }
134
135        if (packageNames.contains(systemDialerPackageName)) {
136            return systemDialerPackageName;
137        } else {
138            return null;
139        }
140    }
141
142    /**
143     * Returns a list of installed and available dialer applications.
144     *
145     * In order to appear in the list, a dialer application must implement an intent-filter with
146     * the DIAL intent for the following schemes:
147     *
148     * 1) Empty scheme
149     * 2) tel Uri scheme
150     *
151     * @hide
152     **/
153    public static List<String> getInstalledDialerApplications(Context context) {
154        PackageManager packageManager = context.getPackageManager();
155
156        // Get the list of apps registered for the DIAL intent with empty scheme
157        Intent intent = new Intent(Intent.ACTION_DIAL);
158        List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
159
160        List<String> packageNames = new ArrayList<>();
161
162        for (ResolveInfo resolveInfo : resolveInfoList) {
163            final ActivityInfo activityInfo = resolveInfo.activityInfo;
164            if (activityInfo != null && !packageNames.contains(activityInfo.packageName)) {
165                packageNames.add(activityInfo.packageName);
166            }
167        }
168
169        final Intent dialIntentWithTelScheme = new Intent(Intent.ACTION_DIAL);
170        dialIntentWithTelScheme.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, "", null));
171        return filterByIntent(context, packageNames, dialIntentWithTelScheme);
172    }
173
174    /**
175     * Determines if the package name belongs to the user-selected default dialer or the preloaded
176     * system dialer, and thus should be allowed to perform certain privileged operations.
177     *
178     * @param context A valid context.
179     * @param packageName of the package to check for.
180     *
181     * @return {@code true} if the provided package name corresponds to the user-selected default
182     *         dialer or the preloaded system dialer, {@code false} otherwise.
183     *
184     * @hide
185     */
186    public static boolean isDefaultOrSystemDialer(Context context, String packageName) {
187        if (TextUtils.isEmpty(packageName)) {
188            return false;
189        }
190        final TelecomManager tm = getTelecomManager(context);
191        return packageName.equals(tm.getDefaultDialerPackage())
192                || packageName.equals(tm.getSystemDialerPackage());
193    }
194
195    /**
196     * Filter a given list of package names for those packages that contain an activity that has
197     * an intent filter for a given intent.
198     *
199     * @param context A valid context
200     * @param packageNames List of package names to filter.
201     * @return The filtered list.
202     */
203    private static List<String> filterByIntent(Context context, List<String> packageNames,
204            Intent intent) {
205        if (packageNames == null || packageNames.isEmpty()) {
206            return new ArrayList<>();
207        }
208
209        final List<String> result = new ArrayList<>();
210        final List<ResolveInfo> resolveInfoList =
211                context.getPackageManager().queryIntentActivities(intent, 0);
212        final int length = resolveInfoList.size();
213        for (int i = 0; i < length; i++) {
214            final ActivityInfo info = resolveInfoList.get(i).activityInfo;
215            if (info != null && packageNames.contains(info.packageName)
216                    && !result.contains(info.packageName)) {
217                result.add(info.packageName);
218            }
219        }
220
221        return result;
222    }
223
224
225    private static TelecomManager getTelecomManager(Context context) {
226        return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
227    }
228}
229