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