1 /* 2 * Copyright (C) 2018 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.permissioncontroller.role.model; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.content.pm.PackageManager; 24 import android.content.pm.ResolveInfo; 25 26 import androidx.annotation.NonNull; 27 28 import java.util.ArrayList; 29 import java.util.List; 30 import java.util.Objects; 31 32 /** 33 * Specifies a preferred {@code Activity} configuration to be configured by a {@link Role}. 34 */ 35 public class PreferredActivity { 36 37 /** 38 * The specification of the {@code Activity} to be preferred. 39 */ 40 @NonNull 41 private final RequiredActivity mActivity; 42 43 /** 44 * The list of {@code IntentFilter} specifications to be configured to prefer this 45 * {@code Activity}. 46 */ 47 @NonNull 48 private final List<IntentFilterData> mIntentFilterDatas; 49 PreferredActivity(@onNull RequiredActivity activity, @NonNull List<IntentFilterData> intentFilterDatas)50 public PreferredActivity(@NonNull RequiredActivity activity, 51 @NonNull List<IntentFilterData> intentFilterDatas) { 52 mActivity = activity; 53 mIntentFilterDatas = intentFilterDatas; 54 } 55 56 @NonNull getActivity()57 public RequiredActivity getActivity() { 58 return mActivity; 59 } 60 61 @NonNull getIntentFilterDatas()62 public List<IntentFilterData> getIntentFilterDatas() { 63 return mIntentFilterDatas; 64 } 65 66 /** 67 * Configure this preferred activity specification for an application. 68 * 69 * @param packageName the package name of the application 70 * @param context the {@code Context} to retrieve system services 71 */ configure(@onNull String packageName, @NonNull Context context)72 public void configure(@NonNull String packageName, @NonNull Context context) { 73 ComponentName packageActivity = mActivity.getQualifyingComponentForPackage( 74 packageName, context); 75 if (packageActivity == null) { 76 // We might be running into some race condition here, but we can't do anything about it. 77 // This should be handled by a future reconciliation started by the package change. 78 return; 79 } 80 81 PackageManager packageManager = context.getPackageManager(); 82 int intentFilterDatasSize = mIntentFilterDatas.size(); 83 for (int i = 0; i < intentFilterDatasSize; i++) { 84 IntentFilterData intentFilterData = mIntentFilterDatas.get(i); 85 86 IntentFilter intentFilter = intentFilterData.createIntentFilter(); 87 intentFilter.addCategory(Intent.CATEGORY_DEFAULT); 88 89 // PackageManager.replacePreferredActivity() expects filter to have no data authorities, 90 // paths, or types; and at most one scheme. 91 int match = intentFilterData.getDataScheme() != null 92 ? IntentFilter.MATCH_CATEGORY_SCHEME : IntentFilter.MATCH_CATEGORY_EMPTY; 93 94 Intent intent = intentFilterData.createIntent(); 95 List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent, 96 PackageManager.MATCH_DIRECT_BOOT_AWARE 97 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE 98 | PackageManager.MATCH_DEFAULT_ONLY); 99 List<ComponentName> set = new ArrayList<>(); 100 int resolveInfosSize = resolveInfos.size(); 101 for (int resolveInfosIndex = 0; resolveInfosIndex < resolveInfosSize; 102 resolveInfosIndex++) { 103 ResolveInfo resolveInfo = resolveInfos.get(resolveInfosIndex); 104 105 ComponentName componentName = new ComponentName( 106 resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name); 107 set.add(componentName); 108 } 109 110 packageManager.replacePreferredActivity(intentFilter, match, set, packageActivity); 111 } 112 } 113 114 @Override toString()115 public String toString() { 116 return "PreferredActivity{" 117 + "mActivity=" + mActivity 118 + ", mIntentFilterDatas=" + mIntentFilterDatas 119 + '}'; 120 } 121 122 @Override equals(Object object)123 public boolean equals(Object object) { 124 if (this == object) { 125 return true; 126 } 127 if (object == null || getClass() != object.getClass()) { 128 return false; 129 } 130 PreferredActivity that = (PreferredActivity) object; 131 return Objects.equals(mActivity, that.mActivity) 132 && Objects.equals(mIntentFilterDatas, that.mIntentFilterDatas); 133 } 134 135 @Override hashCode()136 public int hashCode() { 137 return Objects.hash(mActivity, mIntentFilterDatas); 138 } 139 } 140