* Internally there are two important locks: *
* Because this class is very central to the platform's security; please run all * CTS and unit tests whenever making modifications: * *
* $ runtest -c android.content.pm.PackageManagerTests frameworks-core * $ cts-tradefed run commandAndExit cts -m CtsAppSecurityHostTestCases *
If flag value is negative, the default value will be assigned. * * Flag type: {@code long} * Namespace: NAMESPACE_ROLLBACK */ private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS = "enable_rollback_timeout"; /** * The default duration to wait for rollback to be enabled in * milliseconds. */ private static final long DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS = 10 * 1000; /** * The default response for package verification timeout. * * This can be either PackageManager.VERIFICATION_ALLOW or * PackageManager.VERIFICATION_REJECT. */ private static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW; public static final String PLATFORM_PACKAGE_NAME = "android"; private static final String KILL_APP_REASON_GIDS_CHANGED = "permission grant or revoke changed gids"; private static final String KILL_APP_REASON_PERMISSIONS_REVOKED = "permissions revoked"; private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; private static final String PACKAGE_SCHEME = "package"; private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay"; private static final String PRODUCT_OVERLAY_DIR = "/product/overlay"; private static final String PRODUCT_SERVICES_OVERLAY_DIR = "/product_services/overlay"; private static final String ODM_OVERLAY_DIR = "/odm/overlay"; private static final String OEM_OVERLAY_DIR = "/oem/overlay"; /** Canonical intent used to identify what counts as a "web browser" app */ private static final Intent sBrowserIntent; static { sBrowserIntent = new Intent(); sBrowserIntent.setAction(Intent.ACTION_VIEW); sBrowserIntent.addCategory(Intent.CATEGORY_BROWSABLE); sBrowserIntent.setData(Uri.parse("http:")); sBrowserIntent.addFlags(Intent.FLAG_IGNORE_EPHEMERAL); } // Compilation reasons. public static final int REASON_UNKNOWN = -1; public static final int REASON_FIRST_BOOT = 0; public static final int REASON_BOOT = 1; public static final int REASON_INSTALL = 2; public static final int REASON_BACKGROUND_DEXOPT = 3; public static final int REASON_AB_OTA = 4; public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 5; public static final int REASON_SHARED = 6; public static final int REASON_LAST = REASON_SHARED; /** * Whether the package parser cache is enabled. */ private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true; /** * Permissions required in order to receive instant application lifecycle broadcasts. */ private static final String[] INSTANT_APP_BROADCAST_PERMISSION = new String[] { android.Manifest.permission.ACCESS_INSTANT_APPS }; final ServiceThread mHandlerThread; final PackageHandler mHandler; private final ProcessLoggingHandler mProcessLoggingHandler; final int mSdkVersion = Build.VERSION.SDK_INT; final Context mContext; final boolean mFactoryTest; final boolean mOnlyCore; final DisplayMetrics mMetrics; final int mDefParseFlags; final String[] mSeparateProcesses; final boolean mIsUpgrade; final boolean mIsPreNUpgrade; final boolean mIsPreNMR1Upgrade; final boolean mIsPreQUpgrade; @GuardedBy("mPackages") private boolean mDexOptDialogShown; // Used for privilege escalation. MUST NOT BE CALLED WITH mPackages // LOCK HELD. Can be called with mInstallLock held. @GuardedBy("mInstallLock") final Installer mInstaller; /** Directory where installed applications are stored */ private static final File sAppInstallDir = new File(Environment.getDataDirectory(), "app"); /** Directory where installed application's 32-bit native libraries are copied. */ private static final File sAppLib32InstallDir = new File(Environment.getDataDirectory(), "app-lib"); // ---------------------------------------------------------------- // Lock for state used when installing and doing other long running // operations. Methods that must be called with this lock held have // the suffix "LI". final Object mInstallLock = new Object(); // ---------------------------------------------------------------- // Keys are String (package name), values are Package. This also serves // as the lock for the global state. Methods that must be called with // this lock held have the prefix "LP". @GuardedBy("mPackages") final ArrayMap mPackages = new ArrayMap<>(); // Keys are isolated uids and values are the uid of the application // that created the isolated proccess. @GuardedBy("mPackages") final SparseIntArray mIsolatedOwners = new SparseIntArray(); /** * Tracks new system packages [received in an OTA] that we expect to * find updated user-installed versions. Keys are package name, values * are package location. */ final private ArrayMap mExpectingBetter = new ArrayMap<>(); /** * Tracks existing system packages prior to receiving an OTA. Keys are package name. */ final private ArraySet mExistingSystemPackages = new ArraySet<>(); /** * Whether or not system app permissions should be promoted from install to runtime. */ boolean mPromoteSystemApps; @GuardedBy("mPackages") final Settings mSettings; /** * Set of package names that are currently "frozen", which means active * surgery is being done on the code/data for that package. The platform * will refuse to launch frozen packages to avoid race conditions. * * @see PackageFreezer */ @GuardedBy("mPackages") final ArraySet mFrozenPackages = new ArraySet<>(); final ProtectedPackages mProtectedPackages; @GuardedBy("mLoadedVolumes") final ArraySet mLoadedVolumes = new ArraySet<>(); boolean mFirstBoot; PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy; @GuardedBy("mAvailableFeatures") final ArrayMap mAvailableFeatures; private final InstantAppRegistry mInstantAppRegistry; @GuardedBy("mPackages") int mChangedPackagesSequenceNumber; /** * List of changed [installed, removed or updated] packages. * mapping from user id -> sequence number -> package name */ @GuardedBy("mPackages") final SparseArray> mChangedPackages = new SparseArray<>(); /** * The sequence number of the last change to a package. * mapping from user id -> package name -> sequence number */ @GuardedBy("mPackages") final SparseArray> mChangedPackagesSequenceNumbers = new SparseArray<>(); @GuardedBy("mPackages") final private ArraySet mPackageListObservers = new ArraySet<>(); @GuardedBy("mPackages") private final SparseIntArray mDefaultPermissionsGrantedUsers = new SparseIntArray(); private final ModuleInfoProvider mModuleInfoProvider; private final ApexManager mApexManager; class PackageParserCallback implements PackageParser.Callback { @Override public final boolean hasFeature(String feature) { return PackageManagerService.this.hasSystemFeature(feature, 0); } final List getStaticOverlayPackages( Collection allPackages, String targetPackageName) { if ("android".equals(targetPackageName)) { // Static RROs targeting to "android", ie framework-res.apk, are already applied by // native AssetManager. return null; } List overlayPackages = null; for (PackageParser.Package p : allPackages) { if (targetPackageName.equals(p.mOverlayTarget) && p.mOverlayIsStatic) { if (overlayPackages == null) { overlayPackages = new ArrayList<>(); } overlayPackages.add(p); } } if (overlayPackages != null) { Comparator cmp = Comparator.comparingInt(p -> p.mOverlayPriority); overlayPackages.sort(cmp); } return overlayPackages; } final String[] getStaticOverlayPaths(List overlayPackages, String targetPath) { if (overlayPackages == null || overlayPackages.isEmpty()) { return null; } List overlayPathList = null; for (PackageParser.Package overlayPackage : overlayPackages) { if (targetPath == null) { if (overlayPathList == null) { overlayPathList = new ArrayList<>(); } overlayPathList.add(overlayPackage.baseCodePath); continue; } try { // Creates idmaps for system to parse correctly the Android manifest of the // target package. // // OverlayManagerService will update each of them with a correct gid from its // target package app id. mInstaller.idmap(targetPath, overlayPackage.baseCodePath, UserHandle.getSharedAppGid( UserHandle.getUserGid(UserHandle.USER_SYSTEM))); if (overlayPathList == null) { overlayPathList = new ArrayList<>(); } overlayPathList.add(overlayPackage.baseCodePath); } catch (InstallerException e) { Slog.e(TAG, "Failed to generate idmap for " + targetPath + " and " + overlayPackage.baseCodePath); } } return overlayPathList == null ? null : overlayPathList.toArray(new String[0]); } String[] getStaticOverlayPaths(String targetPackageName, String targetPath) { List overlayPackages; synchronized (mInstallLock) { synchronized (mPackages) { overlayPackages = getStaticOverlayPackages( mPackages.values(), targetPackageName); } // It is safe to keep overlayPackages without holding mPackages because static overlay // packages can't be uninstalled or disabled. return getStaticOverlayPaths(overlayPackages, targetPath); } } @Override public final String[] getOverlayApks(String targetPackageName) { return getStaticOverlayPaths(targetPackageName, null); } @Override public final String[] getOverlayPaths(String targetPackageName, String targetPath) { return getStaticOverlayPaths(targetPackageName, targetPath); } } class ParallelPackageParserCallback extends PackageParserCallback { List mOverlayPackages = null; void findStaticOverlayPackages() { synchronized (mPackages) { for (PackageParser.Package p : mPackages.values()) { if (p.mOverlayIsStatic) { if (mOverlayPackages == null) { mOverlayPackages = new ArrayList<>(); } mOverlayPackages.add(p); } } } } @Override synchronized String[] getStaticOverlayPaths(String targetPackageName, String targetPath) { // We can trust mOverlayPackages without holding mPackages because package uninstall // can't happen while running parallel parsing. // And we can call mInstaller inside getStaticOverlayPaths without holding mInstallLock // because mInstallLock is held before running parallel parsing. // Moreover holding mPackages or mInstallLock on each parsing thread causes dead-lock. return mOverlayPackages == null ? null : getStaticOverlayPaths( getStaticOverlayPackages(mOverlayPackages, targetPackageName), targetPath); } } final PackageParser.Callback mPackageParserCallback = new PackageParserCallback(); final ParallelPackageParserCallback mParallelPackageParserCallback = new ParallelPackageParserCallback(); // Currently known shared libraries. final ArrayMap> mSharedLibraries = new ArrayMap<>(); final ArrayMap> mStaticLibsByDeclaringPackage = new ArrayMap<>(); // Mapping from instrumentation class names to info about them. final ArrayMap mInstrumentation = new ArrayMap<>(); // Packages whose data we have transfered into another package, thus // should no longer exist. final ArraySet mTransferedPackages = new ArraySet<>(); // Broadcast actions that are only available to the system. @GuardedBy("mProtectedBroadcasts") final ArraySet mProtectedBroadcasts = new ArraySet<>(); /** List of packages waiting for verification. */ final SparseArray mPendingVerification = new SparseArray<>(); /** List of packages waiting for rollback to be enabled. */ final SparseArray mPendingEnableRollback = new SparseArray<>(); final PackageInstallerService mInstallerService; final ArtManagerService mArtManagerService; private final PackageDexOptimizer mPackageDexOptimizer; // DexManager handles the usage of dex files (e.g. secondary files, whether or not a package // is used by other apps). private final DexManager mDexManager; private final ViewCompiler mViewCompiler; private AtomicInteger mNextMoveId = new AtomicInteger(); private final MoveCallbacks mMoveCallbacks; private final OnPermissionChangeListeners mOnPermissionChangeListeners; // Cache of users who need badging. private final SparseBooleanArray mUserNeedsBadging = new SparseBooleanArray(); /** Token for keys in mPendingVerification. */ private int mPendingVerificationToken = 0; /** Token for keys in mPendingEnableRollback. */ private int mPendingEnableRollbackToken = 0; volatile boolean mSystemReady; volatile boolean mSafeMode; volatile boolean mHasSystemUidErrors; private volatile SparseBooleanArray mWebInstantAppsDisabled = new SparseBooleanArray(); ApplicationInfo mAndroidApplication; final ActivityInfo mResolveActivity = new ActivityInfo(); final ResolveInfo mResolveInfo = new ResolveInfo(); ComponentName mResolveComponentName; PackageParser.Package mPlatformPackage; ComponentName mCustomResolverComponentName; boolean mResolverReplaced = false; private final @Nullable ComponentName mIntentFilterVerifierComponent; private final @Nullable IntentFilterVerifier mIntentFilterVerifier; private int mIntentFilterVerificationToken = 0; /** The service connection to the ephemeral resolver */ final InstantAppResolverConnection mInstantAppResolverConnection; /** Component used to show resolver settings for Instant Apps */ final ComponentName mInstantAppResolverSettingsComponent; /** Activity used to install instant applications */ ActivityInfo mInstantAppInstallerActivity; final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo(); private final Map> mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>()); final SparseArray mIntentFilterVerificationStates = new SparseArray<>(); // TODO remove this and go through mPermissonManager directly final DefaultPermissionGrantPolicy mDefaultPermissionPolicy; private final PermissionManagerServiceInternal mPermissionManager; private final ComponentResolver mComponentResolver; // List of packages names to keep cached, even if they are uninstalled for all users private List mKeepUninstalledPackages; private UserManagerInternal mUserManagerInternal; private ActivityManagerInternal mActivityManagerInternal; private ActivityTaskManagerInternal mActivityTaskManagerInternal; private StorageManagerInternal mStorageManagerInternal; private DeviceIdleController.LocalService mDeviceIdleController; private File mCacheDir; private Future> mPrepareAppDataFuture; private static class IFVerificationParams { PackageParser.Package pkg; boolean replacing; int userId; int verifierUid; public IFVerificationParams(PackageParser.Package _pkg, boolean _replacing, int _userId, int _verifierUid) { pkg = _pkg; replacing = _replacing; userId = _userId; verifierUid = _verifierUid; } } private interface IntentFilterVerifier { boolean addOneIntentFilterVerification(int verifierId, int userId, int verificationId, T filter, String packageName); void startVerifications(int userId); void receiveVerificationResponse(int verificationId); } @GuardedBy("mPackages") private CheckPermissionDelegate mCheckPermissionDelegate; @GuardedBy("mPackages") private PackageManagerInternal.DefaultBrowserProvider mDefaultBrowserProvider; @GuardedBy("mPackages") private PackageManagerInternal.DefaultDialerProvider mDefaultDialerProvider; @GuardedBy("mPackages") private PackageManagerInternal.DefaultHomeProvider mDefaultHomeProvider; private class IntentVerifierProxy implements IntentFilterVerifier { private Context mContext; private ComponentName mIntentFilterVerifierComponent; private ArrayList mCurrentIntentFilterVerifications = new ArrayList<>(); public IntentVerifierProxy(Context context, ComponentName verifierComponent) { mContext = context; mIntentFilterVerifierComponent = verifierComponent; } private String getDefaultScheme() { return IntentFilter.SCHEME_HTTPS; } @Override public void startVerifications(int userId) { // Launch verifications requests int count = mCurrentIntentFilterVerifications.size(); for (int n=0; n filters = ivs.getFilters(); final int filterCount = filters.size(); ArraySet domainsSet = new ArraySet<>(); for (int m=0; m filters = ivs.getFilters(); final int count = filters.size(); if (DEBUG_DOMAIN_VERIFICATION) { Slog.i(TAG, "Received verification response " + verificationId + " for " + count + " filters, verified=" + verified); } for (int n=0; n components within that package> final SparseArray>> mUidMap; public PendingPackageBroadcasts() { mUidMap = new SparseArray<>(2); } public ArrayList get(int userId, String packageName) { ArrayMap> packages = getOrAllocate(userId); return packages.get(packageName); } public void put(int userId, String packageName, ArrayList components) { ArrayMap> packages = getOrAllocate(userId); packages.put(packageName, components); } public void remove(int userId, String packageName) { ArrayMap> packages = mUidMap.get(userId); if (packages != null) { packages.remove(packageName); } } public void remove(int userId) { mUidMap.remove(userId); } public int userIdCount() { return mUidMap.size(); } public int userIdAt(int n) { return mUidMap.keyAt(n); } public ArrayMap> packagesForUserId(int userId) { return mUidMap.get(userId); } public int size() { // total number of pending broadcast entries across all userIds int num = 0; for (int i = 0; i< mUidMap.size(); i++) { num += mUidMap.valueAt(i).size(); } return num; } public void clear() { mUidMap.clear(); } private ArrayMap> getOrAllocate(int userId) { ArrayMap> map = mUidMap.get(userId); if (map == null) { map = new ArrayMap<>(); mUidMap.put(userId, map); } return map; } } final PendingPackageBroadcasts mPendingBroadcasts = new PendingPackageBroadcasts(); static final int SEND_PENDING_BROADCAST = 1; static final int INIT_COPY = 5; static final int POST_INSTALL = 9; static final int WRITE_SETTINGS = 13; static final int WRITE_PACKAGE_RESTRICTIONS = 14; static final int PACKAGE_VERIFIED = 15; static final int CHECK_PENDING_VERIFICATION = 16; static final int START_INTENT_FILTER_VERIFICATIONS = 17; static final int INTENT_FILTER_VERIFIED = 18; static final int WRITE_PACKAGE_LIST = 19; static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20; static final int ENABLE_ROLLBACK_STATUS = 21; static final int ENABLE_ROLLBACK_TIMEOUT = 22; static final int DEFERRED_NO_KILL_POST_DELETE = 23; static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24; static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000; static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500; static final int WRITE_SETTINGS_DELAY = 10*1000; // 10 seconds private static final long BROADCAST_DELAY_DURING_STARTUP = 10 * 1000L; // 10 seconds (in millis) private static final long BROADCAST_DELAY = 1 * 1000L; // 1 second (in millis) // When the service constructor finished plus a delay (used for broadcast delay computation) private long mServiceStartWithDelay; private static final long DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD = 2 * 60 * 60 * 1000L; /* two hours */ static UserManagerService sUserManager; // Stores a list of users whose package restrictions file needs to be updated private ArraySet mDirtyUsers = new ArraySet<>(); // Recordkeeping of restore-after-install operations that are currently in flight // between the Package Manager and the Backup Manager static class PostInstallData { public final InstallArgs args; public final PackageInstalledInfo res; public final Runnable mPostInstallRunnable; PostInstallData(InstallArgs _a, PackageInstalledInfo _r, Runnable postInstallRunnable) { args = _a; res = _r; mPostInstallRunnable = postInstallRunnable; } } final SparseArray mRunningInstalls = new SparseArray<>(); int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows // XML tags for backup/restore of various bits of state private static final String TAG_PREFERRED_BACKUP = "pa"; private static final String TAG_DEFAULT_APPS = "da"; private static final String TAG_INTENT_FILTER_VERIFICATION = "iv"; private static final String TAG_PERMISSION_BACKUP = "perm-grant-backup"; private static final String TAG_ALL_GRANTS = "rt-grants"; private static final String TAG_GRANT = "grant"; private static final String ATTR_PACKAGE_NAME = "pkg"; private static final String TAG_PERMISSION = "perm"; private static final String ATTR_PERMISSION_NAME = "name"; private static final String ATTR_IS_GRANTED = "g"; private static final String ATTR_USER_SET = "set"; private static final String ATTR_USER_FIXED = "fixed"; private static final String ATTR_REVOKE_ON_UPGRADE = "rou"; // System/policy permission grants are not backed up private static final int SYSTEM_RUNTIME_GRANT_MASK = FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED | FLAG_PERMISSION_GRANTED_BY_DEFAULT; // And we back up these user-adjusted states private static final int USER_RUNTIME_GRANT_MASK = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED | FLAG_PERMISSION_REVOKE_ON_UPGRADE; final @Nullable String mRequiredVerifierPackage; final @NonNull String mRequiredInstallerPackage; final @NonNull String mRequiredUninstallerPackage; final @NonNull String mRequiredPermissionControllerPackage; final @Nullable String mSetupWizardPackage; final @Nullable String mStorageManagerPackage; final @Nullable String mSystemTextClassifierPackage; final @Nullable String mWellbeingPackage; final @Nullable String mDocumenterPackage; final @Nullable String mConfiguratorPackage; final @Nullable String mAppPredictionServicePackage; final @Nullable String mIncidentReportApproverPackage; final @NonNull String mServicesSystemSharedLibraryPackageName; final @NonNull String mSharedSystemSharedLibraryPackageName; private final PackageUsage mPackageUsage = new PackageUsage(); private final CompilerStats mCompilerStats = new CompilerStats(); class PackageHandler extends Handler { PackageHandler(Looper looper) { super(looper); } public void handleMessage(Message msg) { try { doHandleMessage(msg); } finally { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } } void doHandleMessage(Message msg) { switch (msg.what) { case INIT_COPY: { HandlerParams params = (HandlerParams) msg.obj; if (params != null) { if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params); Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", System.identityHashCode(params)); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy"); params.startCopy(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } break; } case SEND_PENDING_BROADCAST: { String packages[]; ArrayList components[]; int size = 0; int uids[]; Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mPackages) { size = mPendingBroadcasts.size(); if (size <= 0) { // Nothing to be done. Just return return; } packages = new String[size]; components = new ArrayList[size]; uids = new int[size]; int i = 0; // filling out the above arrays for (int n = 0; n < mPendingBroadcasts.userIdCount(); n++) { int packageUserId = mPendingBroadcasts.userIdAt(n); Iterator>> it = mPendingBroadcasts.packagesForUserId(packageUserId) .entrySet().iterator(); while (it.hasNext() && i < size) { Map.Entry> ent = it.next(); packages[i] = ent.getKey(); components[i] = ent.getValue(); PackageSetting ps = mSettings.mPackages.get(ent.getKey()); uids[i] = (ps != null) ? UserHandle.getUid(packageUserId, ps.appId) : -1; i++; } } size = i; mPendingBroadcasts.clear(); } // Send broadcasts for (int i = 0; i < size; i++) { sendPackageChangedBroadcast(packages[i], true, components[i], uids[i]); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); break; } case POST_INSTALL: { if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1); PostInstallData data = mRunningInstalls.get(msg.arg1); final boolean didRestore = (msg.arg2 != 0); mRunningInstalls.delete(msg.arg1); if (data != null && data.mPostInstallRunnable != null) { data.mPostInstallRunnable.run(); } else if (data != null) { InstallArgs args = data.args; PackageInstalledInfo parentRes = data.res; final boolean grantPermissions = (args.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0; final boolean killApp = (args.installFlags & PackageManager.INSTALL_DONT_KILL_APP) == 0; final boolean virtualPreload = ((args.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); final String[] grantedPermissions = args.installGrantPermissions; final List whitelistedRestrictedPermissions = ((args.installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0 && parentRes.pkg != null) ? parentRes.pkg.requestedPermissions : args.whitelistedRestrictedPermissions; // Handle the parent package handlePackagePostInstall(parentRes, grantPermissions, killApp, virtualPreload, grantedPermissions, whitelistedRestrictedPermissions, didRestore, args.installerPackageName, args.observer); // Handle the child packages final int childCount = (parentRes.addedChildPackages != null) ? parentRes.addedChildPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i); handlePackagePostInstall(childRes, grantPermissions, killApp, virtualPreload, grantedPermissions, whitelistedRestrictedPermissions, false /*didRestore*/, args.installerPackageName, args.observer); } // Log tracing if needed if (args.traceMethod != null) { Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.traceMethod, args.traceCookie); } } else if (DEBUG_INSTALL) { // No post-install when we run restore from installExistingPackageForUser Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1); } Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1); } break; case DEFERRED_NO_KILL_POST_DELETE: { synchronized (mInstallLock) { InstallArgs args = (InstallArgs) msg.obj; if (args != null) { args.doPostDeleteLI(true); } } } break; case DEFERRED_NO_KILL_INSTALL_OBSERVER: { String packageName = (String) msg.obj; if (packageName != null) { notifyInstallObserver(packageName); } } break; case WRITE_SETTINGS: { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mPackages) { removeMessages(WRITE_SETTINGS); removeMessages(WRITE_PACKAGE_RESTRICTIONS); mSettings.writeLPr(); mDirtyUsers.clear(); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } break; case WRITE_PACKAGE_RESTRICTIONS: { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mPackages) { removeMessages(WRITE_PACKAGE_RESTRICTIONS); for (int userId : mDirtyUsers) { mSettings.writePackageRestrictionsLPr(userId); } mDirtyUsers.clear(); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } break; case WRITE_PACKAGE_LIST: { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mPackages) { removeMessages(WRITE_PACKAGE_LIST); mSettings.writePackageListLPr(msg.arg1); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } break; case CHECK_PENDING_VERIFICATION: { final int verificationId = msg.arg1; final PackageVerificationState state = mPendingVerification.get(verificationId); if ((state != null) && !state.timeoutExtended()) { final InstallParams params = state.getInstallParams(); final InstallArgs args = params.mArgs; final Uri originUri = Uri.fromFile(args.origin.resolvedFile); Slog.i(TAG, "Verification timed out for " + originUri); mPendingVerification.remove(verificationId); final UserHandle user = args.getUser(); if (getDefaultVerificationResponse(user) == PackageManager.VERIFICATION_ALLOW) { Slog.i(TAG, "Continuing with installation of " + originUri); state.setVerifierResponse(Binder.getCallingUid(), PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT); broadcastPackageVerified(verificationId, originUri, PackageManager.VERIFICATION_ALLOW, user); } else { broadcastPackageVerified(verificationId, originUri, PackageManager.VERIFICATION_REJECT, user); params.setReturnCode( PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE); } Trace.asyncTraceEnd( TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId); params.handleVerificationFinished(); } break; } case PACKAGE_VERIFIED: { final int verificationId = msg.arg1; final PackageVerificationState state = mPendingVerification.get(verificationId); if (state == null) { Slog.w(TAG, "Invalid verification token " + verificationId + " received"); break; } final PackageVerificationResponse response = (PackageVerificationResponse) msg.obj; state.setVerifierResponse(response.callerUid, response.code); if (state.isVerificationComplete()) { mPendingVerification.remove(verificationId); final InstallParams params = state.getInstallParams(); final InstallArgs args = params.mArgs; final Uri originUri = Uri.fromFile(args.origin.resolvedFile); if (state.isInstallAllowed()) { broadcastPackageVerified(verificationId, originUri, response.code, args.getUser()); } else { params.setReturnCode( PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE); } Trace.asyncTraceEnd( TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId); params.handleVerificationFinished(); } break; } case START_INTENT_FILTER_VERIFICATIONS: { IFVerificationParams params = (IFVerificationParams) msg.obj; verifyIntentFiltersIfNeeded(params.userId, params.verifierUid, params.replacing, params.pkg); break; } case INTENT_FILTER_VERIFIED: { final int verificationId = msg.arg1; final IntentFilterVerificationState state = mIntentFilterVerificationStates.get( verificationId); if (state == null) { Slog.w(TAG, "Invalid IntentFilter verification token " + verificationId + " received"); break; } final int userId = state.getUserId(); if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Processing IntentFilter verification with token:" + verificationId + " and userId:" + userId); final IntentFilterVerificationResponse response = (IntentFilterVerificationResponse) msg.obj; state.setVerifierResponse(response.callerUid, response.code); if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "IntentFilter verification with token:" + verificationId + " and userId:" + userId + " is settings verifier response with response code:" + response.code); if (response.code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) { if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Domains failing verification: " + response.getFailedDomainsString()); } if (state.isVerificationComplete()) { mIntentFilterVerifier.receiveVerificationResponse(verificationId); } else { if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "IntentFilter verification with token:" + verificationId + " was not said to be complete"); } break; } case INSTANT_APP_RESOLUTION_PHASE_TWO: { InstantAppResolver.doInstantAppResolutionPhaseTwo(mContext, mInstantAppResolverConnection, (InstantAppRequest) msg.obj, mInstantAppInstallerActivity, mHandler); break; } case ENABLE_ROLLBACK_STATUS: { final int enableRollbackToken = msg.arg1; final int enableRollbackCode = msg.arg2; InstallParams params = mPendingEnableRollback.get(enableRollbackToken); if (params == null) { Slog.w(TAG, "Invalid rollback enabled token " + enableRollbackToken + " received"); break; } mPendingEnableRollback.remove(enableRollbackToken); if (enableRollbackCode != PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED) { final InstallArgs args = params.mArgs; final Uri originUri = Uri.fromFile(args.origin.resolvedFile); Slog.w(TAG, "Failed to enable rollback for " + originUri); Slog.w(TAG, "Continuing with installation of " + originUri); } Trace.asyncTraceEnd( TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken); params.handleRollbackEnabled(); break; } case ENABLE_ROLLBACK_TIMEOUT: { final int enableRollbackToken = msg.arg1; final InstallParams params = mPendingEnableRollback.get(enableRollbackToken); if (params != null) { final InstallArgs args = params.mArgs; final Uri originUri = Uri.fromFile(args.origin.resolvedFile); Slog.w(TAG, "Enable rollback timed out for " + originUri); mPendingEnableRollback.remove(enableRollbackToken); Slog.w(TAG, "Continuing with installation of " + originUri); Trace.asyncTraceEnd( TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken); params.handleRollbackEnabled(); Intent rollbackTimeoutIntent = new Intent( Intent.ACTION_CANCEL_ENABLE_ROLLBACK); rollbackTimeoutIntent.putExtra( PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, enableRollbackToken); rollbackTimeoutIntent.addFlags( Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcastAsUser(rollbackTimeoutIntent, UserHandle.SYSTEM, android.Manifest.permission.PACKAGE_ROLLBACK_AGENT); } break; } } } } private PermissionCallback mPermissionCallback = new PermissionCallback() { @Override public void onGidsChanged(int appId, int userId) { mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED)); } @Override public void onPermissionGranted(int uid, int userId) { mOnPermissionChangeListeners.onPermissionsChanged(uid); // Not critical; if this is lost, the application has to request again. synchronized (mPackages) { mSettings.writeRuntimePermissionsForUserLPr(userId, false); } } @Override public void onInstallPermissionGranted() { synchronized (mPackages) { scheduleWriteSettingsLocked(); } } @Override public void onPermissionRevoked(int uid, int userId) { mOnPermissionChangeListeners.onPermissionsChanged(uid); synchronized (mPackages) { // Critical; after this call the application should never have the permission mSettings.writeRuntimePermissionsForUserLPr(userId, true); } final int appId = UserHandle.getAppId(uid); killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED); } @Override public void onInstallPermissionRevoked() { synchronized (mPackages) { scheduleWriteSettingsLocked(); } } @Override public void onPermissionUpdated(int[] updatedUserIds, boolean sync) { synchronized (mPackages) { for (int userId : updatedUserIds) { mSettings.writeRuntimePermissionsForUserLPr(userId, sync); } } } @Override public void onInstallPermissionUpdated() { synchronized (mPackages) { scheduleWriteSettingsLocked(); } } @Override public void onPermissionRemoved() { synchronized (mPackages) { mSettings.writeLPr(); } } }; private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions, boolean killApp, boolean virtualPreload, String[] grantedPermissions, List whitelistedRestrictedPermissions, boolean launchedForRestore, String installerPackage, IPackageInstallObserver2 installObserver) { final boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED; final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null; if (succeeded) { // Send the removed broadcasts if (res.removedInfo != null) { res.removedInfo.sendPackageRemovedBroadcasts(killApp); } // Whitelist any restricted permissions first as some may be runtime // that the installer requested to be granted at install time. if (whitelistedRestrictedPermissions != null && !whitelistedRestrictedPermissions.isEmpty()) { mPermissionManager.setWhitelistedRestrictedPermissions( res.pkg, res.newUsers, whitelistedRestrictedPermissions, Process.myUid(), PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER, mPermissionCallback); } // Now that we successfully installed the package, grant runtime // permissions if requested before broadcasting the install. Also // for legacy apps in permission review mode we clear the permission // review flag which is used to emulate runtime permissions for // legacy apps. if (grantPermissions) { final int callingUid = Binder.getCallingUid(); mPermissionManager.grantRequestedRuntimePermissions( res.pkg, res.newUsers, grantedPermissions, callingUid, mPermissionCallback); } final String installerPackageName = res.installerPackageName != null ? res.installerPackageName : res.removedInfo != null ? res.removedInfo.installerPackageName : null; // If this is the first time we have child packages for a disabled privileged // app that had no children, we grant requested runtime permissions to the new // children if the parent on the system image had them already granted. if (res.pkg.parentPackage != null) { final int callingUid = Binder.getCallingUid(); mPermissionManager.grantRuntimePermissionsGrantedToDisabledPackage( res.pkg, callingUid, mPermissionCallback); } synchronized (mPackages) { mInstantAppRegistry.onPackageInstalledLPw(res.pkg, res.newUsers); } final String packageName = res.pkg.applicationInfo.packageName; // Determine the set of users who are adding this package for // the first time vs. those who are seeing an update. int[] firstUserIds = EMPTY_INT_ARRAY; int[] firstInstantUserIds = EMPTY_INT_ARRAY; int[] updateUserIds = EMPTY_INT_ARRAY; int[] instantUserIds = EMPTY_INT_ARRAY; final boolean allNewUsers = res.origUsers == null || res.origUsers.length == 0; final PackageSetting ps = (PackageSetting) res.pkg.mExtras; for (int newUser : res.newUsers) { final boolean isInstantApp = ps.getInstantApp(newUser); if (allNewUsers) { if (isInstantApp) { firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser); } else { firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser); } continue; } boolean isNew = true; for (int origUser : res.origUsers) { if (origUser == newUser) { isNew = false; break; } } if (isNew) { if (isInstantApp) { firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser); } else { firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser); } } else { if (isInstantApp) { instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser); } else { updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser); } } } // Send installed broadcasts if the package is not a static shared lib. if (res.pkg.staticSharedLibName == null) { mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(res.pkg.baseCodePath); // Send added for users that see the package for the first time // sendPackageAddedForNewUsers also deals with system apps int appId = UserHandle.getAppId(res.uid); boolean isSystem = res.pkg.applicationInfo.isSystemApp(); sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload, virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds); // Send added for users that don't see the package for the first time Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, res.uid); if (update) { extras.putBoolean(Intent.EXTRA_REPLACING, true); } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, null /*targetPackage*/, null /*finishedReceiver*/, updateUserIds, instantUserIds); if (installerPackageName != null) { sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, installerPackageName, null /*finishedReceiver*/, updateUserIds, instantUserIds); } // if the required verifier is defined, but, is not the installer of record // for the package, it gets notified final boolean notifyVerifier = mRequiredVerifierPackage != null && !mRequiredVerifierPackage.equals(installerPackageName); if (notifyVerifier) { sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, mRequiredVerifierPackage, null /*finishedReceiver*/, updateUserIds, instantUserIds); } // If package installer is defined, notify package installer about new // app installed if (mRequiredInstallerPackage != null) { sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/, mRequiredInstallerPackage, null /*finishedReceiver*/, firstUserIds, instantUserIds); } // Send replaced for users that don't see the package for the first time if (update) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras, 0 /*flags*/, null /*targetPackage*/, null /*finishedReceiver*/, updateUserIds, instantUserIds); if (installerPackageName != null) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras, 0 /*flags*/, installerPackageName, null /*finishedReceiver*/, updateUserIds, instantUserIds); } if (notifyVerifier) { sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras, 0 /*flags*/, mRequiredVerifierPackage, null /*finishedReceiver*/, updateUserIds, instantUserIds); } sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null /*package*/, null /*extras*/, 0 /*flags*/, packageName /*targetPackage*/, null /*finishedReceiver*/, updateUserIds, instantUserIds); } else if (launchedForRestore && !isSystemApp(res.pkg)) { // First-install and we did a restore, so we're responsible for the // first-launch broadcast. if (DEBUG_BACKUP) { Slog.i(TAG, "Post-restore of " + packageName + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds)); } sendFirstLaunchBroadcast(packageName, installerPackage, firstUserIds, firstInstantUserIds); } // Send broadcast package appeared if external for all users if (isExternal(res.pkg)) { if (!update) { final StorageManager storage = mContext.getSystemService(StorageManager.class); VolumeInfo volume = storage.findVolumeByUuid( res.pkg.applicationInfo.storageUuid.toString()); int packageExternalStorageType = getPackageExternalStorageType(volume, isExternal(res.pkg)); // If the package was installed externally, log it. if (packageExternalStorageType != StorageEnums.UNKNOWN) { StatsLog.write(StatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED, packageExternalStorageType, res.pkg.packageName); } } if (DEBUG_INSTALL) { Slog.i(TAG, "upgrading pkg " + res.pkg + " is external"); } final int[] uidArray = new int[]{res.pkg.applicationInfo.uid}; ArrayList pkgList = new ArrayList<>(1); pkgList.add(packageName); sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null); } } // Work that needs to happen on first install within each user if (firstUserIds != null && firstUserIds.length > 0) { for (int userId : firstUserIds) { // If this app is a browser and it's newly-installed for some // users, clear any default-browser state in those users. The // app's nature doesn't depend on the user, so we can just check // its browser nature in any user and generalize. if (packageIsBrowser(packageName, userId)) { // If this browser is restored from user's backup, do not clear // default-browser state for this user synchronized (mPackages) { final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); if (pkgSetting.getInstallReason(userId) != PackageManager.INSTALL_REASON_DEVICE_RESTORE) { setDefaultBrowserAsyncLPw(null, userId); } } } // We may also need to apply pending (restored) runtime permission grants // within these users. mPermissionManager.restoreDelayedRuntimePermissions(packageName, UserHandle.of(userId)); // Persistent preferred activity might have came into effect due to this // install. updateDefaultHomeNotLocked(userId); } } if (allNewUsers && !update) { notifyPackageAdded(packageName, res.uid); } else { notifyPackageChanged(packageName, res.uid); } // Log current value of "unknown sources" setting EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED, getUnknownSourcesSettings()); // Remove the replaced package's older resources safely now InstallArgs args = res.removedInfo != null ? res.removedInfo.args : null; if (args != null) { if (!killApp) { // If we didn't kill the app, defer the deletion of code/resource files, since // they may still be in use by the running application. This mitigates problems // in cases where resources or code is loaded by a new Activity before // ApplicationInfo changes have propagated to all application threads. scheduleDeferredNoKillPostDelete(args); } else { synchronized (mInstallLock) { args.doPostDeleteLI(true); } } } else { // Force a gc to clear up things. Ask for a background one, it's fine to go on // and not block here. VMRuntime.getRuntime().requestConcurrentGC(); } // Notify DexManager that the package was installed for new users. // The updated users should already be indexed and the package code paths // should not change. // Don't notify the manager for ephemeral apps as they are not expected to // survive long enough to benefit of background optimizations. for (int userId : firstUserIds) { PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId); // There's a race currently where some install events may interleave with an uninstall. // This can lead to package info being null (b/36642664). if (info != null) { mDexManager.notifyPackageInstalled(info, userId); } } } final boolean deferInstallObserver = succeeded && update && !killApp; if (deferInstallObserver) { scheduleDeferredNoKillInstallObserver(res, installObserver); } else { notifyInstallObserver(res, installObserver); } } @Override public void notifyPackagesReplacedReceived(String[] packages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); for (String packageName : packages) { PackageSetting setting = mSettings.mPackages.get(packageName); if (setting != null && filterAppAccessLPr(setting, callingUid, callingUserId)) { notifyInstallObserver(packageName); } } } private void notifyInstallObserver(String packageName) { Pair pair = mNoKillInstallObservers.remove(packageName); if (pair != null) { notifyInstallObserver(pair.first, pair.second); } } private void notifyInstallObserver(PackageInstalledInfo info, IPackageInstallObserver2 installObserver) { if (installObserver != null) { try { Bundle extras = extrasForInstallResult(info); installObserver.onPackageInstalled(info.name, info.returnCode, info.returnMsg, extras); } catch (RemoteException e) { Slog.i(TAG, "Observer no longer exists."); } } } private void scheduleDeferredNoKillPostDelete(InstallArgs args) { Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args); mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_POST_DELETE_DELAY_MS); } private void scheduleDeferredNoKillInstallObserver(PackageInstalledInfo info, IPackageInstallObserver2 observer) { String packageName = info.pkg.packageName; mNoKillInstallObservers.put(packageName, Pair.create(info, observer)); Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_INSTALL_OBSERVER, packageName); mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS); } /** * Gets the type of the external storage a package is installed on. * @param packageVolume The storage volume of the package. * @param packageIsExternal true if the package is currently installed on * external/removable/unprotected storage. * @return {@link StorageEnum#TYPE_UNKNOWN} if the package is not stored externally or the * corresponding {@link StorageEnum} storage type value if it is. */ private static int getPackageExternalStorageType(VolumeInfo packageVolume, boolean packageIsExternal) { if (packageVolume != null) { DiskInfo disk = packageVolume.getDisk(); if (disk != null) { if (disk.isSd()) { return StorageEnums.SD_CARD; } if (disk.isUsb()) { return StorageEnums.USB; } if (packageIsExternal) { return StorageEnums.OTHER; } } } return StorageEnums.UNKNOWN; } private StorageEventListener mStorageListener = new StorageEventListener() { @Override public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { if (vol.type == VolumeInfo.TYPE_PRIVATE) { if (vol.state == VolumeInfo.STATE_MOUNTED) { final String volumeUuid = vol.getFsUuid(); // Clean up any users or apps that were removed or recreated // while this volume was missing sUserManager.reconcileUsers(volumeUuid); reconcileApps(volumeUuid); // Clean up any install sessions that expired or were // cancelled while this volume was missing mInstallerService.onPrivateVolumeMounted(volumeUuid); loadPrivatePackages(vol); } else if (vol.state == VolumeInfo.STATE_EJECTING) { unloadPrivatePackages(vol); } } } @Override public void onVolumeForgotten(String fsUuid) { if (TextUtils.isEmpty(fsUuid)) { Slog.e(TAG, "Forgetting internal storage is probably a mistake; ignoring"); return; } // Remove any apps installed on the forgotten volume synchronized (mPackages) { final List packages = mSettings.getVolumePackagesLPr(fsUuid); for (PackageSetting ps : packages) { Slog.d(TAG, "Destroying " + ps.name + " because volume was forgotten"); deletePackageVersioned(new VersionedPackage(ps.name, PackageManager.VERSION_CODE_HIGHEST), new LegacyPackageDeleteObserver(null).getBinder(), UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS); // Try very hard to release any references to this package // so we don't risk the system server being killed due to // open FDs AttributeCache.instance().removePackage(ps.name); } mSettings.onVolumeForgotten(fsUuid); mSettings.writeLPr(); } } }; Bundle extrasForInstallResult(PackageInstalledInfo res) { Bundle extras = null; switch (res.returnCode) { case PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION: { extras = new Bundle(); extras.putString(PackageManager.EXTRA_FAILURE_EXISTING_PERMISSION, res.origPermission); extras.putString(PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE, res.origPackage); break; } case PackageManager.INSTALL_SUCCEEDED: { extras = new Bundle(); extras.putBoolean(Intent.EXTRA_REPLACING, res.removedInfo != null && res.removedInfo.removedPackage != null); break; } } return extras; } void scheduleWriteSettingsLocked() { if (!mHandler.hasMessages(WRITE_SETTINGS)) { mHandler.sendEmptyMessageDelayed(WRITE_SETTINGS, WRITE_SETTINGS_DELAY); } } void scheduleWritePackageListLocked(int userId) { if (!mHandler.hasMessages(WRITE_PACKAGE_LIST)) { Message msg = mHandler.obtainMessage(WRITE_PACKAGE_LIST); msg.arg1 = userId; mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY); } } void scheduleWritePackageRestrictionsLocked(UserHandle user) { final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier(); scheduleWritePackageRestrictionsLocked(userId); } void scheduleWritePackageRestrictionsLocked(int userId) { final int[] userIds = (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds() : new int[]{userId}; for (int nextUserId : userIds) { if (!sUserManager.exists(nextUserId)) return; mDirtyUsers.add(nextUserId); if (!mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { mHandler.sendEmptyMessageDelayed(WRITE_PACKAGE_RESTRICTIONS, WRITE_SETTINGS_DELAY); } } } public static PackageManagerService main(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { // Self-check for initial settings. PackageManagerServiceCompilerMapping.checkProperties(); PackageManagerService m = new PackageManagerService(context, installer, factoryTest, onlyCore); m.enableSystemUserPackages(); ServiceManager.addService("package", m); final PackageManagerNative pmn = m.new PackageManagerNative(); ServiceManager.addService("package_native", pmn); return m; } private void enableSystemUserPackages() { if (!UserManager.isSplitSystemUser()) { return; } // For system user, enable apps based on the following conditions: // - app is whitelisted or belong to one of these groups: // -- system app which has no launcher icons // -- system app which has INTERACT_ACROSS_USERS permission // -- system IME app // - app is not in the blacklist AppsQueryHelper queryHelper = new AppsQueryHelper(this); Set enableApps = new ArraySet<>(); enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_NON_LAUNCHABLE_APPS | AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM | AppsQueryHelper.GET_IMES, /* systemAppsOnly */ true, UserHandle.SYSTEM)); ArraySet wlApps = SystemConfig.getInstance().getSystemUserWhitelistedApps(); enableApps.addAll(wlApps); enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_REQUIRED_FOR_SYSTEM_USER, /* systemAppsOnly */ false, UserHandle.SYSTEM)); ArraySet blApps = SystemConfig.getInstance().getSystemUserBlacklistedApps(); enableApps.removeAll(blApps); Log.i(TAG, "Applications installed for system user: " + enableApps); List allAps = queryHelper.queryApps(0, /* systemAppsOnly */ false, UserHandle.SYSTEM); final int allAppsSize = allAps.size(); synchronized (mPackages) { for (int i = 0; i < allAppsSize; i++) { String pName = allAps.get(i); PackageSetting pkgSetting = mSettings.mPackages.get(pName); // Should not happen, but we shouldn't be failing if it does if (pkgSetting == null) { continue; } boolean install = enableApps.contains(pName); if (pkgSetting.getInstalled(UserHandle.USER_SYSTEM) != install) { Log.i(TAG, (install ? "Installing " : "Uninstalling ") + pName + " for system user"); pkgSetting.setInstalled(install, UserHandle.USER_SYSTEM); } } scheduleWritePackageRestrictionsLocked(UserHandle.USER_SYSTEM); } } private static void getDefaultDisplayMetrics(Context context, DisplayMetrics metrics) { DisplayManager displayManager = (DisplayManager) context.getSystemService( Context.DISPLAY_SERVICE); displayManager.getDisplay(Display.DEFAULT_DISPLAY).getMetrics(metrics); } /** * Requests that files preopted on a secondary system partition be copied to the data partition * if possible. Note that the actual copying of the files is accomplished by init for security * reasons. This simply requests that the copy takes place and awaits confirmation of its * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy. */ private static void requestCopyPreoptedFiles() { final int WAIT_TIME_MS = 100; final String CP_PREOPT_PROPERTY = "sys.cppreopt"; if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) { SystemProperties.set(CP_PREOPT_PROPERTY, "requested"); // We will wait for up to 100 seconds. final long timeStart = SystemClock.uptimeMillis(); final long timeEnd = timeStart + 100 * 1000; long timeNow = timeStart; while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) { try { Thread.sleep(WAIT_TIME_MS); } catch (InterruptedException e) { // Do nothing } timeNow = SystemClock.uptimeMillis(); if (timeNow > timeEnd) { SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out"); Slog.wtf(TAG, "cppreopt did not finish!"); break; } } Slog.i(TAG, "cppreopts took " + (timeNow - timeStart) + " ms"); } } public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "create package manager"); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis()); if (mSdkVersion <= 0) { Slog.w(TAG, "**** ro.build.version.sdk not set!"); } mContext = context; mFactoryTest = factoryTest; mOnlyCore = onlyCore; mMetrics = new DisplayMetrics(); mInstaller = installer; // Create sub-components that provide services / data. Order here is important. synchronized (mInstallLock) { synchronized (mPackages) { // Expose private service for system components to use. LocalServices.addService( PackageManagerInternal.class, new PackageManagerInternalImpl()); sUserManager = new UserManagerService(context, this, new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages); mComponentResolver = new ComponentResolver(sUserManager, LocalServices.getService(PackageManagerInternal.class), mPackages); mPermissionManager = PermissionManagerService.create(context, mPackages /*externalLock*/); mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy(); mSettings = new Settings(Environment.getDataDirectory(), mPermissionManager.getPermissionSettings(), mPackages); } } mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.se", SE_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); String separateProcesses = SystemProperties.get("debug.separate_processes"); if (separateProcesses != null && separateProcesses.length() > 0) { if ("*".equals(separateProcesses)) { mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES; mSeparateProcesses = null; Slog.w(TAG, "Running with debug.separate_processes: * (ALL)"); } else { mDefParseFlags = 0; mSeparateProcesses = separateProcesses.split(","); Slog.w(TAG, "Running with debug.separate_processes: " + separateProcesses); } } else { mDefParseFlags = 0; mSeparateProcesses = null; } mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context, "*dexopt*"); mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock); mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock); mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper()); mViewCompiler = new ViewCompiler(mInstallLock, mInstaller); mOnPermissionChangeListeners = new OnPermissionChangeListeners( FgThread.get().getLooper()); getDefaultDisplayMetrics(context, mMetrics); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "get system config"); SystemConfig systemConfig = SystemConfig.getInstance(); mAvailableFeatures = systemConfig.getAvailableFeatures(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); mProtectedPackages = new ProtectedPackages(mContext); mApexManager = new ApexManager(context); synchronized (mInstallLock) { // writer synchronized (mPackages) { mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); mHandlerThread.start(); mHandler = new PackageHandler(mHandlerThread.getLooper()); mProcessLoggingHandler = new ProcessLoggingHandler(); Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); mInstantAppRegistry = new InstantAppRegistry(this); ArrayMap libConfig = systemConfig.getSharedLibraries(); final int builtInLibCount = libConfig.size(); for (int i = 0; i < builtInLibCount; i++) { String name = libConfig.keyAt(i); SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i); addBuiltInSharedLibraryLocked(entry.filename, name); } // Now that we have added all the libraries, iterate again to add dependency // information IFF their dependencies are added. long undefinedVersion = SharedLibraryInfo.VERSION_UNDEFINED; for (int i = 0; i < builtInLibCount; i++) { String name = libConfig.keyAt(i); SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i); final int dependencyCount = entry.dependencies.length; for (int j = 0; j < dependencyCount; j++) { final SharedLibraryInfo dependency = getSharedLibraryInfoLPr(entry.dependencies[j], undefinedVersion); if (dependency != null) { getSharedLibraryInfoLPr(name, undefinedVersion).addDependency(dependency); } } } SELinuxMMAC.readInstallPolicy(); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "loadFallbacks"); FallbackCategoryProvider.loadFallbacks(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "read user settings"); mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false)); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); // Clean up orphaned packages for which the code path doesn't exist // and they are an update to a system app - caused by bug/32321269 final int packageSettingCount = mSettings.mPackages.size(); for (int i = packageSettingCount - 1; i >= 0; i--) { PackageSetting ps = mSettings.mPackages.valueAt(i); if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists()) && mSettings.getDisabledSystemPkgLPr(ps.name) != null) { mSettings.mPackages.removeAt(i); mSettings.enableSystemPackageLPw(ps.name); } } if (!mOnlyCore && mFirstBoot) { requestCopyPreoptedFiles(); } String customResolverActivityName = Resources.getSystem().getString( R.string.config_customResolverActivity); if (!TextUtils.isEmpty(customResolverActivityName)) { mCustomResolverComponentName = ComponentName.unflattenFromString( customResolverActivityName); } long startTime = SystemClock.uptimeMillis(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, startTime); final String bootClassPath = System.getenv("BOOTCLASSPATH"); final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH"); if (bootClassPath == null) { Slog.w(TAG, "No BOOTCLASSPATH found!"); } if (systemServerClassPath == null) { Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!"); } File frameworkDir = new File(Environment.getRootDirectory(), "framework"); final VersionInfo ver = mSettings.getInternalVersion(); mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint); if (mIsUpgrade) { logCriticalInfo(Log.INFO, "Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT); } // when upgrading from pre-M, promote system app permissions from install to runtime mPromoteSystemApps = mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1; // When upgrading from pre-N, we need to handle package extraction like first boot, // as there is no profiling data available. mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N; mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1; mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q; int preUpgradeSdkVersion = ver.sdkVersion; // save off the names of pre-existing system packages prior to scanning; we don't // want to automatically grant runtime permissions for new system apps if (mPromoteSystemApps) { Iterator pkgSettingIter = mSettings.mPackages.values().iterator(); while (pkgSettingIter.hasNext()) { PackageSetting ps = pkgSettingIter.next(); if (isSystemApp(ps)) { mExistingSystemPackages.add(ps.name); } } } mCacheDir = preparePackageParserCache(); // Set flag to monitor and not change apk file paths when // scanning install directories. int scanFlags = SCAN_BOOTING | SCAN_INITIAL; if (mIsUpgrade || mFirstBoot) { scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE; } // Collect vendor/product/product_services overlay packages. (Do this before scanning // any apps.) // For security and version matching reason, only consider overlay packages if they // reside in the right directory. scanDirTracedLI(new File(VENDOR_OVERLAY_DIR), mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_VENDOR, 0); scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR), mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRODUCT, 0); scanDirTracedLI(new File(PRODUCT_SERVICES_OVERLAY_DIR), mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRODUCT_SERVICES, 0); scanDirTracedLI(new File(ODM_OVERLAY_DIR), mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_ODM, 0); scanDirTracedLI(new File(OEM_OVERLAY_DIR), mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_OEM, 0); mParallelPackageParserCallback.findStaticOverlayPackages(); // Find base frameworks (resource packages without code). scanDirTracedLI(frameworkDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_NO_DEX | SCAN_AS_SYSTEM | SCAN_AS_PRIVILEGED, 0); if (!mPackages.containsKey("android")) { throw new IllegalStateException( "Failed to load frameworks package; check log for warnings"); } // Collect privileged system packages. final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app"); scanDirTracedLI(privilegedAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRIVILEGED, 0); // Collect ordinary system packages. final File systemAppDir = new File(Environment.getRootDirectory(), "app"); scanDirTracedLI(systemAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM, 0); // Collect privileged vendor packages. File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app"); try { privilegedVendorAppDir = privilegedVendorAppDir.getCanonicalFile(); } catch (IOException e) { // failed to look up canonical path, continue with original one } scanDirTracedLI(privilegedVendorAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_VENDOR | SCAN_AS_PRIVILEGED, 0); // Collect ordinary vendor packages. File vendorAppDir = new File(Environment.getVendorDirectory(), "app"); try { vendorAppDir = vendorAppDir.getCanonicalFile(); } catch (IOException e) { // failed to look up canonical path, continue with original one } scanDirTracedLI(vendorAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_VENDOR, 0); // Collect privileged odm packages. /odm is another vendor partition // other than /vendor. File privilegedOdmAppDir = new File(Environment.getOdmDirectory(), "priv-app"); try { privilegedOdmAppDir = privilegedOdmAppDir.getCanonicalFile(); } catch (IOException e) { // failed to look up canonical path, continue with original one } scanDirTracedLI(privilegedOdmAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_VENDOR | SCAN_AS_PRIVILEGED, 0); // Collect ordinary odm packages. /odm is another vendor partition // other than /vendor. File odmAppDir = new File(Environment.getOdmDirectory(), "app"); try { odmAppDir = odmAppDir.getCanonicalFile(); } catch (IOException e) { // failed to look up canonical path, continue with original one } scanDirTracedLI(odmAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_VENDOR, 0); // Collect all OEM packages. final File oemAppDir = new File(Environment.getOemDirectory(), "app"); scanDirTracedLI(oemAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_OEM, 0); // Collected privileged /product packages. File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app"); try { privilegedProductAppDir = privilegedProductAppDir.getCanonicalFile(); } catch (IOException e) { // failed to look up canonical path, continue with original one } scanDirTracedLI(privilegedProductAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRODUCT | SCAN_AS_PRIVILEGED, 0); // Collect ordinary /product packages. File productAppDir = new File(Environment.getProductDirectory(), "app"); try { productAppDir = productAppDir.getCanonicalFile(); } catch (IOException e) { // failed to look up canonical path, continue with original one } scanDirTracedLI(productAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRODUCT, 0); // Collected privileged /product_services packages. File privilegedProductServicesAppDir = new File(Environment.getProductServicesDirectory(), "priv-app"); try { privilegedProductServicesAppDir = privilegedProductServicesAppDir.getCanonicalFile(); } catch (IOException e) { // failed to look up canonical path, continue with original one } scanDirTracedLI(privilegedProductServicesAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRODUCT_SERVICES | SCAN_AS_PRIVILEGED, 0); // Collect ordinary /product_services packages. File productServicesAppDir = new File(Environment.getProductServicesDirectory(), "app"); try { productServicesAppDir = productServicesAppDir.getCanonicalFile(); } catch (IOException e) { // failed to look up canonical path, continue with original one } scanDirTracedLI(productServicesAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRODUCT_SERVICES, 0); // Prune any system packages that no longer exist. final List possiblyDeletedUpdatedSystemApps = new ArrayList<>(); // Stub packages must either be replaced with full versions in the /data // partition or be disabled. final List stubSystemApps = new ArrayList<>(); if (!mOnlyCore) { // do this first before mucking with mPackages for the "expecting better" case final Iterator pkgIterator = mPackages.values().iterator(); while (pkgIterator.hasNext()) { final PackageParser.Package pkg = pkgIterator.next(); if (pkg.isStub) { stubSystemApps.add(pkg.packageName); } } final Iterator psit = mSettings.mPackages.values().iterator(); while (psit.hasNext()) { PackageSetting ps = psit.next(); /* * If this is not a system app, it can't be a * disable system app. */ if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) { continue; } /* * If the package is scanned, it's not erased. */ final PackageParser.Package scannedPkg = mPackages.get(ps.name); if (scannedPkg != null) { /* * If the system app is both scanned and in the * disabled packages list, then it must have been * added via OTA. Remove it from the currently * scanned package so the previously user-installed * application can be scanned. */ if (mSettings.isDisabledSystemPackageLPr(ps.name)) { logCriticalInfo(Log.WARN, "Expecting better updated system app for " + ps.name + "; removing system app. Last known" + " codePath=" + ps.codePathString + ", versionCode=" + ps.versionCode + "; scanned versionCode=" + scannedPkg.getLongVersionCode()); removePackageLI(scannedPkg, true); mExpectingBetter.put(ps.name, ps.codePath); } continue; } if (!mSettings.isDisabledSystemPackageLPr(ps.name)) { psit.remove(); logCriticalInfo(Log.WARN, "System package " + ps.name + " no longer exists; it's data will be wiped"); // Actual deletion of code and data will be handled by later // reconciliation step } else { // we still have a disabled system package, but, it still might have // been removed. check the code path still exists and check there's // still a package. the latter can happen if an OTA keeps the same // code path, but, changes the package name. final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name); if (disabledPs.codePath == null || !disabledPs.codePath.exists() || disabledPs.pkg == null) { possiblyDeletedUpdatedSystemApps.add(ps.name); } else { // We're expecting that the system app should remain disabled, but add // it to expecting better to recover in case the data version cannot // be scanned. mExpectingBetter.put(disabledPs.name, disabledPs.codePath); } } } } //delete tmp files deleteTempPackageFiles(); final int cachedSystemApps = PackageParser.sCachedPackageReadCount.get(); // Remove any shared userIDs that have no associated packages mSettings.pruneSharedUsersLPw(); final long systemScanTime = SystemClock.uptimeMillis() - startTime; final int systemPackagesCount = mPackages.size(); Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime + " ms, packageCount: " + systemPackagesCount + " , timePerPackage: " + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount) + " , cached: " + cachedSystemApps); if (mIsUpgrade && systemPackagesCount > 0) { MetricsLogger.histogram(null, "ota_package_manager_system_app_avg_scan_time", ((int) systemScanTime) / systemPackagesCount); } if (!mOnlyCore) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis()); scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0); // Remove disable package settings for updated system apps that were // removed via an OTA. If the update is no longer present, remove the // app completely. Otherwise, revoke their system privileges. for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) { final String packageName = possiblyDeletedUpdatedSystemApps.get(i); final PackageParser.Package pkg = mPackages.get(packageName); final String msg; // remove from the disabled system list; do this first so any future // scans of this package are performed without this state mSettings.removeDisabledSystemPackageLPw(packageName); if (pkg == null) { // should have found an update, but, we didn't; remove everything msg = "Updated system package " + packageName + " no longer exists; removing its data"; // Actual deletion of code and data will be handled by later // reconciliation step } else { // found an update; revoke system privileges msg = "Updated system package " + packageName + " no longer exists; rescanning package on data"; // NOTE: We don't do anything special if a stub is removed from the // system image. But, if we were [like removing the uncompressed // version from the /data partition], this is where it'd be done. // remove the package from the system and re-scan it without any // special privileges removePackageLI(pkg, true); try { final File codePath = new File(pkg.applicationInfo.getCodePath()); scanPackageTracedLI(codePath, 0, scanFlags, 0, null); } catch (PackageManagerException e) { Slog.e(TAG, "Failed to parse updated, ex-system package: " + e.getMessage()); } } // one final check. if we still have a package setting [ie. it was // previously scanned and known to the system], but, we don't have // a package [ie. there was an error scanning it from the /data // partition], completely remove the package data. final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && mPackages.get(packageName) == null) { removePackageDataLIF(ps, null, null, 0, false); } logCriticalInfo(Log.WARN, msg); } /* * Make sure all system apps that we expected to appear on * the userdata partition actually showed up. If they never * appeared, crawl back and revive the system version. */ for (int i = 0; i < mExpectingBetter.size(); i++) { final String packageName = mExpectingBetter.keyAt(i); if (!mPackages.containsKey(packageName)) { final File scanFile = mExpectingBetter.valueAt(i); logCriticalInfo(Log.WARN, "Expected better " + packageName + " but never showed up; reverting to system"); final @ParseFlags int reparseFlags; final @ScanFlags int rescanFlags; if (FileUtils.contains(privilegedAppDir, scanFile)) { reparseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR; rescanFlags = scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRIVILEGED; } else if (FileUtils.contains(systemAppDir, scanFile)) { reparseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR; rescanFlags = scanFlags | SCAN_AS_SYSTEM; } else if (FileUtils.contains(privilegedVendorAppDir, scanFile) || FileUtils.contains(privilegedOdmAppDir, scanFile)) { reparseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR; rescanFlags = scanFlags | SCAN_AS_SYSTEM | SCAN_AS_VENDOR | SCAN_AS_PRIVILEGED; } else if (FileUtils.contains(vendorAppDir, scanFile) || FileUtils.contains(odmAppDir, scanFile)) { reparseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR; rescanFlags = scanFlags | SCAN_AS_SYSTEM | SCAN_AS_VENDOR; } else if (FileUtils.contains(oemAppDir, scanFile)) { reparseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR; rescanFlags = scanFlags | SCAN_AS_SYSTEM | SCAN_AS_OEM; } else if (FileUtils.contains(privilegedProductAppDir, scanFile)) { reparseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR; rescanFlags = scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRODUCT | SCAN_AS_PRIVILEGED; } else if (FileUtils.contains(productAppDir, scanFile)) { reparseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR; rescanFlags = scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRODUCT; } else if (FileUtils.contains(privilegedProductServicesAppDir, scanFile)) { reparseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR; rescanFlags = scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRODUCT_SERVICES | SCAN_AS_PRIVILEGED; } else if (FileUtils.contains(productServicesAppDir, scanFile)) { reparseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR; rescanFlags = scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRODUCT_SERVICES; } else { Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile); continue; } mSettings.enableSystemPackageLPw(packageName); try { scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null); } catch (PackageManagerException e) { Slog.e(TAG, "Failed to parse original system package: " + e.getMessage()); } } } // Uncompress and install any stubbed system applications. // This must be done last to ensure all stubs are replaced or disabled. installSystemStubPackages(stubSystemApps, scanFlags); final int cachedNonSystemApps = PackageParser.sCachedPackageReadCount.get() - cachedSystemApps; final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime; final int dataPackagesCount = mPackages.size() - systemPackagesCount; Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime + " ms, packageCount: " + dataPackagesCount + " , timePerPackage: " + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount) + " , cached: " + cachedNonSystemApps); if (mIsUpgrade && dataPackagesCount > 0) { MetricsLogger.histogram(null, "ota_package_manager_data_app_avg_scan_time", ((int) dataScanTime) / dataPackagesCount); } } mExpectingBetter.clear(); // Resolve the storage manager. mStorageManagerPackage = getStorageManagerPackageName(); // Resolve protected action filters. Only the setup wizard is allowed to // have a high priority filter for these actions. mSetupWizardPackage = getSetupWizardPackageName(); mComponentResolver.fixProtectedFilterPriorities(); mSystemTextClassifierPackage = getSystemTextClassifierPackageName(); mWellbeingPackage = getWellbeingPackageName(); mDocumenterPackage = getDocumenterPackageName(); mConfiguratorPackage = mContext.getString(R.string.config_deviceConfiguratorPackageName); mAppPredictionServicePackage = getAppPredictionServicePackageName(); mIncidentReportApproverPackage = getIncidentReportApproverPackageName(); // Now that we know all of the shared libraries, update all clients to have // the correct library paths. updateAllSharedLibrariesLocked(null, Collections.unmodifiableMap(mPackages)); for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) { // NOTE: We ignore potential failures here during a system scan (like // the rest of the commands above) because there's precious little we // can do about it. A settings error is reported, though. final List changedAbiCodePath = adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/); if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) { for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) { final String codePathString = changedAbiCodePath.get(i); try { mInstaller.rmdex(codePathString, getDexCodeInstructionSet(getPreferredInstructionSet())); } catch (InstallerException ignored) { } } } // Adjust seInfo to ensure apps which share a sharedUserId are placed in the same // SELinux domain. setting.fixSeInfoLocked(); } // Now that we know all the packages we are keeping, // read and update their last usage times. mPackageUsage.read(mPackages); mCompilerStats.read(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis()); Slog.i(TAG, "Time to scan packages: " + ((SystemClock.uptimeMillis()-startTime)/1000f) + " seconds"); // If the platform SDK has changed since the last time we booted, // we need to re-grant app permission to catch any new ones that // appear. This is really a hack, and means that apps can in some // cases get permissions that the user didn't initially explicitly // allow... it would be nice to have some better way to handle // this situation. final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion); if (sdkUpdated) { Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to " + mSdkVersion + "; regranting permissions for internal storage"); } mPermissionManager.updateAllPermissions( StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated, mPackages.values(), mPermissionCallback); ver.sdkVersion = mSdkVersion; // If this is the first boot or an update from pre-M, and it is a normal // boot, then we need to initialize the default preferred apps across // all defined users. if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) { for (UserInfo user : sUserManager.getUsers(true)) { mSettings.applyDefaultPreferredAppsLPw(user.id); primeDomainVerificationsLPw(user.id); } } // Prepare storage for system user really early during boot, // since core system apps like SettingsProvider and SystemUI // can't wait for user to start final int storageFlags; if (StorageManager.isFileEncryptedNativeOrEmulated()) { storageFlags = StorageManager.FLAG_STORAGE_DE; } else { storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; } List deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL, UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */, true /* onlyCoreApps */); mPrepareAppDataFuture = SystemServerInitThreadPool.get().submit(() -> { TimingsTraceLog traceLog = new TimingsTraceLog("SystemServerTimingAsync", Trace.TRACE_TAG_PACKAGE_MANAGER); traceLog.traceBegin("AppDataFixup"); try { mInstaller.fixupAppData(StorageManager.UUID_PRIVATE_INTERNAL, StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE); } catch (InstallerException e) { Slog.w(TAG, "Trouble fixing GIDs", e); } traceLog.traceEnd(); traceLog.traceBegin("AppDataPrepare"); if (deferPackages == null || deferPackages.isEmpty()) { return; } int count = 0; for (String pkgName : deferPackages) { PackageParser.Package pkg = null; synchronized (mPackages) { PackageSetting ps = mSettings.getPackageLPr(pkgName); if (ps != null && ps.getInstalled(UserHandle.USER_SYSTEM)) { pkg = ps.pkg; } } if (pkg != null) { synchronized (mInstallLock) { prepareAppDataAndMigrateLIF(pkg, UserHandle.USER_SYSTEM, storageFlags, true /* maybeMigrateAppData */); } count++; } } traceLog.traceEnd(); Slog.i(TAG, "Deferred reconcileAppsData finished " + count + " packages"); }, "prepareAppData"); // If this is first boot after an OTA, and a normal boot, then // we need to clear code cache directories. // Note that we do *not* clear the application profiles. These remain valid // across OTAs and are used to drive profile verification (post OTA) and // profile compilation (without waiting to collect a fresh set of profiles). if (mIsUpgrade && !onlyCore) { Slog.i(TAG, "Build fingerprint changed; clearing code caches"); for (int i = 0; i < mSettings.mPackages.size(); i++) { final PackageSetting ps = mSettings.mPackages.valueAt(i); if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) { // No apps are running this early, so no need to freeze clearAppDataLIF(ps.pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); } } ver.fingerprint = Build.FINGERPRINT; } // Grandfather existing (installed before Q) non-system apps to hide // their icons in launcher. if (!onlyCore && mIsPreQUpgrade) { Slog.i(TAG, "Whitelisting all existing apps to hide their icons"); int size = mSettings.mPackages.size(); for (int i = 0; i < size; i++) { final PackageSetting ps = mSettings.mPackages.valueAt(i); if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) { continue; } ps.disableComponentLPw(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME, UserHandle.USER_SYSTEM); } } // clear only after permissions and other defaults have been updated mExistingSystemPackages.clear(); mPromoteSystemApps = false; // All the changes are done during package scanning. ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION; // can downgrade to reader Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "write settings"); mSettings.writeLPr(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis()); if (!mOnlyCore) { mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr(); mRequiredInstallerPackage = getRequiredInstallerLPr(); mRequiredUninstallerPackage = getRequiredUninstallerLPr(); mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr(); if (mIntentFilterVerifierComponent != null) { mIntentFilterVerifier = new IntentVerifierProxy(mContext, mIntentFilterVerifierComponent); } else { mIntentFilterVerifier = null; } mServicesSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr( PackageManager.SYSTEM_SHARED_LIBRARY_SERVICES, SharedLibraryInfo.VERSION_UNDEFINED); mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr( PackageManager.SYSTEM_SHARED_LIBRARY_SHARED, SharedLibraryInfo.VERSION_UNDEFINED); } else { mRequiredVerifierPackage = null; mRequiredInstallerPackage = null; mRequiredUninstallerPackage = null; mIntentFilterVerifierComponent = null; mIntentFilterVerifier = null; mServicesSystemSharedLibraryPackageName = null; mSharedSystemSharedLibraryPackageName = null; } // PermissionController hosts default permission granting and role management, so it's a // critical part of the core system. mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr(); // Initialize InstantAppRegistry's Instant App list for all users. final int[] userIds = UserManagerService.getInstance().getUserIds(); for (PackageParser.Package pkg : mPackages.values()) { if (pkg.isSystem()) { continue; } for (int userId : userIds) { final PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps == null || !ps.getInstantApp(userId) || !ps.getInstalled(userId)) { continue; } mInstantAppRegistry.addInstantAppLPw(userId, ps.appId); } } mInstallerService = new PackageInstallerService(context, this, mApexManager); final Pair instantAppResolverComponent = getInstantAppResolverLPr(); if (instantAppResolverComponent != null) { if (DEBUG_INSTANT) { Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent); } mInstantAppResolverConnection = new InstantAppResolverConnection( mContext, instantAppResolverComponent.first, instantAppResolverComponent.second); mInstantAppResolverSettingsComponent = getInstantAppResolverSettingsLPr(instantAppResolverComponent.first); } else { mInstantAppResolverConnection = null; mInstantAppResolverSettingsComponent = null; } updateInstantAppInstallerLocked(null); // Read and update the usage of dex files. // Do this at the end of PM init so that all the packages have their // data directory reconciled. // At this point we know the code paths of the packages, so we can validate // the disk file and build the internal cache. // The usage file is expected to be small so loading and verifying it // should take a fairly small time compare to the other activities (e.g. package // scanning). final Map> userPackages = new HashMap<>(); for (int userId : userIds) { 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) mModuleInfoProvider = new ModuleInfoProvider(mContext, this); // 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); // 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); PackageParser.readConfigUseRoundIcon(mContext.getResources()); mServiceStartWithDelay = SystemClock.uptimeMillis() + (60 * 1000L); 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 installSystemStubPackages(@NonNull List systemStubPackageNames, @ScanFlags int scanFlags) { for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) { final String packageName = systemStubPackageNames.get(i); // skip if the system package is already disabled if (mSettings.isDisabledSystemPackageLPr(packageName)) { systemStubPackageNames.remove(i); continue; } // skip if the package isn't installed (?!); this should never happen final PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null) { systemStubPackageNames.remove(i); continue; } // skip if the package has been disabled by the user final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM); if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { systemStubPackageNames.remove(i); continue; } } // install the package to replace the stub on /system try { installStubPackageLI(pkg, 0, scanFlags); ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, UserHandle.USER_SYSTEM, "android"); systemStubPackageNames.remove(i); } 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 = systemStubPackageNames.size() - 1; i >= 0; --i) { final String pkgName = systemStubPackageNames.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); } } /** * Extract, install and enable a stub package. * If the compressed file can not be extracted / installed for any reason, the stub * APK will be installed and the package will be disabled. To recover from this situation, * the user will need to go into system settings and re-enable the package. */ private boolean enableCompressedPackage(PackageParser.Package stubPkg) { final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY | PackageParser.PARSE_ENFORCE_CODE; synchronized (mInstallLock) { final PackageParser.Package pkg; try (PackageFreezer freezer = freezePackage(stubPkg.packageName, "setEnabledSetting")) { pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/); synchronized (mPackages) { prepareAppDataAfterInstallLIF(pkg); try { updateSharedLibrariesLocked(pkg, null, mPackages); } catch (PackageManagerException e) { Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e); } mPermissionManager.updatePermissions( pkg.packageName, pkg, true, mPackages.values(), mPermissionCallback); mSettings.writeLPr(); } } catch (PackageManagerException e) { // Whoops! Something went very wrong; roll back to the stub and disable the package try (PackageFreezer freezer = freezePackage(stubPkg.packageName, "setEnabledSetting")) { synchronized (mPackages) { // NOTE: Ensure the system package is enabled; even for a compressed stub. // If we don't, installing the system package fails during scan enableSystemPackageLPw(stubPkg); } installPackageFromSystemLIF(stubPkg.codePath, null /*allUserHandles*/, null /*origUserHandles*/, null /*origPermissionsState*/, true /*writeSettings*/); } catch (PackageManagerException pme) { // Serious WTF; we have to be able to install the stub Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.packageName, pme); } finally { // Disable the package; the stub by itself is not runnable synchronized (mPackages) { final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName); if (stubPs != null) { stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android"); } mSettings.writeLPr(); } } return false; } clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); mDexManager.notifyPackageUpdated(pkg.packageName, pkg.baseCodePath, pkg.splitCodePaths); } return true; } private PackageParser.Package installStubPackageLI(PackageParser.Package stubPkg, @ParseFlags int parseFlags, @ScanFlags int scanFlags) throws PackageManagerException { if (DEBUG_COMPRESSION) { Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.packageName); } // uncompress the binary to its eventual destination on /data final File scanFile = decompressPackage(stubPkg.packageName, stubPkg.codePath); if (scanFile == null) { throw new PackageManagerException("Unable to decompress stub at " + stubPkg.codePath); } synchronized (mPackages) { mSettings.disableSystemPackageLPw(stubPkg.packageName, true /*replaced*/); } removePackageLI(stubPkg, true /*chatty*/); try { return scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.packageName, e); // Remove the failed install removeCodePathLI(scanFile); throw e; } } /** * 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(String packageName, String codePath) { final File[] compressedFiles = getCompressedFiles(codePath); if (compressedFiles == null || compressedFiles.length == 0) { if (DEBUG_COMPRESSION) { Slog.i(TAG, "No files to decompress: " + codePath); } return null; } final File dstCodePath = getNextCodePath(Environment.getDataAppDirectory(null), 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: " + packageName + ", file: " + dstFileName); break; } } } catch (ErrnoException e) { logCriticalInfo(Log.ERROR, "Failed to decompress" + "; 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: " + packageName); ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; } finally { IoUtils.closeQuietly(handle); } } if (ret != PackageManager.INSTALL_SUCCEEDED) { if (!dstCodePath.exists()) { return null; } removeCodePathLI(dstCodePath); return null; } return dstCodePath; } @GuardedBy("mPackages") 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 @Nullable File preparePackageParserCache() { 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 = Environment.getPackageCacheDirectory(); if (!FileUtils.createDir(cacheBaseDir)) { return null; } // There are several items that need to be combined together to safely // identify cached items. In particular, changing the value of certain // feature flags should cause us to invalidate any caches. final String cacheName = SystemProperties.digestOf( "ro.build.fingerprint", StorageManager.PROP_ISOLATED_STORAGE, StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT); // Reconcile cache directories, keeping only what we'd actually use. for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) { if (Objects.equals(cacheName, cacheDir.getName())) { Slog.d(TAG, "Keeping known cache " + cacheDir.getName()); } else { Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName()); FileUtils.deleteContentsAndDir(cacheDir); } } // Return the versioned package cache directory. File cacheDir = FileUtils.createDir(cacheBaseDir, cacheName); if (cacheDir == null) { // Something went wrong. Attempt to delete everything and return. Slog.wtf(TAG, "Cache directory cannot be created - wiping base dir " + cacheBaseDir); FileUtils.deleteContentsAndDir(cacheBaseDir); return null; } // The following is a workaround to aid development on non-numbered userdebug // builds or cases where "adb sync" is used on userdebug builds. If we detect that // the system partition is newer. // // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build // that starts with "eng." to signify that this is an engineering build and not // destined for release. if (Build.IS_USERDEBUG && Build.VERSION.INCREMENTAL.startsWith("eng.")) { Slog.w(TAG, "Wiping cache directory because the system partition changed."); // Heuristic: If the /system directory has been modified recently due to an "adb sync" // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable // in general and should not be used for production changes. In this specific case, // we know that they will work. File frameworkDir = new File(Environment.getRootDirectory(), "framework"); if (cacheDir.lastModified() < frameworkDir.lastModified()) { FileUtils.deleteContents(cacheBaseDir); cacheDir = FileUtils.createDir(cacheBaseDir, cacheName); } } return cacheDir; } @Override public boolean isFirstBoot() { // allow instant applications return mFirstBoot; } @Override public boolean isOnlyCoreApps() { // allow instant applications return mOnlyCore; } @Override public boolean isDeviceUpgrading() { // allow instant applications // The system property allows testing ota flow when upgraded to the same image. return mIsUpgrade || SystemProperties.getBoolean( "persist.pm.mock-upgrade", false /* default */); } private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() { final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); final List matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/); if (matches.size() == 1) { return matches.get(0).getComponentInfo().packageName; } else if (matches.size() == 0) { Log.e(TAG, "There should probably be a verifier, but, none were found"); return null; } throw new RuntimeException("There must be exactly one verifier; found " + matches); } private @NonNull String getRequiredSharedLibraryLPr(String name, int version) { synchronized (mPackages) { SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(name, version); if (libraryInfo == null) { throw new IllegalStateException("Missing required shared library:" + name); } String packageName = libraryInfo.getPackageName(); if (packageName == null) { throw new IllegalStateException("Expected a package for shared library " + name); } return packageName; } } private @NonNull String getRequiredInstallerLPr() { final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setDataAndType(Uri.parse("content://com.example/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 String getRequiredPermissionControllerLPr() { final Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSIONS); intent.addCategory(Intent.CATEGORY_DEFAULT); final List matches = queryIntentActivitiesInternal(intent, null, 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 permissions manager must be a privileged app"); } return matches.get(0).getComponentInfo().packageName; } else { throw new RuntimeException("There must be exactly one permissions manager; found " + matches); } } private @NonNull ComponentName getIntentFilterVerifierComponentNameLPr() { final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION); final List matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/); ResolveInfo best = null; final int N = matches.size(); for (int i = 0; i < N; i++) { final ResolveInfo cur = matches.get(i); final String packageName = cur.getComponentInfo().packageName; if (checkPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT, packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) { continue; } if (best == null || cur.priority > best.priority) { best = cur; } } if (best != null) { return best.getComponentInfo().getComponentName(); } Slog.w(TAG, "Intent filter verifier not found"); return null; } @Override public @Nullable ComponentName getInstantAppResolverComponent() { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return null; } synchronized (mPackages) { final Pair instantAppResolver = getInstantAppResolverLPr(); if (instantAppResolver == null) { return null; } return instantAppResolver.first; } } private @Nullable Pair getInstantAppResolverLPr() { final String[] packageArray = mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage); if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) { if (DEBUG_INSTANT) { Slog.d(TAG, "Ephemeral resolver NOT found; empty package list"); } return null; } final int callingUid = Binder.getCallingUid(); final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0); String actionName = Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE; final Intent resolverIntent = new Intent(actionName); List resolvers = queryIntentServicesInternal(resolverIntent, null, resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/); final int N = resolvers.size(); if (N == 0) { if (DEBUG_INSTANT) { Slog.d(TAG, "Ephemeral resolver NOT found; no matching intent filters"); } return null; } final Set possiblePackages = new ArraySet<>(Arrays.asList(packageArray)); for (int i = 0; i < N; i++) { final ResolveInfo info = resolvers.get(i); if (info.serviceInfo == null) { continue; } final String packageName = info.serviceInfo.packageName; if (!possiblePackages.contains(packageName) && !Build.IS_DEBUGGABLE) { if (DEBUG_INSTANT) { Slog.d(TAG, "Ephemeral resolver not in allowed package list;" + " pkg: " + packageName + ", info:" + info); } continue; } if (DEBUG_INSTANT) { Slog.v(TAG, "Ephemeral resolver found;" + " pkg: " + packageName + ", info:" + info); } return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName); } if (DEBUG_INSTANT) { Slog.v(TAG, "Ephemeral resolver NOT found"); } return null; } @GuardedBy("mPackages") private @Nullable ActivityInfo getInstantAppInstallerLPr() { String[] orderedActions = Build.IS_ENG ? new String[]{ Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE + "_TEST", Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE} : new String[]{ Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE}; final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | Intent.FLAG_IGNORE_EPHEMERAL | (!Build.IS_ENG ? MATCH_SYSTEM_ONLY : 0); final Intent intent = new Intent(); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE); List matches = null; for (String action : orderedActions) { intent.setAction(action); matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE, resolveFlags, UserHandle.USER_SYSTEM); if (matches.isEmpty()) { if (DEBUG_INSTANT) { Slog.d(TAG, "Instant App installer not found with " + action); } } else { break; } } Iterator iter = matches.iterator(); while (iter.hasNext()) { final ResolveInfo rInfo = iter.next(); final PackageSetting ps = mSettings.mPackages.get(rInfo.activityInfo.packageName); if (ps != null) { final PermissionsState permissionsState = ps.getPermissionsState(); if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0) || Build.IS_ENG) { continue; } } iter.remove(); } if (matches.size() == 0) { return null; } else if (matches.size() == 1) { return (ActivityInfo) matches.get(0).getComponentInfo(); } else { throw new RuntimeException( "There must be at most one ephemeral installer; found " + matches); } } private @Nullable ComponentName getInstantAppResolverSettingsLPr( @NonNull ComponentName resolver) { final Intent intent = new Intent(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS) .addCategory(Intent.CATEGORY_DEFAULT) .setPackage(resolver.getPackageName()); final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; List matches = queryIntentActivitiesInternal(intent, null, resolveFlags, UserHandle.USER_SYSTEM); if (matches.isEmpty()) { return null; } return matches.get(0).getComponentInfo().getComponentName(); } @GuardedBy("mPackages") private void primeDomainVerificationsLPw(int userId) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.d(TAG, "Priming domain verifications in user " + userId); } SystemConfig systemConfig = SystemConfig.getInstance(); ArraySet packages = systemConfig.getLinkedApps(); for (String packageName : packages) { PackageParser.Package pkg = mPackages.get(packageName); if (pkg != null) { if (!pkg.isSystem()) { Slog.w(TAG, "Non-system app '" + packageName + "' in sysconfig "); continue; } ArraySet domains = null; for (PackageParser.Activity a : pkg.activities) { for (ActivityIntentInfo filter : a.intents) { if (hasValidDomains(filter)) { if (domains == null) { domains = new ArraySet<>(); } domains.addAll(filter.getHostsList()); } } } if (domains != null && domains.size() > 0) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.v(TAG, " + " + packageName); } // 'Undefined' in the global IntentFilterVerificationInfo, i.e. the usual // state w.r.t. the formal app-linkage "no verification attempted" state; // and then 'always' in the per-user state actually used for intent resolution. final IntentFilterVerificationInfo ivi; ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName, domains); ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED); mSettings.updateIntentFilterVerificationStatusLPw(packageName, INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, userId); } else { Slog.w(TAG, "Sysconfig package '" + packageName + "' does not handle web links"); } } else { Slog.w(TAG, "Unknown package " + packageName + " in sysconfig "); } } scheduleWritePackageRestrictionsLocked(userId); scheduleWriteSettingsLocked(); } private boolean packageIsBrowser(String packageName, int userId) { List list = queryIntentActivitiesInternal(sBrowserIntent, null, PackageManager.MATCH_ALL, userId); final int N = list.size(); for (int i = 0; i < N; i++) { ResolveInfo info = list.get(i); if (info.priority >= 0 && packageName.equals(info.activityInfo.packageName)) { return true; } } return false; } @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; } } /** * Returns whether or not a full application can see an instant application. * * Currently, there are four cases in which this can occur: * * The calling application is a "special" process. Special processes * are those with a UID < {@link Process#FIRST_APPLICATION_UID}. * The calling application has the permission * {@link android.Manifest.permission#ACCESS_INSTANT_APPS}. * The calling application is the default launcher on the * system partition. * The calling application is the default app prediction service. * */ private boolean canViewInstantApps(int callingUid, int userId) { if (callingUid < Process.FIRST_APPLICATION_UID) { return true; } if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.ACCESS_INSTANT_APPS) == PERMISSION_GRANTED) { return true; } if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.VIEW_INSTANT_APPS) == PERMISSION_GRANTED) { final ComponentName homeComponent = getDefaultHomeActivity(userId); if (homeComponent != null && isCallerSameApp(homeComponent.getPackageName(), callingUid)) { return true; } // TODO(b/122900055) Change/Remove this and replace with new permission role. if (mAppPredictionServicePackage != null && isCallerSameApp(mAppPredictionServicePackage, callingUid)) { return true; } } return false; } private PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) { if (!sUserManager.exists(userId)) return null; if (ps == null) { return null; } final int callingUid = Binder.getCallingUid(); // Filter out ephemeral app metadata: // * The system/shell/root can see metadata for any app // * An installed app can see metadata for 1) other installed apps // and 2) ephemeral apps that have explicitly interacted with it // * Ephemeral apps can only see their own data and exposed installed apps // * Holding a signature permission allows seeing instant apps if (filterAppAccessLPr(ps, callingUid, userId)) { return null; } if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && ps.isSystem()) { flags |= MATCH_ANY_USER; } final PackageUserState state = ps.readUserState(userId); PackageParser.Package p = ps.pkg; if (p != null) { final PermissionsState permissionsState = ps.getPermissionsState(); // Compute GIDs only if requested final int[] gids = (flags & PackageManager.GET_GIDS) == 0 ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId); // Compute granted permissions only if package has requested permissions final Set permissions = ArrayUtils.isEmpty(p.requestedPermissions) ? Collections.emptySet() : permissionsState.getPermissions(userId); PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags, ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId); if (packageInfo == null) { return null; } packageInfo.packageName = packageInfo.applicationInfo.packageName = resolveExternalPackageNameLPr(p); return packageInfo; } else if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && state.isAvailable(flags)) { PackageInfo pi = new PackageInfo(); pi.packageName = ps.name; pi.setLongVersionCode(ps.versionCode); pi.sharedUserId = (ps.sharedUser != null) ? ps.sharedUser.name : null; pi.firstInstallTime = ps.firstInstallTime; pi.lastUpdateTime = ps.lastUpdateTime; ApplicationInfo ai = new ApplicationInfo(); ai.packageName = ps.name; ai.uid = UserHandle.getUid(userId, ps.appId); ai.primaryCpuAbi = ps.primaryCpuAbiString; ai.secondaryCpuAbi = ps.secondaryCpuAbiString; ai.setVersionCode(ps.versionCode); ai.flags = ps.pkgFlags; ai.privateFlags = ps.pkgPrivateFlags; pi.applicationInfo = PackageParser.generateApplicationInfo(ai, flags, state, userId); if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for [" + ps.name + "]. Provides a minimum info."); return pi; } else { return null; } } @Override public void checkPackageStartable(String packageName, int userId) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { throw new SecurityException("Instant applications don't have access to this method"); } final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId); synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) { throw new SecurityException("Package " + packageName + " was not found!"); } if (!ps.getInstalled(userId)) { throw new SecurityException( "Package " + packageName + " was not installed for user " + userId + "!"); } if (mSafeMode && !ps.isSystem()) { throw new SecurityException("Package " + packageName + " not a system app!"); } if (mFrozenPackages.contains(packageName)) { throw new SecurityException("Package " + packageName + " is currently frozen!"); } if (!userKeyUnlocked && !ps.pkg.applicationInfo.isEncryptionAware()) { throw new SecurityException("Package " + packageName + " is not encryption aware!"); } } } @Override public boolean isPackageAvailable(String packageName, int userId) { if (!sUserManager.exists(userId)) return false; final int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "is package available"); synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); if (p != null) { final PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, userId)) { return false; } if (ps != null) { final PackageUserState state = ps.readUserState(userId); if (state != null) { return PackageParser.isAvailable(state); } } } } return false; } @Override public PackageInfo getPackageInfo(String packageName, int flags, int userId) { return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST, flags, Binder.getCallingUid(), userId); } @Override public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage, int flags, int userId) { return getPackageInfoInternal(versionedPackage.getPackageName(), versionedPackage.getLongVersionCode(), flags, Binder.getCallingUid(), userId); } /** * Important: The provided filterCallingUid is used exclusively to filter out packages * that can be seen based on user state. It's typically the original caller uid prior * to clearing. Because it can only be provided by trusted code, it's value can be * trusted and will be used as-is; unlike userId which will be validated by this method. */ private PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags, int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForPackage(flags, userId, packageName); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get package info"); // reader synchronized (mPackages) { // Normalize package name to handle renamed packages and static libs packageName = resolveInternalPackageNameLPr(packageName, versionCode); final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0; if (matchFactoryOnly) { // Instant app filtering for APEX modules is ignored if ((flags & MATCH_APEX) != 0) { return mApexManager.getPackageInfo(packageName, ApexManager.MATCH_FACTORY_PACKAGE); } 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); } if (!matchFactoryOnly && (flags & MATCH_APEX) != 0) { return mApexManager.getPackageInfo(packageName, ApexManager.MATCH_ACTIVE_PACKAGE); } } 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 = mComponentResolver.getActivity(component); if (activity == null) { return false; } final boolean visibleToInstantApp = (activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean explicitlyVisibleToInstantApp = (activity.info.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; return visibleToInstantApp && explicitlyVisibleToInstantApp; } else if (type == TYPE_RECEIVER) { final PackageParser.Activity activity = mComponentResolver.getReceiver(component); if (activity == null) { return false; } final boolean visibleToInstantApp = (activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean explicitlyVisibleToInstantApp = (activity.info.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; return visibleToInstantApp && !explicitlyVisibleToInstantApp; } else if (type == TYPE_SERVICE) { final PackageParser.Service service = mComponentResolver.getService(component); return service != null ? (service.info.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0 : false; } else if (type == TYPE_PROVIDER) { final PackageParser.Provider provider = mComponentResolver.getProvider(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 #canViewInstantApps(int, int) */ @GuardedBy("mPackages") private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, @Nullable ComponentName component, @ComponentType int componentType, int userId) { // if we're in an isolated process, get the real calling UID if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final String instantAppPkgName = getInstantAppPackageName(callingUid); final boolean callerIsInstantApp = instantAppPkgName != null; if (ps == null) { if (callerIsInstantApp) { // pretend the application exists, but, needs to be filtered return true; } return false; } // if the target and caller are the same application, don't filter if (isCallerSameApp(ps.name, callingUid)) { return false; } if (callerIsInstantApp) { // both caller and target are both instant, but, different applications, filter if (ps.getInstantApp(userId)) { return true; } // request for a specific component; if it hasn't been explicitly exposed through // property or instrumentation target, filter if (component != null) { final PackageParser.Instrumentation instrumentation = mInstrumentation.get(component); if (instrumentation != null && isCallerSameApp(instrumentation.info.targetPackage, callingUid)) { return false; } return !isComponentVisibleToInstantApp(component, componentType); } // request for application; if no components have been explicitly exposed, filter return !ps.pkg.visibleToInstantApps; } if (ps.getInstantApp(userId)) { // caller can see all components of all instant applications, don't filter if (canViewInstantApps(callingUid, userId)) { return false; } // request for a specific instant application component, filter if (component != null) { return true; } // request for an instant application; if the caller hasn't been granted access, filter return !mInstantAppRegistry.isInstantAccessGranted( userId, UserHandle.getAppId(callingUid), ps.appId); } return false; } /** * @see #filterAppAccessLPr(PackageSetting, int, ComponentName, int, int) */ @GuardedBy("mPackages") private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, int userId) { return filterAppAccessLPr(ps, callingUid, null, TYPE_UNKNOWN, userId); } @GuardedBy("mPackages") 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; } // Installer gets to see all static libs. if (PackageManager.PERMISSION_GRANTED == checkUidPermission(Manifest.permission.INSTALL_PACKAGES, 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 SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(ps.pkg.staticSharedLibName, ps.pkg.staticSharedLibVersion); if (libraryInfo == 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, libraryInfo.getName()); if (index < 0) { continue; } if (uidPs.pkg.usesStaticLibrariesVersions[index] == libraryInfo.getLongVersion()) { return false; } } } return true; } @Override public String[] currentToCanonicalPackageNames(String[] names) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return names; } final String[] out = new String[names.length]; // reader synchronized (mPackages) { final int callingUserId = UserHandle.getUserId(callingUid); final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId); for (int i=names.length-1; i>=0; i--) { final PackageSetting ps = mSettings.mPackages.get(names[i]); boolean translateName = false; if (ps != null && ps.realName != null) { final boolean targetIsInstantApp = ps.getInstantApp(callingUserId); translateName = !targetIsInstantApp || canViewInstantApps || mInstantAppRegistry.isInstantAccessGranted(callingUserId, UserHandle.getAppId(callingUid), ps.appId); } out[i] = translateName ? ps.realName : names[i]; } } return out; } @Override public String[] canonicalToCurrentPackageNames(String[] names) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return names; } final String[] out = new String[names.length]; // reader synchronized (mPackages) { final int callingUserId = UserHandle.getUserId(callingUid); final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId); for (int i=names.length-1; i>=0; i--) { final String cur = mSettings.getRenamedPackageLPr(names[i]); boolean translateName = false; if (cur != null) { final PackageSetting ps = mSettings.mPackages.get(names[i]); final boolean targetIsInstantApp = ps != null && ps.getInstantApp(callingUserId); translateName = !targetIsInstantApp || canViewInstantApps || mInstantAppRegistry.isInstantAccessGranted(callingUserId, UserHandle.getAppId(callingUid), ps.appId); } out[i] = translateName ? cur : names[i]; } } return out; } @Override public int getPackageUid(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return -1; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForPackage(flags, userId, packageName); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid"); // reader synchronized (mPackages) { final PackageParser.Package p = mPackages.get(packageName); if (p != null && p.isMatch(flags)) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, userId)) { return -1; } return UserHandle.getUid(userId, p.applicationInfo.uid); } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && ps.isMatch(flags) && !filterAppAccessLPr(ps, callingUid, userId)) { return UserHandle.getUid(userId, ps.appId); } } } return -1; } /** * Check if any package sharing/holding a uid has a low enough target SDK. * * @param uid The uid of the packages * @param higherTargetSDK The target SDK that might be higher than the searched package * * @return {@code true} if there is a package sharing/holding the uid with * {@code package.targetSDK < higherTargetSDK} */ private boolean hasTargetSdkInUidLowerThan(int uid, int higherTargetSDK) { int userId = UserHandle.getUserId(uid); synchronized (mPackages) { Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid)); if (obj == null) { return false; } if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (!ps.getInstalled(userId)) { return false; } return ps.pkg.applicationInfo.targetSdkVersion < higherTargetSDK; } else if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; final int numPkgs = sus.packages.size(); for (int i = 0; i < numPkgs; i++) { final PackageSetting ps = sus.packages.valueAt(i); if (!ps.getInstalled(userId)) { continue; } if (ps.pkg.applicationInfo.targetSdkVersion < higherTargetSDK) { return true; } } return false; } else { return false; } } } @Override public int[] getPackageGids(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForPackage(flags, userId, packageName); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids"); // reader synchronized (mPackages) { final PackageParser.Package p = mPackages.get(packageName); if (p != null && p.isMatch(flags)) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, userId)) { return null; } // TODO: Shouldn't this be checking for package installed state for userId and // return null? return ps.getPermissionsState().computeGids(userId); } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && ps.isMatch(flags) && !filterAppAccessLPr(ps, callingUid, userId)) { return ps.getPermissionsState().computeGids(userId); } } } return null; } @Override public PermissionInfo getPermissionInfo(String name, String packageName, int flags) { return mPermissionManager.getPermissionInfo(name, packageName, flags, getCallingUid()); } @Override public @Nullable ParceledListSlice queryPermissionsByGroup(String groupName, int flags) { final List permissionList = mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid()); return (permissionList == null) ? null : new ParceledListSlice<>(permissionList); } @Override public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) { return mPermissionManager.getPermissionGroupInfo(groupName, flags, getCallingUid()); } @Override public @NonNull ParceledListSlice getAllPermissionGroups(int flags) { final List permissionList = mPermissionManager.getAllPermissionGroups(flags, getCallingUid()); return (permissionList == null) ? ParceledListSlice.emptyList() : new ParceledListSlice<>(permissionList); } @GuardedBy("mPackages") 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); if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get application info"); } // writer synchronized (mPackages) { // Normalize package name to handle renamed packages and static libs packageName = resolveInternalPackageNameLPr(packageName, PackageManager.VERSION_CODE_HIGHEST); PackageParser.Package p = mPackages.get(packageName); if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getApplicationInfo " + packageName + ": " + p); if (p != null) { PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) return null; if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; } if (filterAppAccessLPr(ps, filterCallingUid, userId)) { return null; } // Note: isEnabledLP() does not apply here - always return info ApplicationInfo ai = PackageParser.generateApplicationInfo( p, flags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(p); } return ai; } if ("android".equals(packageName)||"system".equals(packageName)) { return mAndroidApplication; } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { // Already generates the external package name return generateApplicationInfoFromSettingsLPw(packageName, flags, filterCallingUid, userId); } } return null; } @GuardedBy("mPackages") private String normalizePackageNameLPr(String packageName) { String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName); return normalizedPackageName != null ? normalizedPackageName : packageName; } @Override public void deletePreloadsFileCache() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CLEAR_APP_CACHE, "deletePreloadsFileCache"); File dir = Environment.getDataPreloadsFileCacheDirectory(); Slog.i(TAG, "Deleting preloaded file cache " + dir); FileUtils.deleteContents(dir); } @Override public void freeStorageAndNotify(final String volumeUuid, final long freeStorageSize, final int storageFlags, final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); mHandler.post(() -> { boolean success = false; try { freeStorage(volumeUuid, freeStorageSize, storageFlags); success = true; } catch (IOException e) { Slog.w(TAG, e); } if (observer != null) { try { observer.onRemoveCompleted(null, success); } catch (RemoteException e) { Slog.w(TAG, e); } } }); } @Override public void freeStorage(final String volumeUuid, final long freeStorageSize, final int storageFlags, final IntentSender pi) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, TAG); mHandler.post(() -> { boolean success = false; try { freeStorage(volumeUuid, freeStorageSize, storageFlags); success = true; } catch (IOException e) { Slog.w(TAG, e); } if (pi != null) { try { pi.sendIntent(null, success ? 1 : 0, null, null, null); } catch (SendIntentException e) { Slog.w(TAG, e); } } }); } /** * Blocking call to clear various types of cached data across the system * until the requested bytes are available. */ public void freeStorage(String volumeUuid, long bytes, int storageFlags) throws IOException { final StorageManager storage = mContext.getSystemService(StorageManager.class); final File file = storage.findPathForUuid(volumeUuid); if (file.getUsableSpace() >= bytes) return; if (ENABLE_FREE_CACHE_V2) { final boolean internalVolume = Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid); final boolean aggressive = (storageFlags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0; final long reservedBytes = storage.getStorageCacheBytes(file, storageFlags); // 1. Pre-flight to determine if we have any chance to succeed // 2. Consider preloaded data (after 1w honeymoon, unless aggressive) if (internalVolume && (aggressive || SystemProperties .getBoolean("persist.sys.preloads.file_cache_expired", false))) { deletePreloadsFileCache(); if (file.getUsableSpace() >= bytes) return; } // 3. Consider parsed APK data (aggressive only) if (internalVolume && aggressive) { FileUtils.deleteContents(mCacheDir); if (file.getUsableSpace() >= bytes) return; } // 4. Consider cached app data (above quotas) try { mInstaller.freeCache(volumeUuid, bytes, reservedBytes, Installer.FLAG_FREE_CACHE_V2); } catch (InstallerException ignored) { } if (file.getUsableSpace() >= bytes) return; // 5. Consider shared libraries with refcount=0 and age>min cache period if (internalVolume && pruneUnusedStaticSharedLibraries(bytes, android.provider.Settings.Global.getLong(mContext.getContentResolver(), Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD, DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD))) { return; } // 6. Consider dexopt output (aggressive only) // TODO: Implement // 7. Consider installed instant apps unused longer than min cache period if (internalVolume && mInstantAppRegistry.pruneInstalledInstantApps(bytes, android.provider.Settings.Global.getLong(mContext.getContentResolver(), Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD, InstantAppRegistry.DEFAULT_INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) { return; } // 8. Consider cached app data (below quotas) try { mInstaller.freeCache(volumeUuid, bytes, reservedBytes, Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA); } catch (InstallerException ignored) { } if (file.getUsableSpace() >= bytes) return; // 9. Consider DropBox entries // TODO: Implement // 10. Consider instant meta-data (uninstalled apps) older that min cache period if (internalVolume && mInstantAppRegistry.pruneUninstalledInstantApps(bytes, android.provider.Settings.Global.getLong(mContext.getContentResolver(), Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD, InstantAppRegistry.DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) { return; } } else { try { mInstaller.freeCache(volumeUuid, bytes, 0, 0); } catch (InstallerException ignored) { } if (file.getUsableSpace() >= bytes) return; } throw new IOException("Failed to free " + bytes + " on storage device at " + file); } private boolean pruneUnusedStaticSharedLibraries(long neededSpace, long maxCachePeriod) throws IOException { final StorageManager storage = mContext.getSystemService(StorageManager.class); final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL); List packagesToDelete = null; final long now = System.currentTimeMillis(); synchronized (mPackages) { final int[] allUsers = sUserManager.getUserIds(); final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { final LongSparseArray versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } final int versionCount = versionedLib.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libInfo = versionedLib.valueAt(j); // Skip packages that are not static shared libs. if (!libInfo.isStatic()) { break; } // Important: We skip static shared libs used for some user since // in such a case we need to keep the APK on the device. The check for // a lib being used for any user is performed by the uninstall call. final VersionedPackage declaringPackage = libInfo.getDeclaringPackage(); // Resolve the package name - we use synthetic package names internally final String internalPackageName = resolveInternalPackageNameLPr( declaringPackage.getPackageName(), declaringPackage.getLongVersionCode()); final PackageSetting ps = mSettings.getPackageLPr(internalPackageName); // Skip unused static shared libs cached less than the min period // to prevent pruning a lib needed by a subsequently installed package. if (ps == null || now - ps.lastUpdateTime < maxCachePeriod) { continue; } if (ps.pkg.isSystem()) { continue; } if (packagesToDelete == null) { packagesToDelete = new ArrayList<>(); } packagesToDelete.add(new VersionedPackage(internalPackageName, declaringPackage.getLongVersionCode())); } } } if (packagesToDelete != null) { final int packageCount = packagesToDelete.size(); for (int i = 0; i < packageCount; i++) { final VersionedPackage pkgToDelete = packagesToDelete.get(i); // Delete the package synchronously (will fail of the lib used for any user). if (deletePackageX(pkgToDelete.getPackageName(), pkgToDelete.getLongVersionCode(), UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS) == PackageManager.DELETE_SUCCEEDED) { if (volume.getUsableSpace() >= neededSpace) { return true; } } } } return false; } /** * Update given flags based on encryption status of current user. */ private int updateFlags(int flags, int userId) { if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_AWARE)) != 0) { // Caller expressed an explicit opinion about what encryption // aware/unaware components they want to see, so fall through and // give them what they want } else { // Caller expressed no opinion, so match based on user state if (getUserManagerInternal().isUserUnlockingOrUnlocked(userId)) { flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; } else { flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE; } } return flags; } private UserManagerInternal getUserManagerInternal() { if (mUserManagerInternal == null) { mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); } return mUserManagerInternal; } private ActivityManagerInternal getActivityManagerInternal() { if (mActivityManagerInternal == null) { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); } return mActivityManagerInternal; } private ActivityTaskManagerInternal getActivityTaskManagerInternal() { if (mActivityTaskManagerInternal == null) { mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); } return mActivityTaskManagerInternal; } private DeviceIdleController.LocalService getDeviceIdleController() { if (mDeviceIdleController == null) { mDeviceIdleController = LocalServices.getService(DeviceIdleController.LocalService.class); } return mDeviceIdleController; } private StorageManagerInternal getStorageManagerInternal() { if (mStorageManagerInternal == null) { mStorageManagerInternal = LocalServices.getService(StorageManagerInternal.class); } return mStorageManagerInternal; } /** * 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; if ((flags & PackageManager.MATCH_ANY_USER) != 0) { // require the permission to be held; the calling uid and given user id referring // to the same user is not sufficient mPermissionManager.enforceCrossUserPermission( Binder.getCallingUid(), userId, false, false, !isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId), "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; } 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) { return updateFlags(flags, userId); } /** * Update given intent when being used to request {@link ResolveInfo}. */ private Intent updateIntentForResolve(Intent intent) { if (intent.getSelector() != null) { intent = intent.getSelector(); } if (DEBUG_PREFERRED) { intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); } return intent; } /** * Update given flags when being used to request {@link ResolveInfo}. * Instant apps are resolved specially, depending upon context. Minimally, * {@code}flags{@code} must have the {@link PackageManager#MATCH_INSTANT} * flag set. However, this flag is only honoured in three circumstances: * * when called from a system process * when the caller holds the permission {@code android.permission.ACCESS_INSTANT_APPS} * when resolution occurs to start an activity with a {@code android.intent.action.VIEW} * action and a {@code android.intent.category.BROWSABLE} category * */ int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid) { return updateFlagsForResolve(flags, userId, intent, callingUid, false /*wantInstantApps*/, false /*onlyExposedExplicitly*/); } int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid, boolean wantInstantApps) { return updateFlagsForResolve(flags, userId, intent, callingUid, wantInstantApps, false /*onlyExposedExplicitly*/); } int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid, boolean wantInstantApps, boolean onlyExposedExplicitly) { // Safe mode means we shouldn't match any third-party components if (mSafeMode) { flags |= PackageManager.MATCH_SYSTEM_ONLY; } if (getInstantAppPackageName(callingUid) != null) { // But, ephemeral apps see both ephemeral and exposed, non-ephemeral components if (onlyExposedExplicitly) { flags |= PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY; } flags |= PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY; flags |= PackageManager.MATCH_INSTANT; } else { final boolean wantMatchInstant = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean allowMatchInstant = wantInstantApps || (wantMatchInstant && canViewInstantApps(callingUid, userId)); flags &= ~(PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY | PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY); if (!allowMatchInstant) { flags &= ~PackageManager.MATCH_INSTANT; } } return updateFlagsForComponent(flags, userId, intent /*cookie*/); } @Override public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId); } /** * Important: The provided filterCallingUid is used exclusively to filter out activities * that can be seen based on user state. It's typically the original caller uid prior * to clearing. Because it can only be provided by trusted code, it's value can be * trusted and will be used as-is; unlike userId which will be validated by this method. */ private ActivityInfo getActivityInfoInternal(ComponentName component, int flags, int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId, component); if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get activity info"); } synchronized (mPackages) { PackageParser.Activity a = mComponentResolver.getActivity(component); if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a); if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) { return null; } return PackageParser.generateActivityInfo( a, flags, ps.readUserState(userId), userId); } if (mResolveComponentName.equals(component)) { return PackageParser.generateActivityInfo( mResolveActivity, flags, new PackageUserState(), userId); } } return null; } private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) { if (!getActivityTaskManagerInternal().isCallerRecents(callingUid)) { return false; } final long token = Binder.clearCallingIdentity(); try { final int callingUserId = UserHandle.getUserId(callingUid); if (ActivityManager.getCurrentUser() != callingUserId) { return false; } return sUserManager.isSameProfileGroup(callingUserId, targetUserId); } finally { Binder.restoreCallingIdentity(token); } } @Override public boolean activitySupportsIntent(ComponentName component, Intent intent, String resolvedType) { synchronized (mPackages) { if (component.equals(mResolveComponentName)) { // The resolver supports EVERYTHING! return true; } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); PackageParser.Activity a = mComponentResolver.getActivity(component); if (a == null) { return false; } PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) { return false; } if (filterAppAccessLPr(ps, callingUid, component, TYPE_ACTIVITY, callingUserId)) { return false; } for (int i=0; i= 0) { return true; } } return false; } } @Override public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get receiver info"); synchronized (mPackages) { PackageParser.Activity a = mComponentResolver.getReceiver(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 || mContext.checkCallingOrSelfPermission( Manifest.permission.ACCESS_SHARED_LIBRARIES) == PERMISSION_GRANTED; synchronized (mPackages) { List result = null; final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { LongSparseArray versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } final int versionCount = versionedLib.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libInfo = versionedLib.valueAt(j); 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.getPath(), libInfo.getPackageName(), libInfo.getAllCodePaths(), libInfo.getName(), libInfo.getLongVersion(), libInfo.getType(), libInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr(libInfo, flags, userId), (libInfo.getDependencies() == null ? null : new ArrayList<>(libInfo.getDependencies()))); if (result == null) { result = new ArrayList<>(); } result.add(resLibInfo); } } return result != null ? new ParceledListSlice<>(result) : null; } } @Nullable @Override public ParceledListSlice getDeclaredSharedLibraries( @NonNull String packageName, int flags, @NonNull int userId) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES, "getDeclaredSharedLibraries"); int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "getDeclaredSharedLibraries"); Preconditions.checkNotNull(packageName, "packageName cannot be null"); Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0"); if (!sUserManager.exists(userId)) { return null; } if (getInstantAppPackageName(callingUid) != null) { return null; } synchronized (mPackages) { List result = null; int libraryCount = mSharedLibraries.size(); for (int i = 0; i < libraryCount; i++) { LongSparseArray versionedLibrary = mSharedLibraries.valueAt(i); if (versionedLibrary == null) { continue; } int versionCount = versionedLibrary.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libraryInfo = versionedLibrary.valueAt(j); VersionedPackage declaringPackage = libraryInfo.getDeclaringPackage(); if (!Objects.equals(declaringPackage.getPackageName(), packageName)) { continue; } long identity = Binder.clearCallingIdentity(); try { PackageInfo packageInfo = getPackageInfoVersioned(declaringPackage, flags | PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId); if (packageInfo == null) { continue; } } finally { Binder.restoreCallingIdentity(identity); } SharedLibraryInfo resultLibraryInfo = new SharedLibraryInfo( libraryInfo.getPath(), libraryInfo.getPackageName(), libraryInfo.getAllCodePaths(), libraryInfo.getName(), libraryInfo.getLongVersion(), libraryInfo.getType(), libraryInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr( libraryInfo, flags, userId), libraryInfo.getDependencies() == null ? null : new ArrayList<>(libraryInfo.getDependencies())); if (result == null) { result = new ArrayList<>(); } result.add(resultLibraryInfo); } } return result != null ? new ParceledListSlice<>(result) : null; } } @GuardedBy("mPackages") 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.readUserState(userId).isAvailable(flags)) { continue; } final String libName = libInfo.getName(); if (libInfo.isStatic()) { final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName); if (libIdx < 0) { continue; } if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getLongVersion()) { continue; } if (versionedPackages == null) { versionedPackages = new ArrayList<>(); } // If the dependent is a static shared lib, use the public package name String dependentPackageName = ps.name; if (ps.pkg != null && ps.pkg.applicationInfo.isStaticSharedLibrary()) { dependentPackageName = ps.pkg.manifestPackageName; } versionedPackages.add(new VersionedPackage(dependentPackageName, ps.versionCode)); } else if (ps.pkg != null) { if (ArrayUtils.contains(ps.pkg.usesLibraries, libName) || ArrayUtils.contains(ps.pkg.usesOptionalLibraries, libName)) { if (versionedPackages == null) { versionedPackages = new ArrayList<>(); } versionedPackages.add(new VersionedPackage(ps.name, ps.versionCode)); } } } return versionedPackages; } @Override public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get service info"); synchronized (mPackages) { PackageParser.Service s = mComponentResolver.getService(component); if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getServiceInfo " + component + ": " + s); if (s != null && mSettings.isEnabledAndMatchLPr(s.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, callingUid, component, TYPE_SERVICE, userId)) { return null; } return PackageParser.generateServiceInfo( s, flags, ps.readUserState(userId), userId); } } return null; } @Override public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get provider info"); synchronized (mPackages) { PackageParser.Provider p = mComponentResolver.getProvider(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 ModuleInfo getModuleInfo(String packageName, @ModuleInfoFlags int flags) { return mModuleInfoProvider.getModuleInfo(packageName, flags); } @Override public List getInstalledModules(int flags) { return mModuleInfoProvider.getInstalledModules(flags); } @Override public String[] getSystemSharedLibraryNames() { // allow instant applications synchronized (mPackages) { Set libs = null; final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { LongSparseArray versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } final int versionCount = versionedLib.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libraryInfo = versionedLib.valueAt(j); if (!libraryInfo.isStatic()) { if (libs == null) { libs = new ArraySet<>(); } libs.add(libraryInfo.getName()); break; } PackageSetting ps = mSettings.getPackageLPr(libraryInfo.getPackageName()); if (ps != null && !filterSharedLibPackageLPr(ps, Binder.getCallingUid(), UserHandle.getUserId(Binder.getCallingUid()), PackageManager.MATCH_STATIC_SHARED_LIBRARIES)) { if (libs == null) { libs = new ArraySet<>(); } libs.add(libraryInfo.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; } } @GuardedBy("mPackages") 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) { final CheckPermissionDelegate checkPermissionDelegate; synchronized (mPackages) { if (mCheckPermissionDelegate == null) { return checkPermissionImpl(permName, pkgName, userId); } checkPermissionDelegate = mCheckPermissionDelegate; } return checkPermissionDelegate.checkPermission(permName, pkgName, userId, PackageManagerService.this::checkPermissionImpl); } private int checkPermissionImpl(String permName, String pkgName, int userId) { return mPermissionManager.checkPermission(permName, pkgName, getCallingUid(), userId); } @Override public int checkUidPermission(String permName, int uid) { final CheckPermissionDelegate checkPermissionDelegate; synchronized (mPackages) { if (mCheckPermissionDelegate == null) { return checkUidPermissionImpl(permName, uid); } checkPermissionDelegate = mCheckPermissionDelegate; } return checkPermissionDelegate.checkUidPermission(permName, uid, PackageManagerService.this::checkUidPermissionImpl); } private int checkUidPermissionImpl(String permName, int uid) { synchronized (mPackages) { final String[] packageNames = getPackagesForUid(uid); PackageParser.Package pkg = null; final int N = packageNames == null ? 0 : packageNames.length; for (int i = 0; pkg == null && i < N; i++) { pkg = mPackages.get(packageNames[i]); } return mPermissionManager.checkUidPermission(permName, pkg, uid, getCallingUid()); } } @Override public boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId) { if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "isPermissionRevokedByPolicy for user " + userId); } if (checkPermission(permission, packageName, userId) == PackageManager.PERMISSION_GRANTED) { return false; } final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { if (!isCallerSameApp(packageName, callingUid)) { return false; } } else { if (isInstantApp(packageName, userId)) { return false; } } final long identity = Binder.clearCallingIdentity(); try { final int flags = getPermissionFlags(permission, packageName, userId); return (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0; } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getPermissionControllerPackageName() { synchronized (mPackages) { return mRequiredPermissionControllerPackage; } } String getPackageInstallerPackageName() { synchronized (mPackages) { return mRequiredInstallerPackage; } } private boolean addDynamicPermission(PermissionInfo info, final boolean async) { return mPermissionManager.addDynamicPermission( info, async, getCallingUid(), new PermissionCallback() { @Override public void onPermissionChanged() { if (!async) { mSettings.writeLPr(); } else { scheduleWriteSettingsLocked(); } } }); } @Override public boolean addPermission(PermissionInfo info) { synchronized (mPackages) { return addDynamicPermission(info, false); } } @Override public boolean addPermissionAsync(PermissionInfo info) { synchronized (mPackages) { return addDynamicPermission(info, true); } } @Override public void removePermission(String permName) { mPermissionManager.removeDynamicPermission(permName, getCallingUid(), mPermissionCallback); } @Override public void grantRuntimePermission(String packageName, String permName, final int userId) { boolean overridePolicy = (checkUidPermission( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED); mPermissionManager.grantRuntimePermission(permName, packageName, overridePolicy, getCallingUid(), userId, mPermissionCallback); } @Override public void revokeRuntimePermission(String packageName, String permName, int userId) { boolean overridePolicy = (checkUidPermission( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED); mPermissionManager.revokeRuntimePermission(permName, packageName, overridePolicy, userId, mPermissionCallback); } @Override public void resetRuntimePermissions() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, "revokeRuntimePermission"); int callingUid = Binder.getCallingUid(); if (callingUid != Process.SYSTEM_UID && callingUid != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "resetRuntimePermissions"); } synchronized (mPackages) { mPermissionManager.updateAllPermissions( StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(), mPermissionCallback); for (int userId : UserManagerService.getInstance().getUserIds()) { final int packageCount = mPackages.size(); for (int i = 0; i < packageCount; i++) { PackageParser.Package pkg = mPackages.valueAt(i); if (!(pkg.mExtras instanceof PackageSetting)) { continue; } PackageSetting ps = (PackageSetting) pkg.mExtras; resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, userId); } } } } @Override public int getPermissionFlags(String permName, String packageName, int userId) { return mPermissionManager.getPermissionFlags( permName, packageName, getCallingUid(), userId); } @Override public void updatePermissionFlags(String permName, String packageName, int flagMask, int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) { int callingUid = getCallingUid(); boolean overridePolicy = false; if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) { long callingIdentity = Binder.clearCallingIdentity(); try { if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0) { if (checkAdjustPolicyFlagPermission) { mContext.enforceCallingOrSelfPermission( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "Need " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " to change policy flags"); } else if (!hasTargetSdkInUidLowerThan(callingUid, Build.VERSION_CODES.Q)) { throw new IllegalArgumentException( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " needs " + " to be checked for packages targeting " + Build.VERSION_CODES.Q + " or later when changing policy " + "flags"); } overridePolicy = true; } } finally { Binder.restoreCallingIdentity(callingIdentity); } } mPermissionManager.updatePermissionFlags( permName, packageName, flagMask, flagValues, callingUid, userId, overridePolicy, mPermissionCallback); } /** * Update the permission flags for all packages and runtime permissions of a user in order * to allow device or profile owner to remove POLICY_FIXED. */ @Override public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) { synchronized (mPackages) { final boolean changed = mPermissionManager.updatePermissionFlagsForAllApps( flagMask, flagValues, getCallingUid(), userId, mPackages.values(), mPermissionCallback); if (changed) { mSettings.writeRuntimePermissionsForUserLPr(userId, false); } } } @Override public @Nullable List getWhitelistedRestrictedPermissions(@NonNull String packageName, @PermissionWhitelistFlags int whitelistFlags, @UserIdInt int userId) { Preconditions.checkNotNull(packageName); Preconditions.checkFlagsArgument(whitelistFlags, PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER); Preconditions.checkArgumentNonNegative(userId, null); if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS, "getWhitelistedRestrictedPermissions for user " + userId); } final PackageParser.Package pkg; synchronized (mPackages) { final PackageSetting packageSetting = mSettings.mPackages.get(packageName); if (packageSetting == null) { Slog.w(TAG, "Unknown package: " + packageName); return null; } pkg = packageSetting.pkg; final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) == PackageManager.PERMISSION_GRANTED; final PackageSetting installerPackageSetting = mSettings.mPackages.get( packageSetting.installerPackageName); final boolean isCallerInstallerOnRecord = installerPackageSetting != null && UserHandle.isSameApp(installerPackageSetting.appId, Binder.getCallingUid()); if ((whitelistFlags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 && !isCallerPrivileged) { throw new SecurityException("Querying system whitelist requires " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } if ((whitelistFlags & (PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) != 0) { if (!isCallerPrivileged && !isCallerInstallerOnRecord) { throw new SecurityException("Querying upgrade or installer whitelist" + " requires being installer on record or " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } } if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(), UserHandle.getCallingUserId())) { return null; } } final long identity = Binder.clearCallingIdentity(); try { return mPermissionManager.getWhitelistedRestrictedPermissions( pkg, whitelistFlags, userId); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean addWhitelistedRestrictedPermission(@NonNull String packageName, @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags, @UserIdInt int userId) { // Other argument checks are done in get/setWhitelistedRestrictedPermissions Preconditions.checkNotNull(permission); if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) { return false; } List permissions = getWhitelistedRestrictedPermissions(packageName, whitelistFlags, userId); if (permissions == null) { permissions = new ArrayList<>(1); } if (permissions.indexOf(permission) < 0) { permissions.add(permission); return setWhitelistedRestrictedPermissions(packageName, permissions, whitelistFlags, userId); } return false; } private boolean checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission( @NonNull String permission) { synchronized (mPackages) { final BasePermission bp = mPermissionManager.getPermissionTEMP(permission); if (bp == null) { Slog.w(TAG, "No such permissions: " + permission); return false; } if (bp.isHardOrSoftRestricted() && bp.isImmutablyRestricted() && mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Cannot modify whitelisting of an immutably " + "restricted permission: " + permission); } return true; } } @Override public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName, @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags, @UserIdInt int userId) { // Other argument checks are done in get/setWhitelistedRestrictedPermissions Preconditions.checkNotNull(permission); if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) { return false; } final List permissions = getWhitelistedRestrictedPermissions(packageName, whitelistFlags, userId); if (permissions != null && permissions.remove(permission)) { return setWhitelistedRestrictedPermissions(packageName, permissions, whitelistFlags, userId); } return false; } private boolean setWhitelistedRestrictedPermissions(@NonNull String packageName, @Nullable List permissions, @PermissionWhitelistFlags int whitelistFlag, @UserIdInt int userId) { Preconditions.checkNotNull(packageName); Preconditions.checkFlagsArgument(whitelistFlag, PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER); Preconditions.checkArgument(Integer.bitCount(whitelistFlag) == 1); Preconditions.checkArgumentNonNegative(userId, null); if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingOrSelfPermission( Manifest.permission.INTERACT_ACROSS_USERS, "setWhitelistedRestrictedPermissions for user " + userId); } final PackageParser.Package pkg; synchronized (mPackages) { final PackageSetting packageSetting = mSettings.mPackages.get(packageName); if (packageSetting == null) { Slog.w(TAG, "Unknown package: " + packageName); return false; } pkg = packageSetting.pkg; final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) == PackageManager.PERMISSION_GRANTED; final PackageSetting installerPackageSetting = mSettings.mPackages.get( packageSetting.installerPackageName); final boolean isCallerInstallerOnRecord = installerPackageSetting != null && UserHandle.isSameApp(installerPackageSetting.appId, Binder.getCallingUid()); if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 && !isCallerPrivileged) { throw new SecurityException("Modifying system whitelist requires " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) { if (!isCallerPrivileged && !isCallerInstallerOnRecord) { throw new SecurityException("Modifying upgrade whitelist requires" + " being installer on record or " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } final List whitelistedPermissions = getWhitelistedRestrictedPermissions( packageName, whitelistFlag, userId); if (permissions == null || permissions.isEmpty()) { if (whitelistedPermissions == null || whitelistedPermissions.isEmpty()) { return true; } } else { // Only the system can add and remove while the installer can only remove. final int permissionCount = permissions.size(); for (int i = 0; i < permissionCount; i++) { if ((whitelistedPermissions == null || !whitelistedPermissions.contains(permissions.get(i))) && !isCallerPrivileged) { throw new SecurityException("Adding to upgrade whitelist requires" + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } } } } if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) { if (!isCallerPrivileged && !isCallerInstallerOnRecord) { throw new SecurityException("Modifying installer whitelist requires" + " being installer on record or " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } } if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(), UserHandle.getCallingUserId())) { return false; } } final long identity = Binder.clearCallingIdentity(); try { mPermissionManager.setWhitelistedRestrictedPermissions(pkg, new int[]{userId}, permissions, Process.myUid(), whitelistFlag, mPermissionCallback); } finally { Binder.restoreCallingIdentity(identity); } return true; } @Override public boolean shouldShowRequestPermissionRationale(String permissionName, String packageName, int userId) { if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "canShowRequestPermissionRationale for user " + userId); } final int uid = getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId); if (UserHandle.getAppId(getCallingUid()) != UserHandle.getAppId(uid)) { return false; } if (checkPermission(permissionName, packageName, userId) == PackageManager.PERMISSION_GRANTED) { return false; } final int flags; final long identity = Binder.clearCallingIdentity(); try { flags = getPermissionFlags(permissionName, packageName, userId); } finally { Binder.restoreCallingIdentity(identity); } final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED | PackageManager.FLAG_PERMISSION_POLICY_FIXED | PackageManager.FLAG_PERMISSION_USER_FIXED; if ((flags & fixedFlags) != 0) { return false; } return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; } @Override public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { mContext.enforceCallingOrSelfPermission( Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS, "addOnPermissionsChangeListener"); synchronized (mPackages) { mOnPermissionChangeListeners.addListenerLocked(listener); } } @Override public void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { throw new SecurityException("Instant applications don't have access to this method"); } synchronized (mPackages) { mOnPermissionChangeListeners.removeListenerLocked(listener); } } @Override public boolean isProtectedBroadcast(String actionName) { // allow instant applications synchronized (mProtectedBroadcasts) { if (mProtectedBroadcasts.contains(actionName)) { return true; } else if (actionName != null) { // TODO: remove these terrible hacks if (actionName.startsWith("android.net.netmon.lingerExpired") || actionName.startsWith("com.android.server.sip.SipWakeupTimer") || actionName.startsWith("com.android.internal.telephony.data-reconnect") || actionName.startsWith("android.net.netmon.launchCaptivePortalApp")) { return true; } } } return false; } @Override public int checkSignatures(String pkg1, String pkg2) { synchronized (mPackages) { final PackageParser.Package p1 = mPackages.get(pkg1); final PackageParser.Package p2 = mPackages.get(pkg2); if (p1 == null || p1.mExtras == null || p2 == null || p2.mExtras == null) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps1 = (PackageSetting) p1.mExtras; final PackageSetting ps2 = (PackageSetting) p2.mExtras; if (filterAppAccessLPr(ps1, callingUid, callingUserId) || filterAppAccessLPr(ps2, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } return compareSignatures(p1.mSigningDetails.signatures, p2.mSigningDetails.signatures); } } @Override public int checkUidSignatures(int uid1, int uid2) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; // Map to base uids. final int appId1 = UserHandle.getAppId(uid1); final int appId2 = UserHandle.getAppId(uid2); // reader synchronized (mPackages) { Signature[] s1; Signature[] s2; Object obj = mSettings.getSettingLPr(appId1); if (obj != null) { if (obj instanceof SharedUserSetting) { if (isCallerInstantApp) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s1 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s1 = ps.signatures.mSigningDetails.signatures; } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } obj = mSettings.getSettingLPr(appId2); if (obj != null) { if (obj instanceof SharedUserSetting) { if (isCallerInstantApp) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s2 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s2 = ps.signatures.mSigningDetails.signatures; } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } return compareSignatures(s1, s2); } } @Override public boolean hasSigningCertificate( String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) { synchronized (mPackages) { final PackageParser.Package p = mPackages.get(packageName); if (p == null || p.mExtras == null) { return false; } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return false; } switch (type) { case CERT_INPUT_RAW_X509: return p.mSigningDetails.hasCertificate(certificate); case CERT_INPUT_SHA256: return p.mSigningDetails.hasSha256Certificate(certificate); default: return false; } } } @Override public boolean hasUidSigningCertificate( int uid, byte[] certificate, @PackageManager.CertificateInputType int type) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); // Map to base uids. final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final PackageParser.SigningDetails signingDetails; final Object obj = mSettings.getSettingLPr(appId); if (obj != null) { if (obj instanceof SharedUserSetting) { final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; if (isCallerInstantApp) { return false; } signingDetails = ((SharedUserSetting)obj).signatures.mSigningDetails; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return false; } signingDetails = ps.signatures.mSigningDetails; } else { return false; } } else { return false; } switch (type) { case CERT_INPUT_RAW_X509: return signingDetails.hasCertificate(certificate); case CERT_INPUT_SHA256: return signingDetails.hasSha256Certificate(certificate); default: return false; } } } /** * This method should typically only be used when granting or revoking * permissions, since the app may immediately restart after this call. * * If you're doing surgery on app code/data, use {@link PackageFreezer} to * guard your work against the app being relaunched. */ private void killUid(int appId, int userId, String reason) { final long identity = Binder.clearCallingIdentity(); try { IActivityManager am = ActivityManager.getService(); if (am != null) { try { am.killUid(appId, userId, reason); } catch (RemoteException e) { /* ignore - same process */ } } } finally { Binder.restoreCallingIdentity(identity); } } /** * If the database version for this type of package (internal storage or * external storage) is less than the version where package signatures * were updated, return true. */ private boolean isCompatSignatureUpdateNeeded(PackageParser.Package scannedPkg) { return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg)); } private static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) { return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY; } private boolean isRecoverSignatureUpdateNeeded(PackageParser.Package scannedPkg) { return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg)); } private static boolean isRecoverSignatureUpdateNeeded(VersionInfo ver) { return ver.databaseVersion < DatabaseVersion.SIGNATURE_MALFORMED_RECOVER; } @Override public List getAllPackages() { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); synchronized (mPackages) { if (canViewInstantApps(callingUid, callingUserId)) { return new ArrayList<>(mPackages.keySet()); } final String instantAppPkgName = getInstantAppPackageName(callingUid); final List result = new ArrayList<>(); if (instantAppPkgName != null) { // caller is an instant application; filter unexposed applications for (PackageParser.Package pkg : mPackages.values()) { if (!pkg.visibleToInstantApps) { continue; } result.add(pkg.packageName); } } else { // caller is a normal application; filter instant applications for (PackageParser.Package pkg : mPackages.values()) { final PackageSetting ps = pkg.mExtras != null ? (PackageSetting) pkg.mExtras : null; if (ps != null && ps.getInstantApp(callingUserId) && !mInstantAppRegistry.isInstantAccessGranted( callingUserId, UserHandle.getAppId(callingUid), ps.appId)) { continue; } result.add(pkg.packageName); } } return result; } } /** * IMPORTANT: Not all packages returned by this method may be known * to the system. There are two conditions in which this may occur: * * The package is on adoptable storage and the device has been removed * The package is being removed and the internal structures are partially updated * * The second is an artifact of the current data structures and should be fixed. See * b/111075456 for one such instance. */ @Override public String[] getPackagesForUid(int uid) { final int callingUid = Binder.getCallingUid(); final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; final int userId = UserHandle.getUserId(uid); final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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 appId = UserHandle.getAppId(uids[i]); final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; final Iterator it = sus.packages.iterator(); while (it.hasNext()) { if (it.next().isPrivileged()) { return true; } } } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; return ps.isPrivileged(); } } return false; } @Override public String[] getAppOpPermissionPackages(String permName) { return mPermissionManager.getAppOpPermissionPackages(permName); } @Override public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { return resolveIntentInternal(intent, resolvedType, flags, userId, false, Binder.getCallingUid()); } /** * Normally instant apps can only be resolved when they're visible to the caller. * However, if {@code resolveForStart} is {@code true}, all instant apps are visible * since we need to allow the system to start any installed application. */ private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType, int flags, int userId, boolean resolveForStart, int filterCallingUid) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent"); if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "resolve intent"); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, filterCallingUid, userId, resolveForStart, true /*allowDynamicSplits*/); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); final ResolveInfo bestChoice = chooseBestActivity(intent, resolvedType, flags, query, userId); return bestChoice; } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @Override public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) { if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) { throw new SecurityException( "findPersistentPreferredActivity can only be run by the system"); } if (!sUserManager.exists(userId)) { return null; } final int callingUid = Binder.getCallingUid(); intent = updateIntentForResolve(intent); final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver()); final int flags = updateFlagsForResolve( 0, userId, intent, callingUid, false /*includeInstantApps*/); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); synchronized (mPackages) { return findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false, userId); } } @Override public void setLastChosenActivity(Intent intent, String resolvedType, int flags, IntentFilter filter, int match, ComponentName activity) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; } final int userId = UserHandle.getCallingUserId(); if (DEBUG_PREFERRED) { Log.v(TAG, "setLastChosenActivity intent=" + intent + " resolvedType=" + resolvedType + " flags=" + flags + " filter=" + filter + " match=" + match + " activity=" + activity); filter.dump(new PrintStreamPrinter(System.out), " "); } intent.setComponent(null); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); // Find any earlier preferred or last chosen entries and nuke them findPreferredActivityNotLocked( 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 findPreferredActivityNotLocked( intent, resolvedType, flags, query, 0, false, false, false, userId); } /** * Returns whether or not instant apps have been disabled remotely. */ private boolean areWebInstantAppsDisabled(int userId) { return mWebInstantAppsDisabled.get(userId); } private boolean isInstantAppResolutionAllowed( Intent intent, List resolvedActivities, int userId, boolean skipPackageCheck) { if (mInstantAppResolverConnection == null) { return false; } if (mInstantAppInstallerActivity == null) { return false; } if (intent.getComponent() != null) { return false; } if ((intent.getFlags() & Intent.FLAG_IGNORE_EPHEMERAL) != 0) { return false; } if (!skipPackageCheck && intent.getPackage() != null) { return false; } if (!intent.isWebIntent()) { // for non web intents, we should not resolve externally if an app already exists to // handle it or if the caller didn't explicitly request it. if ((resolvedActivities != null && resolvedActivities.size() != 0) || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) { return false; } } else { if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) { return false; } else if (areWebInstantAppsDisabled(userId)) { return false; } } // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. // Or if there's already an ephemeral app installed that handles the action synchronized (mPackages) { final int count = (resolvedActivities == null ? 0 : resolvedActivities.size()); for (int n = 0; n < count; n++) { final ResolveInfo info = resolvedActivities.get(n); final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { // only check domain verification status if the app is not a browser if (!info.handleAllWebDataURI) { // Try to get the status from User settings first final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int) (packedStatus >> 32); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName + ", status: " + status); } return false; } } if (ps.getInstantApp(userId)) { if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app installed;" + " pkg: " + packageName); } return false; } } } } // We've exhausted all ways to deny ephemeral application; let the system look for them. return true; } private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj, Intent origIntent, String resolvedType, String callingPackage, Bundle verificationBundle, int userId) { final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO, new InstantAppRequest(responseObj, origIntent, resolvedType, callingPackage, userId, verificationBundle, false /*resolveForStart*/)); mHandler.sendMessage(msg); } private ResolveInfo chooseBestActivity(Intent intent, String resolvedType, int flags, List query, int userId) { if (query != null) { final int N = query.size(); if (N == 1) { return query.get(0); } else if (N > 1) { final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); // If there is more than one activity with the same priority, // then let the user decide between them. ResolveInfo r0 = query.get(0); ResolveInfo r1 = query.get(1); if (DEBUG_INTENT_MATCHING || debug) { Slog.v(TAG, r0.activityInfo.name + "=" + r0.priority + " vs " + r1.activityInfo.name + "=" + r1.priority); } // If the first activity has a higher priority, or a different // default, then it is always desirable to pick it. if (r0.priority != r1.priority || r0.preferredOrder != r1.preferredOrder || r0.isDefault != r1.isDefault) { return query.get(0); } // If we have saved a preference for a preferred activity for // this Intent, use that. ResolveInfo ri = findPreferredActivityNotLocked(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; } @GuardedBy("mPackages") 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; jmust not hold {@link #mPackages} */ ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags, List query, int priority, boolean always, boolean removeMatches, boolean debug, int userId) { if (Thread.holdsLock(mPackages)) { Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding mPackages", new Throwable()); } if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); // Do NOT hold the packages lock; this calls up into the settings provider which // could cause a deadlock. final boolean isDeviceProvisioned = android.provider.Settings.Global.getInt(mContext.getContentResolver(), android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1; 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"); } } final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent) && !isDeviceProvisioned; if (ai == null) { // Do not remove launcher's preferred activity during SetupWizard // due to it may not install yet if (excludeSetupWizardHomeActivity) { continue; } // This previously registered preferred activity // component is no longer known. Most likely an update // to the app was installed and in the new version this // component no longer exists. Clean it up by removing // it from the preferred activities list, and skip it. Slog.w(TAG, "Removing dangling preferred activity: " + pa.mPref.mComponent); pir.removeFilter(pa); changed = true; continue; } for (int j=0; j matches = getMatchingCrossProfileIntentFilters(intent, resolvedType, sourceUserId); if (matches != null) { int size = matches.size(); for (int i = 0; i < size; i++) { if (matches.get(i).getTargetUserId() == targetUserId) return true; } } if (intent.hasWebURI()) { // cross-profile app linking works only towards the parent. final int callingUid = Binder.getCallingUid(); final UserInfo parent = getProfileParent(sourceUserId); synchronized(mPackages) { int flags = updateFlagsForResolve(0, parent.id, intent, callingUid, false /*includeInstantApps*/); CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr( intent, resolvedType, flags, sourceUserId, parent.id); return xpDomainInfo != null; } } return false; } private UserInfo getProfileParent(int userId) { final long identity = Binder.clearCallingIdentity(); try { return sUserManager.getProfileParent(userId); } finally { Binder.restoreCallingIdentity(identity); } } private List getMatchingCrossProfileIntentFilters(Intent intent, String resolvedType, int userId) { CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId); if (resolver != null) { return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId); } return null; } @Override public @NonNull ParceledListSlice queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); return new ParceledListSlice<>( queryIntentActivitiesInternal(intent, resolvedType, flags, userId)); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } /** * Returns the package name of the calling Uid if it's an instant app. If it isn't * instant, returns {@code null}. */ private String getInstantAppPackageName(int callingUid) { synchronized (mPackages) { // If the caller is an isolated app use the owner's uid for the lookup. if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final int appId = UserHandle.getAppId(callingUid); final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid)); return isInstantApp ? ps.pkg.packageName : null; } } return null; } private @NonNull List queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int userId) { return queryIntentActivitiesInternal( intent, resolvedType, flags, Binder.getCallingUid(), userId, false /*resolveForStart*/, true /*allowDynamicSplits*/); } private @NonNull List queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final String instantAppPkgName = getInstantAppPackageName(filterCallingUid); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "query intent activities"); final String pkgName = intent.getPackage(); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart, comp != null || pkgName != null /*onlyExposedExplicitly*/); if (comp != null) { final List list = new ArrayList<>(1); final ActivityInfo ai = getActivityInfo(comp, flags, userId); if (ai != null) { // When specifying an explicit component, we prevent the activity from being // used when either 1) the calling package is normal and the activity is within // an ephemeral application or 2) the calling package is ephemeral and the // activity is not visible to ephemeral applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean matchExplicitlyVisibleOnly = (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (ai.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetVisibleToInstantApp = (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean isTargetExplicitlyVisibleToInstantApp = isTargetVisibleToInstantApp && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; final boolean isTargetHiddenFromInstantApp = !isTargetVisibleToInstantApp || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp); final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } } return applyPostResolutionFilter( list, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // reader boolean sortResult = false; boolean addInstant = false; List result; synchronized (mPackages) { if (pkgName == null) { List matchingFilters = getMatchingCrossProfileIntentFilters(intent, resolvedType, userId); // Check for results that need to skip the current profile. ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent, resolvedType, flags, userId); if (xpResolveInfo != null) { List xpResult = new ArrayList<>(1); xpResult.add(xpResolveInfo); return applyPostResolutionFilter( filterIfNotSystemUser(xpResult, userId), instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // Check for results in the current profile. result = filterIfNotSystemUser(mComponentResolver.queryActivities( intent, resolvedType, flags, userId), userId); addInstant = isInstantAppResolutionAllowed(intent, result, userId, false /*skipPackageCheck*/); // Check for cross profile results. boolean hasNonNegativePriorityResult = hasNonNegativePriority(result); xpResolveInfo = queryCrossProfileIntents( matchingFilters, intent, resolvedType, flags, userId, hasNonNegativePriorityResult); if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) { boolean isVisibleToUser = filterIfNotSystemUser( Collections.singletonList(xpResolveInfo), userId).size() > 0; if (isVisibleToUser) { result.add(xpResolveInfo); sortResult = true; } } if (intent.hasWebURI()) { CrossProfileDomainInfo xpDomainInfo = null; final UserInfo parent = getProfileParent(userId); if (parent != null) { xpDomainInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType, flags, userId, parent.id); } if (xpDomainInfo != null) { if (xpResolveInfo != null) { // If we didn't remove it, the cross-profile ResolveInfo would be twice // in the result. result.remove(xpResolveInfo); } if (result.size() == 0 && !addInstant) { // No result in current profile, but found candidate in parent user. // And we are not going to add emphemeral app, so we can return the // result straight away. result.add(xpDomainInfo.resolveInfo); return applyPostResolutionFilter(result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } } else if (result.size() <= 1 && !addInstant) { // No result in parent user and <= 1 result in current profile, and we // are not going to add emphemeral app, so we can return the result without // further processing. return applyPostResolutionFilter(result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // We have more than one candidate (combining results from current and parent // profile), so we need filtering and sorting. result = filterCandidatesWithDomainPreferredActivitiesLPr( intent, flags, result, xpDomainInfo, userId); sortResult = true; } } else { final PackageParser.Package pkg = mPackages.get(pkgName); result = null; if (pkg != null) { result = filterIfNotSystemUser(mComponentResolver.queryActivities( intent, resolvedType, flags, pkg.activities, userId), userId); } if (result == null || result.size() == 0) { // the caller wants to resolve for a particular package; however, there // were no installed results, so, try to find an ephemeral result addInstant = isInstantAppResolutionAllowed( intent, null /*result*/, userId, true /*skipPackageCheck*/); if (result == null) { result = new ArrayList<>(); } } } } if (addInstant) { result = maybeAddInstantAppInstaller( result, intent, resolvedType, flags, userId, resolveForStart); } if (sortResult) { Collections.sort(result, RESOLVE_PRIORITY_SORTER); } return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } private List maybeAddInstantAppInstaller(List result, Intent intent, String resolvedType, int flags, int userId, boolean resolveForStart) { // first, check to see if we've got an instant app already installed final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0; ResolveInfo localInstantApp = null; boolean blockResolution = false; if (!alreadyResolvedLocally) { final List instantApps = mComponentResolver.queryActivities( intent, resolvedType, flags | PackageManager.GET_RESOLVED_FILTER | PackageManager.MATCH_INSTANT | PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY, userId); for (int i = instantApps.size() - 1; i >= 0; --i) { final ResolveInfo info = instantApps.get(i); final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps.getInstantApp(userId)) { final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int)(packedStatus >> 32); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { // there's a local instant application installed, but, the user has // chosen to never use it; skip resolution and don't acknowledge // an instant application is even available if (DEBUG_INSTANT) { Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName); } blockResolution = true; break; } else { // we have a locally installed instant application; skip resolution // but acknowledge there's an instant application available if (DEBUG_INSTANT) { Slog.v(TAG, "Found installed instant app; pkg: " + packageName); } localInstantApp = info; break; } } } } // no app installed, let's see if one's available AuxiliaryResolveInfo auxiliaryResponse = null; if (!blockResolution) { if (localInstantApp == null) { // we don't have an instant app locally, resolve externally Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral"); final InstantAppRequest requestObject = new InstantAppRequest( null /*responseObj*/, intent /*origIntent*/, resolvedType, null /*callingPackage*/, userId, null /*verificationBundle*/, resolveForStart); auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne( mInstantAppResolverConnection, requestObject); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } else { // we have an instant application locally, but, we can't admit that since // callers shouldn't be able to determine prior browsing. create a dummy // auxiliary response so the downstream code behaves as if there's an // instant application available externally. when it comes time to start // the instant application, we'll do the right thing. final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo; auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */, ai.packageName, ai.longVersionCode, null /* splitName */); } } if (intent.isWebIntent() && auxiliaryResponse == null) { return result; } final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName); if (ps == null || !ps.readUserState(userId).isEnabled(mInstantAppInstallerActivity, 0)) { return result; } final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo); ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo( mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId); ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART | IntentFilter.MATCH_ADJUSTMENT_NORMAL; // add a non-generic filter ephemeralInstaller.filter = new IntentFilter(); if (intent.getAction() != null) { ephemeralInstaller.filter.addAction(intent.getAction()); } if (intent.getData() != null && intent.getData().getPath() != null) { ephemeralInstaller.filter.addDataPath( intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL); } ephemeralInstaller.isInstantAppAvailable = true; // make sure this resolver is the default ephemeralInstaller.isDefault = true; ephemeralInstaller.auxiliaryInfo = auxiliaryResponse; if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } result.add(ephemeralInstaller); return result; } private static class CrossProfileDomainInfo { /* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */ ResolveInfo resolveInfo; /* Best domain verification status of the activities found in the other profile */ int bestDomainVerificationStatus; } private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType, int flags, int sourceUserId, int parentUserId) { if (!sUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, sourceUserId)) { return null; } List resultTargetUser = mComponentResolver.queryActivities(intent, resolvedType, flags, parentUserId); if (resultTargetUser == null || resultTargetUser.isEmpty()) { return null; } CrossProfileDomainInfo result = null; int size = resultTargetUser.size(); for (int i = 0; i < size; i++) { ResolveInfo riTargetUser = resultTargetUser.get(i); // Intent filter verification is only for filters that specify a host. So don't return // those that handle all web uris. if (riTargetUser.handleAllWebDataURI) { continue; } String packageName = riTargetUser.activityInfo.packageName; PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) { continue; } long verificationState = getDomainVerificationStatusLPr(ps, parentUserId); int status = (int)(verificationState >> 32); if (result == null) { result = new CrossProfileDomainInfo(); result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(), sourceUserId, parentUserId); result.bestDomainVerificationStatus = status; } else { result.bestDomainVerificationStatus = bestDomainVerificationStatus(status, result.bestDomainVerificationStatus); } } // Don't consider matches with status NEVER across profiles. if (result != null && result.bestDomainVerificationStatus == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return null; } return result; } /** * Verification statuses are ordered from the worse to the best, except for * INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse. */ private int bestDomainVerificationStatus(int status1, int status2) { if (status1 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return status2; } if (status2 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return status1; } return (int) MathUtils.max(status1, status2); } private boolean isUserEnabled(int userId) { long callingId = Binder.clearCallingIdentity(); try { UserInfo userInfo = sUserManager.getUserInfo(userId); return userInfo != null && userInfo.isEnabled(); } finally { Binder.restoreCallingIdentity(callingId); } } /** * Filter out activities with systemUserOnly flag set, when current user is not System. * * @return filtered list */ private List filterIfNotSystemUser(List resolveInfos, int userId) { if (userId == UserHandle.USER_SYSTEM) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { ResolveInfo info = resolveInfos.get(i); if ((info.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) { resolveInfos.remove(i); } } return resolveInfos; } /** * Filters out ephemeral activities. * When resolving for an ephemeral app, only activities that 1) are defined in the * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned. * * @param resolveInfos The pre-filtered list of resolved activities * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering * is performed. * @param intent * @return A filtered list of resolved activities. */ private List applyPostResolutionFilter(List resolveInfos, String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, boolean resolveForStart, int userId, Intent intent) { final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId); for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); // remove locally resolved instant app web results when disabled if (info.isInstantAppAvailable && blockInstant) { resolveInfos.remove(i); continue; } // allow activities that are defined in the provided package if (allowDynamicSplits && info.activityInfo != null && info.activityInfo.splitName != null && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames, info.activityInfo.splitName)) { if (mInstantAppInstallerActivity == null) { if (DEBUG_INSTALL) { Slog.v(TAG, "No installer - not adding it to the ResolveInfo list"); } resolveInfos.remove(i); continue; } if (blockInstant && isInstantApp(info.activityInfo.packageName, userId)) { resolveInfos.remove(i); continue; } // requested activity is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTALL) { Slog.v(TAG, "Adding installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); final ComponentName installFailureActivity = findInstallFailureActivity( info.activityInfo.packageName, filterCallingUid, userId); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( installFailureActivity, info.activityInfo.packageName, info.activityInfo.applicationInfo.longVersionCode, info.activityInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // This resolve info may appear in the chooser UI, so let us make it // look as the one it replaces as far as the user is concerned which // requires loading the correct label and icon for the resolve info. installerInfo.resolvePackageName = info.getComponentInfo().packageName; installerInfo.labelRes = info.resolveLabelResId(); installerInfo.icon = info.resolveIconResId(); installerInfo.isInstantAppAvailable = true; resolveInfos.set(i, installerInfo); continue; } // caller is a full app, don't need to apply any other filtering if (ephemeralPkgName == null) { continue; } else if (ephemeralPkgName.equals(info.activityInfo.packageName)) { // caller is same app; don't need to apply any other filtering continue; } else if (resolveForStart && (intent.isWebIntent() || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) && intent.getPackage() == null && intent.getComponent() == null) { // ephemeral apps can launch other ephemeral apps indirectly continue; } // allow activities that have been explicitly exposed to ephemeral apps final boolean isEphemeralApp = info.activityInfo.applicationInfo.isInstantApp(); if (!isEphemeralApp && ((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } /** * Returns the activity component that can handle install failures. * By default, the instant application installer handles failures. However, an * application may want to handle failures on its own. Applications do this by * creating an activity with an intent filter that handles the action * {@link Intent#ACTION_INSTALL_FAILURE}. */ private @Nullable ComponentName findInstallFailureActivity( String packageName, int filterCallingUid, int userId) { final Intent failureActivityIntent = new Intent(Intent.ACTION_INSTALL_FAILURE); failureActivityIntent.setPackage(packageName); // IMPORTANT: disallow dynamic splits to avoid an infinite loop final List result = queryIntentActivitiesInternal( failureActivityIntent, null /*resolvedType*/, 0 /*flags*/, filterCallingUid, userId, false /*resolveForStart*/, false /*allowDynamicSplits*/); final int NR = result.size(); if (NR > 0) { for (int i = 0; i < NR; i++) { final ResolveInfo info = result.get(i); if (info.activityInfo.splitName != null) { continue; } return new ComponentName(packageName, info.activityInfo.name); } } return null; } /** * @param resolveInfos list of resolve infos in descending priority order * @return if the list contains a resolve info with non-negative priority */ private boolean hasNonNegativePriority(List resolveInfos) { return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0; } private List filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent, int matchFlags, List candidates, CrossProfileDomainInfo xpDomainInfo, int userId) { final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0; if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) { Slog.v(TAG, "Filtering results with preferred activities. Candidates count: " + candidates.size()); } final ArrayList result = new ArrayList<>(); final ArrayList alwaysList = new ArrayList<>(); final ArrayList undefinedList = new ArrayList<>(); final ArrayList alwaysAskList = new ArrayList<>(); final ArrayList neverList = new ArrayList<>(); final 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 = mComponentResolver.queryActivities(intent, resolvedType, flags, targetUserId); if (resultTargetUser != null && isUserEnabled(targetUserId)) { // If all the matches in the target profile are suspended, return null. for (int i = resultTargetUser.size() - 1; i >= 0; i--) { if ((resultTargetUser.get(i).activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SUSPENDED) == 0) { return createForwardingResolveInfoUnchecked(filter, sourceUserId, targetUserId); } } } return null; } private ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter, int sourceUserId, int targetUserId) { ResolveInfo forwardingResolveInfo = new ResolveInfo(); long ident = Binder.clearCallingIdentity(); boolean targetIsProfile; try { targetIsProfile = sUserManager.getUserInfo(targetUserId).isManagedProfile(); } finally { Binder.restoreCallingIdentity(ident); } String className; if (targetIsProfile) { className = FORWARD_INTENT_TO_MANAGED_PROFILE; } else { className = FORWARD_INTENT_TO_PARENT; } ComponentName forwardingActivityComponentName = new ComponentName( mAndroidApplication.packageName, className); ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0, sourceUserId); if (!targetIsProfile) { forwardingActivityInfo.showUserIcon = targetUserId; forwardingResolveInfo.noResourceId = true; } forwardingResolveInfo.activityInfo = forwardingActivityInfo; forwardingResolveInfo.priority = 0; forwardingResolveInfo.preferredOrder = 0; forwardingResolveInfo.match = 0; forwardingResolveInfo.isDefault = true; forwardingResolveInfo.filter = filter; forwardingResolveInfo.targetUserId = targetUserId; return forwardingResolveInfo; } @Override public @NonNull ParceledListSlice queryIntentActivityOptions(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>(queryIntentActivityOptionsInternal(caller, specifics, specificTypes, intent, resolvedType, flags, userId)); } private @NonNull List queryIntentActivityOptionsInternal(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent activity options"); final String resultsAction = intent.getAction(); final List results = queryIntentActivitiesInternal(intent, resolvedType, flags | PackageManager.GET_RESOLVED_FILTER, userId); if (DEBUG_INTENT_MATCHING) { Log.v(TAG, "Query " + intent + ": " + results); } int specificsPos = 0; int N; // todo: note that the algorithm used here is O(N^2). This // isn't a problem in our current environment, but if we start running // into situations where we have more than 5 or 10 matches then this // should probably be changed to something smarter... // First we go through and resolve each of the specific items // that were supplied, taking care of removing any corresponding // duplicate items in the generic resolve list. if (specifics != null) { for (int i=0; i it = rii.filter.actionsIterator(); if (it == null) { continue; } while (it.hasNext()) { final String action = it.next(); if (resultsAction != null && resultsAction.equals(action)) { // If this action was explicitly requested, then don't // remove things that have it. continue; } for (int j=i+1; j queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>( queryIntentReceiversInternal(intent, resolvedType, flags, userId, false /*allowDynamicSplits*/)); } private @NonNull List queryIntentReceiversInternal(Intent intent, String resolvedType, int flags, int userId, boolean allowDynamicSplits) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ActivityInfo ai = getReceiverInfo(comp, flags, userId); if (ai != null) { // When specifying an explicit component, we prevent the activity from being // used when either 1) the calling package is normal and the activity is within // an instant application or 2) the calling package is ephemeral and the // activity is not visible to instant applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean matchExplicitlyVisibleOnly = (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (ai.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetVisibleToInstantApp = (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean isTargetExplicitlyVisibleToInstantApp = isTargetVisibleToInstantApp && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; final boolean isTargetHiddenFromInstantApp = !isTargetVisibleToInstantApp || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp); final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } } return applyPostResolutionFilter( list, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { final List result = mComponentResolver.queryReceivers(intent, resolvedType, flags, userId); return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { final List result = mComponentResolver.queryReceivers( intent, resolvedType, flags, pkg.receivers, userId); return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } return Collections.emptyList(); } } @Override public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) { final int callingUid = Binder.getCallingUid(); return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid); } private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForResolve( flags, userId, intent, callingUid, false /*includeInstantApps*/); List query = queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/); if (query != null) { if (query.size() >= 1) { // If there is more than one service with the same priority, // just arbitrarily pick the first one. return query.get(0); } } return null; } @Override public @NonNull ParceledListSlice queryIntentServices(Intent intent, String resolvedType, int flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>(queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/)); } private @NonNull List queryIntentServicesInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps) { if (!sUserManager.exists(userId)) return Collections.emptyList(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ServiceInfo si = getServiceInfo(comp, flags, userId); if (si != null) { // When specifying an explicit component, we prevent the service from being // used when either 1) the service is in an instant application and the // caller is not the same instant application or 2) the calling package is // ephemeral and the activity is not visible to ephemeral applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (si.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetHiddenFromInstantApp = (si.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0; final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.serviceInfo = si; list.add(ri); } } return list; } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostServiceResolutionFilter( mComponentResolver.queryServices(intent, resolvedType, flags, userId), instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return applyPostServiceResolutionFilter( mComponentResolver.queryServices(intent, resolvedType, flags, pkg.services, userId), instantAppPkgName); } return Collections.emptyList(); } } private List applyPostServiceResolutionFilter(List resolveInfos, String instantAppPkgName) { if (instantAppPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp(); // allow services that are defined in the provided package if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) { if (info.serviceInfo.splitName != null && !ArrayUtils.contains(info.serviceInfo.applicationInfo.splitNames, info.serviceInfo.splitName)) { // requested service is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( null /* installFailureActivity */, info.serviceInfo.packageName, info.serviceInfo.applicationInfo.longVersionCode, info.serviceInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // load resources from the correct package installerInfo.resolvePackageName = info.getComponentInfo().packageName; resolveInfos.set(i, installerInfo); } continue; } // allow services that have been explicitly exposed to ephemeral apps if (!isEphemeralApp && ((info.serviceInfo.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } @Override public @NonNull ParceledListSlice queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>( queryIntentContentProvidersInternal(intent, resolvedType, flags, userId)); } private @NonNull List queryIntentContentProvidersInternal( Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ProviderInfo pi = getProviderInfo(comp, flags, userId); if (pi != null) { // When specifying an explicit component, we prevent the provider from being // used when either 1) the provider is in an instant application and the // caller is not the same instant application or 2) the calling package is an // instant application and the provider is not visible to instant applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (pi.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetHiddenFromInstantApp = (pi.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0; final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.providerInfo = pi; list.add(ri); } } return list; } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostContentProviderResolutionFilter( mComponentResolver.queryProviders(intent, resolvedType, flags, userId), instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return applyPostContentProviderResolutionFilter( mComponentResolver.queryProviders(intent, resolvedType, flags, pkg.providers, userId), instantAppPkgName); } return Collections.emptyList(); } } private List applyPostContentProviderResolutionFilter( List resolveInfos, String instantAppPkgName) { if (instantAppPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.providerInfo.applicationInfo.isInstantApp(); // allow providers that are defined in the provided package if (isEphemeralApp && instantAppPkgName.equals(info.providerInfo.packageName)) { if (info.providerInfo.splitName != null && !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames, info.providerInfo.splitName)) { // requested provider is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( null /*failureActivity*/, info.providerInfo.packageName, info.providerInfo.applicationInfo.longVersionCode, info.providerInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // load resources from the correct package installerInfo.resolvePackageName = info.getComponentInfo().packageName; resolveInfos.set(i, installerInfo); } continue; } // allow providers that have been explicitly exposed to instant applications if (!isEphemeralApp && ((info.providerInfo.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } @Override public ParceledListSlice getInstalledPackages(int flags, int userId) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return ParceledListSlice.emptyList(); } if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; final boolean listApex = (flags & MATCH_APEX) != 0; final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0; mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed packages"); // writer synchronized (mPackages) { ArrayList list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo(ps, flags, userId); if (pi != null) { list.add(pi); } } } else { list = new ArrayList<>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { final PackageSetting ps = (PackageSetting) p.mExtras; if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo((PackageSetting) p.mExtras, flags, userId); if (pi != null) { list.add(pi); } } } if (listApex) { if (listFactory) { list.addAll(mApexManager.getFactoryPackages()); } else { list.addAll(mApexManager.getActivePackages()); } if (listUninstalled) { list.addAll(mApexManager.getInactivePackages()); } } return new ParceledListSlice<>(list); } } private void addPackageHoldingPermissions(ArrayList list, PackageSetting ps, String[] permissions, boolean[] tmp, int flags, int userId) { int numMatch = 0; final PermissionsState permissionsState = ps.getPermissionsState(); for (int i=0; i getPackagesHoldingPermissions( String[] permissions, int flags, int userId) { if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, permissions); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "get packages holding permissions"); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; // writer synchronized (mPackages) { ArrayList list = new ArrayList<>(); boolean[] tmpBools = new boolean[permissions.length]; if (listUninstalled) { for (PackageSetting ps : mSettings.mPackages.values()) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } } else { for (PackageParser.Package pkg : mPackages.values()) { PackageSetting ps = (PackageSetting)pkg.mExtras; if (ps != null) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } } } return new ParceledListSlice<>(list); } } @Override public ParceledListSlice getInstalledApplications(int flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>( getInstalledApplicationsListInternal(flags, userId, callingUid)); } private List getInstalledApplicationsListInternal(int flags, int userId, int callingUid) { if (getInstantAppPackageName(callingUid) != null) { return Collections.emptyList(); } if (!sUserManager.exists(userId)) return Collections.emptyList(); flags = updateFlagsForApplication(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; mPermissionManager.enforceCrossUserPermission( callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed application info"); // writer synchronized (mPackages) { ArrayList list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { ApplicationInfo ai; int effectiveFlags = flags; if (ps.isSystem()) { effectiveFlags |= PackageManager.MATCH_ANY_USER; } if (ps.pkg != null) { if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(ps.pkg); } } else { // Shared lib filtering done in generateApplicationInfoFromSettingsLPw // and already converts to externally visible package name ai = generateApplicationInfoFromSettingsLPw(ps.name, callingUid, effectiveFlags, userId); } if (ai != null) { list.add(ai); } } } else { list = new ArrayList<>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { if (p.mExtras != null) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(p); list.add(ai); } } } } return list; } } @Override public ParceledListSlice getInstantApps(int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } if (!canViewInstantApps(Binder.getCallingUid(), userId)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getEphemeralApplications"); } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getEphemeralApplications"); synchronized (mPackages) { List instantApps = mInstantAppRegistry .getInstantAppsLPr(userId); if (instantApps != null) { return new ParceledListSlice<>(instantApps); } } return null; } @Override public boolean isInstantApp(String packageName, int userId) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "isInstantApp"); if (HIDE_EPHEMERAL_APIS) { return false; } synchronized (mPackages) { int callingUid = Binder.getCallingUid(); if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final PackageSetting ps = mSettings.mPackages.get(packageName); PackageParser.Package pkg = mPackages.get(packageName); final boolean returnAllowed = ps != null && (isCallerSameApp(packageName, callingUid) || canViewInstantApps(callingUid, userId) || mInstantAppRegistry.isInstantAccessGranted( userId, UserHandle.getAppId(callingUid), ps.appId)); if (returnAllowed) { return ps.getInstantApp(userId); } } return false; } @Override public byte[] getInstantAppCookie(String packageName, int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return null; } synchronized (mPackages) { return mInstantAppRegistry.getInstantAppCookieLPw( packageName, userId); } } @Override public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) { if (HIDE_EPHEMERAL_APIS) { return true; } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, true /* checkShell */, "setInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return false; } synchronized (mPackages) { return mInstantAppRegistry.setInstantAppCookieLPw( packageName, cookie, userId); } } @Override public Bitmap getInstantAppIcon(String packageName, int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } if (!canViewInstantApps(Binder.getCallingUid(), userId)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getInstantAppIcon"); } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppIcon"); synchronized (mPackages) { return mInstantAppRegistry.getInstantAppIconLPw( packageName, userId); } } private boolean isCallerSameApp(String packageName, int uid) { PackageParser.Package pkg = mPackages.get(packageName); return pkg != null && UserHandle.getAppId(uid) == pkg.applicationInfo.uid; } @Override public @NonNull ParceledListSlice getPersistentApplications(int flags) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(getPersistentApplicationsInternal(flags)); } private @NonNull List getPersistentApplicationsInternal(int flags) { final ArrayList finalList = new ArrayList<>(); // reader synchronized (mPackages) { final Iterator i = mPackages.values().iterator(); final int userId = UserHandle.getCallingUserId(); while (i.hasNext()) { final PackageParser.Package p = i.next(); if (p.applicationInfo == null) continue; final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0) && !p.applicationInfo.isDirectBootAware(); final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0) && p.applicationInfo.isDirectBootAware(); if ((p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0 && (!mSafeMode || isSystemApp(p)) && (matchesUnaware || matchesAware)) { PackageSetting ps = mSettings.mPackages.get(p.packageName); if (ps != null) { ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); if (ai != null) { finalList.add(ai); } } } } } return finalList; } @Override public ProviderInfo resolveContentProvider(String name, int flags, int userId) { return resolveContentProviderInternal(name, flags, userId); } private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId, name); final int callingUid = Binder.getCallingUid(); final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId); if (providerInfo == null) { return null; } if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { return null; } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { return null; } return providerInfo; } } /** * @deprecated */ @Deprecated public void querySyncProviders(List outNames, List outInfo) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; } mComponentResolver.querySyncProviders( outNames, outInfo, mSafeMode, UserHandle.getCallingUserId()); } @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; final List matchList = mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId); final int listSize = (matchList == null ? 0 : matchList.size()); synchronized (mPackages) { for (int i = 0; i < listSize; i++) { final ProviderInfo providerInfo = matchList.get(i); if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { continue; } final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { continue; } if (finalList == null) { finalList = new ArrayList<>(listSize - i); } finalList.add(providerInfo); } } if (finalList != null) { finalList.sort(sProviderInitOrderSorter); return new ParceledListSlice<>(finalList); } return ParceledListSlice.emptyList(); } @Override public InstrumentationInfo getInstrumentationInfo(ComponentName component, int flags) { // reader synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) { return null; } final PackageParser.Instrumentation i = mInstrumentation.get(component); return PackageParser.generateInstrumentationInfo(i, flags); } } @Override public @NonNull ParceledListSlice queryInstrumentation( String targetPackage, int flags) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(targetPackage); if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags)); } private @NonNull List queryInstrumentationInternal(String targetPackage, int flags) { ArrayList finalList = new ArrayList<>(); // reader synchronized (mPackages) { final Iterator i = mInstrumentation.values().iterator(); while (i.hasNext()) { final PackageParser.Instrumentation p = i.next(); if (targetPackage == null || targetPackage.equals(p.info.targetPackage)) { InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p, flags); if (ii != null) { finalList.add(ii); } } } } return finalList; } private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]"); try { scanDirLI(scanDir, parseFlags, scanFlags, currentTime); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) { final File[] files = scanDir.listFiles(); if (ArrayUtils.isEmpty(files)) { Log.d(TAG, "No files in app dir " + scanDir); return; } if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags + " flags=0x" + Integer.toHexString(parseFlags)); } try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser( mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mParallelPackageParserCallback)) { // Submit files for parsing in parallel int fileCount = 0; for (File file : files) { final boolean isPackage = (isApkFile(file) || file.isDirectory()) && !PackageInstallerService.isStageName(file.getName()); if (!isPackage) { // Ignore entries which are not packages continue; } parallelPackageParser.submit(file, parseFlags); fileCount++; } // Process results one by one for (; fileCount > 0; fileCount--) { ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take(); Throwable throwable = parseResult.throwable; int errorCode = PackageManager.INSTALL_SUCCEEDED; if (throwable == null) { // TODO(toddke): move lower in the scan chain // Static shared libraries have synthetic package names if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(parseResult.pkg); } try { scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags, currentTime, null); } catch (PackageManagerException e) { errorCode = e.error; Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage()); } } else if (throwable instanceof PackageParser.PackageParserException) { PackageParser.PackageParserException e = (PackageParser.PackageParserException) throwable; errorCode = e.error; Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage()); } else { throw new IllegalStateException("Unexpected exception occurred while parsing " + parseResult.scanFile, throwable); } // Delete invalid userdata apps if ((scanFlags & SCAN_AS_SYSTEM) == 0 && errorCode != PackageManager.INSTALL_SUCCEEDED) { logCriticalInfo(Log.WARN, "Deleting invalid package at " + parseResult.scanFile); removeCodePathLI(parseResult.scanFile); } } } } public static void reportSettingsProblem(int priority, String msg) { logCriticalInfo(priority, msg); } private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, boolean forceCollect, boolean skipVerify) throws PackageManagerException { // When upgrading from pre-N MR1, verify the package time stamp using the package // directory and not the APK file. final long lastModifiedTime = mIsPreNMR1Upgrade ? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg); final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(pkg); if (ps != null && !forceCollect && ps.codePathString.equals(pkg.codePath) && ps.timeStamp == lastModifiedTime && !isCompatSignatureUpdateNeeded(settingsVersionForPackage) && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) { if (ps.signatures.mSigningDetails.signatures != null && ps.signatures.mSigningDetails.signatures.length != 0 && ps.signatures.mSigningDetails.signatureSchemeVersion != SignatureSchemeVersion.UNKNOWN) { // Optimization: reuse the existing cached signing data // if the package appears to be unchanged. pkg.mSigningDetails = new PackageParser.SigningDetails(ps.signatures.mSigningDetails); return; } Slog.w(TAG, "PackageSetting for " + ps.name + " is missing signatures. Collecting certs again to recover them."); } else { Slog.i(TAG, pkg.codePath + " changed; collecting certs" + (forceCollect ? " (forced)" : "")); } try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); PackageParser.collectCertificates(pkg, skipVerify); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } /** * Clear the package profile if this was an upgrade and the package * version was updated. */ private void maybeClearProfilesForUpgradesLI( @Nullable PackageSetting originalPkgSetting, @NonNull PackageParser.Package currentPkg) { if (originalPkgSetting == null || !isDeviceUpgrading()) { return; } if (originalPkgSetting.versionCode == currentPkg.mVersionCode) { return; } clearAppProfilesLIF(currentPkg, UserHandle.USER_ALL); if (DEBUG_INSTALL) { Slog.d(TAG, originalPkgSetting.name + " clear profile due to version change " + originalPkgSetting.versionCode + " != " + currentPkg.mVersionCode); } } /** * Traces a package scan. * @see #scanPackageLI(File, int, int, long, UserHandle) */ @GuardedBy({"mInstallLock", "mPackages"}) 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 */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); pp.setDisplayMetrics(mMetrics); pp.setCallback(mPackageParserCallback); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); final PackageParser.Package pkg; try { pkg = pp.parsePackage(scanFile, parseFlags); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } // Static shared libraries have synthetic package names if (pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(pkg); } return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user); } /** * Scans a package and returns the newly parsed package. * @throws PackageManagerException on a parse error. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg, final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { // If the package has children and this is the first dive in the function // we scan the package with the SCAN_CHECK_ONLY flag set to see whether all // packages (parent and children) would be successfully scanned before the // actual scan since scanning mutates internal state and we want to atomically // install the package and its children. if ((scanFlags & SCAN_CHECK_ONLY) == 0) { if (pkg.childPackages != null && pkg.childPackages.size() > 0) { scanFlags |= SCAN_CHECK_ONLY; } } else { scanFlags &= ~SCAN_CHECK_ONLY; } // Scan the parent PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags, scanFlags, currentTime, user); // Scan the children final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPackage = pkg.childPackages.get(i); addForInitLI(childPackage, parseFlags, scanFlags, currentTime, user); } if ((scanFlags & SCAN_CHECK_ONLY) != 0) { return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user); } return scannedPkg; } /** * Returns if forced apk verification can be skipped for the whole package, including splits. */ private boolean canSkipForcedPackageVerification(PackageParser.Package pkg) { if (!canSkipForcedApkVerification(pkg.baseCodePath)) { return false; } // TODO: Allow base and splits to be verified individually. if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { for (int i = 0; i < pkg.splitCodePaths.length; i++) { if (!canSkipForcedApkVerification(pkg.splitCodePaths[i])) { return false; } } } return true; } /** * Returns if forced apk verification can be skipped, depending on current FSVerity setup and * whether the apk contains signed root hash. Note that the signer's certificate still needs to * match one in a trusted source, and should be done separately. */ private boolean canSkipForcedApkVerification(String apkPath) { if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) { return VerityUtils.hasFsverity(apkPath); } try { final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath); if (rootHashObserved == null) { return false; // APK does not contain Merkle tree root hash. } synchronized (mInstallLock) { // Returns whether the observed root hash matches what kernel has. mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved); return true; } } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) { Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e); } return false; } /** * Adds a new package to the internal data structures during platform initialization. * After adding, the package is known to the system and available for querying. * For packages located on the device ROM [eg. packages located in /system, /vendor, * etc...], additional checks are performed. Basic verification [such as ensuring * matching signatures, checking version codes, etc...] occurs if the package is * identical to a previously known package. If the package fails a signature check, * the version installed on /data will be removed. If the version of the new package * is less than or equal than the version on /data, it will be ignored. * Regardless of the package location, the results are applied to the internal * structures and the package is made available to the rest of the system. * NOTE: The return value should be removed. It's the passed in package object. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package addForInitLI(PackageParser.Package pkg, @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { final boolean scanSystemPartition = (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0; final String renamedPkgName; final PackageSetting disabledPkgSetting; final boolean isSystemPkgUpdated; final boolean pkgAlreadyExists; PackageSetting pkgSetting; // NOTE: installPackageLI() has the same code to setup the package's // application info. This probably should be done lower in the call // stack [such as scanPackageOnly()]. However, we verify the application // info prior to that [in scanPackageNew()] and thus have to setup // the application info early. pkg.setApplicationVolumeUuid(pkg.volumeUuid); pkg.setApplicationInfoCodePath(pkg.codePath); pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath); pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths); pkg.setApplicationInfoResourcePath(pkg.codePath); pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath); pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths); synchronized (mPackages) { renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage); final String realPkgName = getRealPackageName(pkg, renamedPkgName); if (realPkgName != null) { ensurePackageRenamed(pkg, renamedPkgName); } final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName); final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName); pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting; pkgAlreadyExists = pkgSetting != null; final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName; disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName); isSystemPkgUpdated = disabledPkgSetting != null; if (DEBUG_INSTALL && isSystemPkgUpdated) { Slog.d(TAG, "updatedPkg = " + disabledPkgSetting); } final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null) ? mSettings.getSharedUserLPw(pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true) : null; if (DEBUG_PACKAGE_SCANNING && (parseFlags & PackageParser.PARSE_CHATTY) != 0 && sharedUserSetting != null) { Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + sharedUserSetting.userId + "):" + " packages=" + sharedUserSetting.packages); } if (scanSystemPartition) { // Potentially prune child packages. If the application on the /system // partition has been updated via OTA, but, is still disabled by a // version on /data, cycle through all of its children packages and // remove children that are no longer defined. if (isSystemPkgUpdated) { final int scannedChildCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; final int disabledChildCount = disabledPkgSetting.childPackageNames != null ? disabledPkgSetting.childPackageNames.size() : 0; for (int i = 0; i < disabledChildCount; i++) { String disabledChildPackageName = disabledPkgSetting.childPackageNames.get(i); boolean disabledPackageAvailable = false; for (int j = 0; j < scannedChildCount; j++) { PackageParser.Package childPkg = pkg.childPackages.get(j); if (childPkg.packageName.equals(disabledChildPackageName)) { disabledPackageAvailable = true; break; } } if (!disabledPackageAvailable) { mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName); } } // we're updating the disabled package, so, scan it as the package setting final ScanRequest request = new ScanRequest(pkg, sharedUserSetting, null, disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */, null /* originalPkgSetting */, null, parseFlags, scanFlags, (pkg == mPlatformPackage), user); applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage); final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L); if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) { scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting); } } } } final boolean newPkgChangedPaths = pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath); final boolean newPkgVersionGreater = pkgAlreadyExists && pkg.getLongVersionCode() > pkgSetting.versionCode; final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated && newPkgChangedPaths && newPkgVersionGreater; if (isSystemPkgBetter) { // The version of the application on /system is greater than the version on // /data. Switch back to the application on /system. // It's safe to assume the application on /system will correctly scan. If not, // there won't be a working copy of the application. synchronized (mPackages) { // just remove the loaded entries from package lists mPackages.remove(pkgSetting.name); } logCriticalInfo(Log.WARN, "System package updated;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); final InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); args.cleanUpResourcesLI(); synchronized (mPackages) { mSettings.enableSystemPackageLPw(pkgSetting.name); } } if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) { // The version of the application on the /system partition is less than or // equal to the version on the /data partition. Throw an exception and use // the application already installed on the /data partition. throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at " + pkg.codePath + " ignored: updated version " + pkgSetting.versionCode + " better than this " + pkg.getLongVersionCode()); } // Verify certificates against what was last scanned. If there was an upgrade and this is an // app in a system partition, or if this is an updated priv app, we will force re-collecting // certificate. final boolean forceCollect = (mIsUpgrade && scanSystemPartition) || PackageManagerServiceUtils.isApkVerificationForced(disabledPkgSetting); // Full APK verification can be skipped during certificate collection, only if the file is // in verified partition, or can be verified on access (when apk verity is enabled). In both // cases, only data in Signing Block is verified instead of the whole file. final boolean skipVerify = scanSystemPartition || (forceCollect && canSkipForcedPackageVerification(pkg)); collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify); // Reset profile if the application version is changed maybeClearProfilesForUpgradesLI(pkgSetting, pkg); /* * A new system app appeared, but we already had a non-system one of the * same name installed earlier. */ boolean shouldHideSystemApp = false; // A new application appeared on /system, but, we already have a copy of // the application installed on /data. if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists && !pkgSetting.isSystem()) { if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA) && !pkgSetting.signatures.mSigningDetails.checkCapability( pkg.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) { logCriticalInfo(Log.WARN, "System package signature mismatch;" + " name: " + pkgSetting.name); try (PackageFreezer freezer = freezePackage(pkg.packageName, "scanPackageInternalLI")) { deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null); } pkgSetting = null; } else if (newPkgVersionGreater) { // The application on /system is newer than the application on /data. // Simply remove the application on /data [keeping application data] // and replace it with the version on /system. logCriticalInfo(Log.WARN, "System package enabled;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } } else { // The application on /system is older than the application on /data. Hide // the application on /system and the version on /data will be scanned later // and re-added like an update. shouldHideSystemApp = true; logCriticalInfo(Log.INFO, "System package disabled;" + " name: " + pkgSetting.name + "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode + "; new: " + pkg.codePath + " @ " + pkg.codePath); } } final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user); if (scanResult.success) { synchronized (mPackages) { boolean appIdCreated = false; try { final String pkgName = scanResult.pkgSetting.name; final Map reconcileResult = reconcilePackagesLocked( new ReconcileRequest( Collections.singletonMap(pkgName, scanResult), mSharedLibraries, mPackages, Collections.singletonMap( pkgName, getSettingsVersionForPackage(pkg)), Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))), mSettings.mKeySetManagerService); appIdCreated = optimisticallyRegisterAppId(scanResult); commitReconciledScanResultLocked(reconcileResult.get(pkgName)); } catch (PackageManagerException e) { if (appIdCreated) { cleanUpAppIdCreation(scanResult); } throw e; } } } if (shouldHideSystemApp) { synchronized (mPackages) { mSettings.disableSystemPackageLPw(pkg.packageName, true); } } return scanResult.pkgSetting.pkg; } private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) { // Derive the new package synthetic package name pkg.setPackageName(pkg.packageName + STATIC_SHARED_LIB_DELIMITER + pkg.staticSharedLibVersion); } static String fixProcessName(String defProcessName, String processName) { if (processName == null) { return defProcessName; } return processName; } /** * Enforces that only the system UID or root's UID can call a method exposed * via Binder. * * @param message used as message if SecurityException is thrown * @throws SecurityException if the caller is not system or root */ private static void enforceSystemOrRoot(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) { throw new SecurityException(message); } } /** * Enforces that only the system UID or root's UID or shell'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 shell */ private static void enforceSystemOrRootOrShell(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID && uid != Process.SHELL_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 = isDeviceUpgrading(); // First boot or factory reset. // Note: we also handle devices that are upgrading to N right now as if it is their // first boot, as they do not have profile data. boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade; // We need to re-extract after a pruned cache, as AoT-ed files will be out of date. boolean causePrunedCache = VMRuntime.didPruneDalvikCache(); if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) { return; } List pkgs; synchronized (mPackages) { pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this); } final long startTime = System.nanoTime(); final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */, causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT, false /* bootComplete */); final int elapsedTimeSeconds = (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime); MetricsLogger.histogram(mContext, "opt_dialog_num_dexopted", stats[0]); MetricsLogger.histogram(mContext, "opt_dialog_num_skipped", stats[1]); MetricsLogger.histogram(mContext, "opt_dialog_num_failed", stats[2]); MetricsLogger.histogram(mContext, "opt_dialog_num_total", getOptimizablePackages().size()); MetricsLogger.histogram(mContext, "opt_dialog_time_s", elapsedTimeSeconds); } /* * Return the prebuilt profile path given a package base code path. */ private static String getPrebuildProfilePath(PackageParser.Package pkg) { return pkg.baseCodePath + ".prof"; } /** * Performs dexopt on the set of packages in {@code packages} and returns an int array * containing statistics about the invocation. The array consists of three elements, * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped} * and {@code numberOfPackagesFailed}. */ private int[] performDexOptUpgrade(List pkgs, boolean showDialog, final int compilationReason, boolean bootComplete) { int numberOfPackagesVisited = 0; int numberOfPackagesOptimized = 0; int numberOfPackagesSkipped = 0; int numberOfPackagesFailed = 0; final int numberOfPackagesToDexopt = pkgs.size(); for (PackageParser.Package pkg : pkgs) { numberOfPackagesVisited++; boolean useProfileForDexopt = false; if ((isFirstBoot() || isDeviceUpgrading()) && isSystemApp(pkg)) { // Copy over initial preopt profiles since we won't get any JIT samples for methods // that are already compiled. File profileFile = new File(getPrebuildProfilePath(pkg)); // Copy profile if it exists. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The issue // is that we don't have a good way to say "do this only once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Installer failed to copy system profile!"); } else { // Disabled as this causes speed-profile compilation during first boot // even if things are already compiled. // useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } else { PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName); // Handle compressed APKs in this path. Only do this for stubs with profiles to // minimize the number off apps being speed-profile compiled during first boot. // The other paths will not change the filter. if (disabledPs != null && disabledPs.pkg.isStub) { // The package is the stub one, remove the stub suffix to get the normal // package and APK names. String systemProfilePath = getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, ""); profileFile = new File(systemProfilePath); // If we have a profile for a compressed APK, copy it to the reference // location. // Note that copying the profile here will cause it to override the // reference profile every OTA even though the existing reference profile // may have more data. We can't copy during decompression since the // directories are not set up at that point. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The // issue is that we don't have a good way to say "do this only // once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Failed to copy system profile for stub package!"); } else { useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } } } } if (!PackageDexOptimizer.canOptimizePackage(pkg)) { if (DEBUG_DEXOPT) { Log.i(TAG, "Skipping update of of non-optimizable app " + pkg.packageName); } numberOfPackagesSkipped++; continue; } if (DEBUG_DEXOPT) { Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " + numberOfPackagesToDexopt + ": " + pkg.packageName); } if (showDialog) { try { ActivityManager.getService().showBootMessage( mContext.getResources().getString(R.string.android_upgrading_apk, numberOfPackagesVisited, numberOfPackagesToDexopt), true); } catch (RemoteException e) { } synchronized (mPackages) { mDexOptDialogShown = true; } } int pkgCompilationReason = compilationReason; if (useProfileForDexopt) { // Use background dexopt mode to try and use the profile. Note that this does not // guarantee usage of the profile. pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT; } if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { mArtManagerService.compileLayouts(pkg); } // checkProfiles is false to avoid merging profiles during boot which // might interfere with background compilation (b/28612421). // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a // trade-off worth doing to save boot time work. int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0; if (compilationReason == REASON_FIRST_BOOT) { // TODO: This doesn't cover the upgrade case, we should check for this too. dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE; } int primaryDexOptStaus = performDexOptTraced(new DexoptOptions( pkg.packageName, pkgCompilationReason, dexoptFlags)); switch (primaryDexOptStaus) { case PackageDexOptimizer.DEX_OPT_PERFORMED: numberOfPackagesOptimized++; break; case PackageDexOptimizer.DEX_OPT_SKIPPED: numberOfPackagesSkipped++; break; case PackageDexOptimizer.DEX_OPT_FAILED: numberOfPackagesFailed++; break; default: Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus); break; } } return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped, numberOfPackagesFailed }; } @Override public void notifyPackageUse(String packageName, int reason) { synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); if (getInstantAppPackageName(callingUid) != null) { if (!isCallerSameApp(packageName, callingUid)) { return; } } else { if (isInstantApp(packageName, callingUserId)) { return; } } notifyPackageUseLocked(packageName, reason); } } @GuardedBy("mPackages") public CheckPermissionDelegate getCheckPermissionDelegateLocked() { return mCheckPermissionDelegate; } @GuardedBy("mPackages") public void setCheckPermissionDelegateLocked(CheckPermissionDelegate delegate) { mCheckPermissionDelegate = delegate; } @GuardedBy("mPackages") private void notifyPackageUseLocked(String packageName, int reason) { final PackageParser.Package p = mPackages.get(packageName); if (p == null) { return; } p.mLastPackageUsageTimeInMills[reason] = System.currentTimeMillis(); } @Override public void notifyDexLoad(String loadingPackageName, List classLoaderNames, List classPaths, String loaderIsa) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); if (ai == null) { Slog.w(TAG, "Loading a package that does not exist for the calling user. package=" + loadingPackageName + ", user=" + userId); return; } mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId); } @Override public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule, IDexModuleRegisterCallback callback) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(packageName, /*flags*/ 0, userId); DexManager.RegisterDexModuleResult result; if (ai == null) { Slog.w(TAG, "Registering a dex module for a package that does not exist for the" + " calling user. package=" + packageName + ", user=" + userId); result = new DexManager.RegisterDexModuleResult(false, "Package not installed"); } else { result = mDexManager.registerDexModule(ai, dexModulePath, isSharedModule, userId); } if (callback != null) { mHandler.post(() -> { try { callback.onDexModuleRegistered(dexModulePath, result.success, result.message); } catch (RemoteException e) { Slog.w(TAG, "Failed to callback after module registration " + dexModulePath, e); } }); } } /** * Ask the package manager to perform a dex-opt with the given compiler filter. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptMode(String packageName, boolean checkProfiles, String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) { int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) | (force ? DexoptOptions.DEXOPT_FORCE : 0) | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0); return performDexOpt(new DexoptOptions(packageName, REASON_UNKNOWN, targetCompilerFilter, splitName, flags)); } /** * Ask the package manager to perform a dex-opt with the given compiler filter on the * secondary dex files belonging to the given package. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptSecondary(String packageName, String compilerFilter, boolean force) { int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES | DexoptOptions.DEXOPT_BOOT_COMPLETE | (force ? DexoptOptions.DEXOPT_FORCE : 0); return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags)); } /** * Ask the package manager to compile layouts in the given package. */ @Override public boolean compileLayouts(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { return false; } } return mViewCompiler.compileLayouts(pkg); } /*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 = findSharedLibraries(p); final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo); if (!deps.isEmpty()) { DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(), options.getCompilationReason(), options.getCompilerFilter(), options.getSplitName(), options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY); for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = null; synchronized (mPackages) { depPackage = mPackages.get(info.getPackageName()); } if (depPackage != null) { // TODO: Analyze and investigate if we (should) profile libraries. pdo.performDexOpt(depPackage, instructionSets, getOrCreateCompilerPackageStats(depPackage), mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions); } else { // TODO(ngeoffray): Support dexopting system shared libraries. } } } return pdo.performDexOpt(p, instructionSets, getOrCreateCompilerPackageStats(p), mDexManager.getPackageUseInfoOrDefault(p.packageName), options); } /** * Reconcile the information we have about the secondary dex files belonging to * {@code packageName} 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(@Nullable List packageNames) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return false; } enforceSystemOrRootOrShell("runBackgroundDexoptJob"); final long identity = Binder.clearCallingIdentity(); try { return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames); } finally { Binder.restoreCallingIdentity(identity); } } private static List findSharedLibraries(PackageParser.Package p) { if (p.usesLibraryInfos != null) { ArrayList retValue = new ArrayList<>(); Set collectedNames = new HashSet<>(); for (SharedLibraryInfo info : p.usesLibraryInfos) { findSharedLibrariesRecursive(info, retValue, collectedNames); } return retValue; } else { return Collections.emptyList(); } } private static void findSharedLibrariesRecursive(SharedLibraryInfo info, ArrayList collected, Set collectedNames) { if (!collectedNames.contains(info.getName())) { collectedNames.add(info.getName()); collected.add(info); if (info.getDependencies() != null) { for (SharedLibraryInfo dep : info.getDependencies()) { findSharedLibrariesRecursive(dep, collected, collectedNames); } } } } List findSharedNonSystemLibraries(PackageParser.Package pkg) { List deps = findSharedLibraries(pkg); if (!deps.isEmpty()) { ArrayList retValue = new ArrayList<>(); synchronized (mPackages) { for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = mPackages.get(info.getPackageName()); if (depPackage != null) { retValue.add(depPackage); } } } return retValue; } else { return Collections.emptyList(); } } @Nullable private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) { return getSharedLibraryInfo(name, version, mSharedLibraries, null); } @Nullable private static SharedLibraryInfo getSharedLibraryInfo(String name, long version, Map> existingLibraries, @Nullable Map> newLibraries) { if (newLibraries != null) { final LongSparseArray versionedLib = newLibraries.get(name); SharedLibraryInfo info = null; if (versionedLib != null) { info = versionedLib.get(version); } if (info != null) { return info; } } final LongSparseArray versionedLib = existingLibraries.get(name); if (versionedLib == null) { return null; } return versionedLib.get(version); } private SharedLibraryInfo getLatestSharedLibraVersionLPr(PackageParser.Package pkg) { LongSparseArray versionedLib = mSharedLibraries.get( pkg.staticSharedLibName); if (versionedLib == null) { return null; } long previousLibVersion = -1; final int versionCount = versionedLib.size(); for (int i = 0; i < versionCount; i++) { final long libVersion = versionedLib.keyAt(i); if (libVersion < pkg.staticSharedLibVersion) { previousLibVersion = Math.max(previousLibVersion, libVersion); } } if (previousLibVersion >= 0) { return versionedLib.get(previousLibVersion); } return null; } @Nullable private PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) { PackageSetting sharedLibPackage = null; synchronized (mPackages) { final SharedLibraryInfo latestSharedLibraVersionLPr = getLatestSharedLibraVersionLPr(scanResult.pkgSetting.pkg); if (latestSharedLibraVersionLPr != null) { sharedLibPackage = mSettings.getPackageLPr( latestSharedLibraVersionLPr.getPackageName()); } } return sharedLibPackage; } public void shutdown() { mPackageUsage.writeNow(mPackages); mCompilerStats.writeNow(); mDexManager.writePackageDexUsageNow(); PackageWatchdog.getInstance(mContext).writeNow(); // This is the last chance to write out pending restriction settings synchronized (mPackages) { if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS); for (int userId : mDirtyUsers) { mSettings.writePackageRestrictionsLPr(userId); } mDirtyUsers.clear(); } } } @Override public void dumpProfiles(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } /* Only the shell, root, or the app user should be able to dump profiles. */ int callingUid = Binder.getCallingUid(); if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID && callingUid != pkg.applicationInfo.uid) { throw new SecurityException("dumpProfiles"); } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles"); mArtManagerService.dumpProfiles(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @Override public void forceDexOpt(String packageName) { enforceSystemOrRoot("forceDexOpt"); PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); // Whoever is calling forceDexOpt wants a compiled package. // Don't use profiles since that may cause compilation to be skipped. final int res = performDexOptInternalWithDependenciesLI( pkg, new DexoptOptions(packageName, getDefaultCompilerFilter(), DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE)); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) { throw new IllegalStateException("Failed to dexopt: " + res); } } } @GuardedBy("mPackages") private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) { if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package not in system partition"); return false; } else if (mPackages.get(oldPkg.name) != null) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package still exists"); return false; } return true; } @GuardedBy("mInstallLock") void removeCodePathLI(File codePath) { if (codePath.isDirectory()) { try { mInstaller.rmPackageDir(codePath.getAbsolutePath()); } catch (InstallerException e) { Slog.w(TAG, "Failed to remove code path", e); } } else { codePath.delete(); } } private int[] resolveUserIds(int userId) { return (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds() : new int[] { userId }; } private void clearAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } clearAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { clearAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } clearAppProfilesLIF(pkg, UserHandle.USER_ALL); } private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } } private void destroyAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } } private void destroyAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.destroyAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId); } } private void destroyAppProfilesLIF(PackageParser.Package pkg) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppProfilesLeafLIF(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppProfilesLeafLIF(pkg.childPackages.get(i)); } } private void destroyAppProfilesLeafLIF(PackageParser.Package pkg) { try { mInstaller.destroyAppProfiles(pkg.packageName); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } private void clearAppProfilesLIF(PackageParser.Package pkg, int userId) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } mArtManagerService.clearAppProfiles(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { mArtManagerService.clearAppProfiles(pkg.childPackages.get(i)); } } private void setInstallAndUpdateTime(PackageParser.Package pkg, long firstInstallTime, long lastUpdateTime) { // Set parent install/update time PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } // Set children install/update time final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPkg = pkg.childPackages.get(i); ps = (PackageSetting) childPkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } } } @GuardedBy("mPackages") private void applyDefiningSharedLibraryUpdateLocked( PackageParser.Package pkg, SharedLibraryInfo libInfo, BiConsumer action) { // Note that libraries defined by this package may be null if: // - Package manager was unable to create the shared library. The package still // gets installed, but the shared library does not get created. // Or: // - Package manager is in a state where package isn't scanned yet. This will // get called again after scanning to fix the dependencies. if (pkg.isLibrary()) { if (pkg.staticSharedLibName != null) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( pkg.staticSharedLibName, pkg.staticSharedLibVersion); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } else { for (String libraryName : pkg.libraryNames) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( libraryName, SharedLibraryInfo.VERSION_UNDEFINED); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } } } } @GuardedBy("mPackages") private void addSharedLibraryLPr(PackageParser.Package pkg, Set usesLibraryFiles, SharedLibraryInfo libInfo, PackageParser.Package changingLib) { if (libInfo.getPath() != null) { usesLibraryFiles.add(libInfo.getPath()); return; } PackageParser.Package p = mPackages.get(libInfo.getPackageName()); if (changingLib != null && changingLib.packageName.equals(libInfo.getPackageName())) { // If we are doing this while in the middle of updating a library apk, // then we need to make sure to use that new apk for determining the // dependencies here. (We haven't yet finished committing the new apk // to the package manager state.) if (p == null || p.packageName.equals(changingLib.packageName)) { p = changingLib; } } if (p != null) { usesLibraryFiles.addAll(p.getAllCodePaths()); // If the package provides libraries, add the dependency to them. applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, (definingLibrary, dependency) -> { definingLibrary.addDependency(dependency); }); if (p.usesLibraryFiles != null) { Collections.addAll(usesLibraryFiles, p.usesLibraryFiles); } } } @GuardedBy("mPackages") private void updateSharedLibrariesLocked(PackageParser.Package pkg, PackageParser.Package changingLib, Map availablePackages) throws PackageManagerException { final ArrayList sharedLibraryInfos = collectSharedLibraryInfos(pkg, availablePackages, mSharedLibraries, null); executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos); } private static ArrayList collectSharedLibraryInfos(PackageParser.Package pkg, Map availablePackages, @NonNull final Map> existingLibraries, @Nullable final Map> newLibraries) throws PackageManagerException { if (pkg == null) { return null; } // The collection used here must maintain the order of addition (so // that libraries are searched in the correct order) and must have no // duplicates. ArrayList usesLibraryInfos = null; if (pkg.usesLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesLibraries, null, null, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, null, availablePackages, existingLibraries, newLibraries); } if (pkg.usesStaticLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } if (pkg.usesOptionalLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesOptionalLibraries, null, null, pkg.packageName, false, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } return usesLibraryInfos; } private void executeSharedLibrariesUpdateLPr(PackageParser.Package pkg, PackageParser.Package changingLib, ArrayList usesLibraryInfos) { // If the package provides libraries, clear their old dependencies. // This method will set them up again. applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> { definingLibrary.clearDependencies(); }); if (usesLibraryInfos != null) { pkg.usesLibraryInfos = usesLibraryInfos; // Use LinkedHashSet to preserve the order of files added to // usesLibraryFiles while eliminating duplicates. Set usesLibraryFiles = new LinkedHashSet<>(); for (SharedLibraryInfo libInfo : usesLibraryInfos) { addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib); } pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]); } else { pkg.usesLibraryInfos = null; pkg.usesLibraryFiles = null; } } @GuardedBy("mPackages") private static ArrayList collectSharedLibraryInfos( @NonNull List requestedLibraries, @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, @NonNull String packageName, boolean required, int targetSdk, @Nullable ArrayList outUsedLibraries, @NonNull final Map
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 installSystemStubPackages(@NonNull List systemStubPackageNames, @ScanFlags int scanFlags) { for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) { final String packageName = systemStubPackageNames.get(i); // skip if the system package is already disabled if (mSettings.isDisabledSystemPackageLPr(packageName)) { systemStubPackageNames.remove(i); continue; } // skip if the package isn't installed (?!); this should never happen final PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null) { systemStubPackageNames.remove(i); continue; } // skip if the package has been disabled by the user final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM); if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { systemStubPackageNames.remove(i); continue; } } // install the package to replace the stub on /system try { installStubPackageLI(pkg, 0, scanFlags); ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, UserHandle.USER_SYSTEM, "android"); systemStubPackageNames.remove(i); } 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 = systemStubPackageNames.size() - 1; i >= 0; --i) { final String pkgName = systemStubPackageNames.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); } } /** * Extract, install and enable a stub package. * If the compressed file can not be extracted / installed for any reason, the stub * APK will be installed and the package will be disabled. To recover from this situation, * the user will need to go into system settings and re-enable the package. */ private boolean enableCompressedPackage(PackageParser.Package stubPkg) { final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY | PackageParser.PARSE_ENFORCE_CODE; synchronized (mInstallLock) { final PackageParser.Package pkg; try (PackageFreezer freezer = freezePackage(stubPkg.packageName, "setEnabledSetting")) { pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/); synchronized (mPackages) { prepareAppDataAfterInstallLIF(pkg); try { updateSharedLibrariesLocked(pkg, null, mPackages); } catch (PackageManagerException e) { Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e); } mPermissionManager.updatePermissions( pkg.packageName, pkg, true, mPackages.values(), mPermissionCallback); mSettings.writeLPr(); } } catch (PackageManagerException e) { // Whoops! Something went very wrong; roll back to the stub and disable the package try (PackageFreezer freezer = freezePackage(stubPkg.packageName, "setEnabledSetting")) { synchronized (mPackages) { // NOTE: Ensure the system package is enabled; even for a compressed stub. // If we don't, installing the system package fails during scan enableSystemPackageLPw(stubPkg); } installPackageFromSystemLIF(stubPkg.codePath, null /*allUserHandles*/, null /*origUserHandles*/, null /*origPermissionsState*/, true /*writeSettings*/); } catch (PackageManagerException pme) { // Serious WTF; we have to be able to install the stub Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.packageName, pme); } finally { // Disable the package; the stub by itself is not runnable synchronized (mPackages) { final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName); if (stubPs != null) { stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android"); } mSettings.writeLPr(); } } return false; } clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); mDexManager.notifyPackageUpdated(pkg.packageName, pkg.baseCodePath, pkg.splitCodePaths); } return true; } private PackageParser.Package installStubPackageLI(PackageParser.Package stubPkg, @ParseFlags int parseFlags, @ScanFlags int scanFlags) throws PackageManagerException { if (DEBUG_COMPRESSION) { Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.packageName); } // uncompress the binary to its eventual destination on /data final File scanFile = decompressPackage(stubPkg.packageName, stubPkg.codePath); if (scanFile == null) { throw new PackageManagerException("Unable to decompress stub at " + stubPkg.codePath); } synchronized (mPackages) { mSettings.disableSystemPackageLPw(stubPkg.packageName, true /*replaced*/); } removePackageLI(stubPkg, true /*chatty*/); try { return scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.packageName, e); // Remove the failed install removeCodePathLI(scanFile); throw e; } } /** * 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(String packageName, String codePath) { final File[] compressedFiles = getCompressedFiles(codePath); if (compressedFiles == null || compressedFiles.length == 0) { if (DEBUG_COMPRESSION) { Slog.i(TAG, "No files to decompress: " + codePath); } return null; } final File dstCodePath = getNextCodePath(Environment.getDataAppDirectory(null), 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: " + packageName + ", file: " + dstFileName); break; } } } catch (ErrnoException e) { logCriticalInfo(Log.ERROR, "Failed to decompress" + "; 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: " + packageName); ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; } finally { IoUtils.closeQuietly(handle); } } if (ret != PackageManager.INSTALL_SUCCEEDED) { if (!dstCodePath.exists()) { return null; } removeCodePathLI(dstCodePath); return null; } return dstCodePath; } @GuardedBy("mPackages") 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 @Nullable File preparePackageParserCache() { 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 = Environment.getPackageCacheDirectory(); if (!FileUtils.createDir(cacheBaseDir)) { return null; } // There are several items that need to be combined together to safely // identify cached items. In particular, changing the value of certain // feature flags should cause us to invalidate any caches. final String cacheName = SystemProperties.digestOf( "ro.build.fingerprint", StorageManager.PROP_ISOLATED_STORAGE, StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT); // Reconcile cache directories, keeping only what we'd actually use. for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) { if (Objects.equals(cacheName, cacheDir.getName())) { Slog.d(TAG, "Keeping known cache " + cacheDir.getName()); } else { Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName()); FileUtils.deleteContentsAndDir(cacheDir); } } // Return the versioned package cache directory. File cacheDir = FileUtils.createDir(cacheBaseDir, cacheName); if (cacheDir == null) { // Something went wrong. Attempt to delete everything and return. Slog.wtf(TAG, "Cache directory cannot be created - wiping base dir " + cacheBaseDir); FileUtils.deleteContentsAndDir(cacheBaseDir); return null; } // The following is a workaround to aid development on non-numbered userdebug // builds or cases where "adb sync" is used on userdebug builds. If we detect that // the system partition is newer. // // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build // that starts with "eng." to signify that this is an engineering build and not // destined for release. if (Build.IS_USERDEBUG && Build.VERSION.INCREMENTAL.startsWith("eng.")) { Slog.w(TAG, "Wiping cache directory because the system partition changed."); // Heuristic: If the /system directory has been modified recently due to an "adb sync" // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable // in general and should not be used for production changes. In this specific case, // we know that they will work. File frameworkDir = new File(Environment.getRootDirectory(), "framework"); if (cacheDir.lastModified() < frameworkDir.lastModified()) { FileUtils.deleteContents(cacheBaseDir); cacheDir = FileUtils.createDir(cacheBaseDir, cacheName); } } return cacheDir; } @Override public boolean isFirstBoot() { // allow instant applications return mFirstBoot; } @Override public boolean isOnlyCoreApps() { // allow instant applications return mOnlyCore; } @Override public boolean isDeviceUpgrading() { // allow instant applications // The system property allows testing ota flow when upgraded to the same image. return mIsUpgrade || SystemProperties.getBoolean( "persist.pm.mock-upgrade", false /* default */); } private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() { final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); final List matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/); if (matches.size() == 1) { return matches.get(0).getComponentInfo().packageName; } else if (matches.size() == 0) { Log.e(TAG, "There should probably be a verifier, but, none were found"); return null; } throw new RuntimeException("There must be exactly one verifier; found " + matches); } private @NonNull String getRequiredSharedLibraryLPr(String name, int version) { synchronized (mPackages) { SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(name, version); if (libraryInfo == null) { throw new IllegalStateException("Missing required shared library:" + name); } String packageName = libraryInfo.getPackageName(); if (packageName == null) { throw new IllegalStateException("Expected a package for shared library " + name); } return packageName; } } private @NonNull String getRequiredInstallerLPr() { final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setDataAndType(Uri.parse("content://com.example/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 String getRequiredPermissionControllerLPr() { final Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSIONS); intent.addCategory(Intent.CATEGORY_DEFAULT); final List matches = queryIntentActivitiesInternal(intent, null, 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 permissions manager must be a privileged app"); } return matches.get(0).getComponentInfo().packageName; } else { throw new RuntimeException("There must be exactly one permissions manager; found " + matches); } } private @NonNull ComponentName getIntentFilterVerifierComponentNameLPr() { final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION); final List matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/); ResolveInfo best = null; final int N = matches.size(); for (int i = 0; i < N; i++) { final ResolveInfo cur = matches.get(i); final String packageName = cur.getComponentInfo().packageName; if (checkPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT, packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) { continue; } if (best == null || cur.priority > best.priority) { best = cur; } } if (best != null) { return best.getComponentInfo().getComponentName(); } Slog.w(TAG, "Intent filter verifier not found"); return null; } @Override public @Nullable ComponentName getInstantAppResolverComponent() { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return null; } synchronized (mPackages) { final Pair instantAppResolver = getInstantAppResolverLPr(); if (instantAppResolver == null) { return null; } return instantAppResolver.first; } } private @Nullable Pair getInstantAppResolverLPr() { final String[] packageArray = mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage); if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) { if (DEBUG_INSTANT) { Slog.d(TAG, "Ephemeral resolver NOT found; empty package list"); } return null; } final int callingUid = Binder.getCallingUid(); final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0); String actionName = Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE; final Intent resolverIntent = new Intent(actionName); List resolvers = queryIntentServicesInternal(resolverIntent, null, resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/); final int N = resolvers.size(); if (N == 0) { if (DEBUG_INSTANT) { Slog.d(TAG, "Ephemeral resolver NOT found; no matching intent filters"); } return null; } final Set possiblePackages = new ArraySet<>(Arrays.asList(packageArray)); for (int i = 0; i < N; i++) { final ResolveInfo info = resolvers.get(i); if (info.serviceInfo == null) { continue; } final String packageName = info.serviceInfo.packageName; if (!possiblePackages.contains(packageName) && !Build.IS_DEBUGGABLE) { if (DEBUG_INSTANT) { Slog.d(TAG, "Ephemeral resolver not in allowed package list;" + " pkg: " + packageName + ", info:" + info); } continue; } if (DEBUG_INSTANT) { Slog.v(TAG, "Ephemeral resolver found;" + " pkg: " + packageName + ", info:" + info); } return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName); } if (DEBUG_INSTANT) { Slog.v(TAG, "Ephemeral resolver NOT found"); } return null; } @GuardedBy("mPackages") private @Nullable ActivityInfo getInstantAppInstallerLPr() { String[] orderedActions = Build.IS_ENG ? new String[]{ Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE + "_TEST", Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE} : new String[]{ Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE}; final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | Intent.FLAG_IGNORE_EPHEMERAL | (!Build.IS_ENG ? MATCH_SYSTEM_ONLY : 0); final Intent intent = new Intent(); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE); List matches = null; for (String action : orderedActions) { intent.setAction(action); matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE, resolveFlags, UserHandle.USER_SYSTEM); if (matches.isEmpty()) { if (DEBUG_INSTANT) { Slog.d(TAG, "Instant App installer not found with " + action); } } else { break; } } Iterator iter = matches.iterator(); while (iter.hasNext()) { final ResolveInfo rInfo = iter.next(); final PackageSetting ps = mSettings.mPackages.get(rInfo.activityInfo.packageName); if (ps != null) { final PermissionsState permissionsState = ps.getPermissionsState(); if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0) || Build.IS_ENG) { continue; } } iter.remove(); } if (matches.size() == 0) { return null; } else if (matches.size() == 1) { return (ActivityInfo) matches.get(0).getComponentInfo(); } else { throw new RuntimeException( "There must be at most one ephemeral installer; found " + matches); } } private @Nullable ComponentName getInstantAppResolverSettingsLPr( @NonNull ComponentName resolver) { final Intent intent = new Intent(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS) .addCategory(Intent.CATEGORY_DEFAULT) .setPackage(resolver.getPackageName()); final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; List matches = queryIntentActivitiesInternal(intent, null, resolveFlags, UserHandle.USER_SYSTEM); if (matches.isEmpty()) { return null; } return matches.get(0).getComponentInfo().getComponentName(); } @GuardedBy("mPackages") private void primeDomainVerificationsLPw(int userId) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.d(TAG, "Priming domain verifications in user " + userId); } SystemConfig systemConfig = SystemConfig.getInstance(); ArraySet packages = systemConfig.getLinkedApps(); for (String packageName : packages) { PackageParser.Package pkg = mPackages.get(packageName); if (pkg != null) { if (!pkg.isSystem()) { Slog.w(TAG, "Non-system app '" + packageName + "' in sysconfig "); continue; } ArraySet domains = null; for (PackageParser.Activity a : pkg.activities) { for (ActivityIntentInfo filter : a.intents) { if (hasValidDomains(filter)) { if (domains == null) { domains = new ArraySet<>(); } domains.addAll(filter.getHostsList()); } } } if (domains != null && domains.size() > 0) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.v(TAG, " + " + packageName); } // 'Undefined' in the global IntentFilterVerificationInfo, i.e. the usual // state w.r.t. the formal app-linkage "no verification attempted" state; // and then 'always' in the per-user state actually used for intent resolution. final IntentFilterVerificationInfo ivi; ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName, domains); ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED); mSettings.updateIntentFilterVerificationStatusLPw(packageName, INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, userId); } else { Slog.w(TAG, "Sysconfig package '" + packageName + "' does not handle web links"); } } else { Slog.w(TAG, "Unknown package " + packageName + " in sysconfig "); } } scheduleWritePackageRestrictionsLocked(userId); scheduleWriteSettingsLocked(); } private boolean packageIsBrowser(String packageName, int userId) { List list = queryIntentActivitiesInternal(sBrowserIntent, null, PackageManager.MATCH_ALL, userId); final int N = list.size(); for (int i = 0; i < N; i++) { ResolveInfo info = list.get(i); if (info.priority >= 0 && packageName.equals(info.activityInfo.packageName)) { return true; } } return false; } @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; } } /** * Returns whether or not a full application can see an instant application. * * Currently, there are four cases in which this can occur: * * The calling application is a "special" process. Special processes * are those with a UID < {@link Process#FIRST_APPLICATION_UID}. * The calling application has the permission * {@link android.Manifest.permission#ACCESS_INSTANT_APPS}. * The calling application is the default launcher on the * system partition. * The calling application is the default app prediction service. * */ private boolean canViewInstantApps(int callingUid, int userId) { if (callingUid < Process.FIRST_APPLICATION_UID) { return true; } if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.ACCESS_INSTANT_APPS) == PERMISSION_GRANTED) { return true; } if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.VIEW_INSTANT_APPS) == PERMISSION_GRANTED) { final ComponentName homeComponent = getDefaultHomeActivity(userId); if (homeComponent != null && isCallerSameApp(homeComponent.getPackageName(), callingUid)) { return true; } // TODO(b/122900055) Change/Remove this and replace with new permission role. if (mAppPredictionServicePackage != null && isCallerSameApp(mAppPredictionServicePackage, callingUid)) { return true; } } return false; } private PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) { if (!sUserManager.exists(userId)) return null; if (ps == null) { return null; } final int callingUid = Binder.getCallingUid(); // Filter out ephemeral app metadata: // * The system/shell/root can see metadata for any app // * An installed app can see metadata for 1) other installed apps // and 2) ephemeral apps that have explicitly interacted with it // * Ephemeral apps can only see their own data and exposed installed apps // * Holding a signature permission allows seeing instant apps if (filterAppAccessLPr(ps, callingUid, userId)) { return null; } if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && ps.isSystem()) { flags |= MATCH_ANY_USER; } final PackageUserState state = ps.readUserState(userId); PackageParser.Package p = ps.pkg; if (p != null) { final PermissionsState permissionsState = ps.getPermissionsState(); // Compute GIDs only if requested final int[] gids = (flags & PackageManager.GET_GIDS) == 0 ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId); // Compute granted permissions only if package has requested permissions final Set permissions = ArrayUtils.isEmpty(p.requestedPermissions) ? Collections.emptySet() : permissionsState.getPermissions(userId); PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags, ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId); if (packageInfo == null) { return null; } packageInfo.packageName = packageInfo.applicationInfo.packageName = resolveExternalPackageNameLPr(p); return packageInfo; } else if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && state.isAvailable(flags)) { PackageInfo pi = new PackageInfo(); pi.packageName = ps.name; pi.setLongVersionCode(ps.versionCode); pi.sharedUserId = (ps.sharedUser != null) ? ps.sharedUser.name : null; pi.firstInstallTime = ps.firstInstallTime; pi.lastUpdateTime = ps.lastUpdateTime; ApplicationInfo ai = new ApplicationInfo(); ai.packageName = ps.name; ai.uid = UserHandle.getUid(userId, ps.appId); ai.primaryCpuAbi = ps.primaryCpuAbiString; ai.secondaryCpuAbi = ps.secondaryCpuAbiString; ai.setVersionCode(ps.versionCode); ai.flags = ps.pkgFlags; ai.privateFlags = ps.pkgPrivateFlags; pi.applicationInfo = PackageParser.generateApplicationInfo(ai, flags, state, userId); if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for [" + ps.name + "]. Provides a minimum info."); return pi; } else { return null; } } @Override public void checkPackageStartable(String packageName, int userId) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { throw new SecurityException("Instant applications don't have access to this method"); } final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId); synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) { throw new SecurityException("Package " + packageName + " was not found!"); } if (!ps.getInstalled(userId)) { throw new SecurityException( "Package " + packageName + " was not installed for user " + userId + "!"); } if (mSafeMode && !ps.isSystem()) { throw new SecurityException("Package " + packageName + " not a system app!"); } if (mFrozenPackages.contains(packageName)) { throw new SecurityException("Package " + packageName + " is currently frozen!"); } if (!userKeyUnlocked && !ps.pkg.applicationInfo.isEncryptionAware()) { throw new SecurityException("Package " + packageName + " is not encryption aware!"); } } } @Override public boolean isPackageAvailable(String packageName, int userId) { if (!sUserManager.exists(userId)) return false; final int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "is package available"); synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); if (p != null) { final PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, userId)) { return false; } if (ps != null) { final PackageUserState state = ps.readUserState(userId); if (state != null) { return PackageParser.isAvailable(state); } } } } return false; } @Override public PackageInfo getPackageInfo(String packageName, int flags, int userId) { return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST, flags, Binder.getCallingUid(), userId); } @Override public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage, int flags, int userId) { return getPackageInfoInternal(versionedPackage.getPackageName(), versionedPackage.getLongVersionCode(), flags, Binder.getCallingUid(), userId); } /** * Important: The provided filterCallingUid is used exclusively to filter out packages * that can be seen based on user state. It's typically the original caller uid prior * to clearing. Because it can only be provided by trusted code, it's value can be * trusted and will be used as-is; unlike userId which will be validated by this method. */ private PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags, int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForPackage(flags, userId, packageName); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get package info"); // reader synchronized (mPackages) { // Normalize package name to handle renamed packages and static libs packageName = resolveInternalPackageNameLPr(packageName, versionCode); final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0; if (matchFactoryOnly) { // Instant app filtering for APEX modules is ignored if ((flags & MATCH_APEX) != 0) { return mApexManager.getPackageInfo(packageName, ApexManager.MATCH_FACTORY_PACKAGE); } 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); } if (!matchFactoryOnly && (flags & MATCH_APEX) != 0) { return mApexManager.getPackageInfo(packageName, ApexManager.MATCH_ACTIVE_PACKAGE); } } 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 = mComponentResolver.getActivity(component); if (activity == null) { return false; } final boolean visibleToInstantApp = (activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean explicitlyVisibleToInstantApp = (activity.info.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; return visibleToInstantApp && explicitlyVisibleToInstantApp; } else if (type == TYPE_RECEIVER) { final PackageParser.Activity activity = mComponentResolver.getReceiver(component); if (activity == null) { return false; } final boolean visibleToInstantApp = (activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean explicitlyVisibleToInstantApp = (activity.info.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; return visibleToInstantApp && !explicitlyVisibleToInstantApp; } else if (type == TYPE_SERVICE) { final PackageParser.Service service = mComponentResolver.getService(component); return service != null ? (service.info.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0 : false; } else if (type == TYPE_PROVIDER) { final PackageParser.Provider provider = mComponentResolver.getProvider(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 #canViewInstantApps(int, int) */ @GuardedBy("mPackages") private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, @Nullable ComponentName component, @ComponentType int componentType, int userId) { // if we're in an isolated process, get the real calling UID if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final String instantAppPkgName = getInstantAppPackageName(callingUid); final boolean callerIsInstantApp = instantAppPkgName != null; if (ps == null) { if (callerIsInstantApp) { // pretend the application exists, but, needs to be filtered return true; } return false; } // if the target and caller are the same application, don't filter if (isCallerSameApp(ps.name, callingUid)) { return false; } if (callerIsInstantApp) { // both caller and target are both instant, but, different applications, filter if (ps.getInstantApp(userId)) { return true; } // request for a specific component; if it hasn't been explicitly exposed through // property or instrumentation target, filter if (component != null) { final PackageParser.Instrumentation instrumentation = mInstrumentation.get(component); if (instrumentation != null && isCallerSameApp(instrumentation.info.targetPackage, callingUid)) { return false; } return !isComponentVisibleToInstantApp(component, componentType); } // request for application; if no components have been explicitly exposed, filter return !ps.pkg.visibleToInstantApps; } if (ps.getInstantApp(userId)) { // caller can see all components of all instant applications, don't filter if (canViewInstantApps(callingUid, userId)) { return false; } // request for a specific instant application component, filter if (component != null) { return true; } // request for an instant application; if the caller hasn't been granted access, filter return !mInstantAppRegistry.isInstantAccessGranted( userId, UserHandle.getAppId(callingUid), ps.appId); } return false; } /** * @see #filterAppAccessLPr(PackageSetting, int, ComponentName, int, int) */ @GuardedBy("mPackages") private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, int userId) { return filterAppAccessLPr(ps, callingUid, null, TYPE_UNKNOWN, userId); } @GuardedBy("mPackages") 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; } // Installer gets to see all static libs. if (PackageManager.PERMISSION_GRANTED == checkUidPermission(Manifest.permission.INSTALL_PACKAGES, 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 SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(ps.pkg.staticSharedLibName, ps.pkg.staticSharedLibVersion); if (libraryInfo == 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, libraryInfo.getName()); if (index < 0) { continue; } if (uidPs.pkg.usesStaticLibrariesVersions[index] == libraryInfo.getLongVersion()) { return false; } } } return true; } @Override public String[] currentToCanonicalPackageNames(String[] names) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return names; } final String[] out = new String[names.length]; // reader synchronized (mPackages) { final int callingUserId = UserHandle.getUserId(callingUid); final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId); for (int i=names.length-1; i>=0; i--) { final PackageSetting ps = mSettings.mPackages.get(names[i]); boolean translateName = false; if (ps != null && ps.realName != null) { final boolean targetIsInstantApp = ps.getInstantApp(callingUserId); translateName = !targetIsInstantApp || canViewInstantApps || mInstantAppRegistry.isInstantAccessGranted(callingUserId, UserHandle.getAppId(callingUid), ps.appId); } out[i] = translateName ? ps.realName : names[i]; } } return out; } @Override public String[] canonicalToCurrentPackageNames(String[] names) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return names; } final String[] out = new String[names.length]; // reader synchronized (mPackages) { final int callingUserId = UserHandle.getUserId(callingUid); final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId); for (int i=names.length-1; i>=0; i--) { final String cur = mSettings.getRenamedPackageLPr(names[i]); boolean translateName = false; if (cur != null) { final PackageSetting ps = mSettings.mPackages.get(names[i]); final boolean targetIsInstantApp = ps != null && ps.getInstantApp(callingUserId); translateName = !targetIsInstantApp || canViewInstantApps || mInstantAppRegistry.isInstantAccessGranted(callingUserId, UserHandle.getAppId(callingUid), ps.appId); } out[i] = translateName ? cur : names[i]; } } return out; } @Override public int getPackageUid(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return -1; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForPackage(flags, userId, packageName); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid"); // reader synchronized (mPackages) { final PackageParser.Package p = mPackages.get(packageName); if (p != null && p.isMatch(flags)) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, userId)) { return -1; } return UserHandle.getUid(userId, p.applicationInfo.uid); } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && ps.isMatch(flags) && !filterAppAccessLPr(ps, callingUid, userId)) { return UserHandle.getUid(userId, ps.appId); } } } return -1; } /** * Check if any package sharing/holding a uid has a low enough target SDK. * * @param uid The uid of the packages * @param higherTargetSDK The target SDK that might be higher than the searched package * * @return {@code true} if there is a package sharing/holding the uid with * {@code package.targetSDK < higherTargetSDK} */ private boolean hasTargetSdkInUidLowerThan(int uid, int higherTargetSDK) { int userId = UserHandle.getUserId(uid); synchronized (mPackages) { Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid)); if (obj == null) { return false; } if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (!ps.getInstalled(userId)) { return false; } return ps.pkg.applicationInfo.targetSdkVersion < higherTargetSDK; } else if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; final int numPkgs = sus.packages.size(); for (int i = 0; i < numPkgs; i++) { final PackageSetting ps = sus.packages.valueAt(i); if (!ps.getInstalled(userId)) { continue; } if (ps.pkg.applicationInfo.targetSdkVersion < higherTargetSDK) { return true; } } return false; } else { return false; } } } @Override public int[] getPackageGids(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForPackage(flags, userId, packageName); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids"); // reader synchronized (mPackages) { final PackageParser.Package p = mPackages.get(packageName); if (p != null && p.isMatch(flags)) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, userId)) { return null; } // TODO: Shouldn't this be checking for package installed state for userId and // return null? return ps.getPermissionsState().computeGids(userId); } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && ps.isMatch(flags) && !filterAppAccessLPr(ps, callingUid, userId)) { return ps.getPermissionsState().computeGids(userId); } } } return null; } @Override public PermissionInfo getPermissionInfo(String name, String packageName, int flags) { return mPermissionManager.getPermissionInfo(name, packageName, flags, getCallingUid()); } @Override public @Nullable ParceledListSlice queryPermissionsByGroup(String groupName, int flags) { final List permissionList = mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid()); return (permissionList == null) ? null : new ParceledListSlice<>(permissionList); } @Override public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) { return mPermissionManager.getPermissionGroupInfo(groupName, flags, getCallingUid()); } @Override public @NonNull ParceledListSlice getAllPermissionGroups(int flags) { final List permissionList = mPermissionManager.getAllPermissionGroups(flags, getCallingUid()); return (permissionList == null) ? ParceledListSlice.emptyList() : new ParceledListSlice<>(permissionList); } @GuardedBy("mPackages") 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); if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get application info"); } // writer synchronized (mPackages) { // Normalize package name to handle renamed packages and static libs packageName = resolveInternalPackageNameLPr(packageName, PackageManager.VERSION_CODE_HIGHEST); PackageParser.Package p = mPackages.get(packageName); if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getApplicationInfo " + packageName + ": " + p); if (p != null) { PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) return null; if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; } if (filterAppAccessLPr(ps, filterCallingUid, userId)) { return null; } // Note: isEnabledLP() does not apply here - always return info ApplicationInfo ai = PackageParser.generateApplicationInfo( p, flags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(p); } return ai; } if ("android".equals(packageName)||"system".equals(packageName)) { return mAndroidApplication; } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { // Already generates the external package name return generateApplicationInfoFromSettingsLPw(packageName, flags, filterCallingUid, userId); } } return null; } @GuardedBy("mPackages") private String normalizePackageNameLPr(String packageName) { String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName); return normalizedPackageName != null ? normalizedPackageName : packageName; } @Override public void deletePreloadsFileCache() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CLEAR_APP_CACHE, "deletePreloadsFileCache"); File dir = Environment.getDataPreloadsFileCacheDirectory(); Slog.i(TAG, "Deleting preloaded file cache " + dir); FileUtils.deleteContents(dir); } @Override public void freeStorageAndNotify(final String volumeUuid, final long freeStorageSize, final int storageFlags, final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); mHandler.post(() -> { boolean success = false; try { freeStorage(volumeUuid, freeStorageSize, storageFlags); success = true; } catch (IOException e) { Slog.w(TAG, e); } if (observer != null) { try { observer.onRemoveCompleted(null, success); } catch (RemoteException e) { Slog.w(TAG, e); } } }); } @Override public void freeStorage(final String volumeUuid, final long freeStorageSize, final int storageFlags, final IntentSender pi) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, TAG); mHandler.post(() -> { boolean success = false; try { freeStorage(volumeUuid, freeStorageSize, storageFlags); success = true; } catch (IOException e) { Slog.w(TAG, e); } if (pi != null) { try { pi.sendIntent(null, success ? 1 : 0, null, null, null); } catch (SendIntentException e) { Slog.w(TAG, e); } } }); } /** * Blocking call to clear various types of cached data across the system * until the requested bytes are available. */ public void freeStorage(String volumeUuid, long bytes, int storageFlags) throws IOException { final StorageManager storage = mContext.getSystemService(StorageManager.class); final File file = storage.findPathForUuid(volumeUuid); if (file.getUsableSpace() >= bytes) return; if (ENABLE_FREE_CACHE_V2) { final boolean internalVolume = Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid); final boolean aggressive = (storageFlags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0; final long reservedBytes = storage.getStorageCacheBytes(file, storageFlags); // 1. Pre-flight to determine if we have any chance to succeed // 2. Consider preloaded data (after 1w honeymoon, unless aggressive) if (internalVolume && (aggressive || SystemProperties .getBoolean("persist.sys.preloads.file_cache_expired", false))) { deletePreloadsFileCache(); if (file.getUsableSpace() >= bytes) return; } // 3. Consider parsed APK data (aggressive only) if (internalVolume && aggressive) { FileUtils.deleteContents(mCacheDir); if (file.getUsableSpace() >= bytes) return; } // 4. Consider cached app data (above quotas) try { mInstaller.freeCache(volumeUuid, bytes, reservedBytes, Installer.FLAG_FREE_CACHE_V2); } catch (InstallerException ignored) { } if (file.getUsableSpace() >= bytes) return; // 5. Consider shared libraries with refcount=0 and age>min cache period if (internalVolume && pruneUnusedStaticSharedLibraries(bytes, android.provider.Settings.Global.getLong(mContext.getContentResolver(), Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD, DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD))) { return; } // 6. Consider dexopt output (aggressive only) // TODO: Implement // 7. Consider installed instant apps unused longer than min cache period if (internalVolume && mInstantAppRegistry.pruneInstalledInstantApps(bytes, android.provider.Settings.Global.getLong(mContext.getContentResolver(), Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD, InstantAppRegistry.DEFAULT_INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) { return; } // 8. Consider cached app data (below quotas) try { mInstaller.freeCache(volumeUuid, bytes, reservedBytes, Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA); } catch (InstallerException ignored) { } if (file.getUsableSpace() >= bytes) return; // 9. Consider DropBox entries // TODO: Implement // 10. Consider instant meta-data (uninstalled apps) older that min cache period if (internalVolume && mInstantAppRegistry.pruneUninstalledInstantApps(bytes, android.provider.Settings.Global.getLong(mContext.getContentResolver(), Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD, InstantAppRegistry.DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) { return; } } else { try { mInstaller.freeCache(volumeUuid, bytes, 0, 0); } catch (InstallerException ignored) { } if (file.getUsableSpace() >= bytes) return; } throw new IOException("Failed to free " + bytes + " on storage device at " + file); } private boolean pruneUnusedStaticSharedLibraries(long neededSpace, long maxCachePeriod) throws IOException { final StorageManager storage = mContext.getSystemService(StorageManager.class); final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL); List packagesToDelete = null; final long now = System.currentTimeMillis(); synchronized (mPackages) { final int[] allUsers = sUserManager.getUserIds(); final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { final LongSparseArray versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } final int versionCount = versionedLib.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libInfo = versionedLib.valueAt(j); // Skip packages that are not static shared libs. if (!libInfo.isStatic()) { break; } // Important: We skip static shared libs used for some user since // in such a case we need to keep the APK on the device. The check for // a lib being used for any user is performed by the uninstall call. final VersionedPackage declaringPackage = libInfo.getDeclaringPackage(); // Resolve the package name - we use synthetic package names internally final String internalPackageName = resolveInternalPackageNameLPr( declaringPackage.getPackageName(), declaringPackage.getLongVersionCode()); final PackageSetting ps = mSettings.getPackageLPr(internalPackageName); // Skip unused static shared libs cached less than the min period // to prevent pruning a lib needed by a subsequently installed package. if (ps == null || now - ps.lastUpdateTime < maxCachePeriod) { continue; } if (ps.pkg.isSystem()) { continue; } if (packagesToDelete == null) { packagesToDelete = new ArrayList<>(); } packagesToDelete.add(new VersionedPackage(internalPackageName, declaringPackage.getLongVersionCode())); } } } if (packagesToDelete != null) { final int packageCount = packagesToDelete.size(); for (int i = 0; i < packageCount; i++) { final VersionedPackage pkgToDelete = packagesToDelete.get(i); // Delete the package synchronously (will fail of the lib used for any user). if (deletePackageX(pkgToDelete.getPackageName(), pkgToDelete.getLongVersionCode(), UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS) == PackageManager.DELETE_SUCCEEDED) { if (volume.getUsableSpace() >= neededSpace) { return true; } } } } return false; } /** * Update given flags based on encryption status of current user. */ private int updateFlags(int flags, int userId) { if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_AWARE)) != 0) { // Caller expressed an explicit opinion about what encryption // aware/unaware components they want to see, so fall through and // give them what they want } else { // Caller expressed no opinion, so match based on user state if (getUserManagerInternal().isUserUnlockingOrUnlocked(userId)) { flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; } else { flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE; } } return flags; } private UserManagerInternal getUserManagerInternal() { if (mUserManagerInternal == null) { mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); } return mUserManagerInternal; } private ActivityManagerInternal getActivityManagerInternal() { if (mActivityManagerInternal == null) { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); } return mActivityManagerInternal; } private ActivityTaskManagerInternal getActivityTaskManagerInternal() { if (mActivityTaskManagerInternal == null) { mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); } return mActivityTaskManagerInternal; } private DeviceIdleController.LocalService getDeviceIdleController() { if (mDeviceIdleController == null) { mDeviceIdleController = LocalServices.getService(DeviceIdleController.LocalService.class); } return mDeviceIdleController; } private StorageManagerInternal getStorageManagerInternal() { if (mStorageManagerInternal == null) { mStorageManagerInternal = LocalServices.getService(StorageManagerInternal.class); } return mStorageManagerInternal; } /** * 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; if ((flags & PackageManager.MATCH_ANY_USER) != 0) { // require the permission to be held; the calling uid and given user id referring // to the same user is not sufficient mPermissionManager.enforceCrossUserPermission( Binder.getCallingUid(), userId, false, false, !isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId), "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; } 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) { return updateFlags(flags, userId); } /** * Update given intent when being used to request {@link ResolveInfo}. */ private Intent updateIntentForResolve(Intent intent) { if (intent.getSelector() != null) { intent = intent.getSelector(); } if (DEBUG_PREFERRED) { intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); } return intent; } /** * Update given flags when being used to request {@link ResolveInfo}. * Instant apps are resolved specially, depending upon context. Minimally, * {@code}flags{@code} must have the {@link PackageManager#MATCH_INSTANT} * flag set. However, this flag is only honoured in three circumstances: * * when called from a system process * when the caller holds the permission {@code android.permission.ACCESS_INSTANT_APPS} * when resolution occurs to start an activity with a {@code android.intent.action.VIEW} * action and a {@code android.intent.category.BROWSABLE} category * */ int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid) { return updateFlagsForResolve(flags, userId, intent, callingUid, false /*wantInstantApps*/, false /*onlyExposedExplicitly*/); } int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid, boolean wantInstantApps) { return updateFlagsForResolve(flags, userId, intent, callingUid, wantInstantApps, false /*onlyExposedExplicitly*/); } int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid, boolean wantInstantApps, boolean onlyExposedExplicitly) { // Safe mode means we shouldn't match any third-party components if (mSafeMode) { flags |= PackageManager.MATCH_SYSTEM_ONLY; } if (getInstantAppPackageName(callingUid) != null) { // But, ephemeral apps see both ephemeral and exposed, non-ephemeral components if (onlyExposedExplicitly) { flags |= PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY; } flags |= PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY; flags |= PackageManager.MATCH_INSTANT; } else { final boolean wantMatchInstant = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean allowMatchInstant = wantInstantApps || (wantMatchInstant && canViewInstantApps(callingUid, userId)); flags &= ~(PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY | PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY); if (!allowMatchInstant) { flags &= ~PackageManager.MATCH_INSTANT; } } return updateFlagsForComponent(flags, userId, intent /*cookie*/); } @Override public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId); } /** * Important: The provided filterCallingUid is used exclusively to filter out activities * that can be seen based on user state. It's typically the original caller uid prior * to clearing. Because it can only be provided by trusted code, it's value can be * trusted and will be used as-is; unlike userId which will be validated by this method. */ private ActivityInfo getActivityInfoInternal(ComponentName component, int flags, int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId, component); if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get activity info"); } synchronized (mPackages) { PackageParser.Activity a = mComponentResolver.getActivity(component); if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a); if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) { return null; } return PackageParser.generateActivityInfo( a, flags, ps.readUserState(userId), userId); } if (mResolveComponentName.equals(component)) { return PackageParser.generateActivityInfo( mResolveActivity, flags, new PackageUserState(), userId); } } return null; } private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) { if (!getActivityTaskManagerInternal().isCallerRecents(callingUid)) { return false; } final long token = Binder.clearCallingIdentity(); try { final int callingUserId = UserHandle.getUserId(callingUid); if (ActivityManager.getCurrentUser() != callingUserId) { return false; } return sUserManager.isSameProfileGroup(callingUserId, targetUserId); } finally { Binder.restoreCallingIdentity(token); } } @Override public boolean activitySupportsIntent(ComponentName component, Intent intent, String resolvedType) { synchronized (mPackages) { if (component.equals(mResolveComponentName)) { // The resolver supports EVERYTHING! return true; } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); PackageParser.Activity a = mComponentResolver.getActivity(component); if (a == null) { return false; } PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) { return false; } if (filterAppAccessLPr(ps, callingUid, component, TYPE_ACTIVITY, callingUserId)) { return false; } for (int i=0; i= 0) { return true; } } return false; } } @Override public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get receiver info"); synchronized (mPackages) { PackageParser.Activity a = mComponentResolver.getReceiver(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 || mContext.checkCallingOrSelfPermission( Manifest.permission.ACCESS_SHARED_LIBRARIES) == PERMISSION_GRANTED; synchronized (mPackages) { List result = null; final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { LongSparseArray versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } final int versionCount = versionedLib.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libInfo = versionedLib.valueAt(j); 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.getPath(), libInfo.getPackageName(), libInfo.getAllCodePaths(), libInfo.getName(), libInfo.getLongVersion(), libInfo.getType(), libInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr(libInfo, flags, userId), (libInfo.getDependencies() == null ? null : new ArrayList<>(libInfo.getDependencies()))); if (result == null) { result = new ArrayList<>(); } result.add(resLibInfo); } } return result != null ? new ParceledListSlice<>(result) : null; } } @Nullable @Override public ParceledListSlice getDeclaredSharedLibraries( @NonNull String packageName, int flags, @NonNull int userId) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES, "getDeclaredSharedLibraries"); int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "getDeclaredSharedLibraries"); Preconditions.checkNotNull(packageName, "packageName cannot be null"); Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0"); if (!sUserManager.exists(userId)) { return null; } if (getInstantAppPackageName(callingUid) != null) { return null; } synchronized (mPackages) { List result = null; int libraryCount = mSharedLibraries.size(); for (int i = 0; i < libraryCount; i++) { LongSparseArray versionedLibrary = mSharedLibraries.valueAt(i); if (versionedLibrary == null) { continue; } int versionCount = versionedLibrary.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libraryInfo = versionedLibrary.valueAt(j); VersionedPackage declaringPackage = libraryInfo.getDeclaringPackage(); if (!Objects.equals(declaringPackage.getPackageName(), packageName)) { continue; } long identity = Binder.clearCallingIdentity(); try { PackageInfo packageInfo = getPackageInfoVersioned(declaringPackage, flags | PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId); if (packageInfo == null) { continue; } } finally { Binder.restoreCallingIdentity(identity); } SharedLibraryInfo resultLibraryInfo = new SharedLibraryInfo( libraryInfo.getPath(), libraryInfo.getPackageName(), libraryInfo.getAllCodePaths(), libraryInfo.getName(), libraryInfo.getLongVersion(), libraryInfo.getType(), libraryInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr( libraryInfo, flags, userId), libraryInfo.getDependencies() == null ? null : new ArrayList<>(libraryInfo.getDependencies())); if (result == null) { result = new ArrayList<>(); } result.add(resultLibraryInfo); } } return result != null ? new ParceledListSlice<>(result) : null; } } @GuardedBy("mPackages") 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.readUserState(userId).isAvailable(flags)) { continue; } final String libName = libInfo.getName(); if (libInfo.isStatic()) { final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName); if (libIdx < 0) { continue; } if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getLongVersion()) { continue; } if (versionedPackages == null) { versionedPackages = new ArrayList<>(); } // If the dependent is a static shared lib, use the public package name String dependentPackageName = ps.name; if (ps.pkg != null && ps.pkg.applicationInfo.isStaticSharedLibrary()) { dependentPackageName = ps.pkg.manifestPackageName; } versionedPackages.add(new VersionedPackage(dependentPackageName, ps.versionCode)); } else if (ps.pkg != null) { if (ArrayUtils.contains(ps.pkg.usesLibraries, libName) || ArrayUtils.contains(ps.pkg.usesOptionalLibraries, libName)) { if (versionedPackages == null) { versionedPackages = new ArrayList<>(); } versionedPackages.add(new VersionedPackage(ps.name, ps.versionCode)); } } } return versionedPackages; } @Override public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get service info"); synchronized (mPackages) { PackageParser.Service s = mComponentResolver.getService(component); if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getServiceInfo " + component + ": " + s); if (s != null && mSettings.isEnabledAndMatchLPr(s.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, callingUid, component, TYPE_SERVICE, userId)) { return null; } return PackageParser.generateServiceInfo( s, flags, ps.readUserState(userId), userId); } } return null; } @Override public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get provider info"); synchronized (mPackages) { PackageParser.Provider p = mComponentResolver.getProvider(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 ModuleInfo getModuleInfo(String packageName, @ModuleInfoFlags int flags) { return mModuleInfoProvider.getModuleInfo(packageName, flags); } @Override public List getInstalledModules(int flags) { return mModuleInfoProvider.getInstalledModules(flags); } @Override public String[] getSystemSharedLibraryNames() { // allow instant applications synchronized (mPackages) { Set libs = null; final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { LongSparseArray versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } final int versionCount = versionedLib.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libraryInfo = versionedLib.valueAt(j); if (!libraryInfo.isStatic()) { if (libs == null) { libs = new ArraySet<>(); } libs.add(libraryInfo.getName()); break; } PackageSetting ps = mSettings.getPackageLPr(libraryInfo.getPackageName()); if (ps != null && !filterSharedLibPackageLPr(ps, Binder.getCallingUid(), UserHandle.getUserId(Binder.getCallingUid()), PackageManager.MATCH_STATIC_SHARED_LIBRARIES)) { if (libs == null) { libs = new ArraySet<>(); } libs.add(libraryInfo.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; } } @GuardedBy("mPackages") 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) { final CheckPermissionDelegate checkPermissionDelegate; synchronized (mPackages) { if (mCheckPermissionDelegate == null) { return checkPermissionImpl(permName, pkgName, userId); } checkPermissionDelegate = mCheckPermissionDelegate; } return checkPermissionDelegate.checkPermission(permName, pkgName, userId, PackageManagerService.this::checkPermissionImpl); } private int checkPermissionImpl(String permName, String pkgName, int userId) { return mPermissionManager.checkPermission(permName, pkgName, getCallingUid(), userId); } @Override public int checkUidPermission(String permName, int uid) { final CheckPermissionDelegate checkPermissionDelegate; synchronized (mPackages) { if (mCheckPermissionDelegate == null) { return checkUidPermissionImpl(permName, uid); } checkPermissionDelegate = mCheckPermissionDelegate; } return checkPermissionDelegate.checkUidPermission(permName, uid, PackageManagerService.this::checkUidPermissionImpl); } private int checkUidPermissionImpl(String permName, int uid) { synchronized (mPackages) { final String[] packageNames = getPackagesForUid(uid); PackageParser.Package pkg = null; final int N = packageNames == null ? 0 : packageNames.length; for (int i = 0; pkg == null && i < N; i++) { pkg = mPackages.get(packageNames[i]); } return mPermissionManager.checkUidPermission(permName, pkg, uid, getCallingUid()); } } @Override public boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId) { if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "isPermissionRevokedByPolicy for user " + userId); } if (checkPermission(permission, packageName, userId) == PackageManager.PERMISSION_GRANTED) { return false; } final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { if (!isCallerSameApp(packageName, callingUid)) { return false; } } else { if (isInstantApp(packageName, userId)) { return false; } } final long identity = Binder.clearCallingIdentity(); try { final int flags = getPermissionFlags(permission, packageName, userId); return (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0; } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getPermissionControllerPackageName() { synchronized (mPackages) { return mRequiredPermissionControllerPackage; } } String getPackageInstallerPackageName() { synchronized (mPackages) { return mRequiredInstallerPackage; } } private boolean addDynamicPermission(PermissionInfo info, final boolean async) { return mPermissionManager.addDynamicPermission( info, async, getCallingUid(), new PermissionCallback() { @Override public void onPermissionChanged() { if (!async) { mSettings.writeLPr(); } else { scheduleWriteSettingsLocked(); } } }); } @Override public boolean addPermission(PermissionInfo info) { synchronized (mPackages) { return addDynamicPermission(info, false); } } @Override public boolean addPermissionAsync(PermissionInfo info) { synchronized (mPackages) { return addDynamicPermission(info, true); } } @Override public void removePermission(String permName) { mPermissionManager.removeDynamicPermission(permName, getCallingUid(), mPermissionCallback); } @Override public void grantRuntimePermission(String packageName, String permName, final int userId) { boolean overridePolicy = (checkUidPermission( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED); mPermissionManager.grantRuntimePermission(permName, packageName, overridePolicy, getCallingUid(), userId, mPermissionCallback); } @Override public void revokeRuntimePermission(String packageName, String permName, int userId) { boolean overridePolicy = (checkUidPermission( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED); mPermissionManager.revokeRuntimePermission(permName, packageName, overridePolicy, userId, mPermissionCallback); } @Override public void resetRuntimePermissions() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, "revokeRuntimePermission"); int callingUid = Binder.getCallingUid(); if (callingUid != Process.SYSTEM_UID && callingUid != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "resetRuntimePermissions"); } synchronized (mPackages) { mPermissionManager.updateAllPermissions( StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(), mPermissionCallback); for (int userId : UserManagerService.getInstance().getUserIds()) { final int packageCount = mPackages.size(); for (int i = 0; i < packageCount; i++) { PackageParser.Package pkg = mPackages.valueAt(i); if (!(pkg.mExtras instanceof PackageSetting)) { continue; } PackageSetting ps = (PackageSetting) pkg.mExtras; resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, userId); } } } } @Override public int getPermissionFlags(String permName, String packageName, int userId) { return mPermissionManager.getPermissionFlags( permName, packageName, getCallingUid(), userId); } @Override public void updatePermissionFlags(String permName, String packageName, int flagMask, int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) { int callingUid = getCallingUid(); boolean overridePolicy = false; if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) { long callingIdentity = Binder.clearCallingIdentity(); try { if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0) { if (checkAdjustPolicyFlagPermission) { mContext.enforceCallingOrSelfPermission( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "Need " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " to change policy flags"); } else if (!hasTargetSdkInUidLowerThan(callingUid, Build.VERSION_CODES.Q)) { throw new IllegalArgumentException( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " needs " + " to be checked for packages targeting " + Build.VERSION_CODES.Q + " or later when changing policy " + "flags"); } overridePolicy = true; } } finally { Binder.restoreCallingIdentity(callingIdentity); } } mPermissionManager.updatePermissionFlags( permName, packageName, flagMask, flagValues, callingUid, userId, overridePolicy, mPermissionCallback); } /** * Update the permission flags for all packages and runtime permissions of a user in order * to allow device or profile owner to remove POLICY_FIXED. */ @Override public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) { synchronized (mPackages) { final boolean changed = mPermissionManager.updatePermissionFlagsForAllApps( flagMask, flagValues, getCallingUid(), userId, mPackages.values(), mPermissionCallback); if (changed) { mSettings.writeRuntimePermissionsForUserLPr(userId, false); } } } @Override public @Nullable List getWhitelistedRestrictedPermissions(@NonNull String packageName, @PermissionWhitelistFlags int whitelistFlags, @UserIdInt int userId) { Preconditions.checkNotNull(packageName); Preconditions.checkFlagsArgument(whitelistFlags, PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER); Preconditions.checkArgumentNonNegative(userId, null); if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS, "getWhitelistedRestrictedPermissions for user " + userId); } final PackageParser.Package pkg; synchronized (mPackages) { final PackageSetting packageSetting = mSettings.mPackages.get(packageName); if (packageSetting == null) { Slog.w(TAG, "Unknown package: " + packageName); return null; } pkg = packageSetting.pkg; final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) == PackageManager.PERMISSION_GRANTED; final PackageSetting installerPackageSetting = mSettings.mPackages.get( packageSetting.installerPackageName); final boolean isCallerInstallerOnRecord = installerPackageSetting != null && UserHandle.isSameApp(installerPackageSetting.appId, Binder.getCallingUid()); if ((whitelistFlags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 && !isCallerPrivileged) { throw new SecurityException("Querying system whitelist requires " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } if ((whitelistFlags & (PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) != 0) { if (!isCallerPrivileged && !isCallerInstallerOnRecord) { throw new SecurityException("Querying upgrade or installer whitelist" + " requires being installer on record or " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } } if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(), UserHandle.getCallingUserId())) { return null; } } final long identity = Binder.clearCallingIdentity(); try { return mPermissionManager.getWhitelistedRestrictedPermissions( pkg, whitelistFlags, userId); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean addWhitelistedRestrictedPermission(@NonNull String packageName, @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags, @UserIdInt int userId) { // Other argument checks are done in get/setWhitelistedRestrictedPermissions Preconditions.checkNotNull(permission); if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) { return false; } List permissions = getWhitelistedRestrictedPermissions(packageName, whitelistFlags, userId); if (permissions == null) { permissions = new ArrayList<>(1); } if (permissions.indexOf(permission) < 0) { permissions.add(permission); return setWhitelistedRestrictedPermissions(packageName, permissions, whitelistFlags, userId); } return false; } private boolean checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission( @NonNull String permission) { synchronized (mPackages) { final BasePermission bp = mPermissionManager.getPermissionTEMP(permission); if (bp == null) { Slog.w(TAG, "No such permissions: " + permission); return false; } if (bp.isHardOrSoftRestricted() && bp.isImmutablyRestricted() && mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Cannot modify whitelisting of an immutably " + "restricted permission: " + permission); } return true; } } @Override public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName, @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags, @UserIdInt int userId) { // Other argument checks are done in get/setWhitelistedRestrictedPermissions Preconditions.checkNotNull(permission); if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) { return false; } final List permissions = getWhitelistedRestrictedPermissions(packageName, whitelistFlags, userId); if (permissions != null && permissions.remove(permission)) { return setWhitelistedRestrictedPermissions(packageName, permissions, whitelistFlags, userId); } return false; } private boolean setWhitelistedRestrictedPermissions(@NonNull String packageName, @Nullable List permissions, @PermissionWhitelistFlags int whitelistFlag, @UserIdInt int userId) { Preconditions.checkNotNull(packageName); Preconditions.checkFlagsArgument(whitelistFlag, PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER); Preconditions.checkArgument(Integer.bitCount(whitelistFlag) == 1); Preconditions.checkArgumentNonNegative(userId, null); if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingOrSelfPermission( Manifest.permission.INTERACT_ACROSS_USERS, "setWhitelistedRestrictedPermissions for user " + userId); } final PackageParser.Package pkg; synchronized (mPackages) { final PackageSetting packageSetting = mSettings.mPackages.get(packageName); if (packageSetting == null) { Slog.w(TAG, "Unknown package: " + packageName); return false; } pkg = packageSetting.pkg; final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) == PackageManager.PERMISSION_GRANTED; final PackageSetting installerPackageSetting = mSettings.mPackages.get( packageSetting.installerPackageName); final boolean isCallerInstallerOnRecord = installerPackageSetting != null && UserHandle.isSameApp(installerPackageSetting.appId, Binder.getCallingUid()); if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 && !isCallerPrivileged) { throw new SecurityException("Modifying system whitelist requires " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) { if (!isCallerPrivileged && !isCallerInstallerOnRecord) { throw new SecurityException("Modifying upgrade whitelist requires" + " being installer on record or " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } final List whitelistedPermissions = getWhitelistedRestrictedPermissions( packageName, whitelistFlag, userId); if (permissions == null || permissions.isEmpty()) { if (whitelistedPermissions == null || whitelistedPermissions.isEmpty()) { return true; } } else { // Only the system can add and remove while the installer can only remove. final int permissionCount = permissions.size(); for (int i = 0; i < permissionCount; i++) { if ((whitelistedPermissions == null || !whitelistedPermissions.contains(permissions.get(i))) && !isCallerPrivileged) { throw new SecurityException("Adding to upgrade whitelist requires" + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } } } } if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) { if (!isCallerPrivileged && !isCallerInstallerOnRecord) { throw new SecurityException("Modifying installer whitelist requires" + " being installer on record or " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } } if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(), UserHandle.getCallingUserId())) { return false; } } final long identity = Binder.clearCallingIdentity(); try { mPermissionManager.setWhitelistedRestrictedPermissions(pkg, new int[]{userId}, permissions, Process.myUid(), whitelistFlag, mPermissionCallback); } finally { Binder.restoreCallingIdentity(identity); } return true; } @Override public boolean shouldShowRequestPermissionRationale(String permissionName, String packageName, int userId) { if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "canShowRequestPermissionRationale for user " + userId); } final int uid = getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId); if (UserHandle.getAppId(getCallingUid()) != UserHandle.getAppId(uid)) { return false; } if (checkPermission(permissionName, packageName, userId) == PackageManager.PERMISSION_GRANTED) { return false; } final int flags; final long identity = Binder.clearCallingIdentity(); try { flags = getPermissionFlags(permissionName, packageName, userId); } finally { Binder.restoreCallingIdentity(identity); } final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED | PackageManager.FLAG_PERMISSION_POLICY_FIXED | PackageManager.FLAG_PERMISSION_USER_FIXED; if ((flags & fixedFlags) != 0) { return false; } return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; } @Override public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { mContext.enforceCallingOrSelfPermission( Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS, "addOnPermissionsChangeListener"); synchronized (mPackages) { mOnPermissionChangeListeners.addListenerLocked(listener); } } @Override public void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { throw new SecurityException("Instant applications don't have access to this method"); } synchronized (mPackages) { mOnPermissionChangeListeners.removeListenerLocked(listener); } } @Override public boolean isProtectedBroadcast(String actionName) { // allow instant applications synchronized (mProtectedBroadcasts) { if (mProtectedBroadcasts.contains(actionName)) { return true; } else if (actionName != null) { // TODO: remove these terrible hacks if (actionName.startsWith("android.net.netmon.lingerExpired") || actionName.startsWith("com.android.server.sip.SipWakeupTimer") || actionName.startsWith("com.android.internal.telephony.data-reconnect") || actionName.startsWith("android.net.netmon.launchCaptivePortalApp")) { return true; } } } return false; } @Override public int checkSignatures(String pkg1, String pkg2) { synchronized (mPackages) { final PackageParser.Package p1 = mPackages.get(pkg1); final PackageParser.Package p2 = mPackages.get(pkg2); if (p1 == null || p1.mExtras == null || p2 == null || p2.mExtras == null) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps1 = (PackageSetting) p1.mExtras; final PackageSetting ps2 = (PackageSetting) p2.mExtras; if (filterAppAccessLPr(ps1, callingUid, callingUserId) || filterAppAccessLPr(ps2, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } return compareSignatures(p1.mSigningDetails.signatures, p2.mSigningDetails.signatures); } } @Override public int checkUidSignatures(int uid1, int uid2) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; // Map to base uids. final int appId1 = UserHandle.getAppId(uid1); final int appId2 = UserHandle.getAppId(uid2); // reader synchronized (mPackages) { Signature[] s1; Signature[] s2; Object obj = mSettings.getSettingLPr(appId1); if (obj != null) { if (obj instanceof SharedUserSetting) { if (isCallerInstantApp) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s1 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s1 = ps.signatures.mSigningDetails.signatures; } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } obj = mSettings.getSettingLPr(appId2); if (obj != null) { if (obj instanceof SharedUserSetting) { if (isCallerInstantApp) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s2 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s2 = ps.signatures.mSigningDetails.signatures; } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } return compareSignatures(s1, s2); } } @Override public boolean hasSigningCertificate( String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) { synchronized (mPackages) { final PackageParser.Package p = mPackages.get(packageName); if (p == null || p.mExtras == null) { return false; } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return false; } switch (type) { case CERT_INPUT_RAW_X509: return p.mSigningDetails.hasCertificate(certificate); case CERT_INPUT_SHA256: return p.mSigningDetails.hasSha256Certificate(certificate); default: return false; } } } @Override public boolean hasUidSigningCertificate( int uid, byte[] certificate, @PackageManager.CertificateInputType int type) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); // Map to base uids. final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final PackageParser.SigningDetails signingDetails; final Object obj = mSettings.getSettingLPr(appId); if (obj != null) { if (obj instanceof SharedUserSetting) { final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; if (isCallerInstantApp) { return false; } signingDetails = ((SharedUserSetting)obj).signatures.mSigningDetails; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return false; } signingDetails = ps.signatures.mSigningDetails; } else { return false; } } else { return false; } switch (type) { case CERT_INPUT_RAW_X509: return signingDetails.hasCertificate(certificate); case CERT_INPUT_SHA256: return signingDetails.hasSha256Certificate(certificate); default: return false; } } } /** * This method should typically only be used when granting or revoking * permissions, since the app may immediately restart after this call. * * If you're doing surgery on app code/data, use {@link PackageFreezer} to * guard your work against the app being relaunched. */ private void killUid(int appId, int userId, String reason) { final long identity = Binder.clearCallingIdentity(); try { IActivityManager am = ActivityManager.getService(); if (am != null) { try { am.killUid(appId, userId, reason); } catch (RemoteException e) { /* ignore - same process */ } } } finally { Binder.restoreCallingIdentity(identity); } } /** * If the database version for this type of package (internal storage or * external storage) is less than the version where package signatures * were updated, return true. */ private boolean isCompatSignatureUpdateNeeded(PackageParser.Package scannedPkg) { return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg)); } private static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) { return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY; } private boolean isRecoverSignatureUpdateNeeded(PackageParser.Package scannedPkg) { return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg)); } private static boolean isRecoverSignatureUpdateNeeded(VersionInfo ver) { return ver.databaseVersion < DatabaseVersion.SIGNATURE_MALFORMED_RECOVER; } @Override public List getAllPackages() { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); synchronized (mPackages) { if (canViewInstantApps(callingUid, callingUserId)) { return new ArrayList<>(mPackages.keySet()); } final String instantAppPkgName = getInstantAppPackageName(callingUid); final List result = new ArrayList<>(); if (instantAppPkgName != null) { // caller is an instant application; filter unexposed applications for (PackageParser.Package pkg : mPackages.values()) { if (!pkg.visibleToInstantApps) { continue; } result.add(pkg.packageName); } } else { // caller is a normal application; filter instant applications for (PackageParser.Package pkg : mPackages.values()) { final PackageSetting ps = pkg.mExtras != null ? (PackageSetting) pkg.mExtras : null; if (ps != null && ps.getInstantApp(callingUserId) && !mInstantAppRegistry.isInstantAccessGranted( callingUserId, UserHandle.getAppId(callingUid), ps.appId)) { continue; } result.add(pkg.packageName); } } return result; } } /** * IMPORTANT: Not all packages returned by this method may be known * to the system. There are two conditions in which this may occur: * * The package is on adoptable storage and the device has been removed * The package is being removed and the internal structures are partially updated * * The second is an artifact of the current data structures and should be fixed. See * b/111075456 for one such instance. */ @Override public String[] getPackagesForUid(int uid) { final int callingUid = Binder.getCallingUid(); final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; final int userId = UserHandle.getUserId(uid); final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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 appId = UserHandle.getAppId(uids[i]); final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; final Iterator it = sus.packages.iterator(); while (it.hasNext()) { if (it.next().isPrivileged()) { return true; } } } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; return ps.isPrivileged(); } } return false; } @Override public String[] getAppOpPermissionPackages(String permName) { return mPermissionManager.getAppOpPermissionPackages(permName); } @Override public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { return resolveIntentInternal(intent, resolvedType, flags, userId, false, Binder.getCallingUid()); } /** * Normally instant apps can only be resolved when they're visible to the caller. * However, if {@code resolveForStart} is {@code true}, all instant apps are visible * since we need to allow the system to start any installed application. */ private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType, int flags, int userId, boolean resolveForStart, int filterCallingUid) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent"); if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "resolve intent"); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, filterCallingUid, userId, resolveForStart, true /*allowDynamicSplits*/); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); final ResolveInfo bestChoice = chooseBestActivity(intent, resolvedType, flags, query, userId); return bestChoice; } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @Override public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) { if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) { throw new SecurityException( "findPersistentPreferredActivity can only be run by the system"); } if (!sUserManager.exists(userId)) { return null; } final int callingUid = Binder.getCallingUid(); intent = updateIntentForResolve(intent); final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver()); final int flags = updateFlagsForResolve( 0, userId, intent, callingUid, false /*includeInstantApps*/); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); synchronized (mPackages) { return findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false, userId); } } @Override public void setLastChosenActivity(Intent intent, String resolvedType, int flags, IntentFilter filter, int match, ComponentName activity) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; } final int userId = UserHandle.getCallingUserId(); if (DEBUG_PREFERRED) { Log.v(TAG, "setLastChosenActivity intent=" + intent + " resolvedType=" + resolvedType + " flags=" + flags + " filter=" + filter + " match=" + match + " activity=" + activity); filter.dump(new PrintStreamPrinter(System.out), " "); } intent.setComponent(null); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); // Find any earlier preferred or last chosen entries and nuke them findPreferredActivityNotLocked( 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 findPreferredActivityNotLocked( intent, resolvedType, flags, query, 0, false, false, false, userId); } /** * Returns whether or not instant apps have been disabled remotely. */ private boolean areWebInstantAppsDisabled(int userId) { return mWebInstantAppsDisabled.get(userId); } private boolean isInstantAppResolutionAllowed( Intent intent, List resolvedActivities, int userId, boolean skipPackageCheck) { if (mInstantAppResolverConnection == null) { return false; } if (mInstantAppInstallerActivity == null) { return false; } if (intent.getComponent() != null) { return false; } if ((intent.getFlags() & Intent.FLAG_IGNORE_EPHEMERAL) != 0) { return false; } if (!skipPackageCheck && intent.getPackage() != null) { return false; } if (!intent.isWebIntent()) { // for non web intents, we should not resolve externally if an app already exists to // handle it or if the caller didn't explicitly request it. if ((resolvedActivities != null && resolvedActivities.size() != 0) || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) { return false; } } else { if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) { return false; } else if (areWebInstantAppsDisabled(userId)) { return false; } } // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. // Or if there's already an ephemeral app installed that handles the action synchronized (mPackages) { final int count = (resolvedActivities == null ? 0 : resolvedActivities.size()); for (int n = 0; n < count; n++) { final ResolveInfo info = resolvedActivities.get(n); final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { // only check domain verification status if the app is not a browser if (!info.handleAllWebDataURI) { // Try to get the status from User settings first final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int) (packedStatus >> 32); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName + ", status: " + status); } return false; } } if (ps.getInstantApp(userId)) { if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app installed;" + " pkg: " + packageName); } return false; } } } } // We've exhausted all ways to deny ephemeral application; let the system look for them. return true; } private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj, Intent origIntent, String resolvedType, String callingPackage, Bundle verificationBundle, int userId) { final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO, new InstantAppRequest(responseObj, origIntent, resolvedType, callingPackage, userId, verificationBundle, false /*resolveForStart*/)); mHandler.sendMessage(msg); } private ResolveInfo chooseBestActivity(Intent intent, String resolvedType, int flags, List query, int userId) { if (query != null) { final int N = query.size(); if (N == 1) { return query.get(0); } else if (N > 1) { final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); // If there is more than one activity with the same priority, // then let the user decide between them. ResolveInfo r0 = query.get(0); ResolveInfo r1 = query.get(1); if (DEBUG_INTENT_MATCHING || debug) { Slog.v(TAG, r0.activityInfo.name + "=" + r0.priority + " vs " + r1.activityInfo.name + "=" + r1.priority); } // If the first activity has a higher priority, or a different // default, then it is always desirable to pick it. if (r0.priority != r1.priority || r0.preferredOrder != r1.preferredOrder || r0.isDefault != r1.isDefault) { return query.get(0); } // If we have saved a preference for a preferred activity for // this Intent, use that. ResolveInfo ri = findPreferredActivityNotLocked(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; } @GuardedBy("mPackages") 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; jmust not hold {@link #mPackages} */ ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags, List query, int priority, boolean always, boolean removeMatches, boolean debug, int userId) { if (Thread.holdsLock(mPackages)) { Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding mPackages", new Throwable()); } if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); // Do NOT hold the packages lock; this calls up into the settings provider which // could cause a deadlock. final boolean isDeviceProvisioned = android.provider.Settings.Global.getInt(mContext.getContentResolver(), android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1; 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"); } } final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent) && !isDeviceProvisioned; if (ai == null) { // Do not remove launcher's preferred activity during SetupWizard // due to it may not install yet if (excludeSetupWizardHomeActivity) { continue; } // This previously registered preferred activity // component is no longer known. Most likely an update // to the app was installed and in the new version this // component no longer exists. Clean it up by removing // it from the preferred activities list, and skip it. Slog.w(TAG, "Removing dangling preferred activity: " + pa.mPref.mComponent); pir.removeFilter(pa); changed = true; continue; } for (int j=0; j matches = getMatchingCrossProfileIntentFilters(intent, resolvedType, sourceUserId); if (matches != null) { int size = matches.size(); for (int i = 0; i < size; i++) { if (matches.get(i).getTargetUserId() == targetUserId) return true; } } if (intent.hasWebURI()) { // cross-profile app linking works only towards the parent. final int callingUid = Binder.getCallingUid(); final UserInfo parent = getProfileParent(sourceUserId); synchronized(mPackages) { int flags = updateFlagsForResolve(0, parent.id, intent, callingUid, false /*includeInstantApps*/); CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr( intent, resolvedType, flags, sourceUserId, parent.id); return xpDomainInfo != null; } } return false; } private UserInfo getProfileParent(int userId) { final long identity = Binder.clearCallingIdentity(); try { return sUserManager.getProfileParent(userId); } finally { Binder.restoreCallingIdentity(identity); } } private List getMatchingCrossProfileIntentFilters(Intent intent, String resolvedType, int userId) { CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId); if (resolver != null) { return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId); } return null; } @Override public @NonNull ParceledListSlice queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); return new ParceledListSlice<>( queryIntentActivitiesInternal(intent, resolvedType, flags, userId)); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } /** * Returns the package name of the calling Uid if it's an instant app. If it isn't * instant, returns {@code null}. */ private String getInstantAppPackageName(int callingUid) { synchronized (mPackages) { // If the caller is an isolated app use the owner's uid for the lookup. if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final int appId = UserHandle.getAppId(callingUid); final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid)); return isInstantApp ? ps.pkg.packageName : null; } } return null; } private @NonNull List queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int userId) { return queryIntentActivitiesInternal( intent, resolvedType, flags, Binder.getCallingUid(), userId, false /*resolveForStart*/, true /*allowDynamicSplits*/); } private @NonNull List queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final String instantAppPkgName = getInstantAppPackageName(filterCallingUid); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "query intent activities"); final String pkgName = intent.getPackage(); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart, comp != null || pkgName != null /*onlyExposedExplicitly*/); if (comp != null) { final List list = new ArrayList<>(1); final ActivityInfo ai = getActivityInfo(comp, flags, userId); if (ai != null) { // When specifying an explicit component, we prevent the activity from being // used when either 1) the calling package is normal and the activity is within // an ephemeral application or 2) the calling package is ephemeral and the // activity is not visible to ephemeral applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean matchExplicitlyVisibleOnly = (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (ai.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetVisibleToInstantApp = (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean isTargetExplicitlyVisibleToInstantApp = isTargetVisibleToInstantApp && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; final boolean isTargetHiddenFromInstantApp = !isTargetVisibleToInstantApp || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp); final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } } return applyPostResolutionFilter( list, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // reader boolean sortResult = false; boolean addInstant = false; List result; synchronized (mPackages) { if (pkgName == null) { List matchingFilters = getMatchingCrossProfileIntentFilters(intent, resolvedType, userId); // Check for results that need to skip the current profile. ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent, resolvedType, flags, userId); if (xpResolveInfo != null) { List xpResult = new ArrayList<>(1); xpResult.add(xpResolveInfo); return applyPostResolutionFilter( filterIfNotSystemUser(xpResult, userId), instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // Check for results in the current profile. result = filterIfNotSystemUser(mComponentResolver.queryActivities( intent, resolvedType, flags, userId), userId); addInstant = isInstantAppResolutionAllowed(intent, result, userId, false /*skipPackageCheck*/); // Check for cross profile results. boolean hasNonNegativePriorityResult = hasNonNegativePriority(result); xpResolveInfo = queryCrossProfileIntents( matchingFilters, intent, resolvedType, flags, userId, hasNonNegativePriorityResult); if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) { boolean isVisibleToUser = filterIfNotSystemUser( Collections.singletonList(xpResolveInfo), userId).size() > 0; if (isVisibleToUser) { result.add(xpResolveInfo); sortResult = true; } } if (intent.hasWebURI()) { CrossProfileDomainInfo xpDomainInfo = null; final UserInfo parent = getProfileParent(userId); if (parent != null) { xpDomainInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType, flags, userId, parent.id); } if (xpDomainInfo != null) { if (xpResolveInfo != null) { // If we didn't remove it, the cross-profile ResolveInfo would be twice // in the result. result.remove(xpResolveInfo); } if (result.size() == 0 && !addInstant) { // No result in current profile, but found candidate in parent user. // And we are not going to add emphemeral app, so we can return the // result straight away. result.add(xpDomainInfo.resolveInfo); return applyPostResolutionFilter(result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } } else if (result.size() <= 1 && !addInstant) { // No result in parent user and <= 1 result in current profile, and we // are not going to add emphemeral app, so we can return the result without // further processing. return applyPostResolutionFilter(result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // We have more than one candidate (combining results from current and parent // profile), so we need filtering and sorting. result = filterCandidatesWithDomainPreferredActivitiesLPr( intent, flags, result, xpDomainInfo, userId); sortResult = true; } } else { final PackageParser.Package pkg = mPackages.get(pkgName); result = null; if (pkg != null) { result = filterIfNotSystemUser(mComponentResolver.queryActivities( intent, resolvedType, flags, pkg.activities, userId), userId); } if (result == null || result.size() == 0) { // the caller wants to resolve for a particular package; however, there // were no installed results, so, try to find an ephemeral result addInstant = isInstantAppResolutionAllowed( intent, null /*result*/, userId, true /*skipPackageCheck*/); if (result == null) { result = new ArrayList<>(); } } } } if (addInstant) { result = maybeAddInstantAppInstaller( result, intent, resolvedType, flags, userId, resolveForStart); } if (sortResult) { Collections.sort(result, RESOLVE_PRIORITY_SORTER); } return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } private List maybeAddInstantAppInstaller(List result, Intent intent, String resolvedType, int flags, int userId, boolean resolveForStart) { // first, check to see if we've got an instant app already installed final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0; ResolveInfo localInstantApp = null; boolean blockResolution = false; if (!alreadyResolvedLocally) { final List instantApps = mComponentResolver.queryActivities( intent, resolvedType, flags | PackageManager.GET_RESOLVED_FILTER | PackageManager.MATCH_INSTANT | PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY, userId); for (int i = instantApps.size() - 1; i >= 0; --i) { final ResolveInfo info = instantApps.get(i); final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps.getInstantApp(userId)) { final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int)(packedStatus >> 32); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { // there's a local instant application installed, but, the user has // chosen to never use it; skip resolution and don't acknowledge // an instant application is even available if (DEBUG_INSTANT) { Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName); } blockResolution = true; break; } else { // we have a locally installed instant application; skip resolution // but acknowledge there's an instant application available if (DEBUG_INSTANT) { Slog.v(TAG, "Found installed instant app; pkg: " + packageName); } localInstantApp = info; break; } } } } // no app installed, let's see if one's available AuxiliaryResolveInfo auxiliaryResponse = null; if (!blockResolution) { if (localInstantApp == null) { // we don't have an instant app locally, resolve externally Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral"); final InstantAppRequest requestObject = new InstantAppRequest( null /*responseObj*/, intent /*origIntent*/, resolvedType, null /*callingPackage*/, userId, null /*verificationBundle*/, resolveForStart); auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne( mInstantAppResolverConnection, requestObject); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } else { // we have an instant application locally, but, we can't admit that since // callers shouldn't be able to determine prior browsing. create a dummy // auxiliary response so the downstream code behaves as if there's an // instant application available externally. when it comes time to start // the instant application, we'll do the right thing. final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo; auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */, ai.packageName, ai.longVersionCode, null /* splitName */); } } if (intent.isWebIntent() && auxiliaryResponse == null) { return result; } final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName); if (ps == null || !ps.readUserState(userId).isEnabled(mInstantAppInstallerActivity, 0)) { return result; } final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo); ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo( mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId); ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART | IntentFilter.MATCH_ADJUSTMENT_NORMAL; // add a non-generic filter ephemeralInstaller.filter = new IntentFilter(); if (intent.getAction() != null) { ephemeralInstaller.filter.addAction(intent.getAction()); } if (intent.getData() != null && intent.getData().getPath() != null) { ephemeralInstaller.filter.addDataPath( intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL); } ephemeralInstaller.isInstantAppAvailable = true; // make sure this resolver is the default ephemeralInstaller.isDefault = true; ephemeralInstaller.auxiliaryInfo = auxiliaryResponse; if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } result.add(ephemeralInstaller); return result; } private static class CrossProfileDomainInfo { /* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */ ResolveInfo resolveInfo; /* Best domain verification status of the activities found in the other profile */ int bestDomainVerificationStatus; } private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType, int flags, int sourceUserId, int parentUserId) { if (!sUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, sourceUserId)) { return null; } List resultTargetUser = mComponentResolver.queryActivities(intent, resolvedType, flags, parentUserId); if (resultTargetUser == null || resultTargetUser.isEmpty()) { return null; } CrossProfileDomainInfo result = null; int size = resultTargetUser.size(); for (int i = 0; i < size; i++) { ResolveInfo riTargetUser = resultTargetUser.get(i); // Intent filter verification is only for filters that specify a host. So don't return // those that handle all web uris. if (riTargetUser.handleAllWebDataURI) { continue; } String packageName = riTargetUser.activityInfo.packageName; PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) { continue; } long verificationState = getDomainVerificationStatusLPr(ps, parentUserId); int status = (int)(verificationState >> 32); if (result == null) { result = new CrossProfileDomainInfo(); result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(), sourceUserId, parentUserId); result.bestDomainVerificationStatus = status; } else { result.bestDomainVerificationStatus = bestDomainVerificationStatus(status, result.bestDomainVerificationStatus); } } // Don't consider matches with status NEVER across profiles. if (result != null && result.bestDomainVerificationStatus == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return null; } return result; } /** * Verification statuses are ordered from the worse to the best, except for * INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse. */ private int bestDomainVerificationStatus(int status1, int status2) { if (status1 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return status2; } if (status2 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return status1; } return (int) MathUtils.max(status1, status2); } private boolean isUserEnabled(int userId) { long callingId = Binder.clearCallingIdentity(); try { UserInfo userInfo = sUserManager.getUserInfo(userId); return userInfo != null && userInfo.isEnabled(); } finally { Binder.restoreCallingIdentity(callingId); } } /** * Filter out activities with systemUserOnly flag set, when current user is not System. * * @return filtered list */ private List filterIfNotSystemUser(List resolveInfos, int userId) { if (userId == UserHandle.USER_SYSTEM) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { ResolveInfo info = resolveInfos.get(i); if ((info.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) { resolveInfos.remove(i); } } return resolveInfos; } /** * Filters out ephemeral activities. * When resolving for an ephemeral app, only activities that 1) are defined in the * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned. * * @param resolveInfos The pre-filtered list of resolved activities * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering * is performed. * @param intent * @return A filtered list of resolved activities. */ private List applyPostResolutionFilter(List resolveInfos, String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, boolean resolveForStart, int userId, Intent intent) { final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId); for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); // remove locally resolved instant app web results when disabled if (info.isInstantAppAvailable && blockInstant) { resolveInfos.remove(i); continue; } // allow activities that are defined in the provided package if (allowDynamicSplits && info.activityInfo != null && info.activityInfo.splitName != null && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames, info.activityInfo.splitName)) { if (mInstantAppInstallerActivity == null) { if (DEBUG_INSTALL) { Slog.v(TAG, "No installer - not adding it to the ResolveInfo list"); } resolveInfos.remove(i); continue; } if (blockInstant && isInstantApp(info.activityInfo.packageName, userId)) { resolveInfos.remove(i); continue; } // requested activity is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTALL) { Slog.v(TAG, "Adding installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); final ComponentName installFailureActivity = findInstallFailureActivity( info.activityInfo.packageName, filterCallingUid, userId); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( installFailureActivity, info.activityInfo.packageName, info.activityInfo.applicationInfo.longVersionCode, info.activityInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // This resolve info may appear in the chooser UI, so let us make it // look as the one it replaces as far as the user is concerned which // requires loading the correct label and icon for the resolve info. installerInfo.resolvePackageName = info.getComponentInfo().packageName; installerInfo.labelRes = info.resolveLabelResId(); installerInfo.icon = info.resolveIconResId(); installerInfo.isInstantAppAvailable = true; resolveInfos.set(i, installerInfo); continue; } // caller is a full app, don't need to apply any other filtering if (ephemeralPkgName == null) { continue; } else if (ephemeralPkgName.equals(info.activityInfo.packageName)) { // caller is same app; don't need to apply any other filtering continue; } else if (resolveForStart && (intent.isWebIntent() || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) && intent.getPackage() == null && intent.getComponent() == null) { // ephemeral apps can launch other ephemeral apps indirectly continue; } // allow activities that have been explicitly exposed to ephemeral apps final boolean isEphemeralApp = info.activityInfo.applicationInfo.isInstantApp(); if (!isEphemeralApp && ((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } /** * Returns the activity component that can handle install failures. * By default, the instant application installer handles failures. However, an * application may want to handle failures on its own. Applications do this by * creating an activity with an intent filter that handles the action * {@link Intent#ACTION_INSTALL_FAILURE}. */ private @Nullable ComponentName findInstallFailureActivity( String packageName, int filterCallingUid, int userId) { final Intent failureActivityIntent = new Intent(Intent.ACTION_INSTALL_FAILURE); failureActivityIntent.setPackage(packageName); // IMPORTANT: disallow dynamic splits to avoid an infinite loop final List result = queryIntentActivitiesInternal( failureActivityIntent, null /*resolvedType*/, 0 /*flags*/, filterCallingUid, userId, false /*resolveForStart*/, false /*allowDynamicSplits*/); final int NR = result.size(); if (NR > 0) { for (int i = 0; i < NR; i++) { final ResolveInfo info = result.get(i); if (info.activityInfo.splitName != null) { continue; } return new ComponentName(packageName, info.activityInfo.name); } } return null; } /** * @param resolveInfos list of resolve infos in descending priority order * @return if the list contains a resolve info with non-negative priority */ private boolean hasNonNegativePriority(List resolveInfos) { return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0; } private List filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent, int matchFlags, List candidates, CrossProfileDomainInfo xpDomainInfo, int userId) { final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0; if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) { Slog.v(TAG, "Filtering results with preferred activities. Candidates count: " + candidates.size()); } final ArrayList result = new ArrayList<>(); final ArrayList alwaysList = new ArrayList<>(); final ArrayList undefinedList = new ArrayList<>(); final ArrayList alwaysAskList = new ArrayList<>(); final ArrayList neverList = new ArrayList<>(); final 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 = mComponentResolver.queryActivities(intent, resolvedType, flags, targetUserId); if (resultTargetUser != null && isUserEnabled(targetUserId)) { // If all the matches in the target profile are suspended, return null. for (int i = resultTargetUser.size() - 1; i >= 0; i--) { if ((resultTargetUser.get(i).activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SUSPENDED) == 0) { return createForwardingResolveInfoUnchecked(filter, sourceUserId, targetUserId); } } } return null; } private ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter, int sourceUserId, int targetUserId) { ResolveInfo forwardingResolveInfo = new ResolveInfo(); long ident = Binder.clearCallingIdentity(); boolean targetIsProfile; try { targetIsProfile = sUserManager.getUserInfo(targetUserId).isManagedProfile(); } finally { Binder.restoreCallingIdentity(ident); } String className; if (targetIsProfile) { className = FORWARD_INTENT_TO_MANAGED_PROFILE; } else { className = FORWARD_INTENT_TO_PARENT; } ComponentName forwardingActivityComponentName = new ComponentName( mAndroidApplication.packageName, className); ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0, sourceUserId); if (!targetIsProfile) { forwardingActivityInfo.showUserIcon = targetUserId; forwardingResolveInfo.noResourceId = true; } forwardingResolveInfo.activityInfo = forwardingActivityInfo; forwardingResolveInfo.priority = 0; forwardingResolveInfo.preferredOrder = 0; forwardingResolveInfo.match = 0; forwardingResolveInfo.isDefault = true; forwardingResolveInfo.filter = filter; forwardingResolveInfo.targetUserId = targetUserId; return forwardingResolveInfo; } @Override public @NonNull ParceledListSlice queryIntentActivityOptions(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>(queryIntentActivityOptionsInternal(caller, specifics, specificTypes, intent, resolvedType, flags, userId)); } private @NonNull List queryIntentActivityOptionsInternal(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent activity options"); final String resultsAction = intent.getAction(); final List results = queryIntentActivitiesInternal(intent, resolvedType, flags | PackageManager.GET_RESOLVED_FILTER, userId); if (DEBUG_INTENT_MATCHING) { Log.v(TAG, "Query " + intent + ": " + results); } int specificsPos = 0; int N; // todo: note that the algorithm used here is O(N^2). This // isn't a problem in our current environment, but if we start running // into situations where we have more than 5 or 10 matches then this // should probably be changed to something smarter... // First we go through and resolve each of the specific items // that were supplied, taking care of removing any corresponding // duplicate items in the generic resolve list. if (specifics != null) { for (int i=0; i it = rii.filter.actionsIterator(); if (it == null) { continue; } while (it.hasNext()) { final String action = it.next(); if (resultsAction != null && resultsAction.equals(action)) { // If this action was explicitly requested, then don't // remove things that have it. continue; } for (int j=i+1; j queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>( queryIntentReceiversInternal(intent, resolvedType, flags, userId, false /*allowDynamicSplits*/)); } private @NonNull List queryIntentReceiversInternal(Intent intent, String resolvedType, int flags, int userId, boolean allowDynamicSplits) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ActivityInfo ai = getReceiverInfo(comp, flags, userId); if (ai != null) { // When specifying an explicit component, we prevent the activity from being // used when either 1) the calling package is normal and the activity is within // an instant application or 2) the calling package is ephemeral and the // activity is not visible to instant applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean matchExplicitlyVisibleOnly = (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (ai.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetVisibleToInstantApp = (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean isTargetExplicitlyVisibleToInstantApp = isTargetVisibleToInstantApp && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; final boolean isTargetHiddenFromInstantApp = !isTargetVisibleToInstantApp || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp); final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } } return applyPostResolutionFilter( list, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { final List result = mComponentResolver.queryReceivers(intent, resolvedType, flags, userId); return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { final List result = mComponentResolver.queryReceivers( intent, resolvedType, flags, pkg.receivers, userId); return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } return Collections.emptyList(); } } @Override public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) { final int callingUid = Binder.getCallingUid(); return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid); } private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForResolve( flags, userId, intent, callingUid, false /*includeInstantApps*/); List query = queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/); if (query != null) { if (query.size() >= 1) { // If there is more than one service with the same priority, // just arbitrarily pick the first one. return query.get(0); } } return null; } @Override public @NonNull ParceledListSlice queryIntentServices(Intent intent, String resolvedType, int flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>(queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/)); } private @NonNull List queryIntentServicesInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps) { if (!sUserManager.exists(userId)) return Collections.emptyList(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ServiceInfo si = getServiceInfo(comp, flags, userId); if (si != null) { // When specifying an explicit component, we prevent the service from being // used when either 1) the service is in an instant application and the // caller is not the same instant application or 2) the calling package is // ephemeral and the activity is not visible to ephemeral applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (si.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetHiddenFromInstantApp = (si.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0; final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.serviceInfo = si; list.add(ri); } } return list; } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostServiceResolutionFilter( mComponentResolver.queryServices(intent, resolvedType, flags, userId), instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return applyPostServiceResolutionFilter( mComponentResolver.queryServices(intent, resolvedType, flags, pkg.services, userId), instantAppPkgName); } return Collections.emptyList(); } } private List applyPostServiceResolutionFilter(List resolveInfos, String instantAppPkgName) { if (instantAppPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp(); // allow services that are defined in the provided package if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) { if (info.serviceInfo.splitName != null && !ArrayUtils.contains(info.serviceInfo.applicationInfo.splitNames, info.serviceInfo.splitName)) { // requested service is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( null /* installFailureActivity */, info.serviceInfo.packageName, info.serviceInfo.applicationInfo.longVersionCode, info.serviceInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // load resources from the correct package installerInfo.resolvePackageName = info.getComponentInfo().packageName; resolveInfos.set(i, installerInfo); } continue; } // allow services that have been explicitly exposed to ephemeral apps if (!isEphemeralApp && ((info.serviceInfo.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } @Override public @NonNull ParceledListSlice queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>( queryIntentContentProvidersInternal(intent, resolvedType, flags, userId)); } private @NonNull List queryIntentContentProvidersInternal( Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ProviderInfo pi = getProviderInfo(comp, flags, userId); if (pi != null) { // When specifying an explicit component, we prevent the provider from being // used when either 1) the provider is in an instant application and the // caller is not the same instant application or 2) the calling package is an // instant application and the provider is not visible to instant applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (pi.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetHiddenFromInstantApp = (pi.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0; final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.providerInfo = pi; list.add(ri); } } return list; } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostContentProviderResolutionFilter( mComponentResolver.queryProviders(intent, resolvedType, flags, userId), instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return applyPostContentProviderResolutionFilter( mComponentResolver.queryProviders(intent, resolvedType, flags, pkg.providers, userId), instantAppPkgName); } return Collections.emptyList(); } } private List applyPostContentProviderResolutionFilter( List resolveInfos, String instantAppPkgName) { if (instantAppPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.providerInfo.applicationInfo.isInstantApp(); // allow providers that are defined in the provided package if (isEphemeralApp && instantAppPkgName.equals(info.providerInfo.packageName)) { if (info.providerInfo.splitName != null && !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames, info.providerInfo.splitName)) { // requested provider is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( null /*failureActivity*/, info.providerInfo.packageName, info.providerInfo.applicationInfo.longVersionCode, info.providerInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // load resources from the correct package installerInfo.resolvePackageName = info.getComponentInfo().packageName; resolveInfos.set(i, installerInfo); } continue; } // allow providers that have been explicitly exposed to instant applications if (!isEphemeralApp && ((info.providerInfo.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } @Override public ParceledListSlice getInstalledPackages(int flags, int userId) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return ParceledListSlice.emptyList(); } if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; final boolean listApex = (flags & MATCH_APEX) != 0; final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0; mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed packages"); // writer synchronized (mPackages) { ArrayList list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo(ps, flags, userId); if (pi != null) { list.add(pi); } } } else { list = new ArrayList<>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { final PackageSetting ps = (PackageSetting) p.mExtras; if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo((PackageSetting) p.mExtras, flags, userId); if (pi != null) { list.add(pi); } } } if (listApex) { if (listFactory) { list.addAll(mApexManager.getFactoryPackages()); } else { list.addAll(mApexManager.getActivePackages()); } if (listUninstalled) { list.addAll(mApexManager.getInactivePackages()); } } return new ParceledListSlice<>(list); } } private void addPackageHoldingPermissions(ArrayList list, PackageSetting ps, String[] permissions, boolean[] tmp, int flags, int userId) { int numMatch = 0; final PermissionsState permissionsState = ps.getPermissionsState(); for (int i=0; i getPackagesHoldingPermissions( String[] permissions, int flags, int userId) { if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, permissions); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "get packages holding permissions"); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; // writer synchronized (mPackages) { ArrayList list = new ArrayList<>(); boolean[] tmpBools = new boolean[permissions.length]; if (listUninstalled) { for (PackageSetting ps : mSettings.mPackages.values()) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } } else { for (PackageParser.Package pkg : mPackages.values()) { PackageSetting ps = (PackageSetting)pkg.mExtras; if (ps != null) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } } } return new ParceledListSlice<>(list); } } @Override public ParceledListSlice getInstalledApplications(int flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>( getInstalledApplicationsListInternal(flags, userId, callingUid)); } private List getInstalledApplicationsListInternal(int flags, int userId, int callingUid) { if (getInstantAppPackageName(callingUid) != null) { return Collections.emptyList(); } if (!sUserManager.exists(userId)) return Collections.emptyList(); flags = updateFlagsForApplication(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; mPermissionManager.enforceCrossUserPermission( callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed application info"); // writer synchronized (mPackages) { ArrayList list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { ApplicationInfo ai; int effectiveFlags = flags; if (ps.isSystem()) { effectiveFlags |= PackageManager.MATCH_ANY_USER; } if (ps.pkg != null) { if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(ps.pkg); } } else { // Shared lib filtering done in generateApplicationInfoFromSettingsLPw // and already converts to externally visible package name ai = generateApplicationInfoFromSettingsLPw(ps.name, callingUid, effectiveFlags, userId); } if (ai != null) { list.add(ai); } } } else { list = new ArrayList<>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { if (p.mExtras != null) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(p); list.add(ai); } } } } return list; } } @Override public ParceledListSlice getInstantApps(int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } if (!canViewInstantApps(Binder.getCallingUid(), userId)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getEphemeralApplications"); } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getEphemeralApplications"); synchronized (mPackages) { List instantApps = mInstantAppRegistry .getInstantAppsLPr(userId); if (instantApps != null) { return new ParceledListSlice<>(instantApps); } } return null; } @Override public boolean isInstantApp(String packageName, int userId) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "isInstantApp"); if (HIDE_EPHEMERAL_APIS) { return false; } synchronized (mPackages) { int callingUid = Binder.getCallingUid(); if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final PackageSetting ps = mSettings.mPackages.get(packageName); PackageParser.Package pkg = mPackages.get(packageName); final boolean returnAllowed = ps != null && (isCallerSameApp(packageName, callingUid) || canViewInstantApps(callingUid, userId) || mInstantAppRegistry.isInstantAccessGranted( userId, UserHandle.getAppId(callingUid), ps.appId)); if (returnAllowed) { return ps.getInstantApp(userId); } } return false; } @Override public byte[] getInstantAppCookie(String packageName, int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return null; } synchronized (mPackages) { return mInstantAppRegistry.getInstantAppCookieLPw( packageName, userId); } } @Override public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) { if (HIDE_EPHEMERAL_APIS) { return true; } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, true /* checkShell */, "setInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return false; } synchronized (mPackages) { return mInstantAppRegistry.setInstantAppCookieLPw( packageName, cookie, userId); } } @Override public Bitmap getInstantAppIcon(String packageName, int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } if (!canViewInstantApps(Binder.getCallingUid(), userId)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getInstantAppIcon"); } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppIcon"); synchronized (mPackages) { return mInstantAppRegistry.getInstantAppIconLPw( packageName, userId); } } private boolean isCallerSameApp(String packageName, int uid) { PackageParser.Package pkg = mPackages.get(packageName); return pkg != null && UserHandle.getAppId(uid) == pkg.applicationInfo.uid; } @Override public @NonNull ParceledListSlice getPersistentApplications(int flags) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(getPersistentApplicationsInternal(flags)); } private @NonNull List getPersistentApplicationsInternal(int flags) { final ArrayList finalList = new ArrayList<>(); // reader synchronized (mPackages) { final Iterator i = mPackages.values().iterator(); final int userId = UserHandle.getCallingUserId(); while (i.hasNext()) { final PackageParser.Package p = i.next(); if (p.applicationInfo == null) continue; final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0) && !p.applicationInfo.isDirectBootAware(); final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0) && p.applicationInfo.isDirectBootAware(); if ((p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0 && (!mSafeMode || isSystemApp(p)) && (matchesUnaware || matchesAware)) { PackageSetting ps = mSettings.mPackages.get(p.packageName); if (ps != null) { ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); if (ai != null) { finalList.add(ai); } } } } } return finalList; } @Override public ProviderInfo resolveContentProvider(String name, int flags, int userId) { return resolveContentProviderInternal(name, flags, userId); } private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId, name); final int callingUid = Binder.getCallingUid(); final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId); if (providerInfo == null) { return null; } if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { return null; } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { return null; } return providerInfo; } } /** * @deprecated */ @Deprecated public void querySyncProviders(List outNames, List outInfo) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; } mComponentResolver.querySyncProviders( outNames, outInfo, mSafeMode, UserHandle.getCallingUserId()); } @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; final List matchList = mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId); final int listSize = (matchList == null ? 0 : matchList.size()); synchronized (mPackages) { for (int i = 0; i < listSize; i++) { final ProviderInfo providerInfo = matchList.get(i); if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { continue; } final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { continue; } if (finalList == null) { finalList = new ArrayList<>(listSize - i); } finalList.add(providerInfo); } } if (finalList != null) { finalList.sort(sProviderInitOrderSorter); return new ParceledListSlice<>(finalList); } return ParceledListSlice.emptyList(); } @Override public InstrumentationInfo getInstrumentationInfo(ComponentName component, int flags) { // reader synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) { return null; } final PackageParser.Instrumentation i = mInstrumentation.get(component); return PackageParser.generateInstrumentationInfo(i, flags); } } @Override public @NonNull ParceledListSlice queryInstrumentation( String targetPackage, int flags) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(targetPackage); if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags)); } private @NonNull List queryInstrumentationInternal(String targetPackage, int flags) { ArrayList finalList = new ArrayList<>(); // reader synchronized (mPackages) { final Iterator i = mInstrumentation.values().iterator(); while (i.hasNext()) { final PackageParser.Instrumentation p = i.next(); if (targetPackage == null || targetPackage.equals(p.info.targetPackage)) { InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p, flags); if (ii != null) { finalList.add(ii); } } } } return finalList; } private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]"); try { scanDirLI(scanDir, parseFlags, scanFlags, currentTime); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) { final File[] files = scanDir.listFiles(); if (ArrayUtils.isEmpty(files)) { Log.d(TAG, "No files in app dir " + scanDir); return; } if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags + " flags=0x" + Integer.toHexString(parseFlags)); } try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser( mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mParallelPackageParserCallback)) { // Submit files for parsing in parallel int fileCount = 0; for (File file : files) { final boolean isPackage = (isApkFile(file) || file.isDirectory()) && !PackageInstallerService.isStageName(file.getName()); if (!isPackage) { // Ignore entries which are not packages continue; } parallelPackageParser.submit(file, parseFlags); fileCount++; } // Process results one by one for (; fileCount > 0; fileCount--) { ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take(); Throwable throwable = parseResult.throwable; int errorCode = PackageManager.INSTALL_SUCCEEDED; if (throwable == null) { // TODO(toddke): move lower in the scan chain // Static shared libraries have synthetic package names if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(parseResult.pkg); } try { scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags, currentTime, null); } catch (PackageManagerException e) { errorCode = e.error; Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage()); } } else if (throwable instanceof PackageParser.PackageParserException) { PackageParser.PackageParserException e = (PackageParser.PackageParserException) throwable; errorCode = e.error; Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage()); } else { throw new IllegalStateException("Unexpected exception occurred while parsing " + parseResult.scanFile, throwable); } // Delete invalid userdata apps if ((scanFlags & SCAN_AS_SYSTEM) == 0 && errorCode != PackageManager.INSTALL_SUCCEEDED) { logCriticalInfo(Log.WARN, "Deleting invalid package at " + parseResult.scanFile); removeCodePathLI(parseResult.scanFile); } } } } public static void reportSettingsProblem(int priority, String msg) { logCriticalInfo(priority, msg); } private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, boolean forceCollect, boolean skipVerify) throws PackageManagerException { // When upgrading from pre-N MR1, verify the package time stamp using the package // directory and not the APK file. final long lastModifiedTime = mIsPreNMR1Upgrade ? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg); final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(pkg); if (ps != null && !forceCollect && ps.codePathString.equals(pkg.codePath) && ps.timeStamp == lastModifiedTime && !isCompatSignatureUpdateNeeded(settingsVersionForPackage) && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) { if (ps.signatures.mSigningDetails.signatures != null && ps.signatures.mSigningDetails.signatures.length != 0 && ps.signatures.mSigningDetails.signatureSchemeVersion != SignatureSchemeVersion.UNKNOWN) { // Optimization: reuse the existing cached signing data // if the package appears to be unchanged. pkg.mSigningDetails = new PackageParser.SigningDetails(ps.signatures.mSigningDetails); return; } Slog.w(TAG, "PackageSetting for " + ps.name + " is missing signatures. Collecting certs again to recover them."); } else { Slog.i(TAG, pkg.codePath + " changed; collecting certs" + (forceCollect ? " (forced)" : "")); } try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); PackageParser.collectCertificates(pkg, skipVerify); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } /** * Clear the package profile if this was an upgrade and the package * version was updated. */ private void maybeClearProfilesForUpgradesLI( @Nullable PackageSetting originalPkgSetting, @NonNull PackageParser.Package currentPkg) { if (originalPkgSetting == null || !isDeviceUpgrading()) { return; } if (originalPkgSetting.versionCode == currentPkg.mVersionCode) { return; } clearAppProfilesLIF(currentPkg, UserHandle.USER_ALL); if (DEBUG_INSTALL) { Slog.d(TAG, originalPkgSetting.name + " clear profile due to version change " + originalPkgSetting.versionCode + " != " + currentPkg.mVersionCode); } } /** * Traces a package scan. * @see #scanPackageLI(File, int, int, long, UserHandle) */ @GuardedBy({"mInstallLock", "mPackages"}) 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 */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); pp.setDisplayMetrics(mMetrics); pp.setCallback(mPackageParserCallback); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); final PackageParser.Package pkg; try { pkg = pp.parsePackage(scanFile, parseFlags); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } // Static shared libraries have synthetic package names if (pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(pkg); } return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user); } /** * Scans a package and returns the newly parsed package. * @throws PackageManagerException on a parse error. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg, final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { // If the package has children and this is the first dive in the function // we scan the package with the SCAN_CHECK_ONLY flag set to see whether all // packages (parent and children) would be successfully scanned before the // actual scan since scanning mutates internal state and we want to atomically // install the package and its children. if ((scanFlags & SCAN_CHECK_ONLY) == 0) { if (pkg.childPackages != null && pkg.childPackages.size() > 0) { scanFlags |= SCAN_CHECK_ONLY; } } else { scanFlags &= ~SCAN_CHECK_ONLY; } // Scan the parent PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags, scanFlags, currentTime, user); // Scan the children final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPackage = pkg.childPackages.get(i); addForInitLI(childPackage, parseFlags, scanFlags, currentTime, user); } if ((scanFlags & SCAN_CHECK_ONLY) != 0) { return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user); } return scannedPkg; } /** * Returns if forced apk verification can be skipped for the whole package, including splits. */ private boolean canSkipForcedPackageVerification(PackageParser.Package pkg) { if (!canSkipForcedApkVerification(pkg.baseCodePath)) { return false; } // TODO: Allow base and splits to be verified individually. if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { for (int i = 0; i < pkg.splitCodePaths.length; i++) { if (!canSkipForcedApkVerification(pkg.splitCodePaths[i])) { return false; } } } return true; } /** * Returns if forced apk verification can be skipped, depending on current FSVerity setup and * whether the apk contains signed root hash. Note that the signer's certificate still needs to * match one in a trusted source, and should be done separately. */ private boolean canSkipForcedApkVerification(String apkPath) { if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) { return VerityUtils.hasFsverity(apkPath); } try { final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath); if (rootHashObserved == null) { return false; // APK does not contain Merkle tree root hash. } synchronized (mInstallLock) { // Returns whether the observed root hash matches what kernel has. mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved); return true; } } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) { Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e); } return false; } /** * Adds a new package to the internal data structures during platform initialization. * After adding, the package is known to the system and available for querying. * For packages located on the device ROM [eg. packages located in /system, /vendor, * etc...], additional checks are performed. Basic verification [such as ensuring * matching signatures, checking version codes, etc...] occurs if the package is * identical to a previously known package. If the package fails a signature check, * the version installed on /data will be removed. If the version of the new package * is less than or equal than the version on /data, it will be ignored. * Regardless of the package location, the results are applied to the internal * structures and the package is made available to the rest of the system. * NOTE: The return value should be removed. It's the passed in package object. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package addForInitLI(PackageParser.Package pkg, @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { final boolean scanSystemPartition = (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0; final String renamedPkgName; final PackageSetting disabledPkgSetting; final boolean isSystemPkgUpdated; final boolean pkgAlreadyExists; PackageSetting pkgSetting; // NOTE: installPackageLI() has the same code to setup the package's // application info. This probably should be done lower in the call // stack [such as scanPackageOnly()]. However, we verify the application // info prior to that [in scanPackageNew()] and thus have to setup // the application info early. pkg.setApplicationVolumeUuid(pkg.volumeUuid); pkg.setApplicationInfoCodePath(pkg.codePath); pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath); pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths); pkg.setApplicationInfoResourcePath(pkg.codePath); pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath); pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths); synchronized (mPackages) { renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage); final String realPkgName = getRealPackageName(pkg, renamedPkgName); if (realPkgName != null) { ensurePackageRenamed(pkg, renamedPkgName); } final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName); final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName); pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting; pkgAlreadyExists = pkgSetting != null; final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName; disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName); isSystemPkgUpdated = disabledPkgSetting != null; if (DEBUG_INSTALL && isSystemPkgUpdated) { Slog.d(TAG, "updatedPkg = " + disabledPkgSetting); } final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null) ? mSettings.getSharedUserLPw(pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true) : null; if (DEBUG_PACKAGE_SCANNING && (parseFlags & PackageParser.PARSE_CHATTY) != 0 && sharedUserSetting != null) { Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + sharedUserSetting.userId + "):" + " packages=" + sharedUserSetting.packages); } if (scanSystemPartition) { // Potentially prune child packages. If the application on the /system // partition has been updated via OTA, but, is still disabled by a // version on /data, cycle through all of its children packages and // remove children that are no longer defined. if (isSystemPkgUpdated) { final int scannedChildCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; final int disabledChildCount = disabledPkgSetting.childPackageNames != null ? disabledPkgSetting.childPackageNames.size() : 0; for (int i = 0; i < disabledChildCount; i++) { String disabledChildPackageName = disabledPkgSetting.childPackageNames.get(i); boolean disabledPackageAvailable = false; for (int j = 0; j < scannedChildCount; j++) { PackageParser.Package childPkg = pkg.childPackages.get(j); if (childPkg.packageName.equals(disabledChildPackageName)) { disabledPackageAvailable = true; break; } } if (!disabledPackageAvailable) { mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName); } } // we're updating the disabled package, so, scan it as the package setting final ScanRequest request = new ScanRequest(pkg, sharedUserSetting, null, disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */, null /* originalPkgSetting */, null, parseFlags, scanFlags, (pkg == mPlatformPackage), user); applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage); final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L); if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) { scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting); } } } } final boolean newPkgChangedPaths = pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath); final boolean newPkgVersionGreater = pkgAlreadyExists && pkg.getLongVersionCode() > pkgSetting.versionCode; final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated && newPkgChangedPaths && newPkgVersionGreater; if (isSystemPkgBetter) { // The version of the application on /system is greater than the version on // /data. Switch back to the application on /system. // It's safe to assume the application on /system will correctly scan. If not, // there won't be a working copy of the application. synchronized (mPackages) { // just remove the loaded entries from package lists mPackages.remove(pkgSetting.name); } logCriticalInfo(Log.WARN, "System package updated;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); final InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); args.cleanUpResourcesLI(); synchronized (mPackages) { mSettings.enableSystemPackageLPw(pkgSetting.name); } } if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) { // The version of the application on the /system partition is less than or // equal to the version on the /data partition. Throw an exception and use // the application already installed on the /data partition. throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at " + pkg.codePath + " ignored: updated version " + pkgSetting.versionCode + " better than this " + pkg.getLongVersionCode()); } // Verify certificates against what was last scanned. If there was an upgrade and this is an // app in a system partition, or if this is an updated priv app, we will force re-collecting // certificate. final boolean forceCollect = (mIsUpgrade && scanSystemPartition) || PackageManagerServiceUtils.isApkVerificationForced(disabledPkgSetting); // Full APK verification can be skipped during certificate collection, only if the file is // in verified partition, or can be verified on access (when apk verity is enabled). In both // cases, only data in Signing Block is verified instead of the whole file. final boolean skipVerify = scanSystemPartition || (forceCollect && canSkipForcedPackageVerification(pkg)); collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify); // Reset profile if the application version is changed maybeClearProfilesForUpgradesLI(pkgSetting, pkg); /* * A new system app appeared, but we already had a non-system one of the * same name installed earlier. */ boolean shouldHideSystemApp = false; // A new application appeared on /system, but, we already have a copy of // the application installed on /data. if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists && !pkgSetting.isSystem()) { if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA) && !pkgSetting.signatures.mSigningDetails.checkCapability( pkg.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) { logCriticalInfo(Log.WARN, "System package signature mismatch;" + " name: " + pkgSetting.name); try (PackageFreezer freezer = freezePackage(pkg.packageName, "scanPackageInternalLI")) { deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null); } pkgSetting = null; } else if (newPkgVersionGreater) { // The application on /system is newer than the application on /data. // Simply remove the application on /data [keeping application data] // and replace it with the version on /system. logCriticalInfo(Log.WARN, "System package enabled;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } } else { // The application on /system is older than the application on /data. Hide // the application on /system and the version on /data will be scanned later // and re-added like an update. shouldHideSystemApp = true; logCriticalInfo(Log.INFO, "System package disabled;" + " name: " + pkgSetting.name + "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode + "; new: " + pkg.codePath + " @ " + pkg.codePath); } } final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user); if (scanResult.success) { synchronized (mPackages) { boolean appIdCreated = false; try { final String pkgName = scanResult.pkgSetting.name; final Map reconcileResult = reconcilePackagesLocked( new ReconcileRequest( Collections.singletonMap(pkgName, scanResult), mSharedLibraries, mPackages, Collections.singletonMap( pkgName, getSettingsVersionForPackage(pkg)), Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))), mSettings.mKeySetManagerService); appIdCreated = optimisticallyRegisterAppId(scanResult); commitReconciledScanResultLocked(reconcileResult.get(pkgName)); } catch (PackageManagerException e) { if (appIdCreated) { cleanUpAppIdCreation(scanResult); } throw e; } } } if (shouldHideSystemApp) { synchronized (mPackages) { mSettings.disableSystemPackageLPw(pkg.packageName, true); } } return scanResult.pkgSetting.pkg; } private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) { // Derive the new package synthetic package name pkg.setPackageName(pkg.packageName + STATIC_SHARED_LIB_DELIMITER + pkg.staticSharedLibVersion); } static String fixProcessName(String defProcessName, String processName) { if (processName == null) { return defProcessName; } return processName; } /** * Enforces that only the system UID or root's UID can call a method exposed * via Binder. * * @param message used as message if SecurityException is thrown * @throws SecurityException if the caller is not system or root */ private static void enforceSystemOrRoot(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) { throw new SecurityException(message); } } /** * Enforces that only the system UID or root's UID or shell'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 shell */ private static void enforceSystemOrRootOrShell(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID && uid != Process.SHELL_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 = isDeviceUpgrading(); // First boot or factory reset. // Note: we also handle devices that are upgrading to N right now as if it is their // first boot, as they do not have profile data. boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade; // We need to re-extract after a pruned cache, as AoT-ed files will be out of date. boolean causePrunedCache = VMRuntime.didPruneDalvikCache(); if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) { return; } List pkgs; synchronized (mPackages) { pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this); } final long startTime = System.nanoTime(); final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */, causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT, false /* bootComplete */); final int elapsedTimeSeconds = (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime); MetricsLogger.histogram(mContext, "opt_dialog_num_dexopted", stats[0]); MetricsLogger.histogram(mContext, "opt_dialog_num_skipped", stats[1]); MetricsLogger.histogram(mContext, "opt_dialog_num_failed", stats[2]); MetricsLogger.histogram(mContext, "opt_dialog_num_total", getOptimizablePackages().size()); MetricsLogger.histogram(mContext, "opt_dialog_time_s", elapsedTimeSeconds); } /* * Return the prebuilt profile path given a package base code path. */ private static String getPrebuildProfilePath(PackageParser.Package pkg) { return pkg.baseCodePath + ".prof"; } /** * Performs dexopt on the set of packages in {@code packages} and returns an int array * containing statistics about the invocation. The array consists of three elements, * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped} * and {@code numberOfPackagesFailed}. */ private int[] performDexOptUpgrade(List pkgs, boolean showDialog, final int compilationReason, boolean bootComplete) { int numberOfPackagesVisited = 0; int numberOfPackagesOptimized = 0; int numberOfPackagesSkipped = 0; int numberOfPackagesFailed = 0; final int numberOfPackagesToDexopt = pkgs.size(); for (PackageParser.Package pkg : pkgs) { numberOfPackagesVisited++; boolean useProfileForDexopt = false; if ((isFirstBoot() || isDeviceUpgrading()) && isSystemApp(pkg)) { // Copy over initial preopt profiles since we won't get any JIT samples for methods // that are already compiled. File profileFile = new File(getPrebuildProfilePath(pkg)); // Copy profile if it exists. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The issue // is that we don't have a good way to say "do this only once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Installer failed to copy system profile!"); } else { // Disabled as this causes speed-profile compilation during first boot // even if things are already compiled. // useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } else { PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName); // Handle compressed APKs in this path. Only do this for stubs with profiles to // minimize the number off apps being speed-profile compiled during first boot. // The other paths will not change the filter. if (disabledPs != null && disabledPs.pkg.isStub) { // The package is the stub one, remove the stub suffix to get the normal // package and APK names. String systemProfilePath = getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, ""); profileFile = new File(systemProfilePath); // If we have a profile for a compressed APK, copy it to the reference // location. // Note that copying the profile here will cause it to override the // reference profile every OTA even though the existing reference profile // may have more data. We can't copy during decompression since the // directories are not set up at that point. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The // issue is that we don't have a good way to say "do this only // once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Failed to copy system profile for stub package!"); } else { useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } } } } if (!PackageDexOptimizer.canOptimizePackage(pkg)) { if (DEBUG_DEXOPT) { Log.i(TAG, "Skipping update of of non-optimizable app " + pkg.packageName); } numberOfPackagesSkipped++; continue; } if (DEBUG_DEXOPT) { Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " + numberOfPackagesToDexopt + ": " + pkg.packageName); } if (showDialog) { try { ActivityManager.getService().showBootMessage( mContext.getResources().getString(R.string.android_upgrading_apk, numberOfPackagesVisited, numberOfPackagesToDexopt), true); } catch (RemoteException e) { } synchronized (mPackages) { mDexOptDialogShown = true; } } int pkgCompilationReason = compilationReason; if (useProfileForDexopt) { // Use background dexopt mode to try and use the profile. Note that this does not // guarantee usage of the profile. pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT; } if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { mArtManagerService.compileLayouts(pkg); } // checkProfiles is false to avoid merging profiles during boot which // might interfere with background compilation (b/28612421). // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a // trade-off worth doing to save boot time work. int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0; if (compilationReason == REASON_FIRST_BOOT) { // TODO: This doesn't cover the upgrade case, we should check for this too. dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE; } int primaryDexOptStaus = performDexOptTraced(new DexoptOptions( pkg.packageName, pkgCompilationReason, dexoptFlags)); switch (primaryDexOptStaus) { case PackageDexOptimizer.DEX_OPT_PERFORMED: numberOfPackagesOptimized++; break; case PackageDexOptimizer.DEX_OPT_SKIPPED: numberOfPackagesSkipped++; break; case PackageDexOptimizer.DEX_OPT_FAILED: numberOfPackagesFailed++; break; default: Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus); break; } } return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped, numberOfPackagesFailed }; } @Override public void notifyPackageUse(String packageName, int reason) { synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); if (getInstantAppPackageName(callingUid) != null) { if (!isCallerSameApp(packageName, callingUid)) { return; } } else { if (isInstantApp(packageName, callingUserId)) { return; } } notifyPackageUseLocked(packageName, reason); } } @GuardedBy("mPackages") public CheckPermissionDelegate getCheckPermissionDelegateLocked() { return mCheckPermissionDelegate; } @GuardedBy("mPackages") public void setCheckPermissionDelegateLocked(CheckPermissionDelegate delegate) { mCheckPermissionDelegate = delegate; } @GuardedBy("mPackages") private void notifyPackageUseLocked(String packageName, int reason) { final PackageParser.Package p = mPackages.get(packageName); if (p == null) { return; } p.mLastPackageUsageTimeInMills[reason] = System.currentTimeMillis(); } @Override public void notifyDexLoad(String loadingPackageName, List classLoaderNames, List classPaths, String loaderIsa) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); if (ai == null) { Slog.w(TAG, "Loading a package that does not exist for the calling user. package=" + loadingPackageName + ", user=" + userId); return; } mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId); } @Override public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule, IDexModuleRegisterCallback callback) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(packageName, /*flags*/ 0, userId); DexManager.RegisterDexModuleResult result; if (ai == null) { Slog.w(TAG, "Registering a dex module for a package that does not exist for the" + " calling user. package=" + packageName + ", user=" + userId); result = new DexManager.RegisterDexModuleResult(false, "Package not installed"); } else { result = mDexManager.registerDexModule(ai, dexModulePath, isSharedModule, userId); } if (callback != null) { mHandler.post(() -> { try { callback.onDexModuleRegistered(dexModulePath, result.success, result.message); } catch (RemoteException e) { Slog.w(TAG, "Failed to callback after module registration " + dexModulePath, e); } }); } } /** * Ask the package manager to perform a dex-opt with the given compiler filter. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptMode(String packageName, boolean checkProfiles, String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) { int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) | (force ? DexoptOptions.DEXOPT_FORCE : 0) | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0); return performDexOpt(new DexoptOptions(packageName, REASON_UNKNOWN, targetCompilerFilter, splitName, flags)); } /** * Ask the package manager to perform a dex-opt with the given compiler filter on the * secondary dex files belonging to the given package. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptSecondary(String packageName, String compilerFilter, boolean force) { int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES | DexoptOptions.DEXOPT_BOOT_COMPLETE | (force ? DexoptOptions.DEXOPT_FORCE : 0); return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags)); } /** * Ask the package manager to compile layouts in the given package. */ @Override public boolean compileLayouts(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { return false; } } return mViewCompiler.compileLayouts(pkg); } /*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 = findSharedLibraries(p); final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo); if (!deps.isEmpty()) { DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(), options.getCompilationReason(), options.getCompilerFilter(), options.getSplitName(), options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY); for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = null; synchronized (mPackages) { depPackage = mPackages.get(info.getPackageName()); } if (depPackage != null) { // TODO: Analyze and investigate if we (should) profile libraries. pdo.performDexOpt(depPackage, instructionSets, getOrCreateCompilerPackageStats(depPackage), mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions); } else { // TODO(ngeoffray): Support dexopting system shared libraries. } } } return pdo.performDexOpt(p, instructionSets, getOrCreateCompilerPackageStats(p), mDexManager.getPackageUseInfoOrDefault(p.packageName), options); } /** * Reconcile the information we have about the secondary dex files belonging to * {@code packageName} 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(@Nullable List packageNames) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return false; } enforceSystemOrRootOrShell("runBackgroundDexoptJob"); final long identity = Binder.clearCallingIdentity(); try { return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames); } finally { Binder.restoreCallingIdentity(identity); } } private static List findSharedLibraries(PackageParser.Package p) { if (p.usesLibraryInfos != null) { ArrayList retValue = new ArrayList<>(); Set collectedNames = new HashSet<>(); for (SharedLibraryInfo info : p.usesLibraryInfos) { findSharedLibrariesRecursive(info, retValue, collectedNames); } return retValue; } else { return Collections.emptyList(); } } private static void findSharedLibrariesRecursive(SharedLibraryInfo info, ArrayList collected, Set collectedNames) { if (!collectedNames.contains(info.getName())) { collectedNames.add(info.getName()); collected.add(info); if (info.getDependencies() != null) { for (SharedLibraryInfo dep : info.getDependencies()) { findSharedLibrariesRecursive(dep, collected, collectedNames); } } } } List findSharedNonSystemLibraries(PackageParser.Package pkg) { List deps = findSharedLibraries(pkg); if (!deps.isEmpty()) { ArrayList retValue = new ArrayList<>(); synchronized (mPackages) { for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = mPackages.get(info.getPackageName()); if (depPackage != null) { retValue.add(depPackage); } } } return retValue; } else { return Collections.emptyList(); } } @Nullable private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) { return getSharedLibraryInfo(name, version, mSharedLibraries, null); } @Nullable private static SharedLibraryInfo getSharedLibraryInfo(String name, long version, Map> existingLibraries, @Nullable Map> newLibraries) { if (newLibraries != null) { final LongSparseArray versionedLib = newLibraries.get(name); SharedLibraryInfo info = null; if (versionedLib != null) { info = versionedLib.get(version); } if (info != null) { return info; } } final LongSparseArray versionedLib = existingLibraries.get(name); if (versionedLib == null) { return null; } return versionedLib.get(version); } private SharedLibraryInfo getLatestSharedLibraVersionLPr(PackageParser.Package pkg) { LongSparseArray versionedLib = mSharedLibraries.get( pkg.staticSharedLibName); if (versionedLib == null) { return null; } long previousLibVersion = -1; final int versionCount = versionedLib.size(); for (int i = 0; i < versionCount; i++) { final long libVersion = versionedLib.keyAt(i); if (libVersion < pkg.staticSharedLibVersion) { previousLibVersion = Math.max(previousLibVersion, libVersion); } } if (previousLibVersion >= 0) { return versionedLib.get(previousLibVersion); } return null; } @Nullable private PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) { PackageSetting sharedLibPackage = null; synchronized (mPackages) { final SharedLibraryInfo latestSharedLibraVersionLPr = getLatestSharedLibraVersionLPr(scanResult.pkgSetting.pkg); if (latestSharedLibraVersionLPr != null) { sharedLibPackage = mSettings.getPackageLPr( latestSharedLibraVersionLPr.getPackageName()); } } return sharedLibPackage; } public void shutdown() { mPackageUsage.writeNow(mPackages); mCompilerStats.writeNow(); mDexManager.writePackageDexUsageNow(); PackageWatchdog.getInstance(mContext).writeNow(); // This is the last chance to write out pending restriction settings synchronized (mPackages) { if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS); for (int userId : mDirtyUsers) { mSettings.writePackageRestrictionsLPr(userId); } mDirtyUsers.clear(); } } } @Override public void dumpProfiles(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } /* Only the shell, root, or the app user should be able to dump profiles. */ int callingUid = Binder.getCallingUid(); if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID && callingUid != pkg.applicationInfo.uid) { throw new SecurityException("dumpProfiles"); } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles"); mArtManagerService.dumpProfiles(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @Override public void forceDexOpt(String packageName) { enforceSystemOrRoot("forceDexOpt"); PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); // Whoever is calling forceDexOpt wants a compiled package. // Don't use profiles since that may cause compilation to be skipped. final int res = performDexOptInternalWithDependenciesLI( pkg, new DexoptOptions(packageName, getDefaultCompilerFilter(), DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE)); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) { throw new IllegalStateException("Failed to dexopt: " + res); } } } @GuardedBy("mPackages") private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) { if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package not in system partition"); return false; } else if (mPackages.get(oldPkg.name) != null) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package still exists"); return false; } return true; } @GuardedBy("mInstallLock") void removeCodePathLI(File codePath) { if (codePath.isDirectory()) { try { mInstaller.rmPackageDir(codePath.getAbsolutePath()); } catch (InstallerException e) { Slog.w(TAG, "Failed to remove code path", e); } } else { codePath.delete(); } } private int[] resolveUserIds(int userId) { return (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds() : new int[] { userId }; } private void clearAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } clearAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { clearAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } clearAppProfilesLIF(pkg, UserHandle.USER_ALL); } private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } } private void destroyAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } } private void destroyAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.destroyAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId); } } private void destroyAppProfilesLIF(PackageParser.Package pkg) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppProfilesLeafLIF(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppProfilesLeafLIF(pkg.childPackages.get(i)); } } private void destroyAppProfilesLeafLIF(PackageParser.Package pkg) { try { mInstaller.destroyAppProfiles(pkg.packageName); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } private void clearAppProfilesLIF(PackageParser.Package pkg, int userId) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } mArtManagerService.clearAppProfiles(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { mArtManagerService.clearAppProfiles(pkg.childPackages.get(i)); } } private void setInstallAndUpdateTime(PackageParser.Package pkg, long firstInstallTime, long lastUpdateTime) { // Set parent install/update time PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } // Set children install/update time final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPkg = pkg.childPackages.get(i); ps = (PackageSetting) childPkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } } } @GuardedBy("mPackages") private void applyDefiningSharedLibraryUpdateLocked( PackageParser.Package pkg, SharedLibraryInfo libInfo, BiConsumer action) { // Note that libraries defined by this package may be null if: // - Package manager was unable to create the shared library. The package still // gets installed, but the shared library does not get created. // Or: // - Package manager is in a state where package isn't scanned yet. This will // get called again after scanning to fix the dependencies. if (pkg.isLibrary()) { if (pkg.staticSharedLibName != null) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( pkg.staticSharedLibName, pkg.staticSharedLibVersion); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } else { for (String libraryName : pkg.libraryNames) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( libraryName, SharedLibraryInfo.VERSION_UNDEFINED); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } } } } @GuardedBy("mPackages") private void addSharedLibraryLPr(PackageParser.Package pkg, Set usesLibraryFiles, SharedLibraryInfo libInfo, PackageParser.Package changingLib) { if (libInfo.getPath() != null) { usesLibraryFiles.add(libInfo.getPath()); return; } PackageParser.Package p = mPackages.get(libInfo.getPackageName()); if (changingLib != null && changingLib.packageName.equals(libInfo.getPackageName())) { // If we are doing this while in the middle of updating a library apk, // then we need to make sure to use that new apk for determining the // dependencies here. (We haven't yet finished committing the new apk // to the package manager state.) if (p == null || p.packageName.equals(changingLib.packageName)) { p = changingLib; } } if (p != null) { usesLibraryFiles.addAll(p.getAllCodePaths()); // If the package provides libraries, add the dependency to them. applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, (definingLibrary, dependency) -> { definingLibrary.addDependency(dependency); }); if (p.usesLibraryFiles != null) { Collections.addAll(usesLibraryFiles, p.usesLibraryFiles); } } } @GuardedBy("mPackages") private void updateSharedLibrariesLocked(PackageParser.Package pkg, PackageParser.Package changingLib, Map availablePackages) throws PackageManagerException { final ArrayList sharedLibraryInfos = collectSharedLibraryInfos(pkg, availablePackages, mSharedLibraries, null); executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos); } private static ArrayList collectSharedLibraryInfos(PackageParser.Package pkg, Map availablePackages, @NonNull final Map> existingLibraries, @Nullable final Map> newLibraries) throws PackageManagerException { if (pkg == null) { return null; } // The collection used here must maintain the order of addition (so // that libraries are searched in the correct order) and must have no // duplicates. ArrayList usesLibraryInfos = null; if (pkg.usesLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesLibraries, null, null, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, null, availablePackages, existingLibraries, newLibraries); } if (pkg.usesStaticLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } if (pkg.usesOptionalLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesOptionalLibraries, null, null, pkg.packageName, false, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } return usesLibraryInfos; } private void executeSharedLibrariesUpdateLPr(PackageParser.Package pkg, PackageParser.Package changingLib, ArrayList usesLibraryInfos) { // If the package provides libraries, clear their old dependencies. // This method will set them up again. applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> { definingLibrary.clearDependencies(); }); if (usesLibraryInfos != null) { pkg.usesLibraryInfos = usesLibraryInfos; // Use LinkedHashSet to preserve the order of files added to // usesLibraryFiles while eliminating duplicates. Set usesLibraryFiles = new LinkedHashSet<>(); for (SharedLibraryInfo libInfo : usesLibraryInfos) { addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib); } pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]); } else { pkg.usesLibraryInfos = null; pkg.usesLibraryFiles = null; } } @GuardedBy("mPackages") private static ArrayList collectSharedLibraryInfos( @NonNull List requestedLibraries, @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, @NonNull String packageName, boolean required, int targetSdk, @Nullable ArrayList outUsedLibraries, @NonNull final Map
If the compressed file can not be extracted / installed for any reason, the stub * APK will be installed and the package will be disabled. To recover from this situation, * the user will need to go into system settings and re-enable the package. */ private boolean enableCompressedPackage(PackageParser.Package stubPkg) { final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY | PackageParser.PARSE_ENFORCE_CODE; synchronized (mInstallLock) { final PackageParser.Package pkg; try (PackageFreezer freezer = freezePackage(stubPkg.packageName, "setEnabledSetting")) { pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/); synchronized (mPackages) { prepareAppDataAfterInstallLIF(pkg); try { updateSharedLibrariesLocked(pkg, null, mPackages); } catch (PackageManagerException e) { Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e); } mPermissionManager.updatePermissions( pkg.packageName, pkg, true, mPackages.values(), mPermissionCallback); mSettings.writeLPr(); } } catch (PackageManagerException e) { // Whoops! Something went very wrong; roll back to the stub and disable the package try (PackageFreezer freezer = freezePackage(stubPkg.packageName, "setEnabledSetting")) { synchronized (mPackages) { // NOTE: Ensure the system package is enabled; even for a compressed stub. // If we don't, installing the system package fails during scan enableSystemPackageLPw(stubPkg); } installPackageFromSystemLIF(stubPkg.codePath, null /*allUserHandles*/, null /*origUserHandles*/, null /*origPermissionsState*/, true /*writeSettings*/); } catch (PackageManagerException pme) { // Serious WTF; we have to be able to install the stub Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.packageName, pme); } finally { // Disable the package; the stub by itself is not runnable synchronized (mPackages) { final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName); if (stubPs != null) { stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android"); } mSettings.writeLPr(); } } return false; } clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); mDexManager.notifyPackageUpdated(pkg.packageName, pkg.baseCodePath, pkg.splitCodePaths); } return true; } private PackageParser.Package installStubPackageLI(PackageParser.Package stubPkg, @ParseFlags int parseFlags, @ScanFlags int scanFlags) throws PackageManagerException { if (DEBUG_COMPRESSION) { Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.packageName); } // uncompress the binary to its eventual destination on /data final File scanFile = decompressPackage(stubPkg.packageName, stubPkg.codePath); if (scanFile == null) { throw new PackageManagerException("Unable to decompress stub at " + stubPkg.codePath); } synchronized (mPackages) { mSettings.disableSystemPackageLPw(stubPkg.packageName, true /*replaced*/); } removePackageLI(stubPkg, true /*chatty*/); try { return scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.packageName, e); // Remove the failed install removeCodePathLI(scanFile); throw e; } } /** * 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(String packageName, String codePath) { final File[] compressedFiles = getCompressedFiles(codePath); if (compressedFiles == null || compressedFiles.length == 0) { if (DEBUG_COMPRESSION) { Slog.i(TAG, "No files to decompress: " + codePath); } return null; } final File dstCodePath = getNextCodePath(Environment.getDataAppDirectory(null), 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: " + packageName + ", file: " + dstFileName); break; } } } catch (ErrnoException e) { logCriticalInfo(Log.ERROR, "Failed to decompress" + "; 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: " + packageName); ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; } finally { IoUtils.closeQuietly(handle); } } if (ret != PackageManager.INSTALL_SUCCEEDED) { if (!dstCodePath.exists()) { return null; } removeCodePathLI(dstCodePath); return null; } return dstCodePath; } @GuardedBy("mPackages") 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 @Nullable File preparePackageParserCache() { 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 = Environment.getPackageCacheDirectory(); if (!FileUtils.createDir(cacheBaseDir)) { return null; } // There are several items that need to be combined together to safely // identify cached items. In particular, changing the value of certain // feature flags should cause us to invalidate any caches. final String cacheName = SystemProperties.digestOf( "ro.build.fingerprint", StorageManager.PROP_ISOLATED_STORAGE, StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT); // Reconcile cache directories, keeping only what we'd actually use. for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) { if (Objects.equals(cacheName, cacheDir.getName())) { Slog.d(TAG, "Keeping known cache " + cacheDir.getName()); } else { Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName()); FileUtils.deleteContentsAndDir(cacheDir); } } // Return the versioned package cache directory. File cacheDir = FileUtils.createDir(cacheBaseDir, cacheName); if (cacheDir == null) { // Something went wrong. Attempt to delete everything and return. Slog.wtf(TAG, "Cache directory cannot be created - wiping base dir " + cacheBaseDir); FileUtils.deleteContentsAndDir(cacheBaseDir); return null; } // The following is a workaround to aid development on non-numbered userdebug // builds or cases where "adb sync" is used on userdebug builds. If we detect that // the system partition is newer. // // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build // that starts with "eng." to signify that this is an engineering build and not // destined for release. if (Build.IS_USERDEBUG && Build.VERSION.INCREMENTAL.startsWith("eng.")) { Slog.w(TAG, "Wiping cache directory because the system partition changed."); // Heuristic: If the /system directory has been modified recently due to an "adb sync" // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable // in general and should not be used for production changes. In this specific case, // we know that they will work. File frameworkDir = new File(Environment.getRootDirectory(), "framework"); if (cacheDir.lastModified() < frameworkDir.lastModified()) { FileUtils.deleteContents(cacheBaseDir); cacheDir = FileUtils.createDir(cacheBaseDir, cacheName); } } return cacheDir; } @Override public boolean isFirstBoot() { // allow instant applications return mFirstBoot; } @Override public boolean isOnlyCoreApps() { // allow instant applications return mOnlyCore; } @Override public boolean isDeviceUpgrading() { // allow instant applications // The system property allows testing ota flow when upgraded to the same image. return mIsUpgrade || SystemProperties.getBoolean( "persist.pm.mock-upgrade", false /* default */); } private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() { final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); final List matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/); if (matches.size() == 1) { return matches.get(0).getComponentInfo().packageName; } else if (matches.size() == 0) { Log.e(TAG, "There should probably be a verifier, but, none were found"); return null; } throw new RuntimeException("There must be exactly one verifier; found " + matches); } private @NonNull String getRequiredSharedLibraryLPr(String name, int version) { synchronized (mPackages) { SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(name, version); if (libraryInfo == null) { throw new IllegalStateException("Missing required shared library:" + name); } String packageName = libraryInfo.getPackageName(); if (packageName == null) { throw new IllegalStateException("Expected a package for shared library " + name); } return packageName; } } private @NonNull String getRequiredInstallerLPr() { final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setDataAndType(Uri.parse("content://com.example/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 String getRequiredPermissionControllerLPr() { final Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSIONS); intent.addCategory(Intent.CATEGORY_DEFAULT); final List matches = queryIntentActivitiesInternal(intent, null, 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 permissions manager must be a privileged app"); } return matches.get(0).getComponentInfo().packageName; } else { throw new RuntimeException("There must be exactly one permissions manager; found " + matches); } } private @NonNull ComponentName getIntentFilterVerifierComponentNameLPr() { final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION); final List matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/); ResolveInfo best = null; final int N = matches.size(); for (int i = 0; i < N; i++) { final ResolveInfo cur = matches.get(i); final String packageName = cur.getComponentInfo().packageName; if (checkPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT, packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) { continue; } if (best == null || cur.priority > best.priority) { best = cur; } } if (best != null) { return best.getComponentInfo().getComponentName(); } Slog.w(TAG, "Intent filter verifier not found"); return null; } @Override public @Nullable ComponentName getInstantAppResolverComponent() { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return null; } synchronized (mPackages) { final Pair instantAppResolver = getInstantAppResolverLPr(); if (instantAppResolver == null) { return null; } return instantAppResolver.first; } } private @Nullable Pair getInstantAppResolverLPr() { final String[] packageArray = mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage); if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) { if (DEBUG_INSTANT) { Slog.d(TAG, "Ephemeral resolver NOT found; empty package list"); } return null; } final int callingUid = Binder.getCallingUid(); final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0); String actionName = Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE; final Intent resolverIntent = new Intent(actionName); List resolvers = queryIntentServicesInternal(resolverIntent, null, resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/); final int N = resolvers.size(); if (N == 0) { if (DEBUG_INSTANT) { Slog.d(TAG, "Ephemeral resolver NOT found; no matching intent filters"); } return null; } final Set possiblePackages = new ArraySet<>(Arrays.asList(packageArray)); for (int i = 0; i < N; i++) { final ResolveInfo info = resolvers.get(i); if (info.serviceInfo == null) { continue; } final String packageName = info.serviceInfo.packageName; if (!possiblePackages.contains(packageName) && !Build.IS_DEBUGGABLE) { if (DEBUG_INSTANT) { Slog.d(TAG, "Ephemeral resolver not in allowed package list;" + " pkg: " + packageName + ", info:" + info); } continue; } if (DEBUG_INSTANT) { Slog.v(TAG, "Ephemeral resolver found;" + " pkg: " + packageName + ", info:" + info); } return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName); } if (DEBUG_INSTANT) { Slog.v(TAG, "Ephemeral resolver NOT found"); } return null; } @GuardedBy("mPackages") private @Nullable ActivityInfo getInstantAppInstallerLPr() { String[] orderedActions = Build.IS_ENG ? new String[]{ Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE + "_TEST", Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE} : new String[]{ Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE}; final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | Intent.FLAG_IGNORE_EPHEMERAL | (!Build.IS_ENG ? MATCH_SYSTEM_ONLY : 0); final Intent intent = new Intent(); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE); List matches = null; for (String action : orderedActions) { intent.setAction(action); matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE, resolveFlags, UserHandle.USER_SYSTEM); if (matches.isEmpty()) { if (DEBUG_INSTANT) { Slog.d(TAG, "Instant App installer not found with " + action); } } else { break; } } Iterator iter = matches.iterator(); while (iter.hasNext()) { final ResolveInfo rInfo = iter.next(); final PackageSetting ps = mSettings.mPackages.get(rInfo.activityInfo.packageName); if (ps != null) { final PermissionsState permissionsState = ps.getPermissionsState(); if (permissionsState.hasPermission(Manifest.permission.INSTALL_PACKAGES, 0) || Build.IS_ENG) { continue; } } iter.remove(); } if (matches.size() == 0) { return null; } else if (matches.size() == 1) { return (ActivityInfo) matches.get(0).getComponentInfo(); } else { throw new RuntimeException( "There must be at most one ephemeral installer; found " + matches); } } private @Nullable ComponentName getInstantAppResolverSettingsLPr( @NonNull ComponentName resolver) { final Intent intent = new Intent(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS) .addCategory(Intent.CATEGORY_DEFAULT) .setPackage(resolver.getPackageName()); final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; List matches = queryIntentActivitiesInternal(intent, null, resolveFlags, UserHandle.USER_SYSTEM); if (matches.isEmpty()) { return null; } return matches.get(0).getComponentInfo().getComponentName(); } @GuardedBy("mPackages") private void primeDomainVerificationsLPw(int userId) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.d(TAG, "Priming domain verifications in user " + userId); } SystemConfig systemConfig = SystemConfig.getInstance(); ArraySet packages = systemConfig.getLinkedApps(); for (String packageName : packages) { PackageParser.Package pkg = mPackages.get(packageName); if (pkg != null) { if (!pkg.isSystem()) { Slog.w(TAG, "Non-system app '" + packageName + "' in sysconfig "); continue; } ArraySet domains = null; for (PackageParser.Activity a : pkg.activities) { for (ActivityIntentInfo filter : a.intents) { if (hasValidDomains(filter)) { if (domains == null) { domains = new ArraySet<>(); } domains.addAll(filter.getHostsList()); } } } if (domains != null && domains.size() > 0) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.v(TAG, " + " + packageName); } // 'Undefined' in the global IntentFilterVerificationInfo, i.e. the usual // state w.r.t. the formal app-linkage "no verification attempted" state; // and then 'always' in the per-user state actually used for intent resolution. final IntentFilterVerificationInfo ivi; ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName, domains); ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED); mSettings.updateIntentFilterVerificationStatusLPw(packageName, INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, userId); } else { Slog.w(TAG, "Sysconfig package '" + packageName + "' does not handle web links"); } } else { Slog.w(TAG, "Unknown package " + packageName + " in sysconfig "); } } scheduleWritePackageRestrictionsLocked(userId); scheduleWriteSettingsLocked(); } private boolean packageIsBrowser(String packageName, int userId) { List list = queryIntentActivitiesInternal(sBrowserIntent, null, PackageManager.MATCH_ALL, userId); final int N = list.size(); for (int i = 0; i < N; i++) { ResolveInfo info = list.get(i); if (info.priority >= 0 && packageName.equals(info.activityInfo.packageName)) { return true; } } return false; } @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; } } /** * Returns whether or not a full application can see an instant application. * * Currently, there are four cases in which this can occur: * * The calling application is a "special" process. Special processes * are those with a UID < {@link Process#FIRST_APPLICATION_UID}. * The calling application has the permission * {@link android.Manifest.permission#ACCESS_INSTANT_APPS}. * The calling application is the default launcher on the * system partition. * The calling application is the default app prediction service. * */ private boolean canViewInstantApps(int callingUid, int userId) { if (callingUid < Process.FIRST_APPLICATION_UID) { return true; } if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.ACCESS_INSTANT_APPS) == PERMISSION_GRANTED) { return true; } if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.VIEW_INSTANT_APPS) == PERMISSION_GRANTED) { final ComponentName homeComponent = getDefaultHomeActivity(userId); if (homeComponent != null && isCallerSameApp(homeComponent.getPackageName(), callingUid)) { return true; } // TODO(b/122900055) Change/Remove this and replace with new permission role. if (mAppPredictionServicePackage != null && isCallerSameApp(mAppPredictionServicePackage, callingUid)) { return true; } } return false; } private PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) { if (!sUserManager.exists(userId)) return null; if (ps == null) { return null; } final int callingUid = Binder.getCallingUid(); // Filter out ephemeral app metadata: // * The system/shell/root can see metadata for any app // * An installed app can see metadata for 1) other installed apps // and 2) ephemeral apps that have explicitly interacted with it // * Ephemeral apps can only see their own data and exposed installed apps // * Holding a signature permission allows seeing instant apps if (filterAppAccessLPr(ps, callingUid, userId)) { return null; } if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && ps.isSystem()) { flags |= MATCH_ANY_USER; } final PackageUserState state = ps.readUserState(userId); PackageParser.Package p = ps.pkg; if (p != null) { final PermissionsState permissionsState = ps.getPermissionsState(); // Compute GIDs only if requested final int[] gids = (flags & PackageManager.GET_GIDS) == 0 ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId); // Compute granted permissions only if package has requested permissions final Set permissions = ArrayUtils.isEmpty(p.requestedPermissions) ? Collections.emptySet() : permissionsState.getPermissions(userId); PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags, ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId); if (packageInfo == null) { return null; } packageInfo.packageName = packageInfo.applicationInfo.packageName = resolveExternalPackageNameLPr(p); return packageInfo; } else if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && state.isAvailable(flags)) { PackageInfo pi = new PackageInfo(); pi.packageName = ps.name; pi.setLongVersionCode(ps.versionCode); pi.sharedUserId = (ps.sharedUser != null) ? ps.sharedUser.name : null; pi.firstInstallTime = ps.firstInstallTime; pi.lastUpdateTime = ps.lastUpdateTime; ApplicationInfo ai = new ApplicationInfo(); ai.packageName = ps.name; ai.uid = UserHandle.getUid(userId, ps.appId); ai.primaryCpuAbi = ps.primaryCpuAbiString; ai.secondaryCpuAbi = ps.secondaryCpuAbiString; ai.setVersionCode(ps.versionCode); ai.flags = ps.pkgFlags; ai.privateFlags = ps.pkgPrivateFlags; pi.applicationInfo = PackageParser.generateApplicationInfo(ai, flags, state, userId); if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for [" + ps.name + "]. Provides a minimum info."); return pi; } else { return null; } } @Override public void checkPackageStartable(String packageName, int userId) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { throw new SecurityException("Instant applications don't have access to this method"); } final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId); synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) { throw new SecurityException("Package " + packageName + " was not found!"); } if (!ps.getInstalled(userId)) { throw new SecurityException( "Package " + packageName + " was not installed for user " + userId + "!"); } if (mSafeMode && !ps.isSystem()) { throw new SecurityException("Package " + packageName + " not a system app!"); } if (mFrozenPackages.contains(packageName)) { throw new SecurityException("Package " + packageName + " is currently frozen!"); } if (!userKeyUnlocked && !ps.pkg.applicationInfo.isEncryptionAware()) { throw new SecurityException("Package " + packageName + " is not encryption aware!"); } } } @Override public boolean isPackageAvailable(String packageName, int userId) { if (!sUserManager.exists(userId)) return false; final int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "is package available"); synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); if (p != null) { final PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, userId)) { return false; } if (ps != null) { final PackageUserState state = ps.readUserState(userId); if (state != null) { return PackageParser.isAvailable(state); } } } } return false; } @Override public PackageInfo getPackageInfo(String packageName, int flags, int userId) { return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST, flags, Binder.getCallingUid(), userId); } @Override public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage, int flags, int userId) { return getPackageInfoInternal(versionedPackage.getPackageName(), versionedPackage.getLongVersionCode(), flags, Binder.getCallingUid(), userId); } /** * Important: The provided filterCallingUid is used exclusively to filter out packages * that can be seen based on user state. It's typically the original caller uid prior * to clearing. Because it can only be provided by trusted code, it's value can be * trusted and will be used as-is; unlike userId which will be validated by this method. */ private PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags, int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForPackage(flags, userId, packageName); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get package info"); // reader synchronized (mPackages) { // Normalize package name to handle renamed packages and static libs packageName = resolveInternalPackageNameLPr(packageName, versionCode); final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0; if (matchFactoryOnly) { // Instant app filtering for APEX modules is ignored if ((flags & MATCH_APEX) != 0) { return mApexManager.getPackageInfo(packageName, ApexManager.MATCH_FACTORY_PACKAGE); } 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); } if (!matchFactoryOnly && (flags & MATCH_APEX) != 0) { return mApexManager.getPackageInfo(packageName, ApexManager.MATCH_ACTIVE_PACKAGE); } } 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 = mComponentResolver.getActivity(component); if (activity == null) { return false; } final boolean visibleToInstantApp = (activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean explicitlyVisibleToInstantApp = (activity.info.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; return visibleToInstantApp && explicitlyVisibleToInstantApp; } else if (type == TYPE_RECEIVER) { final PackageParser.Activity activity = mComponentResolver.getReceiver(component); if (activity == null) { return false; } final boolean visibleToInstantApp = (activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean explicitlyVisibleToInstantApp = (activity.info.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; return visibleToInstantApp && !explicitlyVisibleToInstantApp; } else if (type == TYPE_SERVICE) { final PackageParser.Service service = mComponentResolver.getService(component); return service != null ? (service.info.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0 : false; } else if (type == TYPE_PROVIDER) { final PackageParser.Provider provider = mComponentResolver.getProvider(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 #canViewInstantApps(int, int) */ @GuardedBy("mPackages") private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, @Nullable ComponentName component, @ComponentType int componentType, int userId) { // if we're in an isolated process, get the real calling UID if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final String instantAppPkgName = getInstantAppPackageName(callingUid); final boolean callerIsInstantApp = instantAppPkgName != null; if (ps == null) { if (callerIsInstantApp) { // pretend the application exists, but, needs to be filtered return true; } return false; } // if the target and caller are the same application, don't filter if (isCallerSameApp(ps.name, callingUid)) { return false; } if (callerIsInstantApp) { // both caller and target are both instant, but, different applications, filter if (ps.getInstantApp(userId)) { return true; } // request for a specific component; if it hasn't been explicitly exposed through // property or instrumentation target, filter if (component != null) { final PackageParser.Instrumentation instrumentation = mInstrumentation.get(component); if (instrumentation != null && isCallerSameApp(instrumentation.info.targetPackage, callingUid)) { return false; } return !isComponentVisibleToInstantApp(component, componentType); } // request for application; if no components have been explicitly exposed, filter return !ps.pkg.visibleToInstantApps; } if (ps.getInstantApp(userId)) { // caller can see all components of all instant applications, don't filter if (canViewInstantApps(callingUid, userId)) { return false; } // request for a specific instant application component, filter if (component != null) { return true; } // request for an instant application; if the caller hasn't been granted access, filter return !mInstantAppRegistry.isInstantAccessGranted( userId, UserHandle.getAppId(callingUid), ps.appId); } return false; } /** * @see #filterAppAccessLPr(PackageSetting, int, ComponentName, int, int) */ @GuardedBy("mPackages") private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, int userId) { return filterAppAccessLPr(ps, callingUid, null, TYPE_UNKNOWN, userId); } @GuardedBy("mPackages") 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; } // Installer gets to see all static libs. if (PackageManager.PERMISSION_GRANTED == checkUidPermission(Manifest.permission.INSTALL_PACKAGES, 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 SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(ps.pkg.staticSharedLibName, ps.pkg.staticSharedLibVersion); if (libraryInfo == 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, libraryInfo.getName()); if (index < 0) { continue; } if (uidPs.pkg.usesStaticLibrariesVersions[index] == libraryInfo.getLongVersion()) { return false; } } } return true; } @Override public String[] currentToCanonicalPackageNames(String[] names) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return names; } final String[] out = new String[names.length]; // reader synchronized (mPackages) { final int callingUserId = UserHandle.getUserId(callingUid); final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId); for (int i=names.length-1; i>=0; i--) { final PackageSetting ps = mSettings.mPackages.get(names[i]); boolean translateName = false; if (ps != null && ps.realName != null) { final boolean targetIsInstantApp = ps.getInstantApp(callingUserId); translateName = !targetIsInstantApp || canViewInstantApps || mInstantAppRegistry.isInstantAccessGranted(callingUserId, UserHandle.getAppId(callingUid), ps.appId); } out[i] = translateName ? ps.realName : names[i]; } } return out; } @Override public String[] canonicalToCurrentPackageNames(String[] names) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return names; } final String[] out = new String[names.length]; // reader synchronized (mPackages) { final int callingUserId = UserHandle.getUserId(callingUid); final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId); for (int i=names.length-1; i>=0; i--) { final String cur = mSettings.getRenamedPackageLPr(names[i]); boolean translateName = false; if (cur != null) { final PackageSetting ps = mSettings.mPackages.get(names[i]); final boolean targetIsInstantApp = ps != null && ps.getInstantApp(callingUserId); translateName = !targetIsInstantApp || canViewInstantApps || mInstantAppRegistry.isInstantAccessGranted(callingUserId, UserHandle.getAppId(callingUid), ps.appId); } out[i] = translateName ? cur : names[i]; } } return out; } @Override public int getPackageUid(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return -1; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForPackage(flags, userId, packageName); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid"); // reader synchronized (mPackages) { final PackageParser.Package p = mPackages.get(packageName); if (p != null && p.isMatch(flags)) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, userId)) { return -1; } return UserHandle.getUid(userId, p.applicationInfo.uid); } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && ps.isMatch(flags) && !filterAppAccessLPr(ps, callingUid, userId)) { return UserHandle.getUid(userId, ps.appId); } } } return -1; } /** * Check if any package sharing/holding a uid has a low enough target SDK. * * @param uid The uid of the packages * @param higherTargetSDK The target SDK that might be higher than the searched package * * @return {@code true} if there is a package sharing/holding the uid with * {@code package.targetSDK < higherTargetSDK} */ private boolean hasTargetSdkInUidLowerThan(int uid, int higherTargetSDK) { int userId = UserHandle.getUserId(uid); synchronized (mPackages) { Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid)); if (obj == null) { return false; } if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (!ps.getInstalled(userId)) { return false; } return ps.pkg.applicationInfo.targetSdkVersion < higherTargetSDK; } else if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; final int numPkgs = sus.packages.size(); for (int i = 0; i < numPkgs; i++) { final PackageSetting ps = sus.packages.valueAt(i); if (!ps.getInstalled(userId)) { continue; } if (ps.pkg.applicationInfo.targetSdkVersion < higherTargetSDK) { return true; } } return false; } else { return false; } } } @Override public int[] getPackageGids(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForPackage(flags, userId, packageName); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids"); // reader synchronized (mPackages) { final PackageParser.Package p = mPackages.get(packageName); if (p != null && p.isMatch(flags)) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, userId)) { return null; } // TODO: Shouldn't this be checking for package installed state for userId and // return null? return ps.getPermissionsState().computeGids(userId); } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && ps.isMatch(flags) && !filterAppAccessLPr(ps, callingUid, userId)) { return ps.getPermissionsState().computeGids(userId); } } } return null; } @Override public PermissionInfo getPermissionInfo(String name, String packageName, int flags) { return mPermissionManager.getPermissionInfo(name, packageName, flags, getCallingUid()); } @Override public @Nullable ParceledListSlice queryPermissionsByGroup(String groupName, int flags) { final List permissionList = mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid()); return (permissionList == null) ? null : new ParceledListSlice<>(permissionList); } @Override public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) { return mPermissionManager.getPermissionGroupInfo(groupName, flags, getCallingUid()); } @Override public @NonNull ParceledListSlice getAllPermissionGroups(int flags) { final List permissionList = mPermissionManager.getAllPermissionGroups(flags, getCallingUid()); return (permissionList == null) ? ParceledListSlice.emptyList() : new ParceledListSlice<>(permissionList); } @GuardedBy("mPackages") 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); if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get application info"); } // writer synchronized (mPackages) { // Normalize package name to handle renamed packages and static libs packageName = resolveInternalPackageNameLPr(packageName, PackageManager.VERSION_CODE_HIGHEST); PackageParser.Package p = mPackages.get(packageName); if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getApplicationInfo " + packageName + ": " + p); if (p != null) { PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) return null; if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; } if (filterAppAccessLPr(ps, filterCallingUid, userId)) { return null; } // Note: isEnabledLP() does not apply here - always return info ApplicationInfo ai = PackageParser.generateApplicationInfo( p, flags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(p); } return ai; } if ("android".equals(packageName)||"system".equals(packageName)) { return mAndroidApplication; } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { // Already generates the external package name return generateApplicationInfoFromSettingsLPw(packageName, flags, filterCallingUid, userId); } } return null; } @GuardedBy("mPackages") private String normalizePackageNameLPr(String packageName) { String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName); return normalizedPackageName != null ? normalizedPackageName : packageName; } @Override public void deletePreloadsFileCache() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CLEAR_APP_CACHE, "deletePreloadsFileCache"); File dir = Environment.getDataPreloadsFileCacheDirectory(); Slog.i(TAG, "Deleting preloaded file cache " + dir); FileUtils.deleteContents(dir); } @Override public void freeStorageAndNotify(final String volumeUuid, final long freeStorageSize, final int storageFlags, final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); mHandler.post(() -> { boolean success = false; try { freeStorage(volumeUuid, freeStorageSize, storageFlags); success = true; } catch (IOException e) { Slog.w(TAG, e); } if (observer != null) { try { observer.onRemoveCompleted(null, success); } catch (RemoteException e) { Slog.w(TAG, e); } } }); } @Override public void freeStorage(final String volumeUuid, final long freeStorageSize, final int storageFlags, final IntentSender pi) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, TAG); mHandler.post(() -> { boolean success = false; try { freeStorage(volumeUuid, freeStorageSize, storageFlags); success = true; } catch (IOException e) { Slog.w(TAG, e); } if (pi != null) { try { pi.sendIntent(null, success ? 1 : 0, null, null, null); } catch (SendIntentException e) { Slog.w(TAG, e); } } }); } /** * Blocking call to clear various types of cached data across the system * until the requested bytes are available. */ public void freeStorage(String volumeUuid, long bytes, int storageFlags) throws IOException { final StorageManager storage = mContext.getSystemService(StorageManager.class); final File file = storage.findPathForUuid(volumeUuid); if (file.getUsableSpace() >= bytes) return; if (ENABLE_FREE_CACHE_V2) { final boolean internalVolume = Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid); final boolean aggressive = (storageFlags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0; final long reservedBytes = storage.getStorageCacheBytes(file, storageFlags); // 1. Pre-flight to determine if we have any chance to succeed // 2. Consider preloaded data (after 1w honeymoon, unless aggressive) if (internalVolume && (aggressive || SystemProperties .getBoolean("persist.sys.preloads.file_cache_expired", false))) { deletePreloadsFileCache(); if (file.getUsableSpace() >= bytes) return; } // 3. Consider parsed APK data (aggressive only) if (internalVolume && aggressive) { FileUtils.deleteContents(mCacheDir); if (file.getUsableSpace() >= bytes) return; } // 4. Consider cached app data (above quotas) try { mInstaller.freeCache(volumeUuid, bytes, reservedBytes, Installer.FLAG_FREE_CACHE_V2); } catch (InstallerException ignored) { } if (file.getUsableSpace() >= bytes) return; // 5. Consider shared libraries with refcount=0 and age>min cache period if (internalVolume && pruneUnusedStaticSharedLibraries(bytes, android.provider.Settings.Global.getLong(mContext.getContentResolver(), Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD, DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD))) { return; } // 6. Consider dexopt output (aggressive only) // TODO: Implement // 7. Consider installed instant apps unused longer than min cache period if (internalVolume && mInstantAppRegistry.pruneInstalledInstantApps(bytes, android.provider.Settings.Global.getLong(mContext.getContentResolver(), Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD, InstantAppRegistry.DEFAULT_INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) { return; } // 8. Consider cached app data (below quotas) try { mInstaller.freeCache(volumeUuid, bytes, reservedBytes, Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA); } catch (InstallerException ignored) { } if (file.getUsableSpace() >= bytes) return; // 9. Consider DropBox entries // TODO: Implement // 10. Consider instant meta-data (uninstalled apps) older that min cache period if (internalVolume && mInstantAppRegistry.pruneUninstalledInstantApps(bytes, android.provider.Settings.Global.getLong(mContext.getContentResolver(), Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD, InstantAppRegistry.DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) { return; } } else { try { mInstaller.freeCache(volumeUuid, bytes, 0, 0); } catch (InstallerException ignored) { } if (file.getUsableSpace() >= bytes) return; } throw new IOException("Failed to free " + bytes + " on storage device at " + file); } private boolean pruneUnusedStaticSharedLibraries(long neededSpace, long maxCachePeriod) throws IOException { final StorageManager storage = mContext.getSystemService(StorageManager.class); final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL); List packagesToDelete = null; final long now = System.currentTimeMillis(); synchronized (mPackages) { final int[] allUsers = sUserManager.getUserIds(); final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { final LongSparseArray versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } final int versionCount = versionedLib.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libInfo = versionedLib.valueAt(j); // Skip packages that are not static shared libs. if (!libInfo.isStatic()) { break; } // Important: We skip static shared libs used for some user since // in such a case we need to keep the APK on the device. The check for // a lib being used for any user is performed by the uninstall call. final VersionedPackage declaringPackage = libInfo.getDeclaringPackage(); // Resolve the package name - we use synthetic package names internally final String internalPackageName = resolveInternalPackageNameLPr( declaringPackage.getPackageName(), declaringPackage.getLongVersionCode()); final PackageSetting ps = mSettings.getPackageLPr(internalPackageName); // Skip unused static shared libs cached less than the min period // to prevent pruning a lib needed by a subsequently installed package. if (ps == null || now - ps.lastUpdateTime < maxCachePeriod) { continue; } if (ps.pkg.isSystem()) { continue; } if (packagesToDelete == null) { packagesToDelete = new ArrayList<>(); } packagesToDelete.add(new VersionedPackage(internalPackageName, declaringPackage.getLongVersionCode())); } } } if (packagesToDelete != null) { final int packageCount = packagesToDelete.size(); for (int i = 0; i < packageCount; i++) { final VersionedPackage pkgToDelete = packagesToDelete.get(i); // Delete the package synchronously (will fail of the lib used for any user). if (deletePackageX(pkgToDelete.getPackageName(), pkgToDelete.getLongVersionCode(), UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS) == PackageManager.DELETE_SUCCEEDED) { if (volume.getUsableSpace() >= neededSpace) { return true; } } } } return false; } /** * Update given flags based on encryption status of current user. */ private int updateFlags(int flags, int userId) { if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_AWARE)) != 0) { // Caller expressed an explicit opinion about what encryption // aware/unaware components they want to see, so fall through and // give them what they want } else { // Caller expressed no opinion, so match based on user state if (getUserManagerInternal().isUserUnlockingOrUnlocked(userId)) { flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; } else { flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE; } } return flags; } private UserManagerInternal getUserManagerInternal() { if (mUserManagerInternal == null) { mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); } return mUserManagerInternal; } private ActivityManagerInternal getActivityManagerInternal() { if (mActivityManagerInternal == null) { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); } return mActivityManagerInternal; } private ActivityTaskManagerInternal getActivityTaskManagerInternal() { if (mActivityTaskManagerInternal == null) { mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); } return mActivityTaskManagerInternal; } private DeviceIdleController.LocalService getDeviceIdleController() { if (mDeviceIdleController == null) { mDeviceIdleController = LocalServices.getService(DeviceIdleController.LocalService.class); } return mDeviceIdleController; } private StorageManagerInternal getStorageManagerInternal() { if (mStorageManagerInternal == null) { mStorageManagerInternal = LocalServices.getService(StorageManagerInternal.class); } return mStorageManagerInternal; } /** * 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; if ((flags & PackageManager.MATCH_ANY_USER) != 0) { // require the permission to be held; the calling uid and given user id referring // to the same user is not sufficient mPermissionManager.enforceCrossUserPermission( Binder.getCallingUid(), userId, false, false, !isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId), "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; } 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) { return updateFlags(flags, userId); } /** * Update given intent when being used to request {@link ResolveInfo}. */ private Intent updateIntentForResolve(Intent intent) { if (intent.getSelector() != null) { intent = intent.getSelector(); } if (DEBUG_PREFERRED) { intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); } return intent; } /** * Update given flags when being used to request {@link ResolveInfo}. * Instant apps are resolved specially, depending upon context. Minimally, * {@code}flags{@code} must have the {@link PackageManager#MATCH_INSTANT} * flag set. However, this flag is only honoured in three circumstances: * * when called from a system process * when the caller holds the permission {@code android.permission.ACCESS_INSTANT_APPS} * when resolution occurs to start an activity with a {@code android.intent.action.VIEW} * action and a {@code android.intent.category.BROWSABLE} category * */ int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid) { return updateFlagsForResolve(flags, userId, intent, callingUid, false /*wantInstantApps*/, false /*onlyExposedExplicitly*/); } int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid, boolean wantInstantApps) { return updateFlagsForResolve(flags, userId, intent, callingUid, wantInstantApps, false /*onlyExposedExplicitly*/); } int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid, boolean wantInstantApps, boolean onlyExposedExplicitly) { // Safe mode means we shouldn't match any third-party components if (mSafeMode) { flags |= PackageManager.MATCH_SYSTEM_ONLY; } if (getInstantAppPackageName(callingUid) != null) { // But, ephemeral apps see both ephemeral and exposed, non-ephemeral components if (onlyExposedExplicitly) { flags |= PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY; } flags |= PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY; flags |= PackageManager.MATCH_INSTANT; } else { final boolean wantMatchInstant = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean allowMatchInstant = wantInstantApps || (wantMatchInstant && canViewInstantApps(callingUid, userId)); flags &= ~(PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY | PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY); if (!allowMatchInstant) { flags &= ~PackageManager.MATCH_INSTANT; } } return updateFlagsForComponent(flags, userId, intent /*cookie*/); } @Override public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId); } /** * Important: The provided filterCallingUid is used exclusively to filter out activities * that can be seen based on user state. It's typically the original caller uid prior * to clearing. Because it can only be provided by trusted code, it's value can be * trusted and will be used as-is; unlike userId which will be validated by this method. */ private ActivityInfo getActivityInfoInternal(ComponentName component, int flags, int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId, component); if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get activity info"); } synchronized (mPackages) { PackageParser.Activity a = mComponentResolver.getActivity(component); if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a); if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) { return null; } return PackageParser.generateActivityInfo( a, flags, ps.readUserState(userId), userId); } if (mResolveComponentName.equals(component)) { return PackageParser.generateActivityInfo( mResolveActivity, flags, new PackageUserState(), userId); } } return null; } private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) { if (!getActivityTaskManagerInternal().isCallerRecents(callingUid)) { return false; } final long token = Binder.clearCallingIdentity(); try { final int callingUserId = UserHandle.getUserId(callingUid); if (ActivityManager.getCurrentUser() != callingUserId) { return false; } return sUserManager.isSameProfileGroup(callingUserId, targetUserId); } finally { Binder.restoreCallingIdentity(token); } } @Override public boolean activitySupportsIntent(ComponentName component, Intent intent, String resolvedType) { synchronized (mPackages) { if (component.equals(mResolveComponentName)) { // The resolver supports EVERYTHING! return true; } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); PackageParser.Activity a = mComponentResolver.getActivity(component); if (a == null) { return false; } PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) { return false; } if (filterAppAccessLPr(ps, callingUid, component, TYPE_ACTIVITY, callingUserId)) { return false; } for (int i=0; i= 0) { return true; } } return false; } } @Override public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get receiver info"); synchronized (mPackages) { PackageParser.Activity a = mComponentResolver.getReceiver(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 || mContext.checkCallingOrSelfPermission( Manifest.permission.ACCESS_SHARED_LIBRARIES) == PERMISSION_GRANTED; synchronized (mPackages) { List result = null; final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { LongSparseArray versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } final int versionCount = versionedLib.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libInfo = versionedLib.valueAt(j); 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.getPath(), libInfo.getPackageName(), libInfo.getAllCodePaths(), libInfo.getName(), libInfo.getLongVersion(), libInfo.getType(), libInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr(libInfo, flags, userId), (libInfo.getDependencies() == null ? null : new ArrayList<>(libInfo.getDependencies()))); if (result == null) { result = new ArrayList<>(); } result.add(resLibInfo); } } return result != null ? new ParceledListSlice<>(result) : null; } } @Nullable @Override public ParceledListSlice getDeclaredSharedLibraries( @NonNull String packageName, int flags, @NonNull int userId) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES, "getDeclaredSharedLibraries"); int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "getDeclaredSharedLibraries"); Preconditions.checkNotNull(packageName, "packageName cannot be null"); Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0"); if (!sUserManager.exists(userId)) { return null; } if (getInstantAppPackageName(callingUid) != null) { return null; } synchronized (mPackages) { List result = null; int libraryCount = mSharedLibraries.size(); for (int i = 0; i < libraryCount; i++) { LongSparseArray versionedLibrary = mSharedLibraries.valueAt(i); if (versionedLibrary == null) { continue; } int versionCount = versionedLibrary.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libraryInfo = versionedLibrary.valueAt(j); VersionedPackage declaringPackage = libraryInfo.getDeclaringPackage(); if (!Objects.equals(declaringPackage.getPackageName(), packageName)) { continue; } long identity = Binder.clearCallingIdentity(); try { PackageInfo packageInfo = getPackageInfoVersioned(declaringPackage, flags | PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId); if (packageInfo == null) { continue; } } finally { Binder.restoreCallingIdentity(identity); } SharedLibraryInfo resultLibraryInfo = new SharedLibraryInfo( libraryInfo.getPath(), libraryInfo.getPackageName(), libraryInfo.getAllCodePaths(), libraryInfo.getName(), libraryInfo.getLongVersion(), libraryInfo.getType(), libraryInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr( libraryInfo, flags, userId), libraryInfo.getDependencies() == null ? null : new ArrayList<>(libraryInfo.getDependencies())); if (result == null) { result = new ArrayList<>(); } result.add(resultLibraryInfo); } } return result != null ? new ParceledListSlice<>(result) : null; } } @GuardedBy("mPackages") 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.readUserState(userId).isAvailable(flags)) { continue; } final String libName = libInfo.getName(); if (libInfo.isStatic()) { final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName); if (libIdx < 0) { continue; } if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getLongVersion()) { continue; } if (versionedPackages == null) { versionedPackages = new ArrayList<>(); } // If the dependent is a static shared lib, use the public package name String dependentPackageName = ps.name; if (ps.pkg != null && ps.pkg.applicationInfo.isStaticSharedLibrary()) { dependentPackageName = ps.pkg.manifestPackageName; } versionedPackages.add(new VersionedPackage(dependentPackageName, ps.versionCode)); } else if (ps.pkg != null) { if (ArrayUtils.contains(ps.pkg.usesLibraries, libName) || ArrayUtils.contains(ps.pkg.usesOptionalLibraries, libName)) { if (versionedPackages == null) { versionedPackages = new ArrayList<>(); } versionedPackages.add(new VersionedPackage(ps.name, ps.versionCode)); } } } return versionedPackages; } @Override public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get service info"); synchronized (mPackages) { PackageParser.Service s = mComponentResolver.getService(component); if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getServiceInfo " + component + ": " + s); if (s != null && mSettings.isEnabledAndMatchLPr(s.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, callingUid, component, TYPE_SERVICE, userId)) { return null; } return PackageParser.generateServiceInfo( s, flags, ps.readUserState(userId), userId); } } return null; } @Override public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get provider info"); synchronized (mPackages) { PackageParser.Provider p = mComponentResolver.getProvider(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 ModuleInfo getModuleInfo(String packageName, @ModuleInfoFlags int flags) { return mModuleInfoProvider.getModuleInfo(packageName, flags); } @Override public List getInstalledModules(int flags) { return mModuleInfoProvider.getInstalledModules(flags); } @Override public String[] getSystemSharedLibraryNames() { // allow instant applications synchronized (mPackages) { Set libs = null; final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { LongSparseArray versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } final int versionCount = versionedLib.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libraryInfo = versionedLib.valueAt(j); if (!libraryInfo.isStatic()) { if (libs == null) { libs = new ArraySet<>(); } libs.add(libraryInfo.getName()); break; } PackageSetting ps = mSettings.getPackageLPr(libraryInfo.getPackageName()); if (ps != null && !filterSharedLibPackageLPr(ps, Binder.getCallingUid(), UserHandle.getUserId(Binder.getCallingUid()), PackageManager.MATCH_STATIC_SHARED_LIBRARIES)) { if (libs == null) { libs = new ArraySet<>(); } libs.add(libraryInfo.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; } } @GuardedBy("mPackages") 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) { final CheckPermissionDelegate checkPermissionDelegate; synchronized (mPackages) { if (mCheckPermissionDelegate == null) { return checkPermissionImpl(permName, pkgName, userId); } checkPermissionDelegate = mCheckPermissionDelegate; } return checkPermissionDelegate.checkPermission(permName, pkgName, userId, PackageManagerService.this::checkPermissionImpl); } private int checkPermissionImpl(String permName, String pkgName, int userId) { return mPermissionManager.checkPermission(permName, pkgName, getCallingUid(), userId); } @Override public int checkUidPermission(String permName, int uid) { final CheckPermissionDelegate checkPermissionDelegate; synchronized (mPackages) { if (mCheckPermissionDelegate == null) { return checkUidPermissionImpl(permName, uid); } checkPermissionDelegate = mCheckPermissionDelegate; } return checkPermissionDelegate.checkUidPermission(permName, uid, PackageManagerService.this::checkUidPermissionImpl); } private int checkUidPermissionImpl(String permName, int uid) { synchronized (mPackages) { final String[] packageNames = getPackagesForUid(uid); PackageParser.Package pkg = null; final int N = packageNames == null ? 0 : packageNames.length; for (int i = 0; pkg == null && i < N; i++) { pkg = mPackages.get(packageNames[i]); } return mPermissionManager.checkUidPermission(permName, pkg, uid, getCallingUid()); } } @Override public boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId) { if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "isPermissionRevokedByPolicy for user " + userId); } if (checkPermission(permission, packageName, userId) == PackageManager.PERMISSION_GRANTED) { return false; } final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { if (!isCallerSameApp(packageName, callingUid)) { return false; } } else { if (isInstantApp(packageName, userId)) { return false; } } final long identity = Binder.clearCallingIdentity(); try { final int flags = getPermissionFlags(permission, packageName, userId); return (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0; } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getPermissionControllerPackageName() { synchronized (mPackages) { return mRequiredPermissionControllerPackage; } } String getPackageInstallerPackageName() { synchronized (mPackages) { return mRequiredInstallerPackage; } } private boolean addDynamicPermission(PermissionInfo info, final boolean async) { return mPermissionManager.addDynamicPermission( info, async, getCallingUid(), new PermissionCallback() { @Override public void onPermissionChanged() { if (!async) { mSettings.writeLPr(); } else { scheduleWriteSettingsLocked(); } } }); } @Override public boolean addPermission(PermissionInfo info) { synchronized (mPackages) { return addDynamicPermission(info, false); } } @Override public boolean addPermissionAsync(PermissionInfo info) { synchronized (mPackages) { return addDynamicPermission(info, true); } } @Override public void removePermission(String permName) { mPermissionManager.removeDynamicPermission(permName, getCallingUid(), mPermissionCallback); } @Override public void grantRuntimePermission(String packageName, String permName, final int userId) { boolean overridePolicy = (checkUidPermission( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED); mPermissionManager.grantRuntimePermission(permName, packageName, overridePolicy, getCallingUid(), userId, mPermissionCallback); } @Override public void revokeRuntimePermission(String packageName, String permName, int userId) { boolean overridePolicy = (checkUidPermission( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED); mPermissionManager.revokeRuntimePermission(permName, packageName, overridePolicy, userId, mPermissionCallback); } @Override public void resetRuntimePermissions() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, "revokeRuntimePermission"); int callingUid = Binder.getCallingUid(); if (callingUid != Process.SYSTEM_UID && callingUid != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "resetRuntimePermissions"); } synchronized (mPackages) { mPermissionManager.updateAllPermissions( StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(), mPermissionCallback); for (int userId : UserManagerService.getInstance().getUserIds()) { final int packageCount = mPackages.size(); for (int i = 0; i < packageCount; i++) { PackageParser.Package pkg = mPackages.valueAt(i); if (!(pkg.mExtras instanceof PackageSetting)) { continue; } PackageSetting ps = (PackageSetting) pkg.mExtras; resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, userId); } } } } @Override public int getPermissionFlags(String permName, String packageName, int userId) { return mPermissionManager.getPermissionFlags( permName, packageName, getCallingUid(), userId); } @Override public void updatePermissionFlags(String permName, String packageName, int flagMask, int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) { int callingUid = getCallingUid(); boolean overridePolicy = false; if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) { long callingIdentity = Binder.clearCallingIdentity(); try { if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0) { if (checkAdjustPolicyFlagPermission) { mContext.enforceCallingOrSelfPermission( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "Need " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " to change policy flags"); } else if (!hasTargetSdkInUidLowerThan(callingUid, Build.VERSION_CODES.Q)) { throw new IllegalArgumentException( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " needs " + " to be checked for packages targeting " + Build.VERSION_CODES.Q + " or later when changing policy " + "flags"); } overridePolicy = true; } } finally { Binder.restoreCallingIdentity(callingIdentity); } } mPermissionManager.updatePermissionFlags( permName, packageName, flagMask, flagValues, callingUid, userId, overridePolicy, mPermissionCallback); } /** * Update the permission flags for all packages and runtime permissions of a user in order * to allow device or profile owner to remove POLICY_FIXED. */ @Override public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) { synchronized (mPackages) { final boolean changed = mPermissionManager.updatePermissionFlagsForAllApps( flagMask, flagValues, getCallingUid(), userId, mPackages.values(), mPermissionCallback); if (changed) { mSettings.writeRuntimePermissionsForUserLPr(userId, false); } } } @Override public @Nullable List getWhitelistedRestrictedPermissions(@NonNull String packageName, @PermissionWhitelistFlags int whitelistFlags, @UserIdInt int userId) { Preconditions.checkNotNull(packageName); Preconditions.checkFlagsArgument(whitelistFlags, PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER); Preconditions.checkArgumentNonNegative(userId, null); if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS, "getWhitelistedRestrictedPermissions for user " + userId); } final PackageParser.Package pkg; synchronized (mPackages) { final PackageSetting packageSetting = mSettings.mPackages.get(packageName); if (packageSetting == null) { Slog.w(TAG, "Unknown package: " + packageName); return null; } pkg = packageSetting.pkg; final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) == PackageManager.PERMISSION_GRANTED; final PackageSetting installerPackageSetting = mSettings.mPackages.get( packageSetting.installerPackageName); final boolean isCallerInstallerOnRecord = installerPackageSetting != null && UserHandle.isSameApp(installerPackageSetting.appId, Binder.getCallingUid()); if ((whitelistFlags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 && !isCallerPrivileged) { throw new SecurityException("Querying system whitelist requires " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } if ((whitelistFlags & (PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) != 0) { if (!isCallerPrivileged && !isCallerInstallerOnRecord) { throw new SecurityException("Querying upgrade or installer whitelist" + " requires being installer on record or " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } } if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(), UserHandle.getCallingUserId())) { return null; } } final long identity = Binder.clearCallingIdentity(); try { return mPermissionManager.getWhitelistedRestrictedPermissions( pkg, whitelistFlags, userId); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean addWhitelistedRestrictedPermission(@NonNull String packageName, @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags, @UserIdInt int userId) { // Other argument checks are done in get/setWhitelistedRestrictedPermissions Preconditions.checkNotNull(permission); if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) { return false; } List permissions = getWhitelistedRestrictedPermissions(packageName, whitelistFlags, userId); if (permissions == null) { permissions = new ArrayList<>(1); } if (permissions.indexOf(permission) < 0) { permissions.add(permission); return setWhitelistedRestrictedPermissions(packageName, permissions, whitelistFlags, userId); } return false; } private boolean checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission( @NonNull String permission) { synchronized (mPackages) { final BasePermission bp = mPermissionManager.getPermissionTEMP(permission); if (bp == null) { Slog.w(TAG, "No such permissions: " + permission); return false; } if (bp.isHardOrSoftRestricted() && bp.isImmutablyRestricted() && mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Cannot modify whitelisting of an immutably " + "restricted permission: " + permission); } return true; } } @Override public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName, @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags, @UserIdInt int userId) { // Other argument checks are done in get/setWhitelistedRestrictedPermissions Preconditions.checkNotNull(permission); if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) { return false; } final List permissions = getWhitelistedRestrictedPermissions(packageName, whitelistFlags, userId); if (permissions != null && permissions.remove(permission)) { return setWhitelistedRestrictedPermissions(packageName, permissions, whitelistFlags, userId); } return false; } private boolean setWhitelistedRestrictedPermissions(@NonNull String packageName, @Nullable List permissions, @PermissionWhitelistFlags int whitelistFlag, @UserIdInt int userId) { Preconditions.checkNotNull(packageName); Preconditions.checkFlagsArgument(whitelistFlag, PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER); Preconditions.checkArgument(Integer.bitCount(whitelistFlag) == 1); Preconditions.checkArgumentNonNegative(userId, null); if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingOrSelfPermission( Manifest.permission.INTERACT_ACROSS_USERS, "setWhitelistedRestrictedPermissions for user " + userId); } final PackageParser.Package pkg; synchronized (mPackages) { final PackageSetting packageSetting = mSettings.mPackages.get(packageName); if (packageSetting == null) { Slog.w(TAG, "Unknown package: " + packageName); return false; } pkg = packageSetting.pkg; final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) == PackageManager.PERMISSION_GRANTED; final PackageSetting installerPackageSetting = mSettings.mPackages.get( packageSetting.installerPackageName); final boolean isCallerInstallerOnRecord = installerPackageSetting != null && UserHandle.isSameApp(installerPackageSetting.appId, Binder.getCallingUid()); if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 && !isCallerPrivileged) { throw new SecurityException("Modifying system whitelist requires " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) { if (!isCallerPrivileged && !isCallerInstallerOnRecord) { throw new SecurityException("Modifying upgrade whitelist requires" + " being installer on record or " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } final List whitelistedPermissions = getWhitelistedRestrictedPermissions( packageName, whitelistFlag, userId); if (permissions == null || permissions.isEmpty()) { if (whitelistedPermissions == null || whitelistedPermissions.isEmpty()) { return true; } } else { // Only the system can add and remove while the installer can only remove. final int permissionCount = permissions.size(); for (int i = 0; i < permissionCount; i++) { if ((whitelistedPermissions == null || !whitelistedPermissions.contains(permissions.get(i))) && !isCallerPrivileged) { throw new SecurityException("Adding to upgrade whitelist requires" + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } } } } if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) { if (!isCallerPrivileged && !isCallerInstallerOnRecord) { throw new SecurityException("Modifying installer whitelist requires" + " being installer on record or " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } } if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(), UserHandle.getCallingUserId())) { return false; } } final long identity = Binder.clearCallingIdentity(); try { mPermissionManager.setWhitelistedRestrictedPermissions(pkg, new int[]{userId}, permissions, Process.myUid(), whitelistFlag, mPermissionCallback); } finally { Binder.restoreCallingIdentity(identity); } return true; } @Override public boolean shouldShowRequestPermissionRationale(String permissionName, String packageName, int userId) { if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "canShowRequestPermissionRationale for user " + userId); } final int uid = getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId); if (UserHandle.getAppId(getCallingUid()) != UserHandle.getAppId(uid)) { return false; } if (checkPermission(permissionName, packageName, userId) == PackageManager.PERMISSION_GRANTED) { return false; } final int flags; final long identity = Binder.clearCallingIdentity(); try { flags = getPermissionFlags(permissionName, packageName, userId); } finally { Binder.restoreCallingIdentity(identity); } final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED | PackageManager.FLAG_PERMISSION_POLICY_FIXED | PackageManager.FLAG_PERMISSION_USER_FIXED; if ((flags & fixedFlags) != 0) { return false; } return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; } @Override public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { mContext.enforceCallingOrSelfPermission( Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS, "addOnPermissionsChangeListener"); synchronized (mPackages) { mOnPermissionChangeListeners.addListenerLocked(listener); } } @Override public void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { throw new SecurityException("Instant applications don't have access to this method"); } synchronized (mPackages) { mOnPermissionChangeListeners.removeListenerLocked(listener); } } @Override public boolean isProtectedBroadcast(String actionName) { // allow instant applications synchronized (mProtectedBroadcasts) { if (mProtectedBroadcasts.contains(actionName)) { return true; } else if (actionName != null) { // TODO: remove these terrible hacks if (actionName.startsWith("android.net.netmon.lingerExpired") || actionName.startsWith("com.android.server.sip.SipWakeupTimer") || actionName.startsWith("com.android.internal.telephony.data-reconnect") || actionName.startsWith("android.net.netmon.launchCaptivePortalApp")) { return true; } } } return false; } @Override public int checkSignatures(String pkg1, String pkg2) { synchronized (mPackages) { final PackageParser.Package p1 = mPackages.get(pkg1); final PackageParser.Package p2 = mPackages.get(pkg2); if (p1 == null || p1.mExtras == null || p2 == null || p2.mExtras == null) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps1 = (PackageSetting) p1.mExtras; final PackageSetting ps2 = (PackageSetting) p2.mExtras; if (filterAppAccessLPr(ps1, callingUid, callingUserId) || filterAppAccessLPr(ps2, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } return compareSignatures(p1.mSigningDetails.signatures, p2.mSigningDetails.signatures); } } @Override public int checkUidSignatures(int uid1, int uid2) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; // Map to base uids. final int appId1 = UserHandle.getAppId(uid1); final int appId2 = UserHandle.getAppId(uid2); // reader synchronized (mPackages) { Signature[] s1; Signature[] s2; Object obj = mSettings.getSettingLPr(appId1); if (obj != null) { if (obj instanceof SharedUserSetting) { if (isCallerInstantApp) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s1 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s1 = ps.signatures.mSigningDetails.signatures; } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } obj = mSettings.getSettingLPr(appId2); if (obj != null) { if (obj instanceof SharedUserSetting) { if (isCallerInstantApp) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s2 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s2 = ps.signatures.mSigningDetails.signatures; } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } return compareSignatures(s1, s2); } } @Override public boolean hasSigningCertificate( String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) { synchronized (mPackages) { final PackageParser.Package p = mPackages.get(packageName); if (p == null || p.mExtras == null) { return false; } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return false; } switch (type) { case CERT_INPUT_RAW_X509: return p.mSigningDetails.hasCertificate(certificate); case CERT_INPUT_SHA256: return p.mSigningDetails.hasSha256Certificate(certificate); default: return false; } } } @Override public boolean hasUidSigningCertificate( int uid, byte[] certificate, @PackageManager.CertificateInputType int type) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); // Map to base uids. final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final PackageParser.SigningDetails signingDetails; final Object obj = mSettings.getSettingLPr(appId); if (obj != null) { if (obj instanceof SharedUserSetting) { final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; if (isCallerInstantApp) { return false; } signingDetails = ((SharedUserSetting)obj).signatures.mSigningDetails; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return false; } signingDetails = ps.signatures.mSigningDetails; } else { return false; } } else { return false; } switch (type) { case CERT_INPUT_RAW_X509: return signingDetails.hasCertificate(certificate); case CERT_INPUT_SHA256: return signingDetails.hasSha256Certificate(certificate); default: return false; } } } /** * This method should typically only be used when granting or revoking * permissions, since the app may immediately restart after this call. * * If you're doing surgery on app code/data, use {@link PackageFreezer} to * guard your work against the app being relaunched. */ private void killUid(int appId, int userId, String reason) { final long identity = Binder.clearCallingIdentity(); try { IActivityManager am = ActivityManager.getService(); if (am != null) { try { am.killUid(appId, userId, reason); } catch (RemoteException e) { /* ignore - same process */ } } } finally { Binder.restoreCallingIdentity(identity); } } /** * If the database version for this type of package (internal storage or * external storage) is less than the version where package signatures * were updated, return true. */ private boolean isCompatSignatureUpdateNeeded(PackageParser.Package scannedPkg) { return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg)); } private static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) { return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY; } private boolean isRecoverSignatureUpdateNeeded(PackageParser.Package scannedPkg) { return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg)); } private static boolean isRecoverSignatureUpdateNeeded(VersionInfo ver) { return ver.databaseVersion < DatabaseVersion.SIGNATURE_MALFORMED_RECOVER; } @Override public List getAllPackages() { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); synchronized (mPackages) { if (canViewInstantApps(callingUid, callingUserId)) { return new ArrayList<>(mPackages.keySet()); } final String instantAppPkgName = getInstantAppPackageName(callingUid); final List result = new ArrayList<>(); if (instantAppPkgName != null) { // caller is an instant application; filter unexposed applications for (PackageParser.Package pkg : mPackages.values()) { if (!pkg.visibleToInstantApps) { continue; } result.add(pkg.packageName); } } else { // caller is a normal application; filter instant applications for (PackageParser.Package pkg : mPackages.values()) { final PackageSetting ps = pkg.mExtras != null ? (PackageSetting) pkg.mExtras : null; if (ps != null && ps.getInstantApp(callingUserId) && !mInstantAppRegistry.isInstantAccessGranted( callingUserId, UserHandle.getAppId(callingUid), ps.appId)) { continue; } result.add(pkg.packageName); } } return result; } } /** * IMPORTANT: Not all packages returned by this method may be known * to the system. There are two conditions in which this may occur: * * The package is on adoptable storage and the device has been removed * The package is being removed and the internal structures are partially updated * * The second is an artifact of the current data structures and should be fixed. See * b/111075456 for one such instance. */ @Override public String[] getPackagesForUid(int uid) { final int callingUid = Binder.getCallingUid(); final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; final int userId = UserHandle.getUserId(uid); final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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 appId = UserHandle.getAppId(uids[i]); final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; final Iterator it = sus.packages.iterator(); while (it.hasNext()) { if (it.next().isPrivileged()) { return true; } } } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; return ps.isPrivileged(); } } return false; } @Override public String[] getAppOpPermissionPackages(String permName) { return mPermissionManager.getAppOpPermissionPackages(permName); } @Override public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { return resolveIntentInternal(intent, resolvedType, flags, userId, false, Binder.getCallingUid()); } /** * Normally instant apps can only be resolved when they're visible to the caller. * However, if {@code resolveForStart} is {@code true}, all instant apps are visible * since we need to allow the system to start any installed application. */ private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType, int flags, int userId, boolean resolveForStart, int filterCallingUid) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent"); if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "resolve intent"); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, filterCallingUid, userId, resolveForStart, true /*allowDynamicSplits*/); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); final ResolveInfo bestChoice = chooseBestActivity(intent, resolvedType, flags, query, userId); return bestChoice; } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @Override public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) { if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) { throw new SecurityException( "findPersistentPreferredActivity can only be run by the system"); } if (!sUserManager.exists(userId)) { return null; } final int callingUid = Binder.getCallingUid(); intent = updateIntentForResolve(intent); final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver()); final int flags = updateFlagsForResolve( 0, userId, intent, callingUid, false /*includeInstantApps*/); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); synchronized (mPackages) { return findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false, userId); } } @Override public void setLastChosenActivity(Intent intent, String resolvedType, int flags, IntentFilter filter, int match, ComponentName activity) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; } final int userId = UserHandle.getCallingUserId(); if (DEBUG_PREFERRED) { Log.v(TAG, "setLastChosenActivity intent=" + intent + " resolvedType=" + resolvedType + " flags=" + flags + " filter=" + filter + " match=" + match + " activity=" + activity); filter.dump(new PrintStreamPrinter(System.out), " "); } intent.setComponent(null); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); // Find any earlier preferred or last chosen entries and nuke them findPreferredActivityNotLocked( 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 findPreferredActivityNotLocked( intent, resolvedType, flags, query, 0, false, false, false, userId); } /** * Returns whether or not instant apps have been disabled remotely. */ private boolean areWebInstantAppsDisabled(int userId) { return mWebInstantAppsDisabled.get(userId); } private boolean isInstantAppResolutionAllowed( Intent intent, List resolvedActivities, int userId, boolean skipPackageCheck) { if (mInstantAppResolverConnection == null) { return false; } if (mInstantAppInstallerActivity == null) { return false; } if (intent.getComponent() != null) { return false; } if ((intent.getFlags() & Intent.FLAG_IGNORE_EPHEMERAL) != 0) { return false; } if (!skipPackageCheck && intent.getPackage() != null) { return false; } if (!intent.isWebIntent()) { // for non web intents, we should not resolve externally if an app already exists to // handle it or if the caller didn't explicitly request it. if ((resolvedActivities != null && resolvedActivities.size() != 0) || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) { return false; } } else { if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) { return false; } else if (areWebInstantAppsDisabled(userId)) { return false; } } // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. // Or if there's already an ephemeral app installed that handles the action synchronized (mPackages) { final int count = (resolvedActivities == null ? 0 : resolvedActivities.size()); for (int n = 0; n < count; n++) { final ResolveInfo info = resolvedActivities.get(n); final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { // only check domain verification status if the app is not a browser if (!info.handleAllWebDataURI) { // Try to get the status from User settings first final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int) (packedStatus >> 32); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName + ", status: " + status); } return false; } } if (ps.getInstantApp(userId)) { if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app installed;" + " pkg: " + packageName); } return false; } } } } // We've exhausted all ways to deny ephemeral application; let the system look for them. return true; } private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj, Intent origIntent, String resolvedType, String callingPackage, Bundle verificationBundle, int userId) { final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO, new InstantAppRequest(responseObj, origIntent, resolvedType, callingPackage, userId, verificationBundle, false /*resolveForStart*/)); mHandler.sendMessage(msg); } private ResolveInfo chooseBestActivity(Intent intent, String resolvedType, int flags, List query, int userId) { if (query != null) { final int N = query.size(); if (N == 1) { return query.get(0); } else if (N > 1) { final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); // If there is more than one activity with the same priority, // then let the user decide between them. ResolveInfo r0 = query.get(0); ResolveInfo r1 = query.get(1); if (DEBUG_INTENT_MATCHING || debug) { Slog.v(TAG, r0.activityInfo.name + "=" + r0.priority + " vs " + r1.activityInfo.name + "=" + r1.priority); } // If the first activity has a higher priority, or a different // default, then it is always desirable to pick it. if (r0.priority != r1.priority || r0.preferredOrder != r1.preferredOrder || r0.isDefault != r1.isDefault) { return query.get(0); } // If we have saved a preference for a preferred activity for // this Intent, use that. ResolveInfo ri = findPreferredActivityNotLocked(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; } @GuardedBy("mPackages") 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; jmust not hold {@link #mPackages} */ ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags, List query, int priority, boolean always, boolean removeMatches, boolean debug, int userId) { if (Thread.holdsLock(mPackages)) { Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding mPackages", new Throwable()); } if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); // Do NOT hold the packages lock; this calls up into the settings provider which // could cause a deadlock. final boolean isDeviceProvisioned = android.provider.Settings.Global.getInt(mContext.getContentResolver(), android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1; 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"); } } final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent) && !isDeviceProvisioned; if (ai == null) { // Do not remove launcher's preferred activity during SetupWizard // due to it may not install yet if (excludeSetupWizardHomeActivity) { continue; } // This previously registered preferred activity // component is no longer known. Most likely an update // to the app was installed and in the new version this // component no longer exists. Clean it up by removing // it from the preferred activities list, and skip it. Slog.w(TAG, "Removing dangling preferred activity: " + pa.mPref.mComponent); pir.removeFilter(pa); changed = true; continue; } for (int j=0; j matches = getMatchingCrossProfileIntentFilters(intent, resolvedType, sourceUserId); if (matches != null) { int size = matches.size(); for (int i = 0; i < size; i++) { if (matches.get(i).getTargetUserId() == targetUserId) return true; } } if (intent.hasWebURI()) { // cross-profile app linking works only towards the parent. final int callingUid = Binder.getCallingUid(); final UserInfo parent = getProfileParent(sourceUserId); synchronized(mPackages) { int flags = updateFlagsForResolve(0, parent.id, intent, callingUid, false /*includeInstantApps*/); CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr( intent, resolvedType, flags, sourceUserId, parent.id); return xpDomainInfo != null; } } return false; } private UserInfo getProfileParent(int userId) { final long identity = Binder.clearCallingIdentity(); try { return sUserManager.getProfileParent(userId); } finally { Binder.restoreCallingIdentity(identity); } } private List getMatchingCrossProfileIntentFilters(Intent intent, String resolvedType, int userId) { CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId); if (resolver != null) { return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId); } return null; } @Override public @NonNull ParceledListSlice queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); return new ParceledListSlice<>( queryIntentActivitiesInternal(intent, resolvedType, flags, userId)); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } /** * Returns the package name of the calling Uid if it's an instant app. If it isn't * instant, returns {@code null}. */ private String getInstantAppPackageName(int callingUid) { synchronized (mPackages) { // If the caller is an isolated app use the owner's uid for the lookup. if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final int appId = UserHandle.getAppId(callingUid); final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid)); return isInstantApp ? ps.pkg.packageName : null; } } return null; } private @NonNull List queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int userId) { return queryIntentActivitiesInternal( intent, resolvedType, flags, Binder.getCallingUid(), userId, false /*resolveForStart*/, true /*allowDynamicSplits*/); } private @NonNull List queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final String instantAppPkgName = getInstantAppPackageName(filterCallingUid); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "query intent activities"); final String pkgName = intent.getPackage(); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart, comp != null || pkgName != null /*onlyExposedExplicitly*/); if (comp != null) { final List list = new ArrayList<>(1); final ActivityInfo ai = getActivityInfo(comp, flags, userId); if (ai != null) { // When specifying an explicit component, we prevent the activity from being // used when either 1) the calling package is normal and the activity is within // an ephemeral application or 2) the calling package is ephemeral and the // activity is not visible to ephemeral applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean matchExplicitlyVisibleOnly = (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (ai.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetVisibleToInstantApp = (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean isTargetExplicitlyVisibleToInstantApp = isTargetVisibleToInstantApp && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; final boolean isTargetHiddenFromInstantApp = !isTargetVisibleToInstantApp || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp); final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } } return applyPostResolutionFilter( list, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // reader boolean sortResult = false; boolean addInstant = false; List result; synchronized (mPackages) { if (pkgName == null) { List matchingFilters = getMatchingCrossProfileIntentFilters(intent, resolvedType, userId); // Check for results that need to skip the current profile. ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent, resolvedType, flags, userId); if (xpResolveInfo != null) { List xpResult = new ArrayList<>(1); xpResult.add(xpResolveInfo); return applyPostResolutionFilter( filterIfNotSystemUser(xpResult, userId), instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // Check for results in the current profile. result = filterIfNotSystemUser(mComponentResolver.queryActivities( intent, resolvedType, flags, userId), userId); addInstant = isInstantAppResolutionAllowed(intent, result, userId, false /*skipPackageCheck*/); // Check for cross profile results. boolean hasNonNegativePriorityResult = hasNonNegativePriority(result); xpResolveInfo = queryCrossProfileIntents( matchingFilters, intent, resolvedType, flags, userId, hasNonNegativePriorityResult); if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) { boolean isVisibleToUser = filterIfNotSystemUser( Collections.singletonList(xpResolveInfo), userId).size() > 0; if (isVisibleToUser) { result.add(xpResolveInfo); sortResult = true; } } if (intent.hasWebURI()) { CrossProfileDomainInfo xpDomainInfo = null; final UserInfo parent = getProfileParent(userId); if (parent != null) { xpDomainInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType, flags, userId, parent.id); } if (xpDomainInfo != null) { if (xpResolveInfo != null) { // If we didn't remove it, the cross-profile ResolveInfo would be twice // in the result. result.remove(xpResolveInfo); } if (result.size() == 0 && !addInstant) { // No result in current profile, but found candidate in parent user. // And we are not going to add emphemeral app, so we can return the // result straight away. result.add(xpDomainInfo.resolveInfo); return applyPostResolutionFilter(result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } } else if (result.size() <= 1 && !addInstant) { // No result in parent user and <= 1 result in current profile, and we // are not going to add emphemeral app, so we can return the result without // further processing. return applyPostResolutionFilter(result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // We have more than one candidate (combining results from current and parent // profile), so we need filtering and sorting. result = filterCandidatesWithDomainPreferredActivitiesLPr( intent, flags, result, xpDomainInfo, userId); sortResult = true; } } else { final PackageParser.Package pkg = mPackages.get(pkgName); result = null; if (pkg != null) { result = filterIfNotSystemUser(mComponentResolver.queryActivities( intent, resolvedType, flags, pkg.activities, userId), userId); } if (result == null || result.size() == 0) { // the caller wants to resolve for a particular package; however, there // were no installed results, so, try to find an ephemeral result addInstant = isInstantAppResolutionAllowed( intent, null /*result*/, userId, true /*skipPackageCheck*/); if (result == null) { result = new ArrayList<>(); } } } } if (addInstant) { result = maybeAddInstantAppInstaller( result, intent, resolvedType, flags, userId, resolveForStart); } if (sortResult) { Collections.sort(result, RESOLVE_PRIORITY_SORTER); } return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } private List maybeAddInstantAppInstaller(List result, Intent intent, String resolvedType, int flags, int userId, boolean resolveForStart) { // first, check to see if we've got an instant app already installed final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0; ResolveInfo localInstantApp = null; boolean blockResolution = false; if (!alreadyResolvedLocally) { final List instantApps = mComponentResolver.queryActivities( intent, resolvedType, flags | PackageManager.GET_RESOLVED_FILTER | PackageManager.MATCH_INSTANT | PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY, userId); for (int i = instantApps.size() - 1; i >= 0; --i) { final ResolveInfo info = instantApps.get(i); final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps.getInstantApp(userId)) { final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int)(packedStatus >> 32); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { // there's a local instant application installed, but, the user has // chosen to never use it; skip resolution and don't acknowledge // an instant application is even available if (DEBUG_INSTANT) { Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName); } blockResolution = true; break; } else { // we have a locally installed instant application; skip resolution // but acknowledge there's an instant application available if (DEBUG_INSTANT) { Slog.v(TAG, "Found installed instant app; pkg: " + packageName); } localInstantApp = info; break; } } } } // no app installed, let's see if one's available AuxiliaryResolveInfo auxiliaryResponse = null; if (!blockResolution) { if (localInstantApp == null) { // we don't have an instant app locally, resolve externally Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral"); final InstantAppRequest requestObject = new InstantAppRequest( null /*responseObj*/, intent /*origIntent*/, resolvedType, null /*callingPackage*/, userId, null /*verificationBundle*/, resolveForStart); auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne( mInstantAppResolverConnection, requestObject); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } else { // we have an instant application locally, but, we can't admit that since // callers shouldn't be able to determine prior browsing. create a dummy // auxiliary response so the downstream code behaves as if there's an // instant application available externally. when it comes time to start // the instant application, we'll do the right thing. final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo; auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */, ai.packageName, ai.longVersionCode, null /* splitName */); } } if (intent.isWebIntent() && auxiliaryResponse == null) { return result; } final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName); if (ps == null || !ps.readUserState(userId).isEnabled(mInstantAppInstallerActivity, 0)) { return result; } final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo); ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo( mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId); ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART | IntentFilter.MATCH_ADJUSTMENT_NORMAL; // add a non-generic filter ephemeralInstaller.filter = new IntentFilter(); if (intent.getAction() != null) { ephemeralInstaller.filter.addAction(intent.getAction()); } if (intent.getData() != null && intent.getData().getPath() != null) { ephemeralInstaller.filter.addDataPath( intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL); } ephemeralInstaller.isInstantAppAvailable = true; // make sure this resolver is the default ephemeralInstaller.isDefault = true; ephemeralInstaller.auxiliaryInfo = auxiliaryResponse; if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } result.add(ephemeralInstaller); return result; } private static class CrossProfileDomainInfo { /* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */ ResolveInfo resolveInfo; /* Best domain verification status of the activities found in the other profile */ int bestDomainVerificationStatus; } private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType, int flags, int sourceUserId, int parentUserId) { if (!sUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, sourceUserId)) { return null; } List resultTargetUser = mComponentResolver.queryActivities(intent, resolvedType, flags, parentUserId); if (resultTargetUser == null || resultTargetUser.isEmpty()) { return null; } CrossProfileDomainInfo result = null; int size = resultTargetUser.size(); for (int i = 0; i < size; i++) { ResolveInfo riTargetUser = resultTargetUser.get(i); // Intent filter verification is only for filters that specify a host. So don't return // those that handle all web uris. if (riTargetUser.handleAllWebDataURI) { continue; } String packageName = riTargetUser.activityInfo.packageName; PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) { continue; } long verificationState = getDomainVerificationStatusLPr(ps, parentUserId); int status = (int)(verificationState >> 32); if (result == null) { result = new CrossProfileDomainInfo(); result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(), sourceUserId, parentUserId); result.bestDomainVerificationStatus = status; } else { result.bestDomainVerificationStatus = bestDomainVerificationStatus(status, result.bestDomainVerificationStatus); } } // Don't consider matches with status NEVER across profiles. if (result != null && result.bestDomainVerificationStatus == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return null; } return result; } /** * Verification statuses are ordered from the worse to the best, except for * INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse. */ private int bestDomainVerificationStatus(int status1, int status2) { if (status1 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return status2; } if (status2 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return status1; } return (int) MathUtils.max(status1, status2); } private boolean isUserEnabled(int userId) { long callingId = Binder.clearCallingIdentity(); try { UserInfo userInfo = sUserManager.getUserInfo(userId); return userInfo != null && userInfo.isEnabled(); } finally { Binder.restoreCallingIdentity(callingId); } } /** * Filter out activities with systemUserOnly flag set, when current user is not System. * * @return filtered list */ private List filterIfNotSystemUser(List resolveInfos, int userId) { if (userId == UserHandle.USER_SYSTEM) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { ResolveInfo info = resolveInfos.get(i); if ((info.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) { resolveInfos.remove(i); } } return resolveInfos; } /** * Filters out ephemeral activities. * When resolving for an ephemeral app, only activities that 1) are defined in the * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned. * * @param resolveInfos The pre-filtered list of resolved activities * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering * is performed. * @param intent * @return A filtered list of resolved activities. */ private List applyPostResolutionFilter(List resolveInfos, String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, boolean resolveForStart, int userId, Intent intent) { final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId); for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); // remove locally resolved instant app web results when disabled if (info.isInstantAppAvailable && blockInstant) { resolveInfos.remove(i); continue; } // allow activities that are defined in the provided package if (allowDynamicSplits && info.activityInfo != null && info.activityInfo.splitName != null && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames, info.activityInfo.splitName)) { if (mInstantAppInstallerActivity == null) { if (DEBUG_INSTALL) { Slog.v(TAG, "No installer - not adding it to the ResolveInfo list"); } resolveInfos.remove(i); continue; } if (blockInstant && isInstantApp(info.activityInfo.packageName, userId)) { resolveInfos.remove(i); continue; } // requested activity is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTALL) { Slog.v(TAG, "Adding installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); final ComponentName installFailureActivity = findInstallFailureActivity( info.activityInfo.packageName, filterCallingUid, userId); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( installFailureActivity, info.activityInfo.packageName, info.activityInfo.applicationInfo.longVersionCode, info.activityInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // This resolve info may appear in the chooser UI, so let us make it // look as the one it replaces as far as the user is concerned which // requires loading the correct label and icon for the resolve info. installerInfo.resolvePackageName = info.getComponentInfo().packageName; installerInfo.labelRes = info.resolveLabelResId(); installerInfo.icon = info.resolveIconResId(); installerInfo.isInstantAppAvailable = true; resolveInfos.set(i, installerInfo); continue; } // caller is a full app, don't need to apply any other filtering if (ephemeralPkgName == null) { continue; } else if (ephemeralPkgName.equals(info.activityInfo.packageName)) { // caller is same app; don't need to apply any other filtering continue; } else if (resolveForStart && (intent.isWebIntent() || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) && intent.getPackage() == null && intent.getComponent() == null) { // ephemeral apps can launch other ephemeral apps indirectly continue; } // allow activities that have been explicitly exposed to ephemeral apps final boolean isEphemeralApp = info.activityInfo.applicationInfo.isInstantApp(); if (!isEphemeralApp && ((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } /** * Returns the activity component that can handle install failures. * By default, the instant application installer handles failures. However, an * application may want to handle failures on its own. Applications do this by * creating an activity with an intent filter that handles the action * {@link Intent#ACTION_INSTALL_FAILURE}. */ private @Nullable ComponentName findInstallFailureActivity( String packageName, int filterCallingUid, int userId) { final Intent failureActivityIntent = new Intent(Intent.ACTION_INSTALL_FAILURE); failureActivityIntent.setPackage(packageName); // IMPORTANT: disallow dynamic splits to avoid an infinite loop final List result = queryIntentActivitiesInternal( failureActivityIntent, null /*resolvedType*/, 0 /*flags*/, filterCallingUid, userId, false /*resolveForStart*/, false /*allowDynamicSplits*/); final int NR = result.size(); if (NR > 0) { for (int i = 0; i < NR; i++) { final ResolveInfo info = result.get(i); if (info.activityInfo.splitName != null) { continue; } return new ComponentName(packageName, info.activityInfo.name); } } return null; } /** * @param resolveInfos list of resolve infos in descending priority order * @return if the list contains a resolve info with non-negative priority */ private boolean hasNonNegativePriority(List resolveInfos) { return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0; } private List filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent, int matchFlags, List candidates, CrossProfileDomainInfo xpDomainInfo, int userId) { final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0; if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) { Slog.v(TAG, "Filtering results with preferred activities. Candidates count: " + candidates.size()); } final ArrayList result = new ArrayList<>(); final ArrayList alwaysList = new ArrayList<>(); final ArrayList undefinedList = new ArrayList<>(); final ArrayList alwaysAskList = new ArrayList<>(); final ArrayList neverList = new ArrayList<>(); final 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 = mComponentResolver.queryActivities(intent, resolvedType, flags, targetUserId); if (resultTargetUser != null && isUserEnabled(targetUserId)) { // If all the matches in the target profile are suspended, return null. for (int i = resultTargetUser.size() - 1; i >= 0; i--) { if ((resultTargetUser.get(i).activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SUSPENDED) == 0) { return createForwardingResolveInfoUnchecked(filter, sourceUserId, targetUserId); } } } return null; } private ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter, int sourceUserId, int targetUserId) { ResolveInfo forwardingResolveInfo = new ResolveInfo(); long ident = Binder.clearCallingIdentity(); boolean targetIsProfile; try { targetIsProfile = sUserManager.getUserInfo(targetUserId).isManagedProfile(); } finally { Binder.restoreCallingIdentity(ident); } String className; if (targetIsProfile) { className = FORWARD_INTENT_TO_MANAGED_PROFILE; } else { className = FORWARD_INTENT_TO_PARENT; } ComponentName forwardingActivityComponentName = new ComponentName( mAndroidApplication.packageName, className); ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0, sourceUserId); if (!targetIsProfile) { forwardingActivityInfo.showUserIcon = targetUserId; forwardingResolveInfo.noResourceId = true; } forwardingResolveInfo.activityInfo = forwardingActivityInfo; forwardingResolveInfo.priority = 0; forwardingResolveInfo.preferredOrder = 0; forwardingResolveInfo.match = 0; forwardingResolveInfo.isDefault = true; forwardingResolveInfo.filter = filter; forwardingResolveInfo.targetUserId = targetUserId; return forwardingResolveInfo; } @Override public @NonNull ParceledListSlice queryIntentActivityOptions(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>(queryIntentActivityOptionsInternal(caller, specifics, specificTypes, intent, resolvedType, flags, userId)); } private @NonNull List queryIntentActivityOptionsInternal(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent activity options"); final String resultsAction = intent.getAction(); final List results = queryIntentActivitiesInternal(intent, resolvedType, flags | PackageManager.GET_RESOLVED_FILTER, userId); if (DEBUG_INTENT_MATCHING) { Log.v(TAG, "Query " + intent + ": " + results); } int specificsPos = 0; int N; // todo: note that the algorithm used here is O(N^2). This // isn't a problem in our current environment, but if we start running // into situations where we have more than 5 or 10 matches then this // should probably be changed to something smarter... // First we go through and resolve each of the specific items // that were supplied, taking care of removing any corresponding // duplicate items in the generic resolve list. if (specifics != null) { for (int i=0; i it = rii.filter.actionsIterator(); if (it == null) { continue; } while (it.hasNext()) { final String action = it.next(); if (resultsAction != null && resultsAction.equals(action)) { // If this action was explicitly requested, then don't // remove things that have it. continue; } for (int j=i+1; j queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>( queryIntentReceiversInternal(intent, resolvedType, flags, userId, false /*allowDynamicSplits*/)); } private @NonNull List queryIntentReceiversInternal(Intent intent, String resolvedType, int flags, int userId, boolean allowDynamicSplits) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ActivityInfo ai = getReceiverInfo(comp, flags, userId); if (ai != null) { // When specifying an explicit component, we prevent the activity from being // used when either 1) the calling package is normal and the activity is within // an instant application or 2) the calling package is ephemeral and the // activity is not visible to instant applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean matchExplicitlyVisibleOnly = (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (ai.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetVisibleToInstantApp = (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean isTargetExplicitlyVisibleToInstantApp = isTargetVisibleToInstantApp && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; final boolean isTargetHiddenFromInstantApp = !isTargetVisibleToInstantApp || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp); final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } } return applyPostResolutionFilter( list, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { final List result = mComponentResolver.queryReceivers(intent, resolvedType, flags, userId); return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { final List result = mComponentResolver.queryReceivers( intent, resolvedType, flags, pkg.receivers, userId); return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } return Collections.emptyList(); } } @Override public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) { final int callingUid = Binder.getCallingUid(); return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid); } private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForResolve( flags, userId, intent, callingUid, false /*includeInstantApps*/); List query = queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/); if (query != null) { if (query.size() >= 1) { // If there is more than one service with the same priority, // just arbitrarily pick the first one. return query.get(0); } } return null; } @Override public @NonNull ParceledListSlice queryIntentServices(Intent intent, String resolvedType, int flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>(queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/)); } private @NonNull List queryIntentServicesInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps) { if (!sUserManager.exists(userId)) return Collections.emptyList(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ServiceInfo si = getServiceInfo(comp, flags, userId); if (si != null) { // When specifying an explicit component, we prevent the service from being // used when either 1) the service is in an instant application and the // caller is not the same instant application or 2) the calling package is // ephemeral and the activity is not visible to ephemeral applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (si.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetHiddenFromInstantApp = (si.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0; final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.serviceInfo = si; list.add(ri); } } return list; } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostServiceResolutionFilter( mComponentResolver.queryServices(intent, resolvedType, flags, userId), instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return applyPostServiceResolutionFilter( mComponentResolver.queryServices(intent, resolvedType, flags, pkg.services, userId), instantAppPkgName); } return Collections.emptyList(); } } private List applyPostServiceResolutionFilter(List resolveInfos, String instantAppPkgName) { if (instantAppPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp(); // allow services that are defined in the provided package if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) { if (info.serviceInfo.splitName != null && !ArrayUtils.contains(info.serviceInfo.applicationInfo.splitNames, info.serviceInfo.splitName)) { // requested service is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( null /* installFailureActivity */, info.serviceInfo.packageName, info.serviceInfo.applicationInfo.longVersionCode, info.serviceInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // load resources from the correct package installerInfo.resolvePackageName = info.getComponentInfo().packageName; resolveInfos.set(i, installerInfo); } continue; } // allow services that have been explicitly exposed to ephemeral apps if (!isEphemeralApp && ((info.serviceInfo.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } @Override public @NonNull ParceledListSlice queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>( queryIntentContentProvidersInternal(intent, resolvedType, flags, userId)); } private @NonNull List queryIntentContentProvidersInternal( Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ProviderInfo pi = getProviderInfo(comp, flags, userId); if (pi != null) { // When specifying an explicit component, we prevent the provider from being // used when either 1) the provider is in an instant application and the // caller is not the same instant application or 2) the calling package is an // instant application and the provider is not visible to instant applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (pi.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetHiddenFromInstantApp = (pi.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0; final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.providerInfo = pi; list.add(ri); } } return list; } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostContentProviderResolutionFilter( mComponentResolver.queryProviders(intent, resolvedType, flags, userId), instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return applyPostContentProviderResolutionFilter( mComponentResolver.queryProviders(intent, resolvedType, flags, pkg.providers, userId), instantAppPkgName); } return Collections.emptyList(); } } private List applyPostContentProviderResolutionFilter( List resolveInfos, String instantAppPkgName) { if (instantAppPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.providerInfo.applicationInfo.isInstantApp(); // allow providers that are defined in the provided package if (isEphemeralApp && instantAppPkgName.equals(info.providerInfo.packageName)) { if (info.providerInfo.splitName != null && !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames, info.providerInfo.splitName)) { // requested provider is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( null /*failureActivity*/, info.providerInfo.packageName, info.providerInfo.applicationInfo.longVersionCode, info.providerInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // load resources from the correct package installerInfo.resolvePackageName = info.getComponentInfo().packageName; resolveInfos.set(i, installerInfo); } continue; } // allow providers that have been explicitly exposed to instant applications if (!isEphemeralApp && ((info.providerInfo.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } @Override public ParceledListSlice getInstalledPackages(int flags, int userId) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return ParceledListSlice.emptyList(); } if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; final boolean listApex = (flags & MATCH_APEX) != 0; final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0; mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed packages"); // writer synchronized (mPackages) { ArrayList list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo(ps, flags, userId); if (pi != null) { list.add(pi); } } } else { list = new ArrayList<>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { final PackageSetting ps = (PackageSetting) p.mExtras; if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo((PackageSetting) p.mExtras, flags, userId); if (pi != null) { list.add(pi); } } } if (listApex) { if (listFactory) { list.addAll(mApexManager.getFactoryPackages()); } else { list.addAll(mApexManager.getActivePackages()); } if (listUninstalled) { list.addAll(mApexManager.getInactivePackages()); } } return new ParceledListSlice<>(list); } } private void addPackageHoldingPermissions(ArrayList list, PackageSetting ps, String[] permissions, boolean[] tmp, int flags, int userId) { int numMatch = 0; final PermissionsState permissionsState = ps.getPermissionsState(); for (int i=0; i getPackagesHoldingPermissions( String[] permissions, int flags, int userId) { if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, permissions); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "get packages holding permissions"); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; // writer synchronized (mPackages) { ArrayList list = new ArrayList<>(); boolean[] tmpBools = new boolean[permissions.length]; if (listUninstalled) { for (PackageSetting ps : mSettings.mPackages.values()) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } } else { for (PackageParser.Package pkg : mPackages.values()) { PackageSetting ps = (PackageSetting)pkg.mExtras; if (ps != null) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } } } return new ParceledListSlice<>(list); } } @Override public ParceledListSlice getInstalledApplications(int flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>( getInstalledApplicationsListInternal(flags, userId, callingUid)); } private List getInstalledApplicationsListInternal(int flags, int userId, int callingUid) { if (getInstantAppPackageName(callingUid) != null) { return Collections.emptyList(); } if (!sUserManager.exists(userId)) return Collections.emptyList(); flags = updateFlagsForApplication(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; mPermissionManager.enforceCrossUserPermission( callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed application info"); // writer synchronized (mPackages) { ArrayList list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { ApplicationInfo ai; int effectiveFlags = flags; if (ps.isSystem()) { effectiveFlags |= PackageManager.MATCH_ANY_USER; } if (ps.pkg != null) { if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(ps.pkg); } } else { // Shared lib filtering done in generateApplicationInfoFromSettingsLPw // and already converts to externally visible package name ai = generateApplicationInfoFromSettingsLPw(ps.name, callingUid, effectiveFlags, userId); } if (ai != null) { list.add(ai); } } } else { list = new ArrayList<>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { if (p.mExtras != null) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(p); list.add(ai); } } } } return list; } } @Override public ParceledListSlice getInstantApps(int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } if (!canViewInstantApps(Binder.getCallingUid(), userId)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getEphemeralApplications"); } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getEphemeralApplications"); synchronized (mPackages) { List instantApps = mInstantAppRegistry .getInstantAppsLPr(userId); if (instantApps != null) { return new ParceledListSlice<>(instantApps); } } return null; } @Override public boolean isInstantApp(String packageName, int userId) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "isInstantApp"); if (HIDE_EPHEMERAL_APIS) { return false; } synchronized (mPackages) { int callingUid = Binder.getCallingUid(); if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final PackageSetting ps = mSettings.mPackages.get(packageName); PackageParser.Package pkg = mPackages.get(packageName); final boolean returnAllowed = ps != null && (isCallerSameApp(packageName, callingUid) || canViewInstantApps(callingUid, userId) || mInstantAppRegistry.isInstantAccessGranted( userId, UserHandle.getAppId(callingUid), ps.appId)); if (returnAllowed) { return ps.getInstantApp(userId); } } return false; } @Override public byte[] getInstantAppCookie(String packageName, int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return null; } synchronized (mPackages) { return mInstantAppRegistry.getInstantAppCookieLPw( packageName, userId); } } @Override public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) { if (HIDE_EPHEMERAL_APIS) { return true; } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, true /* checkShell */, "setInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return false; } synchronized (mPackages) { return mInstantAppRegistry.setInstantAppCookieLPw( packageName, cookie, userId); } } @Override public Bitmap getInstantAppIcon(String packageName, int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } if (!canViewInstantApps(Binder.getCallingUid(), userId)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getInstantAppIcon"); } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppIcon"); synchronized (mPackages) { return mInstantAppRegistry.getInstantAppIconLPw( packageName, userId); } } private boolean isCallerSameApp(String packageName, int uid) { PackageParser.Package pkg = mPackages.get(packageName); return pkg != null && UserHandle.getAppId(uid) == pkg.applicationInfo.uid; } @Override public @NonNull ParceledListSlice getPersistentApplications(int flags) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(getPersistentApplicationsInternal(flags)); } private @NonNull List getPersistentApplicationsInternal(int flags) { final ArrayList finalList = new ArrayList<>(); // reader synchronized (mPackages) { final Iterator i = mPackages.values().iterator(); final int userId = UserHandle.getCallingUserId(); while (i.hasNext()) { final PackageParser.Package p = i.next(); if (p.applicationInfo == null) continue; final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0) && !p.applicationInfo.isDirectBootAware(); final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0) && p.applicationInfo.isDirectBootAware(); if ((p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0 && (!mSafeMode || isSystemApp(p)) && (matchesUnaware || matchesAware)) { PackageSetting ps = mSettings.mPackages.get(p.packageName); if (ps != null) { ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); if (ai != null) { finalList.add(ai); } } } } } return finalList; } @Override public ProviderInfo resolveContentProvider(String name, int flags, int userId) { return resolveContentProviderInternal(name, flags, userId); } private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId, name); final int callingUid = Binder.getCallingUid(); final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId); if (providerInfo == null) { return null; } if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { return null; } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { return null; } return providerInfo; } } /** * @deprecated */ @Deprecated public void querySyncProviders(List outNames, List outInfo) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; } mComponentResolver.querySyncProviders( outNames, outInfo, mSafeMode, UserHandle.getCallingUserId()); } @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; final List matchList = mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId); final int listSize = (matchList == null ? 0 : matchList.size()); synchronized (mPackages) { for (int i = 0; i < listSize; i++) { final ProviderInfo providerInfo = matchList.get(i); if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { continue; } final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { continue; } if (finalList == null) { finalList = new ArrayList<>(listSize - i); } finalList.add(providerInfo); } } if (finalList != null) { finalList.sort(sProviderInitOrderSorter); return new ParceledListSlice<>(finalList); } return ParceledListSlice.emptyList(); } @Override public InstrumentationInfo getInstrumentationInfo(ComponentName component, int flags) { // reader synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) { return null; } final PackageParser.Instrumentation i = mInstrumentation.get(component); return PackageParser.generateInstrumentationInfo(i, flags); } } @Override public @NonNull ParceledListSlice queryInstrumentation( String targetPackage, int flags) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(targetPackage); if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags)); } private @NonNull List queryInstrumentationInternal(String targetPackage, int flags) { ArrayList finalList = new ArrayList<>(); // reader synchronized (mPackages) { final Iterator i = mInstrumentation.values().iterator(); while (i.hasNext()) { final PackageParser.Instrumentation p = i.next(); if (targetPackage == null || targetPackage.equals(p.info.targetPackage)) { InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p, flags); if (ii != null) { finalList.add(ii); } } } } return finalList; } private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]"); try { scanDirLI(scanDir, parseFlags, scanFlags, currentTime); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) { final File[] files = scanDir.listFiles(); if (ArrayUtils.isEmpty(files)) { Log.d(TAG, "No files in app dir " + scanDir); return; } if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags + " flags=0x" + Integer.toHexString(parseFlags)); } try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser( mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mParallelPackageParserCallback)) { // Submit files for parsing in parallel int fileCount = 0; for (File file : files) { final boolean isPackage = (isApkFile(file) || file.isDirectory()) && !PackageInstallerService.isStageName(file.getName()); if (!isPackage) { // Ignore entries which are not packages continue; } parallelPackageParser.submit(file, parseFlags); fileCount++; } // Process results one by one for (; fileCount > 0; fileCount--) { ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take(); Throwable throwable = parseResult.throwable; int errorCode = PackageManager.INSTALL_SUCCEEDED; if (throwable == null) { // TODO(toddke): move lower in the scan chain // Static shared libraries have synthetic package names if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(parseResult.pkg); } try { scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags, currentTime, null); } catch (PackageManagerException e) { errorCode = e.error; Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage()); } } else if (throwable instanceof PackageParser.PackageParserException) { PackageParser.PackageParserException e = (PackageParser.PackageParserException) throwable; errorCode = e.error; Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage()); } else { throw new IllegalStateException("Unexpected exception occurred while parsing " + parseResult.scanFile, throwable); } // Delete invalid userdata apps if ((scanFlags & SCAN_AS_SYSTEM) == 0 && errorCode != PackageManager.INSTALL_SUCCEEDED) { logCriticalInfo(Log.WARN, "Deleting invalid package at " + parseResult.scanFile); removeCodePathLI(parseResult.scanFile); } } } } public static void reportSettingsProblem(int priority, String msg) { logCriticalInfo(priority, msg); } private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, boolean forceCollect, boolean skipVerify) throws PackageManagerException { // When upgrading from pre-N MR1, verify the package time stamp using the package // directory and not the APK file. final long lastModifiedTime = mIsPreNMR1Upgrade ? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg); final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(pkg); if (ps != null && !forceCollect && ps.codePathString.equals(pkg.codePath) && ps.timeStamp == lastModifiedTime && !isCompatSignatureUpdateNeeded(settingsVersionForPackage) && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) { if (ps.signatures.mSigningDetails.signatures != null && ps.signatures.mSigningDetails.signatures.length != 0 && ps.signatures.mSigningDetails.signatureSchemeVersion != SignatureSchemeVersion.UNKNOWN) { // Optimization: reuse the existing cached signing data // if the package appears to be unchanged. pkg.mSigningDetails = new PackageParser.SigningDetails(ps.signatures.mSigningDetails); return; } Slog.w(TAG, "PackageSetting for " + ps.name + " is missing signatures. Collecting certs again to recover them."); } else { Slog.i(TAG, pkg.codePath + " changed; collecting certs" + (forceCollect ? " (forced)" : "")); } try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); PackageParser.collectCertificates(pkg, skipVerify); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } /** * Clear the package profile if this was an upgrade and the package * version was updated. */ private void maybeClearProfilesForUpgradesLI( @Nullable PackageSetting originalPkgSetting, @NonNull PackageParser.Package currentPkg) { if (originalPkgSetting == null || !isDeviceUpgrading()) { return; } if (originalPkgSetting.versionCode == currentPkg.mVersionCode) { return; } clearAppProfilesLIF(currentPkg, UserHandle.USER_ALL); if (DEBUG_INSTALL) { Slog.d(TAG, originalPkgSetting.name + " clear profile due to version change " + originalPkgSetting.versionCode + " != " + currentPkg.mVersionCode); } } /** * Traces a package scan. * @see #scanPackageLI(File, int, int, long, UserHandle) */ @GuardedBy({"mInstallLock", "mPackages"}) 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 */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); pp.setDisplayMetrics(mMetrics); pp.setCallback(mPackageParserCallback); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); final PackageParser.Package pkg; try { pkg = pp.parsePackage(scanFile, parseFlags); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } // Static shared libraries have synthetic package names if (pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(pkg); } return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user); } /** * Scans a package and returns the newly parsed package. * @throws PackageManagerException on a parse error. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg, final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { // If the package has children and this is the first dive in the function // we scan the package with the SCAN_CHECK_ONLY flag set to see whether all // packages (parent and children) would be successfully scanned before the // actual scan since scanning mutates internal state and we want to atomically // install the package and its children. if ((scanFlags & SCAN_CHECK_ONLY) == 0) { if (pkg.childPackages != null && pkg.childPackages.size() > 0) { scanFlags |= SCAN_CHECK_ONLY; } } else { scanFlags &= ~SCAN_CHECK_ONLY; } // Scan the parent PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags, scanFlags, currentTime, user); // Scan the children final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPackage = pkg.childPackages.get(i); addForInitLI(childPackage, parseFlags, scanFlags, currentTime, user); } if ((scanFlags & SCAN_CHECK_ONLY) != 0) { return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user); } return scannedPkg; } /** * Returns if forced apk verification can be skipped for the whole package, including splits. */ private boolean canSkipForcedPackageVerification(PackageParser.Package pkg) { if (!canSkipForcedApkVerification(pkg.baseCodePath)) { return false; } // TODO: Allow base and splits to be verified individually. if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { for (int i = 0; i < pkg.splitCodePaths.length; i++) { if (!canSkipForcedApkVerification(pkg.splitCodePaths[i])) { return false; } } } return true; } /** * Returns if forced apk verification can be skipped, depending on current FSVerity setup and * whether the apk contains signed root hash. Note that the signer's certificate still needs to * match one in a trusted source, and should be done separately. */ private boolean canSkipForcedApkVerification(String apkPath) { if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) { return VerityUtils.hasFsverity(apkPath); } try { final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath); if (rootHashObserved == null) { return false; // APK does not contain Merkle tree root hash. } synchronized (mInstallLock) { // Returns whether the observed root hash matches what kernel has. mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved); return true; } } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) { Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e); } return false; } /** * Adds a new package to the internal data structures during platform initialization. * After adding, the package is known to the system and available for querying. * For packages located on the device ROM [eg. packages located in /system, /vendor, * etc...], additional checks are performed. Basic verification [such as ensuring * matching signatures, checking version codes, etc...] occurs if the package is * identical to a previously known package. If the package fails a signature check, * the version installed on /data will be removed. If the version of the new package * is less than or equal than the version on /data, it will be ignored. * Regardless of the package location, the results are applied to the internal * structures and the package is made available to the rest of the system. * NOTE: The return value should be removed. It's the passed in package object. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package addForInitLI(PackageParser.Package pkg, @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { final boolean scanSystemPartition = (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0; final String renamedPkgName; final PackageSetting disabledPkgSetting; final boolean isSystemPkgUpdated; final boolean pkgAlreadyExists; PackageSetting pkgSetting; // NOTE: installPackageLI() has the same code to setup the package's // application info. This probably should be done lower in the call // stack [such as scanPackageOnly()]. However, we verify the application // info prior to that [in scanPackageNew()] and thus have to setup // the application info early. pkg.setApplicationVolumeUuid(pkg.volumeUuid); pkg.setApplicationInfoCodePath(pkg.codePath); pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath); pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths); pkg.setApplicationInfoResourcePath(pkg.codePath); pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath); pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths); synchronized (mPackages) { renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage); final String realPkgName = getRealPackageName(pkg, renamedPkgName); if (realPkgName != null) { ensurePackageRenamed(pkg, renamedPkgName); } final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName); final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName); pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting; pkgAlreadyExists = pkgSetting != null; final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName; disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName); isSystemPkgUpdated = disabledPkgSetting != null; if (DEBUG_INSTALL && isSystemPkgUpdated) { Slog.d(TAG, "updatedPkg = " + disabledPkgSetting); } final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null) ? mSettings.getSharedUserLPw(pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true) : null; if (DEBUG_PACKAGE_SCANNING && (parseFlags & PackageParser.PARSE_CHATTY) != 0 && sharedUserSetting != null) { Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + sharedUserSetting.userId + "):" + " packages=" + sharedUserSetting.packages); } if (scanSystemPartition) { // Potentially prune child packages. If the application on the /system // partition has been updated via OTA, but, is still disabled by a // version on /data, cycle through all of its children packages and // remove children that are no longer defined. if (isSystemPkgUpdated) { final int scannedChildCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; final int disabledChildCount = disabledPkgSetting.childPackageNames != null ? disabledPkgSetting.childPackageNames.size() : 0; for (int i = 0; i < disabledChildCount; i++) { String disabledChildPackageName = disabledPkgSetting.childPackageNames.get(i); boolean disabledPackageAvailable = false; for (int j = 0; j < scannedChildCount; j++) { PackageParser.Package childPkg = pkg.childPackages.get(j); if (childPkg.packageName.equals(disabledChildPackageName)) { disabledPackageAvailable = true; break; } } if (!disabledPackageAvailable) { mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName); } } // we're updating the disabled package, so, scan it as the package setting final ScanRequest request = new ScanRequest(pkg, sharedUserSetting, null, disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */, null /* originalPkgSetting */, null, parseFlags, scanFlags, (pkg == mPlatformPackage), user); applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage); final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L); if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) { scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting); } } } } final boolean newPkgChangedPaths = pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath); final boolean newPkgVersionGreater = pkgAlreadyExists && pkg.getLongVersionCode() > pkgSetting.versionCode; final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated && newPkgChangedPaths && newPkgVersionGreater; if (isSystemPkgBetter) { // The version of the application on /system is greater than the version on // /data. Switch back to the application on /system. // It's safe to assume the application on /system will correctly scan. If not, // there won't be a working copy of the application. synchronized (mPackages) { // just remove the loaded entries from package lists mPackages.remove(pkgSetting.name); } logCriticalInfo(Log.WARN, "System package updated;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); final InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); args.cleanUpResourcesLI(); synchronized (mPackages) { mSettings.enableSystemPackageLPw(pkgSetting.name); } } if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) { // The version of the application on the /system partition is less than or // equal to the version on the /data partition. Throw an exception and use // the application already installed on the /data partition. throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at " + pkg.codePath + " ignored: updated version " + pkgSetting.versionCode + " better than this " + pkg.getLongVersionCode()); } // Verify certificates against what was last scanned. If there was an upgrade and this is an // app in a system partition, or if this is an updated priv app, we will force re-collecting // certificate. final boolean forceCollect = (mIsUpgrade && scanSystemPartition) || PackageManagerServiceUtils.isApkVerificationForced(disabledPkgSetting); // Full APK verification can be skipped during certificate collection, only if the file is // in verified partition, or can be verified on access (when apk verity is enabled). In both // cases, only data in Signing Block is verified instead of the whole file. final boolean skipVerify = scanSystemPartition || (forceCollect && canSkipForcedPackageVerification(pkg)); collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify); // Reset profile if the application version is changed maybeClearProfilesForUpgradesLI(pkgSetting, pkg); /* * A new system app appeared, but we already had a non-system one of the * same name installed earlier. */ boolean shouldHideSystemApp = false; // A new application appeared on /system, but, we already have a copy of // the application installed on /data. if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists && !pkgSetting.isSystem()) { if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA) && !pkgSetting.signatures.mSigningDetails.checkCapability( pkg.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) { logCriticalInfo(Log.WARN, "System package signature mismatch;" + " name: " + pkgSetting.name); try (PackageFreezer freezer = freezePackage(pkg.packageName, "scanPackageInternalLI")) { deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null); } pkgSetting = null; } else if (newPkgVersionGreater) { // The application on /system is newer than the application on /data. // Simply remove the application on /data [keeping application data] // and replace it with the version on /system. logCriticalInfo(Log.WARN, "System package enabled;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } } else { // The application on /system is older than the application on /data. Hide // the application on /system and the version on /data will be scanned later // and re-added like an update. shouldHideSystemApp = true; logCriticalInfo(Log.INFO, "System package disabled;" + " name: " + pkgSetting.name + "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode + "; new: " + pkg.codePath + " @ " + pkg.codePath); } } final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user); if (scanResult.success) { synchronized (mPackages) { boolean appIdCreated = false; try { final String pkgName = scanResult.pkgSetting.name; final Map reconcileResult = reconcilePackagesLocked( new ReconcileRequest( Collections.singletonMap(pkgName, scanResult), mSharedLibraries, mPackages, Collections.singletonMap( pkgName, getSettingsVersionForPackage(pkg)), Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))), mSettings.mKeySetManagerService); appIdCreated = optimisticallyRegisterAppId(scanResult); commitReconciledScanResultLocked(reconcileResult.get(pkgName)); } catch (PackageManagerException e) { if (appIdCreated) { cleanUpAppIdCreation(scanResult); } throw e; } } } if (shouldHideSystemApp) { synchronized (mPackages) { mSettings.disableSystemPackageLPw(pkg.packageName, true); } } return scanResult.pkgSetting.pkg; } private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) { // Derive the new package synthetic package name pkg.setPackageName(pkg.packageName + STATIC_SHARED_LIB_DELIMITER + pkg.staticSharedLibVersion); } static String fixProcessName(String defProcessName, String processName) { if (processName == null) { return defProcessName; } return processName; } /** * Enforces that only the system UID or root's UID can call a method exposed * via Binder. * * @param message used as message if SecurityException is thrown * @throws SecurityException if the caller is not system or root */ private static void enforceSystemOrRoot(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) { throw new SecurityException(message); } } /** * Enforces that only the system UID or root's UID or shell'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 shell */ private static void enforceSystemOrRootOrShell(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID && uid != Process.SHELL_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 = isDeviceUpgrading(); // First boot or factory reset. // Note: we also handle devices that are upgrading to N right now as if it is their // first boot, as they do not have profile data. boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade; // We need to re-extract after a pruned cache, as AoT-ed files will be out of date. boolean causePrunedCache = VMRuntime.didPruneDalvikCache(); if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) { return; } List pkgs; synchronized (mPackages) { pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this); } final long startTime = System.nanoTime(); final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */, causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT, false /* bootComplete */); final int elapsedTimeSeconds = (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime); MetricsLogger.histogram(mContext, "opt_dialog_num_dexopted", stats[0]); MetricsLogger.histogram(mContext, "opt_dialog_num_skipped", stats[1]); MetricsLogger.histogram(mContext, "opt_dialog_num_failed", stats[2]); MetricsLogger.histogram(mContext, "opt_dialog_num_total", getOptimizablePackages().size()); MetricsLogger.histogram(mContext, "opt_dialog_time_s", elapsedTimeSeconds); } /* * Return the prebuilt profile path given a package base code path. */ private static String getPrebuildProfilePath(PackageParser.Package pkg) { return pkg.baseCodePath + ".prof"; } /** * Performs dexopt on the set of packages in {@code packages} and returns an int array * containing statistics about the invocation. The array consists of three elements, * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped} * and {@code numberOfPackagesFailed}. */ private int[] performDexOptUpgrade(List pkgs, boolean showDialog, final int compilationReason, boolean bootComplete) { int numberOfPackagesVisited = 0; int numberOfPackagesOptimized = 0; int numberOfPackagesSkipped = 0; int numberOfPackagesFailed = 0; final int numberOfPackagesToDexopt = pkgs.size(); for (PackageParser.Package pkg : pkgs) { numberOfPackagesVisited++; boolean useProfileForDexopt = false; if ((isFirstBoot() || isDeviceUpgrading()) && isSystemApp(pkg)) { // Copy over initial preopt profiles since we won't get any JIT samples for methods // that are already compiled. File profileFile = new File(getPrebuildProfilePath(pkg)); // Copy profile if it exists. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The issue // is that we don't have a good way to say "do this only once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Installer failed to copy system profile!"); } else { // Disabled as this causes speed-profile compilation during first boot // even if things are already compiled. // useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } else { PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName); // Handle compressed APKs in this path. Only do this for stubs with profiles to // minimize the number off apps being speed-profile compiled during first boot. // The other paths will not change the filter. if (disabledPs != null && disabledPs.pkg.isStub) { // The package is the stub one, remove the stub suffix to get the normal // package and APK names. String systemProfilePath = getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, ""); profileFile = new File(systemProfilePath); // If we have a profile for a compressed APK, copy it to the reference // location. // Note that copying the profile here will cause it to override the // reference profile every OTA even though the existing reference profile // may have more data. We can't copy during decompression since the // directories are not set up at that point. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The // issue is that we don't have a good way to say "do this only // once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Failed to copy system profile for stub package!"); } else { useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } } } } if (!PackageDexOptimizer.canOptimizePackage(pkg)) { if (DEBUG_DEXOPT) { Log.i(TAG, "Skipping update of of non-optimizable app " + pkg.packageName); } numberOfPackagesSkipped++; continue; } if (DEBUG_DEXOPT) { Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " + numberOfPackagesToDexopt + ": " + pkg.packageName); } if (showDialog) { try { ActivityManager.getService().showBootMessage( mContext.getResources().getString(R.string.android_upgrading_apk, numberOfPackagesVisited, numberOfPackagesToDexopt), true); } catch (RemoteException e) { } synchronized (mPackages) { mDexOptDialogShown = true; } } int pkgCompilationReason = compilationReason; if (useProfileForDexopt) { // Use background dexopt mode to try and use the profile. Note that this does not // guarantee usage of the profile. pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT; } if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { mArtManagerService.compileLayouts(pkg); } // checkProfiles is false to avoid merging profiles during boot which // might interfere with background compilation (b/28612421). // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a // trade-off worth doing to save boot time work. int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0; if (compilationReason == REASON_FIRST_BOOT) { // TODO: This doesn't cover the upgrade case, we should check for this too. dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE; } int primaryDexOptStaus = performDexOptTraced(new DexoptOptions( pkg.packageName, pkgCompilationReason, dexoptFlags)); switch (primaryDexOptStaus) { case PackageDexOptimizer.DEX_OPT_PERFORMED: numberOfPackagesOptimized++; break; case PackageDexOptimizer.DEX_OPT_SKIPPED: numberOfPackagesSkipped++; break; case PackageDexOptimizer.DEX_OPT_FAILED: numberOfPackagesFailed++; break; default: Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus); break; } } return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped, numberOfPackagesFailed }; } @Override public void notifyPackageUse(String packageName, int reason) { synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); if (getInstantAppPackageName(callingUid) != null) { if (!isCallerSameApp(packageName, callingUid)) { return; } } else { if (isInstantApp(packageName, callingUserId)) { return; } } notifyPackageUseLocked(packageName, reason); } } @GuardedBy("mPackages") public CheckPermissionDelegate getCheckPermissionDelegateLocked() { return mCheckPermissionDelegate; } @GuardedBy("mPackages") public void setCheckPermissionDelegateLocked(CheckPermissionDelegate delegate) { mCheckPermissionDelegate = delegate; } @GuardedBy("mPackages") private void notifyPackageUseLocked(String packageName, int reason) { final PackageParser.Package p = mPackages.get(packageName); if (p == null) { return; } p.mLastPackageUsageTimeInMills[reason] = System.currentTimeMillis(); } @Override public void notifyDexLoad(String loadingPackageName, List classLoaderNames, List classPaths, String loaderIsa) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); if (ai == null) { Slog.w(TAG, "Loading a package that does not exist for the calling user. package=" + loadingPackageName + ", user=" + userId); return; } mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId); } @Override public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule, IDexModuleRegisterCallback callback) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(packageName, /*flags*/ 0, userId); DexManager.RegisterDexModuleResult result; if (ai == null) { Slog.w(TAG, "Registering a dex module for a package that does not exist for the" + " calling user. package=" + packageName + ", user=" + userId); result = new DexManager.RegisterDexModuleResult(false, "Package not installed"); } else { result = mDexManager.registerDexModule(ai, dexModulePath, isSharedModule, userId); } if (callback != null) { mHandler.post(() -> { try { callback.onDexModuleRegistered(dexModulePath, result.success, result.message); } catch (RemoteException e) { Slog.w(TAG, "Failed to callback after module registration " + dexModulePath, e); } }); } } /** * Ask the package manager to perform a dex-opt with the given compiler filter. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptMode(String packageName, boolean checkProfiles, String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) { int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) | (force ? DexoptOptions.DEXOPT_FORCE : 0) | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0); return performDexOpt(new DexoptOptions(packageName, REASON_UNKNOWN, targetCompilerFilter, splitName, flags)); } /** * Ask the package manager to perform a dex-opt with the given compiler filter on the * secondary dex files belonging to the given package. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptSecondary(String packageName, String compilerFilter, boolean force) { int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES | DexoptOptions.DEXOPT_BOOT_COMPLETE | (force ? DexoptOptions.DEXOPT_FORCE : 0); return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags)); } /** * Ask the package manager to compile layouts in the given package. */ @Override public boolean compileLayouts(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { return false; } } return mViewCompiler.compileLayouts(pkg); } /*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 = findSharedLibraries(p); final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo); if (!deps.isEmpty()) { DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(), options.getCompilationReason(), options.getCompilerFilter(), options.getSplitName(), options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY); for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = null; synchronized (mPackages) { depPackage = mPackages.get(info.getPackageName()); } if (depPackage != null) { // TODO: Analyze and investigate if we (should) profile libraries. pdo.performDexOpt(depPackage, instructionSets, getOrCreateCompilerPackageStats(depPackage), mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions); } else { // TODO(ngeoffray): Support dexopting system shared libraries. } } } return pdo.performDexOpt(p, instructionSets, getOrCreateCompilerPackageStats(p), mDexManager.getPackageUseInfoOrDefault(p.packageName), options); } /** * Reconcile the information we have about the secondary dex files belonging to * {@code packageName} 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(@Nullable List packageNames) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return false; } enforceSystemOrRootOrShell("runBackgroundDexoptJob"); final long identity = Binder.clearCallingIdentity(); try { return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames); } finally { Binder.restoreCallingIdentity(identity); } } private static List findSharedLibraries(PackageParser.Package p) { if (p.usesLibraryInfos != null) { ArrayList retValue = new ArrayList<>(); Set collectedNames = new HashSet<>(); for (SharedLibraryInfo info : p.usesLibraryInfos) { findSharedLibrariesRecursive(info, retValue, collectedNames); } return retValue; } else { return Collections.emptyList(); } } private static void findSharedLibrariesRecursive(SharedLibraryInfo info, ArrayList collected, Set collectedNames) { if (!collectedNames.contains(info.getName())) { collectedNames.add(info.getName()); collected.add(info); if (info.getDependencies() != null) { for (SharedLibraryInfo dep : info.getDependencies()) { findSharedLibrariesRecursive(dep, collected, collectedNames); } } } } List findSharedNonSystemLibraries(PackageParser.Package pkg) { List deps = findSharedLibraries(pkg); if (!deps.isEmpty()) { ArrayList retValue = new ArrayList<>(); synchronized (mPackages) { for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = mPackages.get(info.getPackageName()); if (depPackage != null) { retValue.add(depPackage); } } } return retValue; } else { return Collections.emptyList(); } } @Nullable private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) { return getSharedLibraryInfo(name, version, mSharedLibraries, null); } @Nullable private static SharedLibraryInfo getSharedLibraryInfo(String name, long version, Map> existingLibraries, @Nullable Map> newLibraries) { if (newLibraries != null) { final LongSparseArray versionedLib = newLibraries.get(name); SharedLibraryInfo info = null; if (versionedLib != null) { info = versionedLib.get(version); } if (info != null) { return info; } } final LongSparseArray versionedLib = existingLibraries.get(name); if (versionedLib == null) { return null; } return versionedLib.get(version); } private SharedLibraryInfo getLatestSharedLibraVersionLPr(PackageParser.Package pkg) { LongSparseArray versionedLib = mSharedLibraries.get( pkg.staticSharedLibName); if (versionedLib == null) { return null; } long previousLibVersion = -1; final int versionCount = versionedLib.size(); for (int i = 0; i < versionCount; i++) { final long libVersion = versionedLib.keyAt(i); if (libVersion < pkg.staticSharedLibVersion) { previousLibVersion = Math.max(previousLibVersion, libVersion); } } if (previousLibVersion >= 0) { return versionedLib.get(previousLibVersion); } return null; } @Nullable private PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) { PackageSetting sharedLibPackage = null; synchronized (mPackages) { final SharedLibraryInfo latestSharedLibraVersionLPr = getLatestSharedLibraVersionLPr(scanResult.pkgSetting.pkg); if (latestSharedLibraVersionLPr != null) { sharedLibPackage = mSettings.getPackageLPr( latestSharedLibraVersionLPr.getPackageName()); } } return sharedLibPackage; } public void shutdown() { mPackageUsage.writeNow(mPackages); mCompilerStats.writeNow(); mDexManager.writePackageDexUsageNow(); PackageWatchdog.getInstance(mContext).writeNow(); // This is the last chance to write out pending restriction settings synchronized (mPackages) { if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS); for (int userId : mDirtyUsers) { mSettings.writePackageRestrictionsLPr(userId); } mDirtyUsers.clear(); } } } @Override public void dumpProfiles(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } /* Only the shell, root, or the app user should be able to dump profiles. */ int callingUid = Binder.getCallingUid(); if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID && callingUid != pkg.applicationInfo.uid) { throw new SecurityException("dumpProfiles"); } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles"); mArtManagerService.dumpProfiles(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @Override public void forceDexOpt(String packageName) { enforceSystemOrRoot("forceDexOpt"); PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); // Whoever is calling forceDexOpt wants a compiled package. // Don't use profiles since that may cause compilation to be skipped. final int res = performDexOptInternalWithDependenciesLI( pkg, new DexoptOptions(packageName, getDefaultCompilerFilter(), DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE)); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) { throw new IllegalStateException("Failed to dexopt: " + res); } } } @GuardedBy("mPackages") private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) { if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package not in system partition"); return false; } else if (mPackages.get(oldPkg.name) != null) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package still exists"); return false; } return true; } @GuardedBy("mInstallLock") void removeCodePathLI(File codePath) { if (codePath.isDirectory()) { try { mInstaller.rmPackageDir(codePath.getAbsolutePath()); } catch (InstallerException e) { Slog.w(TAG, "Failed to remove code path", e); } } else { codePath.delete(); } } private int[] resolveUserIds(int userId) { return (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds() : new int[] { userId }; } private void clearAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } clearAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { clearAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } clearAppProfilesLIF(pkg, UserHandle.USER_ALL); } private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } } private void destroyAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } } private void destroyAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.destroyAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId); } } private void destroyAppProfilesLIF(PackageParser.Package pkg) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppProfilesLeafLIF(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppProfilesLeafLIF(pkg.childPackages.get(i)); } } private void destroyAppProfilesLeafLIF(PackageParser.Package pkg) { try { mInstaller.destroyAppProfiles(pkg.packageName); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } private void clearAppProfilesLIF(PackageParser.Package pkg, int userId) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } mArtManagerService.clearAppProfiles(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { mArtManagerService.clearAppProfiles(pkg.childPackages.get(i)); } } private void setInstallAndUpdateTime(PackageParser.Package pkg, long firstInstallTime, long lastUpdateTime) { // Set parent install/update time PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } // Set children install/update time final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPkg = pkg.childPackages.get(i); ps = (PackageSetting) childPkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } } } @GuardedBy("mPackages") private void applyDefiningSharedLibraryUpdateLocked( PackageParser.Package pkg, SharedLibraryInfo libInfo, BiConsumer action) { // Note that libraries defined by this package may be null if: // - Package manager was unable to create the shared library. The package still // gets installed, but the shared library does not get created. // Or: // - Package manager is in a state where package isn't scanned yet. This will // get called again after scanning to fix the dependencies. if (pkg.isLibrary()) { if (pkg.staticSharedLibName != null) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( pkg.staticSharedLibName, pkg.staticSharedLibVersion); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } else { for (String libraryName : pkg.libraryNames) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( libraryName, SharedLibraryInfo.VERSION_UNDEFINED); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } } } } @GuardedBy("mPackages") private void addSharedLibraryLPr(PackageParser.Package pkg, Set usesLibraryFiles, SharedLibraryInfo libInfo, PackageParser.Package changingLib) { if (libInfo.getPath() != null) { usesLibraryFiles.add(libInfo.getPath()); return; } PackageParser.Package p = mPackages.get(libInfo.getPackageName()); if (changingLib != null && changingLib.packageName.equals(libInfo.getPackageName())) { // If we are doing this while in the middle of updating a library apk, // then we need to make sure to use that new apk for determining the // dependencies here. (We haven't yet finished committing the new apk // to the package manager state.) if (p == null || p.packageName.equals(changingLib.packageName)) { p = changingLib; } } if (p != null) { usesLibraryFiles.addAll(p.getAllCodePaths()); // If the package provides libraries, add the dependency to them. applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, (definingLibrary, dependency) -> { definingLibrary.addDependency(dependency); }); if (p.usesLibraryFiles != null) { Collections.addAll(usesLibraryFiles, p.usesLibraryFiles); } } } @GuardedBy("mPackages") private void updateSharedLibrariesLocked(PackageParser.Package pkg, PackageParser.Package changingLib, Map availablePackages) throws PackageManagerException { final ArrayList sharedLibraryInfos = collectSharedLibraryInfos(pkg, availablePackages, mSharedLibraries, null); executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos); } private static ArrayList collectSharedLibraryInfos(PackageParser.Package pkg, Map availablePackages, @NonNull final Map> existingLibraries, @Nullable final Map> newLibraries) throws PackageManagerException { if (pkg == null) { return null; } // The collection used here must maintain the order of addition (so // that libraries are searched in the correct order) and must have no // duplicates. ArrayList usesLibraryInfos = null; if (pkg.usesLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesLibraries, null, null, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, null, availablePackages, existingLibraries, newLibraries); } if (pkg.usesStaticLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } if (pkg.usesOptionalLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesOptionalLibraries, null, null, pkg.packageName, false, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } return usesLibraryInfos; } private void executeSharedLibrariesUpdateLPr(PackageParser.Package pkg, PackageParser.Package changingLib, ArrayList usesLibraryInfos) { // If the package provides libraries, clear their old dependencies. // This method will set them up again. applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> { definingLibrary.clearDependencies(); }); if (usesLibraryInfos != null) { pkg.usesLibraryInfos = usesLibraryInfos; // Use LinkedHashSet to preserve the order of files added to // usesLibraryFiles while eliminating duplicates. Set usesLibraryFiles = new LinkedHashSet<>(); for (SharedLibraryInfo libInfo : usesLibraryInfos) { addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib); } pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]); } else { pkg.usesLibraryInfos = null; pkg.usesLibraryFiles = null; } } @GuardedBy("mPackages") private static ArrayList collectSharedLibraryInfos( @NonNull List requestedLibraries, @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, @NonNull String packageName, boolean required, int targetSdk, @Nullable ArrayList outUsedLibraries, @NonNull final Map
* Currently, there are four cases in which this can occur: *
* Access may be limited based upon whether the calling or target applications * are instant applications. * * @see #canViewInstantApps(int, int) */ @GuardedBy("mPackages") private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, @Nullable ComponentName component, @ComponentType int componentType, int userId) { // if we're in an isolated process, get the real calling UID if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final String instantAppPkgName = getInstantAppPackageName(callingUid); final boolean callerIsInstantApp = instantAppPkgName != null; if (ps == null) { if (callerIsInstantApp) { // pretend the application exists, but, needs to be filtered return true; } return false; } // if the target and caller are the same application, don't filter if (isCallerSameApp(ps.name, callingUid)) { return false; } if (callerIsInstantApp) { // both caller and target are both instant, but, different applications, filter if (ps.getInstantApp(userId)) { return true; } // request for a specific component; if it hasn't been explicitly exposed through // property or instrumentation target, filter if (component != null) { final PackageParser.Instrumentation instrumentation = mInstrumentation.get(component); if (instrumentation != null && isCallerSameApp(instrumentation.info.targetPackage, callingUid)) { return false; } return !isComponentVisibleToInstantApp(component, componentType); } // request for application; if no components have been explicitly exposed, filter return !ps.pkg.visibleToInstantApps; } if (ps.getInstantApp(userId)) { // caller can see all components of all instant applications, don't filter if (canViewInstantApps(callingUid, userId)) { return false; } // request for a specific instant application component, filter if (component != null) { return true; } // request for an instant application; if the caller hasn't been granted access, filter return !mInstantAppRegistry.isInstantAccessGranted( userId, UserHandle.getAppId(callingUid), ps.appId); } return false; } /** * @see #filterAppAccessLPr(PackageSetting, int, ComponentName, int, int) */ @GuardedBy("mPackages") private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, int userId) { return filterAppAccessLPr(ps, callingUid, null, TYPE_UNKNOWN, userId); } @GuardedBy("mPackages") 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; } // Installer gets to see all static libs. if (PackageManager.PERMISSION_GRANTED == checkUidPermission(Manifest.permission.INSTALL_PACKAGES, 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 SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(ps.pkg.staticSharedLibName, ps.pkg.staticSharedLibVersion); if (libraryInfo == 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, libraryInfo.getName()); if (index < 0) { continue; } if (uidPs.pkg.usesStaticLibrariesVersions[index] == libraryInfo.getLongVersion()) { return false; } } } return true; } @Override public String[] currentToCanonicalPackageNames(String[] names) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return names; } final String[] out = new String[names.length]; // reader synchronized (mPackages) { final int callingUserId = UserHandle.getUserId(callingUid); final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId); for (int i=names.length-1; i>=0; i--) { final PackageSetting ps = mSettings.mPackages.get(names[i]); boolean translateName = false; if (ps != null && ps.realName != null) { final boolean targetIsInstantApp = ps.getInstantApp(callingUserId); translateName = !targetIsInstantApp || canViewInstantApps || mInstantAppRegistry.isInstantAccessGranted(callingUserId, UserHandle.getAppId(callingUid), ps.appId); } out[i] = translateName ? ps.realName : names[i]; } } return out; } @Override public String[] canonicalToCurrentPackageNames(String[] names) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return names; } final String[] out = new String[names.length]; // reader synchronized (mPackages) { final int callingUserId = UserHandle.getUserId(callingUid); final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId); for (int i=names.length-1; i>=0; i--) { final String cur = mSettings.getRenamedPackageLPr(names[i]); boolean translateName = false; if (cur != null) { final PackageSetting ps = mSettings.mPackages.get(names[i]); final boolean targetIsInstantApp = ps != null && ps.getInstantApp(callingUserId); translateName = !targetIsInstantApp || canViewInstantApps || mInstantAppRegistry.isInstantAccessGranted(callingUserId, UserHandle.getAppId(callingUid), ps.appId); } out[i] = translateName ? cur : names[i]; } } return out; } @Override public int getPackageUid(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return -1; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForPackage(flags, userId, packageName); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid"); // reader synchronized (mPackages) { final PackageParser.Package p = mPackages.get(packageName); if (p != null && p.isMatch(flags)) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, userId)) { return -1; } return UserHandle.getUid(userId, p.applicationInfo.uid); } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && ps.isMatch(flags) && !filterAppAccessLPr(ps, callingUid, userId)) { return UserHandle.getUid(userId, ps.appId); } } } return -1; } /** * Check if any package sharing/holding a uid has a low enough target SDK. * * @param uid The uid of the packages * @param higherTargetSDK The target SDK that might be higher than the searched package * * @return {@code true} if there is a package sharing/holding the uid with * {@code package.targetSDK < higherTargetSDK} */ private boolean hasTargetSdkInUidLowerThan(int uid, int higherTargetSDK) { int userId = UserHandle.getUserId(uid); synchronized (mPackages) { Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid)); if (obj == null) { return false; } if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (!ps.getInstalled(userId)) { return false; } return ps.pkg.applicationInfo.targetSdkVersion < higherTargetSDK; } else if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; final int numPkgs = sus.packages.size(); for (int i = 0; i < numPkgs; i++) { final PackageSetting ps = sus.packages.valueAt(i); if (!ps.getInstalled(userId)) { continue; } if (ps.pkg.applicationInfo.targetSdkVersion < higherTargetSDK) { return true; } } return false; } else { return false; } } } @Override public int[] getPackageGids(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForPackage(flags, userId, packageName); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids"); // reader synchronized (mPackages) { final PackageParser.Package p = mPackages.get(packageName); if (p != null && p.isMatch(flags)) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, userId)) { return null; } // TODO: Shouldn't this be checking for package installed state for userId and // return null? return ps.getPermissionsState().computeGids(userId); } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && ps.isMatch(flags) && !filterAppAccessLPr(ps, callingUid, userId)) { return ps.getPermissionsState().computeGids(userId); } } } return null; } @Override public PermissionInfo getPermissionInfo(String name, String packageName, int flags) { return mPermissionManager.getPermissionInfo(name, packageName, flags, getCallingUid()); } @Override public @Nullable ParceledListSlice queryPermissionsByGroup(String groupName, int flags) { final List permissionList = mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid()); return (permissionList == null) ? null : new ParceledListSlice<>(permissionList); } @Override public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) { return mPermissionManager.getPermissionGroupInfo(groupName, flags, getCallingUid()); } @Override public @NonNull ParceledListSlice getAllPermissionGroups(int flags) { final List permissionList = mPermissionManager.getAllPermissionGroups(flags, getCallingUid()); return (permissionList == null) ? ParceledListSlice.emptyList() : new ParceledListSlice<>(permissionList); } @GuardedBy("mPackages") 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); if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get application info"); } // writer synchronized (mPackages) { // Normalize package name to handle renamed packages and static libs packageName = resolveInternalPackageNameLPr(packageName, PackageManager.VERSION_CODE_HIGHEST); PackageParser.Package p = mPackages.get(packageName); if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getApplicationInfo " + packageName + ": " + p); if (p != null) { PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) return null; if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; } if (filterAppAccessLPr(ps, filterCallingUid, userId)) { return null; } // Note: isEnabledLP() does not apply here - always return info ApplicationInfo ai = PackageParser.generateApplicationInfo( p, flags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(p); } return ai; } if ("android".equals(packageName)||"system".equals(packageName)) { return mAndroidApplication; } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { // Already generates the external package name return generateApplicationInfoFromSettingsLPw(packageName, flags, filterCallingUid, userId); } } return null; } @GuardedBy("mPackages") private String normalizePackageNameLPr(String packageName) { String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName); return normalizedPackageName != null ? normalizedPackageName : packageName; } @Override public void deletePreloadsFileCache() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CLEAR_APP_CACHE, "deletePreloadsFileCache"); File dir = Environment.getDataPreloadsFileCacheDirectory(); Slog.i(TAG, "Deleting preloaded file cache " + dir); FileUtils.deleteContents(dir); } @Override public void freeStorageAndNotify(final String volumeUuid, final long freeStorageSize, final int storageFlags, final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); mHandler.post(() -> { boolean success = false; try { freeStorage(volumeUuid, freeStorageSize, storageFlags); success = true; } catch (IOException e) { Slog.w(TAG, e); } if (observer != null) { try { observer.onRemoveCompleted(null, success); } catch (RemoteException e) { Slog.w(TAG, e); } } }); } @Override public void freeStorage(final String volumeUuid, final long freeStorageSize, final int storageFlags, final IntentSender pi) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, TAG); mHandler.post(() -> { boolean success = false; try { freeStorage(volumeUuid, freeStorageSize, storageFlags); success = true; } catch (IOException e) { Slog.w(TAG, e); } if (pi != null) { try { pi.sendIntent(null, success ? 1 : 0, null, null, null); } catch (SendIntentException e) { Slog.w(TAG, e); } } }); } /** * Blocking call to clear various types of cached data across the system * until the requested bytes are available. */ public void freeStorage(String volumeUuid, long bytes, int storageFlags) throws IOException { final StorageManager storage = mContext.getSystemService(StorageManager.class); final File file = storage.findPathForUuid(volumeUuid); if (file.getUsableSpace() >= bytes) return; if (ENABLE_FREE_CACHE_V2) { final boolean internalVolume = Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid); final boolean aggressive = (storageFlags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0; final long reservedBytes = storage.getStorageCacheBytes(file, storageFlags); // 1. Pre-flight to determine if we have any chance to succeed // 2. Consider preloaded data (after 1w honeymoon, unless aggressive) if (internalVolume && (aggressive || SystemProperties .getBoolean("persist.sys.preloads.file_cache_expired", false))) { deletePreloadsFileCache(); if (file.getUsableSpace() >= bytes) return; } // 3. Consider parsed APK data (aggressive only) if (internalVolume && aggressive) { FileUtils.deleteContents(mCacheDir); if (file.getUsableSpace() >= bytes) return; } // 4. Consider cached app data (above quotas) try { mInstaller.freeCache(volumeUuid, bytes, reservedBytes, Installer.FLAG_FREE_CACHE_V2); } catch (InstallerException ignored) { } if (file.getUsableSpace() >= bytes) return; // 5. Consider shared libraries with refcount=0 and age>min cache period if (internalVolume && pruneUnusedStaticSharedLibraries(bytes, android.provider.Settings.Global.getLong(mContext.getContentResolver(), Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD, DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD))) { return; } // 6. Consider dexopt output (aggressive only) // TODO: Implement // 7. Consider installed instant apps unused longer than min cache period if (internalVolume && mInstantAppRegistry.pruneInstalledInstantApps(bytes, android.provider.Settings.Global.getLong(mContext.getContentResolver(), Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD, InstantAppRegistry.DEFAULT_INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) { return; } // 8. Consider cached app data (below quotas) try { mInstaller.freeCache(volumeUuid, bytes, reservedBytes, Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA); } catch (InstallerException ignored) { } if (file.getUsableSpace() >= bytes) return; // 9. Consider DropBox entries // TODO: Implement // 10. Consider instant meta-data (uninstalled apps) older that min cache period if (internalVolume && mInstantAppRegistry.pruneUninstalledInstantApps(bytes, android.provider.Settings.Global.getLong(mContext.getContentResolver(), Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD, InstantAppRegistry.DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) { return; } } else { try { mInstaller.freeCache(volumeUuid, bytes, 0, 0); } catch (InstallerException ignored) { } if (file.getUsableSpace() >= bytes) return; } throw new IOException("Failed to free " + bytes + " on storage device at " + file); } private boolean pruneUnusedStaticSharedLibraries(long neededSpace, long maxCachePeriod) throws IOException { final StorageManager storage = mContext.getSystemService(StorageManager.class); final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL); List packagesToDelete = null; final long now = System.currentTimeMillis(); synchronized (mPackages) { final int[] allUsers = sUserManager.getUserIds(); final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { final LongSparseArray versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } final int versionCount = versionedLib.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libInfo = versionedLib.valueAt(j); // Skip packages that are not static shared libs. if (!libInfo.isStatic()) { break; } // Important: We skip static shared libs used for some user since // in such a case we need to keep the APK on the device. The check for // a lib being used for any user is performed by the uninstall call. final VersionedPackage declaringPackage = libInfo.getDeclaringPackage(); // Resolve the package name - we use synthetic package names internally final String internalPackageName = resolveInternalPackageNameLPr( declaringPackage.getPackageName(), declaringPackage.getLongVersionCode()); final PackageSetting ps = mSettings.getPackageLPr(internalPackageName); // Skip unused static shared libs cached less than the min period // to prevent pruning a lib needed by a subsequently installed package. if (ps == null || now - ps.lastUpdateTime < maxCachePeriod) { continue; } if (ps.pkg.isSystem()) { continue; } if (packagesToDelete == null) { packagesToDelete = new ArrayList<>(); } packagesToDelete.add(new VersionedPackage(internalPackageName, declaringPackage.getLongVersionCode())); } } } if (packagesToDelete != null) { final int packageCount = packagesToDelete.size(); for (int i = 0; i < packageCount; i++) { final VersionedPackage pkgToDelete = packagesToDelete.get(i); // Delete the package synchronously (will fail of the lib used for any user). if (deletePackageX(pkgToDelete.getPackageName(), pkgToDelete.getLongVersionCode(), UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS) == PackageManager.DELETE_SUCCEEDED) { if (volume.getUsableSpace() >= neededSpace) { return true; } } } } return false; } /** * Update given flags based on encryption status of current user. */ private int updateFlags(int flags, int userId) { if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_AWARE)) != 0) { // Caller expressed an explicit opinion about what encryption // aware/unaware components they want to see, so fall through and // give them what they want } else { // Caller expressed no opinion, so match based on user state if (getUserManagerInternal().isUserUnlockingOrUnlocked(userId)) { flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE; } else { flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE; } } return flags; } private UserManagerInternal getUserManagerInternal() { if (mUserManagerInternal == null) { mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); } return mUserManagerInternal; } private ActivityManagerInternal getActivityManagerInternal() { if (mActivityManagerInternal == null) { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); } return mActivityManagerInternal; } private ActivityTaskManagerInternal getActivityTaskManagerInternal() { if (mActivityTaskManagerInternal == null) { mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); } return mActivityTaskManagerInternal; } private DeviceIdleController.LocalService getDeviceIdleController() { if (mDeviceIdleController == null) { mDeviceIdleController = LocalServices.getService(DeviceIdleController.LocalService.class); } return mDeviceIdleController; } private StorageManagerInternal getStorageManagerInternal() { if (mStorageManagerInternal == null) { mStorageManagerInternal = LocalServices.getService(StorageManagerInternal.class); } return mStorageManagerInternal; } /** * 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; if ((flags & PackageManager.MATCH_ANY_USER) != 0) { // require the permission to be held; the calling uid and given user id referring // to the same user is not sufficient mPermissionManager.enforceCrossUserPermission( Binder.getCallingUid(), userId, false, false, !isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId), "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; } 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) { return updateFlags(flags, userId); } /** * Update given intent when being used to request {@link ResolveInfo}. */ private Intent updateIntentForResolve(Intent intent) { if (intent.getSelector() != null) { intent = intent.getSelector(); } if (DEBUG_PREFERRED) { intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); } return intent; } /** * Update given flags when being used to request {@link ResolveInfo}. * Instant apps are resolved specially, depending upon context. Minimally, * {@code}flags{@code} must have the {@link PackageManager#MATCH_INSTANT} * flag set. However, this flag is only honoured in three circumstances: * * when called from a system process * when the caller holds the permission {@code android.permission.ACCESS_INSTANT_APPS} * when resolution occurs to start an activity with a {@code android.intent.action.VIEW} * action and a {@code android.intent.category.BROWSABLE} category * */ int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid) { return updateFlagsForResolve(flags, userId, intent, callingUid, false /*wantInstantApps*/, false /*onlyExposedExplicitly*/); } int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid, boolean wantInstantApps) { return updateFlagsForResolve(flags, userId, intent, callingUid, wantInstantApps, false /*onlyExposedExplicitly*/); } int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid, boolean wantInstantApps, boolean onlyExposedExplicitly) { // Safe mode means we shouldn't match any third-party components if (mSafeMode) { flags |= PackageManager.MATCH_SYSTEM_ONLY; } if (getInstantAppPackageName(callingUid) != null) { // But, ephemeral apps see both ephemeral and exposed, non-ephemeral components if (onlyExposedExplicitly) { flags |= PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY; } flags |= PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY; flags |= PackageManager.MATCH_INSTANT; } else { final boolean wantMatchInstant = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean allowMatchInstant = wantInstantApps || (wantMatchInstant && canViewInstantApps(callingUid, userId)); flags &= ~(PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY | PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY); if (!allowMatchInstant) { flags &= ~PackageManager.MATCH_INSTANT; } } return updateFlagsForComponent(flags, userId, intent /*cookie*/); } @Override public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId); } /** * Important: The provided filterCallingUid is used exclusively to filter out activities * that can be seen based on user state. It's typically the original caller uid prior * to clearing. Because it can only be provided by trusted code, it's value can be * trusted and will be used as-is; unlike userId which will be validated by this method. */ private ActivityInfo getActivityInfoInternal(ComponentName component, int flags, int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId, component); if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get activity info"); } synchronized (mPackages) { PackageParser.Activity a = mComponentResolver.getActivity(component); if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a); if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) { return null; } return PackageParser.generateActivityInfo( a, flags, ps.readUserState(userId), userId); } if (mResolveComponentName.equals(component)) { return PackageParser.generateActivityInfo( mResolveActivity, flags, new PackageUserState(), userId); } } return null; } private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) { if (!getActivityTaskManagerInternal().isCallerRecents(callingUid)) { return false; } final long token = Binder.clearCallingIdentity(); try { final int callingUserId = UserHandle.getUserId(callingUid); if (ActivityManager.getCurrentUser() != callingUserId) { return false; } return sUserManager.isSameProfileGroup(callingUserId, targetUserId); } finally { Binder.restoreCallingIdentity(token); } } @Override public boolean activitySupportsIntent(ComponentName component, Intent intent, String resolvedType) { synchronized (mPackages) { if (component.equals(mResolveComponentName)) { // The resolver supports EVERYTHING! return true; } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); PackageParser.Activity a = mComponentResolver.getActivity(component); if (a == null) { return false; } PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) { return false; } if (filterAppAccessLPr(ps, callingUid, component, TYPE_ACTIVITY, callingUserId)) { return false; } for (int i=0; i= 0) { return true; } } return false; } } @Override public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get receiver info"); synchronized (mPackages) { PackageParser.Activity a = mComponentResolver.getReceiver(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 || mContext.checkCallingOrSelfPermission( Manifest.permission.ACCESS_SHARED_LIBRARIES) == PERMISSION_GRANTED; synchronized (mPackages) { List result = null; final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { LongSparseArray versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } final int versionCount = versionedLib.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libInfo = versionedLib.valueAt(j); 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.getPath(), libInfo.getPackageName(), libInfo.getAllCodePaths(), libInfo.getName(), libInfo.getLongVersion(), libInfo.getType(), libInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr(libInfo, flags, userId), (libInfo.getDependencies() == null ? null : new ArrayList<>(libInfo.getDependencies()))); if (result == null) { result = new ArrayList<>(); } result.add(resLibInfo); } } return result != null ? new ParceledListSlice<>(result) : null; } } @Nullable @Override public ParceledListSlice getDeclaredSharedLibraries( @NonNull String packageName, int flags, @NonNull int userId) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES, "getDeclaredSharedLibraries"); int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "getDeclaredSharedLibraries"); Preconditions.checkNotNull(packageName, "packageName cannot be null"); Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0"); if (!sUserManager.exists(userId)) { return null; } if (getInstantAppPackageName(callingUid) != null) { return null; } synchronized (mPackages) { List result = null; int libraryCount = mSharedLibraries.size(); for (int i = 0; i < libraryCount; i++) { LongSparseArray versionedLibrary = mSharedLibraries.valueAt(i); if (versionedLibrary == null) { continue; } int versionCount = versionedLibrary.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libraryInfo = versionedLibrary.valueAt(j); VersionedPackage declaringPackage = libraryInfo.getDeclaringPackage(); if (!Objects.equals(declaringPackage.getPackageName(), packageName)) { continue; } long identity = Binder.clearCallingIdentity(); try { PackageInfo packageInfo = getPackageInfoVersioned(declaringPackage, flags | PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId); if (packageInfo == null) { continue; } } finally { Binder.restoreCallingIdentity(identity); } SharedLibraryInfo resultLibraryInfo = new SharedLibraryInfo( libraryInfo.getPath(), libraryInfo.getPackageName(), libraryInfo.getAllCodePaths(), libraryInfo.getName(), libraryInfo.getLongVersion(), libraryInfo.getType(), libraryInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr( libraryInfo, flags, userId), libraryInfo.getDependencies() == null ? null : new ArrayList<>(libraryInfo.getDependencies())); if (result == null) { result = new ArrayList<>(); } result.add(resultLibraryInfo); } } return result != null ? new ParceledListSlice<>(result) : null; } } @GuardedBy("mPackages") 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.readUserState(userId).isAvailable(flags)) { continue; } final String libName = libInfo.getName(); if (libInfo.isStatic()) { final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName); if (libIdx < 0) { continue; } if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getLongVersion()) { continue; } if (versionedPackages == null) { versionedPackages = new ArrayList<>(); } // If the dependent is a static shared lib, use the public package name String dependentPackageName = ps.name; if (ps.pkg != null && ps.pkg.applicationInfo.isStaticSharedLibrary()) { dependentPackageName = ps.pkg.manifestPackageName; } versionedPackages.add(new VersionedPackage(dependentPackageName, ps.versionCode)); } else if (ps.pkg != null) { if (ArrayUtils.contains(ps.pkg.usesLibraries, libName) || ArrayUtils.contains(ps.pkg.usesOptionalLibraries, libName)) { if (versionedPackages == null) { versionedPackages = new ArrayList<>(); } versionedPackages.add(new VersionedPackage(ps.name, ps.versionCode)); } } } return versionedPackages; } @Override public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get service info"); synchronized (mPackages) { PackageParser.Service s = mComponentResolver.getService(component); if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getServiceInfo " + component + ": " + s); if (s != null && mSettings.isEnabledAndMatchLPr(s.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, callingUid, component, TYPE_SERVICE, userId)) { return null; } return PackageParser.generateServiceInfo( s, flags, ps.readUserState(userId), userId); } } return null; } @Override public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get provider info"); synchronized (mPackages) { PackageParser.Provider p = mComponentResolver.getProvider(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 ModuleInfo getModuleInfo(String packageName, @ModuleInfoFlags int flags) { return mModuleInfoProvider.getModuleInfo(packageName, flags); } @Override public List getInstalledModules(int flags) { return mModuleInfoProvider.getInstalledModules(flags); } @Override public String[] getSystemSharedLibraryNames() { // allow instant applications synchronized (mPackages) { Set libs = null; final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { LongSparseArray versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } final int versionCount = versionedLib.size(); for (int j = 0; j < versionCount; j++) { SharedLibraryInfo libraryInfo = versionedLib.valueAt(j); if (!libraryInfo.isStatic()) { if (libs == null) { libs = new ArraySet<>(); } libs.add(libraryInfo.getName()); break; } PackageSetting ps = mSettings.getPackageLPr(libraryInfo.getPackageName()); if (ps != null && !filterSharedLibPackageLPr(ps, Binder.getCallingUid(), UserHandle.getUserId(Binder.getCallingUid()), PackageManager.MATCH_STATIC_SHARED_LIBRARIES)) { if (libs == null) { libs = new ArraySet<>(); } libs.add(libraryInfo.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; } } @GuardedBy("mPackages") 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) { final CheckPermissionDelegate checkPermissionDelegate; synchronized (mPackages) { if (mCheckPermissionDelegate == null) { return checkPermissionImpl(permName, pkgName, userId); } checkPermissionDelegate = mCheckPermissionDelegate; } return checkPermissionDelegate.checkPermission(permName, pkgName, userId, PackageManagerService.this::checkPermissionImpl); } private int checkPermissionImpl(String permName, String pkgName, int userId) { return mPermissionManager.checkPermission(permName, pkgName, getCallingUid(), userId); } @Override public int checkUidPermission(String permName, int uid) { final CheckPermissionDelegate checkPermissionDelegate; synchronized (mPackages) { if (mCheckPermissionDelegate == null) { return checkUidPermissionImpl(permName, uid); } checkPermissionDelegate = mCheckPermissionDelegate; } return checkPermissionDelegate.checkUidPermission(permName, uid, PackageManagerService.this::checkUidPermissionImpl); } private int checkUidPermissionImpl(String permName, int uid) { synchronized (mPackages) { final String[] packageNames = getPackagesForUid(uid); PackageParser.Package pkg = null; final int N = packageNames == null ? 0 : packageNames.length; for (int i = 0; pkg == null && i < N; i++) { pkg = mPackages.get(packageNames[i]); } return mPermissionManager.checkUidPermission(permName, pkg, uid, getCallingUid()); } } @Override public boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId) { if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "isPermissionRevokedByPolicy for user " + userId); } if (checkPermission(permission, packageName, userId) == PackageManager.PERMISSION_GRANTED) { return false; } final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { if (!isCallerSameApp(packageName, callingUid)) { return false; } } else { if (isInstantApp(packageName, userId)) { return false; } } final long identity = Binder.clearCallingIdentity(); try { final int flags = getPermissionFlags(permission, packageName, userId); return (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0; } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getPermissionControllerPackageName() { synchronized (mPackages) { return mRequiredPermissionControllerPackage; } } String getPackageInstallerPackageName() { synchronized (mPackages) { return mRequiredInstallerPackage; } } private boolean addDynamicPermission(PermissionInfo info, final boolean async) { return mPermissionManager.addDynamicPermission( info, async, getCallingUid(), new PermissionCallback() { @Override public void onPermissionChanged() { if (!async) { mSettings.writeLPr(); } else { scheduleWriteSettingsLocked(); } } }); } @Override public boolean addPermission(PermissionInfo info) { synchronized (mPackages) { return addDynamicPermission(info, false); } } @Override public boolean addPermissionAsync(PermissionInfo info) { synchronized (mPackages) { return addDynamicPermission(info, true); } } @Override public void removePermission(String permName) { mPermissionManager.removeDynamicPermission(permName, getCallingUid(), mPermissionCallback); } @Override public void grantRuntimePermission(String packageName, String permName, final int userId) { boolean overridePolicy = (checkUidPermission( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED); mPermissionManager.grantRuntimePermission(permName, packageName, overridePolicy, getCallingUid(), userId, mPermissionCallback); } @Override public void revokeRuntimePermission(String packageName, String permName, int userId) { boolean overridePolicy = (checkUidPermission( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED); mPermissionManager.revokeRuntimePermission(permName, packageName, overridePolicy, userId, mPermissionCallback); } @Override public void resetRuntimePermissions() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, "revokeRuntimePermission"); int callingUid = Binder.getCallingUid(); if (callingUid != Process.SYSTEM_UID && callingUid != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "resetRuntimePermissions"); } synchronized (mPackages) { mPermissionManager.updateAllPermissions( StorageManager.UUID_PRIVATE_INTERNAL, false, mPackages.values(), mPermissionCallback); for (int userId : UserManagerService.getInstance().getUserIds()) { final int packageCount = mPackages.size(); for (int i = 0; i < packageCount; i++) { PackageParser.Package pkg = mPackages.valueAt(i); if (!(pkg.mExtras instanceof PackageSetting)) { continue; } PackageSetting ps = (PackageSetting) pkg.mExtras; resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, userId); } } } } @Override public int getPermissionFlags(String permName, String packageName, int userId) { return mPermissionManager.getPermissionFlags( permName, packageName, getCallingUid(), userId); } @Override public void updatePermissionFlags(String permName, String packageName, int flagMask, int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) { int callingUid = getCallingUid(); boolean overridePolicy = false; if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) { long callingIdentity = Binder.clearCallingIdentity(); try { if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0) { if (checkAdjustPolicyFlagPermission) { mContext.enforceCallingOrSelfPermission( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "Need " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " to change policy flags"); } else if (!hasTargetSdkInUidLowerThan(callingUid, Build.VERSION_CODES.Q)) { throw new IllegalArgumentException( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " needs " + " to be checked for packages targeting " + Build.VERSION_CODES.Q + " or later when changing policy " + "flags"); } overridePolicy = true; } } finally { Binder.restoreCallingIdentity(callingIdentity); } } mPermissionManager.updatePermissionFlags( permName, packageName, flagMask, flagValues, callingUid, userId, overridePolicy, mPermissionCallback); } /** * Update the permission flags for all packages and runtime permissions of a user in order * to allow device or profile owner to remove POLICY_FIXED. */ @Override public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) { synchronized (mPackages) { final boolean changed = mPermissionManager.updatePermissionFlagsForAllApps( flagMask, flagValues, getCallingUid(), userId, mPackages.values(), mPermissionCallback); if (changed) { mSettings.writeRuntimePermissionsForUserLPr(userId, false); } } } @Override public @Nullable List getWhitelistedRestrictedPermissions(@NonNull String packageName, @PermissionWhitelistFlags int whitelistFlags, @UserIdInt int userId) { Preconditions.checkNotNull(packageName); Preconditions.checkFlagsArgument(whitelistFlags, PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER); Preconditions.checkArgumentNonNegative(userId, null); if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS, "getWhitelistedRestrictedPermissions for user " + userId); } final PackageParser.Package pkg; synchronized (mPackages) { final PackageSetting packageSetting = mSettings.mPackages.get(packageName); if (packageSetting == null) { Slog.w(TAG, "Unknown package: " + packageName); return null; } pkg = packageSetting.pkg; final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) == PackageManager.PERMISSION_GRANTED; final PackageSetting installerPackageSetting = mSettings.mPackages.get( packageSetting.installerPackageName); final boolean isCallerInstallerOnRecord = installerPackageSetting != null && UserHandle.isSameApp(installerPackageSetting.appId, Binder.getCallingUid()); if ((whitelistFlags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 && !isCallerPrivileged) { throw new SecurityException("Querying system whitelist requires " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } if ((whitelistFlags & (PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) != 0) { if (!isCallerPrivileged && !isCallerInstallerOnRecord) { throw new SecurityException("Querying upgrade or installer whitelist" + " requires being installer on record or " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } } if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(), UserHandle.getCallingUserId())) { return null; } } final long identity = Binder.clearCallingIdentity(); try { return mPermissionManager.getWhitelistedRestrictedPermissions( pkg, whitelistFlags, userId); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean addWhitelistedRestrictedPermission(@NonNull String packageName, @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags, @UserIdInt int userId) { // Other argument checks are done in get/setWhitelistedRestrictedPermissions Preconditions.checkNotNull(permission); if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) { return false; } List permissions = getWhitelistedRestrictedPermissions(packageName, whitelistFlags, userId); if (permissions == null) { permissions = new ArrayList<>(1); } if (permissions.indexOf(permission) < 0) { permissions.add(permission); return setWhitelistedRestrictedPermissions(packageName, permissions, whitelistFlags, userId); } return false; } private boolean checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission( @NonNull String permission) { synchronized (mPackages) { final BasePermission bp = mPermissionManager.getPermissionTEMP(permission); if (bp == null) { Slog.w(TAG, "No such permissions: " + permission); return false; } if (bp.isHardOrSoftRestricted() && bp.isImmutablyRestricted() && mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Cannot modify whitelisting of an immutably " + "restricted permission: " + permission); } return true; } } @Override public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName, @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags, @UserIdInt int userId) { // Other argument checks are done in get/setWhitelistedRestrictedPermissions Preconditions.checkNotNull(permission); if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) { return false; } final List permissions = getWhitelistedRestrictedPermissions(packageName, whitelistFlags, userId); if (permissions != null && permissions.remove(permission)) { return setWhitelistedRestrictedPermissions(packageName, permissions, whitelistFlags, userId); } return false; } private boolean setWhitelistedRestrictedPermissions(@NonNull String packageName, @Nullable List permissions, @PermissionWhitelistFlags int whitelistFlag, @UserIdInt int userId) { Preconditions.checkNotNull(packageName); Preconditions.checkFlagsArgument(whitelistFlag, PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER); Preconditions.checkArgument(Integer.bitCount(whitelistFlag) == 1); Preconditions.checkArgumentNonNegative(userId, null); if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingOrSelfPermission( Manifest.permission.INTERACT_ACROSS_USERS, "setWhitelistedRestrictedPermissions for user " + userId); } final PackageParser.Package pkg; synchronized (mPackages) { final PackageSetting packageSetting = mSettings.mPackages.get(packageName); if (packageSetting == null) { Slog.w(TAG, "Unknown package: " + packageName); return false; } pkg = packageSetting.pkg; final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) == PackageManager.PERMISSION_GRANTED; final PackageSetting installerPackageSetting = mSettings.mPackages.get( packageSetting.installerPackageName); final boolean isCallerInstallerOnRecord = installerPackageSetting != null && UserHandle.isSameApp(installerPackageSetting.appId, Binder.getCallingUid()); if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 && !isCallerPrivileged) { throw new SecurityException("Modifying system whitelist requires " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) { if (!isCallerPrivileged && !isCallerInstallerOnRecord) { throw new SecurityException("Modifying upgrade whitelist requires" + " being installer on record or " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } final List whitelistedPermissions = getWhitelistedRestrictedPermissions( packageName, whitelistFlag, userId); if (permissions == null || permissions.isEmpty()) { if (whitelistedPermissions == null || whitelistedPermissions.isEmpty()) { return true; } } else { // Only the system can add and remove while the installer can only remove. final int permissionCount = permissions.size(); for (int i = 0; i < permissionCount; i++) { if ((whitelistedPermissions == null || !whitelistedPermissions.contains(permissions.get(i))) && !isCallerPrivileged) { throw new SecurityException("Adding to upgrade whitelist requires" + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } } } } if ((whitelistFlag & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) { if (!isCallerPrivileged && !isCallerInstallerOnRecord) { throw new SecurityException("Modifying installer whitelist requires" + " being installer on record or " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } } if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(), UserHandle.getCallingUserId())) { return false; } } final long identity = Binder.clearCallingIdentity(); try { mPermissionManager.setWhitelistedRestrictedPermissions(pkg, new int[]{userId}, permissions, Process.myUid(), whitelistFlag, mPermissionCallback); } finally { Binder.restoreCallingIdentity(identity); } return true; } @Override public boolean shouldShowRequestPermissionRationale(String permissionName, String packageName, int userId) { if (UserHandle.getCallingUserId() != userId) { mContext.enforceCallingPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "canShowRequestPermissionRationale for user " + userId); } final int uid = getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId); if (UserHandle.getAppId(getCallingUid()) != UserHandle.getAppId(uid)) { return false; } if (checkPermission(permissionName, packageName, userId) == PackageManager.PERMISSION_GRANTED) { return false; } final int flags; final long identity = Binder.clearCallingIdentity(); try { flags = getPermissionFlags(permissionName, packageName, userId); } finally { Binder.restoreCallingIdentity(identity); } final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED | PackageManager.FLAG_PERMISSION_POLICY_FIXED | PackageManager.FLAG_PERMISSION_USER_FIXED; if ((flags & fixedFlags) != 0) { return false; } return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; } @Override public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { mContext.enforceCallingOrSelfPermission( Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS, "addOnPermissionsChangeListener"); synchronized (mPackages) { mOnPermissionChangeListeners.addListenerLocked(listener); } } @Override public void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { throw new SecurityException("Instant applications don't have access to this method"); } synchronized (mPackages) { mOnPermissionChangeListeners.removeListenerLocked(listener); } } @Override public boolean isProtectedBroadcast(String actionName) { // allow instant applications synchronized (mProtectedBroadcasts) { if (mProtectedBroadcasts.contains(actionName)) { return true; } else if (actionName != null) { // TODO: remove these terrible hacks if (actionName.startsWith("android.net.netmon.lingerExpired") || actionName.startsWith("com.android.server.sip.SipWakeupTimer") || actionName.startsWith("com.android.internal.telephony.data-reconnect") || actionName.startsWith("android.net.netmon.launchCaptivePortalApp")) { return true; } } } return false; } @Override public int checkSignatures(String pkg1, String pkg2) { synchronized (mPackages) { final PackageParser.Package p1 = mPackages.get(pkg1); final PackageParser.Package p2 = mPackages.get(pkg2); if (p1 == null || p1.mExtras == null || p2 == null || p2.mExtras == null) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps1 = (PackageSetting) p1.mExtras; final PackageSetting ps2 = (PackageSetting) p2.mExtras; if (filterAppAccessLPr(ps1, callingUid, callingUserId) || filterAppAccessLPr(ps2, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } return compareSignatures(p1.mSigningDetails.signatures, p2.mSigningDetails.signatures); } } @Override public int checkUidSignatures(int uid1, int uid2) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; // Map to base uids. final int appId1 = UserHandle.getAppId(uid1); final int appId2 = UserHandle.getAppId(uid2); // reader synchronized (mPackages) { Signature[] s1; Signature[] s2; Object obj = mSettings.getSettingLPr(appId1); if (obj != null) { if (obj instanceof SharedUserSetting) { if (isCallerInstantApp) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s1 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s1 = ps.signatures.mSigningDetails.signatures; } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } obj = mSettings.getSettingLPr(appId2); if (obj != null) { if (obj instanceof SharedUserSetting) { if (isCallerInstantApp) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s2 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s2 = ps.signatures.mSigningDetails.signatures; } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } } else { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } return compareSignatures(s1, s2); } } @Override public boolean hasSigningCertificate( String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) { synchronized (mPackages) { final PackageParser.Package p = mPackages.get(packageName); if (p == null || p.mExtras == null) { return false; } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = (PackageSetting) p.mExtras; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return false; } switch (type) { case CERT_INPUT_RAW_X509: return p.mSigningDetails.hasCertificate(certificate); case CERT_INPUT_SHA256: return p.mSigningDetails.hasSha256Certificate(certificate); default: return false; } } } @Override public boolean hasUidSigningCertificate( int uid, byte[] certificate, @PackageManager.CertificateInputType int type) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); // Map to base uids. final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final PackageParser.SigningDetails signingDetails; final Object obj = mSettings.getSettingLPr(appId); if (obj != null) { if (obj instanceof SharedUserSetting) { final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; if (isCallerInstantApp) { return false; } signingDetails = ((SharedUserSetting)obj).signatures.mSigningDetails; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return false; } signingDetails = ps.signatures.mSigningDetails; } else { return false; } } else { return false; } switch (type) { case CERT_INPUT_RAW_X509: return signingDetails.hasCertificate(certificate); case CERT_INPUT_SHA256: return signingDetails.hasSha256Certificate(certificate); default: return false; } } } /** * This method should typically only be used when granting or revoking * permissions, since the app may immediately restart after this call. * * If you're doing surgery on app code/data, use {@link PackageFreezer} to * guard your work against the app being relaunched. */ private void killUid(int appId, int userId, String reason) { final long identity = Binder.clearCallingIdentity(); try { IActivityManager am = ActivityManager.getService(); if (am != null) { try { am.killUid(appId, userId, reason); } catch (RemoteException e) { /* ignore - same process */ } } } finally { Binder.restoreCallingIdentity(identity); } } /** * If the database version for this type of package (internal storage or * external storage) is less than the version where package signatures * were updated, return true. */ private boolean isCompatSignatureUpdateNeeded(PackageParser.Package scannedPkg) { return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg)); } private static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) { return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY; } private boolean isRecoverSignatureUpdateNeeded(PackageParser.Package scannedPkg) { return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg)); } private static boolean isRecoverSignatureUpdateNeeded(VersionInfo ver) { return ver.databaseVersion < DatabaseVersion.SIGNATURE_MALFORMED_RECOVER; } @Override public List getAllPackages() { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); synchronized (mPackages) { if (canViewInstantApps(callingUid, callingUserId)) { return new ArrayList<>(mPackages.keySet()); } final String instantAppPkgName = getInstantAppPackageName(callingUid); final List result = new ArrayList<>(); if (instantAppPkgName != null) { // caller is an instant application; filter unexposed applications for (PackageParser.Package pkg : mPackages.values()) { if (!pkg.visibleToInstantApps) { continue; } result.add(pkg.packageName); } } else { // caller is a normal application; filter instant applications for (PackageParser.Package pkg : mPackages.values()) { final PackageSetting ps = pkg.mExtras != null ? (PackageSetting) pkg.mExtras : null; if (ps != null && ps.getInstantApp(callingUserId) && !mInstantAppRegistry.isInstantAccessGranted( callingUserId, UserHandle.getAppId(callingUid), ps.appId)) { continue; } result.add(pkg.packageName); } } return result; } } /** * IMPORTANT: Not all packages returned by this method may be known * to the system. There are two conditions in which this may occur: * * The package is on adoptable storage and the device has been removed * The package is being removed and the internal structures are partially updated * * The second is an artifact of the current data structures and should be fixed. See * b/111075456 for one such instance. */ @Override public String[] getPackagesForUid(int uid) { final int callingUid = Binder.getCallingUid(); final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; final int userId = UserHandle.getUserId(uid); final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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 appId = UserHandle.getAppId(uids[i]); final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; final Iterator it = sus.packages.iterator(); while (it.hasNext()) { if (it.next().isPrivileged()) { return true; } } } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; return ps.isPrivileged(); } } return false; } @Override public String[] getAppOpPermissionPackages(String permName) { return mPermissionManager.getAppOpPermissionPackages(permName); } @Override public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { return resolveIntentInternal(intent, resolvedType, flags, userId, false, Binder.getCallingUid()); } /** * Normally instant apps can only be resolved when they're visible to the caller. * However, if {@code resolveForStart} is {@code true}, all instant apps are visible * since we need to allow the system to start any installed application. */ private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType, int flags, int userId, boolean resolveForStart, int filterCallingUid) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent"); if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "resolve intent"); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, filterCallingUid, userId, resolveForStart, true /*allowDynamicSplits*/); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); final ResolveInfo bestChoice = chooseBestActivity(intent, resolvedType, flags, query, userId); return bestChoice; } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @Override public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) { if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) { throw new SecurityException( "findPersistentPreferredActivity can only be run by the system"); } if (!sUserManager.exists(userId)) { return null; } final int callingUid = Binder.getCallingUid(); intent = updateIntentForResolve(intent); final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver()); final int flags = updateFlagsForResolve( 0, userId, intent, callingUid, false /*includeInstantApps*/); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); synchronized (mPackages) { return findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false, userId); } } @Override public void setLastChosenActivity(Intent intent, String resolvedType, int flags, IntentFilter filter, int match, ComponentName activity) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; } final int userId = UserHandle.getCallingUserId(); if (DEBUG_PREFERRED) { Log.v(TAG, "setLastChosenActivity intent=" + intent + " resolvedType=" + resolvedType + " flags=" + flags + " filter=" + filter + " match=" + match + " activity=" + activity); filter.dump(new PrintStreamPrinter(System.out), " "); } intent.setComponent(null); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); // Find any earlier preferred or last chosen entries and nuke them findPreferredActivityNotLocked( 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 findPreferredActivityNotLocked( intent, resolvedType, flags, query, 0, false, false, false, userId); } /** * Returns whether or not instant apps have been disabled remotely. */ private boolean areWebInstantAppsDisabled(int userId) { return mWebInstantAppsDisabled.get(userId); } private boolean isInstantAppResolutionAllowed( Intent intent, List resolvedActivities, int userId, boolean skipPackageCheck) { if (mInstantAppResolverConnection == null) { return false; } if (mInstantAppInstallerActivity == null) { return false; } if (intent.getComponent() != null) { return false; } if ((intent.getFlags() & Intent.FLAG_IGNORE_EPHEMERAL) != 0) { return false; } if (!skipPackageCheck && intent.getPackage() != null) { return false; } if (!intent.isWebIntent()) { // for non web intents, we should not resolve externally if an app already exists to // handle it or if the caller didn't explicitly request it. if ((resolvedActivities != null && resolvedActivities.size() != 0) || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) { return false; } } else { if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) { return false; } else if (areWebInstantAppsDisabled(userId)) { return false; } } // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. // Or if there's already an ephemeral app installed that handles the action synchronized (mPackages) { final int count = (resolvedActivities == null ? 0 : resolvedActivities.size()); for (int n = 0; n < count; n++) { final ResolveInfo info = resolvedActivities.get(n); final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { // only check domain verification status if the app is not a browser if (!info.handleAllWebDataURI) { // Try to get the status from User settings first final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int) (packedStatus >> 32); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName + ", status: " + status); } return false; } } if (ps.getInstantApp(userId)) { if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app installed;" + " pkg: " + packageName); } return false; } } } } // We've exhausted all ways to deny ephemeral application; let the system look for them. return true; } private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj, Intent origIntent, String resolvedType, String callingPackage, Bundle verificationBundle, int userId) { final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO, new InstantAppRequest(responseObj, origIntent, resolvedType, callingPackage, userId, verificationBundle, false /*resolveForStart*/)); mHandler.sendMessage(msg); } private ResolveInfo chooseBestActivity(Intent intent, String resolvedType, int flags, List query, int userId) { if (query != null) { final int N = query.size(); if (N == 1) { return query.get(0); } else if (N > 1) { final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); // If there is more than one activity with the same priority, // then let the user decide between them. ResolveInfo r0 = query.get(0); ResolveInfo r1 = query.get(1); if (DEBUG_INTENT_MATCHING || debug) { Slog.v(TAG, r0.activityInfo.name + "=" + r0.priority + " vs " + r1.activityInfo.name + "=" + r1.priority); } // If the first activity has a higher priority, or a different // default, then it is always desirable to pick it. if (r0.priority != r1.priority || r0.preferredOrder != r1.preferredOrder || r0.isDefault != r1.isDefault) { return query.get(0); } // If we have saved a preference for a preferred activity for // this Intent, use that. ResolveInfo ri = findPreferredActivityNotLocked(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; } @GuardedBy("mPackages") 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; jmust not hold {@link #mPackages} */ ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags, List query, int priority, boolean always, boolean removeMatches, boolean debug, int userId) { if (Thread.holdsLock(mPackages)) { Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding mPackages", new Throwable()); } if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); // Do NOT hold the packages lock; this calls up into the settings provider which // could cause a deadlock. final boolean isDeviceProvisioned = android.provider.Settings.Global.getInt(mContext.getContentResolver(), android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1; 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"); } } final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent) && !isDeviceProvisioned; if (ai == null) { // Do not remove launcher's preferred activity during SetupWizard // due to it may not install yet if (excludeSetupWizardHomeActivity) { continue; } // This previously registered preferred activity // component is no longer known. Most likely an update // to the app was installed and in the new version this // component no longer exists. Clean it up by removing // it from the preferred activities list, and skip it. Slog.w(TAG, "Removing dangling preferred activity: " + pa.mPref.mComponent); pir.removeFilter(pa); changed = true; continue; } for (int j=0; j matches = getMatchingCrossProfileIntentFilters(intent, resolvedType, sourceUserId); if (matches != null) { int size = matches.size(); for (int i = 0; i < size; i++) { if (matches.get(i).getTargetUserId() == targetUserId) return true; } } if (intent.hasWebURI()) { // cross-profile app linking works only towards the parent. final int callingUid = Binder.getCallingUid(); final UserInfo parent = getProfileParent(sourceUserId); synchronized(mPackages) { int flags = updateFlagsForResolve(0, parent.id, intent, callingUid, false /*includeInstantApps*/); CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr( intent, resolvedType, flags, sourceUserId, parent.id); return xpDomainInfo != null; } } return false; } private UserInfo getProfileParent(int userId) { final long identity = Binder.clearCallingIdentity(); try { return sUserManager.getProfileParent(userId); } finally { Binder.restoreCallingIdentity(identity); } } private List getMatchingCrossProfileIntentFilters(Intent intent, String resolvedType, int userId) { CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId); if (resolver != null) { return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId); } return null; } @Override public @NonNull ParceledListSlice queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); return new ParceledListSlice<>( queryIntentActivitiesInternal(intent, resolvedType, flags, userId)); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } /** * Returns the package name of the calling Uid if it's an instant app. If it isn't * instant, returns {@code null}. */ private String getInstantAppPackageName(int callingUid) { synchronized (mPackages) { // If the caller is an isolated app use the owner's uid for the lookup. if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final int appId = UserHandle.getAppId(callingUid); final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid)); return isInstantApp ? ps.pkg.packageName : null; } } return null; } private @NonNull List queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int userId) { return queryIntentActivitiesInternal( intent, resolvedType, flags, Binder.getCallingUid(), userId, false /*resolveForStart*/, true /*allowDynamicSplits*/); } private @NonNull List queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final String instantAppPkgName = getInstantAppPackageName(filterCallingUid); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "query intent activities"); final String pkgName = intent.getPackage(); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart, comp != null || pkgName != null /*onlyExposedExplicitly*/); if (comp != null) { final List list = new ArrayList<>(1); final ActivityInfo ai = getActivityInfo(comp, flags, userId); if (ai != null) { // When specifying an explicit component, we prevent the activity from being // used when either 1) the calling package is normal and the activity is within // an ephemeral application or 2) the calling package is ephemeral and the // activity is not visible to ephemeral applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean matchExplicitlyVisibleOnly = (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (ai.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetVisibleToInstantApp = (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean isTargetExplicitlyVisibleToInstantApp = isTargetVisibleToInstantApp && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; final boolean isTargetHiddenFromInstantApp = !isTargetVisibleToInstantApp || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp); final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } } return applyPostResolutionFilter( list, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // reader boolean sortResult = false; boolean addInstant = false; List result; synchronized (mPackages) { if (pkgName == null) { List matchingFilters = getMatchingCrossProfileIntentFilters(intent, resolvedType, userId); // Check for results that need to skip the current profile. ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent, resolvedType, flags, userId); if (xpResolveInfo != null) { List xpResult = new ArrayList<>(1); xpResult.add(xpResolveInfo); return applyPostResolutionFilter( filterIfNotSystemUser(xpResult, userId), instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // Check for results in the current profile. result = filterIfNotSystemUser(mComponentResolver.queryActivities( intent, resolvedType, flags, userId), userId); addInstant = isInstantAppResolutionAllowed(intent, result, userId, false /*skipPackageCheck*/); // Check for cross profile results. boolean hasNonNegativePriorityResult = hasNonNegativePriority(result); xpResolveInfo = queryCrossProfileIntents( matchingFilters, intent, resolvedType, flags, userId, hasNonNegativePriorityResult); if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) { boolean isVisibleToUser = filterIfNotSystemUser( Collections.singletonList(xpResolveInfo), userId).size() > 0; if (isVisibleToUser) { result.add(xpResolveInfo); sortResult = true; } } if (intent.hasWebURI()) { CrossProfileDomainInfo xpDomainInfo = null; final UserInfo parent = getProfileParent(userId); if (parent != null) { xpDomainInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType, flags, userId, parent.id); } if (xpDomainInfo != null) { if (xpResolveInfo != null) { // If we didn't remove it, the cross-profile ResolveInfo would be twice // in the result. result.remove(xpResolveInfo); } if (result.size() == 0 && !addInstant) { // No result in current profile, but found candidate in parent user. // And we are not going to add emphemeral app, so we can return the // result straight away. result.add(xpDomainInfo.resolveInfo); return applyPostResolutionFilter(result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } } else if (result.size() <= 1 && !addInstant) { // No result in parent user and <= 1 result in current profile, and we // are not going to add emphemeral app, so we can return the result without // further processing. return applyPostResolutionFilter(result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // We have more than one candidate (combining results from current and parent // profile), so we need filtering and sorting. result = filterCandidatesWithDomainPreferredActivitiesLPr( intent, flags, result, xpDomainInfo, userId); sortResult = true; } } else { final PackageParser.Package pkg = mPackages.get(pkgName); result = null; if (pkg != null) { result = filterIfNotSystemUser(mComponentResolver.queryActivities( intent, resolvedType, flags, pkg.activities, userId), userId); } if (result == null || result.size() == 0) { // the caller wants to resolve for a particular package; however, there // were no installed results, so, try to find an ephemeral result addInstant = isInstantAppResolutionAllowed( intent, null /*result*/, userId, true /*skipPackageCheck*/); if (result == null) { result = new ArrayList<>(); } } } } if (addInstant) { result = maybeAddInstantAppInstaller( result, intent, resolvedType, flags, userId, resolveForStart); } if (sortResult) { Collections.sort(result, RESOLVE_PRIORITY_SORTER); } return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } private List maybeAddInstantAppInstaller(List result, Intent intent, String resolvedType, int flags, int userId, boolean resolveForStart) { // first, check to see if we've got an instant app already installed final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0; ResolveInfo localInstantApp = null; boolean blockResolution = false; if (!alreadyResolvedLocally) { final List instantApps = mComponentResolver.queryActivities( intent, resolvedType, flags | PackageManager.GET_RESOLVED_FILTER | PackageManager.MATCH_INSTANT | PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY, userId); for (int i = instantApps.size() - 1; i >= 0; --i) { final ResolveInfo info = instantApps.get(i); final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps.getInstantApp(userId)) { final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int)(packedStatus >> 32); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { // there's a local instant application installed, but, the user has // chosen to never use it; skip resolution and don't acknowledge // an instant application is even available if (DEBUG_INSTANT) { Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName); } blockResolution = true; break; } else { // we have a locally installed instant application; skip resolution // but acknowledge there's an instant application available if (DEBUG_INSTANT) { Slog.v(TAG, "Found installed instant app; pkg: " + packageName); } localInstantApp = info; break; } } } } // no app installed, let's see if one's available AuxiliaryResolveInfo auxiliaryResponse = null; if (!blockResolution) { if (localInstantApp == null) { // we don't have an instant app locally, resolve externally Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral"); final InstantAppRequest requestObject = new InstantAppRequest( null /*responseObj*/, intent /*origIntent*/, resolvedType, null /*callingPackage*/, userId, null /*verificationBundle*/, resolveForStart); auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne( mInstantAppResolverConnection, requestObject); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } else { // we have an instant application locally, but, we can't admit that since // callers shouldn't be able to determine prior browsing. create a dummy // auxiliary response so the downstream code behaves as if there's an // instant application available externally. when it comes time to start // the instant application, we'll do the right thing. final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo; auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */, ai.packageName, ai.longVersionCode, null /* splitName */); } } if (intent.isWebIntent() && auxiliaryResponse == null) { return result; } final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName); if (ps == null || !ps.readUserState(userId).isEnabled(mInstantAppInstallerActivity, 0)) { return result; } final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo); ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo( mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId); ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART | IntentFilter.MATCH_ADJUSTMENT_NORMAL; // add a non-generic filter ephemeralInstaller.filter = new IntentFilter(); if (intent.getAction() != null) { ephemeralInstaller.filter.addAction(intent.getAction()); } if (intent.getData() != null && intent.getData().getPath() != null) { ephemeralInstaller.filter.addDataPath( intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL); } ephemeralInstaller.isInstantAppAvailable = true; // make sure this resolver is the default ephemeralInstaller.isDefault = true; ephemeralInstaller.auxiliaryInfo = auxiliaryResponse; if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } result.add(ephemeralInstaller); return result; } private static class CrossProfileDomainInfo { /* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */ ResolveInfo resolveInfo; /* Best domain verification status of the activities found in the other profile */ int bestDomainVerificationStatus; } private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType, int flags, int sourceUserId, int parentUserId) { if (!sUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, sourceUserId)) { return null; } List resultTargetUser = mComponentResolver.queryActivities(intent, resolvedType, flags, parentUserId); if (resultTargetUser == null || resultTargetUser.isEmpty()) { return null; } CrossProfileDomainInfo result = null; int size = resultTargetUser.size(); for (int i = 0; i < size; i++) { ResolveInfo riTargetUser = resultTargetUser.get(i); // Intent filter verification is only for filters that specify a host. So don't return // those that handle all web uris. if (riTargetUser.handleAllWebDataURI) { continue; } String packageName = riTargetUser.activityInfo.packageName; PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) { continue; } long verificationState = getDomainVerificationStatusLPr(ps, parentUserId); int status = (int)(verificationState >> 32); if (result == null) { result = new CrossProfileDomainInfo(); result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(), sourceUserId, parentUserId); result.bestDomainVerificationStatus = status; } else { result.bestDomainVerificationStatus = bestDomainVerificationStatus(status, result.bestDomainVerificationStatus); } } // Don't consider matches with status NEVER across profiles. if (result != null && result.bestDomainVerificationStatus == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return null; } return result; } /** * Verification statuses are ordered from the worse to the best, except for * INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse. */ private int bestDomainVerificationStatus(int status1, int status2) { if (status1 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return status2; } if (status2 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return status1; } return (int) MathUtils.max(status1, status2); } private boolean isUserEnabled(int userId) { long callingId = Binder.clearCallingIdentity(); try { UserInfo userInfo = sUserManager.getUserInfo(userId); return userInfo != null && userInfo.isEnabled(); } finally { Binder.restoreCallingIdentity(callingId); } } /** * Filter out activities with systemUserOnly flag set, when current user is not System. * * @return filtered list */ private List filterIfNotSystemUser(List resolveInfos, int userId) { if (userId == UserHandle.USER_SYSTEM) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { ResolveInfo info = resolveInfos.get(i); if ((info.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) { resolveInfos.remove(i); } } return resolveInfos; } /** * Filters out ephemeral activities. * When resolving for an ephemeral app, only activities that 1) are defined in the * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned. * * @param resolveInfos The pre-filtered list of resolved activities * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering * is performed. * @param intent * @return A filtered list of resolved activities. */ private List applyPostResolutionFilter(List resolveInfos, String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, boolean resolveForStart, int userId, Intent intent) { final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId); for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); // remove locally resolved instant app web results when disabled if (info.isInstantAppAvailable && blockInstant) { resolveInfos.remove(i); continue; } // allow activities that are defined in the provided package if (allowDynamicSplits && info.activityInfo != null && info.activityInfo.splitName != null && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames, info.activityInfo.splitName)) { if (mInstantAppInstallerActivity == null) { if (DEBUG_INSTALL) { Slog.v(TAG, "No installer - not adding it to the ResolveInfo list"); } resolveInfos.remove(i); continue; } if (blockInstant && isInstantApp(info.activityInfo.packageName, userId)) { resolveInfos.remove(i); continue; } // requested activity is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTALL) { Slog.v(TAG, "Adding installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); final ComponentName installFailureActivity = findInstallFailureActivity( info.activityInfo.packageName, filterCallingUid, userId); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( installFailureActivity, info.activityInfo.packageName, info.activityInfo.applicationInfo.longVersionCode, info.activityInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // This resolve info may appear in the chooser UI, so let us make it // look as the one it replaces as far as the user is concerned which // requires loading the correct label and icon for the resolve info. installerInfo.resolvePackageName = info.getComponentInfo().packageName; installerInfo.labelRes = info.resolveLabelResId(); installerInfo.icon = info.resolveIconResId(); installerInfo.isInstantAppAvailable = true; resolveInfos.set(i, installerInfo); continue; } // caller is a full app, don't need to apply any other filtering if (ephemeralPkgName == null) { continue; } else if (ephemeralPkgName.equals(info.activityInfo.packageName)) { // caller is same app; don't need to apply any other filtering continue; } else if (resolveForStart && (intent.isWebIntent() || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) && intent.getPackage() == null && intent.getComponent() == null) { // ephemeral apps can launch other ephemeral apps indirectly continue; } // allow activities that have been explicitly exposed to ephemeral apps final boolean isEphemeralApp = info.activityInfo.applicationInfo.isInstantApp(); if (!isEphemeralApp && ((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } /** * Returns the activity component that can handle install failures. * By default, the instant application installer handles failures. However, an * application may want to handle failures on its own. Applications do this by * creating an activity with an intent filter that handles the action * {@link Intent#ACTION_INSTALL_FAILURE}. */ private @Nullable ComponentName findInstallFailureActivity( String packageName, int filterCallingUid, int userId) { final Intent failureActivityIntent = new Intent(Intent.ACTION_INSTALL_FAILURE); failureActivityIntent.setPackage(packageName); // IMPORTANT: disallow dynamic splits to avoid an infinite loop final List result = queryIntentActivitiesInternal( failureActivityIntent, null /*resolvedType*/, 0 /*flags*/, filterCallingUid, userId, false /*resolveForStart*/, false /*allowDynamicSplits*/); final int NR = result.size(); if (NR > 0) { for (int i = 0; i < NR; i++) { final ResolveInfo info = result.get(i); if (info.activityInfo.splitName != null) { continue; } return new ComponentName(packageName, info.activityInfo.name); } } return null; } /** * @param resolveInfos list of resolve infos in descending priority order * @return if the list contains a resolve info with non-negative priority */ private boolean hasNonNegativePriority(List resolveInfos) { return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0; } private List filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent, int matchFlags, List candidates, CrossProfileDomainInfo xpDomainInfo, int userId) { final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0; if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) { Slog.v(TAG, "Filtering results with preferred activities. Candidates count: " + candidates.size()); } final ArrayList result = new ArrayList<>(); final ArrayList alwaysList = new ArrayList<>(); final ArrayList undefinedList = new ArrayList<>(); final ArrayList alwaysAskList = new ArrayList<>(); final ArrayList neverList = new ArrayList<>(); final 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 = mComponentResolver.queryActivities(intent, resolvedType, flags, targetUserId); if (resultTargetUser != null && isUserEnabled(targetUserId)) { // If all the matches in the target profile are suspended, return null. for (int i = resultTargetUser.size() - 1; i >= 0; i--) { if ((resultTargetUser.get(i).activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SUSPENDED) == 0) { return createForwardingResolveInfoUnchecked(filter, sourceUserId, targetUserId); } } } return null; } private ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter, int sourceUserId, int targetUserId) { ResolveInfo forwardingResolveInfo = new ResolveInfo(); long ident = Binder.clearCallingIdentity(); boolean targetIsProfile; try { targetIsProfile = sUserManager.getUserInfo(targetUserId).isManagedProfile(); } finally { Binder.restoreCallingIdentity(ident); } String className; if (targetIsProfile) { className = FORWARD_INTENT_TO_MANAGED_PROFILE; } else { className = FORWARD_INTENT_TO_PARENT; } ComponentName forwardingActivityComponentName = new ComponentName( mAndroidApplication.packageName, className); ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0, sourceUserId); if (!targetIsProfile) { forwardingActivityInfo.showUserIcon = targetUserId; forwardingResolveInfo.noResourceId = true; } forwardingResolveInfo.activityInfo = forwardingActivityInfo; forwardingResolveInfo.priority = 0; forwardingResolveInfo.preferredOrder = 0; forwardingResolveInfo.match = 0; forwardingResolveInfo.isDefault = true; forwardingResolveInfo.filter = filter; forwardingResolveInfo.targetUserId = targetUserId; return forwardingResolveInfo; } @Override public @NonNull ParceledListSlice queryIntentActivityOptions(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>(queryIntentActivityOptionsInternal(caller, specifics, specificTypes, intent, resolvedType, flags, userId)); } private @NonNull List queryIntentActivityOptionsInternal(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent activity options"); final String resultsAction = intent.getAction(); final List results = queryIntentActivitiesInternal(intent, resolvedType, flags | PackageManager.GET_RESOLVED_FILTER, userId); if (DEBUG_INTENT_MATCHING) { Log.v(TAG, "Query " + intent + ": " + results); } int specificsPos = 0; int N; // todo: note that the algorithm used here is O(N^2). This // isn't a problem in our current environment, but if we start running // into situations where we have more than 5 or 10 matches then this // should probably be changed to something smarter... // First we go through and resolve each of the specific items // that were supplied, taking care of removing any corresponding // duplicate items in the generic resolve list. if (specifics != null) { for (int i=0; i it = rii.filter.actionsIterator(); if (it == null) { continue; } while (it.hasNext()) { final String action = it.next(); if (resultsAction != null && resultsAction.equals(action)) { // If this action was explicitly requested, then don't // remove things that have it. continue; } for (int j=i+1; j queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>( queryIntentReceiversInternal(intent, resolvedType, flags, userId, false /*allowDynamicSplits*/)); } private @NonNull List queryIntentReceiversInternal(Intent intent, String resolvedType, int flags, int userId, boolean allowDynamicSplits) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ActivityInfo ai = getReceiverInfo(comp, flags, userId); if (ai != null) { // When specifying an explicit component, we prevent the activity from being // used when either 1) the calling package is normal and the activity is within // an instant application or 2) the calling package is ephemeral and the // activity is not visible to instant applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean matchExplicitlyVisibleOnly = (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (ai.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetVisibleToInstantApp = (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean isTargetExplicitlyVisibleToInstantApp = isTargetVisibleToInstantApp && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; final boolean isTargetHiddenFromInstantApp = !isTargetVisibleToInstantApp || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp); final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } } return applyPostResolutionFilter( list, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { final List result = mComponentResolver.queryReceivers(intent, resolvedType, flags, userId); return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { final List result = mComponentResolver.queryReceivers( intent, resolvedType, flags, pkg.receivers, userId); return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } return Collections.emptyList(); } } @Override public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) { final int callingUid = Binder.getCallingUid(); return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid); } private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForResolve( flags, userId, intent, callingUid, false /*includeInstantApps*/); List query = queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/); if (query != null) { if (query.size() >= 1) { // If there is more than one service with the same priority, // just arbitrarily pick the first one. return query.get(0); } } return null; } @Override public @NonNull ParceledListSlice queryIntentServices(Intent intent, String resolvedType, int flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>(queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/)); } private @NonNull List queryIntentServicesInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps) { if (!sUserManager.exists(userId)) return Collections.emptyList(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ServiceInfo si = getServiceInfo(comp, flags, userId); if (si != null) { // When specifying an explicit component, we prevent the service from being // used when either 1) the service is in an instant application and the // caller is not the same instant application or 2) the calling package is // ephemeral and the activity is not visible to ephemeral applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (si.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetHiddenFromInstantApp = (si.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0; final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.serviceInfo = si; list.add(ri); } } return list; } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostServiceResolutionFilter( mComponentResolver.queryServices(intent, resolvedType, flags, userId), instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return applyPostServiceResolutionFilter( mComponentResolver.queryServices(intent, resolvedType, flags, pkg.services, userId), instantAppPkgName); } return Collections.emptyList(); } } private List applyPostServiceResolutionFilter(List resolveInfos, String instantAppPkgName) { if (instantAppPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp(); // allow services that are defined in the provided package if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) { if (info.serviceInfo.splitName != null && !ArrayUtils.contains(info.serviceInfo.applicationInfo.splitNames, info.serviceInfo.splitName)) { // requested service is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( null /* installFailureActivity */, info.serviceInfo.packageName, info.serviceInfo.applicationInfo.longVersionCode, info.serviceInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // load resources from the correct package installerInfo.resolvePackageName = info.getComponentInfo().packageName; resolveInfos.set(i, installerInfo); } continue; } // allow services that have been explicitly exposed to ephemeral apps if (!isEphemeralApp && ((info.serviceInfo.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } @Override public @NonNull ParceledListSlice queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>( queryIntentContentProvidersInternal(intent, resolvedType, flags, userId)); } private @NonNull List queryIntentContentProvidersInternal( Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ProviderInfo pi = getProviderInfo(comp, flags, userId); if (pi != null) { // When specifying an explicit component, we prevent the provider from being // used when either 1) the provider is in an instant application and the // caller is not the same instant application or 2) the calling package is an // instant application and the provider is not visible to instant applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (pi.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetHiddenFromInstantApp = (pi.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0; final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.providerInfo = pi; list.add(ri); } } return list; } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostContentProviderResolutionFilter( mComponentResolver.queryProviders(intent, resolvedType, flags, userId), instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return applyPostContentProviderResolutionFilter( mComponentResolver.queryProviders(intent, resolvedType, flags, pkg.providers, userId), instantAppPkgName); } return Collections.emptyList(); } } private List applyPostContentProviderResolutionFilter( List resolveInfos, String instantAppPkgName) { if (instantAppPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.providerInfo.applicationInfo.isInstantApp(); // allow providers that are defined in the provided package if (isEphemeralApp && instantAppPkgName.equals(info.providerInfo.packageName)) { if (info.providerInfo.splitName != null && !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames, info.providerInfo.splitName)) { // requested provider is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( null /*failureActivity*/, info.providerInfo.packageName, info.providerInfo.applicationInfo.longVersionCode, info.providerInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // load resources from the correct package installerInfo.resolvePackageName = info.getComponentInfo().packageName; resolveInfos.set(i, installerInfo); } continue; } // allow providers that have been explicitly exposed to instant applications if (!isEphemeralApp && ((info.providerInfo.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } @Override public ParceledListSlice getInstalledPackages(int flags, int userId) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return ParceledListSlice.emptyList(); } if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; final boolean listApex = (flags & MATCH_APEX) != 0; final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0; mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed packages"); // writer synchronized (mPackages) { ArrayList list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo(ps, flags, userId); if (pi != null) { list.add(pi); } } } else { list = new ArrayList<>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { final PackageSetting ps = (PackageSetting) p.mExtras; if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo((PackageSetting) p.mExtras, flags, userId); if (pi != null) { list.add(pi); } } } if (listApex) { if (listFactory) { list.addAll(mApexManager.getFactoryPackages()); } else { list.addAll(mApexManager.getActivePackages()); } if (listUninstalled) { list.addAll(mApexManager.getInactivePackages()); } } return new ParceledListSlice<>(list); } } private void addPackageHoldingPermissions(ArrayList list, PackageSetting ps, String[] permissions, boolean[] tmp, int flags, int userId) { int numMatch = 0; final PermissionsState permissionsState = ps.getPermissionsState(); for (int i=0; i getPackagesHoldingPermissions( String[] permissions, int flags, int userId) { if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, permissions); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "get packages holding permissions"); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; // writer synchronized (mPackages) { ArrayList list = new ArrayList<>(); boolean[] tmpBools = new boolean[permissions.length]; if (listUninstalled) { for (PackageSetting ps : mSettings.mPackages.values()) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } } else { for (PackageParser.Package pkg : mPackages.values()) { PackageSetting ps = (PackageSetting)pkg.mExtras; if (ps != null) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } } } return new ParceledListSlice<>(list); } } @Override public ParceledListSlice getInstalledApplications(int flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>( getInstalledApplicationsListInternal(flags, userId, callingUid)); } private List getInstalledApplicationsListInternal(int flags, int userId, int callingUid) { if (getInstantAppPackageName(callingUid) != null) { return Collections.emptyList(); } if (!sUserManager.exists(userId)) return Collections.emptyList(); flags = updateFlagsForApplication(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; mPermissionManager.enforceCrossUserPermission( callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed application info"); // writer synchronized (mPackages) { ArrayList list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { ApplicationInfo ai; int effectiveFlags = flags; if (ps.isSystem()) { effectiveFlags |= PackageManager.MATCH_ANY_USER; } if (ps.pkg != null) { if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(ps.pkg); } } else { // Shared lib filtering done in generateApplicationInfoFromSettingsLPw // and already converts to externally visible package name ai = generateApplicationInfoFromSettingsLPw(ps.name, callingUid, effectiveFlags, userId); } if (ai != null) { list.add(ai); } } } else { list = new ArrayList<>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { if (p.mExtras != null) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(p); list.add(ai); } } } } return list; } } @Override public ParceledListSlice getInstantApps(int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } if (!canViewInstantApps(Binder.getCallingUid(), userId)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getEphemeralApplications"); } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getEphemeralApplications"); synchronized (mPackages) { List instantApps = mInstantAppRegistry .getInstantAppsLPr(userId); if (instantApps != null) { return new ParceledListSlice<>(instantApps); } } return null; } @Override public boolean isInstantApp(String packageName, int userId) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "isInstantApp"); if (HIDE_EPHEMERAL_APIS) { return false; } synchronized (mPackages) { int callingUid = Binder.getCallingUid(); if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final PackageSetting ps = mSettings.mPackages.get(packageName); PackageParser.Package pkg = mPackages.get(packageName); final boolean returnAllowed = ps != null && (isCallerSameApp(packageName, callingUid) || canViewInstantApps(callingUid, userId) || mInstantAppRegistry.isInstantAccessGranted( userId, UserHandle.getAppId(callingUid), ps.appId)); if (returnAllowed) { return ps.getInstantApp(userId); } } return false; } @Override public byte[] getInstantAppCookie(String packageName, int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return null; } synchronized (mPackages) { return mInstantAppRegistry.getInstantAppCookieLPw( packageName, userId); } } @Override public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) { if (HIDE_EPHEMERAL_APIS) { return true; } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, true /* checkShell */, "setInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return false; } synchronized (mPackages) { return mInstantAppRegistry.setInstantAppCookieLPw( packageName, cookie, userId); } } @Override public Bitmap getInstantAppIcon(String packageName, int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } if (!canViewInstantApps(Binder.getCallingUid(), userId)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getInstantAppIcon"); } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppIcon"); synchronized (mPackages) { return mInstantAppRegistry.getInstantAppIconLPw( packageName, userId); } } private boolean isCallerSameApp(String packageName, int uid) { PackageParser.Package pkg = mPackages.get(packageName); return pkg != null && UserHandle.getAppId(uid) == pkg.applicationInfo.uid; } @Override public @NonNull ParceledListSlice getPersistentApplications(int flags) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(getPersistentApplicationsInternal(flags)); } private @NonNull List getPersistentApplicationsInternal(int flags) { final ArrayList finalList = new ArrayList<>(); // reader synchronized (mPackages) { final Iterator i = mPackages.values().iterator(); final int userId = UserHandle.getCallingUserId(); while (i.hasNext()) { final PackageParser.Package p = i.next(); if (p.applicationInfo == null) continue; final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0) && !p.applicationInfo.isDirectBootAware(); final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0) && p.applicationInfo.isDirectBootAware(); if ((p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0 && (!mSafeMode || isSystemApp(p)) && (matchesUnaware || matchesAware)) { PackageSetting ps = mSettings.mPackages.get(p.packageName); if (ps != null) { ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); if (ai != null) { finalList.add(ai); } } } } } return finalList; } @Override public ProviderInfo resolveContentProvider(String name, int flags, int userId) { return resolveContentProviderInternal(name, flags, userId); } private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId, name); final int callingUid = Binder.getCallingUid(); final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId); if (providerInfo == null) { return null; } if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { return null; } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { return null; } return providerInfo; } } /** * @deprecated */ @Deprecated public void querySyncProviders(List outNames, List outInfo) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; } mComponentResolver.querySyncProviders( outNames, outInfo, mSafeMode, UserHandle.getCallingUserId()); } @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; final List matchList = mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId); final int listSize = (matchList == null ? 0 : matchList.size()); synchronized (mPackages) { for (int i = 0; i < listSize; i++) { final ProviderInfo providerInfo = matchList.get(i); if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { continue; } final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { continue; } if (finalList == null) { finalList = new ArrayList<>(listSize - i); } finalList.add(providerInfo); } } if (finalList != null) { finalList.sort(sProviderInitOrderSorter); return new ParceledListSlice<>(finalList); } return ParceledListSlice.emptyList(); } @Override public InstrumentationInfo getInstrumentationInfo(ComponentName component, int flags) { // reader synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) { return null; } final PackageParser.Instrumentation i = mInstrumentation.get(component); return PackageParser.generateInstrumentationInfo(i, flags); } } @Override public @NonNull ParceledListSlice queryInstrumentation( String targetPackage, int flags) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(targetPackage); if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags)); } private @NonNull List queryInstrumentationInternal(String targetPackage, int flags) { ArrayList finalList = new ArrayList<>(); // reader synchronized (mPackages) { final Iterator i = mInstrumentation.values().iterator(); while (i.hasNext()) { final PackageParser.Instrumentation p = i.next(); if (targetPackage == null || targetPackage.equals(p.info.targetPackage)) { InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p, flags); if (ii != null) { finalList.add(ii); } } } } return finalList; } private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]"); try { scanDirLI(scanDir, parseFlags, scanFlags, currentTime); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) { final File[] files = scanDir.listFiles(); if (ArrayUtils.isEmpty(files)) { Log.d(TAG, "No files in app dir " + scanDir); return; } if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags + " flags=0x" + Integer.toHexString(parseFlags)); } try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser( mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mParallelPackageParserCallback)) { // Submit files for parsing in parallel int fileCount = 0; for (File file : files) { final boolean isPackage = (isApkFile(file) || file.isDirectory()) && !PackageInstallerService.isStageName(file.getName()); if (!isPackage) { // Ignore entries which are not packages continue; } parallelPackageParser.submit(file, parseFlags); fileCount++; } // Process results one by one for (; fileCount > 0; fileCount--) { ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take(); Throwable throwable = parseResult.throwable; int errorCode = PackageManager.INSTALL_SUCCEEDED; if (throwable == null) { // TODO(toddke): move lower in the scan chain // Static shared libraries have synthetic package names if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(parseResult.pkg); } try { scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags, currentTime, null); } catch (PackageManagerException e) { errorCode = e.error; Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage()); } } else if (throwable instanceof PackageParser.PackageParserException) { PackageParser.PackageParserException e = (PackageParser.PackageParserException) throwable; errorCode = e.error; Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage()); } else { throw new IllegalStateException("Unexpected exception occurred while parsing " + parseResult.scanFile, throwable); } // Delete invalid userdata apps if ((scanFlags & SCAN_AS_SYSTEM) == 0 && errorCode != PackageManager.INSTALL_SUCCEEDED) { logCriticalInfo(Log.WARN, "Deleting invalid package at " + parseResult.scanFile); removeCodePathLI(parseResult.scanFile); } } } } public static void reportSettingsProblem(int priority, String msg) { logCriticalInfo(priority, msg); } private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, boolean forceCollect, boolean skipVerify) throws PackageManagerException { // When upgrading from pre-N MR1, verify the package time stamp using the package // directory and not the APK file. final long lastModifiedTime = mIsPreNMR1Upgrade ? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg); final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(pkg); if (ps != null && !forceCollect && ps.codePathString.equals(pkg.codePath) && ps.timeStamp == lastModifiedTime && !isCompatSignatureUpdateNeeded(settingsVersionForPackage) && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) { if (ps.signatures.mSigningDetails.signatures != null && ps.signatures.mSigningDetails.signatures.length != 0 && ps.signatures.mSigningDetails.signatureSchemeVersion != SignatureSchemeVersion.UNKNOWN) { // Optimization: reuse the existing cached signing data // if the package appears to be unchanged. pkg.mSigningDetails = new PackageParser.SigningDetails(ps.signatures.mSigningDetails); return; } Slog.w(TAG, "PackageSetting for " + ps.name + " is missing signatures. Collecting certs again to recover them."); } else { Slog.i(TAG, pkg.codePath + " changed; collecting certs" + (forceCollect ? " (forced)" : "")); } try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); PackageParser.collectCertificates(pkg, skipVerify); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } /** * Clear the package profile if this was an upgrade and the package * version was updated. */ private void maybeClearProfilesForUpgradesLI( @Nullable PackageSetting originalPkgSetting, @NonNull PackageParser.Package currentPkg) { if (originalPkgSetting == null || !isDeviceUpgrading()) { return; } if (originalPkgSetting.versionCode == currentPkg.mVersionCode) { return; } clearAppProfilesLIF(currentPkg, UserHandle.USER_ALL); if (DEBUG_INSTALL) { Slog.d(TAG, originalPkgSetting.name + " clear profile due to version change " + originalPkgSetting.versionCode + " != " + currentPkg.mVersionCode); } } /** * Traces a package scan. * @see #scanPackageLI(File, int, int, long, UserHandle) */ @GuardedBy({"mInstallLock", "mPackages"}) 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 */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); pp.setDisplayMetrics(mMetrics); pp.setCallback(mPackageParserCallback); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); final PackageParser.Package pkg; try { pkg = pp.parsePackage(scanFile, parseFlags); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } // Static shared libraries have synthetic package names if (pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(pkg); } return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user); } /** * Scans a package and returns the newly parsed package. * @throws PackageManagerException on a parse error. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg, final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { // If the package has children and this is the first dive in the function // we scan the package with the SCAN_CHECK_ONLY flag set to see whether all // packages (parent and children) would be successfully scanned before the // actual scan since scanning mutates internal state and we want to atomically // install the package and its children. if ((scanFlags & SCAN_CHECK_ONLY) == 0) { if (pkg.childPackages != null && pkg.childPackages.size() > 0) { scanFlags |= SCAN_CHECK_ONLY; } } else { scanFlags &= ~SCAN_CHECK_ONLY; } // Scan the parent PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags, scanFlags, currentTime, user); // Scan the children final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPackage = pkg.childPackages.get(i); addForInitLI(childPackage, parseFlags, scanFlags, currentTime, user); } if ((scanFlags & SCAN_CHECK_ONLY) != 0) { return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user); } return scannedPkg; } /** * Returns if forced apk verification can be skipped for the whole package, including splits. */ private boolean canSkipForcedPackageVerification(PackageParser.Package pkg) { if (!canSkipForcedApkVerification(pkg.baseCodePath)) { return false; } // TODO: Allow base and splits to be verified individually. if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { for (int i = 0; i < pkg.splitCodePaths.length; i++) { if (!canSkipForcedApkVerification(pkg.splitCodePaths[i])) { return false; } } } return true; } /** * Returns if forced apk verification can be skipped, depending on current FSVerity setup and * whether the apk contains signed root hash. Note that the signer's certificate still needs to * match one in a trusted source, and should be done separately. */ private boolean canSkipForcedApkVerification(String apkPath) { if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) { return VerityUtils.hasFsverity(apkPath); } try { final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath); if (rootHashObserved == null) { return false; // APK does not contain Merkle tree root hash. } synchronized (mInstallLock) { // Returns whether the observed root hash matches what kernel has. mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved); return true; } } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) { Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e); } return false; } /** * Adds a new package to the internal data structures during platform initialization. * After adding, the package is known to the system and available for querying. * For packages located on the device ROM [eg. packages located in /system, /vendor, * etc...], additional checks are performed. Basic verification [such as ensuring * matching signatures, checking version codes, etc...] occurs if the package is * identical to a previously known package. If the package fails a signature check, * the version installed on /data will be removed. If the version of the new package * is less than or equal than the version on /data, it will be ignored. * Regardless of the package location, the results are applied to the internal * structures and the package is made available to the rest of the system. * NOTE: The return value should be removed. It's the passed in package object. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package addForInitLI(PackageParser.Package pkg, @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { final boolean scanSystemPartition = (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0; final String renamedPkgName; final PackageSetting disabledPkgSetting; final boolean isSystemPkgUpdated; final boolean pkgAlreadyExists; PackageSetting pkgSetting; // NOTE: installPackageLI() has the same code to setup the package's // application info. This probably should be done lower in the call // stack [such as scanPackageOnly()]. However, we verify the application // info prior to that [in scanPackageNew()] and thus have to setup // the application info early. pkg.setApplicationVolumeUuid(pkg.volumeUuid); pkg.setApplicationInfoCodePath(pkg.codePath); pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath); pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths); pkg.setApplicationInfoResourcePath(pkg.codePath); pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath); pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths); synchronized (mPackages) { renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage); final String realPkgName = getRealPackageName(pkg, renamedPkgName); if (realPkgName != null) { ensurePackageRenamed(pkg, renamedPkgName); } final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName); final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName); pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting; pkgAlreadyExists = pkgSetting != null; final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName; disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName); isSystemPkgUpdated = disabledPkgSetting != null; if (DEBUG_INSTALL && isSystemPkgUpdated) { Slog.d(TAG, "updatedPkg = " + disabledPkgSetting); } final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null) ? mSettings.getSharedUserLPw(pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true) : null; if (DEBUG_PACKAGE_SCANNING && (parseFlags & PackageParser.PARSE_CHATTY) != 0 && sharedUserSetting != null) { Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + sharedUserSetting.userId + "):" + " packages=" + sharedUserSetting.packages); } if (scanSystemPartition) { // Potentially prune child packages. If the application on the /system // partition has been updated via OTA, but, is still disabled by a // version on /data, cycle through all of its children packages and // remove children that are no longer defined. if (isSystemPkgUpdated) { final int scannedChildCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; final int disabledChildCount = disabledPkgSetting.childPackageNames != null ? disabledPkgSetting.childPackageNames.size() : 0; for (int i = 0; i < disabledChildCount; i++) { String disabledChildPackageName = disabledPkgSetting.childPackageNames.get(i); boolean disabledPackageAvailable = false; for (int j = 0; j < scannedChildCount; j++) { PackageParser.Package childPkg = pkg.childPackages.get(j); if (childPkg.packageName.equals(disabledChildPackageName)) { disabledPackageAvailable = true; break; } } if (!disabledPackageAvailable) { mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName); } } // we're updating the disabled package, so, scan it as the package setting final ScanRequest request = new ScanRequest(pkg, sharedUserSetting, null, disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */, null /* originalPkgSetting */, null, parseFlags, scanFlags, (pkg == mPlatformPackage), user); applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage); final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L); if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) { scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting); } } } } final boolean newPkgChangedPaths = pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath); final boolean newPkgVersionGreater = pkgAlreadyExists && pkg.getLongVersionCode() > pkgSetting.versionCode; final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated && newPkgChangedPaths && newPkgVersionGreater; if (isSystemPkgBetter) { // The version of the application on /system is greater than the version on // /data. Switch back to the application on /system. // It's safe to assume the application on /system will correctly scan. If not, // there won't be a working copy of the application. synchronized (mPackages) { // just remove the loaded entries from package lists mPackages.remove(pkgSetting.name); } logCriticalInfo(Log.WARN, "System package updated;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); final InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); args.cleanUpResourcesLI(); synchronized (mPackages) { mSettings.enableSystemPackageLPw(pkgSetting.name); } } if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) { // The version of the application on the /system partition is less than or // equal to the version on the /data partition. Throw an exception and use // the application already installed on the /data partition. throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at " + pkg.codePath + " ignored: updated version " + pkgSetting.versionCode + " better than this " + pkg.getLongVersionCode()); } // Verify certificates against what was last scanned. If there was an upgrade and this is an // app in a system partition, or if this is an updated priv app, we will force re-collecting // certificate. final boolean forceCollect = (mIsUpgrade && scanSystemPartition) || PackageManagerServiceUtils.isApkVerificationForced(disabledPkgSetting); // Full APK verification can be skipped during certificate collection, only if the file is // in verified partition, or can be verified on access (when apk verity is enabled). In both // cases, only data in Signing Block is verified instead of the whole file. final boolean skipVerify = scanSystemPartition || (forceCollect && canSkipForcedPackageVerification(pkg)); collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify); // Reset profile if the application version is changed maybeClearProfilesForUpgradesLI(pkgSetting, pkg); /* * A new system app appeared, but we already had a non-system one of the * same name installed earlier. */ boolean shouldHideSystemApp = false; // A new application appeared on /system, but, we already have a copy of // the application installed on /data. if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists && !pkgSetting.isSystem()) { if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA) && !pkgSetting.signatures.mSigningDetails.checkCapability( pkg.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) { logCriticalInfo(Log.WARN, "System package signature mismatch;" + " name: " + pkgSetting.name); try (PackageFreezer freezer = freezePackage(pkg.packageName, "scanPackageInternalLI")) { deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null); } pkgSetting = null; } else if (newPkgVersionGreater) { // The application on /system is newer than the application on /data. // Simply remove the application on /data [keeping application data] // and replace it with the version on /system. logCriticalInfo(Log.WARN, "System package enabled;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } } else { // The application on /system is older than the application on /data. Hide // the application on /system and the version on /data will be scanned later // and re-added like an update. shouldHideSystemApp = true; logCriticalInfo(Log.INFO, "System package disabled;" + " name: " + pkgSetting.name + "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode + "; new: " + pkg.codePath + " @ " + pkg.codePath); } } final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user); if (scanResult.success) { synchronized (mPackages) { boolean appIdCreated = false; try { final String pkgName = scanResult.pkgSetting.name; final Map reconcileResult = reconcilePackagesLocked( new ReconcileRequest( Collections.singletonMap(pkgName, scanResult), mSharedLibraries, mPackages, Collections.singletonMap( pkgName, getSettingsVersionForPackage(pkg)), Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))), mSettings.mKeySetManagerService); appIdCreated = optimisticallyRegisterAppId(scanResult); commitReconciledScanResultLocked(reconcileResult.get(pkgName)); } catch (PackageManagerException e) { if (appIdCreated) { cleanUpAppIdCreation(scanResult); } throw e; } } } if (shouldHideSystemApp) { synchronized (mPackages) { mSettings.disableSystemPackageLPw(pkg.packageName, true); } } return scanResult.pkgSetting.pkg; } private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) { // Derive the new package synthetic package name pkg.setPackageName(pkg.packageName + STATIC_SHARED_LIB_DELIMITER + pkg.staticSharedLibVersion); } static String fixProcessName(String defProcessName, String processName) { if (processName == null) { return defProcessName; } return processName; } /** * Enforces that only the system UID or root's UID can call a method exposed * via Binder. * * @param message used as message if SecurityException is thrown * @throws SecurityException if the caller is not system or root */ private static void enforceSystemOrRoot(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) { throw new SecurityException(message); } } /** * Enforces that only the system UID or root's UID or shell'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 shell */ private static void enforceSystemOrRootOrShell(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID && uid != Process.SHELL_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 = isDeviceUpgrading(); // First boot or factory reset. // Note: we also handle devices that are upgrading to N right now as if it is their // first boot, as they do not have profile data. boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade; // We need to re-extract after a pruned cache, as AoT-ed files will be out of date. boolean causePrunedCache = VMRuntime.didPruneDalvikCache(); if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) { return; } List pkgs; synchronized (mPackages) { pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this); } final long startTime = System.nanoTime(); final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */, causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT, false /* bootComplete */); final int elapsedTimeSeconds = (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime); MetricsLogger.histogram(mContext, "opt_dialog_num_dexopted", stats[0]); MetricsLogger.histogram(mContext, "opt_dialog_num_skipped", stats[1]); MetricsLogger.histogram(mContext, "opt_dialog_num_failed", stats[2]); MetricsLogger.histogram(mContext, "opt_dialog_num_total", getOptimizablePackages().size()); MetricsLogger.histogram(mContext, "opt_dialog_time_s", elapsedTimeSeconds); } /* * Return the prebuilt profile path given a package base code path. */ private static String getPrebuildProfilePath(PackageParser.Package pkg) { return pkg.baseCodePath + ".prof"; } /** * Performs dexopt on the set of packages in {@code packages} and returns an int array * containing statistics about the invocation. The array consists of three elements, * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped} * and {@code numberOfPackagesFailed}. */ private int[] performDexOptUpgrade(List pkgs, boolean showDialog, final int compilationReason, boolean bootComplete) { int numberOfPackagesVisited = 0; int numberOfPackagesOptimized = 0; int numberOfPackagesSkipped = 0; int numberOfPackagesFailed = 0; final int numberOfPackagesToDexopt = pkgs.size(); for (PackageParser.Package pkg : pkgs) { numberOfPackagesVisited++; boolean useProfileForDexopt = false; if ((isFirstBoot() || isDeviceUpgrading()) && isSystemApp(pkg)) { // Copy over initial preopt profiles since we won't get any JIT samples for methods // that are already compiled. File profileFile = new File(getPrebuildProfilePath(pkg)); // Copy profile if it exists. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The issue // is that we don't have a good way to say "do this only once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Installer failed to copy system profile!"); } else { // Disabled as this causes speed-profile compilation during first boot // even if things are already compiled. // useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } else { PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName); // Handle compressed APKs in this path. Only do this for stubs with profiles to // minimize the number off apps being speed-profile compiled during first boot. // The other paths will not change the filter. if (disabledPs != null && disabledPs.pkg.isStub) { // The package is the stub one, remove the stub suffix to get the normal // package and APK names. String systemProfilePath = getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, ""); profileFile = new File(systemProfilePath); // If we have a profile for a compressed APK, copy it to the reference // location. // Note that copying the profile here will cause it to override the // reference profile every OTA even though the existing reference profile // may have more data. We can't copy during decompression since the // directories are not set up at that point. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The // issue is that we don't have a good way to say "do this only // once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Failed to copy system profile for stub package!"); } else { useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } } } } if (!PackageDexOptimizer.canOptimizePackage(pkg)) { if (DEBUG_DEXOPT) { Log.i(TAG, "Skipping update of of non-optimizable app " + pkg.packageName); } numberOfPackagesSkipped++; continue; } if (DEBUG_DEXOPT) { Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " + numberOfPackagesToDexopt + ": " + pkg.packageName); } if (showDialog) { try { ActivityManager.getService().showBootMessage( mContext.getResources().getString(R.string.android_upgrading_apk, numberOfPackagesVisited, numberOfPackagesToDexopt), true); } catch (RemoteException e) { } synchronized (mPackages) { mDexOptDialogShown = true; } } int pkgCompilationReason = compilationReason; if (useProfileForDexopt) { // Use background dexopt mode to try and use the profile. Note that this does not // guarantee usage of the profile. pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT; } if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { mArtManagerService.compileLayouts(pkg); } // checkProfiles is false to avoid merging profiles during boot which // might interfere with background compilation (b/28612421). // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a // trade-off worth doing to save boot time work. int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0; if (compilationReason == REASON_FIRST_BOOT) { // TODO: This doesn't cover the upgrade case, we should check for this too. dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE; } int primaryDexOptStaus = performDexOptTraced(new DexoptOptions( pkg.packageName, pkgCompilationReason, dexoptFlags)); switch (primaryDexOptStaus) { case PackageDexOptimizer.DEX_OPT_PERFORMED: numberOfPackagesOptimized++; break; case PackageDexOptimizer.DEX_OPT_SKIPPED: numberOfPackagesSkipped++; break; case PackageDexOptimizer.DEX_OPT_FAILED: numberOfPackagesFailed++; break; default: Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus); break; } } return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped, numberOfPackagesFailed }; } @Override public void notifyPackageUse(String packageName, int reason) { synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); if (getInstantAppPackageName(callingUid) != null) { if (!isCallerSameApp(packageName, callingUid)) { return; } } else { if (isInstantApp(packageName, callingUserId)) { return; } } notifyPackageUseLocked(packageName, reason); } } @GuardedBy("mPackages") public CheckPermissionDelegate getCheckPermissionDelegateLocked() { return mCheckPermissionDelegate; } @GuardedBy("mPackages") public void setCheckPermissionDelegateLocked(CheckPermissionDelegate delegate) { mCheckPermissionDelegate = delegate; } @GuardedBy("mPackages") private void notifyPackageUseLocked(String packageName, int reason) { final PackageParser.Package p = mPackages.get(packageName); if (p == null) { return; } p.mLastPackageUsageTimeInMills[reason] = System.currentTimeMillis(); } @Override public void notifyDexLoad(String loadingPackageName, List classLoaderNames, List classPaths, String loaderIsa) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); if (ai == null) { Slog.w(TAG, "Loading a package that does not exist for the calling user. package=" + loadingPackageName + ", user=" + userId); return; } mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId); } @Override public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule, IDexModuleRegisterCallback callback) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(packageName, /*flags*/ 0, userId); DexManager.RegisterDexModuleResult result; if (ai == null) { Slog.w(TAG, "Registering a dex module for a package that does not exist for the" + " calling user. package=" + packageName + ", user=" + userId); result = new DexManager.RegisterDexModuleResult(false, "Package not installed"); } else { result = mDexManager.registerDexModule(ai, dexModulePath, isSharedModule, userId); } if (callback != null) { mHandler.post(() -> { try { callback.onDexModuleRegistered(dexModulePath, result.success, result.message); } catch (RemoteException e) { Slog.w(TAG, "Failed to callback after module registration " + dexModulePath, e); } }); } } /** * Ask the package manager to perform a dex-opt with the given compiler filter. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptMode(String packageName, boolean checkProfiles, String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) { int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) | (force ? DexoptOptions.DEXOPT_FORCE : 0) | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0); return performDexOpt(new DexoptOptions(packageName, REASON_UNKNOWN, targetCompilerFilter, splitName, flags)); } /** * Ask the package manager to perform a dex-opt with the given compiler filter on the * secondary dex files belonging to the given package. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptSecondary(String packageName, String compilerFilter, boolean force) { int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES | DexoptOptions.DEXOPT_BOOT_COMPLETE | (force ? DexoptOptions.DEXOPT_FORCE : 0); return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags)); } /** * Ask the package manager to compile layouts in the given package. */ @Override public boolean compileLayouts(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { return false; } } return mViewCompiler.compileLayouts(pkg); } /*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 = findSharedLibraries(p); final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo); if (!deps.isEmpty()) { DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(), options.getCompilationReason(), options.getCompilerFilter(), options.getSplitName(), options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY); for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = null; synchronized (mPackages) { depPackage = mPackages.get(info.getPackageName()); } if (depPackage != null) { // TODO: Analyze and investigate if we (should) profile libraries. pdo.performDexOpt(depPackage, instructionSets, getOrCreateCompilerPackageStats(depPackage), mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions); } else { // TODO(ngeoffray): Support dexopting system shared libraries. } } } return pdo.performDexOpt(p, instructionSets, getOrCreateCompilerPackageStats(p), mDexManager.getPackageUseInfoOrDefault(p.packageName), options); } /** * Reconcile the information we have about the secondary dex files belonging to * {@code packageName} 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(@Nullable List packageNames) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return false; } enforceSystemOrRootOrShell("runBackgroundDexoptJob"); final long identity = Binder.clearCallingIdentity(); try { return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames); } finally { Binder.restoreCallingIdentity(identity); } } private static List findSharedLibraries(PackageParser.Package p) { if (p.usesLibraryInfos != null) { ArrayList retValue = new ArrayList<>(); Set collectedNames = new HashSet<>(); for (SharedLibraryInfo info : p.usesLibraryInfos) { findSharedLibrariesRecursive(info, retValue, collectedNames); } return retValue; } else { return Collections.emptyList(); } } private static void findSharedLibrariesRecursive(SharedLibraryInfo info, ArrayList collected, Set collectedNames) { if (!collectedNames.contains(info.getName())) { collectedNames.add(info.getName()); collected.add(info); if (info.getDependencies() != null) { for (SharedLibraryInfo dep : info.getDependencies()) { findSharedLibrariesRecursive(dep, collected, collectedNames); } } } } List findSharedNonSystemLibraries(PackageParser.Package pkg) { List deps = findSharedLibraries(pkg); if (!deps.isEmpty()) { ArrayList retValue = new ArrayList<>(); synchronized (mPackages) { for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = mPackages.get(info.getPackageName()); if (depPackage != null) { retValue.add(depPackage); } } } return retValue; } else { return Collections.emptyList(); } } @Nullable private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) { return getSharedLibraryInfo(name, version, mSharedLibraries, null); } @Nullable private static SharedLibraryInfo getSharedLibraryInfo(String name, long version, Map> existingLibraries, @Nullable Map> newLibraries) { if (newLibraries != null) { final LongSparseArray versionedLib = newLibraries.get(name); SharedLibraryInfo info = null; if (versionedLib != null) { info = versionedLib.get(version); } if (info != null) { return info; } } final LongSparseArray versionedLib = existingLibraries.get(name); if (versionedLib == null) { return null; } return versionedLib.get(version); } private SharedLibraryInfo getLatestSharedLibraVersionLPr(PackageParser.Package pkg) { LongSparseArray versionedLib = mSharedLibraries.get( pkg.staticSharedLibName); if (versionedLib == null) { return null; } long previousLibVersion = -1; final int versionCount = versionedLib.size(); for (int i = 0; i < versionCount; i++) { final long libVersion = versionedLib.keyAt(i); if (libVersion < pkg.staticSharedLibVersion) { previousLibVersion = Math.max(previousLibVersion, libVersion); } } if (previousLibVersion >= 0) { return versionedLib.get(previousLibVersion); } return null; } @Nullable private PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) { PackageSetting sharedLibPackage = null; synchronized (mPackages) { final SharedLibraryInfo latestSharedLibraVersionLPr = getLatestSharedLibraVersionLPr(scanResult.pkgSetting.pkg); if (latestSharedLibraVersionLPr != null) { sharedLibPackage = mSettings.getPackageLPr( latestSharedLibraVersionLPr.getPackageName()); } } return sharedLibPackage; } public void shutdown() { mPackageUsage.writeNow(mPackages); mCompilerStats.writeNow(); mDexManager.writePackageDexUsageNow(); PackageWatchdog.getInstance(mContext).writeNow(); // This is the last chance to write out pending restriction settings synchronized (mPackages) { if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS); for (int userId : mDirtyUsers) { mSettings.writePackageRestrictionsLPr(userId); } mDirtyUsers.clear(); } } } @Override public void dumpProfiles(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } /* Only the shell, root, or the app user should be able to dump profiles. */ int callingUid = Binder.getCallingUid(); if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID && callingUid != pkg.applicationInfo.uid) { throw new SecurityException("dumpProfiles"); } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles"); mArtManagerService.dumpProfiles(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @Override public void forceDexOpt(String packageName) { enforceSystemOrRoot("forceDexOpt"); PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); // Whoever is calling forceDexOpt wants a compiled package. // Don't use profiles since that may cause compilation to be skipped. final int res = performDexOptInternalWithDependenciesLI( pkg, new DexoptOptions(packageName, getDefaultCompilerFilter(), DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE)); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) { throw new IllegalStateException("Failed to dexopt: " + res); } } } @GuardedBy("mPackages") private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) { if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package not in system partition"); return false; } else if (mPackages.get(oldPkg.name) != null) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package still exists"); return false; } return true; } @GuardedBy("mInstallLock") void removeCodePathLI(File codePath) { if (codePath.isDirectory()) { try { mInstaller.rmPackageDir(codePath.getAbsolutePath()); } catch (InstallerException e) { Slog.w(TAG, "Failed to remove code path", e); } } else { codePath.delete(); } } private int[] resolveUserIds(int userId) { return (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds() : new int[] { userId }; } private void clearAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } clearAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { clearAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } clearAppProfilesLIF(pkg, UserHandle.USER_ALL); } private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } } private void destroyAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } } private void destroyAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.destroyAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId); } } private void destroyAppProfilesLIF(PackageParser.Package pkg) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppProfilesLeafLIF(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppProfilesLeafLIF(pkg.childPackages.get(i)); } } private void destroyAppProfilesLeafLIF(PackageParser.Package pkg) { try { mInstaller.destroyAppProfiles(pkg.packageName); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } private void clearAppProfilesLIF(PackageParser.Package pkg, int userId) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } mArtManagerService.clearAppProfiles(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { mArtManagerService.clearAppProfiles(pkg.childPackages.get(i)); } } private void setInstallAndUpdateTime(PackageParser.Package pkg, long firstInstallTime, long lastUpdateTime) { // Set parent install/update time PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } // Set children install/update time final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPkg = pkg.childPackages.get(i); ps = (PackageSetting) childPkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } } } @GuardedBy("mPackages") private void applyDefiningSharedLibraryUpdateLocked( PackageParser.Package pkg, SharedLibraryInfo libInfo, BiConsumer action) { // Note that libraries defined by this package may be null if: // - Package manager was unable to create the shared library. The package still // gets installed, but the shared library does not get created. // Or: // - Package manager is in a state where package isn't scanned yet. This will // get called again after scanning to fix the dependencies. if (pkg.isLibrary()) { if (pkg.staticSharedLibName != null) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( pkg.staticSharedLibName, pkg.staticSharedLibVersion); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } else { for (String libraryName : pkg.libraryNames) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( libraryName, SharedLibraryInfo.VERSION_UNDEFINED); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } } } } @GuardedBy("mPackages") private void addSharedLibraryLPr(PackageParser.Package pkg, Set usesLibraryFiles, SharedLibraryInfo libInfo, PackageParser.Package changingLib) { if (libInfo.getPath() != null) { usesLibraryFiles.add(libInfo.getPath()); return; } PackageParser.Package p = mPackages.get(libInfo.getPackageName()); if (changingLib != null && changingLib.packageName.equals(libInfo.getPackageName())) { // If we are doing this while in the middle of updating a library apk, // then we need to make sure to use that new apk for determining the // dependencies here. (We haven't yet finished committing the new apk // to the package manager state.) if (p == null || p.packageName.equals(changingLib.packageName)) { p = changingLib; } } if (p != null) { usesLibraryFiles.addAll(p.getAllCodePaths()); // If the package provides libraries, add the dependency to them. applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, (definingLibrary, dependency) -> { definingLibrary.addDependency(dependency); }); if (p.usesLibraryFiles != null) { Collections.addAll(usesLibraryFiles, p.usesLibraryFiles); } } } @GuardedBy("mPackages") private void updateSharedLibrariesLocked(PackageParser.Package pkg, PackageParser.Package changingLib, Map availablePackages) throws PackageManagerException { final ArrayList sharedLibraryInfos = collectSharedLibraryInfos(pkg, availablePackages, mSharedLibraries, null); executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos); } private static ArrayList collectSharedLibraryInfos(PackageParser.Package pkg, Map availablePackages, @NonNull final Map> existingLibraries, @Nullable final Map> newLibraries) throws PackageManagerException { if (pkg == null) { return null; } // The collection used here must maintain the order of addition (so // that libraries are searched in the correct order) and must have no // duplicates. ArrayList usesLibraryInfos = null; if (pkg.usesLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesLibraries, null, null, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, null, availablePackages, existingLibraries, newLibraries); } if (pkg.usesStaticLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } if (pkg.usesOptionalLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesOptionalLibraries, null, null, pkg.packageName, false, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } return usesLibraryInfos; } private void executeSharedLibrariesUpdateLPr(PackageParser.Package pkg, PackageParser.Package changingLib, ArrayList usesLibraryInfos) { // If the package provides libraries, clear their old dependencies. // This method will set them up again. applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> { definingLibrary.clearDependencies(); }); if (usesLibraryInfos != null) { pkg.usesLibraryInfos = usesLibraryInfos; // Use LinkedHashSet to preserve the order of files added to // usesLibraryFiles while eliminating duplicates. Set usesLibraryFiles = new LinkedHashSet<>(); for (SharedLibraryInfo libInfo : usesLibraryInfos) { addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib); } pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]); } else { pkg.usesLibraryInfos = null; pkg.usesLibraryFiles = null; } } @GuardedBy("mPackages") private static ArrayList collectSharedLibraryInfos( @NonNull List requestedLibraries, @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, @NonNull String packageName, boolean required, int targetSdk, @Nullable ArrayList outUsedLibraries, @NonNull final Map
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: *
* If you're doing surgery on app code/data, use {@link PackageFreezer} to * guard your work against the app being relaunched. */ private void killUid(int appId, int userId, String reason) { final long identity = Binder.clearCallingIdentity(); try { IActivityManager am = ActivityManager.getService(); if (am != null) { try { am.killUid(appId, userId, reason); } catch (RemoteException e) { /* ignore - same process */ } } } finally { Binder.restoreCallingIdentity(identity); } } /** * If the database version for this type of package (internal storage or * external storage) is less than the version where package signatures * were updated, return true. */ private boolean isCompatSignatureUpdateNeeded(PackageParser.Package scannedPkg) { return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg)); } private static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) { return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY; } private boolean isRecoverSignatureUpdateNeeded(PackageParser.Package scannedPkg) { return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg)); } private static boolean isRecoverSignatureUpdateNeeded(VersionInfo ver) { return ver.databaseVersion < DatabaseVersion.SIGNATURE_MALFORMED_RECOVER; } @Override public List getAllPackages() { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); synchronized (mPackages) { if (canViewInstantApps(callingUid, callingUserId)) { return new ArrayList<>(mPackages.keySet()); } final String instantAppPkgName = getInstantAppPackageName(callingUid); final List result = new ArrayList<>(); if (instantAppPkgName != null) { // caller is an instant application; filter unexposed applications for (PackageParser.Package pkg : mPackages.values()) { if (!pkg.visibleToInstantApps) { continue; } result.add(pkg.packageName); } } else { // caller is a normal application; filter instant applications for (PackageParser.Package pkg : mPackages.values()) { final PackageSetting ps = pkg.mExtras != null ? (PackageSetting) pkg.mExtras : null; if (ps != null && ps.getInstantApp(callingUserId) && !mInstantAppRegistry.isInstantAccessGranted( callingUserId, UserHandle.getAppId(callingUid), ps.appId)) { continue; } result.add(pkg.packageName); } } return result; } } /** * IMPORTANT: Not all packages returned by this method may be known * to the system. There are two conditions in which this may occur: * * The package is on adoptable storage and the device has been removed * The package is being removed and the internal structures are partially updated * * The second is an artifact of the current data structures and should be fixed. See * b/111075456 for one such instance. */ @Override public String[] getPackagesForUid(int uid) { final int callingUid = Binder.getCallingUid(); final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; final int userId = UserHandle.getUserId(uid); final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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 appId = UserHandle.getAppId(uids[i]); final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); 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; } final int appId = UserHandle.getAppId(uid); // reader synchronized (mPackages) { final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; final Iterator it = sus.packages.iterator(); while (it.hasNext()) { if (it.next().isPrivileged()) { return true; } } } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; return ps.isPrivileged(); } } return false; } @Override public String[] getAppOpPermissionPackages(String permName) { return mPermissionManager.getAppOpPermissionPackages(permName); } @Override public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { return resolveIntentInternal(intent, resolvedType, flags, userId, false, Binder.getCallingUid()); } /** * Normally instant apps can only be resolved when they're visible to the caller. * However, if {@code resolveForStart} is {@code true}, all instant apps are visible * since we need to allow the system to start any installed application. */ private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType, int flags, int userId, boolean resolveForStart, int filterCallingUid) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent"); if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "resolve intent"); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, filterCallingUid, userId, resolveForStart, true /*allowDynamicSplits*/); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); final ResolveInfo bestChoice = chooseBestActivity(intent, resolvedType, flags, query, userId); return bestChoice; } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @Override public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) { if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) { throw new SecurityException( "findPersistentPreferredActivity can only be run by the system"); } if (!sUserManager.exists(userId)) { return null; } final int callingUid = Binder.getCallingUid(); intent = updateIntentForResolve(intent); final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver()); final int flags = updateFlagsForResolve( 0, userId, intent, callingUid, false /*includeInstantApps*/); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); synchronized (mPackages) { return findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false, userId); } } @Override public void setLastChosenActivity(Intent intent, String resolvedType, int flags, IntentFilter filter, int match, ComponentName activity) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; } final int userId = UserHandle.getCallingUserId(); if (DEBUG_PREFERRED) { Log.v(TAG, "setLastChosenActivity intent=" + intent + " resolvedType=" + resolvedType + " flags=" + flags + " filter=" + filter + " match=" + match + " activity=" + activity); filter.dump(new PrintStreamPrinter(System.out), " "); } intent.setComponent(null); final List query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); // Find any earlier preferred or last chosen entries and nuke them findPreferredActivityNotLocked( 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 findPreferredActivityNotLocked( intent, resolvedType, flags, query, 0, false, false, false, userId); } /** * Returns whether or not instant apps have been disabled remotely. */ private boolean areWebInstantAppsDisabled(int userId) { return mWebInstantAppsDisabled.get(userId); } private boolean isInstantAppResolutionAllowed( Intent intent, List resolvedActivities, int userId, boolean skipPackageCheck) { if (mInstantAppResolverConnection == null) { return false; } if (mInstantAppInstallerActivity == null) { return false; } if (intent.getComponent() != null) { return false; } if ((intent.getFlags() & Intent.FLAG_IGNORE_EPHEMERAL) != 0) { return false; } if (!skipPackageCheck && intent.getPackage() != null) { return false; } if (!intent.isWebIntent()) { // for non web intents, we should not resolve externally if an app already exists to // handle it or if the caller didn't explicitly request it. if ((resolvedActivities != null && resolvedActivities.size() != 0) || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) { return false; } } else { if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) { return false; } else if (areWebInstantAppsDisabled(userId)) { return false; } } // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. // Or if there's already an ephemeral app installed that handles the action synchronized (mPackages) { final int count = (resolvedActivities == null ? 0 : resolvedActivities.size()); for (int n = 0; n < count; n++) { final ResolveInfo info = resolvedActivities.get(n); final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { // only check domain verification status if the app is not a browser if (!info.handleAllWebDataURI) { // Try to get the status from User settings first final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int) (packedStatus >> 32); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) { if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName + ", status: " + status); } return false; } } if (ps.getInstantApp(userId)) { if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app installed;" + " pkg: " + packageName); } return false; } } } } // We've exhausted all ways to deny ephemeral application; let the system look for them. return true; } private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj, Intent origIntent, String resolvedType, String callingPackage, Bundle verificationBundle, int userId) { final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO, new InstantAppRequest(responseObj, origIntent, resolvedType, callingPackage, userId, verificationBundle, false /*resolveForStart*/)); mHandler.sendMessage(msg); } private ResolveInfo chooseBestActivity(Intent intent, String resolvedType, int flags, List query, int userId) { if (query != null) { final int N = query.size(); if (N == 1) { return query.get(0); } else if (N > 1) { final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); // If there is more than one activity with the same priority, // then let the user decide between them. ResolveInfo r0 = query.get(0); ResolveInfo r1 = query.get(1); if (DEBUG_INTENT_MATCHING || debug) { Slog.v(TAG, r0.activityInfo.name + "=" + r0.priority + " vs " + r1.activityInfo.name + "=" + r1.priority); } // If the first activity has a higher priority, or a different // default, then it is always desirable to pick it. if (r0.priority != r1.priority || r0.preferredOrder != r1.preferredOrder || r0.isDefault != r1.isDefault) { return query.get(0); } // If we have saved a preference for a preferred activity for // this Intent, use that. ResolveInfo ri = findPreferredActivityNotLocked(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; } @GuardedBy("mPackages") 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; jmust not hold {@link #mPackages} */ ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags, List query, int priority, boolean always, boolean removeMatches, boolean debug, int userId) { if (Thread.holdsLock(mPackages)) { Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding mPackages", new Throwable()); } if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); // Do NOT hold the packages lock; this calls up into the settings provider which // could cause a deadlock. final boolean isDeviceProvisioned = android.provider.Settings.Global.getInt(mContext.getContentResolver(), android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1; 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"); } } final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent) && !isDeviceProvisioned; if (ai == null) { // Do not remove launcher's preferred activity during SetupWizard // due to it may not install yet if (excludeSetupWizardHomeActivity) { continue; } // This previously registered preferred activity // component is no longer known. Most likely an update // to the app was installed and in the new version this // component no longer exists. Clean it up by removing // it from the preferred activities list, and skip it. Slog.w(TAG, "Removing dangling preferred activity: " + pa.mPref.mComponent); pir.removeFilter(pa); changed = true; continue; } for (int j=0; j matches = getMatchingCrossProfileIntentFilters(intent, resolvedType, sourceUserId); if (matches != null) { int size = matches.size(); for (int i = 0; i < size; i++) { if (matches.get(i).getTargetUserId() == targetUserId) return true; } } if (intent.hasWebURI()) { // cross-profile app linking works only towards the parent. final int callingUid = Binder.getCallingUid(); final UserInfo parent = getProfileParent(sourceUserId); synchronized(mPackages) { int flags = updateFlagsForResolve(0, parent.id, intent, callingUid, false /*includeInstantApps*/); CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr( intent, resolvedType, flags, sourceUserId, parent.id); return xpDomainInfo != null; } } return false; } private UserInfo getProfileParent(int userId) { final long identity = Binder.clearCallingIdentity(); try { return sUserManager.getProfileParent(userId); } finally { Binder.restoreCallingIdentity(identity); } } private List getMatchingCrossProfileIntentFilters(Intent intent, String resolvedType, int userId) { CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId); if (resolver != null) { return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId); } return null; } @Override public @NonNull ParceledListSlice queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); return new ParceledListSlice<>( queryIntentActivitiesInternal(intent, resolvedType, flags, userId)); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } /** * Returns the package name of the calling Uid if it's an instant app. If it isn't * instant, returns {@code null}. */ private String getInstantAppPackageName(int callingUid) { synchronized (mPackages) { // If the caller is an isolated app use the owner's uid for the lookup. if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final int appId = UserHandle.getAppId(callingUid); final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid)); return isInstantApp ? ps.pkg.packageName : null; } } return null; } private @NonNull List queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int userId) { return queryIntentActivitiesInternal( intent, resolvedType, flags, Binder.getCallingUid(), userId, false /*resolveForStart*/, true /*allowDynamicSplits*/); } private @NonNull List queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final String instantAppPkgName = getInstantAppPackageName(filterCallingUid); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "query intent activities"); final String pkgName = intent.getPackage(); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart, comp != null || pkgName != null /*onlyExposedExplicitly*/); if (comp != null) { final List list = new ArrayList<>(1); final ActivityInfo ai = getActivityInfo(comp, flags, userId); if (ai != null) { // When specifying an explicit component, we prevent the activity from being // used when either 1) the calling package is normal and the activity is within // an ephemeral application or 2) the calling package is ephemeral and the // activity is not visible to ephemeral applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean matchExplicitlyVisibleOnly = (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (ai.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetVisibleToInstantApp = (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean isTargetExplicitlyVisibleToInstantApp = isTargetVisibleToInstantApp && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; final boolean isTargetHiddenFromInstantApp = !isTargetVisibleToInstantApp || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp); final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } } return applyPostResolutionFilter( list, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // reader boolean sortResult = false; boolean addInstant = false; List result; synchronized (mPackages) { if (pkgName == null) { List matchingFilters = getMatchingCrossProfileIntentFilters(intent, resolvedType, userId); // Check for results that need to skip the current profile. ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent, resolvedType, flags, userId); if (xpResolveInfo != null) { List xpResult = new ArrayList<>(1); xpResult.add(xpResolveInfo); return applyPostResolutionFilter( filterIfNotSystemUser(xpResult, userId), instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // Check for results in the current profile. result = filterIfNotSystemUser(mComponentResolver.queryActivities( intent, resolvedType, flags, userId), userId); addInstant = isInstantAppResolutionAllowed(intent, result, userId, false /*skipPackageCheck*/); // Check for cross profile results. boolean hasNonNegativePriorityResult = hasNonNegativePriority(result); xpResolveInfo = queryCrossProfileIntents( matchingFilters, intent, resolvedType, flags, userId, hasNonNegativePriorityResult); if (xpResolveInfo != null && isUserEnabled(xpResolveInfo.targetUserId)) { boolean isVisibleToUser = filterIfNotSystemUser( Collections.singletonList(xpResolveInfo), userId).size() > 0; if (isVisibleToUser) { result.add(xpResolveInfo); sortResult = true; } } if (intent.hasWebURI()) { CrossProfileDomainInfo xpDomainInfo = null; final UserInfo parent = getProfileParent(userId); if (parent != null) { xpDomainInfo = getCrossProfileDomainPreferredLpr(intent, resolvedType, flags, userId, parent.id); } if (xpDomainInfo != null) { if (xpResolveInfo != null) { // If we didn't remove it, the cross-profile ResolveInfo would be twice // in the result. result.remove(xpResolveInfo); } if (result.size() == 0 && !addInstant) { // No result in current profile, but found candidate in parent user. // And we are not going to add emphemeral app, so we can return the // result straight away. result.add(xpDomainInfo.resolveInfo); return applyPostResolutionFilter(result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } } else if (result.size() <= 1 && !addInstant) { // No result in parent user and <= 1 result in current profile, and we // are not going to add emphemeral app, so we can return the result without // further processing. return applyPostResolutionFilter(result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } // We have more than one candidate (combining results from current and parent // profile), so we need filtering and sorting. result = filterCandidatesWithDomainPreferredActivitiesLPr( intent, flags, result, xpDomainInfo, userId); sortResult = true; } } else { final PackageParser.Package pkg = mPackages.get(pkgName); result = null; if (pkg != null) { result = filterIfNotSystemUser(mComponentResolver.queryActivities( intent, resolvedType, flags, pkg.activities, userId), userId); } if (result == null || result.size() == 0) { // the caller wants to resolve for a particular package; however, there // were no installed results, so, try to find an ephemeral result addInstant = isInstantAppResolutionAllowed( intent, null /*result*/, userId, true /*skipPackageCheck*/); if (result == null) { result = new ArrayList<>(); } } } } if (addInstant) { result = maybeAddInstantAppInstaller( result, intent, resolvedType, flags, userId, resolveForStart); } if (sortResult) { Collections.sort(result, RESOLVE_PRIORITY_SORTER); } return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart, userId, intent); } private List maybeAddInstantAppInstaller(List result, Intent intent, String resolvedType, int flags, int userId, boolean resolveForStart) { // first, check to see if we've got an instant app already installed final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0; ResolveInfo localInstantApp = null; boolean blockResolution = false; if (!alreadyResolvedLocally) { final List instantApps = mComponentResolver.queryActivities( intent, resolvedType, flags | PackageManager.GET_RESOLVED_FILTER | PackageManager.MATCH_INSTANT | PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY, userId); for (int i = instantApps.size() - 1; i >= 0; --i) { final ResolveInfo info = instantApps.get(i); final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps.getInstantApp(userId)) { final long packedStatus = getDomainVerificationStatusLPr(ps, userId); final int status = (int)(packedStatus >> 32); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { // there's a local instant application installed, but, the user has // chosen to never use it; skip resolution and don't acknowledge // an instant application is even available if (DEBUG_INSTANT) { Slog.v(TAG, "Instant app marked to never run; pkg: " + packageName); } blockResolution = true; break; } else { // we have a locally installed instant application; skip resolution // but acknowledge there's an instant application available if (DEBUG_INSTANT) { Slog.v(TAG, "Found installed instant app; pkg: " + packageName); } localInstantApp = info; break; } } } } // no app installed, let's see if one's available AuxiliaryResolveInfo auxiliaryResponse = null; if (!blockResolution) { if (localInstantApp == null) { // we don't have an instant app locally, resolve externally Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral"); final InstantAppRequest requestObject = new InstantAppRequest( null /*responseObj*/, intent /*origIntent*/, resolvedType, null /*callingPackage*/, userId, null /*verificationBundle*/, resolveForStart); auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne( mInstantAppResolverConnection, requestObject); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } else { // we have an instant application locally, but, we can't admit that since // callers shouldn't be able to determine prior browsing. create a dummy // auxiliary response so the downstream code behaves as if there's an // instant application available externally. when it comes time to start // the instant application, we'll do the right thing. final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo; auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */, ai.packageName, ai.longVersionCode, null /* splitName */); } } if (intent.isWebIntent() && auxiliaryResponse == null) { return result; } final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName); if (ps == null || !ps.readUserState(userId).isEnabled(mInstantAppInstallerActivity, 0)) { return result; } final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo); ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo( mInstantAppInstallerActivity, 0, ps.readUserState(userId), userId); ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART | IntentFilter.MATCH_ADJUSTMENT_NORMAL; // add a non-generic filter ephemeralInstaller.filter = new IntentFilter(); if (intent.getAction() != null) { ephemeralInstaller.filter.addAction(intent.getAction()); } if (intent.getData() != null && intent.getData().getPath() != null) { ephemeralInstaller.filter.addDataPath( intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL); } ephemeralInstaller.isInstantAppAvailable = true; // make sure this resolver is the default ephemeralInstaller.isDefault = true; ephemeralInstaller.auxiliaryInfo = auxiliaryResponse; if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } result.add(ephemeralInstaller); return result; } private static class CrossProfileDomainInfo { /* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */ ResolveInfo resolveInfo; /* Best domain verification status of the activities found in the other profile */ int bestDomainVerificationStatus; } private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType, int flags, int sourceUserId, int parentUserId) { if (!sUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, sourceUserId)) { return null; } List resultTargetUser = mComponentResolver.queryActivities(intent, resolvedType, flags, parentUserId); if (resultTargetUser == null || resultTargetUser.isEmpty()) { return null; } CrossProfileDomainInfo result = null; int size = resultTargetUser.size(); for (int i = 0; i < size; i++) { ResolveInfo riTargetUser = resultTargetUser.get(i); // Intent filter verification is only for filters that specify a host. So don't return // those that handle all web uris. if (riTargetUser.handleAllWebDataURI) { continue; } String packageName = riTargetUser.activityInfo.packageName; PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) { continue; } long verificationState = getDomainVerificationStatusLPr(ps, parentUserId); int status = (int)(verificationState >> 32); if (result == null) { result = new CrossProfileDomainInfo(); result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(), sourceUserId, parentUserId); result.bestDomainVerificationStatus = status; } else { result.bestDomainVerificationStatus = bestDomainVerificationStatus(status, result.bestDomainVerificationStatus); } } // Don't consider matches with status NEVER across profiles. if (result != null && result.bestDomainVerificationStatus == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return null; } return result; } /** * Verification statuses are ordered from the worse to the best, except for * INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse. */ private int bestDomainVerificationStatus(int status1, int status2) { if (status1 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return status2; } if (status2 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { return status1; } return (int) MathUtils.max(status1, status2); } private boolean isUserEnabled(int userId) { long callingId = Binder.clearCallingIdentity(); try { UserInfo userInfo = sUserManager.getUserInfo(userId); return userInfo != null && userInfo.isEnabled(); } finally { Binder.restoreCallingIdentity(callingId); } } /** * Filter out activities with systemUserOnly flag set, when current user is not System. * * @return filtered list */ private List filterIfNotSystemUser(List resolveInfos, int userId) { if (userId == UserHandle.USER_SYSTEM) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { ResolveInfo info = resolveInfos.get(i); if ((info.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) { resolveInfos.remove(i); } } return resolveInfos; } /** * Filters out ephemeral activities. * When resolving for an ephemeral app, only activities that 1) are defined in the * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned. * * @param resolveInfos The pre-filtered list of resolved activities * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering * is performed. * @param intent * @return A filtered list of resolved activities. */ private List applyPostResolutionFilter(List resolveInfos, String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, boolean resolveForStart, int userId, Intent intent) { final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId); for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); // remove locally resolved instant app web results when disabled if (info.isInstantAppAvailable && blockInstant) { resolveInfos.remove(i); continue; } // allow activities that are defined in the provided package if (allowDynamicSplits && info.activityInfo != null && info.activityInfo.splitName != null && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames, info.activityInfo.splitName)) { if (mInstantAppInstallerActivity == null) { if (DEBUG_INSTALL) { Slog.v(TAG, "No installer - not adding it to the ResolveInfo list"); } resolveInfos.remove(i); continue; } if (blockInstant && isInstantApp(info.activityInfo.packageName, userId)) { resolveInfos.remove(i); continue; } // requested activity is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTALL) { Slog.v(TAG, "Adding installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); final ComponentName installFailureActivity = findInstallFailureActivity( info.activityInfo.packageName, filterCallingUid, userId); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( installFailureActivity, info.activityInfo.packageName, info.activityInfo.applicationInfo.longVersionCode, info.activityInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // This resolve info may appear in the chooser UI, so let us make it // look as the one it replaces as far as the user is concerned which // requires loading the correct label and icon for the resolve info. installerInfo.resolvePackageName = info.getComponentInfo().packageName; installerInfo.labelRes = info.resolveLabelResId(); installerInfo.icon = info.resolveIconResId(); installerInfo.isInstantAppAvailable = true; resolveInfos.set(i, installerInfo); continue; } // caller is a full app, don't need to apply any other filtering if (ephemeralPkgName == null) { continue; } else if (ephemeralPkgName.equals(info.activityInfo.packageName)) { // caller is same app; don't need to apply any other filtering continue; } else if (resolveForStart && (intent.isWebIntent() || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) && intent.getPackage() == null && intent.getComponent() == null) { // ephemeral apps can launch other ephemeral apps indirectly continue; } // allow activities that have been explicitly exposed to ephemeral apps final boolean isEphemeralApp = info.activityInfo.applicationInfo.isInstantApp(); if (!isEphemeralApp && ((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } /** * Returns the activity component that can handle install failures. * By default, the instant application installer handles failures. However, an * application may want to handle failures on its own. Applications do this by * creating an activity with an intent filter that handles the action * {@link Intent#ACTION_INSTALL_FAILURE}. */ private @Nullable ComponentName findInstallFailureActivity( String packageName, int filterCallingUid, int userId) { final Intent failureActivityIntent = new Intent(Intent.ACTION_INSTALL_FAILURE); failureActivityIntent.setPackage(packageName); // IMPORTANT: disallow dynamic splits to avoid an infinite loop final List result = queryIntentActivitiesInternal( failureActivityIntent, null /*resolvedType*/, 0 /*flags*/, filterCallingUid, userId, false /*resolveForStart*/, false /*allowDynamicSplits*/); final int NR = result.size(); if (NR > 0) { for (int i = 0; i < NR; i++) { final ResolveInfo info = result.get(i); if (info.activityInfo.splitName != null) { continue; } return new ComponentName(packageName, info.activityInfo.name); } } return null; } /** * @param resolveInfos list of resolve infos in descending priority order * @return if the list contains a resolve info with non-negative priority */ private boolean hasNonNegativePriority(List resolveInfos) { return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0; } private List filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent, int matchFlags, List candidates, CrossProfileDomainInfo xpDomainInfo, int userId) { final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0; if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) { Slog.v(TAG, "Filtering results with preferred activities. Candidates count: " + candidates.size()); } final ArrayList result = new ArrayList<>(); final ArrayList alwaysList = new ArrayList<>(); final ArrayList undefinedList = new ArrayList<>(); final ArrayList alwaysAskList = new ArrayList<>(); final ArrayList neverList = new ArrayList<>(); final 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 = mComponentResolver.queryActivities(intent, resolvedType, flags, targetUserId); if (resultTargetUser != null && isUserEnabled(targetUserId)) { // If all the matches in the target profile are suspended, return null. for (int i = resultTargetUser.size() - 1; i >= 0; i--) { if ((resultTargetUser.get(i).activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SUSPENDED) == 0) { return createForwardingResolveInfoUnchecked(filter, sourceUserId, targetUserId); } } } return null; } private ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter, int sourceUserId, int targetUserId) { ResolveInfo forwardingResolveInfo = new ResolveInfo(); long ident = Binder.clearCallingIdentity(); boolean targetIsProfile; try { targetIsProfile = sUserManager.getUserInfo(targetUserId).isManagedProfile(); } finally { Binder.restoreCallingIdentity(ident); } String className; if (targetIsProfile) { className = FORWARD_INTENT_TO_MANAGED_PROFILE; } else { className = FORWARD_INTENT_TO_PARENT; } ComponentName forwardingActivityComponentName = new ComponentName( mAndroidApplication.packageName, className); ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0, sourceUserId); if (!targetIsProfile) { forwardingActivityInfo.showUserIcon = targetUserId; forwardingResolveInfo.noResourceId = true; } forwardingResolveInfo.activityInfo = forwardingActivityInfo; forwardingResolveInfo.priority = 0; forwardingResolveInfo.preferredOrder = 0; forwardingResolveInfo.match = 0; forwardingResolveInfo.isDefault = true; forwardingResolveInfo.filter = filter; forwardingResolveInfo.targetUserId = targetUserId; return forwardingResolveInfo; } @Override public @NonNull ParceledListSlice queryIntentActivityOptions(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>(queryIntentActivityOptionsInternal(caller, specifics, specificTypes, intent, resolvedType, flags, userId)); } private @NonNull List queryIntentActivityOptionsInternal(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent activity options"); final String resultsAction = intent.getAction(); final List results = queryIntentActivitiesInternal(intent, resolvedType, flags | PackageManager.GET_RESOLVED_FILTER, userId); if (DEBUG_INTENT_MATCHING) { Log.v(TAG, "Query " + intent + ": " + results); } int specificsPos = 0; int N; // todo: note that the algorithm used here is O(N^2). This // isn't a problem in our current environment, but if we start running // into situations where we have more than 5 or 10 matches then this // should probably be changed to something smarter... // First we go through and resolve each of the specific items // that were supplied, taking care of removing any corresponding // duplicate items in the generic resolve list. if (specifics != null) { for (int i=0; i it = rii.filter.actionsIterator(); if (it == null) { continue; } while (it.hasNext()) { final String action = it.next(); if (resultsAction != null && resultsAction.equals(action)) { // If this action was explicitly requested, then don't // remove things that have it. continue; } for (int j=i+1; j queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>( queryIntentReceiversInternal(intent, resolvedType, flags, userId, false /*allowDynamicSplits*/)); } private @NonNull List queryIntentReceiversInternal(Intent intent, String resolvedType, int flags, int userId, boolean allowDynamicSplits) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ActivityInfo ai = getReceiverInfo(comp, flags, userId); if (ai != null) { // When specifying an explicit component, we prevent the activity from being // used when either 1) the calling package is normal and the activity is within // an instant application or 2) the calling package is ephemeral and the // activity is not visible to instant applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean matchExplicitlyVisibleOnly = (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (ai.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetVisibleToInstantApp = (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean isTargetExplicitlyVisibleToInstantApp = isTargetVisibleToInstantApp && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; final boolean isTargetHiddenFromInstantApp = !isTargetVisibleToInstantApp || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp); final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } } return applyPostResolutionFilter( list, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { final List result = mComponentResolver.queryReceivers(intent, resolvedType, flags, userId); return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { final List result = mComponentResolver.queryReceivers( intent, resolvedType, flags, pkg.receivers, userId); return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } return Collections.emptyList(); } } @Override public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) { final int callingUid = Binder.getCallingUid(); return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid); } private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForResolve( flags, userId, intent, callingUid, false /*includeInstantApps*/); List query = queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/); if (query != null) { if (query.size() >= 1) { // If there is more than one service with the same priority, // just arbitrarily pick the first one. return query.get(0); } } return null; } @Override public @NonNull ParceledListSlice queryIntentServices(Intent intent, String resolvedType, int flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>(queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/)); } private @NonNull List queryIntentServicesInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps) { if (!sUserManager.exists(userId)) return Collections.emptyList(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ServiceInfo si = getServiceInfo(comp, flags, userId); if (si != null) { // When specifying an explicit component, we prevent the service from being // used when either 1) the service is in an instant application and the // caller is not the same instant application or 2) the calling package is // ephemeral and the activity is not visible to ephemeral applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (si.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetHiddenFromInstantApp = (si.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0; final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.serviceInfo = si; list.add(ri); } } return list; } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostServiceResolutionFilter( mComponentResolver.queryServices(intent, resolvedType, flags, userId), instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return applyPostServiceResolutionFilter( mComponentResolver.queryServices(intent, resolvedType, flags, pkg.services, userId), instantAppPkgName); } return Collections.emptyList(); } } private List applyPostServiceResolutionFilter(List resolveInfos, String instantAppPkgName) { if (instantAppPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp(); // allow services that are defined in the provided package if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) { if (info.serviceInfo.splitName != null && !ArrayUtils.contains(info.serviceInfo.applicationInfo.splitNames, info.serviceInfo.splitName)) { // requested service is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( null /* installFailureActivity */, info.serviceInfo.packageName, info.serviceInfo.applicationInfo.longVersionCode, info.serviceInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // load resources from the correct package installerInfo.resolvePackageName = info.getComponentInfo().packageName; resolveInfos.set(i, installerInfo); } continue; } // allow services that have been explicitly exposed to ephemeral apps if (!isEphemeralApp && ((info.serviceInfo.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } @Override public @NonNull ParceledListSlice queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>( queryIntentContentProvidersInternal(intent, resolvedType, flags, userId)); } private @NonNull List queryIntentContentProvidersInternal( Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ProviderInfo pi = getProviderInfo(comp, flags, userId); if (pi != null) { // When specifying an explicit component, we prevent the provider from being // used when either 1) the provider is in an instant application and the // caller is not the same instant application or 2) the calling package is an // instant application and the provider is not visible to instant applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (pi.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetHiddenFromInstantApp = (pi.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0; final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.providerInfo = pi; list.add(ri); } } return list; } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostContentProviderResolutionFilter( mComponentResolver.queryProviders(intent, resolvedType, flags, userId), instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return applyPostContentProviderResolutionFilter( mComponentResolver.queryProviders(intent, resolvedType, flags, pkg.providers, userId), instantAppPkgName); } return Collections.emptyList(); } } private List applyPostContentProviderResolutionFilter( List resolveInfos, String instantAppPkgName) { if (instantAppPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.providerInfo.applicationInfo.isInstantApp(); // allow providers that are defined in the provided package if (isEphemeralApp && instantAppPkgName.equals(info.providerInfo.packageName)) { if (info.providerInfo.splitName != null && !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames, info.providerInfo.splitName)) { // requested provider is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( null /*failureActivity*/, info.providerInfo.packageName, info.providerInfo.applicationInfo.longVersionCode, info.providerInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // load resources from the correct package installerInfo.resolvePackageName = info.getComponentInfo().packageName; resolveInfos.set(i, installerInfo); } continue; } // allow providers that have been explicitly exposed to instant applications if (!isEphemeralApp && ((info.providerInfo.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } @Override public ParceledListSlice getInstalledPackages(int flags, int userId) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return ParceledListSlice.emptyList(); } if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; final boolean listApex = (flags & MATCH_APEX) != 0; final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0; mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed packages"); // writer synchronized (mPackages) { ArrayList list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo(ps, flags, userId); if (pi != null) { list.add(pi); } } } else { list = new ArrayList<>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { final PackageSetting ps = (PackageSetting) p.mExtras; if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo((PackageSetting) p.mExtras, flags, userId); if (pi != null) { list.add(pi); } } } if (listApex) { if (listFactory) { list.addAll(mApexManager.getFactoryPackages()); } else { list.addAll(mApexManager.getActivePackages()); } if (listUninstalled) { list.addAll(mApexManager.getInactivePackages()); } } return new ParceledListSlice<>(list); } } private void addPackageHoldingPermissions(ArrayList list, PackageSetting ps, String[] permissions, boolean[] tmp, int flags, int userId) { int numMatch = 0; final PermissionsState permissionsState = ps.getPermissionsState(); for (int i=0; i getPackagesHoldingPermissions( String[] permissions, int flags, int userId) { if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, permissions); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "get packages holding permissions"); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; // writer synchronized (mPackages) { ArrayList list = new ArrayList<>(); boolean[] tmpBools = new boolean[permissions.length]; if (listUninstalled) { for (PackageSetting ps : mSettings.mPackages.values()) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } } else { for (PackageParser.Package pkg : mPackages.values()) { PackageSetting ps = (PackageSetting)pkg.mExtras; if (ps != null) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } } } return new ParceledListSlice<>(list); } } @Override public ParceledListSlice getInstalledApplications(int flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>( getInstalledApplicationsListInternal(flags, userId, callingUid)); } private List getInstalledApplicationsListInternal(int flags, int userId, int callingUid) { if (getInstantAppPackageName(callingUid) != null) { return Collections.emptyList(); } if (!sUserManager.exists(userId)) return Collections.emptyList(); flags = updateFlagsForApplication(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; mPermissionManager.enforceCrossUserPermission( callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed application info"); // writer synchronized (mPackages) { ArrayList list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { ApplicationInfo ai; int effectiveFlags = flags; if (ps.isSystem()) { effectiveFlags |= PackageManager.MATCH_ANY_USER; } if (ps.pkg != null) { if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(ps.pkg); } } else { // Shared lib filtering done in generateApplicationInfoFromSettingsLPw // and already converts to externally visible package name ai = generateApplicationInfoFromSettingsLPw(ps.name, callingUid, effectiveFlags, userId); } if (ai != null) { list.add(ai); } } } else { list = new ArrayList<>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { if (p.mExtras != null) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(p); list.add(ai); } } } } return list; } } @Override public ParceledListSlice getInstantApps(int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } if (!canViewInstantApps(Binder.getCallingUid(), userId)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getEphemeralApplications"); } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getEphemeralApplications"); synchronized (mPackages) { List instantApps = mInstantAppRegistry .getInstantAppsLPr(userId); if (instantApps != null) { return new ParceledListSlice<>(instantApps); } } return null; } @Override public boolean isInstantApp(String packageName, int userId) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "isInstantApp"); if (HIDE_EPHEMERAL_APIS) { return false; } synchronized (mPackages) { int callingUid = Binder.getCallingUid(); if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final PackageSetting ps = mSettings.mPackages.get(packageName); PackageParser.Package pkg = mPackages.get(packageName); final boolean returnAllowed = ps != null && (isCallerSameApp(packageName, callingUid) || canViewInstantApps(callingUid, userId) || mInstantAppRegistry.isInstantAccessGranted( userId, UserHandle.getAppId(callingUid), ps.appId)); if (returnAllowed) { return ps.getInstantApp(userId); } } return false; } @Override public byte[] getInstantAppCookie(String packageName, int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return null; } synchronized (mPackages) { return mInstantAppRegistry.getInstantAppCookieLPw( packageName, userId); } } @Override public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) { if (HIDE_EPHEMERAL_APIS) { return true; } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, true /* checkShell */, "setInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return false; } synchronized (mPackages) { return mInstantAppRegistry.setInstantAppCookieLPw( packageName, cookie, userId); } } @Override public Bitmap getInstantAppIcon(String packageName, int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } if (!canViewInstantApps(Binder.getCallingUid(), userId)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getInstantAppIcon"); } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppIcon"); synchronized (mPackages) { return mInstantAppRegistry.getInstantAppIconLPw( packageName, userId); } } private boolean isCallerSameApp(String packageName, int uid) { PackageParser.Package pkg = mPackages.get(packageName); return pkg != null && UserHandle.getAppId(uid) == pkg.applicationInfo.uid; } @Override public @NonNull ParceledListSlice getPersistentApplications(int flags) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(getPersistentApplicationsInternal(flags)); } private @NonNull List getPersistentApplicationsInternal(int flags) { final ArrayList finalList = new ArrayList<>(); // reader synchronized (mPackages) { final Iterator i = mPackages.values().iterator(); final int userId = UserHandle.getCallingUserId(); while (i.hasNext()) { final PackageParser.Package p = i.next(); if (p.applicationInfo == null) continue; final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0) && !p.applicationInfo.isDirectBootAware(); final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0) && p.applicationInfo.isDirectBootAware(); if ((p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0 && (!mSafeMode || isSystemApp(p)) && (matchesUnaware || matchesAware)) { PackageSetting ps = mSettings.mPackages.get(p.packageName); if (ps != null) { ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); if (ai != null) { finalList.add(ai); } } } } } return finalList; } @Override public ProviderInfo resolveContentProvider(String name, int flags, int userId) { return resolveContentProviderInternal(name, flags, userId); } private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId, name); final int callingUid = Binder.getCallingUid(); final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId); if (providerInfo == null) { return null; } if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { return null; } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { return null; } return providerInfo; } } /** * @deprecated */ @Deprecated public void querySyncProviders(List outNames, List outInfo) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; } mComponentResolver.querySyncProviders( outNames, outInfo, mSafeMode, UserHandle.getCallingUserId()); } @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; final List matchList = mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId); final int listSize = (matchList == null ? 0 : matchList.size()); synchronized (mPackages) { for (int i = 0; i < listSize; i++) { final ProviderInfo providerInfo = matchList.get(i); if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { continue; } final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { continue; } if (finalList == null) { finalList = new ArrayList<>(listSize - i); } finalList.add(providerInfo); } } if (finalList != null) { finalList.sort(sProviderInitOrderSorter); return new ParceledListSlice<>(finalList); } return ParceledListSlice.emptyList(); } @Override public InstrumentationInfo getInstrumentationInfo(ComponentName component, int flags) { // reader synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) { return null; } final PackageParser.Instrumentation i = mInstrumentation.get(component); return PackageParser.generateInstrumentationInfo(i, flags); } } @Override public @NonNull ParceledListSlice queryInstrumentation( String targetPackage, int flags) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(targetPackage); if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags)); } private @NonNull List queryInstrumentationInternal(String targetPackage, int flags) { ArrayList finalList = new ArrayList<>(); // reader synchronized (mPackages) { final Iterator i = mInstrumentation.values().iterator(); while (i.hasNext()) { final PackageParser.Instrumentation p = i.next(); if (targetPackage == null || targetPackage.equals(p.info.targetPackage)) { InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p, flags); if (ii != null) { finalList.add(ii); } } } } return finalList; } private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]"); try { scanDirLI(scanDir, parseFlags, scanFlags, currentTime); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) { final File[] files = scanDir.listFiles(); if (ArrayUtils.isEmpty(files)) { Log.d(TAG, "No files in app dir " + scanDir); return; } if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags + " flags=0x" + Integer.toHexString(parseFlags)); } try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser( mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mParallelPackageParserCallback)) { // Submit files for parsing in parallel int fileCount = 0; for (File file : files) { final boolean isPackage = (isApkFile(file) || file.isDirectory()) && !PackageInstallerService.isStageName(file.getName()); if (!isPackage) { // Ignore entries which are not packages continue; } parallelPackageParser.submit(file, parseFlags); fileCount++; } // Process results one by one for (; fileCount > 0; fileCount--) { ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take(); Throwable throwable = parseResult.throwable; int errorCode = PackageManager.INSTALL_SUCCEEDED; if (throwable == null) { // TODO(toddke): move lower in the scan chain // Static shared libraries have synthetic package names if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(parseResult.pkg); } try { scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags, currentTime, null); } catch (PackageManagerException e) { errorCode = e.error; Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage()); } } else if (throwable instanceof PackageParser.PackageParserException) { PackageParser.PackageParserException e = (PackageParser.PackageParserException) throwable; errorCode = e.error; Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage()); } else { throw new IllegalStateException("Unexpected exception occurred while parsing " + parseResult.scanFile, throwable); } // Delete invalid userdata apps if ((scanFlags & SCAN_AS_SYSTEM) == 0 && errorCode != PackageManager.INSTALL_SUCCEEDED) { logCriticalInfo(Log.WARN, "Deleting invalid package at " + parseResult.scanFile); removeCodePathLI(parseResult.scanFile); } } } } public static void reportSettingsProblem(int priority, String msg) { logCriticalInfo(priority, msg); } private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, boolean forceCollect, boolean skipVerify) throws PackageManagerException { // When upgrading from pre-N MR1, verify the package time stamp using the package // directory and not the APK file. final long lastModifiedTime = mIsPreNMR1Upgrade ? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg); final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(pkg); if (ps != null && !forceCollect && ps.codePathString.equals(pkg.codePath) && ps.timeStamp == lastModifiedTime && !isCompatSignatureUpdateNeeded(settingsVersionForPackage) && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) { if (ps.signatures.mSigningDetails.signatures != null && ps.signatures.mSigningDetails.signatures.length != 0 && ps.signatures.mSigningDetails.signatureSchemeVersion != SignatureSchemeVersion.UNKNOWN) { // Optimization: reuse the existing cached signing data // if the package appears to be unchanged. pkg.mSigningDetails = new PackageParser.SigningDetails(ps.signatures.mSigningDetails); return; } Slog.w(TAG, "PackageSetting for " + ps.name + " is missing signatures. Collecting certs again to recover them."); } else { Slog.i(TAG, pkg.codePath + " changed; collecting certs" + (forceCollect ? " (forced)" : "")); } try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); PackageParser.collectCertificates(pkg, skipVerify); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } /** * Clear the package profile if this was an upgrade and the package * version was updated. */ private void maybeClearProfilesForUpgradesLI( @Nullable PackageSetting originalPkgSetting, @NonNull PackageParser.Package currentPkg) { if (originalPkgSetting == null || !isDeviceUpgrading()) { return; } if (originalPkgSetting.versionCode == currentPkg.mVersionCode) { return; } clearAppProfilesLIF(currentPkg, UserHandle.USER_ALL); if (DEBUG_INSTALL) { Slog.d(TAG, originalPkgSetting.name + " clear profile due to version change " + originalPkgSetting.versionCode + " != " + currentPkg.mVersionCode); } } /** * Traces a package scan. * @see #scanPackageLI(File, int, int, long, UserHandle) */ @GuardedBy({"mInstallLock", "mPackages"}) 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 */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); pp.setDisplayMetrics(mMetrics); pp.setCallback(mPackageParserCallback); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); final PackageParser.Package pkg; try { pkg = pp.parsePackage(scanFile, parseFlags); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } // Static shared libraries have synthetic package names if (pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(pkg); } return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user); } /** * Scans a package and returns the newly parsed package. * @throws PackageManagerException on a parse error. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg, final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { // If the package has children and this is the first dive in the function // we scan the package with the SCAN_CHECK_ONLY flag set to see whether all // packages (parent and children) would be successfully scanned before the // actual scan since scanning mutates internal state and we want to atomically // install the package and its children. if ((scanFlags & SCAN_CHECK_ONLY) == 0) { if (pkg.childPackages != null && pkg.childPackages.size() > 0) { scanFlags |= SCAN_CHECK_ONLY; } } else { scanFlags &= ~SCAN_CHECK_ONLY; } // Scan the parent PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags, scanFlags, currentTime, user); // Scan the children final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPackage = pkg.childPackages.get(i); addForInitLI(childPackage, parseFlags, scanFlags, currentTime, user); } if ((scanFlags & SCAN_CHECK_ONLY) != 0) { return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user); } return scannedPkg; } /** * Returns if forced apk verification can be skipped for the whole package, including splits. */ private boolean canSkipForcedPackageVerification(PackageParser.Package pkg) { if (!canSkipForcedApkVerification(pkg.baseCodePath)) { return false; } // TODO: Allow base and splits to be verified individually. if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { for (int i = 0; i < pkg.splitCodePaths.length; i++) { if (!canSkipForcedApkVerification(pkg.splitCodePaths[i])) { return false; } } } return true; } /** * Returns if forced apk verification can be skipped, depending on current FSVerity setup and * whether the apk contains signed root hash. Note that the signer's certificate still needs to * match one in a trusted source, and should be done separately. */ private boolean canSkipForcedApkVerification(String apkPath) { if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) { return VerityUtils.hasFsverity(apkPath); } try { final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath); if (rootHashObserved == null) { return false; // APK does not contain Merkle tree root hash. } synchronized (mInstallLock) { // Returns whether the observed root hash matches what kernel has. mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved); return true; } } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) { Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e); } return false; } /** * Adds a new package to the internal data structures during platform initialization. * After adding, the package is known to the system and available for querying. * For packages located on the device ROM [eg. packages located in /system, /vendor, * etc...], additional checks are performed. Basic verification [such as ensuring * matching signatures, checking version codes, etc...] occurs if the package is * identical to a previously known package. If the package fails a signature check, * the version installed on /data will be removed. If the version of the new package * is less than or equal than the version on /data, it will be ignored. * Regardless of the package location, the results are applied to the internal * structures and the package is made available to the rest of the system. * NOTE: The return value should be removed. It's the passed in package object. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package addForInitLI(PackageParser.Package pkg, @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { final boolean scanSystemPartition = (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0; final String renamedPkgName; final PackageSetting disabledPkgSetting; final boolean isSystemPkgUpdated; final boolean pkgAlreadyExists; PackageSetting pkgSetting; // NOTE: installPackageLI() has the same code to setup the package's // application info. This probably should be done lower in the call // stack [such as scanPackageOnly()]. However, we verify the application // info prior to that [in scanPackageNew()] and thus have to setup // the application info early. pkg.setApplicationVolumeUuid(pkg.volumeUuid); pkg.setApplicationInfoCodePath(pkg.codePath); pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath); pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths); pkg.setApplicationInfoResourcePath(pkg.codePath); pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath); pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths); synchronized (mPackages) { renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage); final String realPkgName = getRealPackageName(pkg, renamedPkgName); if (realPkgName != null) { ensurePackageRenamed(pkg, renamedPkgName); } final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName); final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName); pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting; pkgAlreadyExists = pkgSetting != null; final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName; disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName); isSystemPkgUpdated = disabledPkgSetting != null; if (DEBUG_INSTALL && isSystemPkgUpdated) { Slog.d(TAG, "updatedPkg = " + disabledPkgSetting); } final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null) ? mSettings.getSharedUserLPw(pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true) : null; if (DEBUG_PACKAGE_SCANNING && (parseFlags & PackageParser.PARSE_CHATTY) != 0 && sharedUserSetting != null) { Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + sharedUserSetting.userId + "):" + " packages=" + sharedUserSetting.packages); } if (scanSystemPartition) { // Potentially prune child packages. If the application on the /system // partition has been updated via OTA, but, is still disabled by a // version on /data, cycle through all of its children packages and // remove children that are no longer defined. if (isSystemPkgUpdated) { final int scannedChildCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; final int disabledChildCount = disabledPkgSetting.childPackageNames != null ? disabledPkgSetting.childPackageNames.size() : 0; for (int i = 0; i < disabledChildCount; i++) { String disabledChildPackageName = disabledPkgSetting.childPackageNames.get(i); boolean disabledPackageAvailable = false; for (int j = 0; j < scannedChildCount; j++) { PackageParser.Package childPkg = pkg.childPackages.get(j); if (childPkg.packageName.equals(disabledChildPackageName)) { disabledPackageAvailable = true; break; } } if (!disabledPackageAvailable) { mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName); } } // we're updating the disabled package, so, scan it as the package setting final ScanRequest request = new ScanRequest(pkg, sharedUserSetting, null, disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */, null /* originalPkgSetting */, null, parseFlags, scanFlags, (pkg == mPlatformPackage), user); applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage); final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L); if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) { scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting); } } } } final boolean newPkgChangedPaths = pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath); final boolean newPkgVersionGreater = pkgAlreadyExists && pkg.getLongVersionCode() > pkgSetting.versionCode; final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated && newPkgChangedPaths && newPkgVersionGreater; if (isSystemPkgBetter) { // The version of the application on /system is greater than the version on // /data. Switch back to the application on /system. // It's safe to assume the application on /system will correctly scan. If not, // there won't be a working copy of the application. synchronized (mPackages) { // just remove the loaded entries from package lists mPackages.remove(pkgSetting.name); } logCriticalInfo(Log.WARN, "System package updated;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); final InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); args.cleanUpResourcesLI(); synchronized (mPackages) { mSettings.enableSystemPackageLPw(pkgSetting.name); } } if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) { // The version of the application on the /system partition is less than or // equal to the version on the /data partition. Throw an exception and use // the application already installed on the /data partition. throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at " + pkg.codePath + " ignored: updated version " + pkgSetting.versionCode + " better than this " + pkg.getLongVersionCode()); } // Verify certificates against what was last scanned. If there was an upgrade and this is an // app in a system partition, or if this is an updated priv app, we will force re-collecting // certificate. final boolean forceCollect = (mIsUpgrade && scanSystemPartition) || PackageManagerServiceUtils.isApkVerificationForced(disabledPkgSetting); // Full APK verification can be skipped during certificate collection, only if the file is // in verified partition, or can be verified on access (when apk verity is enabled). In both // cases, only data in Signing Block is verified instead of the whole file. final boolean skipVerify = scanSystemPartition || (forceCollect && canSkipForcedPackageVerification(pkg)); collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify); // Reset profile if the application version is changed maybeClearProfilesForUpgradesLI(pkgSetting, pkg); /* * A new system app appeared, but we already had a non-system one of the * same name installed earlier. */ boolean shouldHideSystemApp = false; // A new application appeared on /system, but, we already have a copy of // the application installed on /data. if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists && !pkgSetting.isSystem()) { if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA) && !pkgSetting.signatures.mSigningDetails.checkCapability( pkg.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) { logCriticalInfo(Log.WARN, "System package signature mismatch;" + " name: " + pkgSetting.name); try (PackageFreezer freezer = freezePackage(pkg.packageName, "scanPackageInternalLI")) { deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null); } pkgSetting = null; } else if (newPkgVersionGreater) { // The application on /system is newer than the application on /data. // Simply remove the application on /data [keeping application data] // and replace it with the version on /system. logCriticalInfo(Log.WARN, "System package enabled;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } } else { // The application on /system is older than the application on /data. Hide // the application on /system and the version on /data will be scanned later // and re-added like an update. shouldHideSystemApp = true; logCriticalInfo(Log.INFO, "System package disabled;" + " name: " + pkgSetting.name + "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode + "; new: " + pkg.codePath + " @ " + pkg.codePath); } } final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user); if (scanResult.success) { synchronized (mPackages) { boolean appIdCreated = false; try { final String pkgName = scanResult.pkgSetting.name; final Map reconcileResult = reconcilePackagesLocked( new ReconcileRequest( Collections.singletonMap(pkgName, scanResult), mSharedLibraries, mPackages, Collections.singletonMap( pkgName, getSettingsVersionForPackage(pkg)), Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))), mSettings.mKeySetManagerService); appIdCreated = optimisticallyRegisterAppId(scanResult); commitReconciledScanResultLocked(reconcileResult.get(pkgName)); } catch (PackageManagerException e) { if (appIdCreated) { cleanUpAppIdCreation(scanResult); } throw e; } } } if (shouldHideSystemApp) { synchronized (mPackages) { mSettings.disableSystemPackageLPw(pkg.packageName, true); } } return scanResult.pkgSetting.pkg; } private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) { // Derive the new package synthetic package name pkg.setPackageName(pkg.packageName + STATIC_SHARED_LIB_DELIMITER + pkg.staticSharedLibVersion); } static String fixProcessName(String defProcessName, String processName) { if (processName == null) { return defProcessName; } return processName; } /** * Enforces that only the system UID or root's UID can call a method exposed * via Binder. * * @param message used as message if SecurityException is thrown * @throws SecurityException if the caller is not system or root */ private static void enforceSystemOrRoot(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) { throw new SecurityException(message); } } /** * Enforces that only the system UID or root's UID or shell'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 shell */ private static void enforceSystemOrRootOrShell(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID && uid != Process.SHELL_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 = isDeviceUpgrading(); // First boot or factory reset. // Note: we also handle devices that are upgrading to N right now as if it is their // first boot, as they do not have profile data. boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade; // We need to re-extract after a pruned cache, as AoT-ed files will be out of date. boolean causePrunedCache = VMRuntime.didPruneDalvikCache(); if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) { return; } List pkgs; synchronized (mPackages) { pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this); } final long startTime = System.nanoTime(); final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */, causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT, false /* bootComplete */); final int elapsedTimeSeconds = (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime); MetricsLogger.histogram(mContext, "opt_dialog_num_dexopted", stats[0]); MetricsLogger.histogram(mContext, "opt_dialog_num_skipped", stats[1]); MetricsLogger.histogram(mContext, "opt_dialog_num_failed", stats[2]); MetricsLogger.histogram(mContext, "opt_dialog_num_total", getOptimizablePackages().size()); MetricsLogger.histogram(mContext, "opt_dialog_time_s", elapsedTimeSeconds); } /* * Return the prebuilt profile path given a package base code path. */ private static String getPrebuildProfilePath(PackageParser.Package pkg) { return pkg.baseCodePath + ".prof"; } /** * Performs dexopt on the set of packages in {@code packages} and returns an int array * containing statistics about the invocation. The array consists of three elements, * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped} * and {@code numberOfPackagesFailed}. */ private int[] performDexOptUpgrade(List pkgs, boolean showDialog, final int compilationReason, boolean bootComplete) { int numberOfPackagesVisited = 0; int numberOfPackagesOptimized = 0; int numberOfPackagesSkipped = 0; int numberOfPackagesFailed = 0; final int numberOfPackagesToDexopt = pkgs.size(); for (PackageParser.Package pkg : pkgs) { numberOfPackagesVisited++; boolean useProfileForDexopt = false; if ((isFirstBoot() || isDeviceUpgrading()) && isSystemApp(pkg)) { // Copy over initial preopt profiles since we won't get any JIT samples for methods // that are already compiled. File profileFile = new File(getPrebuildProfilePath(pkg)); // Copy profile if it exists. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The issue // is that we don't have a good way to say "do this only once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Installer failed to copy system profile!"); } else { // Disabled as this causes speed-profile compilation during first boot // even if things are already compiled. // useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } else { PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName); // Handle compressed APKs in this path. Only do this for stubs with profiles to // minimize the number off apps being speed-profile compiled during first boot. // The other paths will not change the filter. if (disabledPs != null && disabledPs.pkg.isStub) { // The package is the stub one, remove the stub suffix to get the normal // package and APK names. String systemProfilePath = getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, ""); profileFile = new File(systemProfilePath); // If we have a profile for a compressed APK, copy it to the reference // location. // Note that copying the profile here will cause it to override the // reference profile every OTA even though the existing reference profile // may have more data. We can't copy during decompression since the // directories are not set up at that point. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The // issue is that we don't have a good way to say "do this only // once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Failed to copy system profile for stub package!"); } else { useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } } } } if (!PackageDexOptimizer.canOptimizePackage(pkg)) { if (DEBUG_DEXOPT) { Log.i(TAG, "Skipping update of of non-optimizable app " + pkg.packageName); } numberOfPackagesSkipped++; continue; } if (DEBUG_DEXOPT) { Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " + numberOfPackagesToDexopt + ": " + pkg.packageName); } if (showDialog) { try { ActivityManager.getService().showBootMessage( mContext.getResources().getString(R.string.android_upgrading_apk, numberOfPackagesVisited, numberOfPackagesToDexopt), true); } catch (RemoteException e) { } synchronized (mPackages) { mDexOptDialogShown = true; } } int pkgCompilationReason = compilationReason; if (useProfileForDexopt) { // Use background dexopt mode to try and use the profile. Note that this does not // guarantee usage of the profile. pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT; } if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { mArtManagerService.compileLayouts(pkg); } // checkProfiles is false to avoid merging profiles during boot which // might interfere with background compilation (b/28612421). // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a // trade-off worth doing to save boot time work. int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0; if (compilationReason == REASON_FIRST_BOOT) { // TODO: This doesn't cover the upgrade case, we should check for this too. dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE; } int primaryDexOptStaus = performDexOptTraced(new DexoptOptions( pkg.packageName, pkgCompilationReason, dexoptFlags)); switch (primaryDexOptStaus) { case PackageDexOptimizer.DEX_OPT_PERFORMED: numberOfPackagesOptimized++; break; case PackageDexOptimizer.DEX_OPT_SKIPPED: numberOfPackagesSkipped++; break; case PackageDexOptimizer.DEX_OPT_FAILED: numberOfPackagesFailed++; break; default: Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus); break; } } return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped, numberOfPackagesFailed }; } @Override public void notifyPackageUse(String packageName, int reason) { synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); if (getInstantAppPackageName(callingUid) != null) { if (!isCallerSameApp(packageName, callingUid)) { return; } } else { if (isInstantApp(packageName, callingUserId)) { return; } } notifyPackageUseLocked(packageName, reason); } } @GuardedBy("mPackages") public CheckPermissionDelegate getCheckPermissionDelegateLocked() { return mCheckPermissionDelegate; } @GuardedBy("mPackages") public void setCheckPermissionDelegateLocked(CheckPermissionDelegate delegate) { mCheckPermissionDelegate = delegate; } @GuardedBy("mPackages") private void notifyPackageUseLocked(String packageName, int reason) { final PackageParser.Package p = mPackages.get(packageName); if (p == null) { return; } p.mLastPackageUsageTimeInMills[reason] = System.currentTimeMillis(); } @Override public void notifyDexLoad(String loadingPackageName, List classLoaderNames, List classPaths, String loaderIsa) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); if (ai == null) { Slog.w(TAG, "Loading a package that does not exist for the calling user. package=" + loadingPackageName + ", user=" + userId); return; } mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId); } @Override public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule, IDexModuleRegisterCallback callback) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(packageName, /*flags*/ 0, userId); DexManager.RegisterDexModuleResult result; if (ai == null) { Slog.w(TAG, "Registering a dex module for a package that does not exist for the" + " calling user. package=" + packageName + ", user=" + userId); result = new DexManager.RegisterDexModuleResult(false, "Package not installed"); } else { result = mDexManager.registerDexModule(ai, dexModulePath, isSharedModule, userId); } if (callback != null) { mHandler.post(() -> { try { callback.onDexModuleRegistered(dexModulePath, result.success, result.message); } catch (RemoteException e) { Slog.w(TAG, "Failed to callback after module registration " + dexModulePath, e); } }); } } /** * Ask the package manager to perform a dex-opt with the given compiler filter. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptMode(String packageName, boolean checkProfiles, String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) { int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) | (force ? DexoptOptions.DEXOPT_FORCE : 0) | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0); return performDexOpt(new DexoptOptions(packageName, REASON_UNKNOWN, targetCompilerFilter, splitName, flags)); } /** * Ask the package manager to perform a dex-opt with the given compiler filter on the * secondary dex files belonging to the given package. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptSecondary(String packageName, String compilerFilter, boolean force) { int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES | DexoptOptions.DEXOPT_BOOT_COMPLETE | (force ? DexoptOptions.DEXOPT_FORCE : 0); return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags)); } /** * Ask the package manager to compile layouts in the given package. */ @Override public boolean compileLayouts(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { return false; } } return mViewCompiler.compileLayouts(pkg); } /*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 = findSharedLibraries(p); final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo); if (!deps.isEmpty()) { DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(), options.getCompilationReason(), options.getCompilerFilter(), options.getSplitName(), options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY); for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = null; synchronized (mPackages) { depPackage = mPackages.get(info.getPackageName()); } if (depPackage != null) { // TODO: Analyze and investigate if we (should) profile libraries. pdo.performDexOpt(depPackage, instructionSets, getOrCreateCompilerPackageStats(depPackage), mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions); } else { // TODO(ngeoffray): Support dexopting system shared libraries. } } } return pdo.performDexOpt(p, instructionSets, getOrCreateCompilerPackageStats(p), mDexManager.getPackageUseInfoOrDefault(p.packageName), options); } /** * Reconcile the information we have about the secondary dex files belonging to * {@code packageName} 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(@Nullable List packageNames) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return false; } enforceSystemOrRootOrShell("runBackgroundDexoptJob"); final long identity = Binder.clearCallingIdentity(); try { return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames); } finally { Binder.restoreCallingIdentity(identity); } } private static List findSharedLibraries(PackageParser.Package p) { if (p.usesLibraryInfos != null) { ArrayList retValue = new ArrayList<>(); Set collectedNames = new HashSet<>(); for (SharedLibraryInfo info : p.usesLibraryInfos) { findSharedLibrariesRecursive(info, retValue, collectedNames); } return retValue; } else { return Collections.emptyList(); } } private static void findSharedLibrariesRecursive(SharedLibraryInfo info, ArrayList collected, Set collectedNames) { if (!collectedNames.contains(info.getName())) { collectedNames.add(info.getName()); collected.add(info); if (info.getDependencies() != null) { for (SharedLibraryInfo dep : info.getDependencies()) { findSharedLibrariesRecursive(dep, collected, collectedNames); } } } } List findSharedNonSystemLibraries(PackageParser.Package pkg) { List deps = findSharedLibraries(pkg); if (!deps.isEmpty()) { ArrayList retValue = new ArrayList<>(); synchronized (mPackages) { for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = mPackages.get(info.getPackageName()); if (depPackage != null) { retValue.add(depPackage); } } } return retValue; } else { return Collections.emptyList(); } } @Nullable private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) { return getSharedLibraryInfo(name, version, mSharedLibraries, null); } @Nullable private static SharedLibraryInfo getSharedLibraryInfo(String name, long version, Map> existingLibraries, @Nullable Map> newLibraries) { if (newLibraries != null) { final LongSparseArray versionedLib = newLibraries.get(name); SharedLibraryInfo info = null; if (versionedLib != null) { info = versionedLib.get(version); } if (info != null) { return info; } } final LongSparseArray versionedLib = existingLibraries.get(name); if (versionedLib == null) { return null; } return versionedLib.get(version); } private SharedLibraryInfo getLatestSharedLibraVersionLPr(PackageParser.Package pkg) { LongSparseArray versionedLib = mSharedLibraries.get( pkg.staticSharedLibName); if (versionedLib == null) { return null; } long previousLibVersion = -1; final int versionCount = versionedLib.size(); for (int i = 0; i < versionCount; i++) { final long libVersion = versionedLib.keyAt(i); if (libVersion < pkg.staticSharedLibVersion) { previousLibVersion = Math.max(previousLibVersion, libVersion); } } if (previousLibVersion >= 0) { return versionedLib.get(previousLibVersion); } return null; } @Nullable private PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) { PackageSetting sharedLibPackage = null; synchronized (mPackages) { final SharedLibraryInfo latestSharedLibraVersionLPr = getLatestSharedLibraVersionLPr(scanResult.pkgSetting.pkg); if (latestSharedLibraVersionLPr != null) { sharedLibPackage = mSettings.getPackageLPr( latestSharedLibraVersionLPr.getPackageName()); } } return sharedLibPackage; } public void shutdown() { mPackageUsage.writeNow(mPackages); mCompilerStats.writeNow(); mDexManager.writePackageDexUsageNow(); PackageWatchdog.getInstance(mContext).writeNow(); // This is the last chance to write out pending restriction settings synchronized (mPackages) { if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS); for (int userId : mDirtyUsers) { mSettings.writePackageRestrictionsLPr(userId); } mDirtyUsers.clear(); } } } @Override public void dumpProfiles(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } /* Only the shell, root, or the app user should be able to dump profiles. */ int callingUid = Binder.getCallingUid(); if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID && callingUid != pkg.applicationInfo.uid) { throw new SecurityException("dumpProfiles"); } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles"); mArtManagerService.dumpProfiles(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @Override public void forceDexOpt(String packageName) { enforceSystemOrRoot("forceDexOpt"); PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); // Whoever is calling forceDexOpt wants a compiled package. // Don't use profiles since that may cause compilation to be skipped. final int res = performDexOptInternalWithDependenciesLI( pkg, new DexoptOptions(packageName, getDefaultCompilerFilter(), DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE)); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) { throw new IllegalStateException("Failed to dexopt: " + res); } } } @GuardedBy("mPackages") private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) { if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package not in system partition"); return false; } else if (mPackages.get(oldPkg.name) != null) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package still exists"); return false; } return true; } @GuardedBy("mInstallLock") void removeCodePathLI(File codePath) { if (codePath.isDirectory()) { try { mInstaller.rmPackageDir(codePath.getAbsolutePath()); } catch (InstallerException e) { Slog.w(TAG, "Failed to remove code path", e); } } else { codePath.delete(); } } private int[] resolveUserIds(int userId) { return (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds() : new int[] { userId }; } private void clearAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } clearAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { clearAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } clearAppProfilesLIF(pkg, UserHandle.USER_ALL); } private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } } private void destroyAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } } private void destroyAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.destroyAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId); } } private void destroyAppProfilesLIF(PackageParser.Package pkg) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppProfilesLeafLIF(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppProfilesLeafLIF(pkg.childPackages.get(i)); } } private void destroyAppProfilesLeafLIF(PackageParser.Package pkg) { try { mInstaller.destroyAppProfiles(pkg.packageName); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } private void clearAppProfilesLIF(PackageParser.Package pkg, int userId) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } mArtManagerService.clearAppProfiles(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { mArtManagerService.clearAppProfiles(pkg.childPackages.get(i)); } } private void setInstallAndUpdateTime(PackageParser.Package pkg, long firstInstallTime, long lastUpdateTime) { // Set parent install/update time PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } // Set children install/update time final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPkg = pkg.childPackages.get(i); ps = (PackageSetting) childPkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } } } @GuardedBy("mPackages") private void applyDefiningSharedLibraryUpdateLocked( PackageParser.Package pkg, SharedLibraryInfo libInfo, BiConsumer action) { // Note that libraries defined by this package may be null if: // - Package manager was unable to create the shared library. The package still // gets installed, but the shared library does not get created. // Or: // - Package manager is in a state where package isn't scanned yet. This will // get called again after scanning to fix the dependencies. if (pkg.isLibrary()) { if (pkg.staticSharedLibName != null) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( pkg.staticSharedLibName, pkg.staticSharedLibVersion); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } else { for (String libraryName : pkg.libraryNames) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( libraryName, SharedLibraryInfo.VERSION_UNDEFINED); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } } } } @GuardedBy("mPackages") private void addSharedLibraryLPr(PackageParser.Package pkg, Set usesLibraryFiles, SharedLibraryInfo libInfo, PackageParser.Package changingLib) { if (libInfo.getPath() != null) { usesLibraryFiles.add(libInfo.getPath()); return; } PackageParser.Package p = mPackages.get(libInfo.getPackageName()); if (changingLib != null && changingLib.packageName.equals(libInfo.getPackageName())) { // If we are doing this while in the middle of updating a library apk, // then we need to make sure to use that new apk for determining the // dependencies here. (We haven't yet finished committing the new apk // to the package manager state.) if (p == null || p.packageName.equals(changingLib.packageName)) { p = changingLib; } } if (p != null) { usesLibraryFiles.addAll(p.getAllCodePaths()); // If the package provides libraries, add the dependency to them. applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, (definingLibrary, dependency) -> { definingLibrary.addDependency(dependency); }); if (p.usesLibraryFiles != null) { Collections.addAll(usesLibraryFiles, p.usesLibraryFiles); } } } @GuardedBy("mPackages") private void updateSharedLibrariesLocked(PackageParser.Package pkg, PackageParser.Package changingLib, Map availablePackages) throws PackageManagerException { final ArrayList sharedLibraryInfos = collectSharedLibraryInfos(pkg, availablePackages, mSharedLibraries, null); executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos); } private static ArrayList collectSharedLibraryInfos(PackageParser.Package pkg, Map availablePackages, @NonNull final Map> existingLibraries, @Nullable final Map> newLibraries) throws PackageManagerException { if (pkg == null) { return null; } // The collection used here must maintain the order of addition (so // that libraries are searched in the correct order) and must have no // duplicates. ArrayList usesLibraryInfos = null; if (pkg.usesLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesLibraries, null, null, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, null, availablePackages, existingLibraries, newLibraries); } if (pkg.usesStaticLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } if (pkg.usesOptionalLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesOptionalLibraries, null, null, pkg.packageName, false, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } return usesLibraryInfos; } private void executeSharedLibrariesUpdateLPr(PackageParser.Package pkg, PackageParser.Package changingLib, ArrayList usesLibraryInfos) { // If the package provides libraries, clear their old dependencies. // This method will set them up again. applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> { definingLibrary.clearDependencies(); }); if (usesLibraryInfos != null) { pkg.usesLibraryInfos = usesLibraryInfos; // Use LinkedHashSet to preserve the order of files added to // usesLibraryFiles while eliminating duplicates. Set usesLibraryFiles = new LinkedHashSet<>(); for (SharedLibraryInfo libInfo : usesLibraryInfos) { addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib); } pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]); } else { pkg.usesLibraryInfos = null; pkg.usesLibraryFiles = null; } } @GuardedBy("mPackages") private static ArrayList collectSharedLibraryInfos( @NonNull List requestedLibraries, @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, @NonNull String packageName, boolean required, int targetSdk, @Nullable ArrayList outUsedLibraries, @NonNull final Map
When resolving for an ephemeral app, only activities that 1) are defined in the * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned. * * @param resolveInfos The pre-filtered list of resolved activities * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering * is performed. * @param intent * @return A filtered list of resolved activities. */ private List applyPostResolutionFilter(List resolveInfos, String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, boolean resolveForStart, int userId, Intent intent) { final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId); for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); // remove locally resolved instant app web results when disabled if (info.isInstantAppAvailable && blockInstant) { resolveInfos.remove(i); continue; } // allow activities that are defined in the provided package if (allowDynamicSplits && info.activityInfo != null && info.activityInfo.splitName != null && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames, info.activityInfo.splitName)) { if (mInstantAppInstallerActivity == null) { if (DEBUG_INSTALL) { Slog.v(TAG, "No installer - not adding it to the ResolveInfo list"); } resolveInfos.remove(i); continue; } if (blockInstant && isInstantApp(info.activityInfo.packageName, userId)) { resolveInfos.remove(i); continue; } // requested activity is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTALL) { Slog.v(TAG, "Adding installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); final ComponentName installFailureActivity = findInstallFailureActivity( info.activityInfo.packageName, filterCallingUid, userId); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( installFailureActivity, info.activityInfo.packageName, info.activityInfo.applicationInfo.longVersionCode, info.activityInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // This resolve info may appear in the chooser UI, so let us make it // look as the one it replaces as far as the user is concerned which // requires loading the correct label and icon for the resolve info. installerInfo.resolvePackageName = info.getComponentInfo().packageName; installerInfo.labelRes = info.resolveLabelResId(); installerInfo.icon = info.resolveIconResId(); installerInfo.isInstantAppAvailable = true; resolveInfos.set(i, installerInfo); continue; } // caller is a full app, don't need to apply any other filtering if (ephemeralPkgName == null) { continue; } else if (ephemeralPkgName.equals(info.activityInfo.packageName)) { // caller is same app; don't need to apply any other filtering continue; } else if (resolveForStart && (intent.isWebIntent() || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) && intent.getPackage() == null && intent.getComponent() == null) { // ephemeral apps can launch other ephemeral apps indirectly continue; } // allow activities that have been explicitly exposed to ephemeral apps final boolean isEphemeralApp = info.activityInfo.applicationInfo.isInstantApp(); if (!isEphemeralApp && ((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } /** * Returns the activity component that can handle install failures. * By default, the instant application installer handles failures. However, an * application may want to handle failures on its own. Applications do this by * creating an activity with an intent filter that handles the action * {@link Intent#ACTION_INSTALL_FAILURE}. */ private @Nullable ComponentName findInstallFailureActivity( String packageName, int filterCallingUid, int userId) { final Intent failureActivityIntent = new Intent(Intent.ACTION_INSTALL_FAILURE); failureActivityIntent.setPackage(packageName); // IMPORTANT: disallow dynamic splits to avoid an infinite loop final List result = queryIntentActivitiesInternal( failureActivityIntent, null /*resolvedType*/, 0 /*flags*/, filterCallingUid, userId, false /*resolveForStart*/, false /*allowDynamicSplits*/); final int NR = result.size(); if (NR > 0) { for (int i = 0; i < NR; i++) { final ResolveInfo info = result.get(i); if (info.activityInfo.splitName != null) { continue; } return new ComponentName(packageName, info.activityInfo.name); } } return null; } /** * @param resolveInfos list of resolve infos in descending priority order * @return if the list contains a resolve info with non-negative priority */ private boolean hasNonNegativePriority(List resolveInfos) { return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0; } private List filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent, int matchFlags, List candidates, CrossProfileDomainInfo xpDomainInfo, int userId) { final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0; if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) { Slog.v(TAG, "Filtering results with preferred activities. Candidates count: " + candidates.size()); } final ArrayList result = new ArrayList<>(); final ArrayList alwaysList = new ArrayList<>(); final ArrayList undefinedList = new ArrayList<>(); final ArrayList alwaysAskList = new ArrayList<>(); final ArrayList neverList = new ArrayList<>(); final 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 = mComponentResolver.queryActivities(intent, resolvedType, flags, targetUserId); if (resultTargetUser != null && isUserEnabled(targetUserId)) { // If all the matches in the target profile are suspended, return null. for (int i = resultTargetUser.size() - 1; i >= 0; i--) { if ((resultTargetUser.get(i).activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SUSPENDED) == 0) { return createForwardingResolveInfoUnchecked(filter, sourceUserId, targetUserId); } } } return null; } private ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter, int sourceUserId, int targetUserId) { ResolveInfo forwardingResolveInfo = new ResolveInfo(); long ident = Binder.clearCallingIdentity(); boolean targetIsProfile; try { targetIsProfile = sUserManager.getUserInfo(targetUserId).isManagedProfile(); } finally { Binder.restoreCallingIdentity(ident); } String className; if (targetIsProfile) { className = FORWARD_INTENT_TO_MANAGED_PROFILE; } else { className = FORWARD_INTENT_TO_PARENT; } ComponentName forwardingActivityComponentName = new ComponentName( mAndroidApplication.packageName, className); ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0, sourceUserId); if (!targetIsProfile) { forwardingActivityInfo.showUserIcon = targetUserId; forwardingResolveInfo.noResourceId = true; } forwardingResolveInfo.activityInfo = forwardingActivityInfo; forwardingResolveInfo.priority = 0; forwardingResolveInfo.preferredOrder = 0; forwardingResolveInfo.match = 0; forwardingResolveInfo.isDefault = true; forwardingResolveInfo.filter = filter; forwardingResolveInfo.targetUserId = targetUserId; return forwardingResolveInfo; } @Override public @NonNull ParceledListSlice queryIntentActivityOptions(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>(queryIntentActivityOptionsInternal(caller, specifics, specificTypes, intent, resolvedType, flags, userId)); } private @NonNull List queryIntentActivityOptionsInternal(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent activity options"); final String resultsAction = intent.getAction(); final List results = queryIntentActivitiesInternal(intent, resolvedType, flags | PackageManager.GET_RESOLVED_FILTER, userId); if (DEBUG_INTENT_MATCHING) { Log.v(TAG, "Query " + intent + ": " + results); } int specificsPos = 0; int N; // todo: note that the algorithm used here is O(N^2). This // isn't a problem in our current environment, but if we start running // into situations where we have more than 5 or 10 matches then this // should probably be changed to something smarter... // First we go through and resolve each of the specific items // that were supplied, taking care of removing any corresponding // duplicate items in the generic resolve list. if (specifics != null) { for (int i=0; i it = rii.filter.actionsIterator(); if (it == null) { continue; } while (it.hasNext()) { final String action = it.next(); if (resultsAction != null && resultsAction.equals(action)) { // If this action was explicitly requested, then don't // remove things that have it. continue; } for (int j=i+1; j queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>( queryIntentReceiversInternal(intent, resolvedType, flags, userId, false /*allowDynamicSplits*/)); } private @NonNull List queryIntentReceiversInternal(Intent intent, String resolvedType, int flags, int userId, boolean allowDynamicSplits) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ActivityInfo ai = getReceiverInfo(comp, flags, userId); if (ai != null) { // When specifying an explicit component, we prevent the activity from being // used when either 1) the calling package is normal and the activity is within // an instant application or 2) the calling package is ephemeral and the // activity is not visible to instant applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean matchExplicitlyVisibleOnly = (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (ai.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetVisibleToInstantApp = (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean isTargetExplicitlyVisibleToInstantApp = isTargetVisibleToInstantApp && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; final boolean isTargetHiddenFromInstantApp = !isTargetVisibleToInstantApp || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp); final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } } return applyPostResolutionFilter( list, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { final List result = mComponentResolver.queryReceivers(intent, resolvedType, flags, userId); return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { final List result = mComponentResolver.queryReceivers( intent, resolvedType, flags, pkg.receivers, userId); return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } return Collections.emptyList(); } } @Override public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) { final int callingUid = Binder.getCallingUid(); return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid); } private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForResolve( flags, userId, intent, callingUid, false /*includeInstantApps*/); List query = queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/); if (query != null) { if (query.size() >= 1) { // If there is more than one service with the same priority, // just arbitrarily pick the first one. return query.get(0); } } return null; } @Override public @NonNull ParceledListSlice queryIntentServices(Intent intent, String resolvedType, int flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>(queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/)); } private @NonNull List queryIntentServicesInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps) { if (!sUserManager.exists(userId)) return Collections.emptyList(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ServiceInfo si = getServiceInfo(comp, flags, userId); if (si != null) { // When specifying an explicit component, we prevent the service from being // used when either 1) the service is in an instant application and the // caller is not the same instant application or 2) the calling package is // ephemeral and the activity is not visible to ephemeral applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (si.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetHiddenFromInstantApp = (si.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0; final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.serviceInfo = si; list.add(ri); } } return list; } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostServiceResolutionFilter( mComponentResolver.queryServices(intent, resolvedType, flags, userId), instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return applyPostServiceResolutionFilter( mComponentResolver.queryServices(intent, resolvedType, flags, pkg.services, userId), instantAppPkgName); } return Collections.emptyList(); } } private List applyPostServiceResolutionFilter(List resolveInfos, String instantAppPkgName) { if (instantAppPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp(); // allow services that are defined in the provided package if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) { if (info.serviceInfo.splitName != null && !ArrayUtils.contains(info.serviceInfo.applicationInfo.splitNames, info.serviceInfo.splitName)) { // requested service is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( null /* installFailureActivity */, info.serviceInfo.packageName, info.serviceInfo.applicationInfo.longVersionCode, info.serviceInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // load resources from the correct package installerInfo.resolvePackageName = info.getComponentInfo().packageName; resolveInfos.set(i, installerInfo); } continue; } // allow services that have been explicitly exposed to ephemeral apps if (!isEphemeralApp && ((info.serviceInfo.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } @Override public @NonNull ParceledListSlice queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>( queryIntentContentProvidersInternal(intent, resolvedType, flags, userId)); } private @NonNull List queryIntentContentProvidersInternal( Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ProviderInfo pi = getProviderInfo(comp, flags, userId); if (pi != null) { // When specifying an explicit component, we prevent the provider from being // used when either 1) the provider is in an instant application and the // caller is not the same instant application or 2) the calling package is an // instant application and the provider is not visible to instant applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (pi.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetHiddenFromInstantApp = (pi.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0; final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.providerInfo = pi; list.add(ri); } } return list; } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostContentProviderResolutionFilter( mComponentResolver.queryProviders(intent, resolvedType, flags, userId), instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return applyPostContentProviderResolutionFilter( mComponentResolver.queryProviders(intent, resolvedType, flags, pkg.providers, userId), instantAppPkgName); } return Collections.emptyList(); } } private List applyPostContentProviderResolutionFilter( List resolveInfos, String instantAppPkgName) { if (instantAppPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.providerInfo.applicationInfo.isInstantApp(); // allow providers that are defined in the provided package if (isEphemeralApp && instantAppPkgName.equals(info.providerInfo.packageName)) { if (info.providerInfo.splitName != null && !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames, info.providerInfo.splitName)) { // requested provider is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( null /*failureActivity*/, info.providerInfo.packageName, info.providerInfo.applicationInfo.longVersionCode, info.providerInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // load resources from the correct package installerInfo.resolvePackageName = info.getComponentInfo().packageName; resolveInfos.set(i, installerInfo); } continue; } // allow providers that have been explicitly exposed to instant applications if (!isEphemeralApp && ((info.providerInfo.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } @Override public ParceledListSlice getInstalledPackages(int flags, int userId) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return ParceledListSlice.emptyList(); } if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; final boolean listApex = (flags & MATCH_APEX) != 0; final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0; mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed packages"); // writer synchronized (mPackages) { ArrayList list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo(ps, flags, userId); if (pi != null) { list.add(pi); } } } else { list = new ArrayList<>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { final PackageSetting ps = (PackageSetting) p.mExtras; if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo((PackageSetting) p.mExtras, flags, userId); if (pi != null) { list.add(pi); } } } if (listApex) { if (listFactory) { list.addAll(mApexManager.getFactoryPackages()); } else { list.addAll(mApexManager.getActivePackages()); } if (listUninstalled) { list.addAll(mApexManager.getInactivePackages()); } } return new ParceledListSlice<>(list); } } private void addPackageHoldingPermissions(ArrayList list, PackageSetting ps, String[] permissions, boolean[] tmp, int flags, int userId) { int numMatch = 0; final PermissionsState permissionsState = ps.getPermissionsState(); for (int i=0; i getPackagesHoldingPermissions( String[] permissions, int flags, int userId) { if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, permissions); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "get packages holding permissions"); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; // writer synchronized (mPackages) { ArrayList list = new ArrayList<>(); boolean[] tmpBools = new boolean[permissions.length]; if (listUninstalled) { for (PackageSetting ps : mSettings.mPackages.values()) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } } else { for (PackageParser.Package pkg : mPackages.values()) { PackageSetting ps = (PackageSetting)pkg.mExtras; if (ps != null) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } } } return new ParceledListSlice<>(list); } } @Override public ParceledListSlice getInstalledApplications(int flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>( getInstalledApplicationsListInternal(flags, userId, callingUid)); } private List getInstalledApplicationsListInternal(int flags, int userId, int callingUid) { if (getInstantAppPackageName(callingUid) != null) { return Collections.emptyList(); } if (!sUserManager.exists(userId)) return Collections.emptyList(); flags = updateFlagsForApplication(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; mPermissionManager.enforceCrossUserPermission( callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed application info"); // writer synchronized (mPackages) { ArrayList list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { ApplicationInfo ai; int effectiveFlags = flags; if (ps.isSystem()) { effectiveFlags |= PackageManager.MATCH_ANY_USER; } if (ps.pkg != null) { if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(ps.pkg); } } else { // Shared lib filtering done in generateApplicationInfoFromSettingsLPw // and already converts to externally visible package name ai = generateApplicationInfoFromSettingsLPw(ps.name, callingUid, effectiveFlags, userId); } if (ai != null) { list.add(ai); } } } else { list = new ArrayList<>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { if (p.mExtras != null) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(p); list.add(ai); } } } } return list; } } @Override public ParceledListSlice getInstantApps(int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } if (!canViewInstantApps(Binder.getCallingUid(), userId)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getEphemeralApplications"); } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getEphemeralApplications"); synchronized (mPackages) { List instantApps = mInstantAppRegistry .getInstantAppsLPr(userId); if (instantApps != null) { return new ParceledListSlice<>(instantApps); } } return null; } @Override public boolean isInstantApp(String packageName, int userId) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "isInstantApp"); if (HIDE_EPHEMERAL_APIS) { return false; } synchronized (mPackages) { int callingUid = Binder.getCallingUid(); if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final PackageSetting ps = mSettings.mPackages.get(packageName); PackageParser.Package pkg = mPackages.get(packageName); final boolean returnAllowed = ps != null && (isCallerSameApp(packageName, callingUid) || canViewInstantApps(callingUid, userId) || mInstantAppRegistry.isInstantAccessGranted( userId, UserHandle.getAppId(callingUid), ps.appId)); if (returnAllowed) { return ps.getInstantApp(userId); } } return false; } @Override public byte[] getInstantAppCookie(String packageName, int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return null; } synchronized (mPackages) { return mInstantAppRegistry.getInstantAppCookieLPw( packageName, userId); } } @Override public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) { if (HIDE_EPHEMERAL_APIS) { return true; } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, true /* checkShell */, "setInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return false; } synchronized (mPackages) { return mInstantAppRegistry.setInstantAppCookieLPw( packageName, cookie, userId); } } @Override public Bitmap getInstantAppIcon(String packageName, int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } if (!canViewInstantApps(Binder.getCallingUid(), userId)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getInstantAppIcon"); } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppIcon"); synchronized (mPackages) { return mInstantAppRegistry.getInstantAppIconLPw( packageName, userId); } } private boolean isCallerSameApp(String packageName, int uid) { PackageParser.Package pkg = mPackages.get(packageName); return pkg != null && UserHandle.getAppId(uid) == pkg.applicationInfo.uid; } @Override public @NonNull ParceledListSlice getPersistentApplications(int flags) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(getPersistentApplicationsInternal(flags)); } private @NonNull List getPersistentApplicationsInternal(int flags) { final ArrayList finalList = new ArrayList<>(); // reader synchronized (mPackages) { final Iterator i = mPackages.values().iterator(); final int userId = UserHandle.getCallingUserId(); while (i.hasNext()) { final PackageParser.Package p = i.next(); if (p.applicationInfo == null) continue; final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0) && !p.applicationInfo.isDirectBootAware(); final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0) && p.applicationInfo.isDirectBootAware(); if ((p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0 && (!mSafeMode || isSystemApp(p)) && (matchesUnaware || matchesAware)) { PackageSetting ps = mSettings.mPackages.get(p.packageName); if (ps != null) { ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); if (ai != null) { finalList.add(ai); } } } } } return finalList; } @Override public ProviderInfo resolveContentProvider(String name, int flags, int userId) { return resolveContentProviderInternal(name, flags, userId); } private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId, name); final int callingUid = Binder.getCallingUid(); final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId); if (providerInfo == null) { return null; } if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { return null; } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { return null; } return providerInfo; } } /** * @deprecated */ @Deprecated public void querySyncProviders(List outNames, List outInfo) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; } mComponentResolver.querySyncProviders( outNames, outInfo, mSafeMode, UserHandle.getCallingUserId()); } @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; final List matchList = mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId); final int listSize = (matchList == null ? 0 : matchList.size()); synchronized (mPackages) { for (int i = 0; i < listSize; i++) { final ProviderInfo providerInfo = matchList.get(i); if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { continue; } final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { continue; } if (finalList == null) { finalList = new ArrayList<>(listSize - i); } finalList.add(providerInfo); } } if (finalList != null) { finalList.sort(sProviderInitOrderSorter); return new ParceledListSlice<>(finalList); } return ParceledListSlice.emptyList(); } @Override public InstrumentationInfo getInstrumentationInfo(ComponentName component, int flags) { // reader synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) { return null; } final PackageParser.Instrumentation i = mInstrumentation.get(component); return PackageParser.generateInstrumentationInfo(i, flags); } } @Override public @NonNull ParceledListSlice queryInstrumentation( String targetPackage, int flags) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(targetPackage); if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags)); } private @NonNull List queryInstrumentationInternal(String targetPackage, int flags) { ArrayList finalList = new ArrayList<>(); // reader synchronized (mPackages) { final Iterator i = mInstrumentation.values().iterator(); while (i.hasNext()) { final PackageParser.Instrumentation p = i.next(); if (targetPackage == null || targetPackage.equals(p.info.targetPackage)) { InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p, flags); if (ii != null) { finalList.add(ii); } } } } return finalList; } private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]"); try { scanDirLI(scanDir, parseFlags, scanFlags, currentTime); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) { final File[] files = scanDir.listFiles(); if (ArrayUtils.isEmpty(files)) { Log.d(TAG, "No files in app dir " + scanDir); return; } if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags + " flags=0x" + Integer.toHexString(parseFlags)); } try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser( mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mParallelPackageParserCallback)) { // Submit files for parsing in parallel int fileCount = 0; for (File file : files) { final boolean isPackage = (isApkFile(file) || file.isDirectory()) && !PackageInstallerService.isStageName(file.getName()); if (!isPackage) { // Ignore entries which are not packages continue; } parallelPackageParser.submit(file, parseFlags); fileCount++; } // Process results one by one for (; fileCount > 0; fileCount--) { ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take(); Throwable throwable = parseResult.throwable; int errorCode = PackageManager.INSTALL_SUCCEEDED; if (throwable == null) { // TODO(toddke): move lower in the scan chain // Static shared libraries have synthetic package names if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(parseResult.pkg); } try { scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags, currentTime, null); } catch (PackageManagerException e) { errorCode = e.error; Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage()); } } else if (throwable instanceof PackageParser.PackageParserException) { PackageParser.PackageParserException e = (PackageParser.PackageParserException) throwable; errorCode = e.error; Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage()); } else { throw new IllegalStateException("Unexpected exception occurred while parsing " + parseResult.scanFile, throwable); } // Delete invalid userdata apps if ((scanFlags & SCAN_AS_SYSTEM) == 0 && errorCode != PackageManager.INSTALL_SUCCEEDED) { logCriticalInfo(Log.WARN, "Deleting invalid package at " + parseResult.scanFile); removeCodePathLI(parseResult.scanFile); } } } } public static void reportSettingsProblem(int priority, String msg) { logCriticalInfo(priority, msg); } private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, boolean forceCollect, boolean skipVerify) throws PackageManagerException { // When upgrading from pre-N MR1, verify the package time stamp using the package // directory and not the APK file. final long lastModifiedTime = mIsPreNMR1Upgrade ? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg); final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(pkg); if (ps != null && !forceCollect && ps.codePathString.equals(pkg.codePath) && ps.timeStamp == lastModifiedTime && !isCompatSignatureUpdateNeeded(settingsVersionForPackage) && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) { if (ps.signatures.mSigningDetails.signatures != null && ps.signatures.mSigningDetails.signatures.length != 0 && ps.signatures.mSigningDetails.signatureSchemeVersion != SignatureSchemeVersion.UNKNOWN) { // Optimization: reuse the existing cached signing data // if the package appears to be unchanged. pkg.mSigningDetails = new PackageParser.SigningDetails(ps.signatures.mSigningDetails); return; } Slog.w(TAG, "PackageSetting for " + ps.name + " is missing signatures. Collecting certs again to recover them."); } else { Slog.i(TAG, pkg.codePath + " changed; collecting certs" + (forceCollect ? " (forced)" : "")); } try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); PackageParser.collectCertificates(pkg, skipVerify); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } /** * Clear the package profile if this was an upgrade and the package * version was updated. */ private void maybeClearProfilesForUpgradesLI( @Nullable PackageSetting originalPkgSetting, @NonNull PackageParser.Package currentPkg) { if (originalPkgSetting == null || !isDeviceUpgrading()) { return; } if (originalPkgSetting.versionCode == currentPkg.mVersionCode) { return; } clearAppProfilesLIF(currentPkg, UserHandle.USER_ALL); if (DEBUG_INSTALL) { Slog.d(TAG, originalPkgSetting.name + " clear profile due to version change " + originalPkgSetting.versionCode + " != " + currentPkg.mVersionCode); } } /** * Traces a package scan. * @see #scanPackageLI(File, int, int, long, UserHandle) */ @GuardedBy({"mInstallLock", "mPackages"}) 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 */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); pp.setDisplayMetrics(mMetrics); pp.setCallback(mPackageParserCallback); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); final PackageParser.Package pkg; try { pkg = pp.parsePackage(scanFile, parseFlags); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } // Static shared libraries have synthetic package names if (pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(pkg); } return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user); } /** * Scans a package and returns the newly parsed package. * @throws PackageManagerException on a parse error. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg, final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { // If the package has children and this is the first dive in the function // we scan the package with the SCAN_CHECK_ONLY flag set to see whether all // packages (parent and children) would be successfully scanned before the // actual scan since scanning mutates internal state and we want to atomically // install the package and its children. if ((scanFlags & SCAN_CHECK_ONLY) == 0) { if (pkg.childPackages != null && pkg.childPackages.size() > 0) { scanFlags |= SCAN_CHECK_ONLY; } } else { scanFlags &= ~SCAN_CHECK_ONLY; } // Scan the parent PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags, scanFlags, currentTime, user); // Scan the children final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPackage = pkg.childPackages.get(i); addForInitLI(childPackage, parseFlags, scanFlags, currentTime, user); } if ((scanFlags & SCAN_CHECK_ONLY) != 0) { return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user); } return scannedPkg; } /** * Returns if forced apk verification can be skipped for the whole package, including splits. */ private boolean canSkipForcedPackageVerification(PackageParser.Package pkg) { if (!canSkipForcedApkVerification(pkg.baseCodePath)) { return false; } // TODO: Allow base and splits to be verified individually. if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { for (int i = 0; i < pkg.splitCodePaths.length; i++) { if (!canSkipForcedApkVerification(pkg.splitCodePaths[i])) { return false; } } } return true; } /** * Returns if forced apk verification can be skipped, depending on current FSVerity setup and * whether the apk contains signed root hash. Note that the signer's certificate still needs to * match one in a trusted source, and should be done separately. */ private boolean canSkipForcedApkVerification(String apkPath) { if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) { return VerityUtils.hasFsverity(apkPath); } try { final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath); if (rootHashObserved == null) { return false; // APK does not contain Merkle tree root hash. } synchronized (mInstallLock) { // Returns whether the observed root hash matches what kernel has. mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved); return true; } } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) { Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e); } return false; } /** * Adds a new package to the internal data structures during platform initialization. * After adding, the package is known to the system and available for querying. * For packages located on the device ROM [eg. packages located in /system, /vendor, * etc...], additional checks are performed. Basic verification [such as ensuring * matching signatures, checking version codes, etc...] occurs if the package is * identical to a previously known package. If the package fails a signature check, * the version installed on /data will be removed. If the version of the new package * is less than or equal than the version on /data, it will be ignored. * Regardless of the package location, the results are applied to the internal * structures and the package is made available to the rest of the system. * NOTE: The return value should be removed. It's the passed in package object. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package addForInitLI(PackageParser.Package pkg, @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { final boolean scanSystemPartition = (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0; final String renamedPkgName; final PackageSetting disabledPkgSetting; final boolean isSystemPkgUpdated; final boolean pkgAlreadyExists; PackageSetting pkgSetting; // NOTE: installPackageLI() has the same code to setup the package's // application info. This probably should be done lower in the call // stack [such as scanPackageOnly()]. However, we verify the application // info prior to that [in scanPackageNew()] and thus have to setup // the application info early. pkg.setApplicationVolumeUuid(pkg.volumeUuid); pkg.setApplicationInfoCodePath(pkg.codePath); pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath); pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths); pkg.setApplicationInfoResourcePath(pkg.codePath); pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath); pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths); synchronized (mPackages) { renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage); final String realPkgName = getRealPackageName(pkg, renamedPkgName); if (realPkgName != null) { ensurePackageRenamed(pkg, renamedPkgName); } final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName); final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName); pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting; pkgAlreadyExists = pkgSetting != null; final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName; disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName); isSystemPkgUpdated = disabledPkgSetting != null; if (DEBUG_INSTALL && isSystemPkgUpdated) { Slog.d(TAG, "updatedPkg = " + disabledPkgSetting); } final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null) ? mSettings.getSharedUserLPw(pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true) : null; if (DEBUG_PACKAGE_SCANNING && (parseFlags & PackageParser.PARSE_CHATTY) != 0 && sharedUserSetting != null) { Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + sharedUserSetting.userId + "):" + " packages=" + sharedUserSetting.packages); } if (scanSystemPartition) { // Potentially prune child packages. If the application on the /system // partition has been updated via OTA, but, is still disabled by a // version on /data, cycle through all of its children packages and // remove children that are no longer defined. if (isSystemPkgUpdated) { final int scannedChildCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; final int disabledChildCount = disabledPkgSetting.childPackageNames != null ? disabledPkgSetting.childPackageNames.size() : 0; for (int i = 0; i < disabledChildCount; i++) { String disabledChildPackageName = disabledPkgSetting.childPackageNames.get(i); boolean disabledPackageAvailable = false; for (int j = 0; j < scannedChildCount; j++) { PackageParser.Package childPkg = pkg.childPackages.get(j); if (childPkg.packageName.equals(disabledChildPackageName)) { disabledPackageAvailable = true; break; } } if (!disabledPackageAvailable) { mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName); } } // we're updating the disabled package, so, scan it as the package setting final ScanRequest request = new ScanRequest(pkg, sharedUserSetting, null, disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */, null /* originalPkgSetting */, null, parseFlags, scanFlags, (pkg == mPlatformPackage), user); applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage); final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L); if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) { scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting); } } } } final boolean newPkgChangedPaths = pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath); final boolean newPkgVersionGreater = pkgAlreadyExists && pkg.getLongVersionCode() > pkgSetting.versionCode; final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated && newPkgChangedPaths && newPkgVersionGreater; if (isSystemPkgBetter) { // The version of the application on /system is greater than the version on // /data. Switch back to the application on /system. // It's safe to assume the application on /system will correctly scan. If not, // there won't be a working copy of the application. synchronized (mPackages) { // just remove the loaded entries from package lists mPackages.remove(pkgSetting.name); } logCriticalInfo(Log.WARN, "System package updated;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); final InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); args.cleanUpResourcesLI(); synchronized (mPackages) { mSettings.enableSystemPackageLPw(pkgSetting.name); } } if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) { // The version of the application on the /system partition is less than or // equal to the version on the /data partition. Throw an exception and use // the application already installed on the /data partition. throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at " + pkg.codePath + " ignored: updated version " + pkgSetting.versionCode + " better than this " + pkg.getLongVersionCode()); } // Verify certificates against what was last scanned. If there was an upgrade and this is an // app in a system partition, or if this is an updated priv app, we will force re-collecting // certificate. final boolean forceCollect = (mIsUpgrade && scanSystemPartition) || PackageManagerServiceUtils.isApkVerificationForced(disabledPkgSetting); // Full APK verification can be skipped during certificate collection, only if the file is // in verified partition, or can be verified on access (when apk verity is enabled). In both // cases, only data in Signing Block is verified instead of the whole file. final boolean skipVerify = scanSystemPartition || (forceCollect && canSkipForcedPackageVerification(pkg)); collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify); // Reset profile if the application version is changed maybeClearProfilesForUpgradesLI(pkgSetting, pkg); /* * A new system app appeared, but we already had a non-system one of the * same name installed earlier. */ boolean shouldHideSystemApp = false; // A new application appeared on /system, but, we already have a copy of // the application installed on /data. if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists && !pkgSetting.isSystem()) { if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA) && !pkgSetting.signatures.mSigningDetails.checkCapability( pkg.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) { logCriticalInfo(Log.WARN, "System package signature mismatch;" + " name: " + pkgSetting.name); try (PackageFreezer freezer = freezePackage(pkg.packageName, "scanPackageInternalLI")) { deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null); } pkgSetting = null; } else if (newPkgVersionGreater) { // The application on /system is newer than the application on /data. // Simply remove the application on /data [keeping application data] // and replace it with the version on /system. logCriticalInfo(Log.WARN, "System package enabled;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } } else { // The application on /system is older than the application on /data. Hide // the application on /system and the version on /data will be scanned later // and re-added like an update. shouldHideSystemApp = true; logCriticalInfo(Log.INFO, "System package disabled;" + " name: " + pkgSetting.name + "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode + "; new: " + pkg.codePath + " @ " + pkg.codePath); } } final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user); if (scanResult.success) { synchronized (mPackages) { boolean appIdCreated = false; try { final String pkgName = scanResult.pkgSetting.name; final Map reconcileResult = reconcilePackagesLocked( new ReconcileRequest( Collections.singletonMap(pkgName, scanResult), mSharedLibraries, mPackages, Collections.singletonMap( pkgName, getSettingsVersionForPackage(pkg)), Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))), mSettings.mKeySetManagerService); appIdCreated = optimisticallyRegisterAppId(scanResult); commitReconciledScanResultLocked(reconcileResult.get(pkgName)); } catch (PackageManagerException e) { if (appIdCreated) { cleanUpAppIdCreation(scanResult); } throw e; } } } if (shouldHideSystemApp) { synchronized (mPackages) { mSettings.disableSystemPackageLPw(pkg.packageName, true); } } return scanResult.pkgSetting.pkg; } private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) { // Derive the new package synthetic package name pkg.setPackageName(pkg.packageName + STATIC_SHARED_LIB_DELIMITER + pkg.staticSharedLibVersion); } static String fixProcessName(String defProcessName, String processName) { if (processName == null) { return defProcessName; } return processName; } /** * Enforces that only the system UID or root's UID can call a method exposed * via Binder. * * @param message used as message if SecurityException is thrown * @throws SecurityException if the caller is not system or root */ private static void enforceSystemOrRoot(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) { throw new SecurityException(message); } } /** * Enforces that only the system UID or root's UID or shell'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 shell */ private static void enforceSystemOrRootOrShell(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID && uid != Process.SHELL_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 = isDeviceUpgrading(); // First boot or factory reset. // Note: we also handle devices that are upgrading to N right now as if it is their // first boot, as they do not have profile data. boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade; // We need to re-extract after a pruned cache, as AoT-ed files will be out of date. boolean causePrunedCache = VMRuntime.didPruneDalvikCache(); if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) { return; } List pkgs; synchronized (mPackages) { pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this); } final long startTime = System.nanoTime(); final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */, causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT, false /* bootComplete */); final int elapsedTimeSeconds = (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime); MetricsLogger.histogram(mContext, "opt_dialog_num_dexopted", stats[0]); MetricsLogger.histogram(mContext, "opt_dialog_num_skipped", stats[1]); MetricsLogger.histogram(mContext, "opt_dialog_num_failed", stats[2]); MetricsLogger.histogram(mContext, "opt_dialog_num_total", getOptimizablePackages().size()); MetricsLogger.histogram(mContext, "opt_dialog_time_s", elapsedTimeSeconds); } /* * Return the prebuilt profile path given a package base code path. */ private static String getPrebuildProfilePath(PackageParser.Package pkg) { return pkg.baseCodePath + ".prof"; } /** * Performs dexopt on the set of packages in {@code packages} and returns an int array * containing statistics about the invocation. The array consists of three elements, * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped} * and {@code numberOfPackagesFailed}. */ private int[] performDexOptUpgrade(List pkgs, boolean showDialog, final int compilationReason, boolean bootComplete) { int numberOfPackagesVisited = 0; int numberOfPackagesOptimized = 0; int numberOfPackagesSkipped = 0; int numberOfPackagesFailed = 0; final int numberOfPackagesToDexopt = pkgs.size(); for (PackageParser.Package pkg : pkgs) { numberOfPackagesVisited++; boolean useProfileForDexopt = false; if ((isFirstBoot() || isDeviceUpgrading()) && isSystemApp(pkg)) { // Copy over initial preopt profiles since we won't get any JIT samples for methods // that are already compiled. File profileFile = new File(getPrebuildProfilePath(pkg)); // Copy profile if it exists. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The issue // is that we don't have a good way to say "do this only once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Installer failed to copy system profile!"); } else { // Disabled as this causes speed-profile compilation during first boot // even if things are already compiled. // useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } else { PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName); // Handle compressed APKs in this path. Only do this for stubs with profiles to // minimize the number off apps being speed-profile compiled during first boot. // The other paths will not change the filter. if (disabledPs != null && disabledPs.pkg.isStub) { // The package is the stub one, remove the stub suffix to get the normal // package and APK names. String systemProfilePath = getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, ""); profileFile = new File(systemProfilePath); // If we have a profile for a compressed APK, copy it to the reference // location. // Note that copying the profile here will cause it to override the // reference profile every OTA even though the existing reference profile // may have more data. We can't copy during decompression since the // directories are not set up at that point. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The // issue is that we don't have a good way to say "do this only // once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Failed to copy system profile for stub package!"); } else { useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } } } } if (!PackageDexOptimizer.canOptimizePackage(pkg)) { if (DEBUG_DEXOPT) { Log.i(TAG, "Skipping update of of non-optimizable app " + pkg.packageName); } numberOfPackagesSkipped++; continue; } if (DEBUG_DEXOPT) { Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " + numberOfPackagesToDexopt + ": " + pkg.packageName); } if (showDialog) { try { ActivityManager.getService().showBootMessage( mContext.getResources().getString(R.string.android_upgrading_apk, numberOfPackagesVisited, numberOfPackagesToDexopt), true); } catch (RemoteException e) { } synchronized (mPackages) { mDexOptDialogShown = true; } } int pkgCompilationReason = compilationReason; if (useProfileForDexopt) { // Use background dexopt mode to try and use the profile. Note that this does not // guarantee usage of the profile. pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT; } if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { mArtManagerService.compileLayouts(pkg); } // checkProfiles is false to avoid merging profiles during boot which // might interfere with background compilation (b/28612421). // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a // trade-off worth doing to save boot time work. int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0; if (compilationReason == REASON_FIRST_BOOT) { // TODO: This doesn't cover the upgrade case, we should check for this too. dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE; } int primaryDexOptStaus = performDexOptTraced(new DexoptOptions( pkg.packageName, pkgCompilationReason, dexoptFlags)); switch (primaryDexOptStaus) { case PackageDexOptimizer.DEX_OPT_PERFORMED: numberOfPackagesOptimized++; break; case PackageDexOptimizer.DEX_OPT_SKIPPED: numberOfPackagesSkipped++; break; case PackageDexOptimizer.DEX_OPT_FAILED: numberOfPackagesFailed++; break; default: Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus); break; } } return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped, numberOfPackagesFailed }; } @Override public void notifyPackageUse(String packageName, int reason) { synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); if (getInstantAppPackageName(callingUid) != null) { if (!isCallerSameApp(packageName, callingUid)) { return; } } else { if (isInstantApp(packageName, callingUserId)) { return; } } notifyPackageUseLocked(packageName, reason); } } @GuardedBy("mPackages") public CheckPermissionDelegate getCheckPermissionDelegateLocked() { return mCheckPermissionDelegate; } @GuardedBy("mPackages") public void setCheckPermissionDelegateLocked(CheckPermissionDelegate delegate) { mCheckPermissionDelegate = delegate; } @GuardedBy("mPackages") private void notifyPackageUseLocked(String packageName, int reason) { final PackageParser.Package p = mPackages.get(packageName); if (p == null) { return; } p.mLastPackageUsageTimeInMills[reason] = System.currentTimeMillis(); } @Override public void notifyDexLoad(String loadingPackageName, List classLoaderNames, List classPaths, String loaderIsa) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); if (ai == null) { Slog.w(TAG, "Loading a package that does not exist for the calling user. package=" + loadingPackageName + ", user=" + userId); return; } mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId); } @Override public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule, IDexModuleRegisterCallback callback) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(packageName, /*flags*/ 0, userId); DexManager.RegisterDexModuleResult result; if (ai == null) { Slog.w(TAG, "Registering a dex module for a package that does not exist for the" + " calling user. package=" + packageName + ", user=" + userId); result = new DexManager.RegisterDexModuleResult(false, "Package not installed"); } else { result = mDexManager.registerDexModule(ai, dexModulePath, isSharedModule, userId); } if (callback != null) { mHandler.post(() -> { try { callback.onDexModuleRegistered(dexModulePath, result.success, result.message); } catch (RemoteException e) { Slog.w(TAG, "Failed to callback after module registration " + dexModulePath, e); } }); } } /** * Ask the package manager to perform a dex-opt with the given compiler filter. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptMode(String packageName, boolean checkProfiles, String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) { int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) | (force ? DexoptOptions.DEXOPT_FORCE : 0) | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0); return performDexOpt(new DexoptOptions(packageName, REASON_UNKNOWN, targetCompilerFilter, splitName, flags)); } /** * Ask the package manager to perform a dex-opt with the given compiler filter on the * secondary dex files belonging to the given package. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptSecondary(String packageName, String compilerFilter, boolean force) { int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES | DexoptOptions.DEXOPT_BOOT_COMPLETE | (force ? DexoptOptions.DEXOPT_FORCE : 0); return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags)); } /** * Ask the package manager to compile layouts in the given package. */ @Override public boolean compileLayouts(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { return false; } } return mViewCompiler.compileLayouts(pkg); } /*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 = findSharedLibraries(p); final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo); if (!deps.isEmpty()) { DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(), options.getCompilationReason(), options.getCompilerFilter(), options.getSplitName(), options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY); for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = null; synchronized (mPackages) { depPackage = mPackages.get(info.getPackageName()); } if (depPackage != null) { // TODO: Analyze and investigate if we (should) profile libraries. pdo.performDexOpt(depPackage, instructionSets, getOrCreateCompilerPackageStats(depPackage), mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions); } else { // TODO(ngeoffray): Support dexopting system shared libraries. } } } return pdo.performDexOpt(p, instructionSets, getOrCreateCompilerPackageStats(p), mDexManager.getPackageUseInfoOrDefault(p.packageName), options); } /** * Reconcile the information we have about the secondary dex files belonging to * {@code packageName} 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(@Nullable List packageNames) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return false; } enforceSystemOrRootOrShell("runBackgroundDexoptJob"); final long identity = Binder.clearCallingIdentity(); try { return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames); } finally { Binder.restoreCallingIdentity(identity); } } private static List findSharedLibraries(PackageParser.Package p) { if (p.usesLibraryInfos != null) { ArrayList retValue = new ArrayList<>(); Set collectedNames = new HashSet<>(); for (SharedLibraryInfo info : p.usesLibraryInfos) { findSharedLibrariesRecursive(info, retValue, collectedNames); } return retValue; } else { return Collections.emptyList(); } } private static void findSharedLibrariesRecursive(SharedLibraryInfo info, ArrayList collected, Set collectedNames) { if (!collectedNames.contains(info.getName())) { collectedNames.add(info.getName()); collected.add(info); if (info.getDependencies() != null) { for (SharedLibraryInfo dep : info.getDependencies()) { findSharedLibrariesRecursive(dep, collected, collectedNames); } } } } List findSharedNonSystemLibraries(PackageParser.Package pkg) { List deps = findSharedLibraries(pkg); if (!deps.isEmpty()) { ArrayList retValue = new ArrayList<>(); synchronized (mPackages) { for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = mPackages.get(info.getPackageName()); if (depPackage != null) { retValue.add(depPackage); } } } return retValue; } else { return Collections.emptyList(); } } @Nullable private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) { return getSharedLibraryInfo(name, version, mSharedLibraries, null); } @Nullable private static SharedLibraryInfo getSharedLibraryInfo(String name, long version, Map> existingLibraries, @Nullable Map> newLibraries) { if (newLibraries != null) { final LongSparseArray versionedLib = newLibraries.get(name); SharedLibraryInfo info = null; if (versionedLib != null) { info = versionedLib.get(version); } if (info != null) { return info; } } final LongSparseArray versionedLib = existingLibraries.get(name); if (versionedLib == null) { return null; } return versionedLib.get(version); } private SharedLibraryInfo getLatestSharedLibraVersionLPr(PackageParser.Package pkg) { LongSparseArray versionedLib = mSharedLibraries.get( pkg.staticSharedLibName); if (versionedLib == null) { return null; } long previousLibVersion = -1; final int versionCount = versionedLib.size(); for (int i = 0; i < versionCount; i++) { final long libVersion = versionedLib.keyAt(i); if (libVersion < pkg.staticSharedLibVersion) { previousLibVersion = Math.max(previousLibVersion, libVersion); } } if (previousLibVersion >= 0) { return versionedLib.get(previousLibVersion); } return null; } @Nullable private PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) { PackageSetting sharedLibPackage = null; synchronized (mPackages) { final SharedLibraryInfo latestSharedLibraVersionLPr = getLatestSharedLibraVersionLPr(scanResult.pkgSetting.pkg); if (latestSharedLibraVersionLPr != null) { sharedLibPackage = mSettings.getPackageLPr( latestSharedLibraVersionLPr.getPackageName()); } } return sharedLibPackage; } public void shutdown() { mPackageUsage.writeNow(mPackages); mCompilerStats.writeNow(); mDexManager.writePackageDexUsageNow(); PackageWatchdog.getInstance(mContext).writeNow(); // This is the last chance to write out pending restriction settings synchronized (mPackages) { if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS); for (int userId : mDirtyUsers) { mSettings.writePackageRestrictionsLPr(userId); } mDirtyUsers.clear(); } } } @Override public void dumpProfiles(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } /* Only the shell, root, or the app user should be able to dump profiles. */ int callingUid = Binder.getCallingUid(); if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID && callingUid != pkg.applicationInfo.uid) { throw new SecurityException("dumpProfiles"); } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles"); mArtManagerService.dumpProfiles(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @Override public void forceDexOpt(String packageName) { enforceSystemOrRoot("forceDexOpt"); PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); // Whoever is calling forceDexOpt wants a compiled package. // Don't use profiles since that may cause compilation to be skipped. final int res = performDexOptInternalWithDependenciesLI( pkg, new DexoptOptions(packageName, getDefaultCompilerFilter(), DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE)); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) { throw new IllegalStateException("Failed to dexopt: " + res); } } } @GuardedBy("mPackages") private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) { if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package not in system partition"); return false; } else if (mPackages.get(oldPkg.name) != null) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package still exists"); return false; } return true; } @GuardedBy("mInstallLock") void removeCodePathLI(File codePath) { if (codePath.isDirectory()) { try { mInstaller.rmPackageDir(codePath.getAbsolutePath()); } catch (InstallerException e) { Slog.w(TAG, "Failed to remove code path", e); } } else { codePath.delete(); } } private int[] resolveUserIds(int userId) { return (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds() : new int[] { userId }; } private void clearAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } clearAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { clearAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } clearAppProfilesLIF(pkg, UserHandle.USER_ALL); } private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } } private void destroyAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } } private void destroyAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.destroyAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId); } } private void destroyAppProfilesLIF(PackageParser.Package pkg) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppProfilesLeafLIF(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppProfilesLeafLIF(pkg.childPackages.get(i)); } } private void destroyAppProfilesLeafLIF(PackageParser.Package pkg) { try { mInstaller.destroyAppProfiles(pkg.packageName); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } private void clearAppProfilesLIF(PackageParser.Package pkg, int userId) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } mArtManagerService.clearAppProfiles(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { mArtManagerService.clearAppProfiles(pkg.childPackages.get(i)); } } private void setInstallAndUpdateTime(PackageParser.Package pkg, long firstInstallTime, long lastUpdateTime) { // Set parent install/update time PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } // Set children install/update time final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPkg = pkg.childPackages.get(i); ps = (PackageSetting) childPkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } } } @GuardedBy("mPackages") private void applyDefiningSharedLibraryUpdateLocked( PackageParser.Package pkg, SharedLibraryInfo libInfo, BiConsumer action) { // Note that libraries defined by this package may be null if: // - Package manager was unable to create the shared library. The package still // gets installed, but the shared library does not get created. // Or: // - Package manager is in a state where package isn't scanned yet. This will // get called again after scanning to fix the dependencies. if (pkg.isLibrary()) { if (pkg.staticSharedLibName != null) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( pkg.staticSharedLibName, pkg.staticSharedLibVersion); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } else { for (String libraryName : pkg.libraryNames) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( libraryName, SharedLibraryInfo.VERSION_UNDEFINED); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } } } } @GuardedBy("mPackages") private void addSharedLibraryLPr(PackageParser.Package pkg, Set usesLibraryFiles, SharedLibraryInfo libInfo, PackageParser.Package changingLib) { if (libInfo.getPath() != null) { usesLibraryFiles.add(libInfo.getPath()); return; } PackageParser.Package p = mPackages.get(libInfo.getPackageName()); if (changingLib != null && changingLib.packageName.equals(libInfo.getPackageName())) { // If we are doing this while in the middle of updating a library apk, // then we need to make sure to use that new apk for determining the // dependencies here. (We haven't yet finished committing the new apk // to the package manager state.) if (p == null || p.packageName.equals(changingLib.packageName)) { p = changingLib; } } if (p != null) { usesLibraryFiles.addAll(p.getAllCodePaths()); // If the package provides libraries, add the dependency to them. applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, (definingLibrary, dependency) -> { definingLibrary.addDependency(dependency); }); if (p.usesLibraryFiles != null) { Collections.addAll(usesLibraryFiles, p.usesLibraryFiles); } } } @GuardedBy("mPackages") private void updateSharedLibrariesLocked(PackageParser.Package pkg, PackageParser.Package changingLib, Map availablePackages) throws PackageManagerException { final ArrayList sharedLibraryInfos = collectSharedLibraryInfos(pkg, availablePackages, mSharedLibraries, null); executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos); } private static ArrayList collectSharedLibraryInfos(PackageParser.Package pkg, Map availablePackages, @NonNull final Map> existingLibraries, @Nullable final Map> newLibraries) throws PackageManagerException { if (pkg == null) { return null; } // The collection used here must maintain the order of addition (so // that libraries are searched in the correct order) and must have no // duplicates. ArrayList usesLibraryInfos = null; if (pkg.usesLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesLibraries, null, null, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, null, availablePackages, existingLibraries, newLibraries); } if (pkg.usesStaticLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } if (pkg.usesOptionalLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesOptionalLibraries, null, null, pkg.packageName, false, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } return usesLibraryInfos; } private void executeSharedLibrariesUpdateLPr(PackageParser.Package pkg, PackageParser.Package changingLib, ArrayList usesLibraryInfos) { // If the package provides libraries, clear their old dependencies. // This method will set them up again. applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> { definingLibrary.clearDependencies(); }); if (usesLibraryInfos != null) { pkg.usesLibraryInfos = usesLibraryInfos; // Use LinkedHashSet to preserve the order of files added to // usesLibraryFiles while eliminating duplicates. Set usesLibraryFiles = new LinkedHashSet<>(); for (SharedLibraryInfo libInfo : usesLibraryInfos) { addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib); } pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]); } else { pkg.usesLibraryInfos = null; pkg.usesLibraryFiles = null; } } @GuardedBy("mPackages") private static ArrayList collectSharedLibraryInfos( @NonNull List requestedLibraries, @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, @NonNull String packageName, boolean required, int targetSdk, @Nullable ArrayList outUsedLibraries, @NonNull final Map
By default, the instant application installer handles failures. However, an * application may want to handle failures on its own. Applications do this by * creating an activity with an intent filter that handles the action * {@link Intent#ACTION_INSTALL_FAILURE}. */ private @Nullable ComponentName findInstallFailureActivity( String packageName, int filterCallingUid, int userId) { final Intent failureActivityIntent = new Intent(Intent.ACTION_INSTALL_FAILURE); failureActivityIntent.setPackage(packageName); // IMPORTANT: disallow dynamic splits to avoid an infinite loop final List result = queryIntentActivitiesInternal( failureActivityIntent, null /*resolvedType*/, 0 /*flags*/, filterCallingUid, userId, false /*resolveForStart*/, false /*allowDynamicSplits*/); final int NR = result.size(); if (NR > 0) { for (int i = 0; i < NR; i++) { final ResolveInfo info = result.get(i); if (info.activityInfo.splitName != null) { continue; } return new ComponentName(packageName, info.activityInfo.name); } } return null; } /** * @param resolveInfos list of resolve infos in descending priority order * @return if the list contains a resolve info with non-negative priority */ private boolean hasNonNegativePriority(List resolveInfos) { return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0; } private List filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent, int matchFlags, List candidates, CrossProfileDomainInfo xpDomainInfo, int userId) { final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0; if (DEBUG_PREFERRED || DEBUG_DOMAIN_VERIFICATION) { Slog.v(TAG, "Filtering results with preferred activities. Candidates count: " + candidates.size()); } final ArrayList result = new ArrayList<>(); final ArrayList alwaysList = new ArrayList<>(); final ArrayList undefinedList = new ArrayList<>(); final ArrayList alwaysAskList = new ArrayList<>(); final ArrayList neverList = new ArrayList<>(); final 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 = mComponentResolver.queryActivities(intent, resolvedType, flags, targetUserId); if (resultTargetUser != null && isUserEnabled(targetUserId)) { // If all the matches in the target profile are suspended, return null. for (int i = resultTargetUser.size() - 1; i >= 0; i--) { if ((resultTargetUser.get(i).activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SUSPENDED) == 0) { return createForwardingResolveInfoUnchecked(filter, sourceUserId, targetUserId); } } } return null; } private ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter, int sourceUserId, int targetUserId) { ResolveInfo forwardingResolveInfo = new ResolveInfo(); long ident = Binder.clearCallingIdentity(); boolean targetIsProfile; try { targetIsProfile = sUserManager.getUserInfo(targetUserId).isManagedProfile(); } finally { Binder.restoreCallingIdentity(ident); } String className; if (targetIsProfile) { className = FORWARD_INTENT_TO_MANAGED_PROFILE; } else { className = FORWARD_INTENT_TO_PARENT; } ComponentName forwardingActivityComponentName = new ComponentName( mAndroidApplication.packageName, className); ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0, sourceUserId); if (!targetIsProfile) { forwardingActivityInfo.showUserIcon = targetUserId; forwardingResolveInfo.noResourceId = true; } forwardingResolveInfo.activityInfo = forwardingActivityInfo; forwardingResolveInfo.priority = 0; forwardingResolveInfo.preferredOrder = 0; forwardingResolveInfo.match = 0; forwardingResolveInfo.isDefault = true; forwardingResolveInfo.filter = filter; forwardingResolveInfo.targetUserId = targetUserId; return forwardingResolveInfo; } @Override public @NonNull ParceledListSlice queryIntentActivityOptions(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>(queryIntentActivityOptionsInternal(caller, specifics, specificTypes, intent, resolvedType, flags, userId)); } private @NonNull List queryIntentActivityOptionsInternal(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent activity options"); final String resultsAction = intent.getAction(); final List results = queryIntentActivitiesInternal(intent, resolvedType, flags | PackageManager.GET_RESOLVED_FILTER, userId); if (DEBUG_INTENT_MATCHING) { Log.v(TAG, "Query " + intent + ": " + results); } int specificsPos = 0; int N; // todo: note that the algorithm used here is O(N^2). This // isn't a problem in our current environment, but if we start running // into situations where we have more than 5 or 10 matches then this // should probably be changed to something smarter... // First we go through and resolve each of the specific items // that were supplied, taking care of removing any corresponding // duplicate items in the generic resolve list. if (specifics != null) { for (int i=0; i it = rii.filter.actionsIterator(); if (it == null) { continue; } while (it.hasNext()) { final String action = it.next(); if (resultsAction != null && resultsAction.equals(action)) { // If this action was explicitly requested, then don't // remove things that have it. continue; } for (int j=i+1; j queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>( queryIntentReceiversInternal(intent, resolvedType, flags, userId, false /*allowDynamicSplits*/)); } private @NonNull List queryIntentReceiversInternal(Intent intent, String resolvedType, int flags, int userId, boolean allowDynamicSplits) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ActivityInfo ai = getReceiverInfo(comp, flags, userId); if (ai != null) { // When specifying an explicit component, we prevent the activity from being // used when either 1) the calling package is normal and the activity is within // an instant application or 2) the calling package is ephemeral and the // activity is not visible to instant applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean matchExplicitlyVisibleOnly = (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (ai.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetVisibleToInstantApp = (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0; final boolean isTargetExplicitlyVisibleToInstantApp = isTargetVisibleToInstantApp && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0; final boolean isTargetHiddenFromInstantApp = !isTargetVisibleToInstantApp || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp); final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } } return applyPostResolutionFilter( list, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { final List result = mComponentResolver.queryReceivers(intent, resolvedType, flags, userId); return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { final List result = mComponentResolver.queryReceivers( intent, resolvedType, flags, pkg.receivers, userId); return applyPostResolutionFilter( result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId, intent); } return Collections.emptyList(); } } @Override public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) { final int callingUid = Binder.getCallingUid(); return resolveServiceInternal(intent, resolvedType, flags, userId, callingUid); } private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForResolve( flags, userId, intent, callingUid, false /*includeInstantApps*/); List query = queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/); if (query != null) { if (query.size() >= 1) { // If there is more than one service with the same priority, // just arbitrarily pick the first one. return query.get(0); } } return null; } @Override public @NonNull ParceledListSlice queryIntentServices(Intent intent, String resolvedType, int flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>(queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/)); } private @NonNull List queryIntentServicesInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps) { if (!sUserManager.exists(userId)) return Collections.emptyList(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ServiceInfo si = getServiceInfo(comp, flags, userId); if (si != null) { // When specifying an explicit component, we prevent the service from being // used when either 1) the service is in an instant application and the // caller is not the same instant application or 2) the calling package is // ephemeral and the activity is not visible to ephemeral applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (si.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetHiddenFromInstantApp = (si.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0; final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.serviceInfo = si; list.add(ri); } } return list; } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostServiceResolutionFilter( mComponentResolver.queryServices(intent, resolvedType, flags, userId), instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return applyPostServiceResolutionFilter( mComponentResolver.queryServices(intent, resolvedType, flags, pkg.services, userId), instantAppPkgName); } return Collections.emptyList(); } } private List applyPostServiceResolutionFilter(List resolveInfos, String instantAppPkgName) { if (instantAppPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp(); // allow services that are defined in the provided package if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) { if (info.serviceInfo.splitName != null && !ArrayUtils.contains(info.serviceInfo.applicationInfo.splitNames, info.serviceInfo.splitName)) { // requested service is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( null /* installFailureActivity */, info.serviceInfo.packageName, info.serviceInfo.applicationInfo.longVersionCode, info.serviceInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // load resources from the correct package installerInfo.resolvePackageName = info.getComponentInfo().packageName; resolveInfos.set(i, installerInfo); } continue; } // allow services that have been explicitly exposed to ephemeral apps if (!isEphemeralApp && ((info.serviceInfo.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } @Override public @NonNull ParceledListSlice queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) { return new ParceledListSlice<>( queryIntentContentProvidersInternal(intent, resolvedType, flags, userId)); } private @NonNull List queryIntentContentProvidersInternal( Intent intent, String resolvedType, int flags, int userId) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { final List list = new ArrayList<>(1); final ProviderInfo pi = getProviderInfo(comp, flags, userId); if (pi != null) { // When specifying an explicit component, we prevent the provider from being // used when either 1) the provider is in an instant application and the // caller is not the same instant application or 2) the calling package is an // instant application and the provider is not visible to instant applications. final boolean matchInstantApp = (flags & PackageManager.MATCH_INSTANT) != 0; final boolean matchVisibleToInstantAppOnly = (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0; final boolean isCallerInstantApp = instantAppPkgName != null; final boolean isTargetSameInstantApp = comp.getPackageName().equals(instantAppPkgName); final boolean isTargetInstantApp = (pi.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0; final boolean isTargetHiddenFromInstantApp = (pi.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0; final boolean blockResolution = !isTargetSameInstantApp && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp) || (matchVisibleToInstantAppOnly && isCallerInstantApp && isTargetHiddenFromInstantApp)); if (!blockResolution) { final ResolveInfo ri = new ResolveInfo(); ri.providerInfo = pi; list.add(ri); } } return list; } // reader synchronized (mPackages) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostContentProviderResolutionFilter( mComponentResolver.queryProviders(intent, resolvedType, flags, userId), instantAppPkgName); } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return applyPostContentProviderResolutionFilter( mComponentResolver.queryProviders(intent, resolvedType, flags, pkg.providers, userId), instantAppPkgName); } return Collections.emptyList(); } } private List applyPostContentProviderResolutionFilter( List resolveInfos, String instantAppPkgName) { if (instantAppPkgName == null) { return resolveInfos; } for (int i = resolveInfos.size() - 1; i >= 0; i--) { final ResolveInfo info = resolveInfos.get(i); final boolean isEphemeralApp = info.providerInfo.applicationInfo.isInstantApp(); // allow providers that are defined in the provided package if (isEphemeralApp && instantAppPkgName.equals(info.providerInfo.packageName)) { if (info.providerInfo.splitName != null && !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames, info.providerInfo.splitName)) { // requested provider is defined in a split that hasn't been installed yet. // add the installer to the resolve list if (DEBUG_INSTANT) { Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list"); } final ResolveInfo installerInfo = new ResolveInfo( mInstantAppInstallerInfo); installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo( null /*failureActivity*/, info.providerInfo.packageName, info.providerInfo.applicationInfo.longVersionCode, info.providerInfo.splitName); // add a non-generic filter installerInfo.filter = new IntentFilter(); // load resources from the correct package installerInfo.resolvePackageName = info.getComponentInfo().packageName; resolveInfos.set(i, installerInfo); } continue; } // allow providers that have been explicitly exposed to instant applications if (!isEphemeralApp && ((info.providerInfo.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) { continue; } resolveInfos.remove(i); } return resolveInfos; } @Override public ParceledListSlice getInstalledPackages(int flags, int userId) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return ParceledListSlice.emptyList(); } if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; final boolean listApex = (flags & MATCH_APEX) != 0; final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0; mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed packages"); // writer synchronized (mPackages) { ArrayList list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo(ps, flags, userId); if (pi != null) { list.add(pi); } } } else { list = new ArrayList<>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { final PackageSetting ps = (PackageSetting) p.mExtras; if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo((PackageSetting) p.mExtras, flags, userId); if (pi != null) { list.add(pi); } } } if (listApex) { if (listFactory) { list.addAll(mApexManager.getFactoryPackages()); } else { list.addAll(mApexManager.getActivePackages()); } if (listUninstalled) { list.addAll(mApexManager.getInactivePackages()); } } return new ParceledListSlice<>(list); } } private void addPackageHoldingPermissions(ArrayList list, PackageSetting ps, String[] permissions, boolean[] tmp, int flags, int userId) { int numMatch = 0; final PermissionsState permissionsState = ps.getPermissionsState(); for (int i=0; i getPackagesHoldingPermissions( String[] permissions, int flags, int userId) { if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, permissions); mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "get packages holding permissions"); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; // writer synchronized (mPackages) { ArrayList list = new ArrayList<>(); boolean[] tmpBools = new boolean[permissions.length]; if (listUninstalled) { for (PackageSetting ps : mSettings.mPackages.values()) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } } else { for (PackageParser.Package pkg : mPackages.values()) { PackageSetting ps = (PackageSetting)pkg.mExtras; if (ps != null) { addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId); } } } return new ParceledListSlice<>(list); } } @Override public ParceledListSlice getInstalledApplications(int flags, int userId) { final int callingUid = Binder.getCallingUid(); return new ParceledListSlice<>( getInstalledApplicationsListInternal(flags, userId, callingUid)); } private List getInstalledApplicationsListInternal(int flags, int userId, int callingUid) { if (getInstantAppPackageName(callingUid) != null) { return Collections.emptyList(); } if (!sUserManager.exists(userId)) return Collections.emptyList(); flags = updateFlagsForApplication(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; mPermissionManager.enforceCrossUserPermission( callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed application info"); // writer synchronized (mPackages) { ArrayList list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { ApplicationInfo ai; int effectiveFlags = flags; if (ps.isSystem()) { effectiveFlags |= PackageManager.MATCH_ANY_USER; } if (ps.pkg != null) { if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(ps.pkg); } } else { // Shared lib filtering done in generateApplicationInfoFromSettingsLPw // and already converts to externally visible package name ai = generateApplicationInfoFromSettingsLPw(ps.name, callingUid, effectiveFlags, userId); } if (ai != null) { list.add(ai); } } } else { list = new ArrayList<>(mPackages.size()); for (PackageParser.Package p : mPackages.values()) { if (p.mExtras != null) { PackageSetting ps = (PackageSetting) p.mExtras; if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) { continue; } if (filterAppAccessLPr(ps, callingUid, userId)) { continue; } ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); if (ai != null) { ai.packageName = resolveExternalPackageNameLPr(p); list.add(ai); } } } } return list; } } @Override public ParceledListSlice getInstantApps(int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } if (!canViewInstantApps(Binder.getCallingUid(), userId)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getEphemeralApplications"); } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getEphemeralApplications"); synchronized (mPackages) { List instantApps = mInstantAppRegistry .getInstantAppsLPr(userId); if (instantApps != null) { return new ParceledListSlice<>(instantApps); } } return null; } @Override public boolean isInstantApp(String packageName, int userId) { mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "isInstantApp"); if (HIDE_EPHEMERAL_APIS) { return false; } synchronized (mPackages) { int callingUid = Binder.getCallingUid(); if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); } final PackageSetting ps = mSettings.mPackages.get(packageName); PackageParser.Package pkg = mPackages.get(packageName); final boolean returnAllowed = ps != null && (isCallerSameApp(packageName, callingUid) || canViewInstantApps(callingUid, userId) || mInstantAppRegistry.isInstantAccessGranted( userId, UserHandle.getAppId(callingUid), ps.appId)); if (returnAllowed) { return ps.getInstantApp(userId); } } return false; } @Override public byte[] getInstantAppCookie(String packageName, int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return null; } synchronized (mPackages) { return mInstantAppRegistry.getInstantAppCookieLPw( packageName, userId); } } @Override public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) { if (HIDE_EPHEMERAL_APIS) { return true; } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, true /* checkShell */, "setInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return false; } synchronized (mPackages) { return mInstantAppRegistry.setInstantAppCookieLPw( packageName, cookie, userId); } } @Override public Bitmap getInstantAppIcon(String packageName, int userId) { if (HIDE_EPHEMERAL_APIS) { return null; } if (!canViewInstantApps(Binder.getCallingUid(), userId)) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getInstantAppIcon"); } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppIcon"); synchronized (mPackages) { return mInstantAppRegistry.getInstantAppIconLPw( packageName, userId); } } private boolean isCallerSameApp(String packageName, int uid) { PackageParser.Package pkg = mPackages.get(packageName); return pkg != null && UserHandle.getAppId(uid) == pkg.applicationInfo.uid; } @Override public @NonNull ParceledListSlice getPersistentApplications(int flags) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(getPersistentApplicationsInternal(flags)); } private @NonNull List getPersistentApplicationsInternal(int flags) { final ArrayList finalList = new ArrayList<>(); // reader synchronized (mPackages) { final Iterator i = mPackages.values().iterator(); final int userId = UserHandle.getCallingUserId(); while (i.hasNext()) { final PackageParser.Package p = i.next(); if (p.applicationInfo == null) continue; final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0) && !p.applicationInfo.isDirectBootAware(); final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0) && p.applicationInfo.isDirectBootAware(); if ((p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0 && (!mSafeMode || isSystemApp(p)) && (matchesUnaware || matchesAware)) { PackageSetting ps = mSettings.mPackages.get(p.packageName); if (ps != null) { ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId), userId); if (ai != null) { finalList.add(ai); } } } } } return finalList; } @Override public ProviderInfo resolveContentProvider(String name, int flags, int userId) { return resolveContentProviderInternal(name, flags, userId); } private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId, name); final int callingUid = Binder.getCallingUid(); final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId); if (providerInfo == null) { return null; } if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { return null; } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { return null; } return providerInfo; } } /** * @deprecated */ @Deprecated public void querySyncProviders(List outNames, List outInfo) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; } mComponentResolver.querySyncProviders( outNames, outInfo, mSafeMode, UserHandle.getCallingUserId()); } @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; final List matchList = mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId); final int listSize = (matchList == null ? 0 : matchList.size()); synchronized (mPackages) { for (int i = 0; i < listSize; i++) { final ProviderInfo providerInfo = matchList.get(i); if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { continue; } final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { continue; } if (finalList == null) { finalList = new ArrayList<>(listSize - i); } finalList.add(providerInfo); } } if (finalList != null) { finalList.sort(sProviderInitOrderSorter); return new ParceledListSlice<>(finalList); } return ParceledListSlice.emptyList(); } @Override public InstrumentationInfo getInstrumentationInfo(ComponentName component, int flags) { // reader synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; if (filterAppAccessLPr(ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) { return null; } final PackageParser.Instrumentation i = mInstrumentation.get(component); return PackageParser.generateInstrumentationInfo(i, flags); } } @Override public @NonNull ParceledListSlice queryInstrumentation( String targetPackage, int flags) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(targetPackage); if (filterAppAccessLPr(ps, callingUid, callingUserId)) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags)); } private @NonNull List queryInstrumentationInternal(String targetPackage, int flags) { ArrayList finalList = new ArrayList<>(); // reader synchronized (mPackages) { final Iterator i = mInstrumentation.values().iterator(); while (i.hasNext()) { final PackageParser.Instrumentation p = i.next(); if (targetPackage == null || targetPackage.equals(p.info.targetPackage)) { InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p, flags); if (ii != null) { finalList.add(ii); } } } } return finalList; } private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]"); try { scanDirLI(scanDir, parseFlags, scanFlags, currentTime); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) { final File[] files = scanDir.listFiles(); if (ArrayUtils.isEmpty(files)) { Log.d(TAG, "No files in app dir " + scanDir); return; } if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags + " flags=0x" + Integer.toHexString(parseFlags)); } try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser( mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mParallelPackageParserCallback)) { // Submit files for parsing in parallel int fileCount = 0; for (File file : files) { final boolean isPackage = (isApkFile(file) || file.isDirectory()) && !PackageInstallerService.isStageName(file.getName()); if (!isPackage) { // Ignore entries which are not packages continue; } parallelPackageParser.submit(file, parseFlags); fileCount++; } // Process results one by one for (; fileCount > 0; fileCount--) { ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take(); Throwable throwable = parseResult.throwable; int errorCode = PackageManager.INSTALL_SUCCEEDED; if (throwable == null) { // TODO(toddke): move lower in the scan chain // Static shared libraries have synthetic package names if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(parseResult.pkg); } try { scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags, currentTime, null); } catch (PackageManagerException e) { errorCode = e.error; Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage()); } } else if (throwable instanceof PackageParser.PackageParserException) { PackageParser.PackageParserException e = (PackageParser.PackageParserException) throwable; errorCode = e.error; Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage()); } else { throw new IllegalStateException("Unexpected exception occurred while parsing " + parseResult.scanFile, throwable); } // Delete invalid userdata apps if ((scanFlags & SCAN_AS_SYSTEM) == 0 && errorCode != PackageManager.INSTALL_SUCCEEDED) { logCriticalInfo(Log.WARN, "Deleting invalid package at " + parseResult.scanFile); removeCodePathLI(parseResult.scanFile); } } } } public static void reportSettingsProblem(int priority, String msg) { logCriticalInfo(priority, msg); } private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, boolean forceCollect, boolean skipVerify) throws PackageManagerException { // When upgrading from pre-N MR1, verify the package time stamp using the package // directory and not the APK file. final long lastModifiedTime = mIsPreNMR1Upgrade ? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg); final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(pkg); if (ps != null && !forceCollect && ps.codePathString.equals(pkg.codePath) && ps.timeStamp == lastModifiedTime && !isCompatSignatureUpdateNeeded(settingsVersionForPackage) && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) { if (ps.signatures.mSigningDetails.signatures != null && ps.signatures.mSigningDetails.signatures.length != 0 && ps.signatures.mSigningDetails.signatureSchemeVersion != SignatureSchemeVersion.UNKNOWN) { // Optimization: reuse the existing cached signing data // if the package appears to be unchanged. pkg.mSigningDetails = new PackageParser.SigningDetails(ps.signatures.mSigningDetails); return; } Slog.w(TAG, "PackageSetting for " + ps.name + " is missing signatures. Collecting certs again to recover them."); } else { Slog.i(TAG, pkg.codePath + " changed; collecting certs" + (forceCollect ? " (forced)" : "")); } try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); PackageParser.collectCertificates(pkg, skipVerify); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } /** * Clear the package profile if this was an upgrade and the package * version was updated. */ private void maybeClearProfilesForUpgradesLI( @Nullable PackageSetting originalPkgSetting, @NonNull PackageParser.Package currentPkg) { if (originalPkgSetting == null || !isDeviceUpgrading()) { return; } if (originalPkgSetting.versionCode == currentPkg.mVersionCode) { return; } clearAppProfilesLIF(currentPkg, UserHandle.USER_ALL); if (DEBUG_INSTALL) { Slog.d(TAG, originalPkgSetting.name + " clear profile due to version change " + originalPkgSetting.versionCode + " != " + currentPkg.mVersionCode); } } /** * Traces a package scan. * @see #scanPackageLI(File, int, int, long, UserHandle) */ @GuardedBy({"mInstallLock", "mPackages"}) 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 */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); pp.setDisplayMetrics(mMetrics); pp.setCallback(mPackageParserCallback); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); final PackageParser.Package pkg; try { pkg = pp.parsePackage(scanFile, parseFlags); } catch (PackageParserException e) { throw PackageManagerException.from(e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } // Static shared libraries have synthetic package names if (pkg.applicationInfo.isStaticSharedLibrary()) { renameStaticSharedLibraryPackage(pkg); } return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user); } /** * Scans a package and returns the newly parsed package. * @throws PackageManagerException on a parse error. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg, final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { // If the package has children and this is the first dive in the function // we scan the package with the SCAN_CHECK_ONLY flag set to see whether all // packages (parent and children) would be successfully scanned before the // actual scan since scanning mutates internal state and we want to atomically // install the package and its children. if ((scanFlags & SCAN_CHECK_ONLY) == 0) { if (pkg.childPackages != null && pkg.childPackages.size() > 0) { scanFlags |= SCAN_CHECK_ONLY; } } else { scanFlags &= ~SCAN_CHECK_ONLY; } // Scan the parent PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags, scanFlags, currentTime, user); // Scan the children final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPackage = pkg.childPackages.get(i); addForInitLI(childPackage, parseFlags, scanFlags, currentTime, user); } if ((scanFlags & SCAN_CHECK_ONLY) != 0) { return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user); } return scannedPkg; } /** * Returns if forced apk verification can be skipped for the whole package, including splits. */ private boolean canSkipForcedPackageVerification(PackageParser.Package pkg) { if (!canSkipForcedApkVerification(pkg.baseCodePath)) { return false; } // TODO: Allow base and splits to be verified individually. if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { for (int i = 0; i < pkg.splitCodePaths.length; i++) { if (!canSkipForcedApkVerification(pkg.splitCodePaths[i])) { return false; } } } return true; } /** * Returns if forced apk verification can be skipped, depending on current FSVerity setup and * whether the apk contains signed root hash. Note that the signer's certificate still needs to * match one in a trusted source, and should be done separately. */ private boolean canSkipForcedApkVerification(String apkPath) { if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) { return VerityUtils.hasFsverity(apkPath); } try { final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath); if (rootHashObserved == null) { return false; // APK does not contain Merkle tree root hash. } synchronized (mInstallLock) { // Returns whether the observed root hash matches what kernel has. mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved); return true; } } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) { Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e); } return false; } /** * Adds a new package to the internal data structures during platform initialization. * After adding, the package is known to the system and available for querying. * For packages located on the device ROM [eg. packages located in /system, /vendor, * etc...], additional checks are performed. Basic verification [such as ensuring * matching signatures, checking version codes, etc...] occurs if the package is * identical to a previously known package. If the package fails a signature check, * the version installed on /data will be removed. If the version of the new package * is less than or equal than the version on /data, it will be ignored. * Regardless of the package location, the results are applied to the internal * structures and the package is made available to the rest of the system. * NOTE: The return value should be removed. It's the passed in package object. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package addForInitLI(PackageParser.Package pkg, @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { final boolean scanSystemPartition = (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0; final String renamedPkgName; final PackageSetting disabledPkgSetting; final boolean isSystemPkgUpdated; final boolean pkgAlreadyExists; PackageSetting pkgSetting; // NOTE: installPackageLI() has the same code to setup the package's // application info. This probably should be done lower in the call // stack [such as scanPackageOnly()]. However, we verify the application // info prior to that [in scanPackageNew()] and thus have to setup // the application info early. pkg.setApplicationVolumeUuid(pkg.volumeUuid); pkg.setApplicationInfoCodePath(pkg.codePath); pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath); pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths); pkg.setApplicationInfoResourcePath(pkg.codePath); pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath); pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths); synchronized (mPackages) { renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage); final String realPkgName = getRealPackageName(pkg, renamedPkgName); if (realPkgName != null) { ensurePackageRenamed(pkg, renamedPkgName); } final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName); final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName); pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting; pkgAlreadyExists = pkgSetting != null; final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName; disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName); isSystemPkgUpdated = disabledPkgSetting != null; if (DEBUG_INSTALL && isSystemPkgUpdated) { Slog.d(TAG, "updatedPkg = " + disabledPkgSetting); } final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null) ? mSettings.getSharedUserLPw(pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true) : null; if (DEBUG_PACKAGE_SCANNING && (parseFlags & PackageParser.PARSE_CHATTY) != 0 && sharedUserSetting != null) { Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + sharedUserSetting.userId + "):" + " packages=" + sharedUserSetting.packages); } if (scanSystemPartition) { // Potentially prune child packages. If the application on the /system // partition has been updated via OTA, but, is still disabled by a // version on /data, cycle through all of its children packages and // remove children that are no longer defined. if (isSystemPkgUpdated) { final int scannedChildCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; final int disabledChildCount = disabledPkgSetting.childPackageNames != null ? disabledPkgSetting.childPackageNames.size() : 0; for (int i = 0; i < disabledChildCount; i++) { String disabledChildPackageName = disabledPkgSetting.childPackageNames.get(i); boolean disabledPackageAvailable = false; for (int j = 0; j < scannedChildCount; j++) { PackageParser.Package childPkg = pkg.childPackages.get(j); if (childPkg.packageName.equals(disabledChildPackageName)) { disabledPackageAvailable = true; break; } } if (!disabledPackageAvailable) { mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName); } } // we're updating the disabled package, so, scan it as the package setting final ScanRequest request = new ScanRequest(pkg, sharedUserSetting, null, disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */, null /* originalPkgSetting */, null, parseFlags, scanFlags, (pkg == mPlatformPackage), user); applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage); final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L); if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) { scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting); } } } } final boolean newPkgChangedPaths = pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath); final boolean newPkgVersionGreater = pkgAlreadyExists && pkg.getLongVersionCode() > pkgSetting.versionCode; final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated && newPkgChangedPaths && newPkgVersionGreater; if (isSystemPkgBetter) { // The version of the application on /system is greater than the version on // /data. Switch back to the application on /system. // It's safe to assume the application on /system will correctly scan. If not, // there won't be a working copy of the application. synchronized (mPackages) { // just remove the loaded entries from package lists mPackages.remove(pkgSetting.name); } logCriticalInfo(Log.WARN, "System package updated;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); final InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); args.cleanUpResourcesLI(); synchronized (mPackages) { mSettings.enableSystemPackageLPw(pkgSetting.name); } } if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) { // The version of the application on the /system partition is less than or // equal to the version on the /data partition. Throw an exception and use // the application already installed on the /data partition. throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at " + pkg.codePath + " ignored: updated version " + pkgSetting.versionCode + " better than this " + pkg.getLongVersionCode()); } // Verify certificates against what was last scanned. If there was an upgrade and this is an // app in a system partition, or if this is an updated priv app, we will force re-collecting // certificate. final boolean forceCollect = (mIsUpgrade && scanSystemPartition) || PackageManagerServiceUtils.isApkVerificationForced(disabledPkgSetting); // Full APK verification can be skipped during certificate collection, only if the file is // in verified partition, or can be verified on access (when apk verity is enabled). In both // cases, only data in Signing Block is verified instead of the whole file. final boolean skipVerify = scanSystemPartition || (forceCollect && canSkipForcedPackageVerification(pkg)); collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify); // Reset profile if the application version is changed maybeClearProfilesForUpgradesLI(pkgSetting, pkg); /* * A new system app appeared, but we already had a non-system one of the * same name installed earlier. */ boolean shouldHideSystemApp = false; // A new application appeared on /system, but, we already have a copy of // the application installed on /data. if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists && !pkgSetting.isSystem()) { if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA) && !pkgSetting.signatures.mSigningDetails.checkCapability( pkg.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) { logCriticalInfo(Log.WARN, "System package signature mismatch;" + " name: " + pkgSetting.name); try (PackageFreezer freezer = freezePackage(pkg.packageName, "scanPackageInternalLI")) { deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null); } pkgSetting = null; } else if (newPkgVersionGreater) { // The application on /system is newer than the application on /data. // Simply remove the application on /data [keeping application data] // and replace it with the version on /system. logCriticalInfo(Log.WARN, "System package enabled;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } } else { // The application on /system is older than the application on /data. Hide // the application on /system and the version on /data will be scanned later // and re-added like an update. shouldHideSystemApp = true; logCriticalInfo(Log.INFO, "System package disabled;" + " name: " + pkgSetting.name + "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode + "; new: " + pkg.codePath + " @ " + pkg.codePath); } } final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user); if (scanResult.success) { synchronized (mPackages) { boolean appIdCreated = false; try { final String pkgName = scanResult.pkgSetting.name; final Map reconcileResult = reconcilePackagesLocked( new ReconcileRequest( Collections.singletonMap(pkgName, scanResult), mSharedLibraries, mPackages, Collections.singletonMap( pkgName, getSettingsVersionForPackage(pkg)), Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))), mSettings.mKeySetManagerService); appIdCreated = optimisticallyRegisterAppId(scanResult); commitReconciledScanResultLocked(reconcileResult.get(pkgName)); } catch (PackageManagerException e) { if (appIdCreated) { cleanUpAppIdCreation(scanResult); } throw e; } } } if (shouldHideSystemApp) { synchronized (mPackages) { mSettings.disableSystemPackageLPw(pkg.packageName, true); } } return scanResult.pkgSetting.pkg; } private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) { // Derive the new package synthetic package name pkg.setPackageName(pkg.packageName + STATIC_SHARED_LIB_DELIMITER + pkg.staticSharedLibVersion); } static String fixProcessName(String defProcessName, String processName) { if (processName == null) { return defProcessName; } return processName; } /** * Enforces that only the system UID or root's UID can call a method exposed * via Binder. * * @param message used as message if SecurityException is thrown * @throws SecurityException if the caller is not system or root */ private static void enforceSystemOrRoot(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) { throw new SecurityException(message); } } /** * Enforces that only the system UID or root's UID or shell'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 shell */ private static void enforceSystemOrRootOrShell(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID && uid != Process.SHELL_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 = isDeviceUpgrading(); // First boot or factory reset. // Note: we also handle devices that are upgrading to N right now as if it is their // first boot, as they do not have profile data. boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade; // We need to re-extract after a pruned cache, as AoT-ed files will be out of date. boolean causePrunedCache = VMRuntime.didPruneDalvikCache(); if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) { return; } List pkgs; synchronized (mPackages) { pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this); } final long startTime = System.nanoTime(); final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */, causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT, false /* bootComplete */); final int elapsedTimeSeconds = (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime); MetricsLogger.histogram(mContext, "opt_dialog_num_dexopted", stats[0]); MetricsLogger.histogram(mContext, "opt_dialog_num_skipped", stats[1]); MetricsLogger.histogram(mContext, "opt_dialog_num_failed", stats[2]); MetricsLogger.histogram(mContext, "opt_dialog_num_total", getOptimizablePackages().size()); MetricsLogger.histogram(mContext, "opt_dialog_time_s", elapsedTimeSeconds); } /* * Return the prebuilt profile path given a package base code path. */ private static String getPrebuildProfilePath(PackageParser.Package pkg) { return pkg.baseCodePath + ".prof"; } /** * Performs dexopt on the set of packages in {@code packages} and returns an int array * containing statistics about the invocation. The array consists of three elements, * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped} * and {@code numberOfPackagesFailed}. */ private int[] performDexOptUpgrade(List pkgs, boolean showDialog, final int compilationReason, boolean bootComplete) { int numberOfPackagesVisited = 0; int numberOfPackagesOptimized = 0; int numberOfPackagesSkipped = 0; int numberOfPackagesFailed = 0; final int numberOfPackagesToDexopt = pkgs.size(); for (PackageParser.Package pkg : pkgs) { numberOfPackagesVisited++; boolean useProfileForDexopt = false; if ((isFirstBoot() || isDeviceUpgrading()) && isSystemApp(pkg)) { // Copy over initial preopt profiles since we won't get any JIT samples for methods // that are already compiled. File profileFile = new File(getPrebuildProfilePath(pkg)); // Copy profile if it exists. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The issue // is that we don't have a good way to say "do this only once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Installer failed to copy system profile!"); } else { // Disabled as this causes speed-profile compilation during first boot // even if things are already compiled. // useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } else { PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName); // Handle compressed APKs in this path. Only do this for stubs with profiles to // minimize the number off apps being speed-profile compiled during first boot. // The other paths will not change the filter. if (disabledPs != null && disabledPs.pkg.isStub) { // The package is the stub one, remove the stub suffix to get the normal // package and APK names. String systemProfilePath = getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, ""); profileFile = new File(systemProfilePath); // If we have a profile for a compressed APK, copy it to the reference // location. // Note that copying the profile here will cause it to override the // reference profile every OTA even though the existing reference profile // may have more data. We can't copy during decompression since the // directories are not set up at that point. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The // issue is that we don't have a good way to say "do this only // once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Failed to copy system profile for stub package!"); } else { useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } } } } if (!PackageDexOptimizer.canOptimizePackage(pkg)) { if (DEBUG_DEXOPT) { Log.i(TAG, "Skipping update of of non-optimizable app " + pkg.packageName); } numberOfPackagesSkipped++; continue; } if (DEBUG_DEXOPT) { Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " + numberOfPackagesToDexopt + ": " + pkg.packageName); } if (showDialog) { try { ActivityManager.getService().showBootMessage( mContext.getResources().getString(R.string.android_upgrading_apk, numberOfPackagesVisited, numberOfPackagesToDexopt), true); } catch (RemoteException e) { } synchronized (mPackages) { mDexOptDialogShown = true; } } int pkgCompilationReason = compilationReason; if (useProfileForDexopt) { // Use background dexopt mode to try and use the profile. Note that this does not // guarantee usage of the profile. pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT; } if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { mArtManagerService.compileLayouts(pkg); } // checkProfiles is false to avoid merging profiles during boot which // might interfere with background compilation (b/28612421). // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a // trade-off worth doing to save boot time work. int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0; if (compilationReason == REASON_FIRST_BOOT) { // TODO: This doesn't cover the upgrade case, we should check for this too. dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE; } int primaryDexOptStaus = performDexOptTraced(new DexoptOptions( pkg.packageName, pkgCompilationReason, dexoptFlags)); switch (primaryDexOptStaus) { case PackageDexOptimizer.DEX_OPT_PERFORMED: numberOfPackagesOptimized++; break; case PackageDexOptimizer.DEX_OPT_SKIPPED: numberOfPackagesSkipped++; break; case PackageDexOptimizer.DEX_OPT_FAILED: numberOfPackagesFailed++; break; default: Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus); break; } } return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped, numberOfPackagesFailed }; } @Override public void notifyPackageUse(String packageName, int reason) { synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); if (getInstantAppPackageName(callingUid) != null) { if (!isCallerSameApp(packageName, callingUid)) { return; } } else { if (isInstantApp(packageName, callingUserId)) { return; } } notifyPackageUseLocked(packageName, reason); } } @GuardedBy("mPackages") public CheckPermissionDelegate getCheckPermissionDelegateLocked() { return mCheckPermissionDelegate; } @GuardedBy("mPackages") public void setCheckPermissionDelegateLocked(CheckPermissionDelegate delegate) { mCheckPermissionDelegate = delegate; } @GuardedBy("mPackages") private void notifyPackageUseLocked(String packageName, int reason) { final PackageParser.Package p = mPackages.get(packageName); if (p == null) { return; } p.mLastPackageUsageTimeInMills[reason] = System.currentTimeMillis(); } @Override public void notifyDexLoad(String loadingPackageName, List classLoaderNames, List classPaths, String loaderIsa) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); if (ai == null) { Slog.w(TAG, "Loading a package that does not exist for the calling user. package=" + loadingPackageName + ", user=" + userId); return; } mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId); } @Override public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule, IDexModuleRegisterCallback callback) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(packageName, /*flags*/ 0, userId); DexManager.RegisterDexModuleResult result; if (ai == null) { Slog.w(TAG, "Registering a dex module for a package that does not exist for the" + " calling user. package=" + packageName + ", user=" + userId); result = new DexManager.RegisterDexModuleResult(false, "Package not installed"); } else { result = mDexManager.registerDexModule(ai, dexModulePath, isSharedModule, userId); } if (callback != null) { mHandler.post(() -> { try { callback.onDexModuleRegistered(dexModulePath, result.success, result.message); } catch (RemoteException e) { Slog.w(TAG, "Failed to callback after module registration " + dexModulePath, e); } }); } } /** * Ask the package manager to perform a dex-opt with the given compiler filter. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptMode(String packageName, boolean checkProfiles, String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) { int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) | (force ? DexoptOptions.DEXOPT_FORCE : 0) | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0); return performDexOpt(new DexoptOptions(packageName, REASON_UNKNOWN, targetCompilerFilter, splitName, flags)); } /** * Ask the package manager to perform a dex-opt with the given compiler filter on the * secondary dex files belonging to the given package. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptSecondary(String packageName, String compilerFilter, boolean force) { int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES | DexoptOptions.DEXOPT_BOOT_COMPLETE | (force ? DexoptOptions.DEXOPT_FORCE : 0); return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags)); } /** * Ask the package manager to compile layouts in the given package. */ @Override public boolean compileLayouts(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { return false; } } return mViewCompiler.compileLayouts(pkg); } /*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 = findSharedLibraries(p); final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo); if (!deps.isEmpty()) { DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(), options.getCompilationReason(), options.getCompilerFilter(), options.getSplitName(), options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY); for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = null; synchronized (mPackages) { depPackage = mPackages.get(info.getPackageName()); } if (depPackage != null) { // TODO: Analyze and investigate if we (should) profile libraries. pdo.performDexOpt(depPackage, instructionSets, getOrCreateCompilerPackageStats(depPackage), mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions); } else { // TODO(ngeoffray): Support dexopting system shared libraries. } } } return pdo.performDexOpt(p, instructionSets, getOrCreateCompilerPackageStats(p), mDexManager.getPackageUseInfoOrDefault(p.packageName), options); } /** * Reconcile the information we have about the secondary dex files belonging to * {@code packageName} 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(@Nullable List packageNames) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return false; } enforceSystemOrRootOrShell("runBackgroundDexoptJob"); final long identity = Binder.clearCallingIdentity(); try { return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames); } finally { Binder.restoreCallingIdentity(identity); } } private static List findSharedLibraries(PackageParser.Package p) { if (p.usesLibraryInfos != null) { ArrayList retValue = new ArrayList<>(); Set collectedNames = new HashSet<>(); for (SharedLibraryInfo info : p.usesLibraryInfos) { findSharedLibrariesRecursive(info, retValue, collectedNames); } return retValue; } else { return Collections.emptyList(); } } private static void findSharedLibrariesRecursive(SharedLibraryInfo info, ArrayList collected, Set collectedNames) { if (!collectedNames.contains(info.getName())) { collectedNames.add(info.getName()); collected.add(info); if (info.getDependencies() != null) { for (SharedLibraryInfo dep : info.getDependencies()) { findSharedLibrariesRecursive(dep, collected, collectedNames); } } } } List findSharedNonSystemLibraries(PackageParser.Package pkg) { List deps = findSharedLibraries(pkg); if (!deps.isEmpty()) { ArrayList retValue = new ArrayList<>(); synchronized (mPackages) { for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = mPackages.get(info.getPackageName()); if (depPackage != null) { retValue.add(depPackage); } } } return retValue; } else { return Collections.emptyList(); } } @Nullable private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) { return getSharedLibraryInfo(name, version, mSharedLibraries, null); } @Nullable private static SharedLibraryInfo getSharedLibraryInfo(String name, long version, Map> existingLibraries, @Nullable Map> newLibraries) { if (newLibraries != null) { final LongSparseArray versionedLib = newLibraries.get(name); SharedLibraryInfo info = null; if (versionedLib != null) { info = versionedLib.get(version); } if (info != null) { return info; } } final LongSparseArray versionedLib = existingLibraries.get(name); if (versionedLib == null) { return null; } return versionedLib.get(version); } private SharedLibraryInfo getLatestSharedLibraVersionLPr(PackageParser.Package pkg) { LongSparseArray versionedLib = mSharedLibraries.get( pkg.staticSharedLibName); if (versionedLib == null) { return null; } long previousLibVersion = -1; final int versionCount = versionedLib.size(); for (int i = 0; i < versionCount; i++) { final long libVersion = versionedLib.keyAt(i); if (libVersion < pkg.staticSharedLibVersion) { previousLibVersion = Math.max(previousLibVersion, libVersion); } } if (previousLibVersion >= 0) { return versionedLib.get(previousLibVersion); } return null; } @Nullable private PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) { PackageSetting sharedLibPackage = null; synchronized (mPackages) { final SharedLibraryInfo latestSharedLibraVersionLPr = getLatestSharedLibraVersionLPr(scanResult.pkgSetting.pkg); if (latestSharedLibraVersionLPr != null) { sharedLibPackage = mSettings.getPackageLPr( latestSharedLibraVersionLPr.getPackageName()); } } return sharedLibPackage; } public void shutdown() { mPackageUsage.writeNow(mPackages); mCompilerStats.writeNow(); mDexManager.writePackageDexUsageNow(); PackageWatchdog.getInstance(mContext).writeNow(); // This is the last chance to write out pending restriction settings synchronized (mPackages) { if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS); for (int userId : mDirtyUsers) { mSettings.writePackageRestrictionsLPr(userId); } mDirtyUsers.clear(); } } } @Override public void dumpProfiles(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } /* Only the shell, root, or the app user should be able to dump profiles. */ int callingUid = Binder.getCallingUid(); if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID && callingUid != pkg.applicationInfo.uid) { throw new SecurityException("dumpProfiles"); } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles"); mArtManagerService.dumpProfiles(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @Override public void forceDexOpt(String packageName) { enforceSystemOrRoot("forceDexOpt"); PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); // Whoever is calling forceDexOpt wants a compiled package. // Don't use profiles since that may cause compilation to be skipped. final int res = performDexOptInternalWithDependenciesLI( pkg, new DexoptOptions(packageName, getDefaultCompilerFilter(), DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE)); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) { throw new IllegalStateException("Failed to dexopt: " + res); } } } @GuardedBy("mPackages") private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) { if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package not in system partition"); return false; } else if (mPackages.get(oldPkg.name) != null) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package still exists"); return false; } return true; } @GuardedBy("mInstallLock") void removeCodePathLI(File codePath) { if (codePath.isDirectory()) { try { mInstaller.rmPackageDir(codePath.getAbsolutePath()); } catch (InstallerException e) { Slog.w(TAG, "Failed to remove code path", e); } } else { codePath.delete(); } } private int[] resolveUserIds(int userId) { return (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds() : new int[] { userId }; } private void clearAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } clearAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { clearAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } clearAppProfilesLIF(pkg, UserHandle.USER_ALL); } private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } } private void destroyAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } } private void destroyAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.destroyAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId); } } private void destroyAppProfilesLIF(PackageParser.Package pkg) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppProfilesLeafLIF(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppProfilesLeafLIF(pkg.childPackages.get(i)); } } private void destroyAppProfilesLeafLIF(PackageParser.Package pkg) { try { mInstaller.destroyAppProfiles(pkg.packageName); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } private void clearAppProfilesLIF(PackageParser.Package pkg, int userId) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } mArtManagerService.clearAppProfiles(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { mArtManagerService.clearAppProfiles(pkg.childPackages.get(i)); } } private void setInstallAndUpdateTime(PackageParser.Package pkg, long firstInstallTime, long lastUpdateTime) { // Set parent install/update time PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } // Set children install/update time final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPkg = pkg.childPackages.get(i); ps = (PackageSetting) childPkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } } } @GuardedBy("mPackages") private void applyDefiningSharedLibraryUpdateLocked( PackageParser.Package pkg, SharedLibraryInfo libInfo, BiConsumer action) { // Note that libraries defined by this package may be null if: // - Package manager was unable to create the shared library. The package still // gets installed, but the shared library does not get created. // Or: // - Package manager is in a state where package isn't scanned yet. This will // get called again after scanning to fix the dependencies. if (pkg.isLibrary()) { if (pkg.staticSharedLibName != null) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( pkg.staticSharedLibName, pkg.staticSharedLibVersion); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } else { for (String libraryName : pkg.libraryNames) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( libraryName, SharedLibraryInfo.VERSION_UNDEFINED); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } } } } @GuardedBy("mPackages") private void addSharedLibraryLPr(PackageParser.Package pkg, Set usesLibraryFiles, SharedLibraryInfo libInfo, PackageParser.Package changingLib) { if (libInfo.getPath() != null) { usesLibraryFiles.add(libInfo.getPath()); return; } PackageParser.Package p = mPackages.get(libInfo.getPackageName()); if (changingLib != null && changingLib.packageName.equals(libInfo.getPackageName())) { // If we are doing this while in the middle of updating a library apk, // then we need to make sure to use that new apk for determining the // dependencies here. (We haven't yet finished committing the new apk // to the package manager state.) if (p == null || p.packageName.equals(changingLib.packageName)) { p = changingLib; } } if (p != null) { usesLibraryFiles.addAll(p.getAllCodePaths()); // If the package provides libraries, add the dependency to them. applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, (definingLibrary, dependency) -> { definingLibrary.addDependency(dependency); }); if (p.usesLibraryFiles != null) { Collections.addAll(usesLibraryFiles, p.usesLibraryFiles); } } } @GuardedBy("mPackages") private void updateSharedLibrariesLocked(PackageParser.Package pkg, PackageParser.Package changingLib, Map availablePackages) throws PackageManagerException { final ArrayList sharedLibraryInfos = collectSharedLibraryInfos(pkg, availablePackages, mSharedLibraries, null); executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos); } private static ArrayList collectSharedLibraryInfos(PackageParser.Package pkg, Map availablePackages, @NonNull final Map> existingLibraries, @Nullable final Map> newLibraries) throws PackageManagerException { if (pkg == null) { return null; } // The collection used here must maintain the order of addition (so // that libraries are searched in the correct order) and must have no // duplicates. ArrayList usesLibraryInfos = null; if (pkg.usesLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesLibraries, null, null, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, null, availablePackages, existingLibraries, newLibraries); } if (pkg.usesStaticLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } if (pkg.usesOptionalLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesOptionalLibraries, null, null, pkg.packageName, false, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } return usesLibraryInfos; } private void executeSharedLibrariesUpdateLPr(PackageParser.Package pkg, PackageParser.Package changingLib, ArrayList usesLibraryInfos) { // If the package provides libraries, clear their old dependencies. // This method will set them up again. applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> { definingLibrary.clearDependencies(); }); if (usesLibraryInfos != null) { pkg.usesLibraryInfos = usesLibraryInfos; // Use LinkedHashSet to preserve the order of files added to // usesLibraryFiles while eliminating duplicates. Set usesLibraryFiles = new LinkedHashSet<>(); for (SharedLibraryInfo libInfo : usesLibraryInfos) { addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib); } pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]); } else { pkg.usesLibraryInfos = null; pkg.usesLibraryFiles = null; } } @GuardedBy("mPackages") private static ArrayList collectSharedLibraryInfos( @NonNull List requestedLibraries, @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, @NonNull String packageName, boolean required, int targetSdk, @Nullable ArrayList outUsedLibraries, @NonNull final Map
After adding, the package is known to the system and available for querying. *
For packages located on the device ROM [eg. packages located in /system, /vendor, * etc...], additional checks are performed. Basic verification [such as ensuring * matching signatures, checking version codes, etc...] occurs if the package is * identical to a previously known package. If the package fails a signature check, * the version installed on /data will be removed. If the version of the new package * is less than or equal than the version on /data, it will be ignored. *
Regardless of the package location, the results are applied to the internal * structures and the package is made available to the rest of the system. *
NOTE: The return value should be removed. It's the passed in package object. */ @GuardedBy({"mInstallLock", "mPackages"}) private PackageParser.Package addForInitLI(PackageParser.Package pkg, @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { final boolean scanSystemPartition = (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0; final String renamedPkgName; final PackageSetting disabledPkgSetting; final boolean isSystemPkgUpdated; final boolean pkgAlreadyExists; PackageSetting pkgSetting; // NOTE: installPackageLI() has the same code to setup the package's // application info. This probably should be done lower in the call // stack [such as scanPackageOnly()]. However, we verify the application // info prior to that [in scanPackageNew()] and thus have to setup // the application info early. pkg.setApplicationVolumeUuid(pkg.volumeUuid); pkg.setApplicationInfoCodePath(pkg.codePath); pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath); pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths); pkg.setApplicationInfoResourcePath(pkg.codePath); pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath); pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths); synchronized (mPackages) { renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage); final String realPkgName = getRealPackageName(pkg, renamedPkgName); if (realPkgName != null) { ensurePackageRenamed(pkg, renamedPkgName); } final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName); final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName); pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting; pkgAlreadyExists = pkgSetting != null; final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName; disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName); isSystemPkgUpdated = disabledPkgSetting != null; if (DEBUG_INSTALL && isSystemPkgUpdated) { Slog.d(TAG, "updatedPkg = " + disabledPkgSetting); } final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null) ? mSettings.getSharedUserLPw(pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true) : null; if (DEBUG_PACKAGE_SCANNING && (parseFlags & PackageParser.PARSE_CHATTY) != 0 && sharedUserSetting != null) { Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + sharedUserSetting.userId + "):" + " packages=" + sharedUserSetting.packages); } if (scanSystemPartition) { // Potentially prune child packages. If the application on the /system // partition has been updated via OTA, but, is still disabled by a // version on /data, cycle through all of its children packages and // remove children that are no longer defined. if (isSystemPkgUpdated) { final int scannedChildCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; final int disabledChildCount = disabledPkgSetting.childPackageNames != null ? disabledPkgSetting.childPackageNames.size() : 0; for (int i = 0; i < disabledChildCount; i++) { String disabledChildPackageName = disabledPkgSetting.childPackageNames.get(i); boolean disabledPackageAvailable = false; for (int j = 0; j < scannedChildCount; j++) { PackageParser.Package childPkg = pkg.childPackages.get(j); if (childPkg.packageName.equals(disabledChildPackageName)) { disabledPackageAvailable = true; break; } } if (!disabledPackageAvailable) { mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName); } } // we're updating the disabled package, so, scan it as the package setting final ScanRequest request = new ScanRequest(pkg, sharedUserSetting, null, disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */, null /* originalPkgSetting */, null, parseFlags, scanFlags, (pkg == mPlatformPackage), user); applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage); final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L); if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) { scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting); } } } } final boolean newPkgChangedPaths = pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath); final boolean newPkgVersionGreater = pkgAlreadyExists && pkg.getLongVersionCode() > pkgSetting.versionCode; final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated && newPkgChangedPaths && newPkgVersionGreater; if (isSystemPkgBetter) { // The version of the application on /system is greater than the version on // /data. Switch back to the application on /system. // It's safe to assume the application on /system will correctly scan. If not, // there won't be a working copy of the application. synchronized (mPackages) { // just remove the loaded entries from package lists mPackages.remove(pkgSetting.name); } logCriticalInfo(Log.WARN, "System package updated;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); final InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); args.cleanUpResourcesLI(); synchronized (mPackages) { mSettings.enableSystemPackageLPw(pkgSetting.name); } } if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) { // The version of the application on the /system partition is less than or // equal to the version on the /data partition. Throw an exception and use // the application already installed on the /data partition. throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at " + pkg.codePath + " ignored: updated version " + pkgSetting.versionCode + " better than this " + pkg.getLongVersionCode()); } // Verify certificates against what was last scanned. If there was an upgrade and this is an // app in a system partition, or if this is an updated priv app, we will force re-collecting // certificate. final boolean forceCollect = (mIsUpgrade && scanSystemPartition) || PackageManagerServiceUtils.isApkVerificationForced(disabledPkgSetting); // Full APK verification can be skipped during certificate collection, only if the file is // in verified partition, or can be verified on access (when apk verity is enabled). In both // cases, only data in Signing Block is verified instead of the whole file. final boolean skipVerify = scanSystemPartition || (forceCollect && canSkipForcedPackageVerification(pkg)); collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify); // Reset profile if the application version is changed maybeClearProfilesForUpgradesLI(pkgSetting, pkg); /* * A new system app appeared, but we already had a non-system one of the * same name installed earlier. */ boolean shouldHideSystemApp = false; // A new application appeared on /system, but, we already have a copy of // the application installed on /data. if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists && !pkgSetting.isSystem()) { if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA) && !pkgSetting.signatures.mSigningDetails.checkCapability( pkg.mSigningDetails, PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) { logCriticalInfo(Log.WARN, "System package signature mismatch;" + " name: " + pkgSetting.name); try (PackageFreezer freezer = freezePackage(pkg.packageName, "scanPackageInternalLI")) { deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null); } pkgSetting = null; } else if (newPkgVersionGreater) { // The application on /system is newer than the application on /data. // Simply remove the application on /data [keeping application data] // and replace it with the version on /system. logCriticalInfo(Log.WARN, "System package enabled;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode() + "; " + pkgSetting.codePathString + " --> " + pkg.codePath); InstallArgs args = createInstallArgsForExisting( pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } } else { // The application on /system is older than the application on /data. Hide // the application on /system and the version on /data will be scanned later // and re-added like an update. shouldHideSystemApp = true; logCriticalInfo(Log.INFO, "System package disabled;" + " name: " + pkgSetting.name + "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode + "; new: " + pkg.codePath + " @ " + pkg.codePath); } } final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user); if (scanResult.success) { synchronized (mPackages) { boolean appIdCreated = false; try { final String pkgName = scanResult.pkgSetting.name; final Map reconcileResult = reconcilePackagesLocked( new ReconcileRequest( Collections.singletonMap(pkgName, scanResult), mSharedLibraries, mPackages, Collections.singletonMap( pkgName, getSettingsVersionForPackage(pkg)), Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))), mSettings.mKeySetManagerService); appIdCreated = optimisticallyRegisterAppId(scanResult); commitReconciledScanResultLocked(reconcileResult.get(pkgName)); } catch (PackageManagerException e) { if (appIdCreated) { cleanUpAppIdCreation(scanResult); } throw e; } } } if (shouldHideSystemApp) { synchronized (mPackages) { mSettings.disableSystemPackageLPw(pkg.packageName, true); } } return scanResult.pkgSetting.pkg; } private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) { // Derive the new package synthetic package name pkg.setPackageName(pkg.packageName + STATIC_SHARED_LIB_DELIMITER + pkg.staticSharedLibVersion); } static String fixProcessName(String defProcessName, String processName) { if (processName == null) { return defProcessName; } return processName; } /** * Enforces that only the system UID or root's UID can call a method exposed * via Binder. * * @param message used as message if SecurityException is thrown * @throws SecurityException if the caller is not system or root */ private static void enforceSystemOrRoot(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) { throw new SecurityException(message); } } /** * Enforces that only the system UID or root's UID or shell'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 shell */ private static void enforceSystemOrRootOrShell(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID && uid != Process.SHELL_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 = isDeviceUpgrading(); // First boot or factory reset. // Note: we also handle devices that are upgrading to N right now as if it is their // first boot, as they do not have profile data. boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade; // We need to re-extract after a pruned cache, as AoT-ed files will be out of date. boolean causePrunedCache = VMRuntime.didPruneDalvikCache(); if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) { return; } List pkgs; synchronized (mPackages) { pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this); } final long startTime = System.nanoTime(); final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */, causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT, false /* bootComplete */); final int elapsedTimeSeconds = (int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime); MetricsLogger.histogram(mContext, "opt_dialog_num_dexopted", stats[0]); MetricsLogger.histogram(mContext, "opt_dialog_num_skipped", stats[1]); MetricsLogger.histogram(mContext, "opt_dialog_num_failed", stats[2]); MetricsLogger.histogram(mContext, "opt_dialog_num_total", getOptimizablePackages().size()); MetricsLogger.histogram(mContext, "opt_dialog_time_s", elapsedTimeSeconds); } /* * Return the prebuilt profile path given a package base code path. */ private static String getPrebuildProfilePath(PackageParser.Package pkg) { return pkg.baseCodePath + ".prof"; } /** * Performs dexopt on the set of packages in {@code packages} and returns an int array * containing statistics about the invocation. The array consists of three elements, * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped} * and {@code numberOfPackagesFailed}. */ private int[] performDexOptUpgrade(List pkgs, boolean showDialog, final int compilationReason, boolean bootComplete) { int numberOfPackagesVisited = 0; int numberOfPackagesOptimized = 0; int numberOfPackagesSkipped = 0; int numberOfPackagesFailed = 0; final int numberOfPackagesToDexopt = pkgs.size(); for (PackageParser.Package pkg : pkgs) { numberOfPackagesVisited++; boolean useProfileForDexopt = false; if ((isFirstBoot() || isDeviceUpgrading()) && isSystemApp(pkg)) { // Copy over initial preopt profiles since we won't get any JIT samples for methods // that are already compiled. File profileFile = new File(getPrebuildProfilePath(pkg)); // Copy profile if it exists. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The issue // is that we don't have a good way to say "do this only once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Installer failed to copy system profile!"); } else { // Disabled as this causes speed-profile compilation during first boot // even if things are already compiled. // useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } else { PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName); // Handle compressed APKs in this path. Only do this for stubs with profiles to // minimize the number off apps being speed-profile compiled during first boot. // The other paths will not change the filter. if (disabledPs != null && disabledPs.pkg.isStub) { // The package is the stub one, remove the stub suffix to get the normal // package and APK names. String systemProfilePath = getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, ""); profileFile = new File(systemProfilePath); // If we have a profile for a compressed APK, copy it to the reference // location. // Note that copying the profile here will cause it to override the // reference profile every OTA even though the existing reference profile // may have more data. We can't copy during decompression since the // directories are not set up at that point. if (profileFile.exists()) { try { // We could also do this lazily before calling dexopt in // PackageDexOptimizer to prevent this happening on first boot. The // issue is that we don't have a good way to say "do this only // once". if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName, ArtManager.getProfileName(null))) { Log.e(TAG, "Failed to copy system profile for stub package!"); } else { useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } } } } } if (!PackageDexOptimizer.canOptimizePackage(pkg)) { if (DEBUG_DEXOPT) { Log.i(TAG, "Skipping update of of non-optimizable app " + pkg.packageName); } numberOfPackagesSkipped++; continue; } if (DEBUG_DEXOPT) { Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " + numberOfPackagesToDexopt + ": " + pkg.packageName); } if (showDialog) { try { ActivityManager.getService().showBootMessage( mContext.getResources().getString(R.string.android_upgrading_apk, numberOfPackagesVisited, numberOfPackagesToDexopt), true); } catch (RemoteException e) { } synchronized (mPackages) { mDexOptDialogShown = true; } } int pkgCompilationReason = compilationReason; if (useProfileForDexopt) { // Use background dexopt mode to try and use the profile. Note that this does not // guarantee usage of the profile. pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT; } if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { mArtManagerService.compileLayouts(pkg); } // checkProfiles is false to avoid merging profiles during boot which // might interfere with background compilation (b/28612421). // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a // trade-off worth doing to save boot time work. int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0; if (compilationReason == REASON_FIRST_BOOT) { // TODO: This doesn't cover the upgrade case, we should check for this too. dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE; } int primaryDexOptStaus = performDexOptTraced(new DexoptOptions( pkg.packageName, pkgCompilationReason, dexoptFlags)); switch (primaryDexOptStaus) { case PackageDexOptimizer.DEX_OPT_PERFORMED: numberOfPackagesOptimized++; break; case PackageDexOptimizer.DEX_OPT_SKIPPED: numberOfPackagesSkipped++; break; case PackageDexOptimizer.DEX_OPT_FAILED: numberOfPackagesFailed++; break; default: Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus); break; } } return new int[] { numberOfPackagesOptimized, numberOfPackagesSkipped, numberOfPackagesFailed }; } @Override public void notifyPackageUse(String packageName, int reason) { synchronized (mPackages) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); if (getInstantAppPackageName(callingUid) != null) { if (!isCallerSameApp(packageName, callingUid)) { return; } } else { if (isInstantApp(packageName, callingUserId)) { return; } } notifyPackageUseLocked(packageName, reason); } } @GuardedBy("mPackages") public CheckPermissionDelegate getCheckPermissionDelegateLocked() { return mCheckPermissionDelegate; } @GuardedBy("mPackages") public void setCheckPermissionDelegateLocked(CheckPermissionDelegate delegate) { mCheckPermissionDelegate = delegate; } @GuardedBy("mPackages") private void notifyPackageUseLocked(String packageName, int reason) { final PackageParser.Package p = mPackages.get(packageName); if (p == null) { return; } p.mLastPackageUsageTimeInMills[reason] = System.currentTimeMillis(); } @Override public void notifyDexLoad(String loadingPackageName, List classLoaderNames, List classPaths, String loaderIsa) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); if (ai == null) { Slog.w(TAG, "Loading a package that does not exist for the calling user. package=" + loadingPackageName + ", user=" + userId); return; } mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId); } @Override public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule, IDexModuleRegisterCallback callback) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(packageName, /*flags*/ 0, userId); DexManager.RegisterDexModuleResult result; if (ai == null) { Slog.w(TAG, "Registering a dex module for a package that does not exist for the" + " calling user. package=" + packageName + ", user=" + userId); result = new DexManager.RegisterDexModuleResult(false, "Package not installed"); } else { result = mDexManager.registerDexModule(ai, dexModulePath, isSharedModule, userId); } if (callback != null) { mHandler.post(() -> { try { callback.onDexModuleRegistered(dexModulePath, result.success, result.message); } catch (RemoteException e) { Slog.w(TAG, "Failed to callback after module registration " + dexModulePath, e); } }); } } /** * Ask the package manager to perform a dex-opt with the given compiler filter. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptMode(String packageName, boolean checkProfiles, String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) { int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) | (force ? DexoptOptions.DEXOPT_FORCE : 0) | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0); return performDexOpt(new DexoptOptions(packageName, REASON_UNKNOWN, targetCompilerFilter, splitName, flags)); } /** * Ask the package manager to perform a dex-opt with the given compiler filter on the * secondary dex files belonging to the given package. * * Note: exposed only for the shell command to allow moving packages explicitly to a * definite state. */ @Override public boolean performDexOptSecondary(String packageName, String compilerFilter, boolean force) { int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES | DexoptOptions.DEXOPT_BOOT_COMPLETE | (force ? DexoptOptions.DEXOPT_FORCE : 0); return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags)); } /** * Ask the package manager to compile layouts in the given package. */ @Override public boolean compileLayouts(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { return false; } } return mViewCompiler.compileLayouts(pkg); } /*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 = findSharedLibraries(p); final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo); if (!deps.isEmpty()) { DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(), options.getCompilationReason(), options.getCompilerFilter(), options.getSplitName(), options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY); for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = null; synchronized (mPackages) { depPackage = mPackages.get(info.getPackageName()); } if (depPackage != null) { // TODO: Analyze and investigate if we (should) profile libraries. pdo.performDexOpt(depPackage, instructionSets, getOrCreateCompilerPackageStats(depPackage), mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions); } else { // TODO(ngeoffray): Support dexopting system shared libraries. } } } return pdo.performDexOpt(p, instructionSets, getOrCreateCompilerPackageStats(p), mDexManager.getPackageUseInfoOrDefault(p.packageName), options); } /** * Reconcile the information we have about the secondary dex files belonging to * {@code packageName} 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(@Nullable List packageNames) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return false; } enforceSystemOrRootOrShell("runBackgroundDexoptJob"); final long identity = Binder.clearCallingIdentity(); try { return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext, packageNames); } finally { Binder.restoreCallingIdentity(identity); } } private static List findSharedLibraries(PackageParser.Package p) { if (p.usesLibraryInfos != null) { ArrayList retValue = new ArrayList<>(); Set collectedNames = new HashSet<>(); for (SharedLibraryInfo info : p.usesLibraryInfos) { findSharedLibrariesRecursive(info, retValue, collectedNames); } return retValue; } else { return Collections.emptyList(); } } private static void findSharedLibrariesRecursive(SharedLibraryInfo info, ArrayList collected, Set collectedNames) { if (!collectedNames.contains(info.getName())) { collectedNames.add(info.getName()); collected.add(info); if (info.getDependencies() != null) { for (SharedLibraryInfo dep : info.getDependencies()) { findSharedLibrariesRecursive(dep, collected, collectedNames); } } } } List findSharedNonSystemLibraries(PackageParser.Package pkg) { List deps = findSharedLibraries(pkg); if (!deps.isEmpty()) { ArrayList retValue = new ArrayList<>(); synchronized (mPackages) { for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = mPackages.get(info.getPackageName()); if (depPackage != null) { retValue.add(depPackage); } } } return retValue; } else { return Collections.emptyList(); } } @Nullable private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) { return getSharedLibraryInfo(name, version, mSharedLibraries, null); } @Nullable private static SharedLibraryInfo getSharedLibraryInfo(String name, long version, Map> existingLibraries, @Nullable Map> newLibraries) { if (newLibraries != null) { final LongSparseArray versionedLib = newLibraries.get(name); SharedLibraryInfo info = null; if (versionedLib != null) { info = versionedLib.get(version); } if (info != null) { return info; } } final LongSparseArray versionedLib = existingLibraries.get(name); if (versionedLib == null) { return null; } return versionedLib.get(version); } private SharedLibraryInfo getLatestSharedLibraVersionLPr(PackageParser.Package pkg) { LongSparseArray versionedLib = mSharedLibraries.get( pkg.staticSharedLibName); if (versionedLib == null) { return null; } long previousLibVersion = -1; final int versionCount = versionedLib.size(); for (int i = 0; i < versionCount; i++) { final long libVersion = versionedLib.keyAt(i); if (libVersion < pkg.staticSharedLibVersion) { previousLibVersion = Math.max(previousLibVersion, libVersion); } } if (previousLibVersion >= 0) { return versionedLib.get(previousLibVersion); } return null; } @Nullable private PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) { PackageSetting sharedLibPackage = null; synchronized (mPackages) { final SharedLibraryInfo latestSharedLibraVersionLPr = getLatestSharedLibraVersionLPr(scanResult.pkgSetting.pkg); if (latestSharedLibraVersionLPr != null) { sharedLibPackage = mSettings.getPackageLPr( latestSharedLibraVersionLPr.getPackageName()); } } return sharedLibPackage; } public void shutdown() { mPackageUsage.writeNow(mPackages); mCompilerStats.writeNow(); mDexManager.writePackageDexUsageNow(); PackageWatchdog.getInstance(mContext).writeNow(); // This is the last chance to write out pending restriction settings synchronized (mPackages) { if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS); for (int userId : mDirtyUsers) { mSettings.writePackageRestrictionsLPr(userId); } mDirtyUsers.clear(); } } } @Override public void dumpProfiles(String packageName) { PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } /* Only the shell, root, or the app user should be able to dump profiles. */ int callingUid = Binder.getCallingUid(); if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID && callingUid != pkg.applicationInfo.uid) { throw new SecurityException("dumpProfiles"); } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles"); mArtManagerService.dumpProfiles(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @Override public void forceDexOpt(String packageName) { enforceSystemOrRoot("forceDexOpt"); PackageParser.Package pkg; synchronized (mPackages) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); // Whoever is calling forceDexOpt wants a compiled package. // Don't use profiles since that may cause compilation to be skipped. final int res = performDexOptInternalWithDependenciesLI( pkg, new DexoptOptions(packageName, getDefaultCompilerFilter(), DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE)); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) { throw new IllegalStateException("Failed to dexopt: " + res); } } } @GuardedBy("mPackages") private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) { if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package not in system partition"); return false; } else if (mPackages.get(oldPkg.name) != null) { Slog.w(TAG, "Unable to update from " + oldPkg.name + " to " + newPkg.packageName + ": old package still exists"); return false; } return true; } @GuardedBy("mInstallLock") void removeCodePathLI(File codePath) { if (codePath.isDirectory()) { try { mInstaller.rmPackageDir(codePath.getAbsolutePath()); } catch (InstallerException e) { Slog.w(TAG, "Failed to remove code path", e); } } else { codePath.delete(); } } private int[] resolveUserIds(int userId) { return (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds() : new int[] { userId }; } private void clearAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } clearAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { clearAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } clearAppProfilesLIF(pkg, UserHandle.USER_ALL); } private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } } private void destroyAppDataLIF(PackageParser.Package pkg, int userId, int flags) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppDataLeafLIF(pkg, userId, flags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppDataLeafLIF(pkg.childPackages.get(i), userId, flags); } } private void destroyAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; synchronized (mPackages) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0; try { mInstaller.destroyAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags, ceDataInode); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId); } } private void destroyAppProfilesLIF(PackageParser.Package pkg) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } destroyAppProfilesLeafLIF(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { destroyAppProfilesLeafLIF(pkg.childPackages.get(i)); } } private void destroyAppProfilesLeafLIF(PackageParser.Package pkg) { try { mInstaller.destroyAppProfiles(pkg.packageName); } catch (InstallerException e) { Slog.w(TAG, String.valueOf(e)); } } private void clearAppProfilesLIF(PackageParser.Package pkg, int userId) { if (pkg == null) { Slog.wtf(TAG, "Package was null!", new Throwable()); return; } mArtManagerService.clearAppProfiles(pkg); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { mArtManagerService.clearAppProfiles(pkg.childPackages.get(i)); } } private void setInstallAndUpdateTime(PackageParser.Package pkg, long firstInstallTime, long lastUpdateTime) { // Set parent install/update time PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } // Set children install/update time final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageParser.Package childPkg = pkg.childPackages.get(i); ps = (PackageSetting) childPkg.mExtras; if (ps != null) { ps.firstInstallTime = firstInstallTime; ps.lastUpdateTime = lastUpdateTime; } } } @GuardedBy("mPackages") private void applyDefiningSharedLibraryUpdateLocked( PackageParser.Package pkg, SharedLibraryInfo libInfo, BiConsumer action) { // Note that libraries defined by this package may be null if: // - Package manager was unable to create the shared library. The package still // gets installed, but the shared library does not get created. // Or: // - Package manager is in a state where package isn't scanned yet. This will // get called again after scanning to fix the dependencies. if (pkg.isLibrary()) { if (pkg.staticSharedLibName != null) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( pkg.staticSharedLibName, pkg.staticSharedLibVersion); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } else { for (String libraryName : pkg.libraryNames) { SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr( libraryName, SharedLibraryInfo.VERSION_UNDEFINED); if (definedLibrary != null) { action.accept(definedLibrary, libInfo); } } } } } @GuardedBy("mPackages") private void addSharedLibraryLPr(PackageParser.Package pkg, Set usesLibraryFiles, SharedLibraryInfo libInfo, PackageParser.Package changingLib) { if (libInfo.getPath() != null) { usesLibraryFiles.add(libInfo.getPath()); return; } PackageParser.Package p = mPackages.get(libInfo.getPackageName()); if (changingLib != null && changingLib.packageName.equals(libInfo.getPackageName())) { // If we are doing this while in the middle of updating a library apk, // then we need to make sure to use that new apk for determining the // dependencies here. (We haven't yet finished committing the new apk // to the package manager state.) if (p == null || p.packageName.equals(changingLib.packageName)) { p = changingLib; } } if (p != null) { usesLibraryFiles.addAll(p.getAllCodePaths()); // If the package provides libraries, add the dependency to them. applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, (definingLibrary, dependency) -> { definingLibrary.addDependency(dependency); }); if (p.usesLibraryFiles != null) { Collections.addAll(usesLibraryFiles, p.usesLibraryFiles); } } } @GuardedBy("mPackages") private void updateSharedLibrariesLocked(PackageParser.Package pkg, PackageParser.Package changingLib, Map availablePackages) throws PackageManagerException { final ArrayList sharedLibraryInfos = collectSharedLibraryInfos(pkg, availablePackages, mSharedLibraries, null); executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos); } private static ArrayList collectSharedLibraryInfos(PackageParser.Package pkg, Map availablePackages, @NonNull final Map> existingLibraries, @Nullable final Map> newLibraries) throws PackageManagerException { if (pkg == null) { return null; } // The collection used here must maintain the order of addition (so // that libraries are searched in the correct order) and must have no // duplicates. ArrayList usesLibraryInfos = null; if (pkg.usesLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesLibraries, null, null, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, null, availablePackages, existingLibraries, newLibraries); } if (pkg.usesStaticLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests, pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } if (pkg.usesOptionalLibraries != null) { usesLibraryInfos = collectSharedLibraryInfos(pkg.usesOptionalLibraries, null, null, pkg.packageName, false, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos, availablePackages, existingLibraries, newLibraries); } return usesLibraryInfos; } private void executeSharedLibrariesUpdateLPr(PackageParser.Package pkg, PackageParser.Package changingLib, ArrayList usesLibraryInfos) { // If the package provides libraries, clear their old dependencies. // This method will set them up again. applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> { definingLibrary.clearDependencies(); }); if (usesLibraryInfos != null) { pkg.usesLibraryInfos = usesLibraryInfos; // Use LinkedHashSet to preserve the order of files added to // usesLibraryFiles while eliminating duplicates. Set usesLibraryFiles = new LinkedHashSet<>(); for (SharedLibraryInfo libInfo : usesLibraryInfos) { addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib); } pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]); } else { pkg.usesLibraryInfos = null; pkg.usesLibraryFiles = null; } } @GuardedBy("mPackages") private static ArrayList collectSharedLibraryInfos( @NonNull List requestedLibraries, @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, @NonNull String packageName, boolean required, int targetSdk, @Nullable ArrayList outUsedLibraries, @NonNull final Map