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

Commit 0acf65b2 authored by Lucas Silva's avatar Lucas Silva
Browse files

Remove uninstalled apps from the SLS list of enabled apps.

This installs a broadcast receiver in the CommunalManager system
service, so that we keep the array of packages in Settings up-to-date
with package removals.

Test: locally on device
Test: atest FrameworksMockingServicesTests:CommunalManagerServiceTest
Bug: 200324021
Change-Id: I227958736b3fe3ccefa32531eda88dfe251494c4
parent 27bed0c6
Loading
Loading
Loading
Loading
+92 −0
Original line number Diff line number Diff line
@@ -17,10 +17,12 @@
package com.android.server.communal;

import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;

import static com.android.server.wm.ActivityInterceptorCallback.COMMUNAL_MODE_ORDERED_ID;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
@@ -31,16 +33,21 @@ import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.Overridable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.LaunchAfterAuthenticationActivity;
@@ -67,6 +74,8 @@ public final class CommunalManagerService extends SystemService {
    private final KeyguardManager mKeyguardManager;
    private final AtomicBoolean mCommunalViewIsShowing = new AtomicBoolean(false);
    private final BinderService mBinderService;
    private final PackageReceiver mPackageReceiver;
    private final PackageManager mPackageManager;

    /**
     * This change id is used to annotate packages which are allowed to run in communal mode.
@@ -123,9 +132,11 @@ public final class CommunalManagerService extends SystemService {
    public CommunalManagerService(Context context) {
        super(context);
        mContext = context;
        mPackageManager = mContext.getPackageManager();
        mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
        mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
        mBinderService = new BinderService();
        mPackageReceiver = new PackageReceiver(mContext);
    }

    @VisibleForTesting
@@ -144,6 +155,13 @@ public final class CommunalManagerService extends SystemService {
        mAtmInternal.registerActivityStartInterceptor(
                COMMUNAL_MODE_ORDERED_ID,
                mActivityInterceptorCallback);
        mPackageReceiver.register();
        removeUninstalledPackagesFromSettings();
    }

    @Override
    public void finalize() {
        mPackageReceiver.unregister();
    }

    private Set<String> getUserEnabledApps() {
@@ -157,6 +175,35 @@ public final class CommunalManagerService extends SystemService {
                : new HashSet<>(Arrays.asList(encodedApps.split(DELIMITER)));
    }

    private void removeUninstalledPackagesFromSettings() {
        for (String packageName : getUserEnabledApps()) {
            if (!isPackageInstalled(packageName, mPackageManager)) {
                removePackageFromSettings(packageName);
            }
        }
    }

    private void removePackageFromSettings(String packageName) {
        Set<String> enabledPackages = getUserEnabledApps();
        if (enabledPackages.remove(packageName)) {
            Settings.Secure.putStringForUser(
                    mContext.getContentResolver(),
                    Settings.Secure.COMMUNAL_MODE_PACKAGES,
                    String.join(DELIMITER, enabledPackages),
                    UserHandle.USER_SYSTEM);
        }
    }

    @VisibleForTesting
    static boolean isPackageInstalled(String packageName, PackageManager packageManager) {
        if (packageManager == null) return false;
        try {
            return packageManager.getPackageInfo(packageName, 0) != null;
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
    }

    private boolean isAppAllowed(ApplicationInfo appInfo) {
        if (isChangeEnabled(ALLOW_COMMUNAL_MODE_BY_DEFAULT, appInfo)) {
            return true;
@@ -188,4 +235,49 @@ public final class CommunalManagerService extends SystemService {
            mCommunalViewIsShowing.set(isShowing);
        }
    }

    /**
     * A {@link BroadcastReceiver} that listens on package removed events and updates any stored
     * package state in Settings.
     */
    private final class PackageReceiver extends BroadcastReceiver {
        private final Context mContext;
        private final IntentFilter mIntentFilter;

        private PackageReceiver(Context context) {
            mContext = context;
            mIntentFilter = new IntentFilter();
            mIntentFilter.addAction(ACTION_PACKAGE_REMOVED);
            mIntentFilter.addDataScheme("package");
        }

        private void register() {
            mContext.registerReceiverAsUser(
                    this,
                    UserHandle.SYSTEM,
                    mIntentFilter,
                    /* broadcastPermission= */null,
                    /* scheduler= */ null);
        }

        private void unregister() {
            mContext.unregisterReceiver(this);
        }

        @Override
        public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
            final Uri data = intent.getData();
            if (data == null) {
                Slog.w(TAG, "Failed to get package name in package receiver");
                return;
            }
            final String packageName = data.getSchemeSpecificPart();
            final String action = intent.getAction();
            if (ACTION_PACKAGE_REMOVED.equals(action)) {
                removePackageFromSettings(packageName);
            } else {
                Slog.w(TAG, "Unsupported action in package receiver: " + action);
            }
        }
    }
}
+68 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.communal;

