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