• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 com.android.server.pm.PackageManagerService.PACKAGE_SCHEME;
20 
21 import android.annotation.AppIdInt;
22 import android.annotation.NonNull;
23 import android.app.ActivityManager;
24 import android.app.IActivityManager;
25 import android.content.Intent;
26 import android.content.pm.PackageInstaller;
27 import android.content.pm.PackageManager;
28 import android.net.Uri;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.os.IRemoteCallback;
32 import android.os.Process;
33 import android.os.RemoteCallbackList;
34 import android.os.RemoteException;
35 import android.os.UserHandle;
36 import android.text.TextUtils;
37 import android.util.Pair;
38 import android.util.Slog;
39 import android.util.SparseArray;
40 
41 import com.android.internal.annotations.GuardedBy;
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.util.ArrayUtils;
44 
45 import java.util.ArrayList;
46 import java.util.function.BiFunction;
47 
48 /** Helper class to handle PackageMonitorCallback and notify the registered client. This is mainly
49  * used by PackageMonitor to improve the broadcast latency. */
50 @VisibleForTesting
51 public class PackageMonitorCallbackHelper {
52 
53     private static final boolean DEBUG = false;
54     private static final String TAG = "PackageMonitorCallbackHelper";
55 
56     @NonNull
57     private final Object mLock = new Object();
58     IActivityManager mActivityManager;
59 
60     @NonNull
61     @GuardedBy("mLock")
62     private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>();
63 
registerPackageMonitorCallback(IRemoteCallback callback, int userId, int uid)64     public void registerPackageMonitorCallback(IRemoteCallback callback, int userId, int uid) {
65         synchronized (mLock) {
66             mCallbacks.register(callback, new RegisterUser(userId, uid));
67         }
68     }
69 
unregisterPackageMonitorCallback(IRemoteCallback callback)70     public void unregisterPackageMonitorCallback(IRemoteCallback callback) {
71         synchronized (mLock) {
72             mCallbacks.unregister(callback);
73         }
74     }
75 
onUserRemoved(int userId)76     public void onUserRemoved(int userId) {
77         final ArrayList<IRemoteCallback> targetUnRegisteredCallbacks = new ArrayList<>();
78         synchronized (mLock) {
79             mCallbacks.broadcast((callback, user) -> {
80                 RegisterUser registerUser = (RegisterUser) user;
81                 if (registerUser.getUserId() == userId) {
82                     targetUnRegisteredCallbacks.add(callback);
83                 }
84             });
85         }
86         if (!targetUnRegisteredCallbacks.isEmpty()) {
87             int count = targetUnRegisteredCallbacks.size();
88             for (int i = 0; i < count; i++) {
89                 unregisterPackageMonitorCallback(targetUnRegisteredCallbacks.get(i));
90             }
91         }
92     }
93 
notifyPackageAddedForNewUsers(String packageName, @AppIdInt int appId, @NonNull int[] userIds, @NonNull int[] instantUserIds, boolean isArchived, int dataLoaderType, SparseArray<int[]> broadcastAllowList, @NonNull Handler handler)94     public void notifyPackageAddedForNewUsers(String packageName,
95             @AppIdInt int appId, @NonNull int[] userIds, @NonNull int[] instantUserIds,
96             boolean isArchived, int dataLoaderType, SparseArray<int[]> broadcastAllowList,
97             @NonNull Handler handler) {
98         Bundle extras = new Bundle(2);
99         // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
100         final int uid = UserHandle.getUid(
101                 (ArrayUtils.isEmpty(userIds) ? instantUserIds[0] : userIds[0]), appId);
102         extras.putInt(Intent.EXTRA_UID, uid);
103         if (isArchived) {
104             extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
105         }
106         extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
107         notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, packageName, extras,
108                 userIds /* userIds */, instantUserIds, broadcastAllowList, handler,
109                 null /* filterExtras */);
110     }
111 
notifyResourcesChanged(boolean mediaStatus, boolean replacing, @NonNull String[] pkgNames, @NonNull int[] uids, @NonNull Handler handler)112     public void notifyResourcesChanged(boolean mediaStatus, boolean replacing,
113             @NonNull String[] pkgNames, @NonNull int[] uids, @NonNull Handler handler) {
114         Bundle extras = new Bundle();
115         extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgNames);
116         extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uids);
117         if (replacing) {
118             extras.putBoolean(Intent.EXTRA_REPLACING, replacing);
119         }
120         String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
121                 : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
122         notifyPackageMonitor(action, null /* pkg */, extras, null /* userIds */,
123                 null /* instantUserIds */, null /* broadcastAllowList */, handler,
124                 null /* filterExtras */);
125     }
126 
notifyPackageChanged(String packageName, boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason, int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList, Handler handler)127     public void notifyPackageChanged(String packageName, boolean dontKillApp,
128             ArrayList<String> componentNames, int packageUid, String reason, int[] userIds,
129             int[] instantUserIds, SparseArray<int[]> broadcastAllowList, Handler handler) {
130         Bundle extras = new Bundle(4);
131         extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));
132         String[] nameList = new String[componentNames.size()];
133         componentNames.toArray(nameList);
134         extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
135         extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, dontKillApp);
136         extras.putInt(Intent.EXTRA_UID, packageUid);
137         if (reason != null) {
138             extras.putString(Intent.EXTRA_REASON, reason);
139         }
140         notifyPackageMonitor(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, userIds,
141                 instantUserIds, broadcastAllowList, handler, null /* filterExtras */);
142     }
143 
notifyPackageMonitor(String action, String pkg, Bundle extras, int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList, Handler handler, BiFunction<Integer, Bundle, Bundle> filterExtras)144     public void notifyPackageMonitor(String action, String pkg, Bundle extras,
145             int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList,
146             Handler handler, BiFunction<Integer, Bundle, Bundle> filterExtras) {
147         if (!isAllowedCallbackAction(action)) {
148             return;
149         }
150         try {
151             final int[] resolvedUserIds;
152             if (userIds == null) {
153                 if (mActivityManager == null) {
154                     mActivityManager = ActivityManager.getService();
155                 }
156                 if (mActivityManager == null) return;
157                 resolvedUserIds = mActivityManager.getRunningUserIds();
158             } else {
159                 resolvedUserIds = userIds;
160             }
161 
162             if (ArrayUtils.isEmpty(instantUserIds)) {
163                 doNotifyCallbacksByAction(
164                         action, pkg, extras, resolvedUserIds, broadcastAllowList, handler,
165                         filterExtras);
166             } else {
167                 doNotifyCallbacksByAction(action, pkg, extras, instantUserIds, broadcastAllowList,
168                         handler, filterExtras);
169             }
170         } catch (RemoteException e) {
171             // do nothing
172         }
173     }
174 
notifyPackageMonitorWithIntent(Intent intent, int userId, int[] broadcastAllowList, Handler handler)175     void notifyPackageMonitorWithIntent(Intent intent,
176             int userId, int[] broadcastAllowList, Handler handler) {
177         if (!isAllowedCallbackAction(intent.getAction())) {
178             return;
179         }
180         doNotifyCallbacksByIntent(intent, userId, broadcastAllowList, handler);
181     }
182 
isAllowedCallbackAction(String action)183     private static boolean isAllowedCallbackAction(String action) {
184         return TextUtils.equals(action, Intent.ACTION_PACKAGE_ADDED)
185                 || TextUtils.equals(action, Intent.ACTION_PACKAGE_REMOVED)
186                 || TextUtils.equals(action, Intent.ACTION_PACKAGE_CHANGED)
187                 || TextUtils.equals(action, Intent.ACTION_UID_REMOVED)
188                 || TextUtils.equals(action, Intent.ACTION_PACKAGES_SUSPENDED)
189                 || TextUtils.equals(action, Intent.ACTION_PACKAGES_UNSUSPENDED)
190                 || TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE)
191                 || TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
192                 || TextUtils.equals(action, Intent.ACTION_PACKAGE_DATA_CLEARED)
193                 || TextUtils.equals(action, Intent.ACTION_PACKAGE_RESTARTED)
194                 || TextUtils.equals(action, Intent.ACTION_PACKAGE_UNSTOPPED);
195 
196     }
197 
doNotifyCallbacksByIntent(Intent intent, int userId, int[] broadcastAllowList, Handler handler)198     private void doNotifyCallbacksByIntent(Intent intent, int userId,
199             int[] broadcastAllowList, Handler handler) {
200         doNotifyCallbacks(intent, userId, broadcastAllowList, handler,
201                 null /* filterExtrasFunction */);
202     }
203 
doNotifyCallbacksByAction(String action, String pkg, Bundle extras, int[] userIds, SparseArray<int[]> broadcastAllowList, Handler handler, BiFunction<Integer, Bundle, Bundle> filterExtrasFunction)204     private void doNotifyCallbacksByAction(String action, String pkg, Bundle extras, int[] userIds,
205             SparseArray<int[]> broadcastAllowList, Handler handler,
206             BiFunction<Integer, Bundle, Bundle> filterExtrasFunction) {
207         for (int userId : userIds) {
208             final Intent intent = new Intent(action,
209                     pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
210             if (extras != null) {
211                 intent.putExtras(extras);
212             }
213             int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
214             if (uid >= 0 && UserHandle.getUserId(uid) != userId) {
215                 uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
216                 intent.putExtra(Intent.EXTRA_UID, uid);
217             }
218             intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
219 
220             final int[] allowUids =
221                     broadcastAllowList != null ? broadcastAllowList.get(userId) : null;
222             doNotifyCallbacks(intent, userId, allowUids, handler, filterExtrasFunction);
223         }
224     }
225 
doNotifyCallbacks(Intent intent, int userId, int[] allowUids, Handler handler, BiFunction<Integer, Bundle, Bundle> filterExtrasFunction)226     private void doNotifyCallbacks(Intent intent, int userId, int[] allowUids, Handler handler,
227             BiFunction<Integer, Bundle, Bundle> filterExtrasFunction) {
228         handler.post(() -> {
229             final ArrayList<Pair<IRemoteCallback, Intent>> target = new ArrayList<>();
230             synchronized (mLock) {
231                 mCallbacks.broadcast((callback, user) -> {
232                     RegisterUser registerUser = (RegisterUser) user;
233                     if ((registerUser.getUserId() != UserHandle.USER_ALL)
234                             && (registerUser.getUserId() != userId)) {
235                         return;
236                     }
237                     int registerUid = registerUser.getUid();
238                     if (allowUids != null && !UserHandle.isSameApp(registerUid, Process.SYSTEM_UID)
239                             && !ArrayUtils.contains(allowUids, registerUid)) {
240                         if (DEBUG) {
241                             Slog.w(TAG, "Skip invoke PackageMonitorCallback for "
242                                     + intent.getAction() + ", uid " + registerUid);
243                         }
244                         return;
245                     }
246                     Intent newIntent = intent;
247                     if (filterExtrasFunction != null) {
248                         final Bundle extras = intent.getExtras();
249                         if (extras != null) {
250                             final Bundle filteredExtras =
251                                     filterExtrasFunction.apply(registerUid, extras);
252                             if (filteredExtras == null) {
253                                 // caller is unable to access this intent
254                                 if (DEBUG) {
255                                     Slog.w(TAG,
256                                             "Skip invoke PackageMonitorCallback for "
257                                                     + intent.getAction()
258                                                     + " because null filteredExtras");
259                                 }
260                                 return;
261                             }
262                             newIntent = new Intent(newIntent);
263                             newIntent.replaceExtras(filteredExtras);
264                         }
265                     }
266                     target.add(new Pair<>(callback, newIntent));
267                 });
268             }
269             for (int i = 0; i < target.size(); i++) {
270                 Pair<IRemoteCallback, Intent> p = target.get(i);
271                 invokeCallback(p.first, p.second);
272             }
273         });
274     }
275 
invokeCallback(IRemoteCallback callback, Intent intent)276     private void invokeCallback(IRemoteCallback callback, Intent intent) {
277         try {
278             Bundle bundle = new Bundle();
279             bundle.putParcelable(
280                     PackageManager.EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT, intent);
281             callback.sendResult(bundle);
282         } catch (RemoteException e) {
283             // do nothing
284         }
285     }
286 
287     private final class RegisterUser {
288         int mUserId;
289         int mUid;
290 
RegisterUser(int userId, int uid)291         RegisterUser(int userId, int uid) {
292             mUid = uid;
293             mUserId = userId;
294         }
295 
getUid()296         public int getUid() {
297             return mUid;
298         }
299 
getUserId()300         public int getUserId() {
301             return mUserId;
302         }
303     }
304 }
305