/* * 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 android.service.selectiontoolbar; import static android.view.selectiontoolbar.SelectionToolbarManager.ERROR_DO_NOT_ALLOW_MULTIPLE_TOOL_BAR; import static android.view.selectiontoolbar.SelectionToolbarManager.NO_TOOLBAR_ID; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.view.selectiontoolbar.ShowInfo; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.UUID; /** * The default implementation of {@link SelectionToolbarRenderService}. * *

NOTE: The requests are handled on the service main thread. * * @hide */ // TODO(b/214122495): fix class not found then move to system service folder public final class DefaultSelectionToolbarRenderService extends SelectionToolbarRenderService { private static final String TAG = "DefaultSelectionToolbarRenderService"; // TODO(b/215497659): handle remove if the client process dies. // Only show one toolbar, dismiss the old ones and remove from cache private final SparseArray> mToolbarCache = new SparseArray<>(); /** * Only allow one package to create one toolbar. */ private boolean canShowToolbar(int uid, ShowInfo showInfo) { if (showInfo.getWidgetToken() != NO_TOOLBAR_ID) { return true; } return mToolbarCache.indexOfKey(uid) < 0; } @Override public void onShow(int callingUid, ShowInfo showInfo, SelectionToolbarRenderService.RemoteCallbackWrapper callbackWrapper) { if (!canShowToolbar(callingUid, showInfo)) { Slog.e(TAG, "Do not allow multiple toolbar for the app."); callbackWrapper.onError(ERROR_DO_NOT_ALLOW_MULTIPLE_TOOL_BAR); return; } long widgetToken = showInfo.getWidgetToken() == NO_TOOLBAR_ID ? UUID.randomUUID().getMostSignificantBits() : showInfo.getWidgetToken(); if (mToolbarCache.indexOfKey(callingUid) < 0) { RemoteSelectionToolbar toolbar = new RemoteSelectionToolbar(this, widgetToken, showInfo, callbackWrapper, this::transferTouch); mToolbarCache.put(callingUid, new Pair<>(widgetToken, toolbar)); } Slog.v(TAG, "onShow() for " + widgetToken); Pair toolbarPair = mToolbarCache.get(callingUid); if (toolbarPair.first == widgetToken) { toolbarPair.second.show(showInfo); } else { Slog.w(TAG, "onShow() for unknown " + widgetToken); } } @Override public void onHide(long widgetToken) { RemoteSelectionToolbar toolbar = getRemoteSelectionToolbarByTokenLocked(widgetToken); if (toolbar != null) { Slog.v(TAG, "onHide() for " + widgetToken); toolbar.hide(widgetToken); } } @Override public void onDismiss(long widgetToken) { RemoteSelectionToolbar toolbar = getRemoteSelectionToolbarByTokenLocked(widgetToken); if (toolbar != null) { Slog.v(TAG, "onDismiss() for " + widgetToken); toolbar.dismiss(widgetToken); removeRemoteSelectionToolbarByTokenLocked(widgetToken); } } @Override public void onToolbarShowTimeout(int callingUid) { Slog.w(TAG, "onToolbarShowTimeout for callingUid = " + callingUid); Pair toolbarPair = mToolbarCache.get(callingUid); if (toolbarPair != null) { RemoteSelectionToolbar remoteToolbar = toolbarPair.second; remoteToolbar.dismiss(toolbarPair.first); remoteToolbar.onToolbarShowTimeout(); mToolbarCache.remove(callingUid); } } private RemoteSelectionToolbar getRemoteSelectionToolbarByTokenLocked(long widgetToken) { for (int i = 0; i < mToolbarCache.size(); i++) { Pair toolbarPair = mToolbarCache.valueAt(i); if (toolbarPair.first == widgetToken) { return toolbarPair.second; } } return null; } private void removeRemoteSelectionToolbarByTokenLocked(long widgetToken) { for (int i = 0; i < mToolbarCache.size(); i++) { Pair toolbarPair = mToolbarCache.valueAt(i); if (toolbarPair.first == widgetToken) { mToolbarCache.remove(mToolbarCache.keyAt(i)); return; } } } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { int size = mToolbarCache.size(); pw.print("number selectionToolbar: "); pw.println(size); String pfx = " "; for (int i = 0; i < size; i++) { pw.print("#"); pw.println(i); int callingUid = mToolbarCache.keyAt(i); pw.print(pfx); pw.print("callingUid: "); pw.println(callingUid); Pair toolbarPair = mToolbarCache.valueAt(i); RemoteSelectionToolbar selectionToolbar = toolbarPair.second; pw.print(pfx); pw.print("selectionToolbar: "); selectionToolbar.dump(pfx, pw); pw.println(); } } }