Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7288456 authored by Joanne Chung's avatar Joanne Chung
Browse files

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
parent c0bcdfdb
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -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();
        }
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -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);
}
+32 −0
Original line number Diff line number Diff line
@@ -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;
@@ -2575,6 +2576,13 @@ public abstract class PackageManager {
    /** {@hide} */
    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
@@ -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");
    }
}
+71 −26
Original line number Diff line number Diff line
@@ -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,7 +48,6 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {

    final IntentFilter mPackageFilt;
    final IntentFilter mNonDataFilt;
    final IntentFilter mExternalFilt;

    Context mRegisteredContext;
    Handler mRegisteredHandler;
@@ -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
@@ -102,6 +100,15 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {

    public void register(Context context, UserHandle user,
            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,6 +142,12 @@ 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;
    }

@@ -330,6 +345,10 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {

    @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) {
@@ -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));
        }
    }
}
+19 −0
Original line number Diff line number Diff line
@@ -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
Loading