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:
Felipe Leme
2018-11-05 15:03:04 -08:00
parent 9a81857fde
commit 7a53408d19
12 changed files with 289 additions and 39 deletions

View File

@@ -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);
}
/**

View File

@@ -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);
}

View File

@@ -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);

View 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;

View File

@@ -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;
}
}
}

View File

@@ -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);
}

View File

@@ -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;