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 */ isHiddenSystemModule(Context context, String packageName)128 public static boolean isHiddenSystemModule(Context context, String packageName) { 129 return ApplicationsState.getInstance((Application) context.getApplicationContext()) 130 .isHiddenModule(packageName); 131 } 132 133 /** 134 * Returns a boolean indicating whether a given package is a system module. 135 */ isSystemModule(Context context, String packageName)136 public static boolean isSystemModule(Context context, String packageName) { 137 return ApplicationsState.getInstance((Application) context.getApplicationContext()) 138 .isSystemModule(packageName); 139 } 140 141 /** 142 * Returns a boolean indicating whether a given package is a mainline module. 143 */ isMainlineModule(PackageManager pm, String packageName)144 public static boolean isMainlineModule(PackageManager pm, String packageName) { 145 // Check if the package is listed among the system modules. 146 try { 147 pm.getModuleInfo(packageName, 0 /* flags */); 148 return true; 149 } catch (PackageManager.NameNotFoundException e) { 150 //pass 151 } 152 153 try { 154 final PackageInfo pkg = pm.getPackageInfo(packageName, 0 /* flags */); 155 // Check if the package is contained in an APEX. There is no public API to properly 156 // check whether a given APK package comes from an APEX registered as module. 157 // Therefore we conservatively assume that any package scanned from an /apex path is 158 // a system package. 159 return pkg.applicationInfo.sourceDir.startsWith( 160 Environment.getApexDirectory().getAbsolutePath()); 161 } catch (PackageManager.NameNotFoundException e) { 162 return false; 163 } 164 } 165 166 /** 167 * Returns a boolean indicating whether a given package is a browser app. 168 * 169 * An app is a "browser" if it has an activity resolution that wound up 170 * marked with the 'handleAllWebDataURI' flag. 171 */ isBrowserApp(Context context, String packageName, int userId)172 public static boolean isBrowserApp(Context context, String packageName, int userId) { 173 sBrowserIntent.setPackage(packageName); 174 final List<ResolveInfo> list = context.getPackageManager().queryIntentActivitiesAsUser( 175 sBrowserIntent, PackageManager.MATCH_ALL, userId); 176 for (ResolveInfo info : list) { 177 if (info.activityInfo != null && info.handleAllWebDataURI) { 178 return true; 179 } 180 } 181 return false; 182 } 183 184 /** 185 * Returns a boolean indicating whether a given package is a default browser. 186 * 187 * @param packageName a given package. 188 * @return true if the given package is default browser. 189 */ isDefaultBrowser(Context context, String packageName)190 public static boolean isDefaultBrowser(Context context, String packageName) { 191 final String defaultBrowserPackage = 192 context.getPackageManager().getDefaultBrowserPackageNameAsUser( 193 UserHandle.myUserId()); 194 return TextUtils.equals(packageName, defaultBrowserPackage); 195 } 196 } 197 198