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

Commit bd4a9f58 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Cleanup communal mode shared app logic."

parents f8205a38 70303b05
Loading
Loading
Loading
Loading
+1 −25
Original line number Diff line number Diff line
@@ -23,9 +23,6 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.Overridable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.RemoteException;
@@ -46,31 +43,10 @@ public final class CommunalManager {
    private final ICommunalManager mService;
    private final ArrayMap<CommunalModeListener, ICommunalModeListener> mCommunalModeListeners;

    /**
     * This change id is used to annotate packages which can run in communal mode by default,
     * without requiring user opt-in.
     *
     * @hide
     */
    @ChangeId
    @Overridable
    @Disabled
    public static final long ALLOW_COMMUNAL_MODE_BY_DEFAULT = 203673428L;

    /**
     * This change id is used to annotate packages which are allowed to run in communal mode.
     *
     * @hide
     */
    @ChangeId
    @Overridable
    @Disabled
    public static final long ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT = 200324021L;

    /** @hide */
    public CommunalManager(ICommunalManager service) {
        mService = service;
        mCommunalModeListeners = new ArrayMap<CommunalModeListener, ICommunalModeListener>();
        mCommunalModeListeners = new ArrayMap<>();
    }

    /**
+0 −7
Original line number Diff line number Diff line
@@ -10489,13 +10489,6 @@ public final class Settings {
         */
        public static final String COMMUNAL_MODE_ENABLED = "communal_mode_enabled";
        /**
         * An array of all the packages which have been enabled for hub mode by the user.
         *
         * @hide
         */
        public static final String COMMUNAL_MODE_PACKAGES = "communal_mode_packages";
        /**
         * An array of SSIDs of Wi-Fi networks that, when connected, are considered safe to enable
         * the communal mode.
+0 −239
Original line number Diff line number Diff line
@@ -16,117 +16,34 @@

package com.android.server.communal;

import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
import static android.app.communal.CommunalManager.ALLOW_COMMUNAL_MODE_BY_DEFAULT;
import static android.app.communal.CommunalManager.ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT;
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.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.communal.ICommunalManager;
import android.app.communal.ICommunalModeListener;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
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.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.LaunchAfterAuthenticationActivity;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.wm.ActivityInterceptorCallback;
import com.android.server.wm.ActivityTaskManagerInternal;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * System service for handling Communal Mode state.
 */
public final class CommunalManagerService extends SystemService {
    private static final String TAG = CommunalManagerService.class.getSimpleName();
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    private static final String DELIMITER = ",";
    private final Context mContext;
    private final ActivityTaskManagerInternal mAtmInternal;
    private final KeyguardManager mKeyguardManager;
    private final AtomicBoolean mCommunalViewIsShowing = new AtomicBoolean(false);
    private final BinderService mBinderService;
    private final PackageReceiver mPackageReceiver;
    private final PackageManager mPackageManager;
    private final DreamManagerInternal mDreamManagerInternal;
    private final RemoteCallbackList<ICommunalModeListener> mListeners =
            new RemoteCallbackList<>();

    private final ActivityInterceptorCallback mActivityInterceptorCallback =
            new ActivityInterceptorCallback() {
                @Nullable
                @Override
                public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
                    if (!shouldIntercept(info.aInfo)) {
                        if (DEBUG) {
                            Slog.d(TAG, "Activity allowed, not intercepting: "
                                    + info.aInfo.getComponentName());
                        }
                        return null;
                    }

                    final IIntentSender target = mAtmInternal.getIntentSender(
                            INTENT_SENDER_ACTIVITY,
                            info.callingPackage,
                            info.callingFeatureId,
                            info.callingUid,
                            info.userId,
                            /* token= */null,
                            /* resultWho= */ null,
                            /* requestCode= */ 0,
                            new Intent[]{info.intent},
                            new String[]{info.resolvedType},
                            PendingIntent.FLAG_IMMUTABLE,
                            /* bOptions= */ null);

                    return new ActivityInterceptResult(
                            LaunchAfterAuthenticationActivity.createLaunchAfterAuthenticationIntent(
                                    new IntentSender(target)),
                            info.checkedOptions);

                }
            };

    public CommunalManagerService(Context context) {
        super(context);
        mContext = context;
        mPackageManager = mContext.getPackageManager();
        mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
        mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
        mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
        mBinderService = new BinderService();
        mPackageReceiver = new PackageReceiver(mContext);
    }

    @VisibleForTesting
@@ -139,116 +56,6 @@ public final class CommunalManagerService extends SystemService {
        publishBinderService(Context.COMMUNAL_SERVICE, mBinderService);
    }

    @Override
    public void onBootPhase(int phase) {
        if (phase != SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) return;
        mAtmInternal.registerActivityStartInterceptor(
                COMMUNAL_MODE_ORDERED_ID,
                mActivityInterceptorCallback);
        mPackageReceiver.register();
        removeUninstalledPackagesFromSettings();
    }

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

    private Set<String> getUserEnabledApps() {
        final String encodedApps = Settings.Secure.getStringForUser(
                mContext.getContentResolver(),
                Settings.Secure.COMMUNAL_MODE_PACKAGES,
                UserHandle.USER_SYSTEM);

        return TextUtils.isEmpty(encodedApps)
                ? Collections.emptySet()
                : 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 (isActiveDream(appInfo) || isChangeEnabled(ALLOW_COMMUNAL_MODE_BY_DEFAULT, appInfo)) {
            return true;
        }

        if (!isChangeEnabled(ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT, appInfo)) {
            if (DEBUG) Slog.d(TAG, "App is not allowlisted: " + appInfo.packageName);
            return false;
        }

        if (!getUserEnabledApps().contains(appInfo.packageName)) {
            if (DEBUG) Slog.d(TAG, "App does not have user consent: " + appInfo.packageName);
            return false;
        }

        return true;
    }

    private boolean isActiveDream(ApplicationInfo appInfo) {
        final ComponentName activeDream = mDreamManagerInternal.getActiveDreamComponent(
                /* doze= */ false);
        final ComponentName activeDoze = mDreamManagerInternal.getActiveDreamComponent(
                /* doze= */ true);
        return isFromPackage(activeDream, appInfo) || isFromPackage(activeDoze, appInfo);
    }

    private static boolean isFromPackage(ComponentName componentName, ApplicationInfo appInfo) {
        if (componentName == null) return false;
        return TextUtils.equals(appInfo.packageName, componentName.getPackageName());
    }

    private static boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
        return CompatChanges.isChangeEnabled(changeId, appInfo.packageName, UserHandle.SYSTEM);
    }

    private boolean shouldIntercept(ActivityInfo activityInfo) {
        if (!mCommunalViewIsShowing.get() || !mKeyguardManager.isKeyguardLocked()) return false;
        ApplicationInfo appInfo = activityInfo.applicationInfo;
        // Dreams are allowed to show, and don't require the showWhenLocked attribute.
        if (isActiveDream(appInfo)) return false;

        // If the activity doesn't have showWhenLocked enabled, disallow the activity.
        final boolean showWhenLocked =
                (activityInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0;
        if (!showWhenLocked) {
            if (DEBUG) {
                Slog.d(TAG, "Activity does not contain showWhenLocked attribute: "
                        + activityInfo.getComponentName());
            }
            return true;
        }

        return !isAppAllowed(appInfo);
    }

    private void dispatchCommunalMode(boolean isShowing) {
        synchronized (mListeners) {
            int i = mListeners.beginBroadcast();
@@ -319,50 +126,4 @@ public final class CommunalManagerService extends SystemService {
            }
        }
    }

    /**
     * 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,
                    Context.RECEIVER_EXPORTED_UNAUDITED);
        }

        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);
            }
        }
    }
}
+0 −267

File changed.

Preview size limit exceeded, changes collapsed.