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

Commit ac90cd8b authored by Sergey Volnov's avatar Sergey Volnov Committed by Android (Google) Code Review
Browse files

Merge "Implement data sharing API for Content Capture."

parents 78acb105 6e049014
Loading
Loading
Loading
Loading
+19 −0
Original line number Original line Diff line number Diff line
@@ -56148,6 +56148,7 @@ package android.view.contentcapture {
    method public boolean isContentCaptureEnabled();
    method public boolean isContentCaptureEnabled();
    method public void removeData(@NonNull android.view.contentcapture.DataRemovalRequest);
    method public void removeData(@NonNull android.view.contentcapture.DataRemovalRequest);
    method public void setContentCaptureEnabled(boolean);
    method public void setContentCaptureEnabled(boolean);
    method public void shareData(@NonNull android.view.contentcapture.DataShareRequest, @NonNull java.util.concurrent.Executor, @NonNull android.view.contentcapture.DataShareWriteAdapter);
  }
  }
  public abstract class ContentCaptureSession implements java.lang.AutoCloseable {
  public abstract class ContentCaptureSession implements java.lang.AutoCloseable {
@@ -56196,6 +56197,24 @@ package android.view.contentcapture {
    method @NonNull public android.content.LocusId getLocusId();
    method @NonNull public android.content.LocusId getLocusId();
  }
  }
  public final class DataShareRequest implements android.os.Parcelable {
    ctor public DataShareRequest(@Nullable android.content.LocusId, @NonNull String);
    method public int describeContents();
    method @Nullable public android.content.LocusId getLocusId();
    method @NonNull public String getMimeType();
    method @NonNull public String getPackageName();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.view.contentcapture.DataShareRequest> CREATOR;
  }
  public interface DataShareWriteAdapter {
    method public default void onError(int);
    method public void onRejected();
    method public void onWrite(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.CancellationSignal);
    field public static final int ERROR_CONCURRENT_REQUEST = 1; // 0x1
    field public static final int ERROR_UNKNOWN = 2; // 0x2
  }
}
}
package android.view.inline {
package android.view.inline {
+11 −0
Original line number Original line Diff line number Diff line
@@ -10140,6 +10140,7 @@ package android.service.contentcapture {
    method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent);
    method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent);
    method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId);
    method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId);
    method public void onDataRemovalRequest(@NonNull android.view.contentcapture.DataRemovalRequest);
    method public void onDataRemovalRequest(@NonNull android.view.contentcapture.DataRemovalRequest);
    method public void onDataShareRequest(@NonNull android.view.contentcapture.DataShareRequest, @NonNull android.service.contentcapture.DataShareCallback);
    method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId);
    method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId);
    method public void onDisconnected();
    method public void onDisconnected();
    method public final void setContentCaptureConditions(@NonNull String, @Nullable java.util.Set<android.view.contentcapture.ContentCaptureCondition>);
    method public final void setContentCaptureConditions(@NonNull String, @Nullable java.util.Set<android.view.contentcapture.ContentCaptureCondition>);
