Fixed how session created / removed events are generated.
Long-story short, they must be flushed right away...
Test: atest CtsContentCaptureServiceTestCases
Bug: 121033016
Change-Id: I1b3132ad49674d43bf63717f79848b6e4b23b605
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index c9d46dd..26bf361 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -123,12 +123,12 @@
};
/**
- * List of sessions per UID.
+ * UIDs associated with each session.
*
* <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<>();
+ private final ArrayMap<String, Integer> mSessionUids = new ArrayMap<>();
@CallSuper
@Override
@@ -285,13 +285,13 @@
@Override
@CallSuper
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- final int size = mSessionsByUid.size();
+ final int size = mSessionUids.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));
+ pw.print(prefix); pw.print(mSessionUids.keyAt(i));
+ pw.print(": uid="); pw.println(mSessionUids.valueAt(i));
}
}
}
@@ -309,7 +309,7 @@
private void handleOnCreateSession(@NonNull ContentCaptureContext context,
@NonNull String sessionId, int uid, IResultReceiver clientReceiver) {
- mSessionsByUid.put(sessionId, uid);
+ mSessionUids.put(sessionId, uid);
onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId));
setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE,
mClientInterface.asBinder());
@@ -336,11 +336,11 @@
case ContentCaptureEvent.TYPE_SESSION_STARTED:
final ContentCaptureContext clientContext = event.getClientContext();
clientContext.setParentSessionId(event.getParentSessionId());
- mSessionsByUid.put(sessionIdString, uid);
+ mSessionUids.put(sessionIdString, uid);
onCreateContentCaptureSession(clientContext, sessionId);
break;
case ContentCaptureEvent.TYPE_SESSION_FINISHED:
- mSessionsByUid.remove(sessionIdString);
+ mSessionUids.remove(sessionIdString);
onDestroyContentCaptureSession(sessionId);
break;
default:
@@ -355,7 +355,7 @@
}
private void handleFinishSession(@NonNull String sessionId) {
- mSessionsByUid.remove(sessionId);
+ mSessionUids.remove(sessionId);
onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId));
}
@@ -372,10 +372,13 @@
default:
sessionId = event.getSessionId();
}
- final Integer rightUid = mSessionsByUid.get(sessionId);
+ final Integer rightUid = mSessionUids.get(sessionId);
if (rightUid == null) {
- if (VERBOSE) Log.v(TAG, "No session for " + sessionId);
- // Just ignore, as the session could have finished
+ if (DEBUG) {
+ Log.d(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId
+ + ": " + mSessionUids);
+ }
+ // Just ignore, as the session could have been finished already
return false;
}
if (rightUid != uid) {
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 9e3da92..0d06430 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -251,6 +251,10 @@
public String toString() {
final StringBuilder string = new StringBuilder("ContentCaptureEvent[type=")
.append(getTypeAsString(mType));
+ string.append(", session=").append(mSessionId);
+ if (mType == TYPE_SESSION_STARTED && mParentSessionId != null) {
+ string.append(", parent=").append(mParentSessionId);
+ }
if (mFlags > 0) {
string.append(", flags=").append(mFlags);
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 46e6882..293ba83 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -73,11 +73,11 @@
public static final int STATE_ACTIVE = 2;
/**
- * Session is disabled.
+ * Session is disabled because there is no service for this user.
*
* @hide
*/
- public static final int STATE_DISABLED = 3;
+ public static final int STATE_DISABLED_NO_SERVICE = 3;
/**
* Session is disabled because its id already existed on server.
@@ -164,7 +164,7 @@
*/
public final void destroy() {
if (!mDestroyed.compareAndSet(false, true)) {
- Log.e(TAG, "destroy(): already destroyed");
+ Log.e(TAG, "destroy(" + mId + "): already destroyed");
return;
}
@@ -341,8 +341,8 @@
return "WAITING_FOR_SERVER";
case STATE_ACTIVE:
return "ACTIVE";
- case STATE_DISABLED:
- return "DISABLED";
+ case STATE_DISABLED_NO_SERVICE:
+ return "DISABLED_NO_SERVICE";
case STATE_DISABLED_DUPLICATED_ID:
return "DISABLED_DUPLICATED_ID";
default:
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index baf4a35..c23c85a 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -171,6 +171,7 @@
@Override
void onDestroy() {
+ mHandler.removeMessages(MSG_FLUSH);
mHandler.sendMessage(
obtainMessage(MainContentCaptureSession::handleDestroySession, this));
}
@@ -237,7 +238,7 @@
Log.w(TAG, "Failed to link to death on " + binder + ": " + e);
}
}
- if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) {
+ if (resultCode == STATE_DISABLED_NO_SERVICE || resultCode == STATE_DISABLED_DUPLICATED_ID) {
mDisabled.set(true);
handleResetSession(/* resetState= */ false);
} else {
@@ -246,7 +247,7 @@
if (VERBOSE) {
Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
+ ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
- + ", binder=" + binder);
+ + ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size()));
}
}
@@ -285,14 +286,14 @@
return;
}
- if (mState != STATE_ACTIVE) {
+ if (mState != STATE_ACTIVE && numberEvents >= MAX_BUFFER_SIZE) {
// Callback from startSession hasn't been called yet - typically happens on system
// apps that are started before the system service
// TODO(b/111276913): try to ignore session while system is not ready / boot
// not complete instead. Similarly, the manager service should return right away
// when the user does not have a service set
- if (VERBOSE) {
- Log.v(TAG, "Closing session for " + getActivityDebugName()
+ if (DEBUG) {
+ Log.d(TAG, "Closing session for " + getActivityDebugName()
+ " after " + numberEvents + " delayed events and state "
+ getStateAsString(mState));
}
@@ -331,7 +332,9 @@
if (mEvents == null) return;
if (mDirectServiceInterface == null) {
- if (DEBUG) Log.d(TAG, "handleForceFlush(): hold your horses, client not ready yet!");
+ if (VERBOSE) {
+ Log.v(TAG, "handleForceFlush(): hold your horses, client not ready: " + mEvents);
+ }
if (!mHandler.hasMessages(MSG_FLUSH)) {
handleScheduleFlush(/* checkExisting= */ false);
}
@@ -435,14 +438,14 @@
new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
.setParentSessionId(parentSessionId)
.setClientContext(clientContext),
- /* forceFlush= */ false));
+ /* forceFlush= */ true));
}
void notifyChildSessionFinished(@NonNull String parentSessionId,
@NonNull String childSessionId) {
mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
- .setParentSessionId(parentSessionId), /* forceFlush= */ false));
+ .setParentSessionId(parentSessionId), /* forceFlush= */ true));
}
void notifyViewAppeared(@NonNull String sessionId, @NonNull ViewStructureImpl node) {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index c467935..09aa421 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -95,7 +95,7 @@
final ComponentName serviceComponentName = updateServiceInfoLocked();
if (serviceComponentName == null) {
- Slog.w(TAG, "updateRemoteService(): no service componennt name");
+ if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): no service component name");
return;
}
@@ -163,7 +163,10 @@
@NonNull String sessionId, int uid, int flags,
@NonNull IResultReceiver clientReceiver) {
if (!isEnabledLocked()) {
- setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED, /* binder=*/ null);
+ // TODO: it would be better to split in differet reasons, like
+ // STATE_DISABLED_NO_SERVICE and STATE_DISABLED_BY_DEVICE_POLICY
+ setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_NO_SERVICE,
+ /* binder= */ null);
return;
}
final ComponentName serviceComponentName = getServiceComponentName();
@@ -195,8 +198,8 @@
Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
+ ": ignoring because service is not set");
// TODO(b/111276913): use a new disabled state?
- setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED,
- /* binder=*/ null);
+ setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_NO_SERVICE,
+ /* binder= */ null);
return;
}
@@ -285,7 +288,7 @@
final int numSessions = mSessions.size();
for (int i = 0; i < numSessions; i++) {
final ContentCaptureServerSession session = mSessions.valueAt(i);
- session.destroyLocked(true);
+ session.destroyLocked(/* notifyRemoteService= */ true);
}
mSessions.clear();
}