Merge "Optimizes the Content Capture workflow by calling the service directly."
This commit is contained in:
committed by
Android (Google) Code Review
commit
ac040fddfd
@@ -357,6 +357,7 @@ java_defaults {
|
||||
"core/java/android/view/autofill/IAutoFillManagerClient.aidl",
|
||||
"core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl",
|
||||
"core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
|
||||
"core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl",
|
||||
"core/java/android/view/contentcapture/IContentCaptureManager.aidl",
|
||||
"core/java/android/view/IApplicationToken.aidl",
|
||||
"core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl",
|
||||
|
||||
@@ -5017,7 +5017,7 @@ package android.service.contentcapture {
|
||||
method public final java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
|
||||
method public final java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
|
||||
method public void onActivitySnapshot(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.SnapshotData);
|
||||
method public abstract void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
|
||||
method public void onContentCaptureEventsRequest(android.view.contentcapture.ContentCaptureSessionId, android.service.contentcapture.ContentCaptureEventsRequest);
|
||||
method public void onCreateContentCaptureSession(android.view.contentcapture.ContentCaptureContext, android.view.contentcapture.ContentCaptureSessionId);
|
||||
method public void onDestroyContentCaptureSession(android.view.contentcapture.ContentCaptureSessionId);
|
||||
method public final void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
|
||||
|
||||
@@ -17,6 +17,7 @@ package android.service.contentcapture;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.view.contentcapture.ContentCaptureEvent;
|
||||
@@ -31,10 +32,10 @@ import java.util.List;
|
||||
@SystemApi
|
||||
public final class ContentCaptureEventsRequest implements Parcelable {
|
||||
|
||||
private final List<ContentCaptureEvent> mEvents;
|
||||
private final ParceledListSlice<ContentCaptureEvent> mEvents;
|
||||
|
||||
/** @hide */
|
||||
public ContentCaptureEventsRequest(@NonNull List<ContentCaptureEvent> events) {
|
||||
public ContentCaptureEventsRequest(@NonNull ParceledListSlice<ContentCaptureEvent> events) {
|
||||
mEvents = events;
|
||||
}
|
||||
|
||||
@@ -43,7 +44,7 @@ public final class ContentCaptureEventsRequest implements Parcelable {
|
||||
*/
|
||||
@NonNull
|
||||
public List<ContentCaptureEvent> getEvents() {
|
||||
return mEvents;
|
||||
return mEvents.getList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,7 +54,7 @@ public final class ContentCaptureEventsRequest implements Parcelable {
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
parcel.writeTypedList(mEvents, flags);
|
||||
parcel.writeParcelable(mEvents, flags);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<ContentCaptureEventsRequest> CREATOR =
|
||||
@@ -61,8 +62,7 @@ public final class ContentCaptureEventsRequest implements Parcelable {
|
||||
|
||||
@Override
|
||||
public ContentCaptureEventsRequest createFromParcel(Parcel parcel) {
|
||||
return new ContentCaptureEventsRequest(parcel
|
||||
.createTypedArrayList(ContentCaptureEvent.CREATOR));
|
||||
return new ContentCaptureEventsRequest(parcel.readParcelable(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,15 +24,27 @@ import android.annotation.SystemApi;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.view.contentcapture.ContentCaptureContext;
|
||||
import android.view.contentcapture.ContentCaptureEvent;
|
||||
import android.view.contentcapture.ContentCaptureManager;
|
||||
import android.view.contentcapture.ContentCaptureSession;
|
||||
import android.view.contentcapture.ContentCaptureSessionId;
|
||||
import android.view.contentcapture.IContentCaptureDirectManager;
|
||||
|
||||
import com.android.internal.os.IResultReceiver;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -63,29 +75,16 @@ public abstract class ContentCaptureService extends Service {
|
||||
|
||||
private Handler mHandler;
|
||||
|
||||
private final IContentCaptureService mInterface = new IContentCaptureService.Stub() {
|
||||
/**
|
||||
* Binder that receives calls from the system server.
|
||||
*/
|
||||
private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() {
|
||||
|
||||
@Override
|
||||
public void onSessionLifecycle(ContentCaptureContext context, String sessionId)
|
||||
throws RemoteException {
|
||||
if (context != null) {
|
||||
mHandler.sendMessage(
|
||||
obtainMessage(ContentCaptureService::handleOnCreateSession,
|
||||
ContentCaptureService.this, context, sessionId));
|
||||
} else {
|
||||
mHandler.sendMessage(
|
||||
obtainMessage(ContentCaptureService::handleOnDestroySession,
|
||||
ContentCaptureService.this, sessionId));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContentCaptureEventsRequest(String sessionId,
|
||||
ContentCaptureEventsRequest request) {
|
||||
mHandler.sendMessage(
|
||||
obtainMessage(ContentCaptureService::handleOnContentCaptureEventsRequest,
|
||||
ContentCaptureService.this, sessionId, request));
|
||||
|
||||
public void onSessionStarted(ContentCaptureContext context, String sessionId, int uid,
|
||||
IResultReceiver clientReceiver) {
|
||||
mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnCreateSession,
|
||||
ContentCaptureService.this, context, sessionId, uid, clientReceiver));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,8 +93,36 @@ public abstract class ContentCaptureService extends Service {
|
||||
obtainMessage(ContentCaptureService::handleOnActivitySnapshot,
|
||||
ContentCaptureService.this, sessionId, snapshotData));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionFinished(String sessionId) {
|
||||
mHandler.sendMessage(obtainMessage(ContentCaptureService::handleFinishSession,
|
||||
ContentCaptureService.this, sessionId));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Binder that receives calls from the app.
|
||||
*/
|
||||
private final IContentCaptureDirectManager mClientInterface =
|
||||
new IContentCaptureDirectManager.Stub() {
|
||||
|
||||
@Override
|
||||
public void sendEvents(String sessionId,
|
||||
@SuppressWarnings("rawtypes") ParceledListSlice events) {
|
||||
mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents,
|
||||
ContentCaptureService.this, sessionId, Binder.getCallingUid(), events));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* List of sessions per UID.
|
||||
*
|
||||
* <p>This map is populated when an session is started, which is called by the system server
|
||||
* and can be trusted. Then subsequent calls made by the app are verified against this map.
|
||||
*/
|
||||
private final ArrayMap<String, Integer> mSessionsByUid = new ArrayMap<>();
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@@ -107,7 +134,7 @@ public abstract class ContentCaptureService extends Service {
|
||||
@Override
|
||||
public final IBinder onBind(Intent intent) {
|
||||
if (SERVICE_INTERFACE.equals(intent.getAction())) {
|
||||
return mInterface.asBinder();
|
||||
return mServerInterface.asBinder();
|
||||
}
|
||||
Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
|
||||
return null;
|
||||
@@ -196,8 +223,10 @@ public abstract class ContentCaptureService extends Service {
|
||||
* @param sessionId the session's Id
|
||||
* @param request the events
|
||||
*/
|
||||
public abstract void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId,
|
||||
@NonNull ContentCaptureEventsRequest request);
|
||||
public void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId,
|
||||
@NonNull ContentCaptureEventsRequest request) {
|
||||
if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the service of {@link SnapshotData snapshot data} associated with a session.
|
||||
@@ -212,10 +241,22 @@ public abstract class ContentCaptureService extends Service {
|
||||
* Destroys the content capture session.
|
||||
*
|
||||
* @param sessionId the id of the session to destroy
|
||||
*/
|
||||
* */
|
||||
public void onDestroyContentCaptureSession(@NonNull ContentCaptureSessionId sessionId) {
|
||||
if (VERBOSE) {
|
||||
Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")");
|
||||
if (VERBOSE) Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
final int size = mSessionsByUid.size();
|
||||
pw.print("Number sessions: "); pw.println(size);
|
||||
if (size > 0) {
|
||||
final String prefix = " ";
|
||||
for (int i = 0; i < size; i++) {
|
||||
pw.print(prefix); pw.print(mSessionsByUid.keyAt(i));
|
||||
pw.print(": uid="); pw.println(mSessionsByUid.valueAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,13 +264,19 @@ public abstract class ContentCaptureService extends Service {
|
||||
// so we don't need to create a temporary InteractionSessionId for each event.
|
||||
|
||||
private void handleOnCreateSession(@NonNull ContentCaptureContext context,
|
||||
@NonNull String sessionId) {
|
||||
@NonNull String sessionId, int uid, IResultReceiver clientReceiver) {
|
||||
mSessionsByUid.put(sessionId, uid);
|
||||
onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId));
|
||||
setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE,
|
||||
mClientInterface.asBinder());
|
||||
}
|
||||
|
||||
private void handleOnContentCaptureEventsRequest(@NonNull String sessionId,
|
||||
@NonNull ContentCaptureEventsRequest request) {
|
||||
onContentCaptureEventsRequest(new ContentCaptureSessionId(sessionId), request);
|
||||
private void handleSendEvents(@NonNull String sessionId, int uid,
|
||||
@NonNull ParceledListSlice<ContentCaptureEvent> events) {
|
||||
if (handleIsRightCallerFor(sessionId, uid)) {
|
||||
onContentCaptureEventsRequest(new ContentCaptureSessionId(sessionId),
|
||||
new ContentCaptureEventsRequest(events));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleOnActivitySnapshot(@NonNull String sessionId,
|
||||
@@ -237,7 +284,52 @@ public abstract class ContentCaptureService extends Service {
|
||||
onActivitySnapshot(new ContentCaptureSessionId(sessionId), snapshotData);
|
||||
}
|
||||
|
||||
private void handleOnDestroySession(@NonNull String sessionId) {
|
||||
private void handleFinishSession(@NonNull String sessionId) {
|
||||
mSessionsByUid.remove(sessionId);
|
||||
onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given {@code uid} owns the session.
|
||||
*/
|
||||
private boolean handleIsRightCallerFor(@NonNull String sessionId, int uid) {
|
||||
final Integer rightUid = mSessionsByUid.get(sessionId);
|
||||
if (rightUid == null) {
|
||||
if (VERBOSE) Log.v(TAG, "No session for " + sessionId);
|
||||
// Just ignore, as the session could have finished
|
||||
return false;
|
||||
}
|
||||
if (rightUid != uid) {
|
||||
Log.e(TAG, "invalid call from UID " + uid + ": session " + sessionId + " belongs to "
|
||||
+ rightUid);
|
||||
//TODO(b/111276913): log metrics as this could be a malicious app forging a sessionId
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the state of the {@link ContentCaptureManager} in the cleint app.
|
||||
*
|
||||
* @param clientReceiver receiver in the client app.
|
||||
* @param binder handle to the {@code IContentCaptureDirectManager} object that resides in the
|
||||
* service.
|
||||
* @hide
|
||||
*/
|
||||
public static void setClientState(@NonNull IResultReceiver clientReceiver,
|
||||
int sessionStatus, @Nullable IBinder binder) {
|
||||
try {
|
||||
final Bundle extras;
|
||||
if (binder != null) {
|
||||
extras = new Bundle();
|
||||
extras.putBinder(ContentCaptureSession.EXTRA_BINDER, binder);
|
||||
} else {
|
||||
extras = null;
|
||||
}
|
||||
clientReceiver.send(sessionStatus, extras);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Error async reporting result to client: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
|
||||
package android.service.contentcapture;
|
||||
|
||||
import android.service.contentcapture.ContentCaptureEventsRequest;
|
||||
import android.service.contentcapture.SnapshotData;
|
||||
import android.view.contentcapture.ContentCaptureContext;
|
||||
|
||||
import com.android.internal.os.IResultReceiver;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -28,11 +29,8 @@ import java.util.List;
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IContentCaptureService {
|
||||
|
||||
// Called when session is created (context not null) or destroyed (context null)
|
||||
void onSessionLifecycle(in ContentCaptureContext context, String sessionId);
|
||||
|
||||
void onContentCaptureEventsRequest(String sessionId, in ContentCaptureEventsRequest request);
|
||||
|
||||
void onSessionStarted(in ContentCaptureContext context, String sessionId, int uid,
|
||||
in IResultReceiver clientReceiver);
|
||||
void onSessionFinished(String sessionId);
|
||||
void onActivitySnapshot(String sessionId, in SnapshotData snapshotData);
|
||||
}
|
||||
|
||||
@@ -27,9 +27,11 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.IBinder.DeathRecipient;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
@@ -45,6 +47,8 @@ import dalvik.system.CloseGuard;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@@ -99,6 +103,13 @@ public final class ContentCaptureSession implements AutoCloseable {
|
||||
*/
|
||||
public static final int STATE_DISABLED = 3;
|
||||
|
||||
/**
|
||||
* Session is disabled because its id already existed on server.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int STATE_DISABLED_DUPLICATED_ID = 4;
|
||||
|
||||
/**
|
||||
* Handler message used to flush the buffer.
|
||||
*/
|
||||
@@ -116,6 +127,13 @@ public final class ContentCaptureSession implements AutoCloseable {
|
||||
// TODO(b/121044064): use settings
|
||||
private static final int FLUSHING_FREQUENCY_MS = 5_000;
|
||||
|
||||
|
||||
/**
|
||||
* Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
|
||||
* @hide
|
||||
*/
|
||||
public static final String EXTRA_BINDER = "binder";
|
||||
|
||||
private final CloseGuard mCloseGuard = CloseGuard.get();
|
||||
|
||||
@NonNull
|
||||
@@ -127,8 +145,21 @@ public final class ContentCaptureSession implements AutoCloseable {
|
||||
@NonNull
|
||||
private final Handler mHandler;
|
||||
|
||||
/**
|
||||
* Interface to the system_server binder object - it's only used to start the session (and
|
||||
* notify when the session is finished).
|
||||
*/
|
||||
@Nullable
|
||||
private final IContentCaptureManager mService;
|
||||
private final IContentCaptureManager mSystemServerInterface;
|
||||
|
||||
/**
|
||||
* Direct interface to the service binder object - it's used to send the events, including the
|
||||
* last ones (when the session is finished)
|
||||
*/
|
||||
@Nullable
|
||||
private IContentCaptureDirectManager mDirectServiceInterface;
|
||||
@Nullable
|
||||
private DeathRecipient mDirectServiceVulture;
|
||||
|
||||
@Nullable
|
||||
private final String mId = UUID.randomUUID().toString();
|
||||
@@ -165,11 +196,11 @@ public final class ContentCaptureSession implements AutoCloseable {
|
||||
|
||||
/** @hide */
|
||||
protected ContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
|
||||
@Nullable IContentCaptureManager service, @NonNull AtomicBoolean disabled,
|
||||
@Nullable IContentCaptureManager systemServerInterface, @NonNull AtomicBoolean disabled,
|
||||
@Nullable ContentCaptureContext clientContext) {
|
||||
mContext = context;
|
||||
mHandler = handler;
|
||||
mService = service;
|
||||
mSystemServerInterface = systemServerInterface;
|
||||
mDisabled = disabled;
|
||||
mClientContext = clientContext;
|
||||
mCloseGuard.open("destroy");
|
||||
@@ -227,6 +258,8 @@ public final class ContentCaptureSession implements AutoCloseable {
|
||||
Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
|
||||
}
|
||||
|
||||
flush();
|
||||
|
||||
mHandler.sendMessage(obtainMessage(ContentCaptureSession::handleDestroySession, this));
|
||||
mCloseGuard.close();
|
||||
}
|
||||
@@ -267,11 +300,20 @@ public final class ContentCaptureSession implements AutoCloseable {
|
||||
final int flags = 0; // TODO(b/111276913): get proper flags
|
||||
|
||||
try {
|
||||
mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
|
||||
mId, mClientContext, flags, new IResultReceiver.Stub() {
|
||||
mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken,
|
||||
componentName, mId, mClientContext, flags, new IResultReceiver.Stub() {
|
||||
@Override
|
||||
public void send(int resultCode, Bundle resultData) {
|
||||
handleSessionStarted(resultCode);
|
||||
IBinder binder = null;
|
||||
if (resultData != null) {
|
||||
binder = resultData.getBinder(EXTRA_BINDER);
|
||||
if (binder == null) {
|
||||
Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
|
||||
handleResetState();
|
||||
return;
|
||||
}
|
||||
}
|
||||
handleSessionStarted(resultCode, binder);
|
||||
}
|
||||
});
|
||||
} catch (RemoteException e) {
|
||||
@@ -280,12 +322,38 @@ public final class ContentCaptureSession implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSessionStarted(int resultCode) {
|
||||
/**
|
||||
* Callback from {@code system_server} after call to
|
||||
* {@link IContentCaptureManager#startSession(int, IBinder, ComponentName, String,
|
||||
* ContentCaptureContext, int, IResultReceiver)}.
|
||||
*
|
||||
* @param resultCode session state
|
||||
* @param binder handle to {@link IContentCaptureDirectManager}
|
||||
*/
|
||||
private void handleSessionStarted(int resultCode, @Nullable IBinder binder) {
|
||||
mState = resultCode;
|
||||
mDisabled.set(mState == STATE_DISABLED);
|
||||
if (binder != null) {
|
||||
mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
|
||||
mDirectServiceVulture = () -> {
|
||||
Log.w(TAG, "Destroying session " + mId + " because service died");
|
||||
destroy();
|
||||
};
|
||||
try {
|
||||
binder.linkToDeath(mDirectServiceVulture, 0);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to link to death on " + binder + ": " + e);
|
||||
}
|
||||
}
|
||||
if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) {
|
||||
mDisabled.set(true);
|
||||
handleResetSession(/* resetState= */ false);
|
||||
} else {
|
||||
mDisabled.set(false);
|
||||
}
|
||||
if (VERBOSE) {
|
||||
Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
|
||||
+ ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get());
|
||||
+ ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
|
||||
+ ", binder=" + binder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +375,7 @@ public final class ContentCaptureSession implements AutoCloseable {
|
||||
final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
|
||||
|
||||
if (bufferEvent && !forceFlush) {
|
||||
handleScheduleFlush();
|
||||
handleScheduleFlush(/* checkExisting= */ true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -331,8 +399,8 @@ public final class ContentCaptureSession implements AutoCloseable {
|
||||
handleForceFlush();
|
||||
}
|
||||
|
||||
private void handleScheduleFlush() {
|
||||
if (mHandler.hasMessages(MSG_FLUSH)) {
|
||||
private void handleScheduleFlush(boolean checkExisting) {
|
||||
if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) {
|
||||
// "Renew" the flush message by removing the previous one
|
||||
mHandler.removeMessages(MSG_FLUSH);
|
||||
}
|
||||
@@ -356,52 +424,80 @@ public final class ContentCaptureSession implements AutoCloseable {
|
||||
private void handleForceFlush() {
|
||||
if (mEvents == null) return;
|
||||
|
||||
if (mDirectServiceInterface == null) {
|
||||
Log.w(TAG, "handleForceFlush(): client not available yet");
|
||||
if (!mHandler.hasMessages(MSG_FLUSH)) {
|
||||
handleScheduleFlush(/* checkExisting= */ false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final int numberEvents = mEvents.size();
|
||||
try {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
|
||||
}
|
||||
mHandler.removeMessages(MSG_FLUSH);
|
||||
mService.sendEvents(mContext.getUserId(), mId, mEvents);
|
||||
// TODO(b/111276913): decide whether we should clear or set it to null, as each has
|
||||
// its own advantages: clearing will save extra allocations while the session is
|
||||
// active, while setting to null would save memory if there's no more event coming.
|
||||
mEvents.clear();
|
||||
|
||||
final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents();
|
||||
mDirectServiceInterface.sendEvents(mId, events);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
|
||||
+ ": " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the buffer and return a {@link ParceledListSlice} with the previous events.
|
||||
*/
|
||||
@NonNull
|
||||
private ParceledListSlice<ContentCaptureEvent> handleClearEvents() {
|
||||
// NOTE: we must save a reference to the current mEvents and then set it to to null,
|
||||
// otherwise clearing it would clear it in the receiving side if the service is also local.
|
||||
final List<ContentCaptureEvent> events = mEvents == null
|
||||
? Collections.emptyList()
|
||||
: mEvents;
|
||||
mEvents = null;
|
||||
return new ParceledListSlice<>(events);
|
||||
}
|
||||
|
||||
private void handleDestroySession() {
|
||||
//TODO(b/111276913): right now both the ContentEvents and lifecycle sessions are sent
|
||||
// to system_server, so it's ok to call both in sequence here. But once we split
|
||||
// them so the events are sent directly to the service, we need to make sure they're
|
||||
// sent in order.
|
||||
try {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
|
||||
+ (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
|
||||
+ getActivityDebugName());
|
||||
}
|
||||
|
||||
mService.finishSession(mContext.getUserId(), mId, mEvents);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error destroying session " + mId + " for " + getActivityDebugName()
|
||||
+ ": " + e);
|
||||
} finally {
|
||||
handleResetState();
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
|
||||
+ (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
|
||||
+ getActivityDebugName());
|
||||
}
|
||||
|
||||
try {
|
||||
mSystemServerInterface.finishSession(mContext.getUserId(), mId);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error destroying system-service session " + mId + " for "
|
||||
+ getActivityDebugName() + ": " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleResetState() {
|
||||
handleResetSession(/* resetState= */ true);
|
||||
}
|
||||
|
||||
// TODO(b/111276913): once we support multiple sessions, we might need to move some of these
|
||||
// clearings out.
|
||||
private void handleResetState() {
|
||||
mState = STATE_UNKNOWN;
|
||||
private void handleResetSession(boolean resetState) {
|
||||
if (resetState) {
|
||||
mState = STATE_UNKNOWN;
|
||||
}
|
||||
mContentCaptureSessionId = null;
|
||||
mApplicationToken = null;
|
||||
mComponentName = null;
|
||||
mEvents = null;
|
||||
if (mDirectServiceInterface != null) {
|
||||
mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
|
||||
}
|
||||
mDirectServiceInterface = null;
|
||||
mHandler.removeMessages(MSG_FLUSH);
|
||||
}
|
||||
|
||||
@@ -492,15 +588,20 @@ public final class ContentCaptureSession implements AutoCloseable {
|
||||
}
|
||||
|
||||
private boolean isContentCaptureEnabled() {
|
||||
return mService != null && !mDisabled.get();
|
||||
return mSystemServerInterface != null && !mDisabled.get();
|
||||
}
|
||||
|
||||
void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
|
||||
pw.print(prefix); pw.print("id: "); pw.println(mId);
|
||||
pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
|
||||
pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
|
||||
if (mService != null) {
|
||||
pw.print(prefix); pw.print("mService: "); pw.println(mService);
|
||||
if (mSystemServerInterface != null) {
|
||||
pw.print(prefix); pw.print("mSystemServerInterface: ");
|
||||
pw.println(mSystemServerInterface);
|
||||
}
|
||||
if (mDirectServiceInterface != null) {
|
||||
pw.print(prefix); pw.print("mDirectServiceInterface: ");
|
||||
pw.println(mDirectServiceInterface);
|
||||
}
|
||||
if (mClientContext != null) {
|
||||
// NOTE: we don't dump clientContent because it could have PII
|
||||
@@ -563,6 +664,8 @@ public final class ContentCaptureSession implements AutoCloseable {
|
||||
return "ACTIVE";
|
||||
case STATE_DISABLED:
|
||||
return "DISABLED";
|
||||
case STATE_DISABLED_DUPLICATED_ID:
|
||||
return "DISABLED_DUPLICATED_ID";
|
||||
default:
|
||||
return "INVALID:" + state;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.view.contentcapture;
|
||||
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.view.contentcapture.ContentCaptureEvent;
|
||||
|
||||
/**
|
||||
* Interface between an app (ContentCaptureManager / ContentCaptureSession) and the app providing
|
||||
* the ContentCaptureService implementation.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IContentCaptureDirectManager {
|
||||
void sendEvents(in String sessionId, in ParceledListSlice events);
|
||||
}
|
||||
@@ -26,12 +26,14 @@ import com.android.internal.os.IResultReceiver;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
* Interface between an app (ContentCaptureManager / ContentCaptureSession) and the system-server
|
||||
* implementation service (ContentCaptureManagerService).
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IContentCaptureManager {
|
||||
void startSession(int userId, IBinder activityToken, in ComponentName componentName,
|
||||
String sessionId, in ContentCaptureContext clientContext, int flags,
|
||||
in IResultReceiver result);
|
||||
void finishSession(int userId, String sessionId, in List<ContentCaptureEvent> events);
|
||||
void sendEvents(int userId, in String sessionId, in List<ContentCaptureEvent> events);
|
||||
void finishSession(int userId, String sessionId);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
@@ -34,7 +35,6 @@ import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Slog;
|
||||
import android.view.contentcapture.ContentCaptureContext;
|
||||
import android.view.contentcapture.ContentCaptureEvent;
|
||||
import android.view.contentcapture.IContentCaptureManager;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
@@ -47,7 +47,6 @@ import com.android.server.infra.AbstractMasterSystemService;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A service used to observe the contents of the screen.
|
||||
@@ -182,30 +181,18 @@ public final class ContentCaptureManagerService extends
|
||||
synchronized (mLock) {
|
||||
final ContentCapturePerUserService service = getServiceForUserLocked(userId);
|
||||
service.startSessionLocked(activityToken, componentName, taskId, displayId,
|
||||
sessionId, clientContext, flags, mAllowInstantService, result);
|
||||
sessionId, Binder.getCallingUid(), clientContext, flags,
|
||||
mAllowInstantService, result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEvents(@UserIdInt int userId, @NonNull String sessionId,
|
||||
@NonNull List<ContentCaptureEvent> events) {
|
||||
Preconditions.checkNotNull(sessionId);
|
||||
Preconditions.checkNotNull(events);
|
||||
|
||||
synchronized (mLock) {
|
||||
final ContentCapturePerUserService service = getServiceForUserLocked(userId);
|
||||
service.sendEventsLocked(sessionId, events);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishSession(@UserIdInt int userId, @NonNull String sessionId,
|
||||
@Nullable List<ContentCaptureEvent> events) {
|
||||
public void finishSession(@UserIdInt int userId, @NonNull String sessionId) {
|
||||
Preconditions.checkNotNull(sessionId);
|
||||
|
||||
synchronized (mLock) {
|
||||
final ContentCapturePerUserService service = getServiceForUserLocked(userId);
|
||||
service.finishSessionLocked(sessionId, events);
|
||||
service.finishSessionLocked(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.server.contentcapture;
|
||||
|
||||
import static android.service.contentcapture.ContentCaptureService.setClientState;
|
||||
|
||||
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
|
||||
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
|
||||
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
|
||||
@@ -38,7 +40,6 @@ import android.service.contentcapture.SnapshotData;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
import android.view.contentcapture.ContentCaptureContext;
|
||||
import android.view.contentcapture.ContentCaptureEvent;
|
||||
import android.view.contentcapture.ContentCaptureSession;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
@@ -48,7 +49,6 @@ import com.android.server.infra.FrameworkResourcesServiceNameResolver;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Per-user instance of {@link ContentCaptureManagerService}.
|
||||
@@ -114,10 +114,10 @@ final class ContentCapturePerUserService
|
||||
@GuardedBy("mLock")
|
||||
public void startSessionLocked(@NonNull IBinder activityToken,
|
||||
@NonNull ComponentName componentName, int taskId, int displayId,
|
||||
@NonNull String sessionId, @Nullable ContentCaptureContext clientContext,
|
||||
int flags, boolean bindInstantServiceAllowed, @NonNull IResultReceiver resultReceiver) {
|
||||
@NonNull String sessionId, int uid, @Nullable ContentCaptureContext clientContext,
|
||||
int flags, boolean bindInstantServiceAllowed, @NonNull IResultReceiver clientReceiver) {
|
||||
if (!isEnabledLocked()) {
|
||||
sendToClient(resultReceiver, ContentCaptureSession.STATE_DISABLED);
|
||||
setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED, /* binder=*/ null);
|
||||
return;
|
||||
}
|
||||
final ComponentName serviceComponentName = getServiceComponentName();
|
||||
@@ -131,35 +131,30 @@ final class ContentCapturePerUserService
|
||||
return;
|
||||
}
|
||||
|
||||
ContentCaptureServerSession session = mSessions.get(sessionId);
|
||||
if (session != null) {
|
||||
if (mMaster.debug) {
|
||||
Slog.d(TAG, "startSession(): reusing session " + sessionId + " for "
|
||||
+ componentName);
|
||||
}
|
||||
// TODO(b/111276913): check if local ids match and decide what to do if they don't
|
||||
// TODO(b/111276913): should we call session.notifySessionStartedLocked() again??
|
||||
// if not, move notifySessionStartedLocked() into session constructor
|
||||
sendToClient(resultReceiver, ContentCaptureSession.STATE_ACTIVE);
|
||||
final ContentCaptureServerSession existingSession = mSessions.get(sessionId);
|
||||
if (existingSession != null) {
|
||||
Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
|
||||
+ ": ignoring because it already exists for " + existingSession.mActivityToken);
|
||||
setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_DUPLICATED_ID,
|
||||
/* binder=*/ null);
|
||||
return;
|
||||
}
|
||||
|
||||
session = new ContentCaptureServerSession(getContext(), mUserId, mLock, activityToken,
|
||||
this, serviceComponentName, componentName, taskId, displayId, sessionId,
|
||||
clientContext, flags, bindInstantServiceAllowed, mMaster.verbose);
|
||||
final ContentCaptureServerSession newSession = new ContentCaptureServerSession(getContext(),
|
||||
mUserId, mLock, activityToken, this, serviceComponentName, componentName, taskId,
|
||||
displayId, sessionId, uid, clientContext, flags, bindInstantServiceAllowed,
|
||||
mMaster.verbose);
|
||||
if (mMaster.verbose) {
|
||||
Slog.v(TAG, "startSession(): new session for " + componentName + " and id "
|
||||
+ sessionId);
|
||||
Slog.v(TAG, "startSession(): new session for "
|
||||
+ ComponentName.flattenToShortString(componentName) + " and id " + sessionId);
|
||||
}
|
||||
mSessions.put(sessionId, session);
|
||||
session.notifySessionStartedLocked();
|
||||
sendToClient(resultReceiver, ContentCaptureSession.STATE_ACTIVE);
|
||||
mSessions.put(sessionId, newSession);
|
||||
newSession.notifySessionStartedLocked(clientReceiver);
|
||||
}
|
||||
|
||||
// TODO(b/111276913): log metrics
|
||||
@GuardedBy("mLock")
|
||||
public void finishSessionLocked(@NonNull String sessionId,
|
||||
@Nullable List<ContentCaptureEvent> events) {
|
||||
public void finishSessionLocked(@NonNull String sessionId) {
|
||||
if (!isEnabledLocked()) {
|
||||
return;
|
||||
}
|
||||
@@ -171,41 +166,8 @@ final class ContentCapturePerUserService
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (events != null && !events.isEmpty()) {
|
||||
// TODO(b/111276913): for now we're sending the events and the onDestroy() in 2 separate
|
||||
// calls because it's not clear yet whether we'll change the manager to send events
|
||||
// to the service directly (i.e., without passing through system server). Once we
|
||||
// decide, we might need to split IContentCaptureManager.onSessionLifecycle() in 2
|
||||
// methods, one for start and another for finish (and passing the events to finish),
|
||||
// otherwise the service might receive the 2 calls out of order.
|
||||
session.sendEventsLocked(events);
|
||||
}
|
||||
if (mMaster.verbose) {
|
||||
Slog.v(TAG, "finishSession(" + (events == null ? 0 : events.size()) + " events): "
|
||||
+ session);
|
||||
}
|
||||
session.removeSelfLocked(true);
|
||||
}
|
||||
|
||||
// TODO(b/111276913): need to figure out why some events are sent before session is started;
|
||||
// probably because ContentCaptureManager is not buffering them until it gets the session back
|
||||
@GuardedBy("mLock")
|
||||
public void sendEventsLocked(@NonNull String sessionId,
|
||||
@NonNull List<ContentCaptureEvent> events) {
|
||||
if (!isEnabledLocked()) {
|
||||
return;
|
||||
}
|
||||
final ContentCaptureServerSession session = mSessions.get(sessionId);
|
||||
if (session == null) {
|
||||
if (mMaster.verbose) {
|
||||
Slog.v(TAG, "sendEvents(): no session for " + sessionId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (mMaster.verbose) {
|
||||
Slog.v(TAG, "sendEvents(): id=" + sessionId + ", events=" + events.size());
|
||||
}
|
||||
session.sendEventsLocked(events);
|
||||
if (mMaster.verbose) Slog.v(TAG, "finishSession(): id=" + sessionId);
|
||||
session.removeSelfLocked(/* notifyRemoteService= */ true);
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@@ -308,12 +270,4 @@ final class ContentCapturePerUserService
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void sendToClient(@NonNull IResultReceiver resultReceiver, int value) {
|
||||
try {
|
||||
resultReceiver.send(value, null);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Error async reporting result to client: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,15 +24,14 @@ import android.service.contentcapture.ContentCaptureService;
|
||||
import android.service.contentcapture.SnapshotData;
|
||||
import android.util.Slog;
|
||||
import android.view.contentcapture.ContentCaptureContext;
|
||||
import android.view.contentcapture.ContentCaptureEvent;
|
||||
import android.view.contentcapture.ContentCaptureSessionId;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.os.IResultReceiver;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
|
||||
final class ContentCaptureServerSession implements ContentCaptureServiceCallbacks {
|
||||
|
||||
@@ -43,18 +42,28 @@ final class ContentCaptureServerSession implements ContentCaptureServiceCallback
|
||||
private final ContentCapturePerUserService mService;
|
||||
private final RemoteContentCaptureService mRemoteService;
|
||||
private final ContentCaptureContext mContentCaptureContext;
|
||||
|
||||
/**
|
||||
* Canonical session id.
|
||||
*/
|
||||
private final String mId;
|
||||
|
||||
/**
|
||||
* UID of the app whose contents is being captured.
|
||||
*/
|
||||
private final int mUid;
|
||||
|
||||
ContentCaptureServerSession(@NonNull Context context, int userId, @NonNull Object lock,
|
||||
@NonNull IBinder activityToken, @NonNull ContentCapturePerUserService service,
|
||||
@NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName,
|
||||
int taskId, int displayId, @NonNull String sessionId,
|
||||
int taskId, int displayId, @NonNull String sessionId, int uid,
|
||||
@Nullable ContentCaptureContext clientContext, int flags,
|
||||
boolean bindInstantServiceAllowed, boolean verbose) {
|
||||
mLock = lock;
|
||||
mActivityToken = activityToken;
|
||||
mService = service;
|
||||
mId = Preconditions.checkNotNull(sessionId);
|
||||
mUid = uid;
|
||||
mRemoteService = new RemoteContentCaptureService(context,
|
||||
ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, userId, this,
|
||||
bindInstantServiceAllowed, verbose);
|
||||
@@ -73,15 +82,8 @@ final class ContentCaptureServerSession implements ContentCaptureServiceCallback
|
||||
* Notifies the {@link ContentCaptureService} that the service started.
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
public void notifySessionStartedLocked() {
|
||||
mRemoteService.onSessionLifecycleRequest(mContentCaptureContext, mId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the {@link ContentCaptureService} of a batch of events.
|
||||
*/
|
||||
public void sendEventsLocked(@NonNull List<ContentCaptureEvent> events) {
|
||||
mRemoteService.onContentCaptureEventsRequest(mId, events);
|
||||
public void notifySessionStartedLocked(@NonNull IResultReceiver clientReceiver) {
|
||||
mRemoteService.onSessionStarted(mContentCaptureContext, mId, mUid, clientReceiver);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,11 +120,11 @@ final class ContentCaptureServerSession implements ContentCaptureServiceCallback
|
||||
@GuardedBy("mLock")
|
||||
public void destroyLocked(boolean notifyRemoteService) {
|
||||
if (mService.isVerbose()) {
|
||||
Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")");
|
||||
Slog.v(TAG, "destroy(notifyRemoteService=" + notifyRemoteService + ")");
|
||||
}
|
||||
// TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
|
||||
if (notifyRemoteService) {
|
||||
mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
|
||||
mRemoteService.onSessionFinished(mId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,13 +135,14 @@ final class ContentCaptureServerSession implements ContentCaptureServiceCallback
|
||||
Slog.d(TAG, "onServiceDied() for " + mId);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
removeSelfLocked(/* notifyRemoteService= */ false);
|
||||
removeSelfLocked(/* notifyRemoteService= */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
|
||||
pw.print(prefix); pw.print("id: "); pw.print(mId); pw.println();
|
||||
pw.print(prefix); pw.print("uid: "); pw.print(mUid); pw.println();
|
||||
pw.print(prefix); pw.print("context: "); mContentCaptureContext.dump(pw); pw.println();
|
||||
pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
|
||||
pw.print(prefix); pw.print("has autofill callback: ");
|
||||
|
||||
@@ -20,17 +20,14 @@ import android.annotation.Nullable;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.IBinder;
|
||||
import android.service.contentcapture.ContentCaptureEventsRequest;
|
||||
import android.service.contentcapture.IContentCaptureService;
|
||||
import android.service.contentcapture.SnapshotData;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.contentcapture.ContentCaptureContext;
|
||||
import android.view.contentcapture.ContentCaptureEvent;
|
||||
|
||||
import com.android.internal.os.IResultReceiver;
|
||||
import com.android.server.infra.AbstractMultiplePendingRequestsRemoteService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
final class RemoteContentCaptureService
|
||||
extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService,
|
||||
IContentCaptureService> {
|
||||
@@ -67,21 +64,19 @@ final class RemoteContentCaptureService
|
||||
|
||||
/**
|
||||
* Called by {@link ContentCaptureServerSession} to generate a call to the
|
||||
* {@link RemoteContentCaptureService} to indicate the session was created (when {@code context}
|
||||
* is not {@code null} or destroyed (when {@code context} is {@code null}).
|
||||
* {@link RemoteContentCaptureService} to indicate the session was created.
|
||||
*/
|
||||
public void onSessionLifecycleRequest(@Nullable ContentCaptureContext context,
|
||||
@NonNull String sessionId) {
|
||||
scheduleAsyncRequest((s) -> s.onSessionLifecycle(context, sessionId));
|
||||
public void onSessionStarted(@Nullable ContentCaptureContext context,
|
||||
@NonNull String sessionId, int uid, @NonNull IResultReceiver clientReceiver) {
|
||||
scheduleAsyncRequest((s) -> s.onSessionStarted(context, sessionId, uid, clientReceiver));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link ContentCaptureServerSession} to send a batch of events to the service.
|
||||
* Called by {@link ContentCaptureServerSession} to generate a call to the
|
||||
* {@link RemoteContentCaptureService} to indicate the session was finished.
|
||||
*/
|
||||
public void onContentCaptureEventsRequest(@NonNull String sessionId,
|
||||
@NonNull List<ContentCaptureEvent> events) {
|
||||
scheduleAsyncRequest((s) -> s.onContentCaptureEventsRequest(sessionId,
|
||||
new ContentCaptureEventsRequest(events)));
|
||||
public void onSessionFinished(@NonNull String sessionId) {
|
||||
scheduleAsyncRequest((s) -> s.onSessionFinished(sessionId));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user