diff options
| author | 2023-06-06 06:24:58 +0000 | |
|---|---|---|
| committer | 2023-06-27 15:50:03 +0000 | |
| commit | f728845660e4dd01a6a3a68fcd5a67cdaa821356 (patch) | |
| tree | a07e3b8cb58f1e3e74a48c4438ca7106d60824e0 | |
| parent | c0bcdfdb7896796efe374f15d9c0da103df2de8b (diff) | |
PackageMonitor improvement
The PackageMonitor receives package broadcast events and notify the
registered client with several callback methods. But the delivery of
the broadcast is unpredictable, e.g. broadcast queue is almost full,
the event will be sent to client late.
Try to improve some package events with callback from system instead.
This change still keeps using some broadcast events sent from user
manager and activity manager service.
Bug: 29385425
Test: manual
Test: atest PackageUpdateMonitorTest#startMonitoring_registerOnlyOnce
Test: atest PackageUpdateMonitorTest#stopMonitoring_unregistersOnlyOnce
Test: atest DataManagerTest#testDeleteUninstalledPackageDataOnPackageRemoved
Change-Id: I44727cead739270b63623aeda2c07cb6e7781d36
10 files changed, 364 insertions, 26 deletions
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index f4a29ed3a92e..0e3a6959bed7 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -91,6 +91,7 @@ import android.graphics.drawable.LayerDrawable; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; @@ -3918,4 +3919,24 @@ public class ApplicationPackageManager extends PackageManager { throw e.rethrowFromSystemServer(); } } + + @Override + public void registerPackageMonitorCallback(@NonNull IRemoteCallback callback, int userId) { + Objects.requireNonNull(callback); + try { + mPM.registerPackageMonitorCallback(callback, userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void unregisterPackageMonitorCallback(@NonNull IRemoteCallback callback) { + Objects.requireNonNull(callback); + try { + mPM.unregisterPackageMonitorCallback(callback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index bf25ae8b43a7..ced640156e23 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -55,6 +55,7 @@ import android.content.pm.dex.IArtManager; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; +import android.os.IRemoteCallback; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.content.IntentSender; @@ -818,4 +819,8 @@ interface IPackageManager { boolean[] canPackageQuery(String sourcePackageName, in String[] targetPackageNames, int userId); boolean waitForHandler(long timeoutMillis, boolean forBackgroundHandler); + + void registerPackageMonitorCallback(IRemoteCallback callback, int userId); + + void unregisterPackageMonitorCallback(IRemoteCallback callback); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index e5468a58e4be..f6b05e2bc826 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -65,6 +65,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.IRemoteCallback; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; @@ -2576,6 +2577,13 @@ public abstract class PackageManager { public static final String EXTRA_MOVE_ID = "android.content.pm.extra.MOVE_ID"; /** + * Extra field name for notifying package change event. Currently, it is used by PackageMonitor. + * @hide + */ + public static final String EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT = + "android.content.pm.extra.EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT"; + + /** * Usable by the required verifier as the {@code verificationCode} argument * for {@link PackageManager#verifyPendingInstall} to indicate that it will * allow the installation to proceed without any of the optional verifiers @@ -11039,4 +11047,28 @@ public abstract class PackageManager { throw new UnsupportedOperationException( "relinquishUpdateOwnership not implemented in subclass"); } + + /** + * Register for notifications of package changes such as install, removal and other events. + * + * @param callback the callback to register for receiving the change events + * @param userId The id of registered user + * @hide + */ + public void registerPackageMonitorCallback(@NonNull IRemoteCallback callback, int userId) { + throw new UnsupportedOperationException( + "registerPackageMonitorCallback not implemented in subclass"); + } + + /** + * Unregister for notifications of package changes such as install, removal and other events. + * + * @param callback the callback to unregister for receiving the change events + * @see #registerPackageMonitorCallback(IRemoteCallback, int) + * @hide + */ + public void unregisterPackageMonitorCallback(@NonNull IRemoteCallback callback) { + throw new UnsupportedOperationException( + "unregisterPackageMonitorCallback not implemented in subclass"); + } } diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java index 76e0e34ef7d3..2b2cda564438 100644 --- a/core/java/com/android/internal/content/PackageMonitor.java +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -22,15 +22,22 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.net.Uri; +import android.os.Bundle; import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.IRemoteCallback; import android.os.Looper; +import android.os.RemoteException; import android.os.UserHandle; +import android.util.Log; import android.util.Slog; import com.android.internal.os.BackgroundThread; import java.util.Objects; +import java.util.concurrent.Executor; /** * Helper class for monitoring the state of packages: adding, removing, @@ -41,8 +48,7 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { final IntentFilter mPackageFilt; final IntentFilter mNonDataFilt; - final IntentFilter mExternalFilt; - + Context mRegisteredContext; Handler mRegisteredHandler; String[] mDisappearingPackages; @@ -55,15 +61,16 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { String[] mTempArray = new String[1]; + PackageMonitorCallback mPackageMonitorCallback; + @UnsupportedAppUsage public PackageMonitor() { final boolean isCore = UserHandle.isCore(android.os.Process.myUid()); mPackageFilt = new IntentFilter(); - mPackageFilt.addAction(Intent.ACTION_PACKAGE_ADDED); - mPackageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED); - mPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED); + // Settings app sends the broadcast mPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); + // AMS sends the broadcast mPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED); mPackageFilt.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); mPackageFilt.addDataScheme("package"); @@ -72,20 +79,11 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { } mNonDataFilt = new IntentFilter(); - mNonDataFilt.addAction(Intent.ACTION_UID_REMOVED); + // UserController sends the broadcast mNonDataFilt.addAction(Intent.ACTION_USER_STOPPED); - mNonDataFilt.addAction(Intent.ACTION_PACKAGES_SUSPENDED); - mNonDataFilt.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); if (isCore) { mNonDataFilt.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); } - - mExternalFilt = new IntentFilter(); - mExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); - mExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); - if (isCore) { - mExternalFilt.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - } } @UnsupportedAppUsage @@ -101,7 +99,16 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { } public void register(Context context, UserHandle user, - boolean externalStorage, Handler handler) { + boolean externalStorage, Handler handler) { + // Remove until all using code are updated to new method. + register(context, user, handler); + } + + + /** + * Register for notifications of package changes such as install, removal and other events. + */ + public void register(Context context, UserHandle user, Handler handler) { if (mRegisteredContext != null) { throw new IllegalStateException("Already registered"); } @@ -110,15 +117,17 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { if (user != null) { context.registerReceiverAsUser(this, user, mPackageFilt, null, mRegisteredHandler); context.registerReceiverAsUser(this, user, mNonDataFilt, null, mRegisteredHandler); - if (externalStorage) { - context.registerReceiverAsUser(this, user, mExternalFilt, null, - mRegisteredHandler); - } } else { context.registerReceiver(this, mPackageFilt, null, mRegisteredHandler); context.registerReceiver(this, mNonDataFilt, null, mRegisteredHandler); - if (externalStorage) { - context.registerReceiver(this, mExternalFilt, null, mRegisteredHandler); + } + if (mPackageMonitorCallback == null) { + PackageManager pm = mRegisteredContext.getPackageManager(); + if (pm != null) { + mPackageMonitorCallback = new PackageMonitorCallback(this, + new HandlerExecutor(mRegisteredHandler)); + int userId = user != null ? user.getIdentifier() : mRegisteredContext.getUserId(); + pm.registerPackageMonitorCallback(mPackageMonitorCallback, userId); } } } @@ -133,9 +142,15 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { throw new IllegalStateException("Not registered"); } mRegisteredContext.unregisterReceiver(this); + + PackageManager pm = mRegisteredContext.getPackageManager(); + if (pm != null && mPackageMonitorCallback != null) { + pm.unregisterPackageMonitorCallback(mPackageMonitorCallback); + } + mPackageMonitorCallback = null; mRegisteredContext = null; } - + public void onBeginPackageChanges() { } @@ -327,9 +342,13 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { String pkg = uri != null ? uri.getSchemeSpecificPart() : null; return pkg; } - + @Override public void onReceive(Context context, Intent intent) { + doHandlePackageEvent(intent); + } + + private void doHandlePackageEvent(Intent intent) { mChangeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); if (mChangeUserId == UserHandle.USER_NULL) { @@ -337,11 +356,11 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { return; } onBeginPackageChanges(); - + mDisappearingPackages = mAppearingPackages = null; mSomePackagesChanged = false; mModifiedComponents = null; - + String action = intent.getAction(); if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { String pkg = getPackageName(intent); @@ -465,4 +484,30 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { onFinishPackageChanges(); mChangeUserId = UserHandle.USER_NULL; } + + private static final class PackageMonitorCallback extends IRemoteCallback.Stub { + + private final PackageMonitor mPackageMonitor; + private final Executor mExecutor; + + PackageMonitorCallback(PackageMonitor monitor, Executor executor) { + mPackageMonitor = monitor; + mExecutor = executor; + } + + @Override + public void sendResult(Bundle data) throws RemoteException { + onHandlePackageMonitorCallback(data); + } + + private void onHandlePackageMonitorCallback(Bundle bundle) { + Intent intent = bundle.getParcelable( + PackageManager.EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT, Intent.class); + if (intent == null) { + Log.w(TAG, "No intent is set for PackageMonitorCallback"); + return; + } + mExecutor.execute(() -> mPackageMonitor.doHandlePackageEvent(intent)); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt index 69547105d419..9260f63c1195 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.controls.controller import android.content.Context +import android.content.pm.PackageManager import android.os.Handler import android.os.UserHandle import android.testing.AndroidTestingRunner @@ -34,6 +35,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest @@ -42,12 +44,14 @@ class PackageUpdateMonitorTest : SysuiTestCase() { @Mock private lateinit var context: Context @Mock private lateinit var bgHandler: Handler + @Mock private lateinit var packageManager: PackageManager private lateinit var underTest: PackageUpdateMonitor @Before fun setup() { MockitoAnnotations.initMocks(this) + whenever(context.packageManager).thenReturn(packageManager) } @Test @@ -58,9 +62,16 @@ class PackageUpdateMonitorTest : SysuiTestCase() { // There are two receivers registered verify(context, times(2)) .registerReceiverAsUser(any(), eq(USER), any(), eq(null), eq(bgHandler)) + verify(packageManager).registerPackageMonitorCallback(any(), eq(USER.getIdentifier())) + // context will be used to get PackageManager, the test should clear invocations + // for next startMonitoring() assertion + clearInvocations(context) underTest.startMonitoring() + // No more interactions for registerReceiverAsUser verifyNoMoreInteractions(context) + // No more interactions for registerPackageMonitorCallback + verifyNoMoreInteractions(packageManager) } @Test @@ -69,12 +80,20 @@ class PackageUpdateMonitorTest : SysuiTestCase() { underTest.startMonitoring() clearInvocations(context) + clearInvocations(packageManager) underTest.stopMonitoring() verify(context).unregisterReceiver(any()) + verify(packageManager).unregisterPackageMonitorCallback(any()) + // context will be used to get PackageManager, the test should clear invocations + // for next stopMonitoring() assertion + clearInvocations(context) underTest.stopMonitoring() + // No more interactions for unregisterReceiver verifyNoMoreInteractions(context) + // No more interactions for unregisterPackageMonitorCallback + verifyNoMoreInteractions(packageManager) } @Test diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index e01e1d3f3edd..58a85cfc19a7 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -2790,6 +2790,8 @@ final class InstallPackageHelper { final String[] pkgNames = new String[]{ request.getRemovedInfo().mRemovedPackage}; final int[] uids = new int[]{request.getRemovedInfo().mUid}; + mPm.notifyResourcesChanged(false /* mediaStatus */, + true /* replacing */, pkgNames, uids); mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer, false /* mediaStatus */, true /* replacing */, pkgNames, uids); } @@ -2995,6 +2997,8 @@ final class InstallPackageHelper { final int[] uids = new int[]{request.getPkg().getUid()}; mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer, true /* mediaStatus */, true /* replacing */, pkgNames, uids); + mPm.notifyResourcesChanged(true /* mediaStatus */, true /* replacing */, + pkgNames, uids); } } else if (!ArrayUtils.isEmpty(request.getLibraryConsumers())) { // if static shared lib // No need to kill consumers if it's installation of new version static shared lib. diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 3377f0090027..8ce06d423ad4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -128,6 +128,7 @@ import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; +import android.os.IRemoteCallback; import android.os.Message; import android.os.Parcel; import android.os.ParcelFileDescriptor; @@ -710,6 +711,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService @NonNull private final PackageObserverHelper mPackageObserverHelper = new PackageObserverHelper(); + @NonNull + private final PackageMonitorCallbackHelper mPackageMonitorCallbackHelper = + new PackageMonitorCallbackHelper(); + private final ModuleInfoProvider mModuleInfoProvider; final ApexManager mApexManager; @@ -2975,6 +2980,25 @@ public class PackageManagerService implements PackageSender, TestUtilityService mHandler.post(() -> mBroadcastHelper.sendPackageBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList, null /* filterExtrasForReceiver */, bOptions)); + if (targetPkg == null) { + // For some broadcast action, e.g. ACTION_PACKAGE_ADDED, this method will be called + // many times to different targets, e.g. installer app, permission controller, other + // registered apps. We should filter it to avoid calling back many times for the same + // action. When the targetPkg is set, it sends the broadcast to specific app, e.g. + // installer app or null for registered apps. The callback only need to send back to the + // registered apps so we check the null condition here. + notifyPackageMonitor(action, pkg, extras, userIds); + } + } + + void notifyPackageMonitor(String action, String pkg, Bundle extras, int[] userIds) { + mPackageMonitorCallbackHelper.notifyPackageMonitor(action, pkg, extras, userIds); + } + + void notifyResourcesChanged(boolean mediaStatus, boolean replacing, + @NonNull String[] pkgNames, @NonNull int[] uids) { + mPackageMonitorCallbackHelper.notifyResourcesChanged(mediaStatus, replacing, pkgNames, + uids); } @Override @@ -3023,6 +3047,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService userIds, snapshot.getPackageStates()); mHandler.post(() -> mBroadcastHelper.sendPackageAddedForNewUsers( packageName, appId, userIds, instantUserIds, dataLoaderType, broadcastAllowList)); + mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(packageName, appId, userIds, + instantUserIds, dataLoaderType); if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) { mHandler.post(() -> { for (int userId : userIds) { @@ -4015,6 +4041,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService mHandler.post(() -> mBroadcastHelper.sendPackageChangedBroadcast( packageName, dontKillApp, componentNames, packageUid, reason, userIds, instantUserIds, broadcastAllowList)); + mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames, + packageUid, reason, userIds); } /** @@ -6142,6 +6170,16 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Override + public void registerPackageMonitorCallback(@NonNull IRemoteCallback callback, int userId) { + mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, userId); + } + + @Override + public void unregisterPackageMonitorCallback(@NonNull IRemoteCallback callback) { + mPackageMonitorCallbackHelper.unregisterPackageMonitorCallback(callback); + } + + @Override public void requestPackageChecksums(@NonNull String packageName, boolean includeSplits, @Checksum.TypeMask int optional, @Checksum.TypeMask int required, @Nullable List trustedInstallers, diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java new file mode 100644 index 000000000000..93345959306d --- /dev/null +++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static com.android.server.pm.PackageManagerService.PACKAGE_SCHEME; + +import android.annotation.AppIdInt; +import android.annotation.NonNull; +import android.app.ActivityManager; +import android.app.IActivityManager; +import android.content.Intent; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.os.IRemoteCallback; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.os.UserHandle; +import android.text.TextUtils; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; + +import java.util.ArrayList; + +/** Helper class to handle PackageMonitorCallback and notify the registered client. This is mainly + * used by PackageMonitor to improve the broadcast latency. */ +class PackageMonitorCallbackHelper { + @NonNull + private final Object mLock = new Object(); + final IActivityManager mActivityManager = ActivityManager.getService(); + + @NonNull + @GuardedBy("mLock") + private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>(); + + public void registerPackageMonitorCallback(IRemoteCallback callback, int userId) { + synchronized (mLock) { + mCallbacks.register(callback, userId); + } + } + + public void unregisterPackageMonitorCallback(IRemoteCallback callback) { + synchronized (mLock) { + mCallbacks.unregister(callback); + } + } + + public void notifyPackageAddedForNewUsers(String packageName, + @AppIdInt int appId, @NonNull int[] userIds, @NonNull int[] instantUserIds, + int dataLoaderType) { + Bundle extras = new Bundle(2); + // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast + final int uid = UserHandle.getUid( + (ArrayUtils.isEmpty(userIds) ? instantUserIds[0] : userIds[0]), appId); + extras.putInt(Intent.EXTRA_UID, uid); + extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType); + notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, packageName, extras , + userIds /* userIds */); + } + + public void notifyResourcesChanged(boolean mediaStatus, boolean replacing, + @NonNull String[] pkgNames, @NonNull int[] uids) { + Bundle extras = new Bundle(); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgNames); + extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uids); + if (replacing) { + extras.putBoolean(Intent.EXTRA_REPLACING, replacing); + } + String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE + : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; + notifyPackageMonitor(action, null /* pkg */, extras, null /* userIds */); + } + + public void notifyPackageChanged(String packageName, boolean dontKillApp, + ArrayList<String> componentNames, int packageUid, String reason, int[] userIds) { + Bundle extras = new Bundle(4); + extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0)); + String[] nameList = new String[componentNames.size()]; + componentNames.toArray(nameList); + extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList); + extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, dontKillApp); + extras.putInt(Intent.EXTRA_UID, packageUid); + if (reason != null) { + extras.putString(Intent.EXTRA_REASON, reason); + } + notifyPackageMonitor(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, userIds); + } + + public void notifyPackageMonitor(String action, String pkg, Bundle extras, + int[] userIds) { + if (!isAllowedCallbackAction(action)) { + return; + } + try { + final int[] resolvedUserIds; + if (userIds == null) { + if (mActivityManager == null) return; + resolvedUserIds = mActivityManager.getRunningUserIds(); + } else { + resolvedUserIds = userIds; + } + doNotifyCallbacks(action, pkg, extras, resolvedUserIds); + } catch (RemoteException e) { + // do nothing + } + } + + private static boolean isAllowedCallbackAction(String action) { + return TextUtils.equals(action, Intent.ACTION_PACKAGE_ADDED) + || TextUtils.equals(action, Intent.ACTION_PACKAGE_REMOVED) + || TextUtils.equals(action, Intent.ACTION_PACKAGE_CHANGED) + || TextUtils.equals(action, Intent.ACTION_UID_REMOVED) + || TextUtils.equals(action, Intent.ACTION_PACKAGES_SUSPENDED) + || TextUtils.equals(action, Intent.ACTION_PACKAGES_UNSUSPENDED) + || TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE) + || TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); + } + + private void doNotifyCallbacks(String action, String pkg, Bundle extras, int[] userIds) { + RemoteCallbackList<IRemoteCallback> callbacks; + synchronized (mLock) { + callbacks = mCallbacks; + } + for (int userId : userIds) { + final Intent intent = new Intent(action, + pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null); + if (extras != null) { + intent.putExtras(extras); + } + int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + if (uid >= 0 && UserHandle.getUserId(uid) != userId) { + uid = UserHandle.getUid(userId, UserHandle.getAppId(uid)); + intent.putExtra(Intent.EXTRA_UID, uid); + } + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + callbacks.broadcast((callback, user) -> { + int registerUserId = (int) user; + if ((registerUserId != UserHandle.USER_ALL) && (registerUserId != userId)) { + return; + } + invokeCallback(callback, intent); + }); + } + } + + private void invokeCallback(IRemoteCallback callback, Intent intent) { + try { + Bundle bundle = new Bundle(); + bundle.putParcelable( + PackageManager.EXTRA_PACKAGE_MONITOR_CALLBACK_RESULT, intent); + callback.sendResult(bundle); + } catch (RemoteException e) { + // do nothing + } + } +} diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java index 8f8f4376d2cc..6f0fe63cdaa8 100644 --- a/services/core/java/com/android/server/pm/StorageEventHelper.java +++ b/services/core/java/com/android/server/pm/StorageEventHelper.java @@ -306,6 +306,7 @@ public final class StorageEventHelper extends StorageEventListener { } mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer, mediaStatus, replacing, packageNames, packageUids); + mPm.notifyResourcesChanged(mediaStatus, replacing, packageNames, packageUids); } /** diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java index 08934c69e099..89aff9eec4cf 100644 --- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java +++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java @@ -633,6 +633,7 @@ public final class SuspendPackageHelper { (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList( mPm.snapshotComputer(), callingUid, intentExtras), options)); + mPm.notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId}); } /** |