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

Commit 8146fd7e authored by Evan Chen's avatar Evan Chen Committed by Android (Google) Code Review
Browse files

Merge "Implment CDM rebinding when binderDied"

parents 5bd1409f 7ca7a14c
Loading
Loading
Loading
Loading
+118 −42
Original line number Diff line number Diff line
@@ -31,9 +31,10 @@ import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.PerUser;
import com.android.internal.util.CollectionUtils;
import com.android.server.companion.presence.CompanionDevicePresenceMonitor;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -69,32 +70,22 @@ public class CompanionApplicationController {

    private static final long REBIND_TIMEOUT = 10 * 1000; // 10 sec

    interface Callback {
        /**
         * @return {@code true} if should schedule rebinding.
         *         {@code false} if we do not need to rebind.
         */
        boolean onCompanionApplicationBindingDied(
                @UserIdInt int userId, @NonNull String packageName);

        /**
         * Callback after timeout for previously scheduled rebind has passed.
         */
        void onRebindCompanionApplicationTimeout(
                @UserIdInt int userId, @NonNull String packageName);
    }

    private final @NonNull Context mContext;
    private final @NonNull Callback mCallback;
    private final @NonNull AssociationStore mAssociationStore;
    private final @NonNull CompanionDevicePresenceMonitor mDevicePresenceMonitor;
    private final @NonNull CompanionServicesRegister mCompanionServicesRegister;

    @GuardedBy("mBoundCompanionApplications")
    private final @NonNull AndroidPackageMap<List<CompanionDeviceServiceConnector>>
            mBoundCompanionApplications;
    @GuardedBy("mScheduledForRebindingCompanionApplications")
    private final @NonNull AndroidPackageMap<Boolean> mScheduledForRebindingCompanionApplications;

    CompanionApplicationController(Context context, Callback callback) {
    CompanionApplicationController(Context context, AssociationStore associationStore,
            CompanionDevicePresenceMonitor companionDevicePresenceMonitor) {
        mContext = context;
        mCallback = callback;
        mAssociationStore = associationStore;
        mDevicePresenceMonitor = companionDevicePresenceMonitor;
        mCompanionServicesRegister = new CompanionServicesRegister();
        mBoundCompanionApplications = new AndroidPackageMap<>();
        mScheduledForRebindingCompanionApplications = new AndroidPackageMap<>();
@@ -125,21 +116,26 @@ public class CompanionApplicationController {
            return;
        }

        final List<CompanionDeviceServiceConnector> serviceConnectors;
        final List<CompanionDeviceServiceConnector> serviceConnectors = new ArrayList<>();
        synchronized (mBoundCompanionApplications) {
            if (mBoundCompanionApplications.containsValueForPackage(userId, packageName)) {
                if (DEBUG) Log.e(TAG, "u" + userId + "/" + packageName + " is ALREADY bound.");
                return;
            }

            serviceConnectors = CollectionUtils.map(companionServices, componentName ->
                            CompanionDeviceServiceConnector.newInstance(mContext, userId,
                                    componentName, isSelfManaged));
            for (int i = 0; i < companionServices.size(); i++) {
                boolean isPrimary = i == 0;
                serviceConnectors.add(CompanionDeviceServiceConnector.newInstance(mContext, userId,
                        companionServices.get(i), isSelfManaged, isPrimary));
            }

            mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors);
        }

        // The first connector in the list is always the primary connector: set a listener to it.
        serviceConnectors.get(0).setListener(this::onPrimaryServiceBindingDied);
        // Set listeners for both Primary and Secondary connectors.
        for (CompanionDeviceServiceConnector serviceConnector : serviceConnectors) {
            serviceConnector.setListener(this::onBinderDied);
        }

        // Now "bind" all the connectors: the primary one and the rest of them.
        for (CompanionDeviceServiceConnector serviceConnector : serviceConnectors) {
@@ -154,9 +150,15 @@ public class CompanionApplicationController {
        if (DEBUG) Log.i(TAG, "unbind() u" + userId + "/" + packageName);

        final List<CompanionDeviceServiceConnector> serviceConnectors;

        synchronized (mBoundCompanionApplications) {
            serviceConnectors = mBoundCompanionApplications.removePackage(userId, packageName);
        }

        synchronized (mScheduledForRebindingCompanionApplications) {
            mScheduledForRebindingCompanionApplications.removePackage(userId, packageName);
        }

        if (serviceConnectors == null) {
            if (DEBUG) {
                Log.e(TAG, "unbindCompanionApplication(): "
@@ -180,24 +182,59 @@ public class CompanionApplicationController {
        }
    }

    private void scheduleRebinding(@UserIdInt int userId, @NonNull String packageName) {
        mScheduledForRebindingCompanionApplications.setValueForPackage(userId, packageName, true);
    private void scheduleRebinding(@UserIdInt int userId, @NonNull String packageName,
            CompanionDeviceServiceConnector serviceConnector) {
        Slog.i(TAG, "scheduleRebinding() " + userId + "/" + packageName);

        if (isRebindingCompanionApplicationScheduled(userId, packageName)) {
            if (DEBUG) {
                Log.i(TAG, "CompanionApplication rebinding has been scheduled, skipping "
                        + serviceConnector.getComponentName());
            }
            return;
        }

        if (serviceConnector.isPrimary()) {
            synchronized (mScheduledForRebindingCompanionApplications) {
                mScheduledForRebindingCompanionApplications.setValueForPackage(
                        userId, packageName, true);
            }
        }

        // Rebinding in 10 seconds.
        Handler.getMain().postDelayed(() ->
                onRebindingCompanionApplicationTimeout(userId, packageName), REBIND_TIMEOUT);
                onRebindingCompanionApplicationTimeout(userId, packageName, serviceConnector),
                REBIND_TIMEOUT);
    }

    boolean isRebindingCompanionApplicationScheduled(
    private boolean isRebindingCompanionApplicationScheduled(
            @UserIdInt int userId, @NonNull String packageName) {
        return mScheduledForRebindingCompanionApplications
                .containsValueForPackage(userId, packageName);
        synchronized (mScheduledForRebindingCompanionApplications) {
            return mScheduledForRebindingCompanionApplications.containsValueForPackage(
                    userId, packageName);
        }
    }

    private void onRebindingCompanionApplicationTimeout(
            @UserIdInt int userId, @NonNull String packageName) {
            @UserIdInt int userId, @NonNull String packageName,
            @NonNull CompanionDeviceServiceConnector serviceConnector) {
        // Re-mark the application is bound.
        if (serviceConnector.isPrimary()) {
            synchronized (mBoundCompanionApplications) {
                if (!mBoundCompanionApplications.containsValueForPackage(userId, packageName)) {
                    List<CompanionDeviceServiceConnector> serviceConnectors =
                            Collections.singletonList(serviceConnector);
                    mBoundCompanionApplications.setValueForPackage(userId, packageName,
                            serviceConnectors);
                }
            }

            synchronized (mScheduledForRebindingCompanionApplications) {
                mScheduledForRebindingCompanionApplications.removePackage(userId, packageName);
            }
        }

        mCallback.onRebindCompanionApplicationTimeout(userId, packageName);
        serviceConnector.connect();
    }

    void notifyCompanionApplicationDeviceAppeared(AssociationInfo association) {
@@ -266,19 +303,27 @@ public class CompanionApplicationController {
        }
    }

    private void onPrimaryServiceBindingDied(@UserIdInt int userId, @NonNull String packageName) {
        if (DEBUG) Log.i(TAG, "onPrimaryServiceBindingDied() u" + userId + "/" + packageName);
    /**
     * Rebinding for Self-Managed secondary services OR Non-Self-Managed services.
     */
    private void onBinderDied(@UserIdInt int userId, @NonNull String packageName,
            @NonNull CompanionDeviceServiceConnector serviceConnector) {

        boolean isPrimary = serviceConnector.isPrimary();
        Slog.i(TAG, "onBinderDied() u" + userId + "/" + packageName + " isPrimary: " + isPrimary);

        // First: mark as NOT bound.
        // First: Only mark not BOUND for primary service.
        synchronized (mBoundCompanionApplications) {
            if (serviceConnector.isPrimary()) {
                mBoundCompanionApplications.removePackage(userId, packageName);
            }
        }

        // Second: schedule rebinding if needed.
        final boolean shouldScheduleRebind = shouldScheduleRebind(userId, packageName, isPrimary);

        // Second: invoke callback, schedule rebinding if needed.
        final boolean shouldScheduleRebind =
                mCallback.onCompanionApplicationBindingDied(userId, packageName);
        if (shouldScheduleRebind) {
            scheduleRebinding(userId, packageName);
            scheduleRebinding(userId, packageName, serviceConnector);
        }
    }

@@ -291,6 +336,37 @@ public class CompanionApplicationController {
        return connectors != null ? connectors.get(0) : null;
    }

    private boolean shouldScheduleRebind(int userId, String packageName, boolean isPrimary) {
        // Make sure do not schedule rebind for the case ServiceConnector still gets callback after
        // app is uninstalled.
        boolean stillAssociated = false;

        for (AssociationInfo ai :
                mAssociationStore.getAssociationsForPackage(userId, packageName)) {
            final int associationId = ai.getId();
            stillAssociated = true;

            if (ai.isSelfManaged()) {
                // Do not rebind if primary one is died for selfManaged application.
                if (isPrimary
                        && mDevicePresenceMonitor.isDevicePresent(associationId)) {
                    mDevicePresenceMonitor.onSelfManagedDeviceReporterBinderDied(associationId);
                    return false;
                }
                // Do not rebind if both primary and secondary services are died for
                // selfManaged application.
                if (!isCompanionApplicationBound(userId, packageName)) {
                    return false;
                }
            } else if (ai.isNotifyOnDeviceNearby()) {
                // Always rebind for non-selfManaged devices.
                return true;
            }
        }

        return stillAssociated;
    }

    private class CompanionServicesRegister extends PerUser<Map<String, List<ComponentName>>> {
        @Override
        public synchronized @NonNull Map<String, List<ComponentName>> forUser(
+1 −33
Original line number Diff line number Diff line
@@ -233,7 +233,7 @@ public class CompanionDeviceManagerService extends SystemService {
        mAssociationRequestsProcessor = new AssociationRequestsProcessor(
                /* cdmService */this, mAssociationStore);
        mCompanionAppController = new CompanionApplicationController(
                context, mApplicationControllerCallback);
                context, mAssociationStore, mDevicePresenceMonitor);
        mTransportManager = new CompanionTransportManager(context);
        mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mAssociationStore,
                mSystemDataTransferRequestStore, mTransportManager);
@@ -387,25 +387,6 @@ public class CompanionDeviceManagerService extends SystemService {
        mCompanionAppController.unbindCompanionApplication(userId, packageName);
    }

    private boolean onCompanionApplicationBindingDiedInternal(
            @UserIdInt int userId, @NonNull String packageName) {
        for (AssociationInfo ai :
                mAssociationStore.getAssociationsForPackage(userId, packageName)) {
            final int associationId = ai.getId();
            if (ai.isSelfManaged()
                    && mDevicePresenceMonitor.isDevicePresent(associationId)) {
                mDevicePresenceMonitor.onSelfManagedDeviceReporterBinderDied(associationId);
            }
        }
        // TODO(b/218613015): implement.
        return false;
    }

    private void onRebindCompanionApplicationTimeoutInternal(
            @UserIdInt int userId, @NonNull String packageName) {
        // TODO(b/218613015): implement.
    }

    /**
     * @return whether the package should be bound (i.e. at least one of the devices associated with
     *         the package is currently present).
@@ -1287,19 +1268,6 @@ public class CompanionDeviceManagerService extends SystemService {
        }
    };

    private final CompanionApplicationController.Callback mApplicationControllerCallback =
            new CompanionApplicationController.Callback() {
        @Override
        public boolean onCompanionApplicationBindingDied(int userId, @NonNull String packageName) {
            return onCompanionApplicationBindingDiedInternal(userId, packageName);
        }

        @Override
        public void onRebindCompanionApplicationTimeout(int userId, @NonNull String packageName) {
            onRebindCompanionApplicationTimeoutInternal(userId, packageName);
        }
    };

    private final PackageMonitor mPackageMonitor = new PackageMonitor() {
        @Override
        public void onPackageRemoved(String packageName, int uid) {
+18 −15
Original line number Diff line number Diff line
@@ -48,7 +48,8 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe

    /** Listener for changes to the state of the {@link CompanionDeviceServiceConnector}  */
    interface Listener {
        void onBindingDied(@UserIdInt int userId, @NonNull String packageName);
        void onBindingDied(@UserIdInt int userId, @NonNull String packageName,
                @NonNull CompanionDeviceServiceConnector serviceConnector);
    }

    private final @UserIdInt int mUserId;
@@ -57,6 +58,7 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe
    // installs a listener to the primary ServiceConnector), hence we should always null-check the
    // reference before calling on it.
    private @Nullable Listener mListener;
    private boolean mIsPrimary;

    /**
     * Create a CompanionDeviceServiceConnector instance.
@@ -74,17 +76,20 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe
     * service importance level should be higher than 125.
     */
    static CompanionDeviceServiceConnector newInstance(@NonNull Context context,
            @UserIdInt int userId, @NonNull ComponentName componentName, boolean isSelfManaged) {
            @UserIdInt int userId, @NonNull ComponentName componentName, boolean isSelfManaged,
            boolean isPrimary) {
        final int bindingFlags = isSelfManaged ? BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE
                : BIND_ALMOST_PERCEPTIBLE;
        return new CompanionDeviceServiceConnector(context, userId, componentName, bindingFlags);
        return new CompanionDeviceServiceConnector(
                context, userId, componentName, bindingFlags, isPrimary);
    }

    private CompanionDeviceServiceConnector(@NonNull Context context, @UserIdInt int userId,
            @NonNull ComponentName componentName, int bindingFlags) {
            @NonNull ComponentName componentName, int bindingFlags, boolean isPrimary) {
        super(context, buildIntent(componentName), bindingFlags, userId, null);
        mUserId = userId;
        mComponentName = componentName;
        mIsPrimary = isPrimary;
    }

    void setListener(@Nullable Listener listener) {
@@ -110,6 +115,14 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe
        post(it -> unbind());
    }

    boolean isPrimary() {
        return mIsPrimary;
    }

    ComponentName getComponentName() {
        return mComponentName;
    }

    @Override
    protected void onServiceConnectionStatusChanged(
            @NonNull ICompanionDeviceService service, boolean isConnected) {
@@ -119,16 +132,6 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe
        }
    }

    // This method is only called when app is force-closed via settings,
    // but does not handle crashes. Binder death should be handled in #binderDied()
    @Override
    public void onBindingDied(@NonNull ComponentName name) {
        // IMPORTANT: call super! this will also invoke binderDied()
        super.onBindingDied(name);

        if (DEBUG) Log.d(TAG, "onBindingDied() " + mComponentName.toShortString());
    }

    @Override
    public void binderDied() {
        super.binderDied();
@@ -137,7 +140,7 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe

        // Handle primary process being killed
        if (mListener != null) {
            mListener.onBindingDied(mUserId, mComponentName.getPackageName());
            mListener.onBindingDied(mUserId, mComponentName.getPackageName(), this);
        }
    }