1 /* 2 * Copyright (C) 2021 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.tv.settings.library.device.apps; 18 19 import android.app.Application; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.PackageInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.ResolveInfo; 28 import android.hardware.usb.IUsbManager; 29 import android.net.Uri; 30 import android.os.Environment; 31 import android.os.RemoteException; 32 import android.os.SystemProperties; 33 import android.os.UserHandle; 34 import android.text.TextUtils; 35 import android.util.Log; 36 37 import com.android.tv.settings.library.util.ResourcesUtil; 38 39 import java.util.ArrayList; 40 import java.util.List; 41 42 public class AppUtils { 43 private static final String TAG = "AppUtils"; 44 45 /** 46 * This should normally only be set in robolectric tests, to avoid getting a method not found 47 * exception when calling the isInstantApp method of the ApplicationInfo class, because 48 * robolectric does not yet have an implementation of it. 49 */ 50 private static final InstantAppDataProvider sInstantAppDataProvider = null; 51 52 private static final Intent sBrowserIntent; 53 54 static { 55 sBrowserIntent = new Intent() 56 .setAction(Intent.ACTION_VIEW) 57 .addCategory(Intent.CATEGORY_BROWSABLE) 58 .setData(Uri.parse("http:")); 59 } 60 hasUsbDefaults(IUsbManager usbManager, String packageName)61 public static boolean hasUsbDefaults(IUsbManager usbManager, String packageName) { 62 try { 63 if (usbManager != null) { 64 return usbManager.hasDefaults(packageName, UserHandle.myUserId()); 65 } 66 } catch (RemoteException e) { 67 Log.e(TAG, "mUsbManager.hasDefaults", e); 68 } 69 return false; 70 } 71 getLaunchByDefaultSummary(ApplicationsState.AppEntry appEntry, IUsbManager usbManager, PackageManager pm, Context context)72 public static CharSequence getLaunchByDefaultSummary(ApplicationsState.AppEntry appEntry, 73 IUsbManager usbManager, PackageManager pm, Context context) { 74 String packageName = appEntry.info.packageName; 75 boolean hasPreferred = hasPreferredActivities(pm, packageName) 76 || hasUsbDefaults(usbManager, packageName); 77 int status = pm.getIntentVerificationStatusAsUser(packageName, UserHandle.myUserId()); 78 // consider a visible current link-handling state to be any explicitly designated behavior 79 boolean hasDomainURLsPreference = 80 status != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; 81 return ResourcesUtil.getString(context, hasPreferred || hasDomainURLsPreference 82 ? "launch_defaults_some" 83 : "launch_defaults_none"); 84 } 85 hasPreferredActivities(PackageManager pm, String packageName)86 public static boolean hasPreferredActivities(PackageManager pm, String packageName) { 87 // Get list of preferred activities 88 List<ComponentName> prefActList = new ArrayList<>(); 89 // Intent list cannot be null. so pass empty list 90 List<IntentFilter> intentList = new ArrayList<>(); 91 pm.getPreferredActivities(intentList, prefActList, packageName); 92 Log.d(TAG, "Have " + prefActList.size() + " number of activities in preferred list"); 93 return prefActList.size() > 0; 94 } 95 96 /** 97 * Returns a boolean indicating whether the given package should be considered an instant app 98 */ isInstant(ApplicationInfo info)99 public static boolean isInstant(ApplicationInfo info) { 100 if (sInstantAppDataProvider != null) { 101 if (sInstantAppDataProvider.isInstantApp(info)) { 102 return true; 103 } 104 } else if (info.isInstantApp()) { 105 return true; 106 } 107 108 // For debugging/testing, we support setting the following property to a comma-separated 109 // list of search terms (typically, but not necessarily, full package names) to match 110 // against the package names of the app. 111 String propVal = SystemProperties.get("settingsdebug.instant.packages"); 112 if (propVal != null && !propVal.isEmpty() && info.packageName != null) { 113 String[] searchTerms = propVal.split(","); 114 if (searchTerms != null) { 115 for (String term : searchTerms) { 116 if (info.packageName.contains(term)) { 117 return true; 118 } 119 } 120 } 121 } 122 return false; 123 } 124 125 /** 126 * Returns a boolean indicating whether the given package is a hidden system module 127 * TODO(b/382016780): to be removed after flag cleanup. 128 */ isHiddenSystemModule(Context context, String packageName)129 public static boolean isHiddenSystemModule(Context context, String packageName) { 130 return ApplicationsState.getInstance((Application) context.getApplicationContext()) 131 .isHiddenModule(packageName); 132 } 133 134 /** 135 * Returns a boolean indicating whether a given package is a system module. 136 * TODO(b/382016780): to be removed after flag cleanup. 137 */ isSystemModule(Context context, String packageName)138 public static boolean isSystemModule(Context context, String packageName) { 139 return ApplicationsState.getInstance((Application) context.getApplicationContext()) 140 .isSystemModule(packageName); 141 } 142 143 /** 144 * Returns a boolean indicating whether a given package is a mainline module. 145 */ isMainlineModule(PackageManager pm, String packageName)146 public static boolean isMainlineModule(PackageManager pm, String packageName) { 147 // Check if the package is listed among the system modules. 148 try { 149 pm.getModuleInfo(packageName, 0 /* flags */); 150 return true; 151 } catch (PackageManager.NameNotFoundException e) { 152 //pass 153 } 154 155 try { 156 final PackageInfo pkg = pm.getPackageInfo(packageName, 0 /* flags */); 157 // Check if the package is contained in an APEX. There is no public API to properly 158 // check whether a given APK package comes from an APEX registered as module. 159 // Therefore we conservatively assume that any package scanned from an /apex path is 160 // a system package. 161 return pkg.applicationInfo.sourceDir.startsWith( 162 Environment.getApexDirectory().getAbsolutePath()); 163 } catch (PackageManager.NameNotFoundException e) { 164 return false; 165 } 166 } 167 168 /** 169 * Returns a boolean indicating whether a given package is a browser app. 170 * 171 * An app is a "browser" if it has an activity resolution that wound up 172 * marked with the 'handleAllWebDataURI' flag. 173 */ isBrowserApp(Context context, String packageName, int userId)174 public static boolean isBrowserApp(Context context, String packageName, int userId) { 175 sBrowserIntent.setPackage(packageName); 176 final List<ResolveInfo> list = context.getPackageManager().queryIntentActivitiesAsUser( 177 sBrowserIntent, PackageManager.MATCH_ALL, userId); 178 for (ResolveInfo info : list) { 179 if (info.activityInfo != null && info.handleAllWebDataURI) { 180 return true; 181 } 182 } 183 return false; 184 } 185 186 /** 187 * Returns a boolean indicating whether a given package is a default browser. 188 * 189 * @param packageName a given package. 190 * @return true if the given package is default browser. 191 */ isDefaultBrowser(Context context, String packageName)192 public static boolean isDefaultBrowser(Context context, String packageName) { 193 final String defaultBrowserPackage = 194 context.getPackageManager().getDefaultBrowserPackageNameAsUser( 195 UserHandle.myUserId()); 196 return TextUtils.equals(packageName, defaultBrowserPackage); 197 } 198 } 199 200