Initial implementation of IntelligenceService.onContentCaptureEvents()
It's only implementing the activity lifecycle events, but at least they're fed
end-to-end in the pipeline (from app to service implementation).
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: I2f46be1e6a6a21ad909e5fffea01bedaa28fb300
This commit is contained in:
@@ -120,6 +120,7 @@ import android.view.autofill.AutofillManager;
|
||||
import android.view.autofill.AutofillManager.AutofillClient;
|
||||
import android.view.autofill.AutofillPopupWindow;
|
||||
import android.view.autofill.IAutofillWindowPresenter;
|
||||
import android.view.intelligence.ContentCaptureEvent;
|
||||
import android.view.intelligence.IntelligenceManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Toast;
|
||||
@@ -1023,6 +1024,31 @@ public class Activity extends ContextThemeWrapper
|
||||
return mIntelligenceManager;
|
||||
}
|
||||
|
||||
private void notifyIntelligenceManagerIfNeeded(@ContentCaptureEvent.EventType int event) {
|
||||
final IntelligenceManager im = getIntelligenceManager();
|
||||
if (im == null || !im.isContentCaptureEnabled()) {
|
||||
return;
|
||||
}
|
||||
switch (event) {
|
||||
case ContentCaptureEvent.TYPE_ACTIVITY_CREATED:
|
||||
//TODO(b/111276913): decide whether the InteractionSessionId should be
|
||||
// saved / restored in the activity bundle.
|
||||
im.onActivityCreated(mToken, getComponentName());
|
||||
break;
|
||||
case ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED:
|
||||
im.onActivityDestroyed();
|
||||
break;
|
||||
case ContentCaptureEvent.TYPE_ACTIVITY_STARTED:
|
||||
case ContentCaptureEvent.TYPE_ACTIVITY_RESUMED:
|
||||
case ContentCaptureEvent.TYPE_ACTIVITY_PAUSED:
|
||||
case ContentCaptureEvent.TYPE_ACTIVITY_STOPPED:
|
||||
im.onActivityLifecycleEvent(event);
|
||||
break;
|
||||
default:
|
||||
Log.w(TAG, "notifyIntelligenceManagerIfNeeded(): invalid type " + event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context newBase) {
|
||||
super.attachBaseContext(newBase);
|
||||
@@ -1099,11 +1125,7 @@ public class Activity extends ContextThemeWrapper
|
||||
mRestoredFromBundle = savedInstanceState != null;
|
||||
mCalled = true;
|
||||
|
||||
if (getIntelligenceManager() != null) {
|
||||
//TODO(b/111276913): decide whether the screen_obs session id should be saved / restored
|
||||
// in the activity bundle.
|
||||
mIntelligenceManager.onActivityCreated(mToken, getComponentName());
|
||||
}
|
||||
notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_CREATED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1337,6 +1359,7 @@ public class Activity extends ContextThemeWrapper
|
||||
if (mAutoFillResetNeeded) {
|
||||
getAutofillManager().onVisibleForAutofill();
|
||||
}
|
||||
notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STARTED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1419,6 +1442,7 @@ public class Activity extends ContextThemeWrapper
|
||||
}
|
||||
}
|
||||
}
|
||||
notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_RESUMED);
|
||||
mCalled = true;
|
||||
}
|
||||
|
||||
@@ -1812,6 +1836,7 @@ public class Activity extends ContextThemeWrapper
|
||||
mAutoFillIgnoreFirstResumePause = false;
|
||||
}
|
||||
}
|
||||
notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_PAUSED);
|
||||
mCalled = true;
|
||||
}
|
||||
|
||||
@@ -2000,6 +2025,7 @@ public class Activity extends ContextThemeWrapper
|
||||
getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL,
|
||||
mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
|
||||
}
|
||||
notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STOPPED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2071,9 +2097,8 @@ public class Activity extends ContextThemeWrapper
|
||||
|
||||
getApplication().dispatchActivityDestroyed(this);
|
||||
|
||||
if (getIntelligenceManager() != null) {
|
||||
mIntelligenceManager.onActivityDestroyed();
|
||||
}
|
||||
notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,11 @@ package android.service.intelligence;
|
||||
import android.service.intelligence.InteractionSessionId;
|
||||
import android.service.intelligence.InteractionContext;
|
||||
|
||||
import android.view.intelligence.ContentCaptureEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Interface from the system to an intelligence service.
|
||||
*
|
||||
@@ -28,4 +33,7 @@ oneway interface IIntelligenceService {
|
||||
|
||||
// Called when session is created (context not null) or destroyed (context null)
|
||||
void onSessionLifecycle(in InteractionContext context, in InteractionSessionId sessionId);
|
||||
|
||||
void onContentCaptureEvents(in InteractionSessionId sessionId,
|
||||
in List<ContentCaptureEvent> events);
|
||||
}
|
||||
|
||||
@@ -70,6 +70,14 @@ public abstract class IntelligenceService extends Service {
|
||||
IntelligenceService.this, sessionId));
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onContentCaptureEvents(InteractionSessionId sessionId,
|
||||
List<ContentCaptureEvent> events) {
|
||||
mHandler.sendMessage(
|
||||
obtainMessage(IntelligenceService::onContentCaptureEvent,
|
||||
IntelligenceService.this, sessionId, events));
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@CallSuper
|
||||
@@ -105,6 +113,7 @@ public abstract class IntelligenceService extends Service {
|
||||
* @param sessionId the session's Id
|
||||
* @param events the events
|
||||
*/
|
||||
// TODO(b/111276913): rename to onContentCaptureEvents
|
||||
public abstract void onContentCaptureEvent(@NonNull InteractionSessionId sessionId,
|
||||
@NonNull List<ContentCaptureEvent> events);
|
||||
|
||||
|
||||
19
core/java/android/view/intelligence/ContentCaptureEvent.aidl
Normal file
19
core/java/android/view/intelligence/ContentCaptureEvent.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.intelligence;
|
||||
|
||||
parcelable ContentCaptureEvent;
|
||||
@@ -30,6 +30,11 @@ import java.lang.annotation.RetentionPolicy;
|
||||
@SystemApi
|
||||
public final class ContentCaptureEvent implements Parcelable {
|
||||
|
||||
/** @hide */
|
||||
public static final int TYPE_ACTIVITY_DESTROYED = -2;
|
||||
/** @hide */
|
||||
public static final int TYPE_ACTIVITY_CREATED = -1;
|
||||
|
||||
/**
|
||||
* Called when the activity is started.
|
||||
*/
|
||||
@@ -85,10 +90,18 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
TYPE_VIEW_TEXT_CHANGED
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface EventType{}
|
||||
public @interface EventType{}
|
||||
|
||||
private final int mType;
|
||||
private final long mEventTime;
|
||||
private final int mFlags;
|
||||
|
||||
|
||||
/** @hide */
|
||||
ContentCaptureEvent() {
|
||||
public ContentCaptureEvent(int type, long eventTime, int flags) {
|
||||
mType = type;
|
||||
mEventTime = eventTime;
|
||||
mFlags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,14 +112,14 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
* {@link #TYPE_VIEW_ADDED}, {@link #TYPE_VIEW_REMOVED}, or {@link #TYPE_VIEW_TEXT_CHANGED}.
|
||||
*/
|
||||
public @EventType int getType() {
|
||||
return 42;
|
||||
return mType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets when the event was generated, in ms.
|
||||
*/
|
||||
public long getEventTime() {
|
||||
return 48151623;
|
||||
return mEventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +129,7 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
* {@link android.view.intelligence.IntelligenceManager#FLAG_USER_INPUT}.
|
||||
*/
|
||||
public int getFlags() {
|
||||
return 0;
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,6 +162,16 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder string = new StringBuilder("ContentCaptureEvent[type=")
|
||||
.append(getTypeAsString(mType)).append(", time=").append(mEventTime);
|
||||
if (mFlags > 0) {
|
||||
string.append(", flags=").append(mFlags);
|
||||
}
|
||||
return string.append(']').toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
@@ -156,7 +179,9 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
// TODO(b/111276913): implement
|
||||
parcel.writeInt(mType);
|
||||
parcel.writeLong(mEventTime);
|
||||
parcel.writeInt(mFlags);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<ContentCaptureEvent> CREATOR =
|
||||
@@ -164,8 +189,10 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
|
||||
@Override
|
||||
public ContentCaptureEvent createFromParcel(Parcel parcel) {
|
||||
// TODO(b/111276913): implement
|
||||
return null;
|
||||
final int type = parcel.readInt();
|
||||
final long eventTime = parcel.readLong();
|
||||
final int flags = parcel.readInt();
|
||||
return new ContentCaptureEvent(type, eventTime, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,4 +200,27 @@ public final class ContentCaptureEvent implements Parcelable {
|
||||
return new ContentCaptureEvent[size];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @hide */
|
||||
public static String getTypeAsString(@EventType int type) {
|
||||
switch (type) {
|
||||
case TYPE_ACTIVITY_STARTED:
|
||||
return "ACTIVITY_STARTED";
|
||||
case TYPE_ACTIVITY_RESUMED:
|
||||
return "ACTIVITY_RESUMED";
|
||||
case TYPE_ACTIVITY_PAUSED:
|
||||
return "ACTIVITY_PAUSED";
|
||||
case TYPE_ACTIVITY_STOPPED:
|
||||
return "ACTIVITY_STOPPED";
|
||||
case TYPE_VIEW_ADDED:
|
||||
return "VIEW_ADDED";
|
||||
case TYPE_VIEW_REMOVED:
|
||||
return "VIEW_REMOVED";
|
||||
case TYPE_VIEW_TEXT_CHANGED:
|
||||
return "VIEW_TEXT_CHANGED";
|
||||
default:
|
||||
return "UKNOWN_TYPE: " + type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,15 @@
|
||||
package android.view.intelligence;
|
||||
|
||||
import android.content.ComponentName;
|
||||
|
||||
import android.os.IBinder;
|
||||
|
||||
import android.view.intelligence.ContentCaptureEvent;
|
||||
|
||||
import com.android.internal.os.IResultReceiver;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
@@ -33,6 +39,14 @@ oneway interface IIntelligenceManager {
|
||||
/**
|
||||
* Finishes a session.
|
||||
*/
|
||||
// TODO(b/111276913): pass just (global) session id
|
||||
void finishSession(int userId, IBinder activityToken, in ComponentName componentName,
|
||||
int localSessionId, int globalSessionId);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -24,13 +24,17 @@ import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.intelligence.ContentCaptureEvent.EventType;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.os.IResultReceiver;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -161,6 +165,38 @@ public final class IntelligenceManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for intermediate events (i.e, other than created and destroyed).
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void onActivityLifecycleEvent(@EventType int type) {
|
||||
if (!isContentCaptureEnabled()) return;
|
||||
|
||||
//TODO(b/111276913): should buffer event (and call service on handler thread), instead of
|
||||
// calling right away
|
||||
final ContentCaptureEvent event = new ContentCaptureEvent(type, SystemClock.uptimeMillis(),
|
||||
0);
|
||||
final List<ContentCaptureEvent> events = Arrays.asList(event);
|
||||
|
||||
synchronized (mLock) {
|
||||
//TODO(b/111276913): check session state; for example, how to handle if it's waiting for
|
||||
// remote id
|
||||
|
||||
if (VERBOSE) {
|
||||
Log.v(TAG, "onActivityLifecycleEvent() for " + mComponentName.flattenToShortString()
|
||||
+ ": " + ContentCaptureEvent.getTypeAsString(type));
|
||||
}
|
||||
|
||||
try {
|
||||
mService.sendEvents(mContext.getUserId(), mApplicationToken, mComponentName,
|
||||
mLocalSessionId, mRemoteSessionId, events);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void onActivityDestroyed() {
|
||||
if (!isContentCaptureEnabled()) return;
|
||||
|
||||
Reference in New Issue
Block a user