> 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);
// Expose private service for system components to use.
LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
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);
}
}
private int decompressFile(File srcFile, File dstFile) throws ErrnoException {
if (DEBUG_COMPRESSION) {
Slog.i(TAG, "Decompress file"
+ "; src: " + srcFile.getAbsolutePath()
+ ", dst: " + dstFile.getAbsolutePath());
}
try (
InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile));
OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/);
) {
Streams.copy(fileIn, fileOut);
Os.chmod(dstFile.getAbsolutePath(), 0644);
return PackageManager.INSTALL_SUCCEEDED;
} catch (IOException e) {
logCriticalInfo(Log.ERROR, "Failed to decompress file"
+ "; src: " + srcFile.getAbsolutePath()
+ ", dst: " + dstFile.getAbsolutePath());
}
return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
}
private File[] getCompressedFiles(String codePath) {
return new File(codePath).listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(COMPRESSED_EXTENSION);
}
});
}
private boolean compressedFileExists(String codePath) {
final File[] compressedFiles = getCompressedFiles(codePath);
return compressedFiles != null && compressedFiles.length > 0;
}
/**
* 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");
}
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);
// 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
return mIsUpgrade;
}
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);
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);
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_EPHEMERAL) {
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*/);
// temporarily look for the old action
if (resolvers.size() == 0) {
if (DEBUG_EPHEMERAL) {
Slog.d(TAG, "Ephemeral resolver not found with new action; try old one");
}
actionName = Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE;
resolverIntent.setAction(actionName);
resolvers = queryIntentServicesInternal(resolverIntent, null,
resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
}
final int N = resolvers.size();
if (N == 0) {
if (DEBUG_EPHEMERAL) {
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_EPHEMERAL) {
Slog.d(TAG, "Ephemeral resolver not in allowed package list;"
+ " pkg: " + packageName + ", info:" + info);
}
continue;
}
if (DEBUG_EPHEMERAL) {
Slog.v(TAG, "Ephemeral resolver found;"
+ " pkg: " + packageName + ", info:" + info);
}
return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName);
}
if (DEBUG_EPHEMERAL) {
Slog.v(TAG, "Ephemeral resolver NOT found");
}
return null;
}
private @Nullable ActivityInfo getInstantAppInstallerLPr() {
final Intent intent = new Intent(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
final int resolveFlags =
MATCH_DIRECT_BOOT_AWARE
| MATCH_DIRECT_BOOT_UNAWARE
| (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
List matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
resolveFlags, UserHandle.USER_SYSTEM);
// temporarily look for the old action
if (matches.isEmpty()) {
if (DEBUG_EPHEMERAL) {
Slog.d(TAG, "Ephemeral installer not found with new action; try old one");
}
intent.setAction(Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE);
matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
resolveFlags, UserHandle.USER_SYSTEM);
}
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)) {
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);
// temporarily look for the old action
if (matches.isEmpty()) {
if (DEBUG_EPHEMERAL) {
Slog.d(TAG, "Ephemeral resolver settings not found with new action; try old one");
}
intent.setAction(Intent.ACTION_EPHEMERAL_RESOLVER_SETTINGS);
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.isSystemApp()) {
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 (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. The special
* processes are {@link Process#SYSTEM_UID}, {@link Process#SHELL_UID}
* and {@code 0}
* - 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.SYSTEM_UID
|| callingUid == Process.SHELL_UID
|| callingUid == Process.ROOT_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;
}
PackageParser.Package p = ps.pkg;
if (p == 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;
}
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);
final PackageUserState state = ps.readUserState(userId);
if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0
&& ps.isSystem()) {
flags |= MATCH_ANY_USER;
}
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;
}
@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.isDirectBootAware()
|| ps.pkg.applicationInfo.isPartiallyDirectBootAware())) {
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();
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.getVersionCode(), 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, int versionCode,
int flags, int filterCallingUid, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForPackage(flags, userId, packageName);
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);
return activity != null
? (activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
: false;
} else if (type == TYPE_RECEIVER) {
final PackageParser.Activity activity = mReceivers.mActivities.get(component);
return activity != null
? (activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
: false;
} 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) {
// request for a specific component; if it hasn't been explicitly exposed, filter
if (component != null) {
return !isComponentVisibleToInstantApp(component, componentType);
}
// request for application; if no components have been explicitly exposed, filter
return ps.getInstantApp(userId) || !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.getVersion()) {
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);
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);
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;
}
static PermissionInfo generatePermissionInfo(BasePermission bp, int flags) {
if (bp.perm != null) {
return PackageParser.generatePermissionInfo(bp.perm, flags);
}
PermissionInfo pi = new PermissionInfo();
pi.name = bp.name;
pi.packageName = bp.sourcePackage;
pi.nonLocalizedLabel = bp.name;
pi.protectionLevel = bp.protectionLevel;
return pi;
}
@Override
public PermissionInfo getPermissionInfo(String name, String packageName, int flags) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return null;
}
// reader
synchronized (mPackages) {
final BasePermission p = mSettings.mPermissions.get(name);
if (p == null) {
return null;
}
// If the caller is an app that targets pre 26 SDK drop protection flags.
final PermissionInfo permissionInfo = generatePermissionInfo(p, flags);
if (permissionInfo != null) {
permissionInfo.protectionLevel = adjustPermissionProtectionFlagsLPr(
permissionInfo.protectionLevel, packageName, callingUid);
}
return permissionInfo;
}
}
private int adjustPermissionProtectionFlagsLPr(int protectionLevel,
String packageName, int uid) {
// Signature permission flags area always reported
final int protectionLevelMasked = protectionLevel
& (PermissionInfo.PROTECTION_NORMAL
| PermissionInfo.PROTECTION_DANGEROUS
| PermissionInfo.PROTECTION_SIGNATURE);
if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) {
return protectionLevel;
}
// System sees all flags.
final int appId = UserHandle.getAppId(uid);
if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID
|| appId == Process.SHELL_UID) {
return protectionLevel;
}
// Normalize package name to handle renamed packages and static libs
packageName = resolveInternalPackageNameLPr(packageName,
PackageManager.VERSION_CODE_HIGHEST);
// Apps that target O see flags for all protection levels.
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps == null) {
return protectionLevel;
}
if (ps.appId != appId) {
return protectionLevel;
}
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
return protectionLevel;
}
if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
return protectionLevelMasked;
}
return protectionLevel;
}
@Override
public @Nullable ParceledListSlice queryPermissionsByGroup(String group,
int flags) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
// reader
synchronized (mPackages) {
if (group != null && !mPermissionGroups.containsKey(group)) {
// This is thrown as NameNotFoundException
return null;
}
ArrayList out = new ArrayList(10);
for (BasePermission p : mSettings.mPermissions.values()) {
if (group == null) {
if (p.perm == null || p.perm.info.group == null) {
out.add(generatePermissionInfo(p, flags));
}
} else {
if (p.perm != null && group.equals(p.perm.info.group)) {
out.add(PackageParser.generatePermissionInfo(p.perm, flags));
}
}
}
return new ParceledListSlice<>(out);
}
}
@Override
public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
// reader
synchronized (mPackages) {
return PackageParser.generatePermissionGroupInfo(
mPermissionGroups.get(name), flags);
}
}
@Override
public @NonNull ParceledListSlice getAllPermissionGroups(int flags) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return ParceledListSlice.emptyList();
}
// reader
synchronized (mPackages) {
final int N = mPermissionGroups.size();
ArrayList out
= new ArrayList(N);
for (PackageParser.PermissionGroup pg : mPermissionGroups.values()) {
out.add(PackageParser.generatePermissionGroupInfo(pg, flags));
}
return new ParceledListSlice<>(out);
}
}
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);
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 SparseArray 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.getVersionCode());
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.getVersionCode()));
}
}
}
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.getVersionCode(),
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 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) {
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
&& Intent.ACTION_VIEW.equals(intent.getAction())
&& hasWebURI(intent))
|| (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);
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;
}
@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);
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++) {
SparseArray 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.getVersion(), 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.getVersion()) {
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);
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);
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++) {
SparseArray 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) {
if (!sUserManager.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
}
final int callingUid = Binder.getCallingUid();
synchronized (mPackages) {
final PackageParser.Package p = mPackages.get(pkgName);
if (p != null && p.mExtras != null) {
final PackageSetting ps = (PackageSetting) p.mExtras;
if (filterAppAccessLPr(ps, callingUid, userId)) {
return PackageManager.PERMISSION_DENIED;
}
final boolean instantApp = ps.getInstantApp(userId);
final PermissionsState permissionsState = ps.getPermissionsState();
if (permissionsState.hasPermission(permName, userId)) {
if (instantApp) {
BasePermission bp = mSettings.mPermissions.get(permName);
if (bp != null && bp.isInstant()) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
return PackageManager.PERMISSION_GRANTED;
}
}
// Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
return PackageManager.PERMISSION_GRANTED;
}
}
}
return PackageManager.PERMISSION_DENIED;
}
@Override
public int checkUidPermission(String permName, int uid) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
final boolean isUidInstantApp = getInstantAppPackageName(uid) != null;
final int userId = UserHandle.getUserId(uid);
if (!sUserManager.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
}
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj != null) {
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
return PackageManager.PERMISSION_DENIED;
}
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return PackageManager.PERMISSION_DENIED;
}
}
final SettingBase settingBase = (SettingBase) obj;
final PermissionsState permissionsState = settingBase.getPermissionsState();
if (permissionsState.hasPermission(permName, userId)) {
if (isUidInstantApp) {
BasePermission bp = mSettings.mPermissions.get(permName);
if (bp != null && bp.isInstant()) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
return PackageManager.PERMISSION_GRANTED;
}
}
// Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
ArraySet perms = mSystemPermissions.get(uid);
if (perms != null) {
if (perms.contains(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms
.contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
return PackageManager.PERMISSION_GRANTED;
}
}
}
}
return PackageManager.PERMISSION_DENIED;
}
@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;
}
}
/**
* Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS
* or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller.
* @param checkShell whether to prevent shell from access if there's a debugging restriction
* @param message the message to log on security exception
*/
void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission,
boolean checkShell, String message) {
if (userId < 0) {
throw new IllegalArgumentException("Invalid userId " + userId);
}
if (checkShell) {
enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
}
if (userId == UserHandle.getUserId(callingUid)) return;
if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
if (requireFullPermission) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
} else {
try {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
} catch (SecurityException se) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS, message);
}
}
}
}
void enforceShellRestriction(String restriction, int callingUid, int userHandle) {
if (callingUid == Process.SHELL_UID) {
if (userHandle >= 0
&& sUserManager.hasUserRestriction(restriction, userHandle)) {
throw new SecurityException("Shell does not have permission to access user "
+ userHandle);
} else if (userHandle < 0) {
Slog.e(TAG, "Unable to check shell permission for user " + userHandle + "\n\t"
+ Debug.getCallers(3));
}
}
}
private BasePermission findPermissionTreeLP(String permName) {
for(BasePermission bp : mSettings.mPermissionTrees.values()) {
if (permName.startsWith(bp.name) &&
permName.length() > bp.name.length() &&
permName.charAt(bp.name.length()) == '.') {
return bp;
}
}
return null;
}
private BasePermission checkPermissionTreeLP(String permName) {
if (permName != null) {
BasePermission bp = findPermissionTreeLP(permName);
if (bp != null) {
if (bp.uid == UserHandle.getAppId(Binder.getCallingUid())) {
return bp;
}
throw new SecurityException("Calling uid "
+ Binder.getCallingUid()
+ " is not allowed to add to permission tree "
+ bp.name + " owned by uid " + bp.uid);
}
}
throw new SecurityException("No permission tree found for " + permName);
}
static boolean compareStrings(CharSequence s1, CharSequence s2) {
if (s1 == null) {
return s2 == null;
}
if (s2 == null) {
return false;
}
if (s1.getClass() != s2.getClass()) {
return false;
}
return s1.equals(s2);
}
static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
if (pi1.icon != pi2.icon) return false;
if (pi1.logo != pi2.logo) return false;
if (pi1.protectionLevel != pi2.protectionLevel) return false;
if (!compareStrings(pi1.name, pi2.name)) return false;
if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
// We'll take care of setting this one.
if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
// These are not currently stored in settings.
//if (!compareStrings(pi1.group, pi2.group)) return false;
//if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
//if (pi1.labelRes != pi2.labelRes) return false;
//if (pi1.descriptionRes != pi2.descriptionRes) return false;
return true;
}
int permissionInfoFootprint(PermissionInfo info) {
int size = info.name.length();
if (info.nonLocalizedLabel != null) size += info.nonLocalizedLabel.length();
if (info.nonLocalizedDescription != null) size += info.nonLocalizedDescription.length();
return size;
}
int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
int size = 0;
for (BasePermission perm : mSettings.mPermissions.values()) {
if (perm.uid == tree.uid) {
size += perm.name.length() + permissionInfoFootprint(perm.perm.info);
}
}
return size;
}
void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) {
// We calculate the max size of permissions defined by this uid and throw
// if that plus the size of 'info' would exceed our stated maximum.
if (tree.uid != Process.SYSTEM_UID) {
final int curTreeSize = calculateCurrentPermissionFootprintLocked(tree);
if (curTreeSize + permissionInfoFootprint(info) > MAX_PERMISSION_TREE_FOOTPRINT) {
throw new SecurityException("Permission tree size cap exceeded");
}
}
}
boolean addPermissionLocked(PermissionInfo info, boolean async) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
throw new SecurityException("Instant apps can't add permissions");
}
if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
throw new SecurityException("Label must be specified in permission");
}
BasePermission tree = checkPermissionTreeLP(info.name);
BasePermission bp = mSettings.mPermissions.get(info.name);
boolean added = bp == null;
boolean changed = true;
int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
if (added) {
enforcePermissionCapLocked(info, tree);
bp = new BasePermission(info.name, tree.sourcePackage,
BasePermission.TYPE_DYNAMIC);
} else if (bp.type != BasePermission.TYPE_DYNAMIC) {
throw new SecurityException(
"Not allowed to modify non-dynamic permission "
+ info.name);
} else {
if (bp.protectionLevel == fixedLevel
&& bp.perm.owner.equals(tree.perm.owner)
&& bp.uid == tree.uid
&& comparePermissionInfos(bp.perm.info, info)) {
changed = false;
}
}
bp.protectionLevel = fixedLevel;
info = new PermissionInfo(info);
info.protectionLevel = fixedLevel;
bp.perm = new PackageParser.Permission(tree.perm.owner, info);
bp.perm.info.packageName = tree.perm.info.packageName;
bp.uid = tree.uid;
if (added) {
mSettings.mPermissions.put(info.name, bp);
}
if (changed) {
if (!async) {
mSettings.writeLPr();
} else {
scheduleWriteSettingsLocked();
}
}
return added;
}
@Override
public boolean addPermission(PermissionInfo info) {
synchronized (mPackages) {
return addPermissionLocked(info, false);
}
}
@Override
public boolean addPermissionAsync(PermissionInfo info) {
synchronized (mPackages) {
return addPermissionLocked(info, true);
}
}
@Override
public void removePermission(String name) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
throw new SecurityException("Instant applications don't have access to this method");
}
synchronized (mPackages) {
checkPermissionTreeLP(name);
BasePermission bp = mSettings.mPermissions.get(name);
if (bp != null) {
if (bp.type != BasePermission.TYPE_DYNAMIC) {
throw new SecurityException(
"Not allowed to modify non-dynamic permission "
+ name);
}
mSettings.mPermissions.remove(name);
mSettings.writeLPr();
}
}
}
private static void enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(
PackageParser.Package pkg, BasePermission bp) {
int index = pkg.requestedPermissions.indexOf(bp.name);
if (index == -1) {
throw new SecurityException("Package " + pkg.packageName
+ " has not requested permission " + bp.name);
}
if (!bp.isRuntime() && !bp.isDevelopment()) {
throw new SecurityException("Permission " + bp.name
+ " is not a changeable permission type");
}
}
@Override
public void grantRuntimePermission(String packageName, String name, final int userId) {
grantRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
}
private void grantRuntimePermission(String packageName, String name, final int userId,
boolean overridePolicy) {
if (!sUserManager.exists(userId)) {
Log.e(TAG, "No such user:" + userId);
return;
}
final int callingUid = Binder.getCallingUid();
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
"grantRuntimePermission");
enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */,
"grantRuntimePermission");
final int uid;
final PackageSetting ps;
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
final BasePermission bp = mSettings.mPermissions.get(name);
if (bp == null) {
throw new IllegalArgumentException("Unknown permission: " + name);
}
ps = (PackageSetting) pkg.mExtras;
if (ps == null
|| filterAppAccessLPr(ps, callingUid, userId)) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);
// If a permission review is required for legacy apps we represent
// their permissions as always granted runtime ones since we need
// to keep the review required permission flag per user while an
// install permission's state is shared across all users.
if (mPermissionReviewRequired
&& pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
&& bp.isRuntime()) {
return;
}
uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
final PermissionsState permissionsState = ps.getPermissionsState();
final int flags = permissionsState.getPermissionFlags(name, userId);
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
throw new SecurityException("Cannot grant system fixed permission "
+ name + " for package " + packageName);
}
if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
throw new SecurityException("Cannot grant policy fixed permission "
+ name + " for package " + packageName);
}
if (bp.isDevelopment()) {
// Development permissions must be handled specially, since they are not
// normal runtime permissions. For now they apply to all users.
if (permissionsState.grantInstallPermission(bp) !=
PermissionsState.PERMISSION_OPERATION_FAILURE) {
scheduleWriteSettingsLocked();
}
return;
}
if (ps.getInstantApp(userId) && !bp.isInstant()) {
throw new SecurityException("Cannot grant non-ephemeral permission"
+ name + " for package " + packageName);
}
if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
return;
}
final int result = permissionsState.grantRuntimePermission(bp, userId);
switch (result) {
case PermissionsState.PERMISSION_OPERATION_FAILURE: {
return;
}
case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
mHandler.post(new Runnable() {
@Override
public void run() {
killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
}
});
}
break;
}
if (bp.isRuntime()) {
logPermissionGranted(mContext, name, packageName);
}
mOnPermissionChangeListeners.onPermissionsChanged(uid);
// Not critical if that is lost - app has to request again.
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
// Only need to do this if user is initialized. Otherwise it's a new user
// and there are no processes running as the user yet and there's no need
// to make an expensive call to remount processes for the changed permissions.
if (READ_EXTERNAL_STORAGE.equals(name)
|| WRITE_EXTERNAL_STORAGE.equals(name)) {
final long token = Binder.clearCallingIdentity();
try {
if (sUserManager.isInitialized(userId)) {
StorageManagerInternal storageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
@Override
public void revokeRuntimePermission(String packageName, String name, int userId) {
revokeRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
}
private void revokeRuntimePermission(String packageName, String name, int userId,
boolean overridePolicy) {
if (!sUserManager.exists(userId)) {
Log.e(TAG, "No such user:" + userId);
return;
}
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
"revokeRuntimePermission");
enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, true /* checkShell */,
"revokeRuntimePermission");
final int appId;
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null
|| filterAppAccessLPr(ps, Binder.getCallingUid(), userId)) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
final BasePermission bp = mSettings.mPermissions.get(name);
if (bp == null) {
throw new IllegalArgumentException("Unknown permission: " + name);
}
enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);
// If a permission review is required for legacy apps we represent
// their permissions as always granted runtime ones since we need
// to keep the review required permission flag per user while an
// install permission's state is shared across all users.
if (mPermissionReviewRequired
&& pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
&& bp.isRuntime()) {
return;
}
final PermissionsState permissionsState = ps.getPermissionsState();
final int flags = permissionsState.getPermissionFlags(name, userId);
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
throw new SecurityException("Cannot revoke system fixed permission "
+ name + " for package " + packageName);
}
if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
throw new SecurityException("Cannot revoke policy fixed permission "
+ name + " for package " + packageName);
}
if (bp.isDevelopment()) {
// Development permissions must be handled specially, since they are not
// normal runtime permissions. For now they apply to all users.
if (permissionsState.revokeInstallPermission(bp) !=
PermissionsState.PERMISSION_OPERATION_FAILURE) {
scheduleWriteSettingsLocked();
}
return;
}
if (permissionsState.revokeRuntimePermission(bp, userId) ==
PermissionsState.PERMISSION_OPERATION_FAILURE) {
return;
}
if (bp.isRuntime()) {
logPermissionRevoked(mContext, name, packageName);
}
mOnPermissionChangeListeners.onPermissionsChanged(pkg.applicationInfo.uid);
// Critical, after this call app should never have the permission.
mSettings.writeRuntimePermissionsForUserLPr(userId, true);
appId = UserHandle.getAppId(pkg.applicationInfo.uid);
}
killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
}
/**
* Get the first event id for the permission.
*
* There are four events for each permission:
* - Request permission: first id + 0
* - Grant permission: first id + 1
* - Request for permission denied: first id + 2
* - Revoke permission: first id + 3
*
*
* @param name name of the permission
*
* @return The first event id for the permission
*/
private static int getBaseEventId(@NonNull String name) {
int eventIdIndex = ALL_DANGEROUS_PERMISSIONS.indexOf(name);
if (eventIdIndex == -1) {
if (AppOpsManager.permissionToOpCode(name) == AppOpsManager.OP_NONE
|| Build.IS_USER) {
Log.i(TAG, "Unknown permission " + name);
return MetricsEvent.ACTION_PERMISSION_REQUEST_UNKNOWN;
} else {
// Most likely #ALL_DANGEROUS_PERMISSIONS needs to be updated.
//
// Also update
// - EventLogger#ALL_DANGEROUS_PERMISSIONS
// - metrics_constants.proto
throw new IllegalStateException("Unknown permission " + name);
}
}
return MetricsEvent.ACTION_PERMISSION_REQUEST_READ_CALENDAR + eventIdIndex * 4;
}
/**
* Log that a permission was revoked.
*
* @param context Context of the caller
* @param name name of the permission
* @param packageName package permission if for
*/
private static void logPermissionRevoked(@NonNull Context context, @NonNull String name,
@NonNull String packageName) {
MetricsLogger.action(context, getBaseEventId(name) + 3, packageName);
}
/**
* Log that a permission request was granted.
*
* @param context Context of the caller
* @param name name of the permission
* @param packageName package permission if for
*/
private static void logPermissionGranted(@NonNull Context context, @NonNull String name,
@NonNull String packageName) {
MetricsLogger.action(context, getBaseEventId(name) + 1, packageName);
}
@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) {
updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL);
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 name, String packageName, int userId) {
if (!sUserManager.exists(userId)) {
return 0;
}
enforceGrantRevokeRuntimePermissionPermissions("getPermissionFlags");
final int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"getPermissionFlags");
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
return 0;
}
final BasePermission bp = mSettings.mPermissions.get(name);
if (bp == null) {
return 0;
}
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null
|| filterAppAccessLPr(ps, callingUid, userId)) {
return 0;
}
PermissionsState permissionsState = ps.getPermissionsState();
return permissionsState.getPermissionFlags(name, userId);
}
}
@Override
public void updatePermissionFlags(String name, String packageName, int flagMask,
int flagValues, int userId) {
if (!sUserManager.exists(userId)) {
return;
}
enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlags");
final int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */,
"updatePermissionFlags");
// Only the system can change these flags and nothing else.
if (getCallingUid() != Process.SYSTEM_UID) {
flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
}
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null
|| filterAppAccessLPr(ps, callingUid, userId)) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
final BasePermission bp = mSettings.mPermissions.get(name);
if (bp == null) {
throw new IllegalArgumentException("Unknown permission: " + name);
}
PermissionsState permissionsState = ps.getPermissionsState();
boolean hadState = permissionsState.getRuntimePermissionState(name, userId) != null;
if (permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues)) {
// Install and runtime permissions are stored in different places,
// so figure out what permission changed and persist the change.
if (permissionsState.getInstallPermissionState(name) != null) {
scheduleWriteSettingsLocked();
} else if (permissionsState.getRuntimePermissionState(name, userId) != null
|| hadState) {
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
}
}
}
/**
* 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) {
if (!sUserManager.exists(userId)) {
return;
}
enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlagsForAllApps");
enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, true /* checkShell */,
"updatePermissionFlagsForAllApps");
// Only the system can change system fixed flags.
if (getCallingUid() != Process.SYSTEM_UID) {
flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
}
synchronized (mPackages) {
boolean changed = false;
final int packageCount = mPackages.size();
for (int pkgIndex = 0; pkgIndex < packageCount; pkgIndex++) {
final PackageParser.Package pkg = mPackages.valueAt(pkgIndex);
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null) {
continue;
}
PermissionsState permissionsState = ps.getPermissionsState();
changed |= permissionsState.updatePermissionFlagsForAllPermissions(
userId, flagMask, flagValues);
}
if (changed) {
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
}
}
private void enforceGrantRevokeRuntimePermissionPermissions(String message) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
!= PackageManager.PERMISSION_GRANTED
&& mContext.checkCallingOrSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(message + " requires "
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS + " or "
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
}
}
@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.mSignatures, p2.mSignatures);
}
}
@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.mSignatures;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
s1 = ps.signatures.mSignatures;
} 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.mSignatures;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
s2 = ps.signatures.mSignatures;
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
return compareSignatures(s1, s2);
}
}
/**
* 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);
}
}
/**
* Compares two sets of signatures. Returns:
*
* {@link PackageManager#SIGNATURE_NEITHER_SIGNED}: if both signature sets are null,
*
* {@link PackageManager#SIGNATURE_FIRST_NOT_SIGNED}: if the first signature set is null,
*
* {@link PackageManager#SIGNATURE_SECOND_NOT_SIGNED}: if the second signature set is null,
*
* {@link PackageManager#SIGNATURE_MATCH}: if the two signature sets are identical,
*
* {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ.
*/
static int compareSignatures(Signature[] s1, Signature[] s2) {
if (s1 == null) {
return s2 == null
? PackageManager.SIGNATURE_NEITHER_SIGNED
: PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
}
if (s2 == null) {
return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
}
if (s1.length != s2.length) {
return PackageManager.SIGNATURE_NO_MATCH;
}
// Since both signature sets are of size 1, we can compare without HashSets.
if (s1.length == 1) {
return s1[0].equals(s2[0]) ?
PackageManager.SIGNATURE_MATCH :
PackageManager.SIGNATURE_NO_MATCH;
}
ArraySet set1 = new ArraySet();
for (Signature sig : s1) {
set1.add(sig);
}
ArraySet set2 = new ArraySet();
for (Signature sig : s2) {
set2.add(sig);
}
// Make sure s2 contains all signatures in s1.
if (set1.equals(set2)) {
return PackageManager.SIGNATURE_MATCH;
}
return PackageManager.SIGNATURE_NO_MATCH;
}
/**
* 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;
}
/**
* Used for backward compatibility to make sure any packages with
* certificate chains get upgraded to the new style. {@code existingSigs}
* will be in the old format (since they were stored on disk from before the
* system upgrade) and {@code scannedSigs} will be in the newer format.
*/
private int compareSignaturesCompat(PackageSignatures existingSigs,
PackageParser.Package scannedPkg) {
if (!isCompatSignatureUpdateNeeded(scannedPkg)) {
return PackageManager.SIGNATURE_NO_MATCH;
}
ArraySet existingSet = new ArraySet();
for (Signature sig : existingSigs.mSignatures) {
existingSet.add(sig);
}
ArraySet scannedCompatSet = new ArraySet();
for (Signature sig : scannedPkg.mSignatures) {
try {
Signature[] chainSignatures = sig.getChainSignatures();
for (Signature chainSig : chainSignatures) {
scannedCompatSet.add(chainSig);
}
} catch (CertificateEncodingException e) {
scannedCompatSet.add(sig);
}
}
/*
* Make sure the expanded scanned set contains all signatures in the
* existing one.
*/
if (scannedCompatSet.equals(existingSet)) {
// Migrate the old signatures to the new scheme.
existingSigs.assignSignatures(scannedPkg.mSignatures);
// The new KeySets will be re-added later in the scanning process.
synchronized (mPackages) {
mSettings.mKeySetManagerService.removeAppKeySetDataLPw(scannedPkg.packageName);
}
return PackageManager.SIGNATURE_MATCH;
}
return PackageManager.SIGNATURE_NO_MATCH;
}
private boolean isRecoverSignatureUpdateNeeded(PackageParser.Package scannedPkg) {
final VersionInfo ver = getSettingsVersionForPackage(scannedPkg);
return ver.databaseVersion < DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
}
private int compareSignaturesRecover(PackageSignatures existingSigs,
PackageParser.Package scannedPkg) {
if (!isRecoverSignatureUpdateNeeded(scannedPkg)) {
return PackageManager.SIGNATURE_NO_MATCH;
}
String msg = null;
try {
if (Signature.areEffectiveMatch(existingSigs.mSignatures, scannedPkg.mSignatures)) {
logCriticalInfo(Log.INFO, "Recovered effectively matching certificates for "
+ scannedPkg.packageName);
return PackageManager.SIGNATURE_MATCH;
}
} catch (CertificateException e) {
msg = e.getMessage();
}
logCriticalInfo(Log.INFO,
"Failed to recover certificates for " + scannedPkg.packageName + ": " + msg);
return PackageManager.SIGNATURE_NO_MATCH;
}
@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 permissionName) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
synchronized (mPackages) {
ArraySet pkgs = mAppOpPermissionPackages.get(permissionName);
if (pkgs == null) {
return null;
}
return pkgs.toArray(new String[pkgs.size()]);
}
}
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
return resolveIntentInternal(
intent, resolvedType, flags, userId, false /*includeInstantApps*/);
}
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);
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);
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 isEphemeralDisabled() {
return mEphemeralAppsDisabled;
}
private boolean isInstantAppAllowed(
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;
}
final boolean isWebUri = hasWebURI(intent);
if (!isWebUri || intent.getData().getHost() == null) {
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_EPHEMERAL) {
Slog.v(TAG, "DENY instant app;"
+ " pkg: " + packageName + ", status: " + status);
}
return false;
}
}
if (ps.getInstantApp(userId)) {
if (DEBUG_EPHEMERAL) {
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));
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 (hasWebURI(intent)) {
// 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);
}
private @NonNull List queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int filterCallingUid, int userId,
boolean resolveForStart) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
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);
}
// reader
boolean sortResult = false;
boolean addEphemeral = false;
List result;
final boolean ephemeralDisabled = isEphemeralDisabled();
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);
}
// Check for results in the current profile.
result = filterIfNotSystemUser(mActivities.queryIntent(
intent, resolvedType, flags, userId), userId);
addEphemeral = !ephemeralDisabled
&& isInstantAppAllowed(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 (hasWebURI(intent)) {
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 && !addEphemeral) {
// 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);
}
} else if (result.size() <= 1 && !addEphemeral) {
// 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);
}
// 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
addEphemeral = !ephemeralDisabled
&& isInstantAppAllowed(
intent, null /*result*/, userId, true /*skipPackageCheck*/);
if (result == null) {
result = new ArrayList<>();
}
}
}
}
if (addEphemeral) {
result = maybeAddInstantAppInstaller(result, intent, resolvedType, flags, userId);
}
if (sortResult) {
Collections.sort(result, mResolvePrioritySorter);
}
return applyPostResolutionFilter(result, instantAppPkgName);
}
private List maybeAddInstantAppInstaller(List result, Intent intent,
String resolvedType, int flags, int userId) {
// 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);
final int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
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_EPHEMERAL) {
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_EPHEMERAL) {
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*/);
auxiliaryResponse =
InstantAppResolver.doInstantAppResolutionPhaseOne(
mContext, 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(
ai.packageName, null /*splitName*/, ai.versionCode, null /*failureIntent*/);
}
}
if (auxiliaryResponse != null) {
if (DEBUG_EPHEMERAL) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
final PackageSetting ps =
mSettings.mPackages.get(mInstantAppInstallerActivity.packageName);
if (ps != null) {
ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId);
ephemeralInstaller.activityInfo.launchToken = auxiliaryResponse.token;
ephemeralInstaller.auxiliaryInfo = auxiliaryResponse;
// make sure this resolver is the default
ephemeralInstaller.isDefault = true;
ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
// add a non-generic filter
ephemeralInstaller.filter = new IntentFilter(intent.getAction());
ephemeralInstaller.filter.addDataPath(
intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
ephemeralInstaller.isInstantAppAvailable = true;
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.
* @return A filtered list of resolved activities.
*/
private List applyPostResolutionFilter(List resolveInfos,
String ephemeralPkgName) {
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
// TODO: When adding on-demand split support for non-instant apps, remove this check
// and always apply post filtering
// allow activities that are defined in the provided package
if (info.activityInfo.splitName != null
&& !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames,
info.activityInfo.splitName)) {
// 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);
installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
info.activityInfo.packageName, info.activityInfo.splitName,
info.activityInfo.applicationInfo.versionCode, null /*failureIntent*/);
// make sure this resolver is the default
installerInfo.isDefault = true;
installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
// 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;
}
// 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;
}
/**
* @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 static boolean hasWebURI(Intent intent) {
if (intent.getData() == null) {
return false;
}
final String scheme = intent.getScheme();
if (TextUtils.isEmpty(scheme)) {
return false;
}
return scheme.equals(IntentFilter.SCHEME_HTTP) || scheme.equals(IntentFilter.SCHEME_HTTPS);
}
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*/);
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));
}
private @NonNull List queryIntentReceiversInternal(Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
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);
}
// reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
final List result =
mReceivers.queryIntent(intent, resolvedType, flags, userId);
return applyPostResolutionFilter(result, instantAppPkgName);
}
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);
}
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();
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) {
// TODO: When adding on-demand split support for non-instant apps, remove this check
// and always apply post filtering
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_EPHEMERAL) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo);
installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
info.serviceInfo.packageName, info.serviceInfo.splitName,
info.serviceInfo.applicationInfo.versionCode, null /*failureIntent*/);
// make sure this resolver is the default
installerInfo.isDefault = true;
installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
// 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) {
// TODO: When adding on-demand split support for non-instant applications, remove
// this check and always apply post filtering
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_EPHEMERAL) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
final ResolveInfo installerInfo = new ResolveInfo(mInstantAppInstallerInfo);
installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
info.providerInfo.packageName, info.providerInfo.splitName,
info.providerInfo.applicationInfo.versionCode, null /*failureIntent*/);
// make sure this resolver is the default
installerInfo.isDefault = true;
installerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
// 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;
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)) {
return null;
}
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)) {
return null;
}
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);
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)) {
return null;
}
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)) {
return null;
}
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 || isEphemeralDisabled()) {
return null;
}
if (!canViewInstantApps(Binder.getCallingUid(), userId)) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
"getEphemeralApplications");
}
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) {
enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"isInstantApp");
if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
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 || isEphemeralDisabled()) {
return null;
}
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 || isEphemeralDisabled()) {
return true;
}
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 || isEphemeralDisabled()) {
return null;
}
if (!canViewInstantApps(Binder.getCallingUid(), userId)) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
"getInstantAppIcon");
}
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) {
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 dir, final int parseFlags, int scanFlags, long currentTime) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + dir.getAbsolutePath() + "]");
try {
scanDirLI(dir, parseFlags, scanFlags, currentTime);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
final File[] files = dir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + dir);
return;
}
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags
+ " flags=0x" + Integer.toHexString(parseFlags));
}
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) {
// Static shared libraries have synthetic package names
if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(parseResult.pkg);
}
try {
if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
scanPackageLI(parseResult.pkg, parseResult.scanFile, 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 ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
errorCode == PackageManager.INSTALL_FAILED_INVALID_APK) {
logCriticalInfo(Log.WARN,
"Deleting invalid package at " + parseResult.scanFile);
removeCodePathLI(parseResult.scanFile);
}
}
parallelPackageParser.close();
}
private static File getSettingsProblemFile() {
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
File fname = new File(systemDir, "uiderrors.txt");
return fname;
}
static void reportSettingsProblem(int priority, String msg) {
logCriticalInfo(priority, msg);
}
public static void logCriticalInfo(int priority, String msg) {
Slog.println(priority, TAG, msg);
EventLogTags.writePmCriticalInfo(msg);
try {
File fname = getSettingsProblemFile();
FileOutputStream out = new FileOutputStream(fname, true);
PrintWriter pw = new FastPrintWriter(out);
SimpleDateFormat formatter = new SimpleDateFormat();
String dateString = formatter.format(new Date(System.currentTimeMillis()));
pw.println(dateString + ": " + msg);
pw.close();
FileUtils.setPermissions(
fname.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH,
-1, -1);
} catch (java.io.IOException e) {
}
}
private long getLastModifiedTime(PackageParser.Package pkg, File srcFile) {
if (srcFile.isDirectory()) {
final File baseFile = new File(pkg.baseCodePath);
long maxModifiedTime = baseFile.lastModified();
if (pkg.splitCodePaths != null) {
for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) {
final File splitFile = new File(pkg.splitCodePaths[i]);
maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
}
}
return maxModifiedTime;
}
return srcFile.lastModified();
}
private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, File srcFile,
final int policyFlags) 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, srcFile);
if (ps != null
&& ps.codePath.equals(srcFile)
&& ps.timeStamp == lastModifiedTime
&& !isCompatSignatureUpdateNeeded(pkg)
&& !isRecoverSignatureUpdateNeeded(pkg)) {
long mSigningKeySetId = ps.keySetData.getProperSigningKeySet();
KeySetManagerService ksms = mSettings.mKeySetManagerService;
ArraySet signingKs;
synchronized (mPackages) {
signingKs = ksms.getPublicKeysFromKeySetLPr(mSigningKeySetId);
}
if (ps.signatures.mSignatures != null
&& ps.signatures.mSignatures.length != 0
&& signingKs != null) {
// Optimization: reuse the existing cached certificates
// if the package appears to be unchanged.
pkg.mSignatures = ps.signatures.mSignatures;
pkg.mSigningKeys = signingKs;
return;
}
Slog.w(TAG, "PackageSetting for " + ps.name
+ " is missing signatures. Collecting certs again to recover them.");
} else {
Slog.i(TAG, srcFile.toString() + " changed; collecting certs");
}
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
PackageParser.collectCertificates(pkg, policyFlags);
} 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);
if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
}
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 scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
}
/**
* Scans a package and returns the newly parsed package.
* @throws PackageManagerException on a parse error.
*/
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile,
final int policyFlags, 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 = scanPackageInternalLI(pkg, scanFile, policyFlags,
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);
scanPackageInternalLI(childPackage, scanFile, policyFlags, scanFlags,
currentTime, user);
}
if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
return scanPackageLI(pkg, scanFile, policyFlags, scanFlags, currentTime, user);
}
return scannedPkg;
}
/**
* Scans a package and returns the newly parsed package.
* @throws PackageManagerException on a parse error.
*/
private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg, File scanFile,
int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user)
throws PackageManagerException {
PackageSetting ps = null;
PackageSetting updatedPkg;
// reader
synchronized (mPackages) {
// Look to see if we already know about this package.
String oldName = mSettings.getRenamedPackageLPr(pkg.packageName);
if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
// This package has been renamed to its original name. Let's
// use that.
ps = mSettings.getPackageLPr(oldName);
}
// If there was no original package, see one for the real package name.
if (ps == null) {
ps = mSettings.getPackageLPr(pkg.packageName);
}
// Check to see if this package could be hiding/updating a system
// package. Must look for it either under the original or real
// package name depending on our state.
updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);
// If this is a package we don't know about on the system partition, we
// may need to remove disabled child packages on the system partition
// or may need to not add child packages if the parent apk is updated
// on the data partition and no longer defines this child package.
if ((policyFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {
// If this is a parent package for an updated system app and this system
// app got an OTA update which no longer defines some of the child packages
// we have to prune them from the disabled system packages.
PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);
if (disabledPs != null) {
final int scannedChildCount = (pkg.childPackages != null)
? pkg.childPackages.size() : 0;
final int disabledChildCount = disabledPs.childPackageNames != null
? disabledPs.childPackageNames.size() : 0;
for (int i = 0; i < disabledChildCount; i++) {
String disabledChildPackageName = disabledPs.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);
}
}
}
}
}
final boolean isUpdatedPkg = updatedPkg != null;
final boolean isUpdatedSystemPkg = isUpdatedPkg
&& (policyFlags & PackageParser.PARSE_IS_SYSTEM) != 0;
boolean isUpdatedPkgBetter = false;
// First check if this is a system package that may involve an update
if (isUpdatedSystemPkg) {
// If new package is not located in "/system/priv-app" (e.g. due to an OTA),
// it needs to drop FLAG_PRIVILEGED.
if (locationIsPrivileged(scanFile)) {
updatedPkg.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
} else {
updatedPkg.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
if (ps != null && !ps.codePath.equals(scanFile)) {
// The path has changed from what was last scanned... check the
// version of the new path against what we have stored to determine
// what to do.
if (DEBUG_INSTALL) Slog.d(TAG, "Path changing from " + ps.codePath);
if (pkg.mVersionCode <= ps.versionCode) {
// The system package has been updated and the code path does not match
// Ignore entry. Skip it.
if (DEBUG_INSTALL) Slog.i(TAG, "Package " + ps.name + " at " + scanFile
+ " ignored: updated version " + ps.versionCode
+ " better than this " + pkg.mVersionCode);
if (!updatedPkg.codePath.equals(scanFile)) {
Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg "
+ ps.name + " changing from " + updatedPkg.codePathString
+ " to " + scanFile);
updatedPkg.codePath = scanFile;
updatedPkg.codePathString = scanFile.toString();
updatedPkg.resourcePath = scanFile;
updatedPkg.resourcePathString = scanFile.toString();
}
updatedPkg.pkg = pkg;
updatedPkg.versionCode = pkg.mVersionCode;
// Update the disabled system child packages to point to the package too.
final int childCount = updatedPkg.childPackageNames != null
? updatedPkg.childPackageNames.size() : 0;
for (int i = 0; i < childCount; i++) {
String childPackageName = updatedPkg.childPackageNames.get(i);
PackageSetting updatedChildPkg = mSettings.getDisabledSystemPkgLPr(
childPackageName);
if (updatedChildPkg != null) {
updatedChildPkg.pkg = pkg;
updatedChildPkg.versionCode = pkg.mVersionCode;
}
}
} else {
// The current app on the system partition is better than
// what we have updated to on the data partition; switch
// back to the system partition version.
// At this point, its safely assumed that package installation for
// apps in system partition will go through. If not there won't be a working
// version of the app
// writer
synchronized (mPackages) {
// Just remove the loaded entries from package lists.
mPackages.remove(ps.name);
}
logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile
+ " reverting from " + ps.codePathString
+ ": new version " + pkg.mVersionCode
+ " better than installed " + ps.versionCode);
InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
synchronized (mPackages) {
mSettings.enableSystemPackageLPw(ps.name);
}
isUpdatedPkgBetter = true;
}
}
}
String resourcePath = null;
String baseResourcePath = null;
if ((policyFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !isUpdatedPkgBetter) {
if (ps != null && ps.resourcePathString != null) {
resourcePath = ps.resourcePathString;
baseResourcePath = ps.resourcePathString;
} else {
// Should not happen at all. Just log an error.
Slog.e(TAG, "Resource path not set for package " + pkg.packageName);
}
} else {
resourcePath = pkg.codePath;
baseResourcePath = pkg.baseCodePath;
}
// Set application objects path explicitly.
pkg.setApplicationVolumeUuid(pkg.volumeUuid);
pkg.setApplicationInfoCodePath(pkg.codePath);
pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
pkg.setApplicationInfoResourcePath(resourcePath);
pkg.setApplicationInfoBaseResourcePath(baseResourcePath);
pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
// throw an exception if we have an update to a system application, but, it's not more
// recent than the package we've already scanned
if (isUpdatedSystemPkg && !isUpdatedPkgBetter) {
throw new PackageManagerException(Log.WARN, "Package " + ps.name + " at "
+ scanFile + " ignored: updated version " + ps.versionCode
+ " better than this " + pkg.mVersionCode);
}
if (isUpdatedPkg) {
// An updated system app will not have the PARSE_IS_SYSTEM flag set
// initially
policyFlags |= PackageParser.PARSE_IS_SYSTEM;
// An updated privileged app will not have the PARSE_IS_PRIVILEGED
// flag set initially
if ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
policyFlags |= PackageParser.PARSE_IS_PRIVILEGED;
}
}
// Verify certificates against what was last scanned
collectCertificatesLI(ps, pkg, scanFile, policyFlags);
/*
* A new system app appeared, but we already had a non-system one of the
* same name installed earlier.
*/
boolean shouldHideSystemApp = false;
if (!isUpdatedPkg && ps != null
&& (policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {
/*
* Check to make sure the signatures match first. If they don't,
* wipe the installed application and its data.
*/
if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)
!= PackageManager.SIGNATURE_MATCH) {
logCriticalInfo(Log.WARN, "Package " + ps.name + " appeared on system, but"
+ " signatures don't match existing userdata copy; removing");
try (PackageFreezer freezer = freezePackage(pkg.packageName,
"scanPackageInternalLI")) {
deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
}
ps = null;
} else {
/*
* If the newly-added system app is an older version than the
* already installed version, hide it. It will be scanned later
* and re-added like an update.
*/
if (pkg.mVersionCode <= ps.versionCode) {
shouldHideSystemApp = true;
logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + scanFile
+ " but new version " + pkg.mVersionCode + " better than installed "
+ ps.versionCode + "; hiding system");
} else {
/*
* The newly found system app is a newer version that the
* one previously installed. Simply remove the
* already-installed application and replace it with our own
* while keeping the application data.
*/
logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile
+ " reverting from " + ps.codePathString + ": new version "
+ pkg.mVersionCode + " better than installed " + ps.versionCode);
InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
}
}
}
// The apk is forward locked (not public) if its code and resources
// are kept in different files. (except for app in either system or
// vendor path).
// TODO grab this value from PackageSettings
if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
policyFlags |= PackageParser.PARSE_FORWARD_LOCK;
}
}
final int userId = ((user == null) ? 0 : user.getIdentifier());
if (ps != null && ps.getInstantApp(userId)) {
scanFlags |= SCAN_AS_INSTANT_APP;
}
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, policyFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
/*
* If the system app should be overridden by a previously installed
* data, hide the system app now and let the /data/app scan pick it up
* again.
*/
if (shouldHideSystemApp) {
synchronized (mPackages) {
mSettings.disableSystemPackageLPw(pkg.packageName, true);
}
}
return scannedPkg;
}
private 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;
}
private void verifySignaturesLP(PackageSetting pkgSetting, PackageParser.Package pkg)
throws PackageManagerException {
if (pkgSetting.signatures.mSignatures != null) {
// Already existing package. Make sure signatures match
boolean match = compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH;
if (!match) {
match = compareSignaturesCompat(pkgSetting.signatures, pkg)
== PackageManager.SIGNATURE_MATCH;
}
if (!match) {
match = compareSignaturesRecover(pkgSetting.signatures, pkg)
== PackageManager.SIGNATURE_MATCH;
}
if (!match) {
throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " signatures do not match the "
+ "previously installed version; ignoring!");
}
}
// Check for shared user signatures
if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
// Already existing package. Make sure signatures match
boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
if (!match) {
match = compareSignaturesCompat(pkgSetting.sharedUser.signatures, pkg)
== PackageManager.SIGNATURE_MATCH;
}
if (!match) {
match = compareSignaturesRecover(pkgSetting.sharedUser.signatures, pkg)
== PackageManager.SIGNATURE_MATCH;
}
if (!match) {
throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
"Package " + pkg.packageName
+ " has no signatures that match those in shared user "
+ pkgSetting.sharedUser.name + "; ignoring!");
}
}
}
/**
* 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 */,
getCompilerFilterForReason(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,
String compilerFilter, 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++;
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)) {
Log.e(TAG, "Installer failed to copy system profile!");
}
} 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;
}
}
// If the OTA updates a system app which was previously preopted to a non-preopted state
// the app might end up being verified at runtime. That's because by default the apps
// are verify-profile but for preopted apps there's no profile.
// Do a hacky check to ensure that if we have no profiles (a reasonable indication
// that before the OTA the app was preopted) the app gets compiled with a non-profile
// filter (by default 'quicken').
// Note that at this stage unused apps are already filtered.
if (isSystemApp(pkg) &&
DexFile.isProfileGuidedCompilerFilter(compilerFilter) &&
!Environment.getReferenceProfile(pkg.packageName).exists()) {
compilerFilter = getNonProfileGuidedCompilerFilter(compilerFilter);
}
// 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;
int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
pkg.packageName,
compilerFilter,
dexoptFlags));
if (pkg.isSystemApp()) {
// Only dexopt shared secondary dex files belonging to system apps to not slow down
// too much boot after an OTA.
int secondaryDexoptFlags = dexoptFlags |
DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
DexoptOptions.DEXOPT_ONLY_SHARED_DEX;
mDexManager.dexoptSecondaryDex(new DexoptOptions(
pkg.packageName,
compilerFilter,
secondaryDexoptFlags));
}
// TODO(shubhamajmera): Record secondary dexopt stats.
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;
}
}
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, 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 pkgs = new ArraySet();
synchronized (mPackages) {
for (PackageParser.Package p : mPackages.values()) {
if (PackageDexOptimizer.canOptimizePackage(p)) {
pkgs.add(p.packageName);
}
}
}
return pkgs;
}
private int performDexOptInternalWithDependenciesLI(PackageParser.Package p,
DexoptOptions options) {
// Select the dex optimizer based on the force parameter.
// Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
// allocate an object here.
PackageDexOptimizer pdo = options.isForce()
? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
: mPackageDexOptimizer;
// Dexopt all dependencies first. Note: we ignore the return value and march on
// on errors.
// Note that we are going to call performDexOpt on those libraries as many times as
// they are referenced in packages. When we do a batch of performDexOpt (for example
// at boot, or background job), the passed 'targetCompilerFilter' stays the same,
// and the first package that uses the library will dexopt it. The
// others will see that the compiled code for the library is up to date.
Collection deps = findSharedNonSystemLibraries(p);
final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
if (!deps.isEmpty()) {
DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
options.getCompilerFilter(), options.getSplitName(),
options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
for (PackageParser.Package depPackage : deps) {
// TODO: Analyze and investigate if we (should) profile libraries.
pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets,
getOrCreateCompilerPackageStats(depPackage),
mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions);
}
}
return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets,
getOrCreateCompilerPackageStats(p),
mDexManager.getPackageUseInfoOrDefault(p.packageName), options);
}
/**
* Reconcile the information we have about the secondary dex files belonging to
* {@code packagName} and the actual dex files. For all dex files that were
* deleted, update the internal records and delete the generated oat files.
*/
@Override
public void reconcileSecondaryDexFiles(String packageName) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return;
} else if (isInstantApp(packageName, UserHandle.getCallingUserId())) {
return;
}
mDexManager.reconcileSecondaryDexFiles(packageName);
}
// TODO(calin): this is only needed for BackgroundDexOptService. Find a cleaner way to inject
// a reference there.
/*package*/ DexManager getDexManager() {
return mDexManager;
}
/**
* Execute the background dexopt job immediately.
*/
@Override
public boolean runBackgroundDexoptJob() {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return false;
}
return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext);
}
List findSharedNonSystemLibraries(PackageParser.Package p) {
if (p.usesLibraries != null || p.usesOptionalLibraries != null
|| p.usesStaticLibraries != null) {
ArrayList retValue = new ArrayList<>();
Set collectedNames = new HashSet<>();
findSharedNonSystemLibrariesRecursive(p, retValue, collectedNames);
retValue.remove(p);
return retValue;
} else {
return Collections.emptyList();
}
}
private void findSharedNonSystemLibrariesRecursive(PackageParser.Package p,
ArrayList