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