• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.role.controller.behavior;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.pm.PackageManager;
22 import android.content.pm.ResolveInfo;
23 import android.net.Uri;
24 import android.os.UserHandle;
25 import android.util.ArraySet;
26 
27 import androidx.annotation.NonNull;
28 import androidx.annotation.Nullable;
29 
30 import com.android.modules.utils.build.SdkLevel;
31 import com.android.role.controller.model.Permissions;
32 import com.android.role.controller.model.Role;
33 import com.android.role.controller.model.RoleBehavior;
34 import com.android.role.controller.model.VisibilityMixin;
35 import com.android.role.controller.util.CollectionUtils;
36 import com.android.role.controller.util.PackageUtils;
37 import com.android.role.controller.util.UserUtils;
38 
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.List;
42 
43 /**
44  * Class for behavior of the browser role.
45  *
46  * @see com.android.settings.applications.DefaultAppSettings
47  * @see com.android.settings.applications.defaultapps.DefaultBrowserPreferenceController
48  * @see com.android.settings.applications.defaultapps.DefaultBrowserPicker
49  * @see com.android.server.pm.PackageManagerService#resolveAllBrowserApps(int)
50  */
51 public class BrowserRoleBehavior implements RoleBehavior {
52     private static final Intent BROWSER_INTENT = new Intent()
53             .setAction(Intent.ACTION_VIEW)
54             .addCategory(Intent.CATEGORY_BROWSABLE)
55             .setData(Uri.fromParts("http", "", null));
56 
57     private static final List<String> SYSTEM_BROWSER_PERMISSIONS = Arrays.asList(
58             android.Manifest.permission.ACCESS_COARSE_LOCATION,
59             android.Manifest.permission.ACCESS_FINE_LOCATION
60     );
61 
62     @Nullable
63     @Override
getFallbackHolderAsUser(@onNull Role role, @NonNull UserHandle user, @NonNull Context context)64     public String getFallbackHolderAsUser(@NonNull Role role, @NonNull UserHandle user,
65             @NonNull Context context) {
66         List<String> qualifyingPackageNames = getQualifyingPackagesAsUserInternal(null, false,
67                 user, context);
68         if (qualifyingPackageNames.size() == 1) {
69             return qualifyingPackageNames.get(0);
70         }
71 
72         if (SdkLevel.isAtLeastS()) {
73             List<String> qualifyingSystemPackageNames = getQualifyingPackagesAsUserInternal(null,
74                     true, user, context);
75             if (qualifyingSystemPackageNames.size() == 1) {
76                 return qualifyingSystemPackageNames.get(0);
77             }
78 
79             List<String> defaultPackageNames = role.getDefaultHoldersAsUser(user, context);
80             return CollectionUtils.firstOrNull(defaultPackageNames);
81         } else {
82             return null;
83         }
84     }
85 
86     // PackageManager.queryIntentActivities() will only return the default browser if one was set.
87     // Code in the Settings app passes PackageManager.MATCH_ALL and perform its own filtering, so we
88     // do the same thing here.
89     @Nullable
90     @Override
getQualifyingPackagesAsUser(@onNull Role role, @NonNull UserHandle user, @NonNull Context context)91     public List<String> getQualifyingPackagesAsUser(@NonNull Role role, @NonNull UserHandle user,
92             @NonNull Context context) {
93         return getQualifyingPackagesAsUserInternal(null, false, user, context);
94     }
95 
96     @Nullable
97     @Override
isPackageQualifiedAsUser(@onNull Role role, @NonNull String packageName, @NonNull UserHandle user, @NonNull Context context)98     public Boolean isPackageQualifiedAsUser(@NonNull Role role, @NonNull String packageName,
99             @NonNull UserHandle user, @NonNull Context context) {
100         List<String> packageNames = getQualifyingPackagesAsUserInternal(packageName, false,
101                 user, context);
102         return !packageNames.isEmpty();
103     }
104 
105     @NonNull
getQualifyingPackagesAsUserInternal(@ullable String packageName, boolean matchSystemOnly, @NonNull UserHandle user, @NonNull Context context)106     private List<String> getQualifyingPackagesAsUserInternal(@Nullable String packageName,
107             boolean matchSystemOnly, @NonNull UserHandle user, @NonNull Context context) {
108         Context userContext = UserUtils.getUserContext(context, user);
109         PackageManager userPackageManager = userContext.getPackageManager();
110         Intent intent = BROWSER_INTENT;
111         if (packageName != null) {
112             intent = new Intent(intent)
113                     .setPackage(packageName);
114         }
115         // To one's surprise, MATCH_ALL doesn't include MATCH_DIRECT_BOOT_*.
116         int flags = PackageManager.MATCH_ALL | PackageManager.MATCH_DIRECT_BOOT_AWARE
117                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DEFAULT_ONLY;
118         if (matchSystemOnly) {
119             flags |= PackageManager.MATCH_SYSTEM_ONLY;
120         }
121         List<ResolveInfo> resolveInfos = userPackageManager.queryIntentActivities(intent, flags);
122         ArraySet<String> packageNames = new ArraySet<>();
123         int resolveInfosSize = resolveInfos.size();
124         for (int i = 0; i < resolveInfosSize; i++) {
125             ResolveInfo resolveInfo = resolveInfos.get(i);
126 
127             if (!resolveInfo.activityInfo.exported || !resolveInfo.handleAllWebDataURI) {
128                 continue;
129             }
130             packageNames.add(resolveInfo.activityInfo.packageName);
131         }
132         return new ArrayList<>(packageNames);
133     }
134 
135     @Override
grantAsUser(@onNull Role role, @NonNull String packageName, @NonNull UserHandle user, @NonNull Context context)136     public void grantAsUser(@NonNull Role role, @NonNull String packageName,
137             @NonNull UserHandle user, @NonNull Context context) {
138         // @see com.android.server.pm.permission.DefaultPermissionGrantPolicy
139         //      #grantDefaultPermissionsToDefaultBrowser(java.lang.String, int)
140         if (SdkLevel.isAtLeastS()) {
141             if (PackageUtils.isSystemPackageAsUser(packageName, user, context)) {
142                 Permissions.grantAsUser(packageName, SYSTEM_BROWSER_PERMISSIONS, false, false,
143                         true, false, false, user, context);
144             }
145         }
146     }
147 
148     @Override
revokeAsUser(@onNull Role role, @NonNull String packageName, @NonNull UserHandle user, @NonNull Context context)149     public void revokeAsUser(@NonNull Role role, @NonNull String packageName,
150             @NonNull UserHandle user, @NonNull Context context) {
151         if (SdkLevel.isAtLeastT()) {
152             if (PackageUtils.isSystemPackageAsUser(packageName, user, context)) {
153                 Permissions.revokeAsUser(packageName, SYSTEM_BROWSER_PERMISSIONS, true, false,
154                         false, user, context);
155             }
156         }
157     }
158 
159     @Override
isVisibleAsUser(@onNull Role role, @NonNull UserHandle user, @NonNull Context context)160     public Boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
161             @NonNull Context context) {
162         return VisibilityMixin.isVisible("config_showBrowserRole", true, user, context);
163     }
164 }
165