| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.pm; |
| |
| import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED; |
| import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED; |
| import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED; |
| import static android.os.Process.SYSTEM_UID; |
| import static android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED; |
| |
| import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP; |
| import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; |
| import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY; |
| import static com.android.server.pm.PackageManagerService.PACKAGE_SCHEME; |
| import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; |
| import static com.android.server.pm.PackageManagerService.TAG; |
| |
| import android.Manifest; |
| import android.annotation.AppIdInt; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.SuppressLint; |
| import android.app.ActivityManager; |
| import android.app.ActivityManagerInternal; |
| import android.app.BroadcastOptions; |
| import android.app.IActivityManager; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.IIntentReceiver; |
| import android.content.Intent; |
| import android.content.pm.PackageInstaller; |
| import android.content.pm.PackageManager; |
| import android.content.pm.UserInfo; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.PowerExemptionManager; |
| import android.os.Process; |
| import android.os.RemoteException; |
| import android.os.UserHandle; |
| import android.os.storage.StorageManager; |
| import android.os.storage.VolumeInfo; |
| import android.provider.DeviceConfig; |
| import android.stats.storage.StorageEnums; |
| import android.util.IntArray; |
| import android.util.Log; |
| import android.util.Pair; |
| import android.util.Slog; |
| import android.util.SparseArray; |
| |
| import com.android.internal.util.ArrayUtils; |
| import com.android.internal.util.FrameworkStatsLog; |
| import com.android.server.pm.pkg.AndroidPackage; |
| import com.android.server.pm.pkg.PackageStateInternal; |
| import com.android.server.pm.pkg.PackageUserStateInternal; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.function.BiFunction; |
| |
| /** |
| * Helper class to send broadcasts for various situations. |
| */ |
| public final class BroadcastHelper { |
| private static final boolean DEBUG_BROADCASTS = false; |
| /** |
| * 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}; |
| |
| private final UserManagerInternal mUmInternal; |
| private final ActivityManagerInternal mAmInternal; |
| private final Context mContext; |
| private final Handler mHandler; |
| private final PackageMonitorCallbackHelper mPackageMonitorCallbackHelper; |
| private final AppsFilterSnapshot mAppsFilter; |
| |
| BroadcastHelper(PackageManagerServiceInjector injector) { |
| mUmInternal = injector.getUserManagerInternal(); |
| mAmInternal = injector.getActivityManagerInternal(); |
| mContext = injector.getContext(); |
| mHandler = injector.getHandler(); |
| mPackageMonitorCallbackHelper = injector.getPackageMonitorCallbackHelper(); |
| mAppsFilter = injector.getAppsFilter(); |
| } |
| |
| /** |
| * Sends a broadcast to registered clients on userId for the given Intent. |
| */ |
| void sendPackageBroadcastWithIntent(Intent intent, int userId, boolean isInstantApp, |
| @Intent.Flags int flags, |
| int[] visibilityAllowList, |
| final IIntentReceiver finishedReceiver, |
| @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, |
| @Nullable Bundle bOptions) { |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags); |
| SparseArray<int[]> broadcastAllowList = new SparseArray<>(); |
| broadcastAllowList.put(userId, visibilityAllowList); |
| broadcastIntent(intent, finishedReceiver, isInstantApp, userId, broadcastAllowList, |
| filterExtrasForReceiver, bOptions); |
| } |
| |
| void sendPackageBroadcast(final String action, final String pkg, final Bundle extras, |
| final int flags, final String targetPkg, final IIntentReceiver finishedReceiver, |
| final int[] userIds, int[] instantUserIds, |
| @Nullable SparseArray<int[]> broadcastAllowList, |
| @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, |
| @Nullable Bundle bOptions) { |
| try { |
| final IActivityManager am = ActivityManager.getService(); |
| if (am == null) return; |
| final int[] resolvedUserIds; |
| if (userIds == null) { |
| resolvedUserIds = am.getRunningUserIds(); |
| } else { |
| resolvedUserIds = userIds; |
| } |
| |
| if (ArrayUtils.isEmpty(instantUserIds)) { |
| doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver, |
| resolvedUserIds, false /* isInstantApp */, broadcastAllowList, |
| filterExtrasForReceiver, bOptions); |
| } else { |
| // send restricted broadcasts for instant apps |
| doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver, |
| instantUserIds, true /* isInstantApp */, null, |
| null /* filterExtrasForReceiver */, bOptions); |
| } |
| } catch (RemoteException ex) { |
| } |
| } |
| |
| /** |
| * Sends a broadcast for the given action. |
| * <p>If {@code isInstantApp} is {@code true}, then the broadcast is protected with |
| * the {@link android.Manifest.permission#ACCESS_INSTANT_APPS} permission. This allows |
| * the system and applications allowed to see instant applications to receive package |
| * lifecycle events for instant applications. |
| */ |
| private void doSendBroadcast( |
| @NonNull String action, |
| @Nullable String pkg, |
| @Nullable Bundle extras, |
| int flags, |
| @Nullable String targetPkg, |
| @Nullable IIntentReceiver finishedReceiver, |
| @NonNull int[] userIds, |
| boolean isInstantApp, |
| @Nullable SparseArray<int[]> broadcastAllowList, |
| @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, |
| @Nullable Bundle bOptions) { |
| for (int userId : userIds) { |
| final Intent intent = new Intent(action, |
| pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null); |
| if (extras != null) { |
| intent.putExtras(extras); |
| } |
| if (targetPkg != null) { |
| intent.setPackage(targetPkg); |
| } |
| // Modify the UID when posting to other users |
| int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); |
| if (uid >= 0 && UserHandle.getUserId(uid) != userId) { |
| uid = UserHandle.getUid(userId, UserHandle.getAppId(uid)); |
| intent.putExtra(Intent.EXTRA_UID, uid); |
| } |
| if (broadcastAllowList != null && PLATFORM_PACKAGE_NAME.equals(targetPkg)) { |
| intent.putExtra(Intent.EXTRA_VISIBILITY_ALLOW_LIST, |
| broadcastAllowList.get(userId)); |
| } |
| intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags); |
| broadcastIntent(intent, finishedReceiver, isInstantApp, userId, broadcastAllowList, |
| filterExtrasForReceiver, bOptions); |
| } |
| } |
| |
| |
| private void broadcastIntent(Intent intent, IIntentReceiver finishedReceiver, |
| boolean isInstantApp, int userId, @Nullable SparseArray<int[]> broadcastAllowList, |
| @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, |
| @Nullable Bundle bOptions) { |
| final String[] requiredPermissions = |
| isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null; |
| if (DEBUG_BROADCASTS) { |
| RuntimeException here = new RuntimeException("here"); |
| here.fillInStackTrace(); |
| Slog.d(TAG, "Sending to user " + userId + ": " |
| + intent.toShortString(false, true, false, false) |
| + " " + intent.getExtras(), here); |
| } |
| final boolean ordered; |
| if (mAmInternal.isModernQueueEnabled()) { |
| // When the modern broadcast stack is enabled, deliver all our |
| // broadcasts as unordered, since the modern stack has better |
| // support for sequencing cold-starts, and it supports |
| // delivering resultTo for non-ordered broadcasts |
| ordered = false; |
| } else { |
| ordered = (finishedReceiver != null); |
| } |
| mAmInternal.broadcastIntent( |
| intent, finishedReceiver, requiredPermissions, ordered, userId, |
| broadcastAllowList == null ? null : broadcastAllowList.get(userId), |
| filterExtrasForReceiver, bOptions); |
| } |
| |
| void sendResourcesChangedBroadcast(@NonNull Computer snapshot, |
| boolean mediaStatus, |
| boolean replacing, |
| @NonNull String[] pkgNames, |
| @NonNull int[] uids) { |
| if (ArrayUtils.isEmpty(pkgNames) || ArrayUtils.isEmpty(uids)) { |
| return; |
| } |
| Bundle extras = new Bundle(); |
| extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgNames); |
| extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uids); |
| if (replacing) { |
| extras.putBoolean(Intent.EXTRA_REPLACING, replacing); |
| } |
| String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE |
| : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; |
| sendPackageBroadcast(action, null /* pkg */, extras, 0 /* flags */, |
| null /* targetPkg */, null /* finishedReceiver */, null /* userIds */, |
| null /* instantUserIds */, null /* broadcastAllowList */, |
| (callingUid, intentExtras) -> filterExtrasChangedPackageList( |
| snapshot, callingUid, intentExtras), |
| null /* bOptions */); |
| } |
| |
| /** |
| * The just-installed/enabled app is bundled on the system, so presumed to be able to run |
| * automatically without needing an explicit launch. |
| * Send it a LOCKED_BOOT_COMPLETED/BOOT_COMPLETED if it would ordinarily have gotten ones. |
| */ |
| private void sendBootCompletedBroadcastToSystemApp(@NonNull String packageName, |
| boolean includeStopped, |
| int userId) { |
| // If user is not running, the app didn't miss any broadcast |
| if (!mUmInternal.isUserRunning(userId)) { |
| return; |
| } |
| final IActivityManager am = ActivityManager.getService(); |
| try { |
| // Deliver LOCKED_BOOT_COMPLETED first |
| Intent lockedBcIntent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED) |
| .setPackage(packageName); |
| if (includeStopped) { |
| lockedBcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); |
| } |
| final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED}; |
| final BroadcastOptions bOptions = getTemporaryAppAllowlistBroadcastOptions( |
| REASON_LOCKED_BOOT_COMPLETED); |
| am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null, |
| requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE, |
| bOptions.toBundle(), false, false, userId); |
| |
| // Deliver BOOT_COMPLETED only if user is unlocked |
| if (mUmInternal.isUserUnlockingOrUnlocked(userId)) { |
| Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED).setPackage(packageName); |
| if (includeStopped) { |
| bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); |
| } |
| am.broadcastIntentWithFeature(null, null, bcIntent, null, null, 0, null, null, |
| requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE, |
| bOptions.toBundle(), false, false, userId); |
| } |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| private @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions( |
| @PowerExemptionManager.ReasonCode int reasonCode) { |
| long duration = 10_000; |
| if (mAmInternal != null) { |
| duration = mAmInternal.getBootTimeTempAllowListDuration(); |
| } |
| final BroadcastOptions bOptions = BroadcastOptions.makeBasic(); |
| bOptions.setTemporaryAppAllowlist(duration, |
| TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, |
| reasonCode, ""); |
| return bOptions; |
| } |
| |
| private void sendPackageChangedBroadcast(@NonNull String packageName, |
| boolean dontKillApp, |
| @NonNull ArrayList<String> componentNames, |
| int packageUid, |
| @Nullable String reason, |
| @Nullable int[] userIds, |
| @Nullable int[] instantUserIds, |
| @Nullable SparseArray<int[]> broadcastAllowList) { |
| if (DEBUG_INSTALL) { |
| Log.v(TAG, "Sending package changed: package=" + packageName + " components=" |
| + componentNames); |
| } |
| Bundle extras = new Bundle(4); |
| extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0)); |
| String[] nameList = new String[componentNames.size()]; |
| componentNames.toArray(nameList); |
| extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList); |
| extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, dontKillApp); |
| extras.putInt(Intent.EXTRA_UID, packageUid); |
| if (reason != null) { |
| extras.putString(Intent.EXTRA_REASON, reason); |
| } |
| // If this is not reporting a change of the overall package, then only send it |
| // to registered receivers. We don't want to launch a swath of apps for every |
| // little component state change. |
| final int flags = !componentNames.contains(packageName) |
| ? Intent.FLAG_RECEIVER_REGISTERED_ONLY : 0; |
| sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null, |
| userIds, instantUserIds, broadcastAllowList, null /* filterExtrasForReceiver */, |
| null /* bOptions */); |
| } |
| |
| static void sendDeviceCustomizationReadyBroadcast() { |
| final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY); |
| intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); |
| final IActivityManager am = ActivityManager.getService(); |
| final String[] requiredPermissions = { |
| Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY, |
| }; |
| try { |
| am.broadcastIntentWithFeature(null, null, intent, null, null, 0, null, null, |
| requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE, null, false, |
| false, UserHandle.USER_ALL); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| void sendSessionCommitBroadcast(@NonNull Computer snapshot, |
| @NonNull PackageInstaller.SessionInfo sessionInfo, |
| int userId, |
| @Nullable String appPredictionServicePackage) { |
| UserManagerService ums = UserManagerService.getInstance(); |
| if (ums == null || sessionInfo.isStaged()) { |
| return; |
| } |
| final UserInfo parent = ums.getProfileParent(userId); |
| final int launcherUserId = (parent != null) ? parent.id : userId; |
| final ComponentName launcherComponent = snapshot.getDefaultHomeActivity(launcherUserId); |
| if (launcherComponent != null) { |
| Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED) |
| .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo) |
| .putExtra(Intent.EXTRA_USER, UserHandle.of(userId)) |
| .setPackage(launcherComponent.getPackageName()); |
| mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUserId)); |
| } |
| // TODO(b/122900055) Change/Remove this and replace with new permission role. |
| if (appPredictionServicePackage != null) { |
| Intent predictorIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED) |
| .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo) |
| .putExtra(Intent.EXTRA_USER, UserHandle.of(userId)) |
| .setPackage(appPredictionServicePackage); |
| mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUserId)); |
| } |
| } |
| |
| void sendPreferredActivityChangedBroadcast(int userId) { |
| mHandler.post(() -> { |
| final IActivityManager am = ActivityManager.getService(); |
| if (am == null) { |
| return; |
| } |
| |
| final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED); |
| intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); |
| try { |
| am.broadcastIntentWithFeature(null, null, intent, null, null, |
| 0, null, null, null, null, null, android.app.AppOpsManager.OP_NONE, |
| null, false, false, userId); |
| } catch (RemoteException e) { |
| } |
| }); |
| } |
| |
| void sendPostInstallBroadcasts(@NonNull Computer snapshot, |
| @NonNull InstallRequest request, |
| @NonNull String packageName, |
| @NonNull String requiredPermissionControllerPackage, |
| @NonNull String[] requiredVerifierPackages, |
| @NonNull String requiredInstallerPackage, |
| @NonNull PackageSender packageSender, |
| boolean isLaunchedForRestore, |
| boolean isKillApp, |
| boolean isUpdate, |
| boolean isArchived) { |
| // Send the removed broadcasts |
| if (request.getRemovedInfo() != null) { |
| if (request.getRemovedInfo().mIsExternal) { |
| if (DEBUG_INSTALL) { |
| Slog.i(TAG, "upgrading pkg " + request.getRemovedInfo().mRemovedPackage |
| + " is ASEC-hosted -> UNAVAILABLE"); |
| } |
| final String[] pkgNames = new String[]{ |
| request.getRemovedInfo().mRemovedPackage}; |
| final int[] uids = new int[]{request.getRemovedInfo().mUid}; |
| notifyResourcesChanged( |
| false /* mediaStatus */, true /* replacing */, pkgNames, uids); |
| sendResourcesChangedBroadcast( |
| snapshot, false /* mediaStatus */, true /* replacing */, pkgNames, uids); |
| } |
| sendPackageRemovedBroadcasts( |
| request.getRemovedInfo(), packageSender, isKillApp, false /*removedBySystem*/, |
| false /*isArchived*/); |
| } |
| |
| final int[] firstUserIds = request.getFirstTimeBroadcastUserIds(); |
| final int[] firstInstantUserIds = request.getFirstTimeBroadcastInstantUserIds(); |
| final int[] updateUserIds = request.getUpdateBroadcastUserIds(); |
| final int[] instantUserIds = request.getUpdateBroadcastInstantUserIds(); |
| |
| final String installerPackageName = |
| request.getInstallerPackageName() != null |
| ? request.getInstallerPackageName() |
| : request.getRemovedInfo() != null |
| ? request.getRemovedInfo().mInstallerPackageName |
| : null; |
| |
| Bundle extras = new Bundle(); |
| extras.putInt(Intent.EXTRA_UID, request.getAppId()); |
| if (isUpdate) { |
| extras.putBoolean(Intent.EXTRA_REPLACING, true); |
| } |
| if (isArchived) { |
| extras.putBoolean(Intent.EXTRA_ARCHIVAL, true); |
| } |
| extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, request.getDataLoaderType()); |
| |
| final String staticSharedLibraryName = request.getPkg().getStaticSharedLibraryName(); |
| // If a package is a static shared library, then only the installer of the package |
| // should get the broadcast. |
| if (installerPackageName != null && staticSharedLibraryName != null) { |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName, |
| extras, 0 /*flags*/, |
| installerPackageName, null /*finishedReceiver*/, |
| request.getNewUsers(), null /* instantUserIds*/, |
| null /* broadcastAllowList */, null); |
| } |
| |
| // Send installed broadcasts if the package is not a static shared lib. |
| if (staticSharedLibraryName == null) { |
| // Send PACKAGE_ADDED broadcast for users that see the package for the first time |
| // sendPackageAddedForNewUsers also deals with system apps |
| final int appId = UserHandle.getAppId(request.getAppId()); |
| final boolean isSystem = request.isInstallSystem(); |
| final boolean isVirtualPreload = |
| ((request.getInstallFlags() & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); |
| sendPackageAddedForNewUsers(snapshot, packageName, |
| isSystem || isVirtualPreload, |
| isVirtualPreload /*startReceiver*/, appId, |
| firstUserIds, firstInstantUserIds, isArchived, request.getDataLoaderType()); |
| |
| // Send PACKAGE_ADDED broadcast for users that don't see |
| // the package for the first time |
| |
| // Send to all running apps. |
| final SparseArray<int[]> newBroadcastAllowList = |
| mAppsFilter.getVisibilityAllowList(snapshot, |
| snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID), |
| updateUserIds, snapshot.getPackageStates()); |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName, |
| extras, 0 /*flags*/, |
| null /*targetPackage*/, null /*finishedReceiver*/, |
| updateUserIds, instantUserIds, newBroadcastAllowList, null); |
| // Send to the installer, even if it's not running. |
| if (installerPackageName != null) { |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName, |
| extras, 0 /*flags*/, |
| installerPackageName, null /*finishedReceiver*/, |
| updateUserIds, instantUserIds, null /* broadcastAllowList */, null); |
| } |
| // Send to PermissionController for all update users, even if it may not be running |
| // for some users |
| if (isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) { |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName, |
| extras, 0 /*flags*/, |
| requiredPermissionControllerPackage, null /*finishedReceiver*/, |
| updateUserIds, instantUserIds, null /* broadcastAllowList */, null); |
| } |
| // Notify required verifier(s) that are not the installer of record for the package. |
| for (String verifierPackageName : requiredVerifierPackages) { |
| if (verifierPackageName != null && !verifierPackageName.equals( |
| installerPackageName)) { |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, |
| packageName, |
| extras, 0 /*flags*/, |
| verifierPackageName, null /*finishedReceiver*/, |
| updateUserIds, instantUserIds, null /* broadcastAllowList */, |
| null); |
| } |
| } |
| // If package installer is defined, notify package installer about new |
| // app installed |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName, |
| extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/, |
| requiredInstallerPackage, null /*finishedReceiver*/, |
| firstUserIds, instantUserIds, null /* broadcastAllowList */, null); |
| |
| // Send replaced for users that don't see the package for the first time |
| if (isUpdate) { |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, |
| packageName, extras, 0 /*flags*/, |
| null /*targetPackage*/, null /*finishedReceiver*/, |
| updateUserIds, instantUserIds, |
| request.getRemovedInfo().mBroadcastAllowList, null); |
| if (installerPackageName != null) { |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, packageName, |
| extras, 0 /*flags*/, |
| installerPackageName, null /*finishedReceiver*/, |
| updateUserIds, instantUserIds, null /*broadcastAllowList*/, |
| null); |
| } |
| for (String verifierPackageName : requiredVerifierPackages) { |
| if (verifierPackageName != null && !verifierPackageName.equals( |
| installerPackageName)) { |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, |
| packageName, extras, 0 /*flags*/, verifierPackageName, |
| null /*finishedReceiver*/, updateUserIds, instantUserIds, |
| null /*broadcastAllowList*/, null); |
| } |
| } |
| sendPackageBroadcastAndNotify(Intent.ACTION_MY_PACKAGE_REPLACED, |
| null /*package*/, null /*extras*/, 0 /*flags*/, |
| packageName /*targetPackage*/, |
| null /*finishedReceiver*/, updateUserIds, instantUserIds, |
| null /*broadcastAllowList*/, |
| getTemporaryAppAllowlistBroadcastOptions( |
| REASON_PACKAGE_REPLACED).toBundle()); |
| } else if (isLaunchedForRestore && !request.isInstallSystem()) { |
| // 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, installerPackageName, |
| firstUserIds, firstInstantUserIds); |
| } |
| |
| // Send broadcast package appeared if external for all users |
| if (request.getPkg().isExternalStorage()) { |
| if (!isUpdate) { |
| final StorageManager storage = mContext.getSystemService(StorageManager.class); |
| VolumeInfo volume = |
| storage.findVolumeByUuid( |
| StorageManager.convert( |
| request.getPkg().getVolumeUuid()).toString()); |
| int packageExternalStorageType = |
| PackageManagerServiceUtils.getPackageExternalStorageType(volume, |
| /* isExternalStorage */ true); |
| // If the package was installed externally, log it. |
| if (packageExternalStorageType != StorageEnums.UNKNOWN) { |
| FrameworkStatsLog.write( |
| FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED, |
| packageExternalStorageType, packageName); |
| } |
| } |
| if (DEBUG_INSTALL) { |
| Slog.i(TAG, "upgrading pkg " + packageName + " is external"); |
| } |
| if (!isArchived) { |
| final String[] pkgNames = new String[]{packageName}; |
| final int[] uids = new int[]{request.getPkg().getUid()}; |
| sendResourcesChangedBroadcast(snapshot, |
| true /* mediaStatus */, true /* replacing */, pkgNames, uids); |
| notifyResourcesChanged(true /* mediaStatus */, |
| true /* replacing */, pkgNames, uids); |
| } |
| } |
| } else { // if static shared lib |
| final ArrayList<AndroidPackage> libraryConsumers = request.getLibraryConsumers(); |
| if (!ArrayUtils.isEmpty(libraryConsumers)) { |
| // No need to kill consumers if it's installation of new version static shared lib. |
| final boolean dontKillApp = !isUpdate; |
| for (int i = 0; i < libraryConsumers.size(); i++) { |
| AndroidPackage pkg = libraryConsumers.get(i); |
| // send broadcast that all consumers of the static shared library have changed |
| sendPackageChangedBroadcast(snapshot, pkg.getPackageName(), |
| dontKillApp, |
| new ArrayList<>(Collections.singletonList(pkg.getPackageName())), |
| pkg.getUid(), null); |
| } |
| } |
| } |
| } |
| |
| private void sendPackageAddedForNewUsers(@NonNull Computer snapshot, |
| @NonNull String packageName, |
| boolean sendBootCompleted, |
| boolean includeStopped, |
| @AppIdInt int appId, |
| int[] userIds, |
| int[] instantUserIds, |
| boolean isArchived, |
| int dataLoaderType) { |
| if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) { |
| return; |
| } |
| SparseArray<int[]> broadcastAllowList = mAppsFilter.getVisibilityAllowList(snapshot, |
| snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID), |
| userIds, snapshot.getPackageStates()); |
| mHandler.post( |
| () -> sendPackageAddedForNewUsers(packageName, appId, userIds, |
| instantUserIds, isArchived, dataLoaderType, broadcastAllowList)); |
| mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(packageName, appId, userIds, |
| instantUserIds, isArchived, dataLoaderType, broadcastAllowList, mHandler); |
| if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) { |
| mHandler.post(() -> { |
| for (int userId : userIds) { |
| sendBootCompletedBroadcastToSystemApp( |
| packageName, includeStopped, userId); |
| } |
| } |
| ); |
| } |
| } |
| |
| private void sendPackageAddedForNewUsers(@NonNull String packageName, |
| @AppIdInt int appId, |
| int[] userIds, |
| int[] instantUserIds, |
| boolean isArchived, |
| int dataLoaderType, |
| @NonNull SparseArray<int[]> broadcastAllowlist) { |
| Bundle extras = new Bundle(1); |
| // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast |
| final int uid = UserHandle.getUid( |
| (ArrayUtils.isEmpty(userIds) ? instantUserIds[0] : userIds[0]), appId); |
| extras.putInt(Intent.EXTRA_UID, uid); |
| if (isArchived) { |
| extras.putBoolean(Intent.EXTRA_ARCHIVAL, true); |
| } |
| extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType); |
| |
| sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, |
| packageName, extras, 0, null, null, userIds, instantUserIds, |
| broadcastAllowlist, null /* filterExtrasForReceiver */, null); |
| // Send to PermissionController for all new users, even if it may not be running for some |
| // users |
| if (isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) { |
| sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, |
| packageName, extras, 0, |
| mContext.getPackageManager().getPermissionControllerPackageName(), |
| null, userIds, instantUserIds, |
| broadcastAllowlist, null /* filterExtrasForReceiver */, null); |
| } |
| } |
| |
| void sendPackageAddedForUser(@NonNull Computer snapshot, |
| @NonNull String packageName, |
| @NonNull PackageStateInternal packageState, |
| int userId, |
| boolean isArchived, |
| int dataLoaderType, |
| @Nullable String appPredictionServicePackage) { |
| final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId); |
| final boolean isSystem = packageState.isSystem(); |
| final boolean isInstantApp = userState.isInstantApp(); |
| final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId }; |
| final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; |
| sendPackageAddedForNewUsers(snapshot, packageName, isSystem /*sendBootCompleted*/, |
| false /*startReceiver*/, packageState.getAppId(), userIds, instantUserIds, |
| isArchived, dataLoaderType); |
| |
| // Send a session commit broadcast |
| final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo(); |
| info.installReason = userState.getInstallReason(); |
| info.appPackageName = packageName; |
| sendSessionCommitBroadcast(snapshot, info, userId, appPredictionServicePackage); |
| } |
| |
| void sendFirstLaunchBroadcast(String pkgName, String installerPkg, |
| int[] userIds, int[] instantUserIds) { |
| sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0, |
| installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */, |
| null /* filterExtrasForReceiver */, null); |
| } |
| |
| /** |
| * Filter package names for the intent extras {@link Intent#EXTRA_CHANGED_PACKAGE_LIST} and |
| * {@link Intent#EXTRA_CHANGED_UID_LIST} by using the rules of the package visibility. |
| * |
| * @param callingUid The uid that is going to access the intent extras. |
| * @param extras The intent extras to filter |
| * @return An extras that have been filtered, or {@code null} if the given uid is unable to |
| * access all the packages in the extras. |
| */ |
| @Nullable |
| private static Bundle filterExtrasChangedPackageList(@NonNull Computer snapshot, int callingUid, |
| @NonNull Bundle extras) { |
| if (UserHandle.isCore(callingUid)) { |
| // see all |
| return extras; |
| } |
| final String[] pkgs = extras.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST); |
| if (ArrayUtils.isEmpty(pkgs)) { |
| return extras; |
| } |
| final int userId = extras.getInt( |
| Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(callingUid)); |
| final int[] uids = extras.getIntArray(Intent.EXTRA_CHANGED_UID_LIST); |
| final Pair<String[], int[]> filteredPkgs = |
| filterPackages(snapshot, pkgs, uids, callingUid, userId); |
| if (ArrayUtils.isEmpty(filteredPkgs.first)) { |
| // caller is unable to access this intent |
| return null; |
| } |
| final Bundle filteredExtras = new Bundle(extras); |
| filteredExtras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, filteredPkgs.first); |
| filteredExtras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, filteredPkgs.second); |
| return filteredExtras; |
| } |
| |
| /** Returns whether the Safety Label Change notification, a privacy feature, is enabled. */ |
| private static boolean isPrivacySafetyLabelChangeNotificationsEnabled(Context context) { |
| PackageManager packageManager = context.getPackageManager(); |
| return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, |
| SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, true) |
| && !packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) |
| && !packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK) |
| && !packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH); |
| } |
| |
| @NonNull |
| private static Pair<String[], int[]> filterPackages(@NonNull Computer snapshot, |
| @NonNull String[] pkgs, @Nullable int[] uids, int callingUid, int userId) { |
| final int pkgSize = pkgs.length; |
| final int uidSize = !ArrayUtils.isEmpty(uids) ? uids.length : 0; |
| |
| final ArrayList<String> pkgList = new ArrayList<>(pkgSize); |
| final IntArray uidList = uidSize > 0 ? new IntArray(uidSize) : null; |
| for (int i = 0; i < pkgSize; i++) { |
| final String packageName = pkgs[i]; |
| if (snapshot.shouldFilterApplication( |
| snapshot.getPackageStateInternal(packageName), callingUid, userId)) { |
| continue; |
| } |
| pkgList.add(packageName); |
| if (uidList != null && i < uidSize) { |
| uidList.add(uids[i]); |
| } |
| } |
| return new Pair<>( |
| pkgList.size() > 0 ? pkgList.toArray(new String[pkgList.size()]) : null, |
| uidList != null && uidList.size() > 0 ? uidList.toArray() : null); |
| } |
| |
| void sendApplicationHiddenForUser(@NonNull String packageName, |
| @NonNull PackageStateInternal packageState, |
| int userId, |
| @NonNull PackageSender packageSender) { |
| final PackageRemovedInfo info = new PackageRemovedInfo(); |
| info.mRemovedPackage = packageName; |
| info.mInstallerPackageName = packageState.getInstallSource().mInstallerPackageName; |
| info.mRemovedUsers = new int[] {userId}; |
| info.mBroadcastUsers = new int[] {userId}; |
| info.mUid = UserHandle.getUid(userId, packageState.getAppId()); |
| info.mRemovedPackageVersionCode = packageState.getVersionCode(); |
| sendPackageRemovedBroadcasts(info, packageSender, true /*killApp*/, |
| false /*removedBySystem*/, false /*isArchived*/); |
| } |
| |
| void sendPackageChangedBroadcast(@NonNull Computer snapshot, |
| @NonNull String packageName, |
| boolean dontKillApp, |
| @NonNull ArrayList<String> componentNames, |
| int packageUid, |
| @NonNull String reason) { |
| PackageStateInternal setting = snapshot.getPackageStateInternal(packageName, |
| Process.SYSTEM_UID); |
| if (setting == null) { |
| return; |
| } |
| final int userId = UserHandle.getUserId(packageUid); |
| final boolean isInstantApp = |
| snapshot.isInstantAppInternal(packageName, userId, Process.SYSTEM_UID); |
| final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId }; |
| final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; |
| final SparseArray<int[]> broadcastAllowList = |
| isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds); |
| mHandler.post(() -> sendPackageChangedBroadcast( |
| packageName, dontKillApp, componentNames, packageUid, reason, userIds, |
| instantUserIds, broadcastAllowList)); |
| mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames, |
| packageUid, reason, userIds, instantUserIds, broadcastAllowList, mHandler); |
| } |
| |
| private void sendPackageBroadcastAndNotify(@NonNull String action, |
| @NonNull String pkg, |
| @NonNull Bundle extras, |
| int flags, |
| @Nullable String targetPkg, |
| @Nullable IIntentReceiver finishedReceiver, |
| @NonNull int[] userIds, |
| @NonNull int[] instantUserIds, |
| @Nullable SparseArray<int[]> broadcastAllowList, |
| @Nullable Bundle bOptions) { |
| mHandler.post(() -> sendPackageBroadcast(action, pkg, extras, flags, |
| targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList, |
| null /* filterExtrasForReceiver */, bOptions)); |
| if (targetPkg == null) { |
| // For some broadcast action, e.g. ACTION_PACKAGE_ADDED, this method will be called |
| // many times to different targets, e.g. installer app, permission controller, other |
| // registered apps. We should filter it to avoid calling back many times for the same |
| // action. When the targetPkg is set, it sends the broadcast to specific app, e.g. |
| // installer app or null for registered apps. The callback only need to send back to the |
| // registered apps so we check the null condition here. |
| notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds, broadcastAllowList); |
| } |
| } |
| |
| void sendSystemPackageUpdatedBroadcasts(@NonNull PackageRemovedInfo packageRemovedInfo) { |
| if (!packageRemovedInfo.mIsRemovedPackageSystemUpdate) { |
| return; |
| } |
| |
| final String removedPackage = packageRemovedInfo.mRemovedPackage; |
| final String installerPackageName = packageRemovedInfo.mInstallerPackageName; |
| final SparseArray<int[]> broadcastAllowList = packageRemovedInfo.mBroadcastAllowList; |
| |
| Bundle extras = new Bundle(2); |
| extras.putInt(Intent.EXTRA_UID, packageRemovedInfo.mUid); |
| extras.putBoolean(Intent.EXTRA_REPLACING, true); |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras, |
| 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null); |
| |
| if (installerPackageName != null) { |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, |
| removedPackage, extras, 0 /*flags*/, |
| installerPackageName, null, null, null, null /* broadcastAllowList */, |
| null); |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, |
| removedPackage, extras, 0 /*flags*/, |
| installerPackageName, null, null, null, null /* broadcastAllowList */, |
| null); |
| } |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, removedPackage, |
| extras, 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null); |
| sendPackageBroadcastAndNotify(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0, |
| removedPackage, null, null, null, null /* broadcastAllowList */, |
| getTemporaryBroadcastOptionsForSystemPackageUpdate(REASON_PACKAGE_REPLACED) |
| .toBundle()); |
| } |
| |
| @SuppressLint("AndroidFrameworkRequiresPermission") |
| private @NonNull BroadcastOptions getTemporaryBroadcastOptionsForSystemPackageUpdate( |
| @PowerExemptionManager.ReasonCode int reasonCode) { |
| long duration = 10_000; |
| if (mAmInternal != null) { |
| duration = mAmInternal.getBootTimeTempAllowListDuration(); |
| } |
| final BroadcastOptions bOptions = BroadcastOptions.makeBasic(); |
| bOptions.setTemporaryAppAllowlist(duration, |
| TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, |
| reasonCode, ""); |
| return bOptions; |
| } |
| |
| |
| void sendPackageRemovedBroadcasts( |
| @NonNull PackageRemovedInfo packageRemovedInfo, |
| @NonNull PackageSender packageSender, |
| boolean killApp, |
| boolean removedBySystem, |
| boolean isArchived) { |
| final String removedPackage = packageRemovedInfo.mRemovedPackage; |
| final String installerPackageName = packageRemovedInfo.mInstallerPackageName; |
| final int[] broadcastUserIds = packageRemovedInfo.mBroadcastUsers; |
| final int[] instantUserIds = packageRemovedInfo.mInstantUserIds; |
| final SparseArray<int[]> broadcastAllowList = packageRemovedInfo.mBroadcastAllowList; |
| final boolean dataRemoved = packageRemovedInfo.mDataRemoved; |
| final boolean isUpdate = packageRemovedInfo.mIsUpdate; |
| final boolean isRemovedPackageSystemUpdate = |
| packageRemovedInfo.mIsRemovedPackageSystemUpdate; |
| final boolean isRemovedForAllUsers = packageRemovedInfo.mRemovedForAllUsers; |
| final boolean isStaticSharedLib = packageRemovedInfo.mIsStaticSharedLib; |
| |
| Bundle extras = new Bundle(); |
| extras.putInt(Intent.EXTRA_UID, packageRemovedInfo.mUid); |
| extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved); |
| extras.putBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, isRemovedPackageSystemUpdate); |
| extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp); |
| extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem); |
| final boolean isReplace = isUpdate || isRemovedPackageSystemUpdate; |
| if (isReplace || isArchived) { |
| extras.putBoolean(Intent.EXTRA_REPLACING, true); |
| } |
| if (isArchived) { |
| extras.putBoolean(Intent.EXTRA_ARCHIVAL, true); |
| } |
| extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, isRemovedForAllUsers); |
| |
| // Send PACKAGE_REMOVED broadcast to the respective installer. |
| if (removedPackage != null && installerPackageName != null) { |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED, |
| removedPackage, extras, 0 /*flags*/, |
| installerPackageName, null, broadcastUserIds, instantUserIds, null, null); |
| } |
| if (isStaticSharedLib) { |
| // When uninstalling static shared libraries, only the package's installer needs to be |
| // sent a PACKAGE_REMOVED broadcast. There are no other intended recipients. |
| return; |
| } |
| if (removedPackage != null) { |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED, |
| removedPackage, extras, 0, null /*targetPackage*/, null, |
| broadcastUserIds, instantUserIds, broadcastAllowList, null); |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED_INTERNAL, |
| removedPackage, extras, 0 /*flags*/, PLATFORM_PACKAGE_NAME, |
| null /*finishedReceiver*/, broadcastUserIds, instantUserIds, |
| broadcastAllowList, null /*bOptions*/); |
| if (dataRemoved && !isRemovedPackageSystemUpdate) { |
| sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_FULLY_REMOVED, |
| removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null, |
| null, broadcastUserIds, instantUserIds, broadcastAllowList, null); |
| packageSender.notifyPackageRemoved(removedPackage, packageRemovedInfo.mUid); |
| } |
| } |
| if (packageRemovedInfo.mIsAppIdRemoved) { |
| // If a system app's updates are uninstalled the UID is not actually removed. Some |
| // services need to know the package name affected. |
| if (isReplace) { |
| extras.putString(Intent.EXTRA_PACKAGE_NAME, removedPackage); |
| } |
| |
| sendPackageBroadcastAndNotify(Intent.ACTION_UID_REMOVED, |
| null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, |
| null, null, broadcastUserIds, instantUserIds, broadcastAllowList, null); |
| } |
| } |
| |
| /** |
| * Send broadcast intents for packages suspension changes. |
| * |
| * @param intent The action name of the suspension intent. |
| * @param pkgList The names of packages which have suspension changes. |
| * @param uidList The uids of packages which have suspension changes. |
| * @param userId The user where packages reside. |
| */ |
| void sendPackagesSuspendedOrUnsuspendedForUser(@NonNull Computer snapshot, |
| @NonNull String intent, |
| @NonNull String[] pkgList, |
| @NonNull int[] uidList, |
| boolean quarantined, |
| int userId) { |
| final Bundle extras = new Bundle(3); |
| extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); |
| extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); |
| if (quarantined) { |
| extras.putBoolean(Intent.EXTRA_QUARANTINED, true); |
| } |
| final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND; |
| final Bundle options = new BroadcastOptions() |
| .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) |
| .toBundle(); |
| mHandler.post(() -> sendPackageBroadcast(intent, null /* pkg */, |
| extras, flags, null /* targetPkg */, null /* finishedReceiver */, |
| new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */, |
| (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList( |
| snapshot, callingUid, intentExtras), |
| options)); |
| notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId}, |
| null /* instantUserIds */, null /* broadcastAllowList */); |
| } |
| |
| void sendMyPackageSuspendedOrUnsuspended(@NonNull Computer snapshot, |
| @NonNull String[] affectedPackages, |
| boolean suspended, |
| int userId) { |
| final String action = suspended |
| ? Intent.ACTION_MY_PACKAGE_SUSPENDED |
| : Intent.ACTION_MY_PACKAGE_UNSUSPENDED; |
| mHandler.post(() -> { |
| final IActivityManager am = ActivityManager.getService(); |
| if (am == null) { |
| Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ " |
| + (suspended ? "" : "UN") + "SUSPENDED broadcasts"); |
| return; |
| } |
| final int[] targetUserIds = new int[] {userId}; |
| for (String packageName : affectedPackages) { |
| final Bundle appExtras = suspended |
| ? SuspendPackageHelper.getSuspendedPackageAppExtras( |
| snapshot, packageName, userId, SYSTEM_UID) |
| : null; |
| final Bundle intentExtras; |
| if (appExtras != null) { |
| intentExtras = new Bundle(1); |
| intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras); |
| } else { |
| intentExtras = null; |
| } |
| doSendBroadcast(action, null, intentExtras, |
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null, |
| targetUserIds, false, null, null, null); |
| } |
| }); |
| } |
| |
| /** |
| * Send broadcast intents for packages distracting changes. |
| * |
| * @param pkgList The names of packages which have suspension changes. |
| * @param uidList The uids of packages which have suspension changes. |
| * @param userId The user where packages reside. |
| */ |
| void sendDistractingPackagesChanged(@NonNull Computer snapshot, |
| @NonNull String[] pkgList, |
| @NonNull int[] uidList, |
| int userId, |
| int distractionFlags) { |
| final Bundle extras = new Bundle(); |
| extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); |
| extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); |
| extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags); |
| |
| mHandler.post(() -> sendPackageBroadcast( |
| Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */, |
| extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */, |
| null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */, |
| null /* broadcastAllowList */, |
| (callingUid, intentExtras) -> filterExtrasChangedPackageList( |
| snapshot, callingUid, intentExtras), |
| null /* bOptions */)); |
| } |
| |
| void sendResourcesChangedBroadcastAndNotify(@NonNull Computer snapshot, |
| boolean mediaStatus, |
| boolean replacing, |
| @NonNull ArrayList<AndroidPackage> packages) { |
| final int size = packages.size(); |
| final String[] packageNames = new String[size]; |
| final int[] packageUids = new int[size]; |
| for (int i = 0; i < size; i++) { |
| final AndroidPackage pkg = packages.get(i); |
| packageNames[i] = pkg.getPackageName(); |
| packageUids[i] = pkg.getUid(); |
| } |
| sendResourcesChangedBroadcast(snapshot, mediaStatus, |
| replacing, packageNames, packageUids); |
| notifyResourcesChanged(mediaStatus, replacing, packageNames, packageUids); |
| } |
| |
| private void notifyPackageMonitor(@NonNull String action, |
| @NonNull String pkg, |
| @Nullable Bundle extras, |
| @NonNull int[] userIds, |
| @NonNull int[] instantUserIds, |
| @Nullable SparseArray<int[]> broadcastAllowList) { |
| mPackageMonitorCallbackHelper.notifyPackageMonitor(action, pkg, extras, userIds, |
| instantUserIds, broadcastAllowList, mHandler); |
| } |
| |
| private void notifyResourcesChanged(boolean mediaStatus, |
| boolean replacing, |
| @NonNull String[] pkgNames, |
| @NonNull int[] uids) { |
| mPackageMonitorCallbackHelper.notifyResourcesChanged(mediaStatus, replacing, pkgNames, |
| uids, mHandler); |
| } |
| } |