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