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

Commit f33bb495 authored by Cintia Martins's avatar Cintia Martins Committed by Android (Google) Code Review
Browse files

Merge "Refactor appBindingService to Allow Binding to Multiple Supervision Apps" into main

parents 241f6b04 e9a23f68
Loading
Loading
Loading
Loading
+109 −29
Original line number Diff line number Diff line
@@ -32,7 +32,6 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.IInterface;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -48,11 +47,13 @@ import com.android.server.SystemService;
import com.android.server.appbinding.finders.AppServiceFinder;
import com.android.server.appbinding.finders.CarrierMessagingClientServiceFinder;
import com.android.server.appbinding.finders.SupervisionAppServiceFinder;
import com.android.server.utils.Slogf;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

/**
@@ -143,8 +144,8 @@ public class AppBindingService extends Binder {
    }

    /** Get the list of services bound to a specific finder class. */
    public <T extends IInterface>  List<AppServiceConnection> getAppServiceConnections(
            Class<? extends AppServiceFinder<?, T>> appServiceFinderClass, int userId) {
    public List<AppServiceConnection> getAppServiceConnections(
            Class<? extends AppServiceFinder<?, ?>> appServiceFinderClass, int userId) {
        List<AppServiceConnection> serviceConnections = new ArrayList<>();
        synchronized (mLock) {
            for (int i = 0; i < mApps.size(); i++) {
@@ -152,10 +153,7 @@ public class AppBindingService extends Binder {
                if (app.getClass() != appServiceFinderClass) {
                    continue;
                }
                AppServiceConnection conn = getBoundConnectionLocked(userId, app);
                if (conn != null) {
                    serviceConnections.add(conn);
                }
                serviceConnections.addAll(getBoundConnectionsLocked(userId, app));
            }
        }
        return serviceConnections;
@@ -163,23 +161,33 @@ public class AppBindingService extends Binder {

    /** Get the connection bound to a specific finder. If the connection does not
     * already exist, create one.  */
    private AppServiceConnection getBoundConnectionLocked(int userId, AppServiceFinder app) {
        AppServiceConnection conn = findConnectionLock(userId, app);
    private List<AppServiceConnection> getBoundConnectionsLocked(int userId, AppServiceFinder app) {
        Set<String> targetPackages = app.getTargetPackages(userId);
        List<AppServiceConnection> connections = new ArrayList<>();
        for (String targetPackage : targetPackages) {
            AppServiceConnection conn = findConnectionLock(userId, app, targetPackage);
            if (conn == null) {
            final ServiceInfo service = app.findService(userId, mIPackageManager,
                    mConstants);
            if (service==null) {
                Slog.d(TAG, "Can't create connection with app " +
                        app.getTargetPackage(userId) + ". Service is null.");
                return null;
            }
            conn = new AppServiceConnection(
                    mContext, userId, mConstants, mHandler,
                    app, service.getComponentName());
        }
                final ServiceInfo service =
                        app.findService(userId, mIPackageManager, mConstants, targetPackage);
                if (service != null) {
                    conn =
                            new AppServiceConnection(
                                    mContext,
                                    userId,
                                    mConstants,
                                    mHandler,
                                    app,
                                    targetPackage,
                                    service.getComponentName());
                    mConnections.add(conn);
                    conn.bind();
        return conn;
                }
            }
            if (conn != null) {
                connections.add(conn);
            }
        }
        return connections;
    }

    private AppBindingService(Injector injector, Context context) {
@@ -249,6 +257,8 @@ public class AppBindingService extends Binder {
        final IntentFilter packageFilter = new IntentFilter();
        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        packageFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
        packageFilter.addDataScheme("package");

        mContext.registerReceiverAsUser(mPackageUserMonitor, UserHandle.ALL,
@@ -329,6 +339,12 @@ public class AppBindingService extends Binder {
                case Intent.ACTION_PACKAGE_CHANGED:
                    handlePackageAddedReplacing(packageName, userId);
                    break;
                case Intent.ACTION_PACKAGE_REMOVED:
                case Intent.ACTION_PACKAGE_FULLY_REMOVED:
                    if (!replacing) {
                        onAppRemoved(userId, packageName);
                    }
                    break;
            }
        }
    };
@@ -386,6 +402,22 @@ public class AppBindingService extends Binder {
        }
    }

    private void onAppRemoved(int userId, String packageName) {
        if (!Flags.enableSupervisionAppService()) {
            return;
        }

        if (DEBUG) {
            Slogf.d(TAG, "onAppRemoved: u%s %s", userId, packageName);
        }
        synchronized (mLock) {
            final AppServiceFinder finder = findFinderLocked(userId, packageName);
            if (finder != null) {
                bindServicesLocked(userId, finder, "package removed");
            }
        }
    }

    /**
     * Called when a target package changes; e.g. when the user changes the default SMS app.
     */
@@ -405,16 +437,21 @@ public class AppBindingService extends Binder {
    private AppServiceFinder findFinderLocked(int userId, @NonNull String packageName) {
        for (int i = 0; i < mApps.size(); i++) {
            final AppServiceFinder app = mApps.get(i);
            if (Flags.enableSupervisionAppService()) {
                if (app.getTargetPackages(userId).contains(packageName)) {
                    return app;
                }
            } else {
                if (packageName.equals(app.getTargetPackage(userId))) {
                    return app;
                }
            }
        }
        return null;
    }

    @Nullable
    private AppServiceConnection findConnectionLock(
            int userId, @NonNull AppServiceFinder target) {
    private AppServiceConnection findConnectionLock(int userId, @NonNull AppServiceFinder target) {
        for (int i = 0; i < mConnections.size(); i++) {
            final AppServiceConnection conn = mConnections.get(i);
            if ((conn.getUserId() == userId) && (conn.getFinder() == target)) {
@@ -424,6 +461,20 @@ public class AppBindingService extends Binder {
        return null;
    }

    @Nullable
    private AppServiceConnection findConnectionLock(
            int userId, @NonNull AppServiceFinder target, String targetPackage) {
        for (int i = 0; i < mConnections.size(); i++) {
            final AppServiceConnection conn = mConnections.get(i);
            if ((conn.getUserId() == userId)
                    && (conn.getFinder() == target)
                    && conn.getPackageName().equals(targetPackage)) {
                return conn;
            }
        }
        return null;
    }

    private void handlePackageAddedReplacing(String packageName, int userId) {
        if (DEBUG) {
            Slog.d(TAG, "handlePackageAddedReplacing: u" + userId + " " + packageName);
@@ -456,7 +507,37 @@ public class AppBindingService extends Binder {
            if (target != null && target != app) {
                continue;
            }
            if (!Flags.enableSupervisionAppService()) {
                bindServicesForFinderLocked(userId, target, reasonForLog, app); // old code
                continue;
            }
            // Disconnect from existing binding.
            unbindServicesLocked(userId, app, reasonForLog);

            final List<ServiceInfo> services =
                    app.findServices(userId, mIPackageManager, mConstants);
            if (services==null || services.isEmpty()) {
                continue;
            }
            for (ServiceInfo service : services) {
                if (DEBUG) {
                    Slog.d(TAG, "bindServicesLocked: u" + userId + " " + app.getAppDescription()
                            + " binding " + service.getComponentName() + " for " + reasonForLog);
                }
                if (service == null) {
                    continue;
                }
                final AppServiceConnection conn =
                        new AppServiceConnection(mContext, userId, mConstants, mHandler,
                                app, service.packageName, service.getComponentName());
                mConnections.add(conn);
                conn.bind();
            }
        }
    }

    private void bindServicesForFinderLocked(int userId, @Nullable AppServiceFinder target,
            @NonNull String reasonForLog, AppServiceFinder app) {
            // Disconnect from existing binding.
            final AppServiceConnection existingConn = findConnectionLock(userId, app);
            if (existingConn != null) {
@@ -465,7 +546,7 @@ public class AppBindingService extends Binder {

            final ServiceInfo service = app.findService(userId, mIPackageManager, mConstants);
            if (service == null) {
                continue;
                return;
            }
            if (DEBUG) {
                Slog.d(TAG, "bindServicesLocked: u" + userId + " " + app.getAppDescription()
@@ -473,11 +554,10 @@ public class AppBindingService extends Binder {
            }
            final AppServiceConnection conn =
                    new AppServiceConnection(mContext, userId, mConstants, mHandler,
                            app, service.getComponentName());
                            app, service.packageName, service.getComponentName());
            mConnections.add(conn);
            conn.bind();
    }
    }

    private void unbindServicesLocked(int userId, @Nullable AppServiceFinder target,
            @NonNull String reasonForLog) {
+9 −3
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ public class AppServiceConnection extends PersistentConnection<IInterface> {
    public static final String TAG = "AppServiceConnection";
    private final AppBindingConstants mConstants;
    private final AppServiceFinder mFinder;
    private final String mPackageName;

    /**
     * Listener for connection status updates
@@ -54,7 +55,7 @@ public class AppServiceConnection extends PersistentConnection<IInterface> {
            new CopyOnWriteArrayList<>();

    AppServiceConnection(Context context, int userId, AppBindingConstants constants,
            Handler handler, AppServiceFinder finder,
            Handler handler, AppServiceFinder finder, String packageName,
            @NonNull ComponentName componentName) {
        super(TAG, context, handler, userId, componentName,
                constants.SERVICE_RECONNECT_BACKOFF_SEC,
@@ -63,6 +64,7 @@ public class AppServiceConnection extends PersistentConnection<IInterface> {
                constants.SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
        mFinder = finder;
        mConstants = constants;
        mPackageName = packageName;
    }

    @Override
@@ -76,8 +78,8 @@ public class AppServiceConnection extends PersistentConnection<IInterface> {

        if (service != null) {
            // Notify all listeners.
            Slogf.d(TAG, "Service for %s is connected. Notifying listeners.",
                    getComponentName());
            Slogf.d(TAG, "Service for %s is connected. Notifying %s listeners.",
                    getComponentName(), mConnectionListeners.size());
            for (ConnectionStatusListener listener : mConnectionListeners) {
                listener.onConnected(this, service);
            }
@@ -91,6 +93,10 @@ public class AppServiceConnection extends PersistentConnection<IInterface> {
        return mFinder;
    }

    public String getPackageName() {
        return mPackageName;
    }

    /**
     * Adds a listener to be notified of connection changes.
     * If service is already connected, notify immediately.
+156 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.appbinding.finders;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.supervision.flags.Flags;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.ServiceInfo;
@@ -34,6 +35,11 @@ import com.android.server.appbinding.AppBindingService;
import com.android.server.appbinding.AppBindingUtils;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;

/**
@@ -53,14 +59,37 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten
    private final Object mLock = new Object();

    @GuardedBy("mLock")
    @Deprecated
    private final SparseArray<String> mTargetPackages = new SparseArray(4);

    @GuardedBy("mLock")
    @Deprecated
    private final SparseArray<ServiceInfo> mTargetServices = new SparseArray(4);

    @GuardedBy("mLock")
    @Deprecated
    private final SparseArray<String> mLastMessages = new SparseArray(4);

    @GuardedBy("mLock")
    private final SparseArray<HashMap<String, TargetServiceInfo>> mServiceInfos =
            new SparseArray(4);

    /**
     * Helper class to store the ServiceInfo and the last message related to it.
     */
    private static class TargetServiceInfo {
        @Nullable
        public final ServiceInfo serviceInfo;
        @Nullable
        public final String lastMessage;

        TargetServiceInfo(@Nullable ServiceInfo serviceInfo, @Nullable String lastMessage) {
            this.serviceInfo = serviceInfo;
            this.lastMessage = lastMessage;
        }
    }


    public AppServiceFinder(Context context,
            BiConsumer<AppServiceFinder, Integer> listener,
            Handler callbackHandler) {
@@ -88,6 +117,7 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten
            mTargetPackages.delete(userId);
            mTargetServices.delete(userId);
            mLastMessages.delete(userId);
            mServiceInfos.delete(userId);
        }
    }

@@ -95,6 +125,7 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten
     * Find the target service from the target app on a given user.
     */
    @Nullable
    @Deprecated
    public final ServiceInfo findService(int userId, IPackageManager ipm,
            AppBindingConstants constants) {
        synchronized (mLock) {
@@ -155,6 +186,90 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten
        }
    }

    /**
     * Find the target service from the target app on a given user.
     */
    @Nullable
    public final ServiceInfo findService(int userId, IPackageManager ipm,
            AppBindingConstants constants, String targetPackage) {
        synchronized (mLock) {
            HashMap<String, TargetServiceInfo> currServiceInfo = mServiceInfos.get(userId);
            if (DEBUG) {
                Slog.d(TAG, getAppDescription() + " package=" + targetPackage);
            }

            if (!isEnabled(constants, userId)) {
                final String message = "feature disabled";
                currServiceInfo.put(targetPackage, new TargetServiceInfo(null, message));
                Slog.i(TAG, getAppDescription() + " " + message);
                return null;
            }

            final StringBuilder errorMessage = new StringBuilder();
            final ServiceInfo service = AppBindingUtils.findService(
                    targetPackage,
                    userId,
                    getServiceAction(),
                    getServicePermission(),
                    getServiceClass(),
                    ipm,
                    errorMessage);

            if (service == null) {
                final String message = errorMessage.toString();
                currServiceInfo.put(targetPackage, new TargetServiceInfo(null, message));
                if (DEBUG) {
                    Slog.w(TAG, getAppDescription() + " package " + targetPackage + " u" + userId
                            + " " + message);
                }
                return null;
            }
            final String error = validateService(service);
            if (error != null) {
                currServiceInfo.put(targetPackage, new TargetServiceInfo(null, error));
                Log.e(TAG, error);
                return null;
            }

            currServiceInfo.put(targetPackage, new TargetServiceInfo(service,
                    "Valid service found"));
            return service;
        }
    }

    /**
     * Find the list of target service from the target apps on a given user.
     */
    @Nullable
    public final List<ServiceInfo> findServices(int userId, IPackageManager ipm,
            AppBindingConstants constants) {
        final Set<String> targetPackages = getTargetPackages(userId);
        synchronized (mLock) {
            mServiceInfos.put(userId, new HashMap<String, TargetServiceInfo>());

            if (DEBUG) {
                Slog.d(TAG, getAppDescription() + " packages=" + targetPackages);
            }

            if (!isEnabled(constants, userId) || targetPackages.isEmpty()) {
                final String message = (!isEnabled(constants, userId)) ? "feature disabled"
                        : "Target packages not found";
                mServiceInfos.get(userId).put(null, new TargetServiceInfo(null, message));
                Slog.w(TAG, getAppDescription() + " u" + userId + " " + message);
                return new ArrayList<>();
            }
        }

        List<ServiceInfo> services = new ArrayList<>();
        for (String targetPackage: targetPackages) {
            ServiceInfo service = findService(userId, ipm, constants, targetPackage);
            if (service != null) {
                services.add(service);
            }
        }
        return services;
    }

    protected abstract Class<TServiceType> getServiceClass();

    /**
@@ -166,8 +281,14 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten
     * @return the target package on a given user.
     */
    @Nullable
    @Deprecated
    public abstract String getTargetPackage(int userId);

    /**
     * @return the target packages on a given user.
     */
    public abstract Set<String> getTargetPackages(int userId);

    /**
     * @return the intent action that identifies the target service in the target app.
     */
@@ -199,6 +320,31 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten
        pw.println();

        synchronized (mLock) {
            for (int i = 0; i < mServiceInfos.size(); i++) {
                final int userId = mServiceInfos.keyAt(i);
                pw.print(prefix);
                pw.print("  User: ");
                pw.print(userId);
                pw.println();
                for (String targetPackage : mServiceInfos.get(userId).keySet()) {
                    pw.print(prefix);
                    pw.print("    Package: ");
                    pw.print(targetPackage);
                    pw.println();

                    pw.print(prefix);
                    pw.print("    Service: ");
                    pw.print(mServiceInfos.get(userId).get(targetPackage).serviceInfo);
                    pw.println();

                    pw.print(prefix);
                    pw.print("    Message: ");
                    pw.print(mServiceInfos.get(userId).get(targetPackage).lastMessage);
                    pw.println();
                }
                pw.println();
            }

            for (int i = 0; i < mTargetPackages.size(); i++) {
                final int userId = mTargetPackages.keyAt(i);
                pw.print(prefix);
@@ -227,6 +373,16 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten
    /** Dumpys support */
    public void dumpSimple(PrintWriter pw) {
        synchronized (mLock) {
            for (int i = 0; i < mServiceInfos.size(); i++) {
                final int userId = mServiceInfos.keyAt(i);
                pw.println(String.format("finder,%s,%s", getAppDescription(), userId));
                for (String targetPackage : mServiceInfos.get(userId).keySet()) {
                    pw.println(String.format("finder,%s,%s,%s,%s,%s",
                            getAppDescription(), userId, targetPackage,
                            mServiceInfos.get(userId).get(targetPackage).serviceInfo,
                            mServiceInfos.get(userId).get(targetPackage).lastMessage));
                }
            }
            for (int i = 0; i < mTargetPackages.size(); i++) {
                final int userId = mTargetPackages.keyAt(i);
                pw.print("finder,");
+19 −0
Original line number Diff line number Diff line
@@ -35,6 +35,9 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.util.CollectionUtils;
import com.android.server.appbinding.AppBindingConstants;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.BiConsumer;

/**
@@ -85,6 +88,7 @@ public class CarrierMessagingClientServiceFinder
    }

    @Override
    @Deprecated
    public String getTargetPackage(int userId) {
        final String ret = CollectionUtils.firstOrNull(mRoleManager.getRoleHoldersAsUser(
                RoleManager.ROLE_SMS, UserHandle.of(userId)));
@@ -96,6 +100,21 @@ public class CarrierMessagingClientServiceFinder
        return ret;
    }

    @Override
    public Set<String> getTargetPackages(int userId) {
        String targetPackage = CollectionUtils.firstOrNull(mRoleManager.getRoleHoldersAsUser(
                RoleManager.ROLE_SMS, UserHandle.of(userId)));

        if (DEBUG) {
            Slog.d(TAG, "getTargetPackages()=" + targetPackage);
        }

        if (targetPackage == null) {
            return Collections.emptySet();
        }
        return new HashSet<>(Collections.singletonList(targetPackage));
    }

    @Override
    public void startMonitoring() {
        mRoleManager.addOnRoleHoldersChangedListenerAsUser(
+13 −0
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@ import com.android.internal.util.CollectionUtils;
import com.android.server.LocalServices;
import com.android.server.appbinding.AppBindingConstants;

import java.util.HashSet;
import java.util.Set;
import java.util.function.BiConsumer;

/** Finds the @{link SupervisionAppService} implementation within the supervision app. */
@@ -77,6 +79,7 @@ public class SupervisionAppServiceFinder

    @Nullable
    @Override
    @Deprecated
    public String getTargetPackage(int userId) {
        final String ret =
                CollectionUtils.firstOrNull(
@@ -85,6 +88,16 @@ public class SupervisionAppServiceFinder
        return ret;
    }

    @Override
    public Set<String> getTargetPackages(int userId) {
        final Set<String> ret = new HashSet<>();
        ret.addAll(mRoleManager.getRoleHoldersAsUser(
                RoleManager.ROLE_SYSTEM_SUPERVISION, UserHandle.of(userId)));
        ret.addAll(mRoleManager.getRoleHoldersAsUser(
                RoleManager.ROLE_SUPERVISION, UserHandle.of(userId)));
        return ret;
    }

    @NonNull
    @Override
    protected String getServiceAction() {
Loading