Refactored InteractionSessionId.
Rather than use a local and global id, just use a local, unique id.
Such change not only simplifies the code, but makes it consistent with
TextClassificationSessionId.
Test: mmm -j packages/experimental/FillService && \
adb install -r ${OUT}/data/app/FillService/FillService.apk && \
adb shell settings put secure intel_service foo.bar.fill/.AiaiService
Bug: 111276913
Change-Id: I0e610f825aab1d2b32bbafa1bd3d3c7897d889d6
diff --git a/core/java/android/service/intelligence/InteractionSessionId.java b/core/java/android/service/intelligence/InteractionSessionId.java
index ca68f8e..a2971ae 100644
--- a/core/java/android/service/intelligence/InteractionSessionId.java
+++ b/core/java/android/service/intelligence/InteractionSessionId.java
@@ -16,43 +16,85 @@
package android.service.intelligence;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.io.PrintWriter;
+import java.util.UUID;
// TODO(b/111276913): add javadocs / implement equals/hashcode/string
/** @hide */
@SystemApi
public final class InteractionSessionId implements Parcelable {
- private final int mGlobalId;
+ private final @NonNull String mValue;
- // TODO(b/111276913): remove if not needed
- private final int mLocalId;
-
- /** @hide */
- public InteractionSessionId(int globalId, int localId) {
- mGlobalId = globalId;
- mLocalId = localId;
+ /**
+ * Creates a new instance.
+ *
+ * @hide
+ */
+ public InteractionSessionId() {
+ this(UUID.randomUUID().toString());
}
- /** @hide */
- public int getGlobalId() {
- return mGlobalId;
+ /**
+ * Creates a new instance.
+ *
+ * @param value The internal value.
+ *
+ * @hide
+ */
+ public InteractionSessionId(@NonNull String value) {
+ mValue = value;
+ }
+
+ /**
+ * @hide
+ */
+ public String getValue() {
+ return mValue;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((mValue == null) ? 0 : mValue.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ final InteractionSessionId other = (InteractionSessionId) obj;
+ if (mValue == null) {
+ if (other.mValue != null) return false;
+ } else if (!mValue.equals(other.mValue)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * <p><b>NOTE: </b>this method is only useful for debugging purposes and is not guaranteed to
+ * be stable, hence it should not be used to identify the session.
+ */
+ @Override
+ public String toString() {
+ return mValue;
}
/** @hide */
// TODO(b/111276913): dump to proto as well
public void dump(PrintWriter pw) {
- pw.print("globalId="); pw.print(mGlobalId);
- pw.print("localId="); pw.print(mLocalId);
- }
-
- @Override
- public String toString() {
- return "SessionId[globalId=" + mGlobalId + ", localId=" + mLocalId + "]";
+ pw.print(mValue);
}
@Override
@@ -62,8 +104,7 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeInt(mGlobalId);
- parcel.writeInt(mLocalId);
+ parcel.writeString(mValue);
}
public static final Parcelable.Creator<InteractionSessionId> CREATOR =
@@ -71,9 +112,7 @@
@Override
public InteractionSessionId createFromParcel(Parcel parcel) {
- final int globalId = parcel.readInt();
- final int localId = parcel.readInt();
- return new InteractionSessionId(globalId, localId);
+ return new InteractionSessionId(parcel.readString());
}
@Override
diff --git a/core/java/android/view/intelligence/IIntelligenceManager.aidl b/core/java/android/view/intelligence/IIntelligenceManager.aidl
index 7bbe99a..2f128de 100644
--- a/core/java/android/view/intelligence/IIntelligenceManager.aidl
+++ b/core/java/android/view/intelligence/IIntelligenceManager.aidl
@@ -17,9 +17,8 @@
package android.view.intelligence;
import android.content.ComponentName;
-
import android.os.IBinder;
-
+import android.service.intelligence.InteractionSessionId;
import android.view.intelligence.ContentCaptureEvent;
import com.android.internal.os.IResultReceiver;
@@ -34,19 +33,16 @@
* Starts a session, sending the "remote" sessionId to the receiver.
*/
void startSession(int userId, IBinder activityToken, in ComponentName componentName,
- int localSessionId, int flags, in IResultReceiver result);
+ in InteractionSessionId sessionId, int flags, in IResultReceiver result);
/**
* Finishes a session.
*/
- // TODO(b/111276913): pass just (global) session id
- void finishSession(int userId, IBinder activityToken, in ComponentName componentName,
- int localSessionId, int globalSessionId);
+ void finishSession(int userId, in InteractionSessionId sessionId);
/**
* Sends a batch of events
*/
- // TODO(b/111276913): pass just (global) session id
- void sendEvents(int userId, IBinder activityToken, in ComponentName componentName,
- int localSessionId, int globalSessionId, in List<ContentCaptureEvent> events);
+ void sendEvents(int userId, in InteractionSessionId sessionId,
+ in List<ContentCaptureEvent> events);
}
diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java
index a30d77e..9bf6c2c 100644
--- a/core/java/android/view/intelligence/IntelligenceManager.java
+++ b/core/java/android/view/intelligence/IntelligenceManager.java
@@ -25,6 +25,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.service.intelligence.InteractionSessionId;
import android.util.Log;
import android.view.intelligence.ContentCaptureEvent.EventType;
@@ -55,9 +56,6 @@
public static final int FLAG_USER_INPUT = 0x1;
- /** @hide */
- public static final int NO_SESSION = 0;
-
/**
* Initial state, when there is no session.
*
@@ -66,11 +64,11 @@
public static final int STATE_UNKNOWN = 0;
/**
- * Service's startSession() was called, but remote session id was not returned yet.
+ * Service's startSession() was called, but server didn't confirm it was created yet.
*
* @hide
*/
- public static final int STATE_WAITING_FOR_SESSION_ID = 1;
+ public static final int STATE_WAITING_FOR_SERVER = 1;
/**
* Session is active.
@@ -79,8 +77,6 @@
*/
public static final int STATE_ACTIVE = 2;
- private static int sNextSessionId;
-
private final Context mContext;
@Nullable
@@ -88,14 +84,9 @@
private final Object mLock = new Object();
- // TODO(b/111276913): localSessionId might be an overkill, perhaps just the global id is enough.
- // Let's keep both for now, and revisit once we decide whether the session id will be persisted
- // when the activity's process is killed
+ @Nullable
@GuardedBy("mLock")
- private int mLocalSessionId = NO_SESSION;
-
- @GuardedBy("mLock")
- private int mRemoteSessionId = NO_SESSION;
+ private InteractionSessionId mId;
@GuardedBy("mLock")
private int mState = STATE_UNKNOWN;
@@ -124,27 +115,25 @@
+ getStateAsStringLocked());
return;
}
- mState = STATE_WAITING_FOR_SESSION_ID;
- mLocalSessionId = ++sNextSessionId;
- mRemoteSessionId = NO_SESSION;
+ mState = STATE_WAITING_FOR_SERVER;
+ mId = new InteractionSessionId();
mApplicationToken = token;
mComponentName = componentName;
if (VERBOSE) {
Log.v(TAG, "onActivityStarted(): token=" + token + ", act=" + componentName
- + ", localSessionId=" + mLocalSessionId);
+ + ", id=" + mId);
}
final int flags = 0; // TODO(b/111276913): get proper flags
try {
mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
- mLocalSessionId, flags, new IResultReceiver.Stub() {
+ mId, flags, new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData)
throws RemoteException {
synchronized (mLock) {
if (resultCode > 0) {
- mRemoteSessionId = resultCode;
mState = STATE_ACTIVE;
} else {
// TODO(b/111276913): handle other cases like disabled by
@@ -153,7 +142,7 @@
}
if (VERBOSE) {
Log.v(TAG, "onActivityStarted() result: code=" + resultCode
- + ", remoteSession=" + mRemoteSessionId
+ + ", id=" + mId
+ ", state=" + getStateAsStringLocked());
}
}
@@ -189,8 +178,7 @@
}
try {
- mService.sendEvents(mContext.getUserId(), mApplicationToken, mComponentName,
- mLocalSessionId, mRemoteSessionId, events);
+ mService.sendEvents(mContext.getUserId(), mId, events);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -207,15 +195,13 @@
if (VERBOSE) {
Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsStringLocked()
- + ", localSessionId=" + mLocalSessionId
- + ", mRemoteSessionId=" + mRemoteSessionId);
+ + ", mId=" + mId);
}
try {
- mService.finishSession(mContext.getUserId(), mApplicationToken, mComponentName,
- mLocalSessionId, mRemoteSessionId);
+ mService.finishSession(mContext.getUserId(), mId);
mState = STATE_UNKNOWN;
- mLocalSessionId = mRemoteSessionId = NO_SESSION;
+ mId = null;
mApplicationToken = null;
mComponentName = null;
} catch (RemoteException e) {
@@ -334,12 +320,11 @@
pw.print(prefix2); pw.print("mService: "); pw.println(mService);
pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
pw.print(prefix2); pw.print("enabled: "); pw.println(isContentCaptureEnabled());
- pw.print(prefix2); pw.print("mLocalSessionId: "); pw.println(mLocalSessionId);
- pw.print(prefix2); pw.print("mRemoteSessionId: "); pw.println(mRemoteSessionId);
- pw.print(prefix2); pw.print("mState: "); pw.print(mState); pw.print(" (");
+ pw.print(prefix2); pw.print("id: "); pw.println(mId);
+ pw.print(prefix2); pw.print("state: "); pw.print(mState); pw.print(" (");
pw.print(getStateAsStringLocked()); pw.println(")");
- pw.print(prefix2); pw.print("mAppToken: "); pw.println(mApplicationToken);
- pw.print(prefix2); pw.print("mComponentName: "); pw.println(mComponentName);
+ pw.print(prefix2); pw.print("appToken: "); pw.println(mApplicationToken);
+ pw.print(prefix2); pw.print("componentName: "); pw.println(mComponentName);
}
}
@@ -353,8 +338,8 @@
switch (state) {
case STATE_UNKNOWN:
return "UNKNOWN";
- case STATE_WAITING_FOR_SESSION_ID:
- return "WAITING_FOR_SESSION_ID";
+ case STATE_WAITING_FOR_SERVER:
+ return "WAITING_FOR_SERVER";
case STATE_ACTIVE:
return "ACTIVE";
default:
diff --git a/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java b/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java
index 14b28d3..9cab1ed 100644
--- a/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java
+++ b/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java
@@ -26,6 +26,7 @@
import android.view.intelligence.ContentCaptureEvent;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
import com.android.server.AbstractRemoteService;
import com.android.server.intelligence.RemoteIntelligenceService.RemoteIntelligenceServiceCallbacks;
@@ -47,15 +48,15 @@
ContentCaptureSession(@NonNull Context context, int userId, @NonNull Object lock,
@NonNull IBinder activityToken, @NonNull IntelligencePerUserService service,
@NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName,
- int taskId, int displayId, int localSessionId, int globalSessionId, int flags,
+ int taskId, int displayId, @NonNull InteractionSessionId sessionId, int flags,
boolean bindInstantServiceAllowed, boolean verbose) {
mLock = lock;
mActivityToken = activityToken;
mService = service;
+ mId = Preconditions.checkNotNull(sessionId);
mRemoteService = new RemoteIntelligenceService(context,
IntelligenceService.SERVICE_INTERFACE, serviceComponentName, userId, this,
bindInstantServiceAllowed, verbose);
- mId = new InteractionSessionId(globalSessionId, localSessionId);
mInterationContext = new InteractionContext(appComponentName, taskId, displayId, flags);
}
@@ -88,7 +89,7 @@
mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
}
} finally {
- mService.removeSessionLocked(mInterationContext.getActivityComponent());
+ mService.removeSessionLocked(mId);
}
}
@@ -114,17 +115,15 @@
}
}
- /**
- * Gets global id, unique per {@link IntelligencePerUserService}.
- */
- public int getGlobalSessionId() {
- return mId.getGlobalId();
- }
-
@GuardedBy("mLock")
public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("id: "); mId.dump(pw); pw.println();
pw.print(prefix); pw.print("context: "); mInterationContext.dump(pw); pw.println();
pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
}
+
+ @Override
+ public String toString() {
+ return "ContentCaptureSession[id=" + mId.getValue() + ", act=" + mActivityToken + "]";
+ }
}
diff --git a/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java
index d67c15b..43d4a44 100644
--- a/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ b/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.os.IBinder;
import android.os.UserManager;
+import android.service.intelligence.InteractionSessionId;
import android.view.intelligence.ContentCaptureEvent;
import android.view.intelligence.IIntelligenceManager;
@@ -87,9 +88,11 @@
@Override
public void startSession(int userId, @NonNull IBinder activityToken,
- @NonNull ComponentName componentName, int localSessionId, int flags,
- @NonNull IResultReceiver result) {
+ @NonNull ComponentName componentName, @NonNull InteractionSessionId sessionId,
+ int flags, @NonNull IResultReceiver result) {
Preconditions.checkNotNull(activityToken);
+ Preconditions.checkNotNull(componentName);
+ Preconditions.checkNotNull(sessionId);
// TODO(b/111276913): refactor getTaskIdForActivity() to also return ComponentName,
// so we don't pass it on startSession (same for Autofill)
@@ -101,31 +104,29 @@
synchronized (mLock) {
final IntelligencePerUserService service = getServiceForUserLocked(userId);
service.startSessionLocked(activityToken, componentName, taskId, displayId,
- localSessionId, flags, result);
+ sessionId, flags, result);
}
}
@Override
- public void sendEvents(int userId, @NonNull IBinder activityToken,
- @NonNull ComponentName componentName, int localSessionId, int globalSessionId,
- List<ContentCaptureEvent> events) {
+ public void sendEvents(int userId, @NonNull InteractionSessionId sessionId,
+ @NonNull List<ContentCaptureEvent> events) {
+ Preconditions.checkNotNull(sessionId);
Preconditions.checkNotNull(events);
synchronized (mLock) {
final IntelligencePerUserService service = getServiceForUserLocked(userId);
- service.sendEventsLocked(componentName, events);
+ service.sendEventsLocked(sessionId, events);
}
}
@Override
- public void finishSession(int userId, @NonNull IBinder activityToken,
- @NonNull ComponentName componentName, int localSessionId, int globalSessionId) {
- Preconditions.checkNotNull(activityToken);
+ public void finishSession(int userId, @NonNull InteractionSessionId sessionId) {
+ Preconditions.checkNotNull(sessionId);
synchronized (mLock) {
final IntelligencePerUserService service = getServiceForUserLocked(userId);
- service.finishSessionLocked(activityToken, componentName, localSessionId,
- globalSessionId);
+ service.finishSessionLocked(sessionId);
}
}
diff --git a/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java
index 1d98819..584b872 100644
--- a/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java
+++ b/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java
@@ -25,9 +25,11 @@
import android.content.pm.ServiceInfo;
import android.os.IBinder;
import android.os.RemoteException;
+import android.service.intelligence.InteractionSessionId;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.intelligence.ContentCaptureEvent;
+import android.view.intelligence.IntelligenceManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
@@ -44,11 +46,9 @@
private static final String TAG = "IntelligencePerUserService";
- private static int sNextSessionId;
-
- // TODO(b/111276913): should key by componentName + taskId or ActivityToken
@GuardedBy("mLock")
- private final ArrayMap<ComponentName, ContentCaptureSession> mSessions = new ArrayMap<>();
+ private final ArrayMap<InteractionSessionId, ContentCaptureSession> mSessions =
+ new ArrayMap<>();
// TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
@@ -85,8 +85,9 @@
// TODO(b/111276913): log metrics
@GuardedBy("mLock")
public void startSessionLocked(@NonNull IBinder activityToken,
- @NonNull ComponentName componentName, int taskId, int displayId, int localSessionId,
- int flags, @NonNull IResultReceiver resultReceiver) {
+ @NonNull ComponentName componentName, int taskId, int displayId,
+ @NonNull InteractionSessionId sessionId, int flags,
+ @NonNull IResultReceiver resultReceiver) {
final ComponentName serviceComponentName = getServiceComponentName();
if (serviceComponentName == null) {
// TODO(b/111276913): this happens when the system service is starting, we should
@@ -96,16 +97,16 @@
return;
}
- ContentCaptureSession session = mSessions.get(componentName);
+ ContentCaptureSession session = mSessions.get(sessionId);
if (session != null) {
if (mMaster.debug) {
- Slog.d(TAG, "startSession(): reusing session " + session.getGlobalSessionId()
- + " for " + componentName);
+ 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, session.getGlobalSessionId());
+ sendToClient(resultReceiver, IntelligenceManager.STATE_ACTIVE);
return;
}
@@ -113,59 +114,53 @@
final boolean bindInstantServiceAllowed = false;
session = new ContentCaptureSession(getContext(), mUserId, mLock, activityToken,
- this, serviceComponentName, componentName, taskId, displayId, localSessionId,
- ++sNextSessionId, flags, bindInstantServiceAllowed, mMaster.verbose);
+ this, serviceComponentName, componentName, taskId, displayId, sessionId, flags,
+ bindInstantServiceAllowed, mMaster.verbose);
if (mMaster.verbose) {
- Slog.v(TAG, "startSession(): new session for " + componentName + "; globalId ="
- + session.getGlobalSessionId());
+ Slog.v(TAG, "startSession(): new session for " + componentName + " and id "
+ + sessionId);
}
- mSessions.put(componentName, session);
+ mSessions.put(sessionId, session);
session.notifySessionStartedLocked();
- sendToClient(resultReceiver, session.getGlobalSessionId());
+ sendToClient(resultReceiver, IntelligenceManager.STATE_ACTIVE);
}
// TODO(b/111276913): log metrics
@GuardedBy("mLock")
- public void finishSessionLocked(@NonNull IBinder activityToken,
- @NonNull ComponentName componentName, int localSessionId, int globalSessionId) {
- final ContentCaptureSession session = mSessions.get(componentName);
+ public void finishSessionLocked(@NonNull InteractionSessionId sessionId) {
+ final ContentCaptureSession session = mSessions.get(sessionId);
if (session == null) {
- Slog.w(TAG, "finishSession(): no session for " + componentName);
+ Slog.w(TAG, "finishSession(): no session with id" + sessionId);
return;
}
if (mMaster.verbose) {
- Slog.v(TAG, "finishSession(): comp=" + componentName + "; globalId ="
- + session.getGlobalSessionId());
+ Slog.v(TAG, "finishSession(): " + session);
}
- // TODO(b/111276913): check if all arguments match existing session and throw exception if
- // not. Or just use componentName if we change AIDL to pass just ApplicationToken and
- // retrieve componentName from AMInternal
session.removeSelfLocked(true);
}
@GuardedBy("mLock")
- public void sendEventsLocked(@NonNull ComponentName componentName,
+ public void sendEventsLocked(@NonNull InteractionSessionId sessionId,
@NonNull List<ContentCaptureEvent> events) {
- final ContentCaptureSession session = mSessions.get(componentName);
+ final ContentCaptureSession session = mSessions.get(sessionId);
if (session == null) {
- Slog.w(TAG, "sendEventsLocked(): no session for " + componentName);
+ Slog.w(TAG, "sendEvents(): no session for " + sessionId);
return;
}
if (mMaster.verbose) {
- Slog.v(TAG, "sendEventsLocked(): comp=" + componentName + "; events =" + events.size());
+ Slog.v(TAG, "sendEvents(): id=" + sessionId + "; events =" + events.size());
}
session.sendEventsLocked(events);
}
@GuardedBy("mLock")
- public void removeSessionLocked(@NonNull ComponentName key) {
- mSessions.remove(key);
+ public void removeSessionLocked(@NonNull InteractionSessionId sessionId) {
+ mSessions.remove(sessionId);
}
@Override
protected void dumpLocked(String prefix, PrintWriter pw) {
super.dumpLocked(prefix, pw);
- pw.print(prefix); pw.print("next id: "); pw.println(sNextSessionId);
if (mSessions.isEmpty()) {
pw.print(prefix); pw.println("no sessions");
} else {
@@ -187,5 +182,4 @@
Slog.w(TAG, "Error async reporting result to client: " + e);
}
}
-
}