@@ -10148,6 +10149,16 @@ package android.service.contentcapture {
    field public static final String SERVICE_META_DATA = "android.content_capture";
    field public static final String SERVICE_META_DATA = "android.content_capture";
  }
  }
  public interface DataShareCallback {
    method public void onAccept(@NonNull java.util.concurrent.Executor, @NonNull android.service.contentcapture.DataShareReadAdapter);
    method public void onReject();
  }
  public interface DataShareReadAdapter {
    method public void onError(int);
    method public void onStart(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.CancellationSignal);
  }
  public final class SnapshotData implements android.os.Parcelable {
  public final class SnapshotData implements android.os.Parcelable {
    method public int describeContents();
    method public int describeContents();
    method @Nullable public android.app.assist.AssistContent getAssistContent();
    method @Nullable public android.app.assist.AssistContent getAssistContent();
+113 −0
Original line number Original line Diff line number Diff line
@@ -34,9 +34,12 @@ import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.Binder;
import android.os.Bundle;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.RemoteException;
import android.util.Log;
import android.util.Log;
import android.util.Slog;
import android.util.Slog;
@@ -49,15 +52,20 @@ import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureSession;
import android.view.contentcapture.ContentCaptureSession;
import android.view.contentcapture.ContentCaptureSessionId;
import android.view.contentcapture.ContentCaptureSessionId;
import android.view.contentcapture.DataRemovalRequest;
import android.view.contentcapture.DataRemovalRequest;
import android.view.contentcapture.DataShareRequest;
import android.view.contentcapture.IContentCaptureDirectManager;
import android.view.contentcapture.IContentCaptureDirectManager;
import android.view.contentcapture.MainContentCaptureSession;
import android.view.contentcapture.MainContentCaptureSession;


import com.android.internal.os.IResultReceiver;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.Preconditions;


import java.io.FileDescriptor;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.List;
import java.util.Set;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;


/**
/**
 * A service used to capture the content of the screen to provide contextual data in other areas of
 * A service used to capture the content of the screen to provide contextual data in other areas of
@@ -165,6 +173,12 @@ public abstract class ContentCaptureService extends Service {
                    ContentCaptureService.this, request));
                    ContentCaptureService.this, request));
        }
        }


        @Override
        public void onDataShared(DataShareRequest request, IDataShareCallback callback) {
            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnDataShared,
                    ContentCaptureService.this, request, callback));
        }

        @Override
        @Override
        public void onActivityEvent(ActivityEvent event) {
        public void onActivityEvent(ActivityEvent event) {
            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnActivityEvent,
            mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnActivityEvent,
@@ -317,6 +331,21 @@ public abstract class ContentCaptureService extends Service {
        if (sVerbose) Log.v(TAG, "onDataRemovalRequest()");
        if (sVerbose) Log.v(TAG, "onDataRemovalRequest()");
    }
    }


    /**
     * Notifies the service that data has been shared via a readable file.
     *
     * @param request request object containing information about data being shared
     * @param callback callback to be fired with response on whether the request is "needed" and can
     *                 be handled by the Content Capture service.
     *
     * @hide
     */
    @SystemApi
    public void onDataShareRequest(@NonNull DataShareRequest request,
            @NonNull DataShareCallback callback) {
        if (sVerbose) Log.v(TAG, "onDataShareRequest()");
    }

    /**
    /**
     * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
     * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
     *
     *
@@ -505,6 +534,37 @@ public abstract class ContentCaptureService extends Service {
        onDataRemovalRequest(request);
        onDataRemovalRequest(request);
    }
    }


    private void handleOnDataShared(@NonNull DataShareRequest request,
            IDataShareCallback callback) {
        onDataShareRequest(request, new DataShareCallback() {

            @Override
            public void onAccept(@NonNull Executor executor,
                    @NonNull DataShareReadAdapter adapter) {
                Preconditions.checkNotNull(adapter);
                Preconditions.checkNotNull(executor);

                DataShareReadAdapterDelegate delegate =
                        new DataShareReadAdapterDelegate(executor, adapter);

                try {
                    callback.accept(delegate);
                } catch (RemoteException e) {
                    Slog.e(TAG, "Failed to accept data sharing", e);
                }
            }

            @Override
            public void onReject() {
                try {
                    callback.reject();
                } catch (RemoteException e) {
                    Slog.e(TAG, "Failed to reject data sharing", e);
                }
            }
        });
    }

    private void handleOnActivityEvent(@NonNull ActivityEvent event) {
    private void handleOnActivityEvent(@NonNull ActivityEvent event) {
        onActivityEvent(event);
        onActivityEvent(event);
    }
    }
@@ -589,4 +649,57 @@ public abstract class ContentCaptureService extends Service {
            Log.e(TAG, "failed to write flush metrics: " + e);
            Log.e(TAG, "failed to write flush metrics: " + e);
        }
        }
    }
    }

    private static class DataShareReadAdapterDelegate extends IDataShareReadAdapter.Stub {

        private final Object mLock = new Object();
        private final WeakReference<DataShareReadAdapter> mAdapterReference;
        private final WeakReference<Executor> mExecutorReference;

        DataShareReadAdapterDelegate(Executor executor, DataShareReadAdapter adapter) {
            Preconditions.checkNotNull(executor);
            Preconditions.checkNotNull(adapter);

            mExecutorReference = new WeakReference<>(executor);
            mAdapterReference = new WeakReference<>(adapter);
        }

        @Override
        public void start(ParcelFileDescriptor fd, ICancellationSignal remoteCancellationSignal)
                throws RemoteException {
            synchronized (mLock) {
                CancellationSignal cancellationSignal = new CancellationSignal();
                cancellationSignal.setRemote(remoteCancellationSignal);

                executeAdapterMethodLocked(
                        adapter -> adapter.onStart(fd, cancellationSignal), "onStart");
            }
        }

        @Override
        public void error(int errorCode) throws RemoteException {
            synchronized (mLock) {
                executeAdapterMethodLocked(
                        adapter -> adapter.onError(errorCode), "onError");
            }
        }

        private void executeAdapterMethodLocked(Consumer<DataShareReadAdapter> adapterFn,
                String methodName) {
            DataShareReadAdapter adapter = mAdapterReference.get();
            Executor executor = mExecutorReference.get();

            if (adapter == null || executor == null) {
                Slog.w(TAG, "Can't execute " + methodName + "(), references have been GC'ed");
                return;
            }

            final long identity = Binder.clearCallingIdentity();
            try {
                executor.execute(() -> adapterFn.accept(adapter));
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
        }
    }
}
}
+47 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 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.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.SystemApi;

import java.util.concurrent.Executor;

/**
 * Callback for the Content Capture Service to accept or reject the data share request from a client
 * app.
 *
 * If the request is rejected, client app would receive a signal and the data share session wouldn't
 * be started.
 *
 * @hide
 **/
@SystemApi
public interface DataShareCallback {

    /** Accept the data share.
     *
     * @param executor executor to be used for running the adapter in.
     * @param adapter adapter to be used for the share operation
     */
    void onAccept(@NonNull @CallbackExecutor Executor executor,
            @NonNull DataShareReadAdapter adapter);

    /** Reject the data share. */
    void onReject();
}
+47 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 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.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;

/**
 * Adapter class to be used for the Content Capture Service app to propagate the status of the
 * session
 *
 * @hide
 **/
@SystemApi
public interface DataShareReadAdapter {

    /**
     * Signals the start of the data sharing session.
     *
     * @param fd file descriptor to use for reading data, that's being shared
     * @param cancellationSignal cancellation signal to use if data is no longer needed and the
     *                           session needs to be terminated.
     **/
    void onStart(@NonNull ParcelFileDescriptor fd, @NonNull CancellationSignal cancellationSignal);

    /**
     * Signals that the session failed to start or terminated unsuccessfully (e.g. due to a
     * timeout).
     **/
    void onError(int errorCode);
}
Loading