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

Commit 0f904301 authored by Evan Severson's avatar Evan Severson
Browse files

Simplify SelectionToolbarManagerService

This service is simply a passthrough to the render service, complex
frameworks to manage events such as user lifecycles are not needed to
be implemented for such a simple functionality.

We also only reference the render service in the system user now since
either process, system ui or android:ui, should be run as the system
user.

Test: CtsWidgetTestCases + Manual
Bug: 363318732
Flag: android.permission.flags.system_selection_toolbar_enabled

Change-Id: I5925387d42a54058ce21f47554378564185e06b6
parent 59177e64
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -2993,6 +2993,11 @@ public final class SystemServer implements Dumpable {
            Slog.d(TAG, "TranslationService not defined by OEM");
        }

        // NOTE: ClipboardService depends on ContentCapture and Autofill
        t.traceBegin("StartClipboardService");
        mSystemServiceManager.startService(ClipboardService.class);
        t.traceEnd();

        if (!isTv) {
            // Selection toolbar service
            t.traceBegin("StartSelectionToolbarManagerService");
@@ -3000,11 +3005,6 @@ public final class SystemServer implements Dumpable {
            t.traceEnd();
        }

        // NOTE: ClipboardService depends on ContentCapture and Autofill
        t.traceBegin("StartClipboardService");
        mSystemServiceManager.startService(ClipboardService.class);
        t.traceEnd();

        t.traceBegin("AppServiceManager");
        mSystemServiceManager.startService(AppBindingService.Lifecycle.class);
        t.traceEnd();
+0 −84
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.selectiontoolbar;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.service.selectiontoolbar.ISelectionToolbarRenderService;
import android.service.selectiontoolbar.SelectionToolbarRenderService;
import android.util.Slog;
import android.view.selectiontoolbar.ISelectionToolbarCallback;
import android.view.selectiontoolbar.ShowInfo;

import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.infra.ServiceConnector;

final class RemoteSelectionToolbarRenderService extends
        ServiceConnector.Impl<ISelectionToolbarRenderService> {
    private static final String TAG = "RemoteSelectionToolbarRenderService";

    private static final long TIMEOUT_IDLE_UNBIND_MS =
            AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;

    private final ComponentName mComponentName;
    private final IBinder mRemoteCallback;

    RemoteSelectionToolbarRenderService(Context context, ComponentName serviceName, int userId,
            IBinder callback) {
        super(context, new Intent(SelectionToolbarRenderService.SERVICE_INTERFACE).setComponent(
                serviceName), 0, userId, ISelectionToolbarRenderService.Stub::asInterface);
        mComponentName = serviceName;
        mRemoteCallback = callback;
        // Bind right away.
        connect();
    }

    @Override // from AbstractRemoteService
    protected long getAutoDisconnectTimeoutMs() {
        return TIMEOUT_IDLE_UNBIND_MS;
    }

    @Override // from ServiceConnector.Impl
    protected void onServiceConnectionStatusChanged(ISelectionToolbarRenderService service,
            boolean connected) {
        try {
            if (connected) {
                service.onConnected(mRemoteCallback);
            }
        } catch (Exception e) {
            Slog.w(TAG, "Exception calling onConnected().", e);
        }
    }

    public ComponentName getComponentName() {
        return mComponentName;
    }

    public void onShow(int callingUid, ShowInfo showInfo, ISelectionToolbarCallback callback) {
        run((s) -> s.onShow(callingUid, showInfo, callback));
    }

    public void onHide(long widgetToken) {
        run((s) -> s.onHide(widgetToken));
    }

    public void onDismiss(int callingUid, long widgetToken) {
        run((s) -> s.onDismiss(callingUid, widgetToken));
    }
}
+112 −54
Original line number Diff line number Diff line
@@ -16,95 +16,153 @@

package com.android.server.selectiontoolbar;

import static android.permission.flags.Flags.useSystemSelectionToolbarInSysui;

import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.UserHandle;
import android.service.selectiontoolbar.DefaultSelectionToolbarRenderService;
import android.service.selectiontoolbar.ISelectionToolbarRenderService;
import android.service.selectiontoolbar.ISelectionToolbarRenderServiceCallback;
import android.service.selectiontoolbar.SelectionToolbarRenderService;
import android.util.Slog;
import android.view.selectiontoolbar.ISelectionToolbarCallback;
import android.view.selectiontoolbar.ISelectionToolbarManager;
import android.view.selectiontoolbar.ShowInfo;

import com.android.internal.util.DumpUtils;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.internal.R;
import com.android.internal.infra.ServiceConnector;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.clipboard.ClipboardManagerInternal;
import com.android.server.input.InputManagerInternal;

import java.io.FileDescriptor;
import java.io.PrintWriter;

/**
 * Entry point service for selection toolbar management.
 */
public final class SelectionToolbarManagerService extends
        AbstractMasterSystemService<SelectionToolbarManagerService,
                SelectionToolbarManagerServiceImpl> {
public class SelectionToolbarManagerService extends SystemService {

    private static final String LOG_TAG = SelectionToolbarManagerService.class.getSimpleName();

    private final SelectionToolbarRenderServiceRemoteCallback mRemoteServiceCallback =
            new SelectionToolbarRenderServiceRemoteCallback();
    private final RemoteRenderServiceConnector mRemoteRenderServiceConnector;

    private InputManagerInternal mInputManagerInternal;
    private ClipboardManagerInternal mClipboardManagerInternal;

    private static final String TAG = "SelectionToolbarManagerService";

    public SelectionToolbarManagerService(Context context) {
        super(context);

        String serviceName;
        if (useSystemSelectionToolbarInSysui()) {
            serviceName = context.getResources()
                    .getString(R.string.config_systemUiSelectionToolbarRenderService);
        } else {
            serviceName = new ComponentName(
                    "android", DefaultSelectionToolbarRenderService.class.getName())
                    .flattenToString();
        }
        final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
        mRemoteRenderServiceConnector = new RemoteRenderServiceConnector(context,
                serviceComponent, UserHandle.USER_SYSTEM, mRemoteServiceCallback);
    }

    @Override
    public void onStart() {
        publishBinderService(Context.SELECTION_TOOLBAR_SERVICE,
                new SelectionToolbarManagerService.SelectionToolbarManagerServiceStub());
        mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
        mClipboardManagerInternal = LocalServices.getService(ClipboardManagerInternal.class);

        publishBinderService(Context.SELECTION_TOOLBAR_SERVICE, new Stub());
    }

    public SelectionToolbarManagerService(Context context) {
        super(context, new SelectionToolbarServiceNameResolver(context), /* disallowProperty= */
                null, PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
    @Override
    public void onBootPhase(int phase) {
        super.onBootPhase(phase);

        if (phase == SystemService.PHASE_BOOT_COMPLETED) {
            mRemoteRenderServiceConnector.connect(); // Prepare the binding in advance
        }
    }

    private class Stub extends ISelectionToolbarManager.Stub {

        @Override
    protected SelectionToolbarManagerServiceImpl newServiceLocked(int resolvedUserId,
            boolean disabled) {
        return new SelectionToolbarManagerServiceImpl(this, mLock, resolvedUserId);
        public void showToolbar(ShowInfo showInfo,
                ISelectionToolbarCallback iSelectionToolbarCallback) {
            mRemoteRenderServiceConnector
                    .showToolbar(Binder.getCallingUid(), showInfo, iSelectionToolbarCallback);
        }

    @SuppressWarnings("GuardedBy") // mLock == service.mLock
    final class SelectionToolbarManagerServiceStub extends ISelectionToolbarManager.Stub {
        @Override
        public void hideToolbar(long widgetToken) {
            mRemoteRenderServiceConnector.hideToolbar(widgetToken);
        }

        @Override
        public void showToolbar(ShowInfo showInfo, ISelectionToolbarCallback callback) {
            int userId = UserHandle.getUserId(Binder.getCallingUid());
            synchronized (mLock) {
                SelectionToolbarManagerServiceImpl service = getServiceForUserLocked(userId);
                if (service != null) {
                    service.showToolbar(showInfo, callback);
                } else {
                    Slog.v(TAG, "showToolbar(): no service for " + userId);
        public void dismissToolbar(long widgetToken) {
            mRemoteRenderServiceConnector.dismissToolbar(Binder.getCallingUid(), widgetToken);
        }

    }

    private final class SelectionToolbarRenderServiceRemoteCallback extends
            ISelectionToolbarRenderServiceCallback.Stub {

        @Override
        public void transferTouch(IBinder source, IBinder target) {
            mInputManagerInternal.transferTouchGesture(source, target, false);
        }

        @Override
        public void hideToolbar(long widgetToken) {
            int userId = UserHandle.getUserId(Binder.getCallingUid());
            synchronized (mLock) {
                SelectionToolbarManagerServiceImpl service = getServiceForUserLocked(userId);
                if (service != null) {
                    service.hideToolbar(widgetToken);
                } else {
                    Slog.v(TAG, "hideToolbar(): no service for " + userId);
        public void onPasteAction(int uid) {
            mClipboardManagerInternal.notifyUserAuthorizedClipAccess(uid);
        }
    }

    private static class RemoteRenderServiceConnector extends
            ServiceConnector.Impl<ISelectionToolbarRenderService> {
        private final IBinder mCallback;

        private RemoteRenderServiceConnector(Context context, ComponentName serviceName,
                int userId, IBinder callback) {
            super(context, new Intent(SelectionToolbarRenderService.SERVICE_INTERFACE)
                            .setComponent(serviceName), 0, userId,
                    ISelectionToolbarRenderService.Stub::asInterface);
            mCallback = callback;
        }

        @Override
        public void dismissToolbar(long widgetToken) {
            int userId = UserHandle.getUserId(Binder.getCallingUid());
            synchronized (mLock) {
                SelectionToolbarManagerServiceImpl service = getServiceForUserLocked(userId);
                if (service != null) {
                    service.dismissToolbar(widgetToken);
                } else {
                    Slog.v(TAG, "dismissToolbar(): no service for " + userId);
        protected long getAutoDisconnectTimeoutMs() {
            return 0; // Never unbind
        }

        @Override
        protected void onServiceConnectionStatusChanged(
                @NonNull ISelectionToolbarRenderService service, boolean isConnected) {
            try {
                if (isConnected) {
                    service.onConnected(mCallback);
                }
            } catch (Exception e) {
                Slog.w(LOG_TAG, "Exception calling onConnected().", e);
            }
        }

        @Override
        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
        private void showToolbar(int callingUid, ShowInfo showInfo,
                ISelectionToolbarCallback callback) {
            run(s -> s.onShow(callingUid, showInfo, callback));
        }

            synchronized (mLock) {
                dumpLocked("", pw);
        private void hideToolbar(long widgetToken) {
            run(s -> s.onHide(widgetToken));
        }

        private void dismissToolbar(int callingUid, long widgetToken) {
            run(s -> s.onDismiss(callingUid, widgetToken));
        }
    }
}
+0 −172
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.selectiontoolbar;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.service.selectiontoolbar.ISelectionToolbarRenderServiceCallback;
import android.util.Slog;
import android.view.selectiontoolbar.ISelectionToolbarCallback;
import android.view.selectiontoolbar.ShowInfo;

import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.clipboard.ClipboardManagerInternal;
import com.android.server.infra.AbstractPerUserSystemService;
import com.android.server.input.InputManagerInternal;

final class SelectionToolbarManagerServiceImpl extends
        AbstractPerUserSystemService<SelectionToolbarManagerServiceImpl,
                SelectionToolbarManagerService> {

    private static final String TAG = "SelectionToolbarManagerServiceImpl";

    @GuardedBy("mLock")
    @Nullable
    private RemoteSelectionToolbarRenderService mRemoteService;

    InputManagerInternal mInputManagerInternal;
    private final SelectionToolbarRenderServiceRemoteCallback mRemoteServiceCallback =
            new SelectionToolbarRenderServiceRemoteCallback();

    SelectionToolbarManagerServiceImpl(@NonNull SelectionToolbarManagerService master,
            @NonNull Object lock, int userId) {
        super(master, lock, userId);
        mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
        updateRemoteServiceLocked();
    }

    @GuardedBy("mLock")
    @Override // from PerUserSystemService
    protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
            throws PackageManager.NameNotFoundException {
        return getServiceInfoOrThrow(serviceComponent, mUserId);
    }

    @GuardedBy({"mLock"})
    @SuppressWarnings("GuardedBy") // mLock == super.mLock
    @Override // from PerUserSystemService
    protected boolean updateLocked(boolean disabled) {
        if (!Thread.holdsLock(super.mLock)) {
            throw new IllegalStateException("Thread does not hold super.mLock.");
        }
        final boolean enabledChanged = super.updateLocked(disabled);
        updateRemoteServiceLocked();
        return enabledChanged;
    }

    /**
     * Updates the reference to the remote service.
     */
    @GuardedBy("mLock")
    private void updateRemoteServiceLocked() {
        if (mRemoteService != null) {
            Slog.d(TAG, "updateRemoteService(): destroying old remote service");
            mRemoteService.unbind();
            mRemoteService = null;
        }
    }

    @GuardedBy("mLock")
    void showToolbar(ShowInfo showInfo, ISelectionToolbarCallback callback) {
        if (!Thread.holdsLock(mLock)) {
            throw new IllegalStateException("Thread does not hold mLock.");
        }
        final RemoteSelectionToolbarRenderService remoteService = ensureRemoteServiceLocked();
        if (remoteService != null) {
            remoteService.onShow(Binder.getCallingUid(), showInfo, callback);
        }
    }

    @GuardedBy("mLock")
    void hideToolbar(long widgetToken) {
        if (!Thread.holdsLock(mLock)) {
            throw new IllegalStateException("Thread does not hold mLock.");
        }
        final RemoteSelectionToolbarRenderService remoteService = ensureRemoteServiceLocked();
        if (remoteService != null) {
            remoteService.onHide(widgetToken);
        }
    }

    @GuardedBy("mLock")
    void dismissToolbar(long widgetToken) {
        if (!Thread.holdsLock(mLock)) {
            throw new IllegalStateException("Thread does not hold mLock.");
        }
        final RemoteSelectionToolbarRenderService remoteService =
                ensureRemoteServiceLocked();
        if (remoteService != null) {
            remoteService.onDismiss(Binder.getCallingUid(), widgetToken);
        }
    }

    @GuardedBy("mLock")
    @NonNull
    private RemoteSelectionToolbarRenderService ensureRemoteServiceLocked() {
        if (mRemoteService == null) {
            final String serviceName = getComponentNameLocked();
            final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
            mRemoteService = new RemoteSelectionToolbarRenderService(getContext(), serviceComponent,
                    mUserId, mRemoteServiceCallback);
        }
        return mRemoteService;
    }

    private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, @UserIdInt int userId)
            throws PackageManager.NameNotFoundException {
        int flags = PackageManager.GET_META_DATA;

        ServiceInfo si = null;
        try {
            si = AppGlobals.getPackageManager().getServiceInfo(comp, flags, userId);
        } catch (RemoteException ignored) {
        }
        if (si == null) {
            throw new PackageManager.NameNotFoundException("Could not get serviceInfo for "
                    + comp.flattenToShortString());
        }
        return si;
    }

    private void transferTouchFocus(IBinder source, IBinder target) {
        mInputManagerInternal.transferTouchGesture(source, target, false);
    }

    private final class SelectionToolbarRenderServiceRemoteCallback extends
            ISelectionToolbarRenderServiceCallback.Stub {

        @Override
        public void transferTouch(IBinder source, IBinder target) {
            transferTouchFocus(source, target);
        }

        @Override
        public void onPasteAction(int uid) {
            LocalServices.getService(ClipboardManagerInternal.class)
                    .notifyUserAuthorizedClipAccess(uid);
        }
    }
}
+0 −62
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.selectiontoolbar;

import static android.permission.flags.Flags.useSystemSelectionToolbarInSysui;

import android.content.ComponentName;
import android.content.Context;
import android.service.selectiontoolbar.DefaultSelectionToolbarRenderService;

import com.android.internal.R;
import com.android.server.infra.ServiceNameResolver;

import java.io.PrintWriter;

final class SelectionToolbarServiceNameResolver implements ServiceNameResolver {
    private String mSystemSelectionToolbarServiceName;

    SelectionToolbarServiceNameResolver(Context context) {
        if (useSystemSelectionToolbarInSysui()) {
            mSystemSelectionToolbarServiceName = context.getResources()
                            .getString(R.string.config_systemUiSelectionToolbarRenderService);
        } else {
            mSystemSelectionToolbarServiceName = new ComponentName(
                    "android", DefaultSelectionToolbarRenderService.class.getName())
                    .flattenToString();
        }
    }

    private String getDefaultServiceName() {
        return mSystemSelectionToolbarServiceName;
    }

    @Override
    public String getDefaultServiceName(int userId) {
        return getDefaultServiceName();
    }

    @Override
    public void dumpShort(PrintWriter pw) {
        pw.print("service="); pw.print(getDefaultServiceName());
    }

    @Override
    public void dumpShort(PrintWriter pw, int userId) {
        pw.print("defaultService="); pw.print(getDefaultServiceName(userId));
    }
}