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

Commit 0f897955 authored by Nino Jagar's avatar Nino Jagar
Browse files

Allow service to update content protection allowlist

Bug: 302188546
Test: Unit tests and extracted from a larger end-to-end commit
Change-Id: I6acc7dc36c9453ef4c6ae14dbdf4a2158f188203
parent 8641972b
Loading
Loading
Loading
Loading
+51 −11
Original line number Diff line number Diff line
@@ -138,7 +138,8 @@ public abstract class ContentCaptureService extends Service {
            new LocalDataShareAdapterResourceManager();

    private Handler mHandler;
    private IContentCaptureServiceCallback mCallback;
    @Nullable private IContentCaptureServiceCallback mContentCaptureServiceCallback;
    @Nullable private IContentProtectionAllowlistCallback mContentProtectionAllowlistCallback;

    private long mCallerMismatchTimeout = 1000;
    private long mLastCallerMismatchLog;
@@ -215,6 +216,16 @@ public abstract class ContentCaptureService extends Service {
                                    Binder.getCallingUid(),
                                    events));
                }

                @Override
                public void onUpdateAllowlistRequest(IBinder callback) {
                    mHandler.sendMessage(
                            obtainMessage(
                                    ContentCaptureService::handleOnUpdateAllowlistRequest,
                                    ContentCaptureService.this,
                                    Binder.getCallingUid(),
                                    callback));
                }
            };

    /** Binder that receives calls from the app in the content capture flow. */
