1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.pm;
18 
19 import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
20 import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
21 import static android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED;
22 
23 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
24 import static com.android.server.pm.PackageManagerService.PACKAGE_SCHEME;
25 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
26 import static com.android.server.pm.PackageManagerService.TAG;
27 
28 import android.Manifest;
29 import android.annotation.AppIdInt;
30 import android.annotation.NonNull;
31 import android.annotation.Nullable;
32 import android.app.ActivityManager;
33 import android.app.ActivityManagerInternal;
34 import android.app.BroadcastOptions;
35 import android.app.IActivityManager;
36 import android.content.ComponentName;
37 import android.content.Context;
38 import android.content.IIntentReceiver;
39 import android.content.Intent;
40 import android.content.pm.PackageInstaller;
41 import android.content.pm.PackageManager;
42 import android.net.Uri;
43 import android.os.Bundle;
44 import android.os.PowerExemptionManager;
45 import android.os.RemoteException;
46 import android.os.UserHandle;
47 import android.provider.DeviceConfig;
48 import android.util.IntArray;
49 import android.util.Log;
50 import android.util.Pair;
51 import android.util.Slog;
52 import android.util.SparseArray;
53 
54 import com.android.internal.util.ArrayUtils;
55 
56 import java.util.ArrayList;
57 import java.util.function.BiFunction;
58 import java.util.function.Supplier;
59 
60 /**
61  * Helper class to send broadcasts for various situations.
62  */
63 public final class BroadcastHelper {
64     private static final boolean DEBUG_BROADCASTS = false;
65     /**
66      * Permissions required in order to receive instant application lifecycle broadcasts.
67      */
68     private static final String[] INSTANT_APP_BROADCAST_PERMISSION =
69             new String[]{android.Manifest.permission.ACCESS_INSTANT_APPS};
70 
71     private final UserManagerInternal mUmInternal;
72     private final ActivityManagerInternal mAmInternal;
73     private final Context mContext;
74 
BroadcastHelper(PackageManagerServiceInjector injector)75     BroadcastHelper(PackageManagerServiceInjector injector) {
76         mUmInternal = injector.getUserManagerInternal();
77         mAmInternal = injector.getActivityManagerInternal();
78         mContext = injector.getContext();
79     }
80 
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)81     public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
82             final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
83             final int[] userIds, int[] instantUserIds,
84             @Nullable SparseArray<int[]> broadcastAllowList,
85             @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
86             @Nullable Bundle bOptions) {
87         try {
88             final IActivityManager am = ActivityManager.getService();
89             if (am == null) return;
90             final int[] resolvedUserIds;
91             if (userIds == null) {
92                 resolvedUserIds = am.getRunningUserIds();
93             } else {
94                 resolvedUserIds = userIds;
95             }
96 
97             if (ArrayUtils.isEmpty(instantUserIds)) {
98                 doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver,
99                         resolvedUserIds, false /* isInstantApp */, broadcastAllowList,
100                         filterExtrasForReceiver, bOptions);
101             } else {
102                 // send restricted broadcasts for instant apps
103                 doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver,
104                         instantUserIds, true /* isInstantApp */, null,
105                         null /* filterExtrasForReceiver */, bOptions);
106             }
107         } catch (RemoteException ex) {
108         }
109     }
110 
111     /**
112      * Sends a broadcast for the given action.
113      * <p>If {@code isInstantApp} is {@code true}, then the broadcast is protected with
114      * the {@link android.Manifest.permission#ACCESS_INSTANT_APPS} permission. This allows
115      * the system and applications allowed to see instant applications to receive package
116      * lifecycle events for instant applications.
117      */
doSendBroadcast(String action, String pkg, Bundle extras, int flags, String targetPkg, IIntentReceiver finishedReceiver, int[] userIds, boolean isInstantApp, @Nullable SparseArray<int[]> broadcastAllowList, @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, @Nullable Bundle bOptions)118     public void doSendBroadcast(String action, String pkg, Bundle extras,
119             int flags, String targetPkg, IIntentReceiver finishedReceiver,
120             int[] userIds, boolean isInstantApp, @Nullable SparseArray<int[]> broadcastAllowList,
121             @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
122             @Nullable Bundle bOptions) {
123         for (int userId : userIds) {
124             final Intent intent = new Intent(action,
125                     pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
126             final String[] requiredPermissions =
127                     isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null;
128             if (extras != null) {
129                 intent.putExtras(extras);
130             }
131             if (targetPkg != null) {
132                 intent.setPackage(targetPkg);
133             }
134             // Modify the UID when posting to other users
135             int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
136             if (uid >= 0 && UserHandle.getUserId(uid) != userId) {
137                 uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
138                 intent.putExtra(Intent.EXTRA_UID, uid);
139             }
140             if (broadcastAllowList != null && PLATFORM_PACKAGE_NAME.equals(targetPkg)) {
141                 intent.putExtra(Intent.EXTRA_VISIBILITY_ALLOW_LIST,
142                          broadcastAllowList.get(userId));
143             }
144             intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
145             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
146             if (DEBUG_BROADCASTS) {
147                 RuntimeException here = new RuntimeException("here");
148                 here.fillInStackTrace();
149                 Slog.d(TAG, "Sending to user " + userId + ": "
150                         + intent.toShortString(false, true, false, false)
151                         + " " + intent.getExtras(), here);
152             }
153             final boolean ordered;
154             if (mAmInternal.isModernQueueEnabled()) {
155                 // When the modern broadcast stack is enabled, deliver all our
156                 // broadcasts as unordered, since the modern stack has better
157                 // support for sequencing cold-starts, and it supports
158                 // delivering resultTo for non-ordered broadcasts
159                 ordered = false;
160             } else {
161                 ordered = (finishedReceiver != null);
162             }
163             mAmInternal.broadcastIntent(
164                     intent, finishedReceiver, requiredPermissions, ordered, userId,
165                     broadcastAllowList == null ? null : broadcastAllowList.get(userId),
166                     filterExtrasForReceiver, bOptions);
167         }
168     }
169 
sendResourcesChangedBroadcast(@onNull Supplier<Computer> snapshotComputer, boolean mediaStatus, boolean replacing, @NonNull String[] pkgNames, @NonNull int[] uids)170     public void sendResourcesChangedBroadcast(@NonNull Supplier<Computer> snapshotComputer,
171             boolean mediaStatus, boolean replacing, @NonNull String[] pkgNames,
172             @NonNull int[] uids) {
173         if (ArrayUtils.isEmpty(pkgNames) || ArrayUtils.isEmpty(uids)) {
174             return;
175         }
176         Bundle extras = new Bundle();
177         extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgNames);
178         extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uids);
179         if (replacing) {
180             extras.putBoolean(Intent.EXTRA_REPLACING, replacing);
181         }
182         String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
183                 : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
184         sendPackageBroadcast(action, null /* pkg */, extras, 0 /* flags */,
185                 null /* targetPkg */, null /* finishedReceiver */, null /* userIds */,
186                 null /* instantUserIds */, null /* broadcastAllowList */,
187                 (callingUid, intentExtras) -> filterExtrasChangedPackageList(
188                         snapshotComputer.get(), callingUid, intentExtras),
189                 null /* bOptions */);
190     }
191 
192     /**
193      * The just-installed/enabled app is bundled on the system, so presumed to be able to run
194      * automatically without needing an explicit launch.
195      * Send it a LOCKED_BOOT_COMPLETED/BOOT_COMPLETED if it would ordinarily have gotten ones.
196      */
sendBootCompletedBroadcastToSystemApp( String packageName, boolean includeStopped, int userId)197     public void sendBootCompletedBroadcastToSystemApp(
198             String packageName, boolean includeStopped, int userId) {
199         // If user is not running, the app didn't miss any broadcast
200         if (!mUmInternal.isUserRunning(userId)) {
201             return;
202         }
203         final IActivityManager am = ActivityManager.getService();
204         try {
205             // Deliver LOCKED_BOOT_COMPLETED first
206             Intent lockedBcIntent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)
207                     .setPackage(packageName);
208             if (includeStopped) {
209                 lockedBcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
210             }
211             final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED};
212             final BroadcastOptions bOptions = getTemporaryAppAllowlistBroadcastOptions(
213                     REASON_LOCKED_BOOT_COMPLETED);
214             am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null,
215                     requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE,
216                     bOptions.toBundle(), false, false, userId);
217 
218             // Deliver BOOT_COMPLETED only if user is unlocked
219             if (mUmInternal.isUserUnlockingOrUnlocked(userId)) {
220                 Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED).setPackage(packageName);
221                 if (includeStopped) {
222                     bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
223                 }
224                 am.broadcastIntentWithFeature(null, null, bcIntent, null, null, 0, null, null,
225                         requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE,
226                         bOptions.toBundle(), false, false, userId);
227             }
228         } catch (RemoteException e) {
229             throw e.rethrowFromSystemServer();
230         }
231     }
232 
getTemporaryAppAllowlistBroadcastOptions( @owerExemptionManager.ReasonCode int reasonCode)233     public @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
234             @PowerExemptionManager.ReasonCode int reasonCode) {
235         long duration = 10_000;
236         if (mAmInternal != null) {
237             duration = mAmInternal.getBootTimeTempAllowListDuration();
238         }
239         final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
240         bOptions.setTemporaryAppAllowlist(duration,
241                 TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
242                 reasonCode, "");
243         return bOptions;
244     }
245 
sendPackageChangedBroadcast(String packageName, boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason, int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList)246     public void sendPackageChangedBroadcast(String packageName, boolean dontKillApp,
247             ArrayList<String> componentNames, int packageUid, String reason,
248             int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
249         if (DEBUG_INSTALL) {
250             Log.v(TAG, "Sending package changed: package=" + packageName + " components="
251                     + componentNames);
252         }
253         Bundle extras = new Bundle(4);
254         extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));
255         String[] nameList = new String[componentNames.size()];
256         componentNames.toArray(nameList);
257         extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
258         extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, dontKillApp);
259         extras.putInt(Intent.EXTRA_UID, packageUid);
260         if (reason != null) {
261             extras.putString(Intent.EXTRA_REASON, reason);
262         }
263         // If this is not reporting a change of the overall package, then only send it
264         // to registered receivers.  We don't want to launch a swath of apps for every
265         // little component state change.
266         final int flags = !componentNames.contains(packageName)
267                 ? Intent.FLAG_RECEIVER_REGISTERED_ONLY : 0;
268         sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null,
269                 userIds, instantUserIds, broadcastAllowList, null /* filterExtrasForReceiver */,
270                 null /* bOptions */);
271     }
272 
sendDeviceCustomizationReadyBroadcast()273     public static void sendDeviceCustomizationReadyBroadcast() {
274         final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY);
275         intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
276         final IActivityManager am = ActivityManager.getService();
277         final String[] requiredPermissions = {
278                 Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY,
279         };
280         try {
281             am.broadcastIntentWithFeature(null, null, intent, null, null, 0, null, null,
282                     requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE, null, false,
283                     false, UserHandle.USER_ALL);
284         } catch (RemoteException e) {
285             throw e.rethrowFromSystemServer();
286         }
287     }
288 
sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId, int launcherUid, @Nullable ComponentName launcherComponent, @Nullable String appPredictionServicePackage)289     public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId,
290             int launcherUid, @Nullable ComponentName launcherComponent,
291             @Nullable String appPredictionServicePackage) {
292         if (launcherComponent != null) {
293             Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
294                     .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
295                     .putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
296                     .setPackage(launcherComponent.getPackageName());
297             mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid));
298         }
299         // TODO(b/122900055) Change/Remove this and replace with new permission role.
300         if (appPredictionServicePackage != null) {
301             Intent predictorIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
302                     .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
303                     .putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
304                     .setPackage(appPredictionServicePackage);
305             mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUid));
306         }
307     }
308 
sendPreferredActivityChangedBroadcast(int userId)309     public void sendPreferredActivityChangedBroadcast(int userId) {
310         final IActivityManager am = ActivityManager.getService();
311         if (am == null) {
312             return;
313         }
314 
315         final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
316         intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
317         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
318         try {
319             am.broadcastIntentWithFeature(null, null, intent, null, null,
320                     0, null, null, null, null, null, android.app.AppOpsManager.OP_NONE,
321                     null, false, false, userId);
322         } catch (RemoteException e) {
323         }
324     }
325 
sendPackageAddedForNewUsers(String packageName, @AppIdInt int appId, int[] userIds, int[] instantUserIds, int dataLoaderType, SparseArray<int[]> broadcastAllowlist)326     public void sendPackageAddedForNewUsers(String packageName,
327             @AppIdInt int appId, int[] userIds, int[] instantUserIds,
328             int dataLoaderType, SparseArray<int[]> broadcastAllowlist) {
329         Bundle extras = new Bundle(1);
330         // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
331         final int uid = UserHandle.getUid(
332                 (ArrayUtils.isEmpty(userIds) ? instantUserIds[0] : userIds[0]), appId);
333         extras.putInt(Intent.EXTRA_UID, uid);
334         extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
335 
336         sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
337                 packageName, extras, 0, null, null, userIds, instantUserIds,
338                 broadcastAllowlist, null /* filterExtrasForReceiver */, null);
339         // Send to PermissionController for all new users, even if it may not be running for some
340         // users
341         if (isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) {
342             sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
343                     packageName, extras, 0,
344                     mContext.getPackageManager().getPermissionControllerPackageName(),
345                     null, userIds, instantUserIds,
346                     broadcastAllowlist, null /* filterExtrasForReceiver */, null);
347         }
348     }
349 
sendFirstLaunchBroadcast(String pkgName, String installerPkg, int[] userIds, int[] instantUserIds)350     public void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
351             int[] userIds, int[] instantUserIds) {
352         sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
353                 installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */,
354                 null /* filterExtrasForReceiver */, null);
355     }
356 
357     /**
358      * Filter package names for the intent extras {@link Intent#EXTRA_CHANGED_PACKAGE_LIST} and
359      * {@link Intent#EXTRA_CHANGED_UID_LIST} by using the rules of the package visibility.
360      *
361      * @param callingUid The uid that is going to access the intent extras.
362      * @param extras The intent extras to filter
363      * @return An extras that have been filtered, or {@code null} if the given uid is unable to
364      * access all the packages in the extras.
365      */
366     @Nullable
filterExtrasChangedPackageList(@onNull Computer snapshot, int callingUid, @NonNull Bundle extras)367     public static Bundle filterExtrasChangedPackageList(@NonNull Computer snapshot, int callingUid,
368             @NonNull Bundle extras) {
369         if (UserHandle.isCore(callingUid)) {
370             // see all
371             return extras;
372         }
373         final String[] pkgs = extras.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST);
374         if (ArrayUtils.isEmpty(pkgs)) {
375             return extras;
376         }
377         final int userId = extras.getInt(
378                 Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(callingUid));
379         final int[] uids = extras.getIntArray(Intent.EXTRA_CHANGED_UID_LIST);
380         final Pair<String[], int[]> filteredPkgs =
381                 filterPackages(snapshot, pkgs, uids, callingUid, userId);
382         if (ArrayUtils.isEmpty(filteredPkgs.first)) {
383             // caller is unable to access this intent
384             return null;
385         }
386         final Bundle filteredExtras = new Bundle(extras);
387         filteredExtras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, filteredPkgs.first);
388         filteredExtras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, filteredPkgs.second);
389         return filteredExtras;
390     }
391 
392     /** Returns whether the Safety Label Change notification, a privacy feature, is enabled. */
isPrivacySafetyLabelChangeNotificationsEnabled(Context context)393     public static boolean isPrivacySafetyLabelChangeNotificationsEnabled(Context context) {
394         PackageManager packageManager = context.getPackageManager();
395         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
396                 SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, true)
397             && !packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
398             && !packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
399             && !packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH);
400     }
401 
402     @NonNull
filterPackages(@onNull Computer snapshot, @NonNull String[] pkgs, @Nullable int[] uids, int callingUid, int userId)403     private static Pair<String[], int[]> filterPackages(@NonNull Computer snapshot,
404             @NonNull String[] pkgs, @Nullable int[] uids, int callingUid, int userId) {
405         final int pkgSize = pkgs.length;
406         final int uidSize = !ArrayUtils.isEmpty(uids) ? uids.length : 0;
407 
408         final ArrayList<String> pkgList = new ArrayList<>(pkgSize);
409         final IntArray uidList = uidSize > 0 ? new IntArray(uidSize) : null;
410         for (int i = 0; i < pkgSize; i++) {
411             final String packageName = pkgs[i];
412             if (snapshot.shouldFilterApplication(
413                     snapshot.getPackageStateInternal(packageName), callingUid, userId)) {
414                 continue;
415             }
416             pkgList.add(packageName);
417             if (uidList != null && i < uidSize) {
418                 uidList.add(uids[i]);
419             }
420         }
421         return new Pair<>(
422                 pkgList.size() > 0 ? pkgList.toArray(new String[pkgList.size()]) : null,
423                 uidList != null && uidList.size() > 0 ? uidList.toArray() : null);
424     }
425 }
426