import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
@@ -27,18 +28,22 @@ import static com.android.server.wm.ActivityInterceptorCallback.COMMUNAL_MODE_OR

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;

import android.Manifest;
import android.annotation.Nullable;
import android.app.KeyguardManager;
import android.app.communal.ICommunalManager;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.net.Uri;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
@@ -89,6 +94,7 @@ public class CommunalManagerServiceTest {
    private KeyguardManager mKeyguardManager;

    private ActivityInterceptorCallback mActivityInterceptorCallback;
    private BroadcastReceiver mPackageReceiver;
    private ActivityInfo mAInfo;
    private ICommunalManager mBinder;
    private ContextWrapper mContextSpy;
@@ -97,6 +103,7 @@ public class CommunalManagerServiceTest {
    public final void setUp() {
        mMockingSession = mockitoSession()
                .initMocks(this)
                .spyStatic(CommunalManagerService.class)
                .mockStatic(CompatChanges.class)
                .strictness(Strictness.WARN)
                .startMocking();
@@ -121,6 +128,12 @@ public class CommunalManagerServiceTest {
                activityInterceptorCaptor.capture());
        mActivityInterceptorCallback = activityInterceptorCaptor.getValue();

        ArgumentCaptor<BroadcastReceiver> packageReceiverCaptor =
                ArgumentCaptor.forClass(BroadcastReceiver.class);
        verify(mContextSpy).registerReceiverAsUser(packageReceiverCaptor.capture(),
                eq(UserHandle.SYSTEM), any(), any(), any());
        mPackageReceiver = packageReceiverCaptor.getValue();

        mBinder = mService.getBinderServiceInstance();

        mAInfo = new ActivityInfo();
@@ -165,6 +178,11 @@ public class CommunalManagerServiceTest {
                Settings.Secure.COMMUNAL_MODE_PACKAGES, packages, UserHandle.USER_SYSTEM);
    }

    private String getAllowedPackages() {
        return Settings.Secure.getStringForUser(mContextSpy.getContentResolver(),
                Settings.Secure.COMMUNAL_MODE_PACKAGES, UserHandle.USER_SYSTEM);
    }

    private void assertDoesIntercept() {
        final Intent intent = new Intent(Intent.ACTION_MAIN);
        assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNotNull();
@@ -175,6 +193,10 @@ public class CommunalManagerServiceTest {
        assertThat(mActivityInterceptorCallback.intercept(buildActivityInfo(intent))).isNull();
    }

    private Intent createPackageIntent(String packageName, @Nullable String action) {
        return new Intent(action, Uri.parse("package:" + packageName));
    }

    @Test
    public void testIntercept_unlocked_communalOff_appNotEnabled_showWhenLockedOff() {
        when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
@@ -263,4 +285,50 @@ public class CommunalManagerServiceTest {
        allowPackages(TEST_PACKAGE_NAME);
        assertDoesIntercept();
    }

    @Test
    public void testUpdateSettings_packageUninstalled() {
        allowPackages("package1,package2");
        assertThat(getAllowedPackages()).isEqualTo("package1,package2");

        mPackageReceiver.onReceive(mContextSpy,
                createPackageIntent("package1", ACTION_PACKAGE_REMOVED));

        assertThat(getAllowedPackages()).isEqualTo("package2");
    }

    @Test
    public void testUpdateSettings_nullAction_doesNothing() {
        allowPackages("package1,package2");
        assertThat(getAllowedPackages()).isEqualTo("package1,package2");

        mPackageReceiver.onReceive(mContextSpy,
                createPackageIntent("package1", null));

        assertThat(getAllowedPackages()).isEqualTo("package1,package2");
    }

    @Test
    public void testUpdateSettings_invalidPackage_doesNothing() {
        allowPackages("package1,package2");
        assertThat(getAllowedPackages()).isEqualTo("package1,package2");

        mPackageReceiver.onReceive(mContextSpy,
                createPackageIntent("package3", ACTION_PACKAGE_REMOVED));

        assertThat(getAllowedPackages()).isEqualTo("package1,package2");
    }

    @Test
    public void testUpdateSettings_onBoot() {
        allowPackages("package1,package2");
        assertThat(getAllowedPackages()).isEqualTo("package1,package2");

        when(CommunalManagerService.isPackageInstalled(eq("package1"), any())).thenReturn(true);
        when(CommunalManagerService.isPackageInstalled(eq("package2"), any())).thenReturn(false);

        mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);

        assertThat(getAllowedPackages()).isEqualTo("package1");
    }
}