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