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.Process; 25 import android.os.UserHandle; 26 import android.util.ArraySet; 27 28 import androidx.annotation.NonNull; 29 import androidx.annotation.Nullable; 30 31 import com.android.modules.utils.build.SdkLevel; 32 import com.android.role.controller.model.Permissions; 33 import com.android.role.controller.model.Role; 34 import com.android.role.controller.model.RoleBehavior; 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 getFallbackHolder(@onNull Role role, @NonNull Context context)64 public String getFallbackHolder(@NonNull Role role, @NonNull Context context) { 65 UserHandle user = Process.myUserHandle(); 66 List<String> qualifyingPackageNames = getQualifyingPackagesAsUserInternal(null, false, user, 67 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.getDefaultHolders(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 isPackageQualified(@onNull Role role, @NonNull String packageName, @NonNull Context context)98 public Boolean isPackageQualified(@NonNull Role role, @NonNull String packageName, 99 @NonNull Context context) { 100 List<String> packageNames = getQualifyingPackagesAsUserInternal(packageName, false, 101 Process.myUserHandle(), 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 grant(@onNull Role role, @NonNull String packageName, @NonNull Context context)136 public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) { 137 // @see com.android.server.pm.permission.DefaultPermissionGrantPolicy 138 // #grantDefaultPermissionsToDefaultBrowser(java.lang.String, int) 139 if (SdkLevel.isAtLeastS()) { 140 if (PackageUtils.isSystemPackage(packageName, context)) { 141 Permissions.grant(packageName, SYSTEM_BROWSER_PERMISSIONS, false, false, true, 142 false, false, context); 143 } 144 } 145 } 146 147 @Override revoke(@onNull Role role, @NonNull String packageName, @NonNull Context context)148 public void revoke(@NonNull Role role, @NonNull String packageName, @NonNull Context context) { 149 if (SdkLevel.isAtLeastT()) { 150 if (PackageUtils.isSystemPackage(packageName, context)) { 151 Permissions.revoke(packageName, SYSTEM_BROWSER_PERMISSIONS, true, false, false, 152 context); 153 } 154 } 155 } 156 } 157