@@ -278,14 +289,31 @@ public abstract class ContentCaptureService extends Service {
     */
    public final void setContentCaptureWhitelist(@Nullable Set<String> packages,
            @Nullable Set<ComponentName> activities) {
        final IContentCaptureServiceCallback callback = mCallback;
        if (callback == null) {
            Log.w(TAG, "setContentCaptureWhitelist(): no server callback");

        IContentCaptureServiceCallback contentCaptureCallback = mContentCaptureServiceCallback;
        IContentProtectionAllowlistCallback contentProtectionAllowlistCallback =
                mContentProtectionAllowlistCallback;

        if (contentCaptureCallback == null && contentProtectionAllowlistCallback == null) {
            Log.w(TAG, "setContentCaptureWhitelist(): missing both server callbacks");
            return;
        }

        if (contentCaptureCallback != null) {
            if (contentProtectionAllowlistCallback != null) {
                throw new IllegalStateException("Have both server callbacks");
            }
            try {
                contentCaptureCallback.setContentCaptureWhitelist(
                        toList(packages), toList(activities));
            } catch (RemoteException e) {
                e.rethrowFromSystemServer();
            }
            return;
        }

        try {
            callback.setContentCaptureWhitelist(toList(packages), toList(activities));
            contentProtectionAllowlistCallback.setAllowlist(toList(packages));
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
@@ -314,7 +342,7 @@ public abstract class ContentCaptureService extends Service {
     */
    public final void setContentCaptureConditions(@NonNull String packageName,
            @Nullable Set<ContentCaptureCondition> conditions) {
        final IContentCaptureServiceCallback callback = mCallback;
        final IContentCaptureServiceCallback callback = mContentCaptureServiceCallback;
        if (callback == null) {
            Log.w(TAG, "setContentCaptureConditions(): no server callback");
            return;
@@ -425,7 +453,7 @@ public abstract class ContentCaptureService extends Service {
    public final void disableSelf() {
        if (sDebug) Log.d(TAG, "disableSelf()");

        final IContentCaptureServiceCallback callback = mCallback;
        final IContentCaptureServiceCallback callback = mContentCaptureServiceCallback;
        if (callback == null) {
            Log.w(TAG, "disableSelf(): no server callback");
            return;
@@ -464,13 +492,14 @@ public abstract class ContentCaptureService extends Service {
    }

    private void handleOnConnected(@NonNull IBinder callback) {
        mCallback = IContentCaptureServiceCallback.Stub.asInterface(callback);
        mContentCaptureServiceCallback = IContentCaptureServiceCallback.Stub.asInterface(callback);
        onConnected();
    }

    private void handleOnDisconnected() {
        onDisconnected();
        mCallback = null;
        mContentCaptureServiceCallback = null;
        mContentProtectionAllowlistCallback = null;
    }

    //TODO(b/111276913): consider caching the InteractionSessionId for the lifetime of the session,
@@ -583,6 +612,16 @@ public abstract class ContentCaptureService extends Service {
        onContentCaptureEvent(sessionId, endEvent);
    }

    private void handleOnUpdateAllowlistRequest(int uid, @NonNull IBinder callback) {
        if (uid != Process.SYSTEM_UID) {
            Log.e(TAG, "handleOnUpdateAllowlistRequest() not allowed for uid: " + uid);
            return;
        }
        mContentProtectionAllowlistCallback =
                IContentProtectionAllowlistCallback.Stub.asInterface(callback);
        onConnected();
    }

    private void handleOnActivitySnapshot(int sessionId, @NonNull SnapshotData snapshotData) {
        onActivitySnapshot(new ContentCaptureSessionId(sessionId), snapshotData);
    }
@@ -701,13 +740,14 @@ public abstract class ContentCaptureService extends Service {
    private void writeFlushMetrics(int sessionId, @Nullable ComponentName app,
            @NonNull FlushMetrics flushMetrics, @Nullable ContentCaptureOptions options,
            int flushReason) {
        if (mCallback == null) {
        if (mContentCaptureServiceCallback == null) {
            Log.w(TAG, "writeSessionFlush(): no server callback");
            return;
        }

        try {
            mCallback.writeSessionFlush(sessionId, app, flushMetrics, options, flushReason);
            mContentCaptureServiceCallback.writeSessionFlush(
                    sessionId, app, flushMetrics, options, flushReason);
        } catch (RemoteException e) {
            Log.e(TAG, "failed to write flush metrics: " + e);
        }
+34 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.contentcapture;

import android.content.ComponentName;
import android.view.contentcapture.ContentCaptureCondition;
import android.service.contentcapture.FlushMetrics;
import android.content.ContentCaptureOptions;

import java.util.List;

/**
 * Interface for the callback used by the content protection service to call the system server and
 * update the allowlist.
 *
 * @hide
 */
oneway interface IContentProtectionAllowlistCallback {
    void setAllowlist(in List<String> packages);
}
+3 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.service.contentcapture;

import android.content.pm.ParceledListSlice;
import android.os.IBinder;
import android.view.contentcapture.ContentCaptureEvent;

/**
@@ -27,4 +28,6 @@ import android.view.contentcapture.ContentCaptureEvent;
oneway interface IContentProtectionService {

    void onLoginDetected(in ParceledListSlice events);

    void onUpdateAllowlistRequest(in IBinder callback);
}
+11 −0
Original line number Diff line number Diff line
@@ -411,6 +411,15 @@ public final class ContentCaptureManager {
    public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_TIMEOUT_MS =
            "content_protection_allowlist_timeout_ms";

    /**
     * Sets the auto disconnect timeout for the content protection service in milliseconds.
     *
     * @hide
     */
    // Unit can't be in the name in order to pass the checkstyle hook, line would be too long.
    public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_AUTO_DISCONNECT_TIMEOUT =
            "content_protection_auto_disconnect_timeout_ms";

    /** @hide */
    @TestApi
    public static final int LOGGING_LEVEL_OFF = 0;
@@ -465,6 +474,8 @@ public final class ContentCaptureManager {
    public static final long DEFAULT_CONTENT_PROTECTION_ALLOWLIST_DELAY_MS = 30000;
    /** @hide */
    public static final long DEFAULT_CONTENT_PROTECTION_ALLOWLIST_TIMEOUT_MS = 250;
    /** @hide */
    public static final long DEFAULT_CONTENT_PROTECTION_AUTO_DISCONNECT_TIMEOUT_MS = 3000;

    private final Object mLock = new Object();

+29 −10
Original line number Diff line number Diff line
@@ -22,9 +22,12 @@ import static android.service.contentcapture.ContentCaptureService.setClientStat
import static android.view.contentcapture.ContentCaptureHelper.toList;
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_DELAY_MS;
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_TIMEOUT_MS;
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_AUTO_DISCONNECT_TIMEOUT;
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE;
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG;
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD;
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG;
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_OK;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_SECURITY_EXCEPTION;
@@ -227,6 +230,9 @@ public class ContentCaptureManagerService extends
    @GuardedBy("mLock")
    long mDevCfgContentProtectionAllowlistTimeoutMs;

    @GuardedBy("mLock")
    long mDevCfgContentProtectionAutoDisconnectTimeoutMs;

    private final Executor mDataShareExecutor = Executors.newCachedThreadPool();
    private final Handler mHandler = new Handler(Looper.getMainLooper());

@@ -435,14 +441,15 @@ public class ContentCaptureManagerService extends
                case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT:
                case ContentCaptureManager
                        .DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING:
                case ContentCaptureManager
                        .DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER:
                case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE:
                    // Content protection below
                case DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER:
                case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE:
                case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG:
                case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG:
                case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD:
                case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_DELAY_MS:
                case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_TIMEOUT_MS:
                case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_AUTO_DISCONNECT_TIMEOUT:
                    setFineTuneParamsFromDeviceConfig();
                    return;
                default:
@@ -502,8 +509,7 @@ public class ContentCaptureManagerService extends
            mDevCfgContentProtectionBufferSize =
                    DeviceConfig.getInt(
                            DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
                            ContentCaptureManager
                                    .DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE,
                            DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE,
                            ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE);
            contentProtectionRequiredGroupsConfig =
                    DeviceConfig.getString(
@@ -533,7 +539,12 @@ public class ContentCaptureManagerService extends
                            DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
                            DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_TIMEOUT_MS,
                            ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_ALLOWLIST_TIMEOUT_MS);

            mDevCfgContentProtectionAutoDisconnectTimeoutMs =
                    DeviceConfig.getLong(
                            DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
                            DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_AUTO_DISCONNECT_TIMEOUT,
                            ContentCaptureManager
                                    .DEFAULT_CONTENT_PROTECTION_AUTO_DISCONNECT_TIMEOUT_MS);
            contentProtectionAllowlistManagerOld = mContentProtectionAllowlistManager;

            if (verbose) {
@@ -565,7 +576,9 @@ public class ContentCaptureManagerService extends
                                + ", contentProtectionAllowlistDelayMs="
                                + contentProtectionAllowlistDelayMs
                                + ", contentProtectionAllowlistTimeoutMs="
                                + contentProtectionAllowlistTimeoutMs);
                                + contentProtectionAllowlistTimeoutMs
                                + ", contentProtectionAutoDisconnectTimeoutMs="
                                + mDevCfgContentProtectionAutoDisconnectTimeoutMs);
            }
        }

@@ -893,6 +906,9 @@ public class ContentCaptureManagerService extends
        pw.print(prefix2);
        pw.print("contentProtectionAllowlistTimeoutMs: ");
        pw.println(mDevCfgContentProtectionAllowlistTimeoutMs);
        pw.print(prefix2);
        pw.print("contentProtectionAutoDisconnectTimeoutMs: ");
        pw.println(mDevCfgContentProtectionAutoDisconnectTimeoutMs);
        pw.print(prefix);
        pw.println("Global Options:");
        mGlobalContentCaptureOptions.dump(prefix2, pw);
@@ -962,12 +978,14 @@ public class ContentCaptureManagerService extends
    @Nullable
    public RemoteContentProtectionService createRemoteContentProtectionService() {
        ComponentName componentName;
        long autoDisconnectTimeoutMs;
        synchronized (mLock) {
            if (!mDevCfgEnableContentProtectionReceiver
                    || mContentProtectionServiceComponentName == null) {
                return null;
            }
            componentName = mContentProtectionServiceComponentName;
            autoDisconnectTimeoutMs = mDevCfgContentProtectionAutoDisconnectTimeoutMs;
        }

        // Check permissions by trying to construct {@link ContentCaptureServiceInfo}
@@ -978,19 +996,20 @@ public class ContentCaptureManagerService extends
            return null;
        }

        return createRemoteContentProtectionService(componentName);
        return createRemoteContentProtectionService(componentName, autoDisconnectTimeoutMs);
    }

    /** @hide */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    @NonNull
    protected RemoteContentProtectionService createRemoteContentProtectionService(
            @NonNull ComponentName componentName) {
            @NonNull ComponentName componentName, long autoDisconnectTimeoutMs) {
        return new RemoteContentProtectionService(
                getContext(),
                componentName,
                UserHandle.getCallingUserId(),
                isBindInstantServiceAllowed());
                isBindInstantServiceAllowed(),
                autoDisconnectTimeoutMs);
    }

    /** @hide */
Loading