• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
22 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
23 import static com.android.server.pm.PackageManagerService.PACKAGE_SCHEME;
24 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
25 import static com.android.server.pm.PackageManagerService.TAG;
26 
27 import android.Manifest;
28 import android.annotation.AppIdInt;
29 import android.annotation.NonNull;
30 import android.annotation.Nullable;
31 import android.app.ActivityManager;
32 import android.app.ActivityManagerInternal;
33 import android.app.BroadcastOptions;
34 import android.app.IActivityManager;
35 import android.content.ComponentName;
36 import android.content.Context;
37 import android.content.IIntentReceiver;
38 import android.content.Intent;
39 import android.content.pm.PackageInstaller;
40 import android.net.Uri;
41 import android.os.Bundle;
42 import android.os.PowerExemptionManager;
43 import android.os.RemoteException;
44 import android.os.UserHandle;
45 import android.util.Log;
46 import android.util.Slog;
47 import android.util.SparseArray;
48 
49 import com.android.internal.util.ArrayUtils;
50 
51 import java.util.ArrayList;
52 
53 /**
54  * Helper class to send broadcasts for various situations.
55  */
56 public final class BroadcastHelper {
57     private static final boolean DEBUG_BROADCASTS = false;
58     /**
59      * Permissions required in order to receive instant application lifecycle broadcasts.
60      */
61     private static final String[] INSTANT_APP_BROADCAST_PERMISSION =
62             new String[]{android.Manifest.permission.ACCESS_INSTANT_APPS};
63 
64     private final UserManagerInternal mUmInternal;
65     private final ActivityManagerInternal mAmInternal;
66     private final Context mContext;
67 
BroadcastHelper(PackageManagerServiceInjector injector)68     BroadcastHelper(PackageManagerServiceInjector injector) {
69         mUmInternal = injector.getUserManagerInternal();
70         mAmInternal = injector.getActivityManagerInternal();
71         mContext = injector.getContext();
72     }
73 
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 Bundle bOptions)74     public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
75             final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
76             final int[] userIds, int[] instantUserIds,
77             @Nullable SparseArray<int[]> broadcastAllowList,
78             @Nullable Bundle bOptions) {
79         try {
80             final IActivityManager am = ActivityManager.getService();
81             if (am == null) return;
82             final int[] resolvedUserIds;
83             if (userIds == null) {
84                 resolvedUserIds = am.getRunningUserIds();
85             } else {
86                 resolvedUserIds = userIds;
87             }
88 
89             if (ArrayUtils.isEmpty(instantUserIds)) {
90                 doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver,
91                         resolvedUserIds, false /* isInstantApp */, broadcastAllowList, bOptions);
92             } else {
93                 // send restricted broadcasts for instant apps
94                 doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver,
95                         instantUserIds, true /* isInstantApp */, null, bOptions);
96             }
97         } catch (RemoteException ex) {
98         }
99     }
100 
101     /**
102      * Sends a broadcast for the given action.
103      * <p>If {@code isInstantApp} is {@code true}, then the broadcast is protected with
104      * the {@link android.Manifest.permission#ACCESS_INSTANT_APPS} permission. This allows
105      * the system and applications allowed to see instant applications to receive package
106      * lifecycle events for instant applications.
107      */
doSendBroadcast(String action, String pkg, Bundle extras, int flags, String targetPkg, IIntentReceiver finishedReceiver, int[] userIds, boolean isInstantApp, @Nullable SparseArray<int[]> broadcastAllowList, @Nullable Bundle bOptions)108     public void doSendBroadcast(String action, String pkg, Bundle extras,
109             int flags, String targetPkg, IIntentReceiver finishedReceiver,
110             int[] userIds, boolean isInstantApp, @Nullable SparseArray<int[]> broadcastAllowList,
111             @Nullable Bundle bOptions) {
112         for (int userId : userIds) {
113             final Intent intent = new Intent(action,
114                     pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
115             final String[] requiredPermissions =
116                     isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null;
117             if (extras != null) {
118                 intent.putExtras(extras);
119             }
120             if (targetPkg != null) {
121                 intent.setPackage(targetPkg);
122             }
123             // Modify the UID when posting to other users
124             int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
125             if (uid >= 0 && UserHandle.getUserId(uid) != userId) {
126                 uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
127                 intent.putExtra(Intent.EXTRA_UID, uid);
128             }
129             if (broadcastAllowList != null && PLATFORM_PACKAGE_NAME.equals(targetPkg)) {
130                 intent.putExtra(Intent.EXTRA_VISIBILITY_ALLOW_LIST,
131                          broadcastAllowList.get(userId));
132             }
133             intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
134             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
135             if (DEBUG_BROADCASTS) {
136                 RuntimeException here = new RuntimeException("here");
137                 here.fillInStackTrace();
138                 Slog.d(TAG, "Sending to user " + userId + ": "
139                         + intent.toShortString(false, true, false, false)
140                         + " " + intent.getExtras(), here);
141             }
142             mAmInternal.broadcastIntent(
143                     intent, finishedReceiver, requiredPermissions,
144                     finishedReceiver != null, userId,
145                     broadcastAllowList == null ? null : broadcastAllowList.get(userId),
146                     bOptions);
147         }
148     }
149 
sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing, ArrayList<String> pkgList, int[] uidArr, IIntentReceiver finishedReceiver)150     public void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
151             ArrayList<String> pkgList, int[] uidArr, IIntentReceiver finishedReceiver) {
152         sendResourcesChangedBroadcast(mediaStatus, replacing,
153                 pkgList.toArray(new String[pkgList.size()]), uidArr, finishedReceiver);
154     }
155 
sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing, String[] pkgList, int[] uidArr, IIntentReceiver finishedReceiver)156     public void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
157             String[] pkgList, int[] uidArr, IIntentReceiver finishedReceiver) {
158         int size = pkgList.length;
159         if (size > 0) {
160             // Send broadcasts here
161             Bundle extras = new Bundle();
162             extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
163             if (uidArr != null) {
164                 extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
165             }
166             if (replacing) {
167                 extras.putBoolean(Intent.EXTRA_REPLACING, replacing);
168             }
169             String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
170                     : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
171             // TODO: not sure how to handle this one.
172             sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver,
173                     null, null, null, null);
174         }
175     }
176 
177     /**
178      * The just-installed/enabled app is bundled on the system, so presumed to be able to run
179      * automatically without needing an explicit launch.
180      * Send it a LOCKED_BOOT_COMPLETED/BOOT_COMPLETED if it would ordinarily have gotten ones.
181      */
sendBootCompletedBroadcastToSystemApp( String packageName, boolean includeStopped, int userId)182     public void sendBootCompletedBroadcastToSystemApp(
183             String packageName, boolean includeStopped, int userId) {
184         // If user is not running, the app didn't miss any broadcast
185         if (!mUmInternal.isUserRunning(userId)) {
186             return;
187         }
188         final IActivityManager am = ActivityManager.getService();
189         try {
190             // Deliver LOCKED_BOOT_COMPLETED first
191             Intent lockedBcIntent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)
192                     .setPackage(packageName);
193             if (includeStopped) {
194                 lockedBcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
195             }
196             final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED};
197             final BroadcastOptions bOptions = getTemporaryAppAllowlistBroadcastOptions(
198                     REASON_LOCKED_BOOT_COMPLETED);
199             am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null,
200                     requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE,
201                     bOptions.toBundle(), false, false, userId);
202 
203             // Deliver BOOT_COMPLETED only if user is unlocked
204             if (mUmInternal.isUserUnlockingOrUnlocked(userId)) {
205                 Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED).setPackage(packageName);
206                 if (includeStopped) {
207                     bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
208                 }
209                 am.broadcastIntentWithFeature(null, null, bcIntent, null, null, 0, null, null,
210                         requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE,
211                         bOptions.toBundle(), false, false, userId);
212             }
213         } catch (RemoteException e) {
214             throw e.rethrowFromSystemServer();
215         }
216     }
217 
getTemporaryAppAllowlistBroadcastOptions( @owerExemptionManager.ReasonCode int reasonCode)218     public @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
219             @PowerExemptionManager.ReasonCode int reasonCode) {
220         long duration = 10_000;
221         if (mAmInternal != null) {
222             duration = mAmInternal.getBootTimeTempAllowListDuration();
223         }
224         final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
225         bOptions.setTemporaryAppAllowlist(duration,
226                 TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
227                 reasonCode, "");
228         return bOptions;
229     }
230 
sendPackageChangedBroadcast(String packageName, boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason, int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList)231     public void sendPackageChangedBroadcast(String packageName, boolean dontKillApp,
232             ArrayList<String> componentNames, int packageUid, String reason,
233             int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
234         if (DEBUG_INSTALL) {
235             Log.v(TAG, "Sending package changed: package=" + packageName + " components="
236                     + componentNames);
237         }
238         Bundle extras = new Bundle(4);
239         extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));
240         String[] nameList = new String[componentNames.size()];
241         componentNames.toArray(nameList);
242         extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
243         extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, dontKillApp);
244         extras.putInt(Intent.EXTRA_UID, packageUid);
245         if (reason != null) {
246             extras.putString(Intent.EXTRA_REASON, reason);
247         }
248         // If this is not reporting a change of the overall package, then only send it
249         // to registered receivers.  We don't want to launch a swath of apps for every
250         // little component state change.
251         final int flags = !componentNames.contains(packageName)
252                 ? Intent.FLAG_RECEIVER_REGISTERED_ONLY : 0;
253         sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null,
254                 userIds, instantUserIds, broadcastAllowList, null);
255     }
256 
sendDeviceCustomizationReadyBroadcast()257     public static void sendDeviceCustomizationReadyBroadcast() {
258         final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY);
259         intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
260         final IActivityManager am = ActivityManager.getService();
261         final String[] requiredPermissions = {
262                 Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY,
263         };
264         try {
265             am.broadcastIntentWithFeature(null, null, intent, null, null, 0, null, null,
266                     requiredPermissions, null, null, android.app.AppOpsManager.OP_NONE, null, false,
267                     false, UserHandle.USER_ALL);
268         } catch (RemoteException e) {
269             throw e.rethrowFromSystemServer();
270         }
271     }
272 
sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId, int launcherUid, @Nullable ComponentName launcherComponent, @Nullable String appPredictionServicePackage)273     public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId,
274             int launcherUid, @Nullable ComponentName launcherComponent,
275             @Nullable String appPredictionServicePackage) {
276         if (launcherComponent != null) {
277             Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
278                     .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
279                     .putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
280                     .setPackage(launcherComponent.getPackageName());
281             mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid));
282         }
283         // TODO(b/122900055) Change/Remove this and replace with new permission role.
284         if (appPredictionServicePackage != null) {
285             Intent predictorIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
286                     .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
287                     .putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
288                     .setPackage(appPredictionServicePackage);
289             mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUid));
290         }
291     }
292 
sendPreferredActivityChangedBroadcast(int userId)293     public void sendPreferredActivityChangedBroadcast(int userId) {
294         final IActivityManager am = ActivityManager.getService();
295         if (am == null) {
296             return;
297         }
298 
299         final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
300         intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
301         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
302         try {
303             am.broadcastIntentWithFeature(null, null, intent, null, null,
304                     0, null, null, null, null, null, android.app.AppOpsManager.OP_NONE,
305                     null, false, false, userId);
306         } catch (RemoteException e) {
307         }
308     }
309 
sendPackageAddedForNewUsers(String packageName, @AppIdInt int appId, int[] userIds, int[] instantUserIds, int dataLoaderType, SparseArray<int[]> broadcastAllowlist)310     public void sendPackageAddedForNewUsers(String packageName,
311             @AppIdInt int appId, int[] userIds, int[] instantUserIds,
312             int dataLoaderType, SparseArray<int[]> broadcastAllowlist) {
313         Bundle extras = new Bundle(1);
314         // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
315         final int uid = UserHandle.getUid(
316                 (ArrayUtils.isEmpty(userIds) ? instantUserIds[0] : userIds[0]), appId);
317         extras.putInt(Intent.EXTRA_UID, uid);
318         extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
319 
320         sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
321                 packageName, extras, 0, null, null, userIds, instantUserIds,
322                 broadcastAllowlist, null);
323     }
324 
sendFirstLaunchBroadcast(String pkgName, String installerPkg, int[] userIds, int[] instantUserIds)325     public void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
326             int[] userIds, int[] instantUserIds) {
327         sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
328                 installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */, null);
329     }
330 }
331