ResolverListController.java revision 0cef910d5e159800bacb69310a69ba04eafcd30c
1/* 2 * Copyright (C) 2016 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 18package com.android.internal.app; 19 20import android.annotation.WorkerThread; 21import android.app.ActivityManager; 22import android.content.ComponentName; 23import android.content.Context; 24import android.content.Intent; 25import android.content.pm.ActivityInfo; 26import android.content.pm.ApplicationInfo; 27import android.content.pm.PackageManager; 28import android.content.pm.ResolveInfo; 29import android.util.Log; 30import com.android.internal.annotations.VisibleForTesting; 31 32import java.util.ArrayList; 33import java.util.Collections; 34import java.util.List; 35 36/** 37 * A helper for the ResolverActivity that exposes methods to retrieve, filter and sort its list of 38 * resolvers. 39 */ 40public class ResolverListController { 41 42 private final Context mContext; 43 private final PackageManager mpm; 44 private final int mLaunchedFromUid; 45 46 // Needed for sorting resolvers. 47 private final Intent mTargetIntent; 48 private final String mReferrerPackage; 49 50 private static final String TAG = "ResolverListController"; 51 private static final boolean DEBUG = false; 52 53 private ResolverComparator mResolverComparator; 54 55 public ResolverListController( 56 Context context, 57 PackageManager pm, 58 Intent targetIntent, 59 String referrerPackage, 60 int launchedFromUid) { 61 mContext = context; 62 mpm = pm; 63 mLaunchedFromUid = launchedFromUid; 64 mTargetIntent = targetIntent; 65 mReferrerPackage = referrerPackage; 66 } 67 68 @VisibleForTesting 69 public List<ResolverActivity.ResolvedComponentInfo> getResolversForIntent( 70 boolean shouldGetResolvedFilter, 71 boolean shouldGetActivityMetadata, 72 List<Intent> intents) { 73 List<ResolverActivity.ResolvedComponentInfo> resolvedComponents = null; 74 for (int i = 0, N = intents.size(); i < N; i++) { 75 final Intent intent = intents.get(i); 76 final List<ResolveInfo> infos = mpm.queryIntentActivities(intent, 77 PackageManager.MATCH_DEFAULT_ONLY 78 | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0) 79 | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0)); 80 if (infos != null) { 81 if (resolvedComponents == null) { 82 resolvedComponents = new ArrayList<>(); 83 } 84 addResolveListDedupe(resolvedComponents, intent, infos); 85 } 86 } 87 return resolvedComponents; 88 } 89 90 @VisibleForTesting 91 public void addResolveListDedupe(List<ResolverActivity.ResolvedComponentInfo> into, 92 Intent intent, 93 List<ResolveInfo> from) { 94 final int fromCount = from.size(); 95 final int intoCount = into.size(); 96 for (int i = 0; i < fromCount; i++) { 97 final ResolveInfo newInfo = from.get(i); 98 boolean found = false; 99 // Only loop to the end of into as it was before we started; no dupes in from. 100 for (int j = 0; j < intoCount; j++) { 101 final ResolverActivity.ResolvedComponentInfo rci = into.get(j); 102 if (isSameResolvedComponent(newInfo, rci)) { 103 found = true; 104 rci.add(intent, newInfo); 105 break; 106 } 107 } 108 if (!found) { 109 final ComponentName name = new ComponentName( 110 newInfo.activityInfo.packageName, newInfo.activityInfo.name); 111 final ResolverActivity.ResolvedComponentInfo rci = 112 new ResolverActivity.ResolvedComponentInfo(name, intent, newInfo); 113 rci.setPinned(isComponentPinned(name)); 114 into.add(rci); 115 } 116 } 117 } 118 119 // Filter out any activities that the launched uid does not have permission for. 120 // 121 // Also filter out those that are suspended because they couldn't be started. We don't do this 122 // when we have an explicit list of resolved activities, because that only happens when 123 // we are being subclassed, so we can safely launch whatever they gave us. 124 // 125 // To preserve the inputList, optionally will return the original list if any modification has 126 // been made. 127 @VisibleForTesting 128 public ArrayList<ResolverActivity.ResolvedComponentInfo> filterIneligibleActivities( 129 List<ResolverActivity.ResolvedComponentInfo> inputList, 130 boolean returnCopyOfOriginalListIfModified) { 131 ArrayList<ResolverActivity.ResolvedComponentInfo> listToReturn = null; 132 for (int i = inputList.size()-1; i >= 0; i--) { 133 ActivityInfo ai = inputList.get(i) 134 .getResolveInfoAt(0).activityInfo; 135 int granted = ActivityManager.checkComponentPermission( 136 ai.permission, mLaunchedFromUid, 137 ai.applicationInfo.uid, ai.exported); 138 boolean suspended = (ai.applicationInfo.flags 139 & ApplicationInfo.FLAG_SUSPENDED) != 0; 140 if (granted != PackageManager.PERMISSION_GRANTED || suspended 141 || isComponentFiltered(ai.getComponentName())) { 142 // Access not allowed! We're about to filter an item, 143 // so modify the unfiltered version if it hasn't already been modified. 144 if (returnCopyOfOriginalListIfModified && listToReturn == null) { 145 listToReturn = new ArrayList<>(inputList); 146 } 147 inputList.remove(i); 148 } 149 } 150 return listToReturn; 151 } 152 153 // Filter out any low priority items. 154 // 155 // To preserve the inputList, optionally will return the original list if any modification has 156 // been made. 157 @VisibleForTesting 158 public ArrayList<ResolverActivity.ResolvedComponentInfo> filterLowPriority( 159 List<ResolverActivity.ResolvedComponentInfo> inputList, 160 boolean returnCopyOfOriginalListIfModified) { 161 ArrayList<ResolverActivity.ResolvedComponentInfo> listToReturn = null; 162 // Only display the first matches that are either of equal 163 // priority or have asked to be default options. 164 ResolverActivity.ResolvedComponentInfo rci0 = inputList.get(0); 165 ResolveInfo r0 = rci0.getResolveInfoAt(0); 166 int N = inputList.size(); 167 for (int i = 1; i < N; i++) { 168 ResolveInfo ri = inputList.get(i).getResolveInfoAt(0); 169 if (DEBUG) Log.v( 170 TAG, 171 r0.activityInfo.name + "=" + 172 r0.priority + "/" + r0.isDefault + " vs " + 173 ri.activityInfo.name + "=" + 174 ri.priority + "/" + ri.isDefault); 175 if (r0.priority != ri.priority || 176 r0.isDefault != ri.isDefault) { 177 while (i < N) { 178 if (returnCopyOfOriginalListIfModified && listToReturn == null) { 179 listToReturn = new ArrayList<>(inputList); 180 } 181 inputList.remove(i); 182 N--; 183 } 184 } 185 } 186 return listToReturn; 187 } 188 189 @VisibleForTesting 190 @WorkerThread 191 public void sort(List<ResolverActivity.ResolvedComponentInfo> inputList) { 192 if (mResolverComparator == null) { 193 mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage); 194 } 195 mResolverComparator.compute(inputList); 196 Collections.sort(inputList, mResolverComparator); 197 } 198 199 private static boolean isSameResolvedComponent(ResolveInfo a, 200 ResolverActivity.ResolvedComponentInfo b) { 201 final ActivityInfo ai = a.activityInfo; 202 return ai.packageName.equals(b.name.getPackageName()) 203 && ai.name.equals(b.name.getClassName()); 204 } 205 206 boolean isComponentPinned(ComponentName name) { 207 return false; 208 } 209 210 boolean isComponentFiltered(ComponentName componentName) { 211 return false; 212 } 213 214 @VisibleForTesting 215 public float getScore(ResolverActivity.DisplayResolveInfo target) { 216 if (mResolverComparator == null) { 217 mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage); 218 } 219 return mResolverComparator.getScore(target.getResolvedComponentName()); 220 } 221 222 public void updateModel(ComponentName componentName) { 223 if (mResolverComparator != null) { 224 mResolverComparator.updateModel(componentName); 225 } 226 } 227} 228