> userPackages = new HashMap<>();
final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
for (int userId : currentUserIds) {
userPackages.put(userId, getInstalledPackages(/*flags*/ 0, userId).getList());
}
mDexManager.load(userPackages);
if (mIsUpgrade) {
MetricsLogger.histogram(null, "ota_package_manager_init_time",
(int) (SystemClock.uptimeMillis() - startTime));
}
} // synchronized (mPackages)
} // synchronized (mInstallLock)
// Now after opening every single application zip, make sure they
// are all flushed. Not really needed, but keeps things nice and
// tidy.
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "GC");
Runtime.getRuntime().gc();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "loadFallbacks");
FallbackCategoryProvider.loadFallbacks();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// The initial scanning above does many calls into installd while
// holding the mPackages lock, but we're mostly interested in yelling
// once we have a booted system.
mInstaller.setWarnIfHeld(mPackages);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
/**
* Uncompress and install stub applications.
* In order to save space on the system partition, some applications are shipped in a
* compressed form. In addition the compressed bits for the full application, the
* system image contains a tiny stub comprised of only the Android manifest.
*
During the first boot, attempt to uncompress and install the full application. If
* the application can't be installed for any reason, disable the stub and prevent
* uncompressing the full application during future boots.
*
In order to forcefully attempt an installation of a full application, go to app
* settings and enable the application.
*/
private void decompressSystemApplications(@NonNull List stubSystemApps, int scanFlags) {
for (int i = stubSystemApps.size() - 1; i >= 0; --i) {
final String pkgName = stubSystemApps.get(i);
// skip if the system package is already disabled
if (mSettings.isDisabledSystemPackageLPr(pkgName)) {
stubSystemApps.remove(i);
continue;
}
// skip if the package isn't installed (?!); this should never happen
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg == null) {
stubSystemApps.remove(i);
continue;
}
// skip if the package has been disabled by the user
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM);
if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
stubSystemApps.remove(i);
continue;
}
}
if (DEBUG_COMPRESSION) {
Slog.i(TAG, "Uncompressing system stub; pkg: " + pkgName);
}
// uncompress the binary to its eventual destination on /data
final File scanFile = decompressPackage(pkg);
if (scanFile == null) {
continue;
}
// install the package to replace the stub on /system
try {
mSettings.disableSystemPackageLPw(pkgName, true /*replaced*/);
removePackageLI(pkg, true /*chatty*/);
scanPackageTracedLI(scanFile, 0 /*reparseFlags*/, scanFlags, 0, null);
ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
UserHandle.USER_SYSTEM, "android");
stubSystemApps.remove(i);
continue;
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage());
}
// any failed attempt to install the package will be cleaned up later
}
// disable any stub still left; these failed to install the full application
for (int i = stubSystemApps.size() - 1; i >= 0; --i) {
final String pkgName = stubSystemApps.get(i);
final PackageSetting ps = mSettings.mPackages.get(pkgName);
ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
UserHandle.USER_SYSTEM, "android");
logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName);
}
}
/**
* Decompresses the given package on the system image onto
* the /data partition.
* @return The directory the package was decompressed into. Otherwise, {@code null}.
*/
private File decompressPackage(PackageParser.Package pkg) {
final File[] compressedFiles = getCompressedFiles(pkg.codePath);
if (compressedFiles == null || compressedFiles.length == 0) {
if (DEBUG_COMPRESSION) {
Slog.i(TAG, "No files to decompress: " + pkg.baseCodePath);
}
return null;
}
final File dstCodePath =
getNextCodePath(Environment.getDataAppDirectory(null), pkg.packageName);
int ret = PackageManager.INSTALL_SUCCEEDED;
try {
Os.mkdir(dstCodePath.getAbsolutePath(), 0755);
Os.chmod(dstCodePath.getAbsolutePath(), 0755);
for (File srcFile : compressedFiles) {
final String srcFileName = srcFile.getName();
final String dstFileName = srcFileName.substring(
0, srcFileName.length() - COMPRESSED_EXTENSION.length());
final File dstFile = new File(dstCodePath, dstFileName);
ret = decompressFile(srcFile, dstFile);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
logCriticalInfo(Log.ERROR, "Failed to decompress"
+ "; pkg: " + pkg.packageName
+ ", file: " + dstFileName);
break;
}
}
} catch (ErrnoException e) {
logCriticalInfo(Log.ERROR, "Failed to decompress"
+ "; pkg: " + pkg.packageName
+ ", err: " + e.errno);
}
if (ret == PackageManager.INSTALL_SUCCEEDED) {
final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(dstCodePath);
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
null /*abiOverride*/);
} catch (IOException e) {
logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
+ "; pkg: " + pkg.packageName);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
} finally {
IoUtils.closeQuietly(handle);
}
}
if (ret != PackageManager.INSTALL_SUCCEEDED) {
if (dstCodePath == null || !dstCodePath.exists()) {
return null;
}
removeCodePathLI(dstCodePath);
return null;
}
return dstCodePath;
}
private void updateInstantAppInstallerLocked(String modifiedPackage) {
// we're only interested in updating the installer appliction when 1) it's not
// already set or 2) the modified package is the installer
if (mInstantAppInstallerActivity != null
&& !mInstantAppInstallerActivity.getComponentName().getPackageName()
.equals(modifiedPackage)) {
return;
}
setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr());
}
private static File preparePackageParserCache(boolean isUpgrade) {
if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
return null;
}
// Disable package parsing on eng builds to allow for faster incremental development.
if (Build.IS_ENG) {
return null;
}
if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) {
Slog.i(TAG, "Disabling package parser cache due to system property.");
return null;
}
// The base directory for the package parser cache lives under /data/system/.
final File cacheBaseDir = FileUtils.createDir(Environment.getDataSystemDirectory(),
"package_cache");
if (cacheBaseDir == null) {
return null;
}
// If this is a system upgrade scenario, delete the contents of the package cache dir.
// This also serves to "GC" unused entries when the package cache version changes (which
// can only happen during upgrades).
if (isUpgrade) {
FileUtils.deleteContents(cacheBaseDir);
}
// Return the versioned package cache directory. This is something like
// "/data/system/package_cache/1"
File cacheDir = FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION);
if (cacheDir == null) {
// Something went wrong. Attempt to delete everything and return.
Slog.wtf(TAG, "Cache directory cannot be created - wiping base dir " + cacheBaseDir);
FileUtils.deleteContentsAndDir(cacheBaseDir);
return null;
}
// The following is a workaround to aid development on non-numbered userdebug
// builds or cases where "adb sync" is used on userdebug builds. If we detect that
// the system partition is newer.
//
// NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build
// that starts with "eng." to signify that this is an engineering build and not
// destined for release.
if (Build.IS_USERDEBUG && Build.VERSION.INCREMENTAL.startsWith("eng.")) {
Slog.w(TAG, "Wiping cache directory because the system partition changed.");
// Heuristic: If the /system directory has been modified recently due to an "adb sync"
// or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable
// in general and should not be used for production changes. In this specific case,
// we know that they will work.
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
if (cacheDir.lastModified() < frameworkDir.lastModified()) {
FileUtils.deleteContents(cacheBaseDir);
cacheDir = FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION);
}
}
return cacheDir;
}
@Override
public boolean isFirstBoot() {
// allow instant applications
return mFirstBoot;
}
@Override
public boolean isOnlyCoreApps() {
// allow instant applications
return mOnlyCore;
}
@Override
public boolean isUpgrade() {
// allow instant applications
// The system property allows testing ota flow when upgraded to the same image.
return mIsUpgrade || SystemProperties.getBoolean(
"persist.pm.mock-upgrade", false /* default */);
}
private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() {
final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
final List matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/);
if (matches.size() == 1) {
return matches.get(0).getComponentInfo().packageName;
} else if (matches.size() == 0) {
Log.e(TAG, "There should probably be a verifier, but, none were found");
return null;
}
throw new RuntimeException("There must be exactly one verifier; found " + matches);
}
private @NonNull String getRequiredSharedLibraryLPr(String name, int version) {
synchronized (mPackages) {
SharedLibraryEntry libraryEntry = getSharedLibraryEntryLPr(name, version);
if (libraryEntry == null) {
throw new IllegalStateException("Missing required shared library:" + name);
}
return libraryEntry.apk;
}
}
private @NonNull String getRequiredInstallerLPr() {
final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
final List matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM);
if (matches.size() == 1) {
ResolveInfo resolveInfo = matches.get(0);
if (!resolveInfo.activityInfo.applicationInfo.isPrivilegedApp()) {
throw new RuntimeException("The installer must be a privileged app");
}
return matches.get(0).getComponentInfo().packageName;
} else {
throw new RuntimeException("There must be exactly one installer; found " + matches);
}
}
private @NonNull String getRequiredUninstallerLPr() {
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.fromParts(PACKAGE_SCHEME, "foo.bar", null));
final ResolveInfo resolveInfo = resolveIntent(intent, null,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM);
if (resolveInfo == null ||
mResolveActivity.name.equals(resolveInfo.getComponentInfo().name)) {
throw new RuntimeException("There must be exactly one uninstaller; found "
+ resolveInfo);
}
return resolveInfo.getComponentInfo().packageName;
}
private @NonNull ComponentName getIntentFilterVerifierComponentNameLPr() {
final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
final List matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/);
ResolveInfo best = null;
final int N = matches.size();
for (int i = 0; i < N; i++) {
final ResolveInfo cur = matches.get(i);
final String packageName = cur.getComponentInfo().packageName;
if (checkPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
continue;
}
if (best == null || cur.priority > best.priority) {
best = cur;
}
}
if (best != null) {
return best.getComponentInfo().getComponentName();
}
Slog.w(TAG, "Intent filter verifier not found");
return null;
}
@Override
public @Nullable ComponentName getInstantAppResolverComponent() {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
synchronized (mPackages) {
final Pair instantAppResolver = getInstantAppResolverLPr();
if (instantAppResolver == null) {
return null;
}
return instantAppResolver.first;
}
}
private @Nullable Pair getInstantAppResolverLPr() {
final String[] packageArray =
mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage);
if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) {
if (DEBUG_INSTANT) {
Slog.d(TAG, "Ephemeral resolver NOT found; empty package list");
}
return null;
}
final int callingUid = Binder.getCallingUid();
final int resolveFlags =
MATCH_DIRECT_BOOT_AWARE
| MATCH_DIRECT_BOOT_UNAWARE
| (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
String actionName = Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE;
final Intent resolverIntent = new Intent(actionName);
List resolvers = queryIntentServicesInternal(resolverIntent, null,
resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
final int N = resolvers.size();
if (N == 0) {
if (DEBUG_INSTANT) {
Slog.d(TAG, "Ephemeral resolver NOT found; no matching intent filters");
}
return null;
}
final Set possiblePackages = new ArraySet<>(Arrays.asList(packageArray));
for (int i = 0; i < N; i++) {
final ResolveInfo info = resolvers.get(i);
if (info.serviceInfo == null) {
continue;
}
final String packageName = info.serviceInfo.packageName;
if (!possiblePackages.contains(packageName) && !Build.IS_DEBUGGABLE) {
if (DEBUG_INSTANT) {
Slog.d(TAG, "Ephemeral resolver not in allowed package list;"
+ " pkg: " + packageName + ", info:" + info);
}
continue;
}
if (DEBUG_INSTANT) {
Slog.v(TAG, "Ephemeral resolver found;"
+ " pkg: " + packageName + ", info:" + info);
}
return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName);
}
if (DEBUG_INSTANT) {
Slog.v(TAG, "Ephemeral resolver NOT found");
}
return null;
}
private @Nullable ActivityInfo getInstantAppInstallerLPr() {
String[] orderedActions = Build.IS_ENG
? new String[]{
Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE + "_TEST",
Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE}
: new String[]{
Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE};
final int resolveFlags =
MATCH_DIRECT_BOOT_AWARE
| MATCH_DIRECT_BOOT_UNAWARE
| Intent.FLAG_IGNORE_EPHEMERAL
| (!Build.IS_ENG ? MATCH_SYSTEM_ONLY : 0);
final Intent intent = new Intent();
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
List matches = null;
for (String action : orderedActions) {
intent.setAction(action);
matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
resolveFlags, UserHandle.USER_SYSTEM);
if (matches.isEmpty()) {
if (DEBUG_INSTANT) {
Slog.d(TAG, "Instant App installer not found with " + action);
}
} else {
break;
}
}
Iterator iter = matches.iterator();
while (iter.hasNext()) {
final ResolveInfo rInfo = iter.next();
final PackageSetting ps = mSettings.mPackages.get(rInfo.activityInfo.packageName);
if (ps != null) {
final PermissionsState permissionsState = ps.getPermissionsState();
if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0)
|| Build.IS_ENG) {
continue;
}
}
iter.remove();
}
if (matches.size() == 0) {
return null;
} else if (matches.size() == 1) {
return (ActivityInfo) matches.get(0).getComponentInfo();
} else {
throw new RuntimeException(
"There must be at most one ephemeral installer; found " + matches);
}
}
private @Nullable ComponentName getInstantAppResolverSettingsLPr(
@NonNull ComponentName resolver) {
final Intent intent = new Intent(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS)
.addCategory(Intent.CATEGORY_DEFAULT)
.setPackage(resolver.getPackageName());
final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
List matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
UserHandle.USER_SYSTEM);
if (matches.isEmpty()) {
return null;
}
return matches.get(0).getComponentInfo().getComponentName();
}
private void primeDomainVerificationsLPw(int userId) {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG, "Priming domain verifications in user " + userId);
}
SystemConfig systemConfig = SystemConfig.getInstance();
ArraySet packages = systemConfig.getLinkedApps();
for (String packageName : packages) {
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg != null) {
if (!pkg.isSystem()) {
Slog.w(TAG, "Non-system app '" + packageName + "' in sysconfig ");
continue;
}
ArraySet domains = null;
for (PackageParser.Activity a : pkg.activities) {
for (ActivityIntentInfo filter : a.intents) {
if (hasValidDomains(filter)) {
if (domains == null) {
domains = new ArraySet();
}
domains.addAll(filter.getHostsList());
}
}
}
if (domains != null && domains.size() > 0) {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.v(TAG, " + " + packageName);
}
// 'Undefined' in the global IntentFilterVerificationInfo, i.e. the usual
// state w.r.t. the formal app-linkage "no verification attempted" state;
// and then 'always' in the per-user state actually used for intent resolution.
final IntentFilterVerificationInfo ivi;
ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName, domains);
ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
mSettings.updateIntentFilterVerificationStatusLPw(packageName,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, userId);
} else {
Slog.w(TAG, "Sysconfig package '" + packageName
+ "' does not handle web links");
}
} else {
Slog.w(TAG, "Unknown package " + packageName + " in sysconfig ");
}
}
scheduleWritePackageRestrictionsLocked(userId);
scheduleWriteSettingsLocked();
}
private void applyFactoryDefaultBrowserLPw(int userId) {
// The default browser app's package name is stored in a string resource,
// with a product-specific overlay used for vendor customization.
String browserPkg = mContext.getResources().getString(
com.android.internal.R.string.default_browser);
if (!TextUtils.isEmpty(browserPkg)) {
// non-empty string => required to be a known package
PackageSetting ps = mSettings.mPackages.get(browserPkg);
if (ps == null) {
Slog.e(TAG, "Product default browser app does not exist: " + browserPkg);
browserPkg = null;
} else {
mSettings.setDefaultBrowserPackageNameLPw(browserPkg, userId);
}
}
// Nothing valid explicitly set? Make the factory-installed browser the explicit
// default. If there's more than one, just leave everything alone.
if (browserPkg == null) {
calculateDefaultBrowserLPw(userId);
}
}
private void calculateDefaultBrowserLPw(int userId) {
List allBrowsers = resolveAllBrowserApps(userId);
final String browserPkg = (allBrowsers.size() == 1) ? allBrowsers.get(0) : null;
mSettings.setDefaultBrowserPackageNameLPw(browserPkg, userId);
}
private List resolveAllBrowserApps(int userId) {
// Resolve the canonical browser intent and check that the handleAllWebDataURI boolean is set
List list = queryIntentActivitiesInternal(sBrowserIntent, null,
PackageManager.MATCH_ALL, userId);
final int count = list.size();
List result = new ArrayList(count);
for (int i=0; i list = queryIntentActivitiesInternal(sBrowserIntent, null,
PackageManager.MATCH_ALL, userId);
final int N = list.size();
for (int i = 0; i < N; i++) {
ResolveInfo info = list.get(i);
if (info.priority >= 0 && packageName.equals(info.activityInfo.packageName)) {
return true;
}
}
return false;
}
private void checkDefaultBrowser() {
final int myUserId = UserHandle.myUserId();
final String packageName = getDefaultBrowserPackageName(myUserId);
if (packageName != null) {
PackageInfo info = getPackageInfo(packageName, 0, myUserId);
if (info == null) {
Slog.w(TAG, "Default browser no longer installed: " + packageName);
synchronized (mPackages) {
applyFactoryDefaultBrowserLPw(myUserId); // leaves ambiguous when > 1
}
}
}
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
return super.onTransact(code, data, reply, flags);
} catch (RuntimeException e) {
if (!(e instanceof SecurityException) && !(e instanceof IllegalArgumentException)) {
Slog.wtf(TAG, "Package Manager Crash", e);
}
throw e;
}
}
static int[] appendInts(int[] cur, int[] add) {
if (add == null) return cur;
if (cur == null) return add;
final int N = add.length;
for (int i=0; i
* Currently, there are three cases in which this can occur:
*
* - The calling application is a "special" process. Special processes
* are those with a UID < {@link Process#FIRST_APPLICATION_UID}.
* - The calling application has the permission
* {@link android.Manifest.permission#ACCESS_INSTANT_APPS}.
* - The calling application is the default launcher on the
* system partition.
*
*/
private boolean canViewInstantApps(int callingUid, int userId) {
if (callingUid < Process.FIRST_APPLICATION_UID) {
return true;
}
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.ACCESS_INSTANT_APPS) == PERMISSION_GRANTED) {
return true;
}
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.VIEW_INSTANT_APPS) == PERMISSION_GRANTED) {
final ComponentName homeComponent = getDefaultHomeActivity(userId);
if (homeComponent != null
&& isCallerSameApp(homeComponent.getPackageName(), callingUid)) {
return true;
}
}
return false;
}
private PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
if (ps == null) {
return null;
}
final int callingUid = Binder.getCallingUid();
// Filter out ephemeral app metadata:
// * The system/shell/root can see metadata for any app
// * An installed app can see metadata for 1) other installed apps
// and 2) ephemeral apps that have explicitly interacted with it
// * Ephemeral apps can only see their own data and exposed installed apps
// * Holding a signature permission allows seeing instant apps
if (filterAppAccessLPr(ps, callingUid, userId)) {
return null;
}
if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0
&& ps.isSystem()) {
flags |= MATCH_ANY_USER;
}
final PackageUserState state = ps.readUserState(userId);
PackageParser.Package p = ps.pkg;
if (p != null) {
final PermissionsState permissionsState = ps.getPermissionsState();
// Compute GIDs only if requested
final int[] gids = (flags & PackageManager.GET_GIDS) == 0
? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
// Compute granted permissions only if package has requested permissions
final Set permissions = ArrayUtils.isEmpty(p.requestedPermissions)
? Collections.emptySet() : permissionsState.getPermissions(userId);
PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags,
ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
if (packageInfo == null) {
return null;
}
packageInfo.packageName = packageInfo.applicationInfo.packageName =
resolveExternalPackageNameLPr(p);
return packageInfo;
} else if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && state.isAvailable(flags)) {
PackageInfo pi = new PackageInfo();
pi.packageName = ps.name;
pi.setLongVersionCode(ps.versionCode);
pi.sharedUserId = (ps.sharedUser != null) ? ps.sharedUser.name : null;
pi.firstInstallTime = ps.firstInstallTime;
pi.lastUpdateTime = ps.lastUpdateTime;
ApplicationInfo ai = new ApplicationInfo();
ai.packageName = ps.name;
ai.uid = UserHandle.getUid(userId, ps.appId);
ai.primaryCpuAbi = ps.primaryCpuAbiString;
ai.secondaryCpuAbi = ps.secondaryCpuAbiString;
ai.versionCode = ps.versionCode;
ai.flags = ps.pkgFlags;
ai.privateFlags = ps.pkgPrivateFlags;
pi.applicationInfo = PackageParser.generateApplicationInfo(ai, flags, state, userId);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for ["
+ ps.name + "]. Provides a minimum info.");
return pi;
} else {
return null;
}
}
@Override
public void checkPackageStartable(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
throw new SecurityException("Instant applications don't have access to this method");
}
final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId);
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
throw new SecurityException("Package " + packageName + " was not found!");
}
if (!ps.getInstalled(userId)) {
throw new SecurityException(
"Package " + packageName + " was not installed for user " + userId + "!");
}
if (mSafeMode && !ps.isSystem()) {
throw new SecurityException("Package " + packageName + " not a system app!");
}
if (mFrozenPackages.contains(packageName)) {
throw new SecurityException("Package " + packageName + " is currently frozen!");
}
if (!userKeyUnlocked && !ps.pkg.applicationInfo.isEncryptionAware()) {
throw new SecurityException("Package " + packageName + " is not encryption aware!");
}
}
}
@Override
public boolean isPackageAvailable(String packageName, int userId) {
if (!sUserManager.exists(userId)) return false;
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "is package available");
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
if (p != null) {
final PackageSetting ps = (PackageSetting) p.mExtras;
if (filterAppAccessLPr(ps, callingUid, userId)) {
return false;
}
if (ps != null) {
final PackageUserState state = ps.readUserState(userId);
if (state != null) {
return PackageParser.isAvailable(state);
}
}
}
}
return false;
}
@Override
public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
flags, Binder.getCallingUid(), userId);
}
@Override
public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage,
int flags, int userId) {
return getPackageInfoInternal(versionedPackage.getPackageName(),
versionedPackage.getLongVersionCode(), flags, Binder.getCallingUid(), userId);
}
/**
* Important: The provided filterCallingUid is used exclusively to filter out packages
* that can be seen based on user state. It's typically the original caller uid prior
* to clearing. Because it can only be provided by trusted code, it's value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
private PackageInfo getPackageInfoInternal(String packageName, long versionCode,
int flags, int filterCallingUid, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForPackage(flags, userId, packageName);
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get package info");
// reader
synchronized (mPackages) {
// Normalize package name to handle renamed packages and static libs
packageName = resolveInternalPackageNameLPr(packageName, versionCode);
final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
if (matchFactoryOnly) {
final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
if (ps != null) {
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
}
if (filterAppAccessLPr(ps, filterCallingUid, userId)) {
return null;
}
return generatePackageInfo(ps, flags, userId);
}
}
PackageParser.Package p = mPackages.get(packageName);
if (matchFactoryOnly && p != null && !isSystemApp(p)) {
return null;
}
if (DEBUG_PACKAGE_INFO)
Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
if (p != null) {
final PackageSetting ps = (PackageSetting) p.mExtras;
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
}
if (ps != null && filterAppAccessLPr(ps, filterCallingUid, userId)) {
return null;
}
return generatePackageInfo((PackageSetting)p.mExtras, flags, userId);
}
if (!matchFactoryOnly && (flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) return null;
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
}
if (filterAppAccessLPr(ps, filterCallingUid, userId)) {
return null;
}
return generatePackageInfo(ps, flags, userId);
}
}
return null;
}
private boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
if (isComponentVisibleToInstantApp(component, TYPE_ACTIVITY)) {
return true;
}
if (isComponentVisibleToInstantApp(component, TYPE_SERVICE)) {
return true;
}
if (isComponentVisibleToInstantApp(component, TYPE_PROVIDER)) {
return true;
}
return false;
}
private boolean isComponentVisibleToInstantApp(
@Nullable ComponentName component, @ComponentType int type) {
if (type == TYPE_ACTIVITY) {
final PackageParser.Activity activity = mActivities.mActivities.get(component);
if (activity == null) {
return false;
}
final boolean visibleToInstantApp =
(activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
final boolean explicitlyVisibleToInstantApp =
(activity.info.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
return visibleToInstantApp && explicitlyVisibleToInstantApp;
} else if (type == TYPE_RECEIVER) {
final PackageParser.Activity activity = mReceivers.mActivities.get(component);
if (activity == null) {
return false;
}
final boolean visibleToInstantApp =
(activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
final boolean explicitlyVisibleToInstantApp =
(activity.info.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
return visibleToInstantApp && !explicitlyVisibleToInstantApp;
} else if (type == TYPE_SERVICE) {
final PackageParser.Service service = mServices.mServices.get(component);
return service != null
? (service.info.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
: false;
} else if (type == TYPE_PROVIDER) {
final PackageParser.Provider provider = mProviders.mProviders.get(component);
return provider != null
? (provider.info.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
: false;
} else if (type == TYPE_UNKNOWN) {
return isComponentVisibleToInstantApp(component);
}
return false;
}
/**
* Returns whether or not access to the application should be filtered.
*
* Access may be limited based upon whether the calling or target applications
* are instant applications.
*
* @see #canAccessInstantApps(int)
*/
private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid,
@Nullable ComponentName component, @ComponentType int componentType, int userId) {
// if we're in an isolated process, get the real calling UID
if (Process.isIsolated(callingUid)) {
callingUid = mIsolatedOwners.get(callingUid);
}
final String instantAppPkgName = getInstantAppPackageName(callingUid);
final boolean callerIsInstantApp = instantAppPkgName != null;
if (ps == null) {
if (callerIsInstantApp) {
// pretend the application exists, but, needs to be filtered
return true;
}
return false;
}
// if the target and caller are the same application, don't filter
if (isCallerSameApp(ps.name, callingUid)) {
return false;
}
if (callerIsInstantApp) {
// both caller and target are both instant, but, different applications, filter
if (ps.getInstantApp(userId)) {
return true;
}
// request for a specific component; if it hasn't been explicitly exposed through
// property or instrumentation target, filter
if (component != null) {
final PackageParser.Instrumentation instrumentation =
mInstrumentation.get(component);
if (instrumentation != null
&& isCallerSameApp(instrumentation.info.targetPackage, callingUid)) {
return false;
}
return !isComponentVisibleToInstantApp(component, componentType);
}
// request for application; if no components have been explicitly exposed, filter
return !ps.pkg.visibleToInstantApps;
}
if (ps.getInstantApp(userId)) {
// caller can see all components of all instant applications, don't filter
if (canViewInstantApps(callingUid, userId)) {
return false;
}
// request for a specific instant application component, filter
if (component != null) {
return true;
}
// request for an instant application; if the caller hasn't been granted access, filter
return !mInstantAppRegistry.isInstantAccessGranted(
userId, UserHandle.getAppId(callingUid), ps.appId);
}
return false;
}
/**
* @see #filterAppAccessLPr(PackageSetting, int, ComponentName, boolean, int)
*/
private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, int userId) {
return filterAppAccessLPr(ps, callingUid, null, TYPE_UNKNOWN, userId);
}
private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
int flags) {
// Callers can access only the libs they depend on, otherwise they need to explicitly
// ask for the shared libraries given the caller is allowed to access all static libs.
if ((flags & PackageManager.MATCH_STATIC_SHARED_LIBRARIES) != 0) {
// System/shell/root get to see all static libs
final int appId = UserHandle.getAppId(uid);
if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
|| appId == Process.ROOT_UID) {
return false;
}
}
// No package means no static lib as it is always on internal storage
if (ps == null || ps.pkg == null || !ps.pkg.applicationInfo.isStaticSharedLibrary()) {
return false;
}
final SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(ps.pkg.staticSharedLibName,
ps.pkg.staticSharedLibVersion);
if (libEntry == null) {
return false;
}
final int resolvedUid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
final String[] uidPackageNames = getPackagesForUid(resolvedUid);
if (uidPackageNames == null) {
return true;
}
for (String uidPackageName : uidPackageNames) {
if (ps.name.equals(uidPackageName)) {
return false;
}
PackageSetting uidPs = mSettings.getPackageLPr(uidPackageName);
if (uidPs != null) {
final int index = ArrayUtils.indexOf(uidPs.usesStaticLibraries,
libEntry.info.getName());
if (index < 0) {
continue;
}
if (uidPs.pkg.usesStaticLibrariesVersions[index] == libEntry.info.getLongVersion()) {
return false;
}
}
}
return true;
}
@Override
public String[] currentToCanonicalPackageNames(String[] names) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return names;
}
final String[] out = new String[names.length];
// reader
synchronized (mPackages) {
final int callingUserId = UserHandle.getUserId(callingUid);
final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId);
for (int i=names.length-1; i>=0; i--) {
final PackageSetting ps = mSettings.mPackages.get(names[i]);
boolean translateName = false;
if (ps != null && ps.realName != null) {
final boolean targetIsInstantApp = ps.getInstantApp(callingUserId);
translateName = !targetIsInstantApp
|| canViewInstantApps
|| mInstantAppRegistry.isInstantAccessGranted(callingUserId,
UserHandle.getAppId(callingUid), ps.appId);
}
out[i] = translateName ? ps.realName : names[i];
}
}
return out;
}
@Override
public String[] canonicalToCurrentPackageNames(String[] names) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return names;
}
final String[] out = new String[names.length];
// reader
synchronized (mPackages) {
final int callingUserId = UserHandle.getUserId(callingUid);
final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId);
for (int i=names.length-1; i>=0; i--) {
final String cur = mSettings.getRenamedPackageLPr(names[i]);
boolean translateName = false;
if (cur != null) {
final PackageSetting ps = mSettings.mPackages.get(names[i]);
final boolean targetIsInstantApp =
ps != null && ps.getInstantApp(callingUserId);
translateName = !targetIsInstantApp
|| canViewInstantApps
|| mInstantAppRegistry.isInstantAccessGranted(callingUserId,
UserHandle.getAppId(callingUid), ps.appId);
}
out[i] = translateName ? cur : names[i];
}
}
return out;
}
@Override
public int getPackageUid(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return -1;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId, packageName);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid");
// reader
synchronized (mPackages) {
final PackageParser.Package p = mPackages.get(packageName);
if (p != null && p.isMatch(flags)) {
PackageSetting ps = (PackageSetting) p.mExtras;
if (filterAppAccessLPr(ps, callingUid, userId)) {
return -1;
}
return UserHandle.getUid(userId, p.applicationInfo.uid);
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null && ps.isMatch(flags)
&& !filterAppAccessLPr(ps, callingUid, userId)) {
return UserHandle.getUid(userId, ps.appId);
}
}
}
return -1;
}
@Override
public int[] getPackageGids(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId, packageName);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids");
// reader
synchronized (mPackages) {
final PackageParser.Package p = mPackages.get(packageName);
if (p != null && p.isMatch(flags)) {
PackageSetting ps = (PackageSetting) p.mExtras;
if (filterAppAccessLPr(ps, callingUid, userId)) {
return null;
}
// TODO: Shouldn't this be checking for package installed state for userId and
// return null?
return ps.getPermissionsState().computeGids(userId);
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null && ps.isMatch(flags)
&& !filterAppAccessLPr(ps, callingUid, userId)) {
return ps.getPermissionsState().computeGids(userId);
}
}
}
return null;
}
@Override
public PermissionInfo getPermissionInfo(String name, String packageName, int flags) {
return mPermissionManager.getPermissionInfo(name, packageName, flags, getCallingUid());
}
@Override
public @Nullable ParceledListSlice queryPermissionsByGroup(String groupName,
int flags) {
final List permissionList =
mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid());
return (permissionList == null) ? null : new ParceledListSlice<>(permissionList);
}
@Override
public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) {
return mPermissionManager.getPermissionGroupInfo(groupName, flags, getCallingUid());
}
@Override
public @NonNull ParceledListSlice getAllPermissionGroups(int flags) {
final List permissionList =
mPermissionManager.getAllPermissionGroups(flags, getCallingUid());
return (permissionList == null)
? ParceledListSlice.emptyList() : new ParceledListSlice<>(permissionList);
}
private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
int filterCallingUid, int userId) {
if (!sUserManager.exists(userId)) return null;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
}
if (filterAppAccessLPr(ps, filterCallingUid, userId)) {
return null;
}
if (ps.pkg == null) {
final PackageInfo pInfo = generatePackageInfo(ps, flags, userId);
if (pInfo != null) {
return pInfo.applicationInfo;
}
return null;
}
ApplicationInfo ai = PackageParser.generateApplicationInfo(ps.pkg, flags,
ps.readUserState(userId), userId);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
}
return ai;
}
return null;
}
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId);
}
/**
* Important: The provided filterCallingUid is used exclusively to filter out applications
* that can be seen based on user state. It's typically the original caller uid prior
* to clearing. Because it can only be provided by trusted code, it's value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
private ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
int filterCallingUid, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForApplication(flags, userId, packageName);
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get application info");
// writer
synchronized (mPackages) {
// Normalize package name to handle renamed packages and static libs
packageName = resolveInternalPackageNameLPr(packageName,
PackageManager.VERSION_CODE_HIGHEST);
PackageParser.Package p = mPackages.get(packageName);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getApplicationInfo " + packageName
+ ": " + p);
if (p != null) {
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) return null;
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
}
if (filterAppAccessLPr(ps, filterCallingUid, userId)) {
return null;
}
// Note: isEnabledLP() does not apply here - always return info
ApplicationInfo ai = PackageParser.generateApplicationInfo(
p, flags, ps.readUserState(userId), userId);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(p);
}
return ai;
}
if ("android".equals(packageName)||"system".equals(packageName)) {
return mAndroidApplication;
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
// Already generates the external package name
return generateApplicationInfoFromSettingsLPw(packageName,
flags, filterCallingUid, userId);
}
}
return null;
}
private String normalizePackageNameLPr(String packageName) {
String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
return normalizedPackageName != null ? normalizedPackageName : packageName;
}
@Override
public void deletePreloadsFileCache() {
if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) {
throw new SecurityException("Only system or settings may call deletePreloadsFileCache");
}
File dir = Environment.getDataPreloadsFileCacheDirectory();
Slog.i(TAG, "Deleting preloaded file cache " + dir);
FileUtils.deleteContents(dir);
}
@Override
public void freeStorageAndNotify(final String volumeUuid, final long freeStorageSize,
final int storageFlags, final IPackageDataObserver observer) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CLEAR_APP_CACHE, null);
mHandler.post(() -> {
boolean success = false;
try {
freeStorage(volumeUuid, freeStorageSize, storageFlags);
success = true;
} catch (IOException e) {
Slog.w(TAG, e);
}
if (observer != null) {
try {
observer.onRemoveCompleted(null, success);
} catch (RemoteException e) {
Slog.w(TAG, e);
}
}
});
}
@Override
public void freeStorage(final String volumeUuid, final long freeStorageSize,
final int storageFlags, final IntentSender pi) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CLEAR_APP_CACHE, TAG);
mHandler.post(() -> {
boolean success = false;
try {
freeStorage(volumeUuid, freeStorageSize, storageFlags);
success = true;
} catch (IOException e) {
Slog.w(TAG, e);
}
if (pi != null) {
try {
pi.sendIntent(null, success ? 1 : 0, null, null, null);
} catch (SendIntentException e) {
Slog.w(TAG, e);
}
}
});
}
/**
* Blocking call to clear various types of cached data across the system
* until the requested bytes are available.
*/
public void freeStorage(String volumeUuid, long bytes, int storageFlags) throws IOException {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
final File file = storage.findPathForUuid(volumeUuid);
if (file.getUsableSpace() >= bytes) return;
if (ENABLE_FREE_CACHE_V2) {
final boolean internalVolume = Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL,
volumeUuid);
final boolean aggressive = (storageFlags
& StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0;
final long reservedBytes = storage.getStorageCacheBytes(file, storageFlags);
// 1. Pre-flight to determine if we have any chance to succeed
// 2. Consider preloaded data (after 1w honeymoon, unless aggressive)
if (internalVolume && (aggressive || SystemProperties
.getBoolean("persist.sys.preloads.file_cache_expired", false))) {
deletePreloadsFileCache();
if (file.getUsableSpace() >= bytes) return;
}
// 3. Consider parsed APK data (aggressive only)
if (internalVolume && aggressive) {
FileUtils.deleteContents(mCacheDir);
if (file.getUsableSpace() >= bytes) return;
}
// 4. Consider cached app data (above quotas)
try {
mInstaller.freeCache(volumeUuid, bytes, reservedBytes,
Installer.FLAG_FREE_CACHE_V2);
} catch (InstallerException ignored) {
}
if (file.getUsableSpace() >= bytes) return;
// 5. Consider shared libraries with refcount=0 and age>min cache period
if (internalVolume && pruneUnusedStaticSharedLibraries(bytes,
android.provider.Settings.Global.getLong(mContext.getContentResolver(),
Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD))) {
return;
}
// 6. Consider dexopt output (aggressive only)
// TODO: Implement
// 7. Consider installed instant apps unused longer than min cache period
if (internalVolume && mInstantAppRegistry.pruneInstalledInstantApps(bytes,
android.provider.Settings.Global.getLong(mContext.getContentResolver(),
Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
InstantAppRegistry.DEFAULT_INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) {
return;
}
// 8. Consider cached app data (below quotas)
try {
mInstaller.freeCache(volumeUuid, bytes, reservedBytes,
Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA);
} catch (InstallerException ignored) {
}
if (file.getUsableSpace() >= bytes) return;
// 9. Consider DropBox entries
// TODO: Implement
// 10. Consider instant meta-data (uninstalled apps) older that min cache period
if (internalVolume && mInstantAppRegistry.pruneUninstalledInstantApps(bytes,
android.provider.Settings.Global.getLong(mContext.getContentResolver(),
Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
InstantAppRegistry.DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) {
return;
}
} else {
try {
mInstaller.freeCache(volumeUuid, bytes, 0, 0);
} catch (InstallerException ignored) {
}
if (file.getUsableSpace() >= bytes) return;
}
throw new IOException("Failed to free " + bytes + " on storage device at " + file);
}
private boolean pruneUnusedStaticSharedLibraries(long neededSpace, long maxCachePeriod)
throws IOException {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);
List packagesToDelete = null;
final long now = System.currentTimeMillis();
synchronized (mPackages) {
final int[] allUsers = sUserManager.getUserIds();
final int libCount = mSharedLibraries.size();
for (int i = 0; i < libCount; i++) {
final LongSparseArray versionedLib
= mSharedLibraries.valueAt(i);
if (versionedLib == null) {
continue;
}
final int versionCount = versionedLib.size();
for (int j = 0; j < versionCount; j++) {
SharedLibraryInfo libInfo = versionedLib.valueAt(j).info;
// Skip packages that are not static shared libs.
if (!libInfo.isStatic()) {
break;
}
// Important: We skip static shared libs used for some user since
// in such a case we need to keep the APK on the device. The check for
// a lib being used for any user is performed by the uninstall call.
final VersionedPackage declaringPackage = libInfo.getDeclaringPackage();
// Resolve the package name - we use synthetic package names internally
final String internalPackageName = resolveInternalPackageNameLPr(
declaringPackage.getPackageName(),
declaringPackage.getLongVersionCode());
final PackageSetting ps = mSettings.getPackageLPr(internalPackageName);
// Skip unused static shared libs cached less than the min period
// to prevent pruning a lib needed by a subsequently installed package.
if (ps == null || now - ps.lastUpdateTime < maxCachePeriod) {
continue;
}
if (packagesToDelete == null) {
packagesToDelete = new ArrayList<>();
}
packagesToDelete.add(new VersionedPackage(internalPackageName,
declaringPackage.getLongVersionCode()));
}
}
}
if (packagesToDelete != null) {
final int packageCount = packagesToDelete.size();
for (int i = 0; i < packageCount; i++) {
final VersionedPackage pkgToDelete = packagesToDelete.get(i);
// Delete the package synchronously (will fail of the lib used for any user).
if (deletePackageX(pkgToDelete.getPackageName(), pkgToDelete.getLongVersionCode(),
UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS)
== PackageManager.DELETE_SUCCEEDED) {
if (volume.getUsableSpace() >= neededSpace) {
return true;
}
}
}
}
return false;
}
/**
* Update given flags based on encryption status of current user.
*/
private int updateFlags(int flags, int userId) {
if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DIRECT_BOOT_AWARE)) != 0) {
// Caller expressed an explicit opinion about what encryption
// aware/unaware components they want to see, so fall through and
// give them what they want
} else {
// Caller expressed no opinion, so match based on user state
if (getUserManagerInternal().isUserUnlockingOrUnlocked(userId)) {
flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
} else {
flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE;
}
}
return flags;
}
private UserManagerInternal getUserManagerInternal() {
if (mUserManagerInternal == null) {
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
}
return mUserManagerInternal;
}
private ActivityManagerInternal getActivityManagerInternal() {
if (mActivityManagerInternal == null) {
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
}
return mActivityManagerInternal;
}
private DeviceIdleController.LocalService getDeviceIdleController() {
if (mDeviceIdleController == null) {
mDeviceIdleController =
LocalServices.getService(DeviceIdleController.LocalService.class);
}
return mDeviceIdleController;
}
/**
* Update given flags when being used to request {@link PackageInfo}.
*/
private int updateFlagsForPackage(int flags, int userId, Object cookie) {
final boolean isCallerSystemUser = UserHandle.getCallingUserId() == UserHandle.USER_SYSTEM;
boolean triaged = true;
if ((flags & (PackageManager.GET_ACTIVITIES | PackageManager.GET_RECEIVERS
| PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS)) != 0) {
// Caller is asking for component details, so they'd better be
// asking for specific encryption matching behavior, or be triaged
if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
triaged = false;
}
}
if ((flags & (PackageManager.MATCH_UNINSTALLED_PACKAGES
| PackageManager.MATCH_SYSTEM_ONLY
| PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
triaged = false;
}
if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
mPermissionManager.enforceCrossUserPermission(
Binder.getCallingUid(), userId, false, false,
"MATCH_ANY_USER flag requires INTERACT_ACROSS_USERS permission at "
+ Debug.getCallers(5));
} else if ((flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0 && isCallerSystemUser
&& sUserManager.hasManagedProfile(UserHandle.USER_SYSTEM)) {
// If the caller wants all packages and has a restricted profile associated with it,
// then match all users. This is to make sure that launchers that need to access work
// profile apps don't start breaking. TODO: Remove this hack when launchers stop using
// MATCH_UNINSTALLED_PACKAGES to query apps in other profiles. b/31000380
flags |= PackageManager.MATCH_ANY_USER;
}
if (DEBUG_TRIAGED_MISSING && (Binder.getCallingUid() == Process.SYSTEM_UID) && !triaged) {
Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
+ " with flags 0x" + Integer.toHexString(flags), new Throwable());
}
return updateFlags(flags, userId);
}
/**
* Update given flags when being used to request {@link ApplicationInfo}.
*/
private int updateFlagsForApplication(int flags, int userId, Object cookie) {
return updateFlagsForPackage(flags, userId, cookie);
}
/**
* Update given flags when being used to request {@link ComponentInfo}.
*/
private int updateFlagsForComponent(int flags, int userId, Object cookie) {
if (cookie instanceof Intent) {
if ((((Intent) cookie).getFlags() & Intent.FLAG_DEBUG_TRIAGED_MISSING) != 0) {
flags |= PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
}
}
boolean triaged = true;
// Caller is asking for component details, so they'd better be
// asking for specific encryption matching behavior, or be triaged
if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
triaged = false;
}
if (DEBUG_TRIAGED_MISSING && (Binder.getCallingUid() == Process.SYSTEM_UID) && !triaged) {
Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
+ " with flags 0x" + Integer.toHexString(flags), new Throwable());
}
return updateFlags(flags, userId);
}
/**
* Update given intent when being used to request {@link ResolveInfo}.
*/
private Intent updateIntentForResolve(Intent intent) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
}
if (DEBUG_PREFERRED) {
intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
}
return intent;
}
/**
* Update given flags when being used to request {@link ResolveInfo}.
* Instant apps are resolved specially, depending upon context. Minimally,
* {@code}flags{@code} must have the {@link PackageManager#MATCH_INSTANT}
* flag set. However, this flag is only honoured in three circumstances:
*
* - when called from a system process
* - when the caller holds the permission {@code android.permission.ACCESS_INSTANT_APPS}
* - when resolution occurs to start an activity with a {@code android.intent.action.VIEW}
* action and a {@code android.intent.category.BROWSABLE} category
*
*/
int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid) {
return updateFlagsForResolve(flags, userId, intent, callingUid,
false /*wantInstantApps*/, false /*onlyExposedExplicitly*/);
}
int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid,
boolean wantInstantApps) {
return updateFlagsForResolve(flags, userId, intent, callingUid,
wantInstantApps, false /*onlyExposedExplicitly*/);
}
int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid,
boolean wantInstantApps, boolean onlyExposedExplicitly) {
// Safe mode means we shouldn't match any third-party components
if (mSafeMode) {
flags |= PackageManager.MATCH_SYSTEM_ONLY;
}
if (getInstantAppPackageName(callingUid) != null) {
// But, ephemeral apps see both ephemeral and exposed, non-ephemeral components
if (onlyExposedExplicitly) {
flags |= PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY;
}
flags |= PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY;
flags |= PackageManager.MATCH_INSTANT;
} else {
final boolean wantMatchInstant = (flags & PackageManager.MATCH_INSTANT) != 0;
final boolean allowMatchInstant = wantInstantApps
|| (wantMatchInstant && canViewInstantApps(callingUid, userId));
flags &= ~(PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY
| PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY);
if (!allowMatchInstant) {
flags &= ~PackageManager.MATCH_INSTANT;
}
}
return updateFlagsForComponent(flags, userId, intent /*cookie*/);
}
@Override
public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
}
/**
* Important: The provided filterCallingUid is used exclusively to filter out activities
* that can be seen based on user state. It's typically the original caller uid prior
* to clearing. Because it can only be provided by trusted code, it's value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
private ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
int filterCallingUid, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId, component);
if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get activity info");
}
synchronized (mPackages) {
PackageParser.Activity a = mActivities.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (filterAppAccessLPr(ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
return null;
}
return PackageParser.generateActivityInfo(
a, flags, ps.readUserState(userId), userId);
}
if (mResolveComponentName.equals(component)) {
return PackageParser.generateActivityInfo(
mResolveActivity, flags, new PackageUserState(), userId);
}
}
return null;
}
private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
if (!getActivityManagerInternal().isCallerRecents(callingUid)) {
return false;
}
final long token = Binder.clearCallingIdentity();
try {
final int callingUserId = UserHandle.getUserId(callingUid);
if (ActivityManager.getCurrentUser() != callingUserId) {
return false;
}
return sUserManager.isSameProfileGroup(callingUserId, targetUserId);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public boolean activitySupportsIntent(ComponentName component, Intent intent,
String resolvedType) {
synchronized (mPackages) {
if (component.equals(mResolveComponentName)) {
// The resolver supports EVERYTHING!
return true;
}
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
PackageParser.Activity a = mActivities.mActivities.get(component);
if (a == null) {
return false;
}
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) {
return false;
}
if (filterAppAccessLPr(ps, callingUid, component, TYPE_ACTIVITY, callingUserId)) {
return false;
}
for (int i=0; i= 0) {
return true;
}
}
return false;
}
}
@Override
public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId, component);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get receiver info");
synchronized (mPackages) {
PackageParser.Activity a = mReceivers.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getReceiverInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (filterAppAccessLPr(ps, callingUid, component, TYPE_RECEIVER, userId)) {
return null;
}
return PackageParser.generateActivityInfo(
a, flags, ps.readUserState(userId), userId);
}
}
return null;
}
@Override
public ParceledListSlice getSharedLibraries(String packageName,
int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0");
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
flags = updateFlagsForPackage(flags, userId, null);
final boolean canSeeStaticLibraries =
mContext.checkCallingOrSelfPermission(INSTALL_PACKAGES)
== PERMISSION_GRANTED
|| mContext.checkCallingOrSelfPermission(DELETE_PACKAGES)
== PERMISSION_GRANTED
|| canRequestPackageInstallsInternal(packageName,
PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId,
false /* throwIfPermNotDeclared*/)
|| mContext.checkCallingOrSelfPermission(REQUEST_DELETE_PACKAGES)
== PERMISSION_GRANTED;
synchronized (mPackages) {
List result = null;
final int libCount = mSharedLibraries.size();
for (int i = 0; i < libCount; i++) {
LongSparseArray versionedLib = mSharedLibraries.valueAt(i);
if (versionedLib == null) {
continue;
}
final int versionCount = versionedLib.size();
for (int j = 0; j < versionCount; j++) {
SharedLibraryInfo libInfo = versionedLib.valueAt(j).info;
if (!canSeeStaticLibraries && libInfo.isStatic()) {
break;
}
final long identity = Binder.clearCallingIdentity();
try {
PackageInfo packageInfo = getPackageInfoVersioned(
libInfo.getDeclaringPackage(), flags
| PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
if (packageInfo == null) {
continue;
}
} finally {
Binder.restoreCallingIdentity(identity);
}
SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getName(),
libInfo.getLongVersion(), libInfo.getType(),
libInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr(libInfo,
flags, userId));
if (result == null) {
result = new ArrayList<>();
}
result.add(resLibInfo);
}
}
return result != null ? new ParceledListSlice<>(result) : null;
}
}
private List getPackagesUsingSharedLibraryLPr(
SharedLibraryInfo libInfo, int flags, int userId) {
List versionedPackages = null;
final int packageCount = mSettings.mPackages.size();
for (int i = 0; i < packageCount; i++) {
PackageSetting ps = mSettings.mPackages.valueAt(i);
if (ps == null) {
continue;
}
if (!ps.getUserState().get(userId).isAvailable(flags)) {
continue;
}
final String libName = libInfo.getName();
if (libInfo.isStatic()) {
final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName);
if (libIdx < 0) {
continue;
}
if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getLongVersion()) {
continue;
}
if (versionedPackages == null) {
versionedPackages = new ArrayList<>();
}
// If the dependent is a static shared lib, use the public package name
String dependentPackageName = ps.name;
if (ps.pkg != null && ps.pkg.applicationInfo.isStaticSharedLibrary()) {
dependentPackageName = ps.pkg.manifestPackageName;
}
versionedPackages.add(new VersionedPackage(dependentPackageName, ps.versionCode));
} else if (ps.pkg != null) {
if (ArrayUtils.contains(ps.pkg.usesLibraries, libName)
|| ArrayUtils.contains(ps.pkg.usesOptionalLibraries, libName)) {
if (versionedPackages == null) {
versionedPackages = new ArrayList<>();
}
versionedPackages.add(new VersionedPackage(ps.name, ps.versionCode));
}
}
}
return versionedPackages;
}
@Override
public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId, component);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get service info");
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getServiceInfo " + component + ": " + s);
if (s != null && mSettings.isEnabledAndMatchLPr(s.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (filterAppAccessLPr(ps, callingUid, component, TYPE_SERVICE, userId)) {
return null;
}
return PackageParser.generateServiceInfo(
s, flags, ps.readUserState(userId), userId);
}
}
return null;
}
@Override
public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId, component);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get provider info");
synchronized (mPackages) {
PackageParser.Provider p = mProviders.mProviders.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getProviderInfo " + component + ": " + p);
if (p != null && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) {
PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) {
return null;
}
return PackageParser.generateProviderInfo(
p, flags, ps.readUserState(userId), userId);
}
}
return null;
}
@Override
public String[] getSystemSharedLibraryNames() {
// allow instant applications
synchronized (mPackages) {
Set libs = null;
final int libCount = mSharedLibraries.size();
for (int i = 0; i < libCount; i++) {
LongSparseArray versionedLib = mSharedLibraries.valueAt(i);
if (versionedLib == null) {
continue;
}
final int versionCount = versionedLib.size();
for (int j = 0; j < versionCount; j++) {
SharedLibraryEntry libEntry = versionedLib.valueAt(j);
if (!libEntry.info.isStatic()) {
if (libs == null) {
libs = new ArraySet<>();
}
libs.add(libEntry.info.getName());
break;
}
PackageSetting ps = mSettings.getPackageLPr(libEntry.apk);
if (ps != null && !filterSharedLibPackageLPr(ps, Binder.getCallingUid(),
UserHandle.getUserId(Binder.getCallingUid()),
PackageManager.MATCH_STATIC_SHARED_LIBRARIES)) {
if (libs == null) {
libs = new ArraySet<>();
}
libs.add(libEntry.info.getName());
break;
}
}
}
if (libs != null) {
String[] libsArray = new String[libs.size()];
libs.toArray(libsArray);
return libsArray;
}
return null;
}
}
@Override
public @NonNull String getServicesSystemSharedLibraryPackageName() {
// allow instant applications
synchronized (mPackages) {
return mServicesSystemSharedLibraryPackageName;
}
}
@Override
public @NonNull String getSharedSystemSharedLibraryPackageName() {
// allow instant applications
synchronized (mPackages) {
return mSharedSystemSharedLibraryPackageName;
}
}
private void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) {
for (int i = userList.length - 1; i >= 0; --i) {
final int userId = userList[i];
// don't add instant app to the list of updates
if (pkgSetting.getInstantApp(userId)) {
continue;
}
SparseArray changedPackages = mChangedPackages.get(userId);
if (changedPackages == null) {
changedPackages = new SparseArray<>();
mChangedPackages.put(userId, changedPackages);
}
Map sequenceNumbers = mChangedPackagesSequenceNumbers.get(userId);
if (sequenceNumbers == null) {
sequenceNumbers = new HashMap<>();
mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers);
}
final Integer sequenceNumber = sequenceNumbers.get(pkgSetting.name);
if (sequenceNumber != null) {
changedPackages.remove(sequenceNumber);
}
changedPackages.put(mChangedPackagesSequenceNumber, pkgSetting.name);
sequenceNumbers.put(pkgSetting.name, mChangedPackagesSequenceNumber);
}
mChangedPackagesSequenceNumber++;
}
@Override
public ChangedPackages getChangedPackages(int sequenceNumber, int userId) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
synchronized (mPackages) {
if (sequenceNumber >= mChangedPackagesSequenceNumber) {
return null;
}
final SparseArray changedPackages = mChangedPackages.get(userId);
if (changedPackages == null) {
return null;
}
final List packageNames =
new ArrayList<>(mChangedPackagesSequenceNumber - sequenceNumber);
for (int i = sequenceNumber; i < mChangedPackagesSequenceNumber; i++) {
final String packageName = changedPackages.get(i);
if (packageName != null) {
packageNames.add(packageName);
}
}
return packageNames.isEmpty()
? null : new ChangedPackages(mChangedPackagesSequenceNumber, packageNames);
}
}
@Override
public @NonNull ParceledListSlice getSystemAvailableFeatures() {
// allow instant applications
ArrayList res;
synchronized (mAvailableFeatures) {
res = new ArrayList<>(mAvailableFeatures.size() + 1);
res.addAll(mAvailableFeatures.values());
}
final FeatureInfo fi = new FeatureInfo();
fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version",
FeatureInfo.GL_ES_VERSION_UNDEFINED);
res.add(fi);
return new ParceledListSlice<>(res);
}
@Override
public boolean hasSystemFeature(String name, int version) {
// allow instant applications
synchronized (mAvailableFeatures) {
final FeatureInfo feat = mAvailableFeatures.get(name);
if (feat == null) {
return false;
} else {
return feat.version >= version;
}
}
}
@Override
public int checkPermission(String permName, String pkgName, int userId) {
return mPermissionManager.checkPermission(permName, pkgName, getCallingUid(), userId);
}
@Override
public int checkUidPermission(String permName, int uid) {
synchronized (mPackages) {
final String[] packageNames = getPackagesForUid(uid);
final PackageParser.Package pkg = (packageNames != null && packageNames.length > 0)
? mPackages.get(packageNames[0])
: null;
return mPermissionManager.checkUidPermission(permName, pkg, uid, getCallingUid());
}
}
@Override
public boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId) {
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"isPermissionRevokedByPolicy for user " + userId);
}
if (checkPermission(permission, packageName, userId)
== PackageManager.PERMISSION_GRANTED) {
return false;
}
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
if (!isCallerSameApp(packageName, callingUid)) {
return false;
}
} else {
if (isInstantApp(packageName, userId)) {
return false;
}
}
final long identity = Binder.clearCallingIdentity();
try {
final int flags = getPermissionFlags(permission, packageName, userId);
return (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public String getPermissionControllerPackageName() {
synchronized (mPackages) {
return mRequiredInstallerPackage;
}
}
private boolean addDynamicPermission(PermissionInfo info, final boolean async) {
return mPermissionManager.addDynamicPermission(
info, async, getCallingUid(), new PermissionCallback() {
@Override
public void onPermissionChanged() {
if (!async) {
mSettings.writeLPr();
} else {
scheduleWriteSettingsLocked();
}
}
});
}
@Override
public boolean addPermission(PermissionInfo info) {
synchronized (mPackages) {
return addDynamicPermission(info, false);
}
}
@Override
public boolean addPermissionAsync(PermissionInfo info) {
synchronized (mPackages) {
return addDynamicPermission(info, true);
}
}
@Override
public void removePermission(String permName) {
mPermissionManager.removeDynamicPermission(permName, getCallingUid(), mPermissionCallback);
}
@Override
public void grantRuntimePermission(String packageName, String permName, final int userId) {
mPermissionManager.grantRuntimePermission(permName, packageName, false /*overridePolicy*/,
getCallingUid(), userId, mPermissionCallback);
}
@Override
public void revokeRuntimePermission(String packageName, String permName, int userId) {
mPermissionManager.revokeRuntimePermission(permName, packageName, false /*overridePolicy*/,
getCallingUid(), userId, mPermissionCallback);
}
@Override
public void resetRuntimePermissions() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
"revokeRuntimePermission");
int callingUid = Binder.getCallingUid();
if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"resetRuntimePermissions");
}
synchronized (mPackages) {
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(),
mPermissionCallback);
for (int userId : UserManagerService.getInstance().getUserIds()) {
final int packageCount = mPackages.size();
for (int i = 0; i < packageCount; i++) {
PackageParser.Package pkg = mPackages.valueAt(i);
if (!(pkg.mExtras instanceof PackageSetting)) {
continue;
}
PackageSetting ps = (PackageSetting) pkg.mExtras;
resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, userId);
}
}
}
}
@Override
public int getPermissionFlags(String permName, String packageName, int userId) {
return mPermissionManager.getPermissionFlags(
permName, packageName, getCallingUid(), userId);
}
@Override
public void updatePermissionFlags(String permName, String packageName, int flagMask,
int flagValues, int userId) {
mPermissionManager.updatePermissionFlags(
permName, packageName, flagMask, flagValues, getCallingUid(), userId,
mPermissionCallback);
}
/**
* Update the permission flags for all packages and runtime permissions of a user in order
* to allow device or profile owner to remove POLICY_FIXED.
*/
@Override
public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) {
synchronized (mPackages) {
final boolean changed = mPermissionManager.updatePermissionFlagsForAllApps(
flagMask, flagValues, getCallingUid(), userId, mPackages.values(),
mPermissionCallback);
if (changed) {
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
}
}
@Override
public boolean shouldShowRequestPermissionRationale(String permissionName,
String packageName, int userId) {
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"canShowRequestPermissionRationale for user " + userId);
}
final int uid = getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId);
if (UserHandle.getAppId(getCallingUid()) != UserHandle.getAppId(uid)) {
return false;
}
if (checkPermission(permissionName, packageName, userId)
== PackageManager.PERMISSION_GRANTED) {
return false;
}
final int flags;
final long identity = Binder.clearCallingIdentity();
try {
flags = getPermissionFlags(permissionName,
packageName, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
| PackageManager.FLAG_PERMISSION_POLICY_FIXED
| PackageManager.FLAG_PERMISSION_USER_FIXED;
if ((flags & fixedFlags) != 0) {
return false;
}
return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
}
@Override
public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS,
"addOnPermissionsChangeListener");
synchronized (mPackages) {
mOnPermissionChangeListeners.addListenerLocked(listener);
}
}
@Override
public void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
throw new SecurityException("Instant applications don't have access to this method");
}
synchronized (mPackages) {
mOnPermissionChangeListeners.removeListenerLocked(listener);
}
}
@Override
public boolean isProtectedBroadcast(String actionName) {
// allow instant applications
synchronized (mProtectedBroadcasts) {
if (mProtectedBroadcasts.contains(actionName)) {
return true;
} else if (actionName != null) {
// TODO: remove these terrible hacks
if (actionName.startsWith("android.net.netmon.lingerExpired")
|| actionName.startsWith("com.android.server.sip.SipWakeupTimer")
|| actionName.startsWith("com.android.internal.telephony.data-reconnect")
|| actionName.startsWith("android.net.netmon.launchCaptivePortalApp")) {
return true;
}
}
}
return false;
}
@Override
public int checkSignatures(String pkg1, String pkg2) {
synchronized (mPackages) {
final PackageParser.Package p1 = mPackages.get(pkg1);
final PackageParser.Package p2 = mPackages.get(pkg2);
if (p1 == null || p1.mExtras == null
|| p2 == null || p2.mExtras == null) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final PackageSetting ps1 = (PackageSetting) p1.mExtras;
final PackageSetting ps2 = (PackageSetting) p2.mExtras;
if (filterAppAccessLPr(ps1, callingUid, callingUserId)
|| filterAppAccessLPr(ps2, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
return compareSignatures(p1.mSigningDetails.signatures, p2.mSigningDetails.signatures);
}
}
@Override
public int checkUidSignatures(int uid1, int uid2) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
// Map to base uids.
uid1 = UserHandle.getAppId(uid1);
uid2 = UserHandle.getAppId(uid2);
// reader
synchronized (mPackages) {
Signature[] s1;
Signature[] s2;
Object obj = mSettings.getUserIdLPr(uid1);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
s1 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
s1 = ps.signatures.mSigningDetails.signatures;
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
obj = mSettings.getUserIdLPr(uid2);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
s2 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
s2 = ps.signatures.mSigningDetails.signatures;
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
return compareSignatures(s1, s2);
}
}
@Override
public boolean hasSigningCertificate(
String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
synchronized (mPackages) {
final PackageParser.Package p = mPackages.get(packageName);
if (p == null || p.mExtras == null) {
return false;
}
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final PackageSetting ps = (PackageSetting) p.mExtras;
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return false;
}
switch (type) {
case CERT_INPUT_RAW_X509:
return p.mSigningDetails.hasCertificate(certificate);
case CERT_INPUT_SHA256:
return p.mSigningDetails.hasSha256Certificate(certificate);
default:
return false;
}
}
}
@Override
public boolean hasUidSigningCertificate(
int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
// Map to base uids.
uid = UserHandle.getAppId(uid);
// reader
synchronized (mPackages) {
final PackageParser.SigningDetails signingDetails;
final Object obj = mSettings.getUserIdLPr(uid);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
if (isCallerInstantApp) {
return false;
}
signingDetails = ((SharedUserSetting)obj).signatures.mSigningDetails;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return false;
}
signingDetails = ps.signatures.mSigningDetails;
} else {
return false;
}
} else {
return false;
}
switch (type) {
case CERT_INPUT_RAW_X509:
return signingDetails.hasCertificate(certificate);
case CERT_INPUT_SHA256:
return signingDetails.hasSha256Certificate(certificate);
default:
return false;
}
}
}
/**
* This method should typically only be used when granting or revoking
* permissions, since the app may immediately restart after this call.
*
* If you're doing surgery on app code/data, use {@link PackageFreezer} to
* guard your work against the app being relaunched.
*/
private void killUid(int appId, int userId, String reason) {
final long identity = Binder.clearCallingIdentity();
try {
IActivityManager am = ActivityManager.getService();
if (am != null) {
try {
am.killUid(appId, userId, reason);
} catch (RemoteException e) {
/* ignore - same process */
}
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
* If the database version for this type of package (internal storage or
* external storage) is less than the version where package signatures
* were updated, return true.
*/
private boolean isCompatSignatureUpdateNeeded(PackageParser.Package scannedPkg) {
final VersionInfo ver = getSettingsVersionForPackage(scannedPkg);
return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY;
}
private boolean isRecoverSignatureUpdateNeeded(PackageParser.Package scannedPkg) {
final VersionInfo ver = getSettingsVersionForPackage(scannedPkg);
return ver.databaseVersion < DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
}
@Override
public List getAllPackages() {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
synchronized (mPackages) {
if (canViewInstantApps(callingUid, callingUserId)) {
return new ArrayList(mPackages.keySet());
}
final String instantAppPkgName = getInstantAppPackageName(callingUid);
final List result = new ArrayList<>();
if (instantAppPkgName != null) {
// caller is an instant application; filter unexposed applications
for (PackageParser.Package pkg : mPackages.values()) {
if (!pkg.visibleToInstantApps) {
continue;
}
result.add(pkg.packageName);
}
} else {
// caller is a normal application; filter instant applications
for (PackageParser.Package pkg : mPackages.values()) {
final PackageSetting ps =
pkg.mExtras != null ? (PackageSetting) pkg.mExtras : null;
if (ps != null
&& ps.getInstantApp(callingUserId)
&& !mInstantAppRegistry.isInstantAccessGranted(
callingUserId, UserHandle.getAppId(callingUid), ps.appId)) {
continue;
}
result.add(pkg.packageName);
}
}
return result;
}
}
@Override
public String[] getPackagesForUid(int uid) {
final int callingUid = Binder.getCallingUid();
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
final int userId = UserHandle.getUserId(uid);
uid = UserHandle.getAppId(uid);
// reader
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(uid);
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
return null;
}
final SharedUserSetting sus = (SharedUserSetting) obj;
final int N = sus.packages.size();
String[] res = new String[N];
final Iterator it = sus.packages.iterator();
int i = 0;
while (it.hasNext()) {
PackageSetting ps = it.next();
if (ps.getInstalled(userId)) {
res[i++] = ps.name;
} else {
res = ArrayUtils.removeElement(String.class, res, res[i]);
}
}
return res;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (ps.getInstalled(userId) && !filterAppAccessLPr(ps, callingUid, userId)) {
return new String[]{ps.name};
}
}
}
return null;
}
@Override
public String getNameForUid(int uid) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return null;
}
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.name + ":" + sus.userId;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
return null;
}
return ps.name;
}
return null;
}
}
@Override
public String[] getNamesForUids(int[] uids) {
if (uids == null || uids.length == 0) {
return null;
}
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return null;
}
final String[] names = new String[uids.length];
synchronized (mPackages) {
for (int i = uids.length - 1; i >= 0; i--) {
final int uid = uids[i];
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
names[i] = "shared:" + sus.name;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
names[i] = null;
} else {
names[i] = ps.name;
}
} else {
names[i] = null;
}
}
}
return names;
}
@Override
public int getUidForSharedUser(String sharedUserName) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return -1;
}
if (sharedUserName == null) {
return -1;
}
// reader
synchronized (mPackages) {
SharedUserSetting suid;
try {
suid = mSettings.getSharedUserLPw(sharedUserName, 0, 0, false);
if (suid != null) {
return suid.userId;
}
} catch (PackageManagerException ignore) {
// can't happen, but, still need to catch it
}
return -1;
}
}
@Override
public int getFlagsForUid(int uid) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return 0;
}
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.pkgFlags;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
return 0;
}
return ps.pkgFlags;
}
}
return 0;
}
@Override
public int getPrivateFlagsForUid(int uid) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return 0;
}
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.pkgPrivateFlags;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
return 0;
}
return ps.pkgPrivateFlags;
}
}
return 0;
}
@Override
public boolean isUidPrivileged(int uid) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return false;
}
uid = UserHandle.getAppId(uid);
// reader
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(uid);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
final Iterator it = sus.packages.iterator();
while (it.hasNext()) {
if (it.next().isPrivileged()) {
return true;
}
}
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
return ps.isPrivileged();
}
}
return false;
}
@Override
public String[] getAppOpPermissionPackages(String permName) {
return mPermissionManager.getAppOpPermissionPackages(permName);
}
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
return resolveIntentInternal(
intent, resolvedType, flags, userId, false /*resolveForStart*/);
}
/**
* Normally instant apps can only be resolved when they're visible to the caller.
* However, if {@code resolveForStart} is {@code true}, all instant apps are visible
* since we need to allow the system to start any installed application.
*/
private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
int flags, int userId, boolean resolveForStart) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForResolve(flags, userId, intent, callingUid, resolveForStart);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
final List query = queryIntentActivitiesInternal(intent, resolvedType,
flags, callingUid, userId, resolveForStart, true /*allowDynamicSplits*/);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
final ResolveInfo bestChoice =
chooseBestActivity(intent, resolvedType, flags, query, userId);
return bestChoice;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
@Override
public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) {
if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) {
throw new SecurityException(
"findPersistentPreferredActivity can only be run by the system");
}
if (!sUserManager.exists(userId)) {
return null;
}
final int callingUid = Binder.getCallingUid();
intent = updateIntentForResolve(intent);
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
final int flags = updateFlagsForResolve(
0, userId, intent, callingUid, false /*includeInstantApps*/);
final List query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
synchronized (mPackages) {
return findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false,
userId);
}
}
@Override
public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
IntentFilter filter, int match, ComponentName activity) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return;
}
final int userId = UserHandle.getCallingUserId();
if (DEBUG_PREFERRED) {
Log.v(TAG, "setLastChosenActivity intent=" + intent
+ " resolvedType=" + resolvedType
+ " flags=" + flags
+ " filter=" + filter
+ " match=" + match
+ " activity=" + activity);
filter.dump(new PrintStreamPrinter(System.out), " ");
}
intent.setComponent(null);
final List query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
// Find any earlier preferred or last chosen entries and nuke them
findPreferredActivity(intent, resolvedType,
flags, query, 0, false, true, false, userId);
// Add the new activity as the last chosen for this filter
addPreferredActivityInternal(filter, match, null, activity, false, userId,
"Setting last chosen");
}
@Override
public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
final int userId = UserHandle.getCallingUserId();
if (DEBUG_PREFERRED) Log.v(TAG, "Querying last chosen activity for " + intent);
final List query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
return findPreferredActivity(intent, resolvedType, flags, query, 0,
false, false, false, userId);
}
/**
* Returns whether or not instant apps have been disabled remotely.
*/
private boolean areWebInstantAppsDisabled() {
return mWebInstantAppsDisabled;
}
private boolean isInstantAppResolutionAllowed(
Intent intent, List resolvedActivities, int userId,
boolean skipPackageCheck) {
if (mInstantAppResolverConnection == null) {
return false;
}
if (mInstantAppInstallerActivity == null) {
return false;
}
if (intent.getComponent() != null) {
return false;
}
if ((intent.getFlags() & Intent.FLAG_IGNORE_EPHEMERAL) != 0) {
return false;
}
if (!skipPackageCheck && intent.getPackage() != null) {
return false;
}
if (!intent.isWebIntent()) {
// for non web intents, we should not resolve externally if an app already exists to
// handle it or if the caller didn't explicitly request it.
if ((resolvedActivities != null && resolvedActivities.size() != 0)
|| (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) {
return false;
}
} else {
if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
return false;
} else if (areWebInstantAppsDisabled()) {
return false;
}
}
// Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
// Or if there's already an ephemeral app installed that handles the action
synchronized (mPackages) {
final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
for (int n = 0; n < count; n++) {
final ResolveInfo info = resolvedActivities.get(n);
final String packageName = info.activityInfo.packageName;
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
// only check domain verification status if the app is not a browser
if (!info.handleAllWebDataURI) {
// Try to get the status from User settings first
final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
final int status = (int) (packedStatus >> 32);
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
|| status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
if (DEBUG_INSTANT) {
Slog.v(TAG, "DENY instant app;"
+ " pkg: " + packageName + ", status: " + status);
}
return false;
}
}
if (ps.getInstantApp(userId)) {
if (DEBUG_INSTANT) {
Slog.v(TAG, "DENY instant app installed;"
+ " pkg: " + packageName);
}
return false;
}
}
}
}
// We've exhausted all ways to deny ephemeral application; let the system look for them.
return true;
}
private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
Intent origIntent, String resolvedType, String callingPackage,
Bundle verificationBundle, int userId) {
final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO,
new InstantAppRequest(responseObj, origIntent, resolvedType,
callingPackage, userId, verificationBundle, false /*resolveForStart*/));
mHandler.sendMessage(msg);
}
private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
int flags, List query, int userId) {
if (query != null) {
final int N = query.size();
if (N == 1) {
return query.get(0);
} else if (N > 1) {
final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
// If there is more than one activity with the same priority,
// then let the user decide between them.
ResolveInfo r0 = query.get(0);
ResolveInfo r1 = query.get(1);
if (DEBUG_INTENT_MATCHING || debug) {
Slog.v(TAG, r0.activityInfo.name + "=" + r0.priority + " vs "
+ r1.activityInfo.name + "=" + r1.priority);
}
// If the first activity has a higher priority, or a different
// default, then it is always desirable to pick it.
if (r0.priority != r1.priority
|| r0.preferredOrder != r1.preferredOrder
|| r0.isDefault != r1.isDefault) {
return query.get(0);
}
// If we have saved a preference for a preferred activity for
// this Intent, use that.
ResolveInfo ri = findPreferredActivity(intent, resolvedType,
flags, query, r0.priority, true, false, debug, userId);
if (ri != null) {
return ri;
}
// If we have an ephemeral app, use it
for (int i = 0; i < N; i++) {
ri = query.get(i);
if (ri.activityInfo.applicationInfo.isInstantApp()) {
final String packageName = ri.activityInfo.packageName;
final PackageSetting ps = mSettings.mPackages.get(packageName);
final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
final int status = (int)(packedStatus >> 32);
if (status != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
return ri;
}
}
}
ri = new ResolveInfo(mResolveInfo);
ri.activityInfo = new ActivityInfo(ri.activityInfo);
ri.activityInfo.labelRes = ResolverActivity.getLabelRes(intent.getAction());
// If all of the options come from the same package, show the application's
// label and icon instead of the generic resolver's.
// Some calls like Intent.resolveActivityInfo query the ResolveInfo from here
// and then throw away the ResolveInfo itself, meaning that the caller loses
// the resolvePackageName. Therefore the activityInfo.labelRes above provides
// a fallback for this case; we only set the target package's resources on
// the ResolveInfo, not the ActivityInfo.
final String intentPackage = intent.getPackage();
if (!TextUtils.isEmpty(intentPackage) && allHavePackage(query, intentPackage)) {
final ApplicationInfo appi = query.get(0).activityInfo.applicationInfo;
ri.resolvePackageName = intentPackage;
if (userNeedsBadging(userId)) {
ri.noResourceId = true;
} else {
ri.icon = appi.icon;
}
ri.iconResourceId = appi.icon;
ri.labelRes = appi.labelRes;
}
ri.activityInfo.applicationInfo = new ApplicationInfo(
ri.activityInfo.applicationInfo);
if (userId != 0) {
ri.activityInfo.applicationInfo.uid = UserHandle.getUid(userId,
UserHandle.getAppId(ri.activityInfo.applicationInfo.uid));
}
// Make sure that the resolver is displayable in car mode
if (ri.activityInfo.metaData == null) ri.activityInfo.metaData = new Bundle();
ri.activityInfo.metaData.putBoolean(Intent.METADATA_DOCK_HOME, true);
return ri;
}
}
return null;
}
/**
* Return true if the given list is not empty and all of its contents have
* an activityInfo with the given package name.
*/
private boolean allHavePackage(List list, String packageName) {
if (ArrayUtils.isEmpty(list)) {
return false;
}
for (int i = 0, N = list.size(); i < N; i++) {
final ResolveInfo ri = list.get(i);
final ActivityInfo ai = ri != null ? ri.activityInfo : null;
if (ai == null || !packageName.equals(ai.packageName)) {
return false;
}
}
return true;
}
private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType,
int flags, List query, boolean debug, int userId) {
final int N = query.size();
PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities
.get(userId);
// Get the list of persistent preferred activities that handle the intent
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for presistent preferred activities...");
List pprefs = ppir != null
? ppir.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
userId)
: null;
if (pprefs != null && pprefs.size() > 0) {
final int M = pprefs.size();
for (int i=0; i")
+ "\n component=" + ppa.mComponent);
ppa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
}
final ActivityInfo ai = getActivityInfo(ppa.mComponent,
flags | MATCH_DISABLED_COMPONENTS, userId);
if (DEBUG_PREFERRED || debug) {
Slog.v(TAG, "Found persistent preferred activity:");
if (ai != null) {
ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
} else {
Slog.v(TAG, " null");
}
}
if (ai == null) {
// This previously registered persistent preferred activity
// component is no longer known. Ignore it and do NOT remove it.
continue;
}
for (int j=0; j query, int priority, boolean always,
boolean removeMatches, boolean debug, int userId) {
if (!sUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForResolve(
flags, userId, intent, callingUid, false /*includeInstantApps*/);
intent = updateIntentForResolve(intent);
// writer
synchronized (mPackages) {
// Try to find a matching persistent preferred activity.
ResolveInfo pri = findPersistentPreferredActivityLP(intent, resolvedType, flags, query,
debug, userId);
// If a persistent preferred activity matched, use it.
if (pri != null) {
return pri;
}
PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
// Get the list of preferred activities that handle the intent
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities...");
List prefs = pir != null
? pir.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
userId)
: null;
if (prefs != null && prefs.size() > 0) {
boolean changed = false;
try {
// First figure out how good the original match set is.
// We will only allow preferred activities that came
// from the same match quality.
int match = 0;
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Figuring out best match...");
final int N = query.size();
for (int j=0; j match) {
match = ri.match;
}
}
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Best match: 0x"
+ Integer.toHexString(match));
match &= IntentFilter.MATCH_CATEGORY_MASK;
final int M = prefs.size();
for (int i=0; i")
+ "\n component=" + pa.mPref.mComponent);
pa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
}
if (pa.mPref.mMatch != match) {
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping bad match "
+ Integer.toHexString(pa.mPref.mMatch));
continue;
}
// If it's not an "always" type preferred activity and that's what we're
// looking for, skip it.
if (always && !pa.mPref.mAlways) {
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping mAlways=false entry");
continue;
}
final ActivityInfo ai = getActivityInfo(
pa.mPref.mComponent, flags | MATCH_DISABLED_COMPONENTS
| MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
userId);
if (DEBUG_PREFERRED || debug) {
Slog.v(TAG, "Found preferred activity:");
if (ai != null) {
ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
} else {
Slog.v(TAG, " null");
}
}
if (ai == null) {
// This previously registered preferred activity
// component is no longer known. Most likely an update
// to the app was installed and in the new version this
// component no longer exists. Clean it up by removing
// it from the preferred activities list, and skip it.
Slog.w(TAG, "Removing dangling preferred activity: "
+ pa.mPref.mComponent);
pir.removeFilter(pa);
changed = true;
continue;
}
for (int j=0; j matches =
getMatchingCrossProfileIntentFilters(intent, resolvedType, sourceUserId);
if (matches != null) {
int size = matches.size();
for (int i = 0; i < size; i++) {
if (matches.get(i).getTargetUserId() == targetUserId) return true;
}
}
if (intent.hasWebURI()) {
// cross-profile app linking works only towards the parent.
final int callingUid = Binder.getCallingUid();
final UserInfo parent = getProfileParent(sourceUserId);
synchronized(mPackages) {
int flags = updateFlagsForResolve(0, parent.id, intent, callingUid,
false /*includeInstantApps*/);
CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr(
intent, resolvedType, flags, sourceUserId, parent.id);
return xpDomainInfo != null;
}
}
return false;
}
private UserInfo getProfileParent(int userId) {
final long identity = Binder.clearCallingIdentity();
try {
return sUserManager.getProfileParent(userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private List getMatchingCrossProfileIntentFilters(Intent intent,
String resolvedType, int userId) {
CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId);
if (resolver != null) {
return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
}
return null;
}
@Override
public @NonNull ParceledListSlice queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
return new ParceledListSlice<>(
queryIntentActivitiesInternal(intent, resolvedType, flags, userId));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
/**
* Returns the package name of the calling Uid if it's an instant app. If it isn't
* instant, returns {@code null}.
*/
private String getInstantAppPackageName(int callingUid) {
synchronized (mPackages) {
// If the caller is an isolated app use the owner's uid for the lookup.
if (Process.isIsolated(callingUid)) {
callingUid = mIsolatedOwners.get(callingUid);
}
final int appId = UserHandle.getAppId(callingUid);
final Object obj = mSettings.getUserIdLPr(appId);
if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid));
return isInstantApp ? ps.pkg.packageName : null;
}
}
return null;
}
private @NonNull List queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int userId) {
return queryIntentActivitiesInternal(
intent, resolvedType, flags, Binder.getCallingUid(), userId,
false /*resolveForStart*/, true /*allowDynamicSplits*/);
}
private @NonNull List queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int filterCallingUid, int userId,
boolean resolveForStart, boolean allowDynamicSplits) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */,
"query intent activities");
final String pkgName = intent.getPackage();
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart,
comp != null || pkgName != null /*onlyExposedExplicitly*/);
if (comp != null) {
final List list = new ArrayList(1);
final ActivityInfo ai = getActivityInfo(comp, flags, userId);
if (ai != null) {
// When specifying an explicit component, we prevent the activity from being
// used when either 1) the calling package is normal and the activity is within
// an ephemeral application or 2) the calling package is ephemeral and the
// activity is not visible to ephemeral applications.
final boolean matchInstantApp =
(flags & PackageManager.MATCH_INSTANT) != 0;
final boolean matchVisibleToInstantAppOnly =
(flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
final boolean matchExplicitlyVisibleOnly =
(flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
final boolean isCallerInstantApp =
instantAppPkgName != null;
final boolean isTargetSameInstantApp =
comp.getPackageName().equals(instantAppPkgName);
final boolean isTargetInstantApp =
(ai.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
final boolean isTargetVisibleToInstantApp =
(ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
final boolean isTargetExplicitlyVisibleToInstantApp =
isTargetVisibleToInstantApp
&& (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
final boolean isTargetHiddenFromInstantApp =
!isTargetVisibleToInstantApp
|| (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp);
final boolean blockResolution =
!isTargetSameInstantApp
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
if (!blockResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
}
return applyPostResolutionFilter(
list, instantAppPkgName, allowDynamicSplits, filterCallingUid, userId, intent);
}
// reader
boolean sortResult = false;
boolean addInstant = false;
List result;
synchronized (mPackages) {
if (pkgName == null) {
List matchingFilters =
getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
// Check for results that need to skip the current profile.
ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
resolvedType, flags, userId);
if (xpResolveInfo != null) {
List xpResult = new ArrayList(1);
xpResult.add(xpResolveInfo);
return applyPostResolutionFilter(
filterIfNotSystemUser(xpResult, userId), instantAppPkgName,
allowDynamicSplits, filterCallingUid, userId, intent);
}
// Check for results in the current profile.
result = filterIfNotSystemUser(mActivities.queryIntent(
intent, resolvedType, flags, userId), userId);
addInstant = isInstantAppResolutionAllowed(intent, result, userId,
false /*skipPackageCheck*/);
// Check for cross profile results.
boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);
xpResolveInfo = queryCrossProfileIntents(
matchingFilters, intent, resolvedType, flags, userId,
hasNonNegativePriorityResult);
if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) {
boolean isVisibleToUser = filterIfNotSystemUser(
Collections.singletonList(xpResolveInfo), userId).size() > 0;
if (isVisibleToUser) {
result.add(xpResolveInfo);
sortResult = true;
}
}
if (intent.hasWebURI()) {
CrossProfileDomainInfo xpDomainInfo = null;
final UserInfo parent = getProfileParent(userId);
if (parent != null) {
xpDomainInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType,
flags, userId, parent.id);
}
if (xpDomainInfo != null) {
if (xpResolveInfo != null) {
// If we didn't remove it, the cross-profile ResolveInfo would be twice
// in the result.
result.remove(xpResolveInfo);
}
if (result.size() == 0 && !addInstant) {
// No result in current profile, but found candidate in parent user.
// And we are not going to add emphemeral app, so we can return the
// result straight away.
result.add(xpDomainInfo.resolveInfo);
return applyPostResolutionFilter(result, instantAppPkgName,
allowDynamicSplits, filterCallingUid, userId, intent);
}
} else if (result.size() <= 1 && !addInstant) {
// No result in parent user and <= 1 result in current profile, and we
// are not going to add emphemeral app, so we can return the result without
// further processing.
return applyPostResolutionFilter(result, instantAppPkgName,
allowDynamicSplits, filterCallingUid, userId, intent);
}
// We have more than one candidate (combining results from current and parent
// profile), so we need filtering and sorting.
result = filterCandidatesWithDomainPreferredActivitiesLPr(
intent, flags, result, xpDomainInfo, userId);
sortResult = true;
}
} else {
final PackageParser.Package pkg = mPackages.get(pkgName);
result = null;
if (pkg != null) {
result = filterIfNotSystemUser(
mActivities.queryIntentForPackage(
intent, resolvedType, flags, pkg.activities, userId),
userId);
}
if (result == null || result.size() == 0) {
// the caller wants to resolve for a particular package; however, there
// were no installed results, so, try to find an ephemeral result
addInstant = isInstantAppResolutionAllowed(
intent, null /*result*/, userId, true /*skipPackageCheck*/);
if (result == null) {
result = new ArrayList<>();
}
}
}
}
if (addInstant) {
result = maybeAddInstantAppInstaller(
result, intent, resolvedType, flags, userId, resolveForStart);
}
if (sortResult) {
Collections.sort(result, mResolvePrioritySorter);
}
return applyPostResolutionFilter(
result, instantAppPkgName, allowDynamicSplits, filterCallingUid, userId, intent);
}
private List maybeAddInstantAppInstaller(List result, Intent intent,
String resolvedType, int flags, int userId, boolean resolveForStart) {
// first, check to see if we've got an instant app already installed
final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0;
ResolveInfo localInstantApp = null;
boolean blockResolution = false;
if (!alreadyResolvedLocally) {
final List instantApps = mActivities.queryIntent(intent, resolvedType,
flags
| PackageManager.GET_RESOLVED_FILTER
| PackageManager.MATCH_INSTANT
| PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY,
userId);
for (int i = instantApps.size() - 1; i >= 0; --i) {
final ResolveInfo info = instantApps.get(i);
final String packageName = info.activityInfo.packageName;
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps.getInstantApp(userId)) {
final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
final int status = (int)(packedStatus >> 32);
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
// there's a local instant application installed, but, the user has
// chosen to never use it; skip resolution and don't acknowledge
// an instant application is even available
if (DEBUG_INSTANT) {
Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName);
}
blockResolution = true;
break;
} else {
// we have a locally installed instant application; skip resolution
// but acknowledge there's an instant application available
if (DEBUG_INSTANT) {
Slog.v(TAG, "Found installed instant app; pkg: " + packageName);
}
localInstantApp = info;
break;
}
}
}
}
// no app installed, let's see if one's available
AuxiliaryResolveInfo auxiliaryResponse = null;
if (!blockResolution) {
if (localInstantApp == null) {
// we don't have an instant app locally, resolve externally
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
final InstantAppRequest requestObject = new InstantAppRequest(
null /*responseObj*/, intent /*origIntent*/, resolvedType,
null /*callingPackage*/, userId, null /*verificationBundle*/,
resolveForStart);
auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(
mInstantAppResolverConnection, requestObject);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
} else {
// we have an instant application locally, but, we can't admit that since
// callers shouldn't be able to determine prior browsing. create a dummy
// auxiliary response so the downstream code behaves as if there's an
// instant application available externally. when it comes time to start
// the instant application, we'll do the right thing.
final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo;
auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */,
ai.packageName, ai.versionCode, null /* splitName */);
}
}
if (intent.isWebIntent() && auxiliaryResponse == null) {
return result;
}
final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName);
if (ps == null
|| ps.getUserState().get(userId) == null
|| !ps.getUserState().get(userId).isEnabled(mInstantAppInstallerActivity, 0)) {
return result;
}
final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId);
ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
// add a non-generic filter
ephemeralInstaller.filter = new IntentFilter();
if (intent.getAction() != null) {
ephemeralInstaller.filter.addAction(intent.getAction());
}
if (intent.getData() != null && intent.getData().getPath() != null) {
ephemeralInstaller.filter.addDataPath(
intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
}
ephemeralInstaller.isInstantAppAvailable = true;
// make sure this resolver is the default
ephemeralInstaller.isDefault = true;
ephemeralInstaller.auxiliaryInfo = auxiliaryResponse;
if (DEBUG_INSTANT) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
result.add(ephemeralInstaller);
return result;
}
private static class CrossProfileDomainInfo {
/* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */
ResolveInfo resolveInfo;
/* Best domain verification status of the activities found in the other profile */
int bestDomainVerificationStatus;
}
private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
String resolvedType, int flags, int sourceUserId, int parentUserId) {
if (!sUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
sourceUserId)) {
return null;
}
List resultTargetUser = mActivities.queryIntent(intent,
resolvedType, flags, parentUserId);
if (resultTargetUser == null || resultTargetUser.isEmpty()) {
return null;
}
CrossProfileDomainInfo result = null;
int size = resultTargetUser.size();
for (int i = 0; i < size; i++) {
ResolveInfo riTargetUser = resultTargetUser.get(i);
// Intent filter verification is only for filters that specify a host. So don't return
// those that handle all web uris.
if (riTargetUser.handleAllWebDataURI) {
continue;
}
String packageName = riTargetUser.activityInfo.packageName;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) {
continue;
}
long verificationState = getDomainVerificationStatusLPr(ps, parentUserId);
int status = (int)(verificationState >> 32);
if (result == null) {
result = new CrossProfileDomainInfo();
result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(),
sourceUserId, parentUserId);
result.bestDomainVerificationStatus = status;
} else {
result.bestDomainVerificationStatus = bestDomainVerificationStatus(status,
result.bestDomainVerificationStatus);
}
}
// Don't consider matches with status NEVER across profiles.
if (result != null && result.bestDomainVerificationStatus
== INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
return null;
}
return result;
}
/**
* Verification statuses are ordered from the worse to the best, except for
* INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse.
*/
private int bestDomainVerificationStatus(int status1, int status2) {
if (status1 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
return status2;
}
if (status2 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
return status1;
}
return (int) MathUtils.max(status1, status2);
}
private boolean isUserEnabled(int userId) {
long callingId = Binder.clearCallingIdentity();
try {
UserInfo userInfo = sUserManager.getUserInfo(userId);
return userInfo != null && userInfo.isEnabled();
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
/**
* Filter out activities with systemUserOnly flag set, when current user is not System.
*
* @return filtered list
*/
private List filterIfNotSystemUser(List resolveInfos, int userId) {
if (userId == UserHandle.USER_SYSTEM) {
return resolveInfos;
}
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
ResolveInfo info = resolveInfos.get(i);
if ((info.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
resolveInfos.remove(i);
}
}
return resolveInfos;
}
/**
* Filters out ephemeral activities.
* When resolving for an ephemeral app, only activities that 1) are defined in the
* ephemeral app or 2) marked with {@code visibleToEphemeral} are returned.
*
* @param resolveInfos The pre-filtered list of resolved activities
* @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering
* is performed.
* @param intent
* @return A filtered list of resolved activities.
*/
private List applyPostResolutionFilter(List resolveInfos,
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, int userId,
Intent intent) {
final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled();
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
// remove locally resolved instant app web results when disabled
if (info.isInstantAppAvailable && blockInstant) {
resolveInfos.remove(i);
continue;
}
// allow activities that are defined in the provided package
if (allowDynamicSplits
&& info.activityInfo != null
&& info.activityInfo.splitName != null
&& !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames,
info.activityInfo.splitName)) {
if (mInstantAppInstallerActivity == null) {
if (DEBUG_INSTALL) {
Slog.v(TAG, "No installer - not adding it to the ResolveInfo list");
}
resolveInfos.remove(i);
continue;
}
if (blockInstant && isInstantApp(info.activityInfo.packageName, userId)) {
resolveInfos.remove(i);
continue;
}
// requested activity is defined in a split that hasn't been installed yet.
// add the installer to the resolve list
if (DEBUG_INSTALL) {
Slog.v(TAG, "Adding installer to the ResolveInfo list");
}
final ResolveInfo installerInfo = new ResolveInfo(
mInstantAppInstallerInfo);
final ComponentName installFailureActivity = findInstallFailureActivity(
info.activityInfo.packageName, filterCallingUid, userId);
installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
installFailureActivity,
info.activityInfo.packageName,
info.activityInfo.applicationInfo.versionCode,
info.activityInfo.splitName);
// add a non-generic filter
installerInfo.filter = new IntentFilter();
// This resolve info may appear in the chooser UI, so let us make it
// look as the one it replaces as far as the user is concerned which
// requires loading the correct label and icon for the resolve info.
installerInfo.resolvePackageName = info.getComponentInfo().packageName;
installerInfo.labelRes = info.resolveLabelResId();
installerInfo.icon = info.resolveIconResId();
installerInfo.isInstantAppAvailable = true;
resolveInfos.set(i, installerInfo);
continue;
}
// caller is a full app, don't need to apply any other filtering
if (ephemeralPkgName == null) {
continue;
} else if (ephemeralPkgName.equals(info.activityInfo.packageName)) {
// caller is same app; don't need to apply any other filtering
continue;
}
// allow activities that have been explicitly exposed to ephemeral apps
final boolean isEphemeralApp = info.activityInfo.applicationInfo.isInstantApp();
if (!isEphemeralApp
&& ((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) {
continue;
}
resolveInfos.remove(i);
}
return resolveInfos;
}
/**
* Returns the activity component that can handle install failures.
* By default, the instant application installer handles failures. However, an
* application may want to handle failures on its own. Applications do this by
* creating an activity with an intent filter that handles the action
* {@link Intent#ACTION_INSTALL_FAILURE}.
*/
private @Nullable ComponentName findInstallFailureActivity(
String packageName, int filterCallingUid, int userId) {
final Intent failureActivityIntent = new Intent(Intent.ACTION_INSTALL_FAILURE);
failureActivityIntent.setPackage(packageName);
// IMPORTANT: disallow dynamic splits to avoid an infinite loop
final List result = queryIntentActivitiesInternal(
failureActivityIntent, null /*resolvedType*/, 0 /*flags*/, filterCallingUid, userId,
false /*resolveForStart*/, false /*allowDynamicSplits*/);
final int NR = result.size();
if (NR > 0) {
for (int i = 0; i < NR; i++) {
final ResolveInfo info = result.get(i);
if (info.activityInfo.splitName != null) {
continue;
}
return new ComponentName(packageName, info.activityInfo.name);
}
}
return null;
}
/**
* @param resolveInfos list of resolve infos in descending priority order
* @return if the list contains a resolve info with non-negative priority
*/
private boolean hasNonNegativePriority(List resolveInfos) {
return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0;
}
private List filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
int matchFlags, List candidates, CrossProfileDomainInfo xpDomainInfo,
int userId) {
final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0;
if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) {
Slog.v(TAG, "Filtering results with preferred activities. Candidates count: " +
candidates.size());
}
ArrayList result = new ArrayList();
ArrayList alwaysList = new ArrayList();
ArrayList undefinedList = new ArrayList();
ArrayList alwaysAskList = new ArrayList();
ArrayList neverList = new ArrayList();
ArrayList matchAllList = new ArrayList();
synchronized (mPackages) {
final int count = candidates.size();
// First, try to use linked apps. Partition the candidates into four lists:
// one for the final results, one for the "do not use ever", one for "undefined status"
// and finally one for "browser app type".
for (int n=0; n> 32);
int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
if (DEBUG_DOMAIN_VERIFICATION || debug) {
Slog.i(TAG, " + always: " + info.activityInfo.packageName
+ " : linkgen=" + linkGeneration);
}
// Use link-enabled generation as preferredOrder, i.e.
// prefer newly-enabled over earlier-enabled.
info.preferredOrder = linkGeneration;
alwaysList.add(info);
} else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
if (DEBUG_DOMAIN_VERIFICATION || debug) {
Slog.i(TAG, " + never: " + info.activityInfo.packageName);
}
neverList.add(info);
} else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
if (DEBUG_DOMAIN_VERIFICATION || debug) {
Slog.i(TAG, " + always-ask: " + info.activityInfo.packageName);
}
alwaysAskList.add(info);
} else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED ||
status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK) {
if (DEBUG_DOMAIN_VERIFICATION || debug) {
Slog.i(TAG, " + ask: " + info.activityInfo.packageName);
}
undefinedList.add(info);
}
}
}
// We'll want to include browser possibilities in a few cases
boolean includeBrowser = false;
// First try to add the "always" resolution(s) for the current user, if any
if (alwaysList.size() > 0) {
result.addAll(alwaysList);
} else {
// Add all undefined apps as we want them to appear in the disambiguation dialog.
result.addAll(undefinedList);
// Maybe add one for the other profile.
if (xpDomainInfo != null && (
xpDomainInfo.bestDomainVerificationStatus
!= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)) {
result.add(xpDomainInfo.resolveInfo);
}
includeBrowser = true;
}
// The presence of any 'always ask' alternatives means we'll also offer browsers.
// If there were 'always' entries their preferred order has been set, so we also
// back that off to make the alternatives equivalent
if (alwaysAskList.size() > 0) {
for (ResolveInfo i : result) {
i.preferredOrder = 0;
}
result.addAll(alwaysAskList);
includeBrowser = true;
}
if (includeBrowser) {
// Also add browsers (all of them or only the default one)
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.v(TAG, " ...including browsers in candidate set");
}
if ((matchFlags & MATCH_ALL) != 0) {
result.addAll(matchAllList);
} else {
// Browser/generic handling case. If there's a default browser, go straight
// to that (but only if there is no other higher-priority match).
final String defaultBrowserPackageName = getDefaultBrowserPackageName(userId);
int maxMatchPrio = 0;
ResolveInfo defaultBrowserMatch = null;
final int numCandidates = matchAllList.size();
for (int n = 0; n < numCandidates; n++) {
ResolveInfo info = matchAllList.get(n);
// track the highest overall match priority...
if (info.priority > maxMatchPrio) {
maxMatchPrio = info.priority;
}
// ...and the highest-priority default browser match
if (info.activityInfo.packageName.equals(defaultBrowserPackageName)) {
if (defaultBrowserMatch == null
|| (defaultBrowserMatch.priority < info.priority)) {
if (debug) {
Slog.v(TAG, "Considering default browser match " + info);
}
defaultBrowserMatch = info;
}
}
}
if (defaultBrowserMatch != null
&& defaultBrowserMatch.priority >= maxMatchPrio
&& !TextUtils.isEmpty(defaultBrowserPackageName))
{
if (debug) {
Slog.v(TAG, "Default browser match " + defaultBrowserMatch);
}
result.add(defaultBrowserMatch);
} else {
result.addAll(matchAllList);
}
}
// If there is nothing selected, add all candidates and remove the ones that the user
// has explicitly put into the INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER state
if (result.size() == 0) {
result.addAll(candidates);
result.removeAll(neverList);
}
}
}
if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) {
Slog.v(TAG, "Filtered results with preferred activities. New candidates count: " +
result.size());
for (ResolveInfo info : result) {
Slog.v(TAG, " + " + info.activityInfo);
}
}
return result;
}
// Returns a packed value as a long:
//
// high 'int'-sized word: link status: undefined/ask/never/always.
// low 'int'-sized word: relative priority among 'always' results.
private long getDomainVerificationStatusLPr(PackageSetting ps, int userId) {
long result = ps.getDomainVerificationStatusForUser(userId);
// if none available, get the master status
if (result >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
if (ps.getIntentFilterVerificationInfo() != null) {
result = ((long)ps.getIntentFilterVerificationInfo().getStatus()) << 32;
}
}
return result;
}
private ResolveInfo querySkipCurrentProfileIntents(
List matchingFilters, Intent intent, String resolvedType,
int flags, int sourceUserId) {
if (matchingFilters != null) {
int size = matchingFilters.size();
for (int i = 0; i < size; i ++) {
CrossProfileIntentFilter filter = matchingFilters.get(i);
if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0) {
// Checking if there are activities in the target user that can handle the
// intent.
ResolveInfo resolveInfo = createForwardingResolveInfo(filter, intent,
resolvedType, flags, sourceUserId);
if (resolveInfo != null) {
return resolveInfo;
}
}
}
}
return null;
}
// Return matching ResolveInfo in target user if any.
private ResolveInfo queryCrossProfileIntents(
List matchingFilters, Intent intent, String resolvedType,
int flags, int sourceUserId, boolean matchInCurrentProfile) {
if (matchingFilters != null) {
// Two {@link CrossProfileIntentFilter}s can have the same targetUserId and
// match the same intent. For performance reasons, it is better not to
// run queryIntent twice for the same userId
SparseBooleanArray alreadyTriedUserIds = new SparseBooleanArray();
int size = matchingFilters.size();
for (int i = 0; i < size; i++) {
CrossProfileIntentFilter filter = matchingFilters.get(i);
int targetUserId = filter.getTargetUserId();
boolean skipCurrentProfile =
(filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0;
boolean skipCurrentProfileIfNoMatchFound =
(filter.getFlags() & PackageManager.ONLY_IF_NO_MATCH_FOUND) != 0;
if (!skipCurrentProfile && !alreadyTriedUserIds.get(targetUserId)
&& (!skipCurrentProfileIfNoMatchFound || !matchInCurrentProfile)) {
// Checking if there are activities in the target user that can handle the
// intent.
ResolveInfo resolveInfo = createForwardingResolveInfo(filter, intent,
resolvedType, flags, sourceUserId);
if (resolveInfo != null) return resolveInfo;
alreadyTriedUserIds.put(targetUserId, true);
}
}
}
return null;
}
/**
* If the filter's target user can handle the intent and is enabled: returns a ResolveInfo that
* will forward the intent to the filter's target user.
* Otherwise, returns null.
*/
private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter, Intent intent,
String resolvedType, int flags, int sourceUserId) {
int targetUserId = filter.getTargetUserId();
List resultTargetUser = mActivities.queryIntent(intent,
resolvedType, flags, targetUserId);
if (resultTargetUser != null && isUserEnabled(targetUserId)) {
// If all the matches in the target profile are suspended, return null.
for (int i = resultTargetUser.size() - 1; i >= 0; i--) {
if ((resultTargetUser.get(i).activityInfo.applicationInfo.flags
& ApplicationInfo.FLAG_SUSPENDED) == 0) {
return createForwardingResolveInfoUnchecked(filter, sourceUserId,
targetUserId);
}
}
}
return null;
}
private ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter,
int sourceUserId, int targetUserId) {
ResolveInfo forwardingResolveInfo = new ResolveInfo();
long ident = Binder.clearCallingIdentity();
boolean targetIsProfile;
try {
targetIsProfile = sUserManager.getUserInfo(targetUserId).isManagedProfile();
} finally {
Binder.restoreCallingIdentity(ident);
}
String className;
if (targetIsProfile) {
className = FORWARD_INTENT_TO_MANAGED_PROFILE;
} else {
className = FORWARD_INTENT_TO_PARENT;
}
ComponentName forwardingActivityComponentName = new ComponentName(
mAndroidApplication.packageName, className);
ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0,
sourceUserId);
if (!targetIsProfile) {
forwardingActivityInfo.showUserIcon = targetUserId;
forwardingResolveInfo.noResourceId = true;
}
forwardingResolveInfo.activityInfo = forwardingActivityInfo;
forwardingResolveInfo.priority = 0;
forwardingResolveInfo.preferredOrder = 0;
forwardingResolveInfo.match = 0;
forwardingResolveInfo.isDefault = true;
forwardingResolveInfo.filter = filter;
forwardingResolveInfo.targetUserId = targetUserId;
return forwardingResolveInfo;
}
@Override
public @NonNull ParceledListSlice queryIntentActivityOptions(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, int flags, int userId) {
return new ParceledListSlice<>(queryIntentActivityOptionsInternal(caller, specifics,
specificTypes, intent, resolvedType, flags, userId));
}
private @NonNull List queryIntentActivityOptionsInternal(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForResolve(flags, userId, intent, callingUid,
false /*includeInstantApps*/);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent activity options");
final String resultsAction = intent.getAction();
final List results = queryIntentActivitiesInternal(intent, resolvedType, flags
| PackageManager.GET_RESOLVED_FILTER, userId);
if (DEBUG_INTENT_MATCHING) {
Log.v(TAG, "Query " + intent + ": " + results);
}
int specificsPos = 0;
int N;
// todo: note that the algorithm used here is O(N^2). This
// isn't a problem in our current environment, but if we start running
// into situations where we have more than 5 or 10 matches then this
// should probably be changed to something smarter...
// First we go through and resolve each of the specific items
// that were supplied, taking care of removing any corresponding
// duplicate items in the generic resolve list.
if (specifics != null) {
for (int i=0; i it = rii.filter.actionsIterator();
if (it == null) {
continue;
}
while (it.hasNext()) {
final String action = it.next();
if (resultsAction != null && resultsAction.equals(action)) {
// If this action was explicitly requested, then don't
// remove things that have it.
continue;
}
for (int j=i+1; j queryIntentReceivers(Intent intent,
String resolvedType, int flags, int userId) {
return new ParceledListSlice<>(
queryIntentReceiversInternal(intent, resolvedType, flags, userId,
false /*allowDynamicSplits*/));
}
private @NonNull List queryIntentReceiversInternal(Intent intent,
String resolvedType, int flags, int userId, boolean allowDynamicSplits) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, intent, callingUid,
false /*includeInstantApps*/);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) {
final List list = new ArrayList(1);
final ActivityInfo ai = getReceiverInfo(comp, flags, userId);
if (ai != null) {
// When specifying an explicit component, we prevent the activity from being
// used when either 1) the calling package is normal and the activity is within
// an instant application or 2) the calling package is ephemeral and the
// activity is not visible to instant applications.
final boolean matchInstantApp =
(flags & PackageManager.MATCH_INSTANT) != 0;
final boolean matchVisibleToInstantAppOnly =
(flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
final boolean matchExplicitlyVisibleOnly =
(flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
final boolean isCallerInstantApp =
instantAppPkgName != null;
final boolean isTargetSameInstantApp =
comp.getPackageName().equals(instantAppPkgName);
final boolean isTargetInstantApp =
(ai.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
final boolean isTargetVisibleToInstantApp =
(ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
final boolean isTargetExplicitlyVisibleToInstantApp =
isTargetVisibleToInstantApp
&& (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
final boolean isTargetHiddenFromInstantApp =
!isTargetVisibleToInstantApp
|| (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp);
final boolean blockResolution =
!isTargetSameInstantApp
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
if (!blockResolution) {
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
}
return applyPostResolutionFilter(
list, instantAppPkgName, allowDynamicSplits, callingUid, userId, intent);
}
// reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
final List result =
mReceivers.queryIntent(intent, resolvedType, flags, userId);
return applyPostResolutionFilter(
result, instantAppPkgName, allowDynamicSplits, callingUid, userId, intent);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
final List result = mReceivers.queryIntentForPackage(
intent, resolvedType, flags, pkg.receivers, userId);
return applyPostResolutionFilter(
result, instantAppPkgName, allowDynamicSplits, callingUid, userId, intent);
}
return Collections.emptyList();
}
}
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {
final int callingUid = Binder.getCallingUid();
return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid);
}
private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags,
int userId, int callingUid) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForResolve(
flags, userId, intent, callingUid, false /*includeInstantApps*/);
List query = queryIntentServicesInternal(
intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
if (query != null) {
if (query.size() >= 1) {
// If there is more than one service with the same priority,
// just arbitrarily pick the first one.
return query.get(0);
}
}
return null;
}
@Override
public @NonNull ParceledListSlice queryIntentServices(Intent intent,
String resolvedType, int flags, int userId) {
final int callingUid = Binder.getCallingUid();
return new ParceledListSlice<>(queryIntentServicesInternal(
intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/));
}
private @NonNull List queryIntentServicesInternal(Intent intent,
String resolvedType, int flags, int userId, int callingUid,
boolean includeInstantApps) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) {
final List list = new ArrayList(1);
final ServiceInfo si = getServiceInfo(comp, flags, userId);
if (si != null) {
// When specifying an explicit component, we prevent the service from being
// used when either 1) the service is in an instant application and the
// caller is not the same instant application or 2) the calling package is
// ephemeral and the activity is not visible to ephemeral applications.
final boolean matchInstantApp =
(flags & PackageManager.MATCH_INSTANT) != 0;
final boolean matchVisibleToInstantAppOnly =
(flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
final boolean isCallerInstantApp =
instantAppPkgName != null;
final boolean isTargetSameInstantApp =
comp.getPackageName().equals(instantAppPkgName);
final boolean isTargetInstantApp =
(si.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
final boolean isTargetHiddenFromInstantApp =
(si.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0;
final boolean blockResolution =
!isTargetSameInstantApp
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
if (!blockResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.serviceInfo = si;
list.add(ri);
}
}
return list;
}
// reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
return applyPostServiceResolutionFilter(
mServices.queryIntent(intent, resolvedType, flags, userId),
instantAppPkgName);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
return applyPostServiceResolutionFilter(
mServices.queryIntentForPackage(intent, resolvedType, flags, pkg.services,
userId),
instantAppPkgName);
}
return Collections.emptyList();
}
}
private List applyPostServiceResolutionFilter(List resolveInfos,
String instantAppPkgName) {
if (instantAppPkgName == null) {
return resolveInfos;
}
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp();
// allow services that are defined in the provided package
if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) {
if (info.serviceInfo.splitName != null
&& !ArrayUtils.contains(info.serviceInfo.applicationInfo.splitNames,
info.serviceInfo.splitName)) {
// requested service is defined in a split that hasn't been installed yet.
// add the installer to the resolve list
if (DEBUG_INSTANT) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
final ResolveInfo installerInfo = new ResolveInfo(
mInstantAppInstallerInfo);
installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
null /* installFailureActivity */,
info.serviceInfo.packageName,
info.serviceInfo.applicationInfo.versionCode,
info.serviceInfo.splitName);
// add a non-generic filter
installerInfo.filter = new IntentFilter();
// load resources from the correct package
installerInfo.resolvePackageName = info.getComponentInfo().packageName;
resolveInfos.set(i, installerInfo);
}
continue;
}
// allow services that have been explicitly exposed to ephemeral apps
if (!isEphemeralApp
&& ((info.serviceInfo.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) {
continue;
}
resolveInfos.remove(i);
}
return resolveInfos;
}
@Override
public @NonNull ParceledListSlice queryIntentContentProviders(Intent intent,
String resolvedType, int flags, int userId) {
return new ParceledListSlice<>(
queryIntentContentProvidersInternal(intent, resolvedType, flags, userId));
}
private @NonNull List queryIntentContentProvidersInternal(
Intent intent, String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, intent, callingUid,
false /*includeInstantApps*/);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) {
final List list = new ArrayList(1);
final ProviderInfo pi = getProviderInfo(comp, flags, userId);
if (pi != null) {
// When specifying an explicit component, we prevent the provider from being
// used when either 1) the provider is in an instant application and the
// caller is not the same instant application or 2) the calling package is an
// instant application and the provider is not visible to instant applications.
final boolean matchInstantApp =
(flags & PackageManager.MATCH_INSTANT) != 0;
final boolean matchVisibleToInstantAppOnly =
(flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
final boolean isCallerInstantApp =
instantAppPkgName != null;
final boolean isTargetSameInstantApp =
comp.getPackageName().equals(instantAppPkgName);
final boolean isTargetInstantApp =
(pi.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
final boolean isTargetHiddenFromInstantApp =
(pi.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0;
final boolean blockResolution =
!isTargetSameInstantApp
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
if (!blockResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.providerInfo = pi;
list.add(ri);
}
}
return list;
}
// reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
return applyPostContentProviderResolutionFilter(
mProviders.queryIntent(intent, resolvedType, flags, userId),
instantAppPkgName);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
return applyPostContentProviderResolutionFilter(
mProviders.queryIntentForPackage(
intent, resolvedType, flags, pkg.providers, userId),
instantAppPkgName);
}
return Collections.emptyList();
}
}
private List applyPostContentProviderResolutionFilter(
List resolveInfos, String instantAppPkgName) {
if (instantAppPkgName == null) {
return resolveInfos;
}
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
final boolean isEphemeralApp = info.providerInfo.applicationInfo.isInstantApp();
// allow providers that are defined in the provided package
if (isEphemeralApp && instantAppPkgName.equals(info.providerInfo.packageName)) {
if (info.providerInfo.splitName != null
&& !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames,
info.providerInfo.splitName)) {
// requested provider is defined in a split that hasn't been installed yet.
// add the installer to the resolve list
if (DEBUG_INSTANT) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
final ResolveInfo installerInfo = new ResolveInfo(
mInstantAppInstallerInfo);
installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
null /*failureActivity*/,
info.providerInfo.packageName,
info.providerInfo.applicationInfo.versionCode,
info.providerInfo.splitName);
// add a non-generic filter
installerInfo.filter = new IntentFilter();
// load resources from the correct package
installerInfo.resolvePackageName = info.getComponentInfo().packageName;
resolveInfos.set(i, installerInfo);
}
continue;
}
// allow providers that have been explicitly exposed to instant applications
if (!isEphemeralApp
&& ((info.providerInfo.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) {
continue;
}
resolveInfos.remove(i);
}
return resolveInfos;
}
@Override
public ParceledListSlice getInstalledPackages(int flags, int userId) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return ParceledListSlice.emptyList();
}
if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForPackage(flags, userId, null);
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"get installed packages");
// writer
synchronized (mPackages) {
ArrayList list;
if (listUninstalled) {
list = new ArrayList<>(mSettings.mPackages.size());
for (PackageSetting ps : mSettings.mPackages.values()) {
if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
continue;
}
if (filterAppAccessLPr(ps, callingUid, userId)) {
continue;
}
final PackageInfo pi = generatePackageInfo(ps, flags, userId);
if (pi != null) {
list.add(pi);
}
}
} else {
list = new ArrayList<>(mPackages.size());
for (PackageParser.Package p : mPackages.values()) {
final PackageSetting ps = (PackageSetting) p.mExtras;
if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
continue;
}
if (filterAppAccessLPr(ps, callingUid, userId)) {
continue;
}
final PackageInfo pi = generatePackageInfo((PackageSetting)
p.mExtras, flags, userId);
if (pi != null) {
list.add(pi);
}
}
}
return new ParceledListSlice<>(list);
}
}
private void addPackageHoldingPermissions(ArrayList list, PackageSetting ps,
String[] permissions, boolean[] tmp, int flags, int userId) {
int numMatch = 0;
final PermissionsState permissionsState = ps.getPermissionsState();
for (int i=0; i getPackagesHoldingPermissions(
String[] permissions, int flags, int userId) {
if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForPackage(flags, userId, permissions);
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"get packages holding permissions");
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
// writer
synchronized (mPackages) {
ArrayList list = new ArrayList();
boolean[] tmpBools = new boolean[permissions.length];
if (listUninstalled) {
for (PackageSetting ps : mSettings.mPackages.values()) {
addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags,
userId);
}
} else {
for (PackageParser.Package pkg : mPackages.values()) {
PackageSetting ps = (PackageSetting)pkg.mExtras;
if (ps != null) {
addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags,
userId);
}
}
}
return new ParceledListSlice(list);
}
}
@Override
public ParceledListSlice getInstalledApplications(int flags, int userId) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return ParceledListSlice.emptyList();
}
if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForApplication(flags, userId, null);
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
// writer
synchronized (mPackages) {
ArrayList list;
if (listUninstalled) {
list = new ArrayList<>(mSettings.mPackages.size());
for (PackageSetting ps : mSettings.mPackages.values()) {
ApplicationInfo ai;
int effectiveFlags = flags;
if (ps.isSystem()) {
effectiveFlags |= PackageManager.MATCH_ANY_USER;
}
if (ps.pkg != null) {
if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
continue;
}
if (filterAppAccessLPr(ps, callingUid, userId)) {
continue;
}
ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags,
ps.readUserState(userId), userId);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
}
} else {
// Shared lib filtering done in generateApplicationInfoFromSettingsLPw
// and already converts to externally visible package name
ai = generateApplicationInfoFromSettingsLPw(ps.name,
callingUid, effectiveFlags, userId);
}
if (ai != null) {
list.add(ai);
}
}
} else {
list = new ArrayList<>(mPackages.size());
for (PackageParser.Package p : mPackages.values()) {
if (p.mExtras != null) {
PackageSetting ps = (PackageSetting) p.mExtras;
if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
continue;
}
if (filterAppAccessLPr(ps, callingUid, userId)) {
continue;
}
ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
ps.readUserState(userId), userId);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(p);
list.add(ai);
}
}
}
}
return new ParceledListSlice<>(list);
}
}
@Override
public ParceledListSlice getInstantApps(int userId) {
if (HIDE_EPHEMERAL_APIS) {
return null;
}
if (!canViewInstantApps(Binder.getCallingUid(), userId)) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
"getEphemeralApplications");
}
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getEphemeralApplications");
synchronized (mPackages) {
List instantApps = mInstantAppRegistry
.getInstantAppsLPr(userId);
if (instantApps != null) {
return new ParceledListSlice<>(instantApps);
}
}
return null;
}
@Override
public boolean isInstantApp(String packageName, int userId) {
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"isInstantApp");
if (HIDE_EPHEMERAL_APIS) {
return false;
}
synchronized (mPackages) {
int callingUid = Binder.getCallingUid();
if (Process.isIsolated(callingUid)) {
callingUid = mIsolatedOwners.get(callingUid);
}
final PackageSetting ps = mSettings.mPackages.get(packageName);
PackageParser.Package pkg = mPackages.get(packageName);
final boolean returnAllowed =
ps != null
&& (isCallerSameApp(packageName, callingUid)
|| canViewInstantApps(callingUid, userId)
|| mInstantAppRegistry.isInstantAccessGranted(
userId, UserHandle.getAppId(callingUid), ps.appId));
if (returnAllowed) {
return ps.getInstantApp(userId);
}
}
return false;
}
@Override
public byte[] getInstantAppCookie(String packageName, int userId) {
if (HIDE_EPHEMERAL_APIS) {
return null;
}
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getInstantAppCookie");
if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
return null;
}
synchronized (mPackages) {
return mInstantAppRegistry.getInstantAppCookieLPw(
packageName, userId);
}
}
@Override
public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) {
if (HIDE_EPHEMERAL_APIS) {
return true;
}
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, true /* checkShell */,
"setInstantAppCookie");
if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
return false;
}
synchronized (mPackages) {
return mInstantAppRegistry.setInstantAppCookieLPw(
packageName, cookie, userId);
}
}
@Override
public Bitmap getInstantAppIcon(String packageName, int userId) {
if (HIDE_EPHEMERAL_APIS) {
return null;
}
if (!canViewInstantApps(Binder.getCallingUid(), userId)) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
"getInstantAppIcon");
}
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"getInstantAppIcon");
synchronized (mPackages) {
return mInstantAppRegistry.getInstantAppIconLPw(
packageName, userId);
}
}
private boolean isCallerSameApp(String packageName, int uid) {
PackageParser.Package pkg = mPackages.get(packageName);
return pkg != null
&& UserHandle.getAppId(uid) == pkg.applicationInfo.uid;
}
@Override
public @NonNull ParceledListSlice getPersistentApplications(int flags) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return ParceledListSlice.emptyList();
}
return new ParceledListSlice<>(getPersistentApplicationsInternal(flags));
}
private @NonNull List getPersistentApplicationsInternal(int flags) {
final ArrayList finalList = new ArrayList();
// reader
synchronized (mPackages) {
final Iterator i = mPackages.values().iterator();
final int userId = UserHandle.getCallingUserId();
while (i.hasNext()) {
final PackageParser.Package p = i.next();
if (p.applicationInfo == null) continue;
final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0)
&& !p.applicationInfo.isDirectBootAware();
final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)
&& p.applicationInfo.isDirectBootAware();
if ((p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p))
&& (matchesUnaware || matchesAware)) {
PackageSetting ps = mSettings.mPackages.get(p.packageName);
if (ps != null) {
ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
ps.readUserState(userId), userId);
if (ai != null) {
finalList.add(ai);
}
}
}
}
}
return finalList;
}
@Override
public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
return resolveContentProviderInternal(name, flags, userId);
}
private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId, name);
final String instantAppPkgName = getInstantAppPackageName(Binder.getCallingUid());
// reader
synchronized (mPackages) {
final PackageParser.Provider provider = mProvidersByAuthority.get(name);
PackageSetting ps = provider != null
? mSettings.mPackages.get(provider.owner.packageName)
: null;
if (ps != null) {
final boolean isInstantApp = ps.getInstantApp(userId);
// normal application; filter out instant application provider
if (instantAppPkgName == null && isInstantApp) {
return null;
}
// instant application; filter out other instant applications
if (instantAppPkgName != null
&& isInstantApp
&& !provider.owner.packageName.equals(instantAppPkgName)) {
return null;
}
// instant application; filter out non-exposed provider
if (instantAppPkgName != null
&& !isInstantApp
&& (provider.info.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0) {
return null;
}
// provider not enabled
if (!mSettings.isEnabledAndMatchLPr(provider.info, flags, userId)) {
return null;
}
return PackageParser.generateProviderInfo(
provider, flags, ps.readUserState(userId), userId);
}
return null;
}
}
/**
* @deprecated
*/
@Deprecated
public void querySyncProviders(List outNames, List outInfo) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return;
}
// reader
synchronized (mPackages) {
final Iterator> i = mProvidersByAuthority
.entrySet().iterator();
final int userId = UserHandle.getCallingUserId();
while (i.hasNext()) {
Map.Entry entry = i.next();
PackageParser.Provider p = entry.getValue();
PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
if (ps != null && p.syncable
&& (!mSafeMode || (p.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)) {
ProviderInfo info = PackageParser.generateProviderInfo(p, 0,
ps.readUserState(userId), userId);
if (info != null) {
outNames.add(entry.getKey());
outInfo.add(info);
}
}
}
}
}
@Override
public @NonNull ParceledListSlice queryContentProviders(String processName,
int uid, int flags, String metaDataKey) {
final int callingUid = Binder.getCallingUid();
final int userId = processName != null ? UserHandle.getUserId(uid)
: UserHandle.getCallingUserId();
if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForComponent(flags, userId, processName);
ArrayList finalList = null;
// reader
synchronized (mPackages) {
final Iterator i = mProviders.mProviders.values().iterator();
while (i.hasNext()) {
final PackageParser.Provider p = i.next();
PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
if (ps != null && p.info.authority != null
&& (processName == null
|| (p.info.processName.equals(processName)
&& UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
&& mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) {
// See PM.queryContentProviders()'s javadoc for why we have the metaData
// parameter.
if (metaDataKey != null
&& (p.metaData == null || !p.metaData.containsKey(metaDataKey))) {
continue;
}
final ComponentName component =
new ComponentName(p.info.packageName, p.info.name);
if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) {
continue;
}
if (finalList == null) {
finalList = new ArrayList(3);
}
ProviderInfo info = PackageParser.generateProviderInfo(p, flags,
ps.readUserState(userId), userId);
if (info != null) {
finalList.add(info);
}
}
}
}
if (finalList != null) {
Collections.sort(finalList, mProviderInitOrderSorter);
return new ParceledListSlice(finalList);
}
return ParceledListSlice.emptyList();
}
@Override
public InstrumentationInfo getInstrumentationInfo(ComponentName component, int flags) {
// reader
synchronized (mPackages) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
if (ps == null) return null;
if (filterAppAccessLPr(ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) {
return null;
}
final PackageParser.Instrumentation i = mInstrumentation.get(component);
return PackageParser.generateInstrumentationInfo(i, flags);
}
}
@Override
public @NonNull ParceledListSlice queryInstrumentation(
String targetPackage, int flags) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final PackageSetting ps = mSettings.mPackages.get(targetPackage);
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return ParceledListSlice.emptyList();
}
return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags));
}
private @NonNull List queryInstrumentationInternal(String targetPackage,
int flags) {
ArrayList finalList = new ArrayList();
// reader
synchronized (mPackages) {
final Iterator i = mInstrumentation.values().iterator();
while (i.hasNext()) {
final PackageParser.Instrumentation p = i.next();
if (targetPackage == null
|| targetPackage.equals(p.info.targetPackage)) {
InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p,
flags);
if (ii != null) {
finalList.add(ii);
}
}
}
}
return finalList;
}
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
scanDirLI(scanDir, parseFlags, scanFlags, currentTime);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + scanDir);
return;
}
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
+ " flags=0x" + Integer.toHexString(parseFlags));
}
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
mParallelPackageParserCallback)) {
// Submit files for parsing in parallel
int fileCount = 0;
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
// Process results one by one
for (; fileCount > 0; fileCount--) {
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
if (throwable == null) {
// TODO(toddke): move lower in the scan chain
// Static shared libraries have synthetic package names
if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(parseResult.pkg);
}
try {
if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
currentTime, null);
}
} catch (PackageManagerException e) {
errorCode = e.error;
Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
}
} else if (throwable instanceof PackageParser.PackageParserException) {
PackageParser.PackageParserException e = (PackageParser.PackageParserException)
throwable;
errorCode = e.error;
Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());
} else {
throw new IllegalStateException("Unexpected exception occurred while parsing "
+ parseResult.scanFile, throwable);
}
// Delete invalid userdata apps
if ((scanFlags & SCAN_AS_SYSTEM) == 0 &&
errorCode == PackageManager.INSTALL_FAILED_INVALID_APK) {
logCriticalInfo(Log.WARN,
"Deleting invalid package at " + parseResult.scanFile);
removeCodePathLI(parseResult.scanFile);
}
}
}
}
public static void reportSettingsProblem(int priority, String msg) {
logCriticalInfo(priority, msg);
}
private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg,
boolean forceCollect, boolean skipVerify) throws PackageManagerException {
// When upgrading from pre-N MR1, verify the package time stamp using the package
// directory and not the APK file.
final long lastModifiedTime = mIsPreNMR1Upgrade
? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg);
if (ps != null && !forceCollect
&& ps.codePathString.equals(pkg.codePath)
&& ps.timeStamp == lastModifiedTime
&& !isCompatSignatureUpdateNeeded(pkg)
&& !isRecoverSignatureUpdateNeeded(pkg)) {
if (ps.signatures.mSigningDetails.signatures != null
&& ps.signatures.mSigningDetails.signatures.length != 0
&& ps.signatures.mSigningDetails.signatureSchemeVersion
!= SignatureSchemeVersion.UNKNOWN) {
// Optimization: reuse the existing cached signing data
// if the package appears to be unchanged.
pkg.mSigningDetails =
new PackageParser.SigningDetails(ps.signatures.mSigningDetails);
return;
}
Slog.w(TAG, "PackageSetting for " + ps.name
+ " is missing signatures. Collecting certs again to recover them.");
} else {
Slog.i(TAG, pkg.codePath + " changed; collecting certs" +
(forceCollect ? " (forced)" : ""));
}
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
PackageParser.collectCertificates(pkg, skipVerify);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
/**
* Traces a package scan.
* @see #scanPackageLI(File, int, int, long, UserHandle)
*/
private PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
try {
return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
/**
* Scans a package and returns the newly parsed package.
* Returns {@code null} in case of errors and the error code is stored in mLastScanError
*/
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// Static shared libraries have synthetic package names
if (pkg.applicationInfo.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(pkg);
}
return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
}
/**
* Scans a package and returns the newly parsed package.
* @throws PackageManagerException on a parse error.
*/
private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
throws PackageManagerException {
// If the package has children and this is the first dive in the function
// we scan the package with the SCAN_CHECK_ONLY flag set to see whether all
// packages (parent and children) would be successfully scanned before the
// actual scan since scanning mutates internal state and we want to atomically
// install the package and its children.
if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
scanFlags |= SCAN_CHECK_ONLY;
}
} else {
scanFlags &= ~SCAN_CHECK_ONLY;
}
// Scan the parent
PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags,
scanFlags, currentTime, user);
// Scan the children
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPackage = pkg.childPackages.get(i);
addForInitLI(childPackage, parseFlags, scanFlags,
currentTime, user);
}
if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
}
return scannedPkg;
}
/**
* Returns if full apk verification can be skipped for the whole package, including the splits.
*/
private boolean canSkipFullPackageVerification(PackageParser.Package pkg) {
if (!canSkipFullApkVerification(pkg.baseCodePath)) {
return false;
}
// TODO: Allow base and splits to be verified individually.
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (int i = 0; i < pkg.splitCodePaths.length; i++) {
if (!canSkipFullApkVerification(pkg.splitCodePaths[i])) {
return false;
}
}
}
return true;
}
/**
* Returns if full apk verification can be skipped, depending on current FSVerity setup and
* whether the apk contains signed root hash. Note that the signer's certificate still needs to
* match one in a trusted source, and should be done separately.
*/
private boolean canSkipFullApkVerification(String apkPath) {
byte[] rootHashObserved = null;
try {
rootHashObserved = VerityUtils.generateFsverityRootHash(apkPath);
if (rootHashObserved == null) {
return false; // APK does not contain Merkle tree root hash.
}
synchronized (mInstallLock) {
// Returns whether the observed root hash matches what kernel has.
mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved);
return true;
}
} catch (InstallerException | IOException | DigestException |
NoSuchAlgorithmException e) {
Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
}
return false;
}
/**
* Adds a new package to the internal data structures during platform initialization.
* After adding, the package is known to the system and available for querying.
*
For packages located on the device ROM [eg. packages located in /system, /vendor,
* etc...], additional checks are performed. Basic verification [such as ensuring
* matching signatures, checking version codes, etc...] occurs if the package is
* identical to a previously known package. If the package fails a signature check,
* the version installed on /data will be removed. If the version of the new package
* is less than or equal than the version on /data, it will be ignored.
*
Regardless of the package location, the results are applied to the internal
* structures and the package is made available to the rest of the system.
*
NOTE: The return value should be removed. It's the passed in package object.
*/
private PackageParser.Package addForInitLI(PackageParser.Package pkg,
@ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
throws PackageManagerException {
final boolean scanSystemPartition = (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0;
final String renamedPkgName;
final PackageSetting disabledPkgSetting;
final boolean isSystemPkgUpdated;
final boolean pkgAlreadyExists;
PackageSetting pkgSetting;
// NOTE: installPackageLI() has the same code to setup the package's
// application info. This probably should be done lower in the call
// stack [such as scanPackageOnly()]. However, we verify the application
// info prior to that [in scanPackageNew()] and thus have to setup
// the application info early.
pkg.setApplicationVolumeUuid(pkg.volumeUuid);
pkg.setApplicationInfoCodePath(pkg.codePath);
pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
pkg.setApplicationInfoResourcePath(pkg.codePath);
pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
synchronized (mPackages) {
renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
final String realPkgName = getRealPackageName(pkg, renamedPkgName);
if (realPkgName != null) {
ensurePackageRenamed(pkg, renamedPkgName);
}
final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName);
final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName);
pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
pkgAlreadyExists = pkgSetting != null;
final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName;
disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName);
isSystemPkgUpdated = disabledPkgSetting != null;
if (DEBUG_INSTALL && isSystemPkgUpdated) {
Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
}
final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null)
? mSettings.getSharedUserLPw(pkg.mSharedUserId,
0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
: null;
if (DEBUG_PACKAGE_SCANNING
&& (parseFlags & PackageParser.PARSE_CHATTY) != 0
&& sharedUserSetting != null) {
Log.d(TAG, "Shared UserID " + pkg.mSharedUserId
+ " (uid=" + sharedUserSetting.userId + "):"
+ " packages=" + sharedUserSetting.packages);
}
if (scanSystemPartition) {
// Potentially prune child packages. If the application on the /system
// partition has been updated via OTA, but, is still disabled by a
// version on /data, cycle through all of its children packages and
// remove children that are no longer defined.
if (isSystemPkgUpdated) {
final int scannedChildCount = (pkg.childPackages != null)
? pkg.childPackages.size() : 0;
final int disabledChildCount = disabledPkgSetting.childPackageNames != null
? disabledPkgSetting.childPackageNames.size() : 0;
for (int i = 0; i < disabledChildCount; i++) {
String disabledChildPackageName =
disabledPkgSetting.childPackageNames.get(i);
boolean disabledPackageAvailable = false;
for (int j = 0; j < scannedChildCount; j++) {
PackageParser.Package childPkg = pkg.childPackages.get(j);
if (childPkg.packageName.equals(disabledChildPackageName)) {
disabledPackageAvailable = true;
break;
}
}
if (!disabledPackageAvailable) {
mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName);
}
}
// we're updating the disabled package, so, scan it as the package setting
final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */,
null /* originalPkgSetting */, null, parseFlags, scanFlags,
(pkg == mPlatformPackage), user);
applyPolicy(pkg, parseFlags, scanFlags);
scanPackageOnlyLI(request, mFactoryTest, -1L);
}
}
}
final boolean newPkgChangedPaths =
pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath);
final boolean newPkgVersionGreater =
pkgAlreadyExists && pkg.getLongVersionCode() > pkgSetting.versionCode;
final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
&& newPkgChangedPaths && newPkgVersionGreater;
if (isSystemPkgBetter) {
// The version of the application on /system is greater than the version on
// /data. Switch back to the application on /system.
// It's safe to assume the application on /system will correctly scan. If not,
// there won't be a working copy of the application.
synchronized (mPackages) {
// just remove the loaded entries from package lists
mPackages.remove(pkgSetting.name);
}
logCriticalInfo(Log.WARN,
"System package updated;"
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
final InstallArgs args = createInstallArgsForExisting(
packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
args.cleanUpResourcesLI();
synchronized (mPackages) {
mSettings.enableSystemPackageLPw(pkgSetting.name);
}
}
if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
// The version of the application on the /system partition is less than or
// equal to the version on the /data partition. Throw an exception and use
// the application already installed on the /data partition.
throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at "
+ pkg.codePath + " ignored: updated version " + disabledPkgSetting.versionCode
+ " better than this " + pkg.getLongVersionCode());
}
// Verify certificates against what was last scanned. If it is an updated priv app, we will
// force re-collecting certificate.
final boolean forceCollect = PackageManagerServiceUtils.isApkVerificationForced(
disabledPkgSetting);
// Full APK verification can be skipped during certificate collection, only if the file is
// in verified partition, or can be verified on access (when apk verity is enabled). In both
// cases, only data in Signing Block is verified instead of the whole file.
final boolean skipVerify = ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) ||
(forceCollect && canSkipFullPackageVerification(pkg));
collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);
boolean shouldHideSystemApp = false;
// A new application appeared on /system, but, we already have a copy of
// the application installed on /data.
if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
&& !pkgSetting.isSystem()) {
if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)) {
logCriticalInfo(Log.WARN,
"System package signature mismatch;"
+ " name: " + pkgSetting.name);
try (PackageFreezer freezer = freezePackage(pkg.packageName,
"scanPackageInternalLI")) {
deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
}
pkgSetting = null;
} else if (newPkgVersionGreater) {
// The application on /system is newer than the application on /data.
// Simply remove the application on /data [keeping application data]
// and replace it with the version on /system.
logCriticalInfo(Log.WARN,
"System package enabled;"
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
InstallArgs args = createInstallArgsForExisting(
packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
} else {
// The application on /system is older than the application on /data. Hide
// the application on /system and the version on /data will be scanned later
// and re-added like an update.
shouldHideSystemApp = true;
logCriticalInfo(Log.INFO,
"System package disabled;"
+ " name: " + pkgSetting.name
+ "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode
+ "; new: " + pkg.codePath + " @ " + pkg.codePath);
}
}
final PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
if (shouldHideSystemApp) {
synchronized (mPackages) {
mSettings.disableSystemPackageLPw(pkg.packageName, true);
}
}
return scannedPkg;
}
private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) {
// Derive the new package synthetic package name
pkg.setPackageName(pkg.packageName + STATIC_SHARED_LIB_DELIMITER
+ pkg.staticSharedLibVersion);
}
private static String fixProcessName(String defProcessName,
String processName) {
if (processName == null) {
return defProcessName;
}
return processName;
}
/**
* Enforces that only the system UID or root's UID can call a method exposed
* via Binder.
*
* @param message used as message if SecurityException is thrown
* @throws SecurityException if the caller is not system or root
*/
private static final void enforceSystemOrRoot(String message) {
final int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) {
throw new SecurityException(message);
}
}
@Override
public void performFstrimIfNeeded() {
enforceSystemOrRoot("Only the system can request fstrim");
// Before everything else, see whether we need to fstrim.
try {
IStorageManager sm = PackageHelper.getStorageManager();
if (sm != null) {
boolean doTrim = false;
final long interval = android.provider.Settings.Global.getLong(
mContext.getContentResolver(),
android.provider.Settings.Global.FSTRIM_MANDATORY_INTERVAL,
DEFAULT_MANDATORY_FSTRIM_INTERVAL);
if (interval > 0) {
final long timeSinceLast = System.currentTimeMillis() - sm.lastMaintenance();
if (timeSinceLast > interval) {
doTrim = true;
Slog.w(TAG, "No disk maintenance in " + timeSinceLast
+ "; running immediately");
}
}
if (doTrim) {
final boolean dexOptDialogShown;
synchronized (mPackages) {
dexOptDialogShown = mDexOptDialogShown;
}
if (!isFirstBoot() && dexOptDialogShown) {
try {
ActivityManager.getService().showBootMessage(
mContext.getResources().getString(
R.string.android_upgrading_fstrim), true);
} catch (RemoteException e) {
}
}
sm.runMaintenance();
}
} else {
Slog.e(TAG, "storageManager service unavailable!");
}
} catch (RemoteException e) {
// Can't happen; StorageManagerService is local
}
}
@Override
public void updatePackagesIfNeeded() {
enforceSystemOrRoot("Only the system can request package update");
// We need to re-extract after an OTA.
boolean causeUpgrade = isUpgrade();
// First boot or factory reset.
// Note: we also handle devices that are upgrading to N right now as if it is their
// first boot, as they do not have profile data.
boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade;
// We need to re-extract after a pruned cache, as AoT-ed files will be out of date.
boolean causePrunedCache = VMRuntime.didPruneDalvikCache();
if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) {
return;
}
List pkgs;
synchronized (mPackages) {
pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
}
final long startTime = System.nanoTime();
final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
false /* bootComplete */);
final int elapsedTimeSeconds =
(int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
MetricsLogger.histogram(mContext, "opt_dialog_num_dexopted", stats[0]);
MetricsLogger.histogram(mContext, "opt_dialog_num_skipped", stats[1]);
MetricsLogger.histogram(mContext, "opt_dialog_num_failed", stats[2]);
MetricsLogger.histogram(mContext, "opt_dialog_num_total", getOptimizablePackages().size());
MetricsLogger.histogram(mContext, "opt_dialog_time_s", elapsedTimeSeconds);
}
/*
* Return the prebuilt profile path given a package base code path.
*/
private static String getPrebuildProfilePath(PackageParser.Package pkg) {
return pkg.baseCodePath + ".prof";
}
/**
* Performs dexopt on the set of packages in {@code packages} and returns an int array
* containing statistics about the invocation. The array consists of three elements,
* which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped}
* and {@code numberOfPackagesFailed}.
*/
private int[] performDexOptUpgrade(List pkgs, boolean showDialog,
final int compilationReason, boolean bootComplete) {
int numberOfPackagesVisited = 0;
int numberOfPackagesOptimized = 0;
int numberOfPackagesSkipped = 0;
int numberOfPackagesFailed = 0;
final int numberOfPackagesToDexopt = pkgs.size();
for (PackageParser.Package pkg : pkgs) {
numberOfPackagesVisited++;
boolean useProfileForDexopt = false;
if ((isFirstBoot() || isUpgrade()) && isSystemApp(pkg)) {
// Copy over initial preopt profiles since we won't get any JIT samples for methods
// that are already compiled.
File profileFile = new File(getPrebuildProfilePath(pkg));
// Copy profile if it exists.
if (profileFile.exists()) {
try {
// We could also do this lazily before calling dexopt in
// PackageDexOptimizer to prevent this happening on first boot. The issue
// is that we don't have a good way to say "do this only once".
if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
pkg.applicationInfo.uid, pkg.packageName,
ArtManager.getProfileName(null))) {
Log.e(TAG, "Installer failed to copy system profile!");
} else {
// Disabled as this causes speed-profile compilation during first boot
// even if things are already compiled.
// useProfileForDexopt = true;
}
} catch (Exception e) {
Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ",
e);
}
} else {
PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);
// Handle compressed APKs in this path. Only do this for stubs with profiles to
// minimize the number off apps being speed-profile compiled during first boot.
// The other paths will not change the filter.
if (disabledPs != null && disabledPs.pkg.isStub) {
// The package is the stub one, remove the stub suffix to get the normal
// package and APK names.
String systemProfilePath =
getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, "");
profileFile = new File(systemProfilePath);
// If we have a profile for a compressed APK, copy it to the reference
// location.
// Note that copying the profile here will cause it to override the
// reference profile every OTA even though the existing reference profile
// may have more data. We can't copy during decompression since the
// directories are not set up at that point.
if (profileFile.exists()) {
try {
// We could also do this lazily before calling dexopt in
// PackageDexOptimizer to prevent this happening on first boot. The
// issue is that we don't have a good way to say "do this only
// once".
if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
pkg.applicationInfo.uid, pkg.packageName,
ArtManager.getProfileName(null))) {
Log.e(TAG, "Failed to copy system profile for stub package!");
} else {
useProfileForDexopt = true;
}
} catch (Exception e) {
Log.e(TAG, "Failed to copy profile " +
profileFile.getAbsolutePath() + " ", e);
}
}
}
}
}
if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
if (DEBUG_DEXOPT) {
Log.i(TAG, "Skipping update of of non-optimizable app " + pkg.packageName);
}
numberOfPackagesSkipped++;
continue;
}
if (DEBUG_DEXOPT) {
Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " +
numberOfPackagesToDexopt + ": " + pkg.packageName);
}
if (showDialog) {
try {
ActivityManager.getService().showBootMessage(
mContext.getResources().getString(R.string.android_upgrading_apk,
numberOfPackagesVisited, numberOfPackagesToDexopt), true);
} catch (RemoteException e) {
}
synchronized (mPackages) {
mDexOptDialogShown = true;
}
}
int pkgCompilationReason = compilationReason;
if (useProfileForDexopt) {
// Use background dexopt mode to try and use the profile. Note that this does not
// guarantee usage of the profile.
pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
}
// checkProfiles is false to avoid merging profiles during boot which
// might interfere with background compilation (b/28612421).
// Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
// behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
// trade-off worth doing to save boot time work.
int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
if (compilationReason == REASON_FIRST_BOOT) {
// TODO: This doesn't cover the upgrade case, we should check for this too.
dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
}
int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
pkg.packageName,
pkgCompilationReason,
dexoptFlags));
switch (primaryDexOptStaus) {
case PackageDexOptimizer.DEX_OPT_PERFORMED:
numberOfPackagesOptimized++;
break;
case PackageDexOptimizer.DEX_OPT_SKIPPED:
numberOfPackagesSkipped++;
break;
case PackageDexOptimizer.DEX_OPT_FAILED:
numberOfPackagesFailed++;
break;
default:
Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus);
break;
}
}
return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped,
numberOfPackagesFailed };
}
@Override
public void notifyPackageUse(String packageName, int reason) {
synchronized (mPackages) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
if (getInstantAppPackageName(callingUid) != null) {
if (!isCallerSameApp(packageName, callingUid)) {
return;
}
} else {
if (isInstantApp(packageName, callingUserId)) {
return;
}
}
notifyPackageUseLocked(packageName, reason);
}
}
@GuardedBy("mPackages")
private void notifyPackageUseLocked(String packageName, int reason) {
final PackageParser.Package p = mPackages.get(packageName);
if (p == null) {
return;
}
p.mLastPackageUsageTimeInMills[reason] = System.currentTimeMillis();
}
@Override
public void notifyDexLoad(String loadingPackageName, List classLoaderNames,
List classPaths, String loaderIsa) {
int userId = UserHandle.getCallingUserId();
ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
if (ai == null) {
Slog.w(TAG, "Loading a package that does not exist for the calling user. package="
+ loadingPackageName + ", user=" + userId);
return;
}
mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId);
}
@Override
public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule,
IDexModuleRegisterCallback callback) {
int userId = UserHandle.getCallingUserId();
ApplicationInfo ai = getApplicationInfo(packageName, /*flags*/ 0, userId);
DexManager.RegisterDexModuleResult result;
if (ai == null) {
Slog.w(TAG, "Registering a dex module for a package that does not exist for the" +
" calling user. package=" + packageName + ", user=" + userId);
result = new DexManager.RegisterDexModuleResult(false, "Package not installed");
} else {
result = mDexManager.registerDexModule(ai, dexModulePath, isSharedModule, userId);
}
if (callback != null) {
mHandler.post(() -> {
try {
callback.onDexModuleRegistered(dexModulePath, result.success, result.message);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to callback after module registration " + dexModulePath, e);
}
});
}
}
/**
* Ask the package manager to perform a dex-opt with the given compiler filter.
*
* Note: exposed only for the shell command to allow moving packages explicitly to a
* definite state.
*/
@Override
public boolean performDexOptMode(String packageName,
boolean checkProfiles, String targetCompilerFilter, boolean force,
boolean bootComplete, String splitName) {
int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) |
(force ? DexoptOptions.DEXOPT_FORCE : 0) |
(bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0);
return performDexOpt(new DexoptOptions(packageName, REASON_UNKNOWN,
targetCompilerFilter, splitName, flags));
}
/**
* Ask the package manager to perform a dex-opt with the given compiler filter on the
* secondary dex files belonging to the given package.
*
* Note: exposed only for the shell command to allow moving packages explicitly to a
* definite state.
*/
@Override
public boolean performDexOptSecondary(String packageName, String compilerFilter,
boolean force) {
int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES |
DexoptOptions.DEXOPT_BOOT_COMPLETE |
(force ? DexoptOptions.DEXOPT_FORCE : 0);
return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags));
}
/*package*/ boolean performDexOpt(DexoptOptions options) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return false;
} else if (isInstantApp(options.getPackageName(), UserHandle.getCallingUserId())) {
return false;
}
if (options.isDexoptOnlySecondaryDex()) {
return mDexManager.dexoptSecondaryDex(options);
} else {
int dexoptStatus = performDexOptWithStatus(options);
return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
}
}
/**
* Perform dexopt on the given package and return one of following result:
* {@link PackageDexOptimizer#DEX_OPT_SKIPPED}
* {@link PackageDexOptimizer#DEX_OPT_PERFORMED}
* {@link PackageDexOptimizer#DEX_OPT_FAILED}
*/
/* package */ int performDexOptWithStatus(DexoptOptions options) {
return performDexOptTraced(options);
}
private int performDexOptTraced(DexoptOptions options) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
try {
return performDexOptInternal(options);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
// Run dexopt on a given package. Returns true if dexopt did not fail, i.e.
// if the package can now be considered up to date for the given filter.
private int performDexOptInternal(DexoptOptions options) {
PackageParser.Package p;
synchronized (mPackages) {
p = mPackages.get(options.getPackageName());
if (p == null) {
// Package could not be found. Report failure.
return PackageDexOptimizer.DEX_OPT_FAILED;
}
mPackageUsage.maybeWriteAsync(mPackages);
mCompilerStats.maybeWriteAsync();
}
long callingId = Binder.clearCallingIdentity();
try {
synchronized (mInstallLock) {
return performDexOptInternalWithDependenciesLI(p, options);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
public ArraySet getOptimizablePackages() {
ArraySet