diff options
| author | 2019-02-27 17:01:51 -0800 | |
|---|---|---|
| committer | 2019-03-07 13:06:21 -0800 | |
| commit | 141864d61086c94fa3c04edac2c60858845c8af2 (patch) | |
| tree | cb2c8c43cf0f85b0adaacb3b43b0ebf59d37cbfc | |
| parent | b14ac6e9af3faa80b0c7cd48e36bda2767e5ed17 (diff) | |
New API: ContentCaptureService.onActivityEvent()
This API is needed for high-level events that happened on activities that are
not whitelisted for Content Capture.
Test: atest CtsContentCaptureServiceTestCases:android.contentcaptureservice.cts.LoginActivityTest#testSimpleLifecycle_defaultSession
Test: atest CtsContentCaptureServiceTestCases # sanity check
Test: m update-api
Bug: 126262658
Change-Id: Id2d4ccfb04d56eba561200d6875374a932c526ae
12 files changed, 273 insertions, 2 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index ced52599948f..ff3ce9a75e2f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -6436,9 +6436,20 @@ package android.service.carrier { package android.service.contentcapture { + public final class ActivityEvent implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.content.ComponentName getComponentName(); + method public int getEventType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.contentcapture.ActivityEvent> CREATOR; + field public static final int TYPE_ACTIVITY_PAUSED = 2; // 0x2 + field public static final int TYPE_ACTIVITY_RESUMED = 1; // 0x1 + } + public abstract class ContentCaptureService extends android.app.Service { ctor public ContentCaptureService(); method public final void disableContentCaptureServices(); + method public void onActivityEvent(@NonNull android.service.contentcapture.ActivityEvent); method public void onActivitySnapshot(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.SnapshotData); method public void onConnected(); method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent); diff --git a/api/test-current.txt b/api/test-current.txt index 3b0295488ded..8b04dd000554 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -2297,9 +2297,20 @@ package android.service.autofill.augmented { package android.service.contentcapture { + public final class ActivityEvent implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.content.ComponentName getComponentName(); + method public int getEventType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.contentcapture.ActivityEvent> CREATOR; + field public static final int TYPE_ACTIVITY_PAUSED = 2; // 0x2 + field public static final int TYPE_ACTIVITY_RESUMED = 1; // 0x1 + } + public abstract class ContentCaptureService extends android.app.Service { ctor public ContentCaptureService(); method public final void disableContentCaptureServices(); + method public void onActivityEvent(@NonNull android.service.contentcapture.ActivityEvent); method public void onActivitySnapshot(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.SnapshotData); method public void onConnected(); method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent); diff --git a/core/java/android/service/contentcapture/ActivityEvent.aidl b/core/java/android/service/contentcapture/ActivityEvent.aidl new file mode 100644 index 000000000000..558cac4d29e8 --- /dev/null +++ b/core/java/android/service/contentcapture/ActivityEvent.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2019, 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.service.contentcapture; + +parcelable ActivityEvent; diff --git a/core/java/android/service/contentcapture/ActivityEvent.java b/core/java/android/service/contentcapture/ActivityEvent.java new file mode 100644 index 000000000000..7ac380d97d9d --- /dev/null +++ b/core/java/android/service/contentcapture/ActivityEvent.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 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.service.contentcapture; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.app.usage.UsageEvents.Event; +import android.content.ComponentName; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Represents an activity-level event that is not associated with a session. + * + * @hide + */ +@SystemApi +@TestApi +public final class ActivityEvent implements Parcelable { + + /** + * The activity resumed. + */ + public static final int TYPE_ACTIVITY_RESUMED = Event.ACTIVITY_RESUMED; + + /** + * The activity paused. + */ + public static final int TYPE_ACTIVITY_PAUSED = Event.ACTIVITY_PAUSED; + + /** @hide */ + @IntDef(prefix = { "TYPE_" }, value = { + TYPE_ACTIVITY_RESUMED, + TYPE_ACTIVITY_PAUSED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ActivityEventType{} + + private final @NonNull ComponentName mComponentName; + private final @ActivityEventType int mType; + + /** @hide */ + public ActivityEvent(@NonNull ComponentName componentName, @ActivityEventType int type) { + mComponentName = componentName; + mType = type; + } + + /** + * Gests the {@link ComponentName} of the activity associated with the event. + */ + @NonNull + public ComponentName getComponentName() { + return mComponentName; + } + + /** + * Gets the event type. + * + * @return either {@link #TYPE_ACTIVITY_RESUMED} or {@value #TYPE_ACTIVITY_PAUSED}. + */ + @ActivityEventType + public int getEventType() { + return mType; + } + + /** @hide */ + public static String getTypeAsString(@ActivityEventType int type) { + switch (type) { + case TYPE_ACTIVITY_RESUMED: + return "ACTIVITY_RESUMED"; + case TYPE_ACTIVITY_PAUSED: + return "ACTIVITY_PAUSED"; + default: + return "UKNOWN_TYPE: " + type; + } + } + + @Override + public String toString() { + return new StringBuilder("ActivityEvent[").append(mComponentName.toShortString()) + .append("]:").append(getTypeAsString(mType)).toString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel parcel, int flags) { + parcel.writeParcelable(mComponentName, flags); + parcel.writeInt(mType); + } + + public static final @android.annotation.NonNull Creator<ActivityEvent> CREATOR = + new Creator<ActivityEvent>() { + + @Override + @NonNull + public ActivityEvent createFromParcel(@NonNull Parcel parcel) { + final ComponentName componentName = parcel.readParcelable(null); + final int eventType = parcel.readInt(); + return new ActivityEvent(componentName, eventType); + } + + @Override + @NonNull + public ActivityEvent[] newArray(int size) { + return new ActivityEvent[size]; + } + }; +} diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index 9c4669f8dcd3..ce3210f89f00 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -127,6 +127,13 @@ public abstract class ContentCaptureService extends Service { obtainMessage(ContentCaptureService::handleOnUserDataRemovalRequest, ContentCaptureService.this, request)); } + + @Override + public void onActivityEvent(ActivityEvent event) { + mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnActivityEvent, + ContentCaptureService.this, event)); + + } }; /** @@ -261,7 +268,21 @@ public abstract class ContentCaptureService extends Service { * @param snapshotData the data */ public void onActivitySnapshot(@NonNull ContentCaptureSessionId sessionId, - @NonNull SnapshotData snapshotData) {} + @NonNull SnapshotData snapshotData) { + if (sVerbose) Log.v(TAG, "onActivitySnapshot(id=" + sessionId + ")"); + } + + /** + * Notifies the service of an activity-level event that is not associated with a session. + * + * <p>This method can be used to track some high-level events for all activities, even those + * that are not whitelisted for Content Capture. + * + * @param event high-level activity event + */ + public void onActivityEvent(@NonNull ActivityEvent event) { + if (sVerbose) Log.v(TAG, "onActivityEvent(): " + event); + } /** * Destroys the content capture session. @@ -397,6 +418,10 @@ public abstract class ContentCaptureService extends Service { onUserDataRemovalRequest(request); } + private void handleOnActivityEvent(@NonNull ActivityEvent event) { + onActivityEvent(event); + } + /** * Checks if the given {@code uid} owns the session associated with the event. */ diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl index eb650324a942..f32432f099c0 100644 --- a/core/java/android/service/contentcapture/IContentCaptureService.aidl +++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl @@ -16,7 +16,9 @@ package android.service.contentcapture; +import android.content.ComponentName; import android.os.IBinder; +import android.service.contentcapture.ActivityEvent; import android.service.contentcapture.SnapshotData; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.UserDataRemovalRequest; @@ -38,4 +40,5 @@ oneway interface IContentCaptureService { void onSessionFinished(String sessionId); void onActivitySnapshot(String sessionId, in SnapshotData snapshotData); void onUserDataRemovalRequest(in UserDataRemovalRequest request); + void onActivityEvent(in ActivityEvent event); } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 9f7a940c1d16..7112beb686b2 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -43,6 +43,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.DeviceConfig; import android.provider.Settings; +import android.service.contentcapture.ActivityEvent.ActivityEventType; import android.util.LocalLog; import android.util.Slog; import android.util.SparseBooleanArray; @@ -632,7 +633,7 @@ public final class ContentCaptureManagerService extends } @Override - public ContentCaptureOptions getOptionsForPackage(int userId, String packageName) { + public ContentCaptureOptions getOptionsForPackage(int userId, @NonNull String packageName) { synchronized (mLock) { final ContentCapturePerUserService service = peekServiceForUserLocked(userId); if (service != null) { @@ -641,5 +642,16 @@ public final class ContentCaptureManagerService extends } return null; } + + @Override + public void notifyActivityEvent(int userId, @NonNull ComponentName activityComponent, + @ActivityEventType int eventType) { + synchronized (mLock) { + final ContentCapturePerUserService service = peekServiceForUserLocked(userId); + if (service != null) { + service.onActivityEventLocked(activityComponent, eventType); + } + } + } } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 9df09b97da40..516f7f59e593 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -47,6 +47,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; +import android.service.contentcapture.ActivityEvent; +import android.service.contentcapture.ActivityEvent.ActivityEventType; import android.service.contentcapture.ContentCaptureService; import android.service.contentcapture.IContentCaptureServiceCallback; import android.service.contentcapture.SnapshotData; @@ -432,6 +434,19 @@ final class ContentCapturePerUserService return options; } + @GuardedBy("mLock") + void onActivityEventLocked(@NonNull ComponentName componentName, @ActivityEventType int type) { + if (mRemoteService == null) { + if (mMaster.debug) Slog.d(mTag, "onActivityEvent(): no remote service"); + return; + } + final ActivityEvent event = new ActivityEvent(componentName, type); + + if (mMaster.verbose) Slog.v(mTag, "onActivityEvent(): " + event); + + mRemoteService.onActivityLifecycleEvent(event); + } + @Override protected void dumpLocked(String prefix, PrintWriter pw) { super.dumpLocked(prefix, pw); diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java index dc07c0ab0c76..f7ac7b8c61b2 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java @@ -23,6 +23,7 @@ import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.os.IBinder; +import android.service.contentcapture.ActivityEvent; import android.service.contentcapture.IContentCaptureService; import android.service.contentcapture.IContentCaptureServiceCallback; import android.service.contentcapture.SnapshotData; @@ -111,6 +112,13 @@ final class RemoteContentCaptureService scheduleAsyncRequest((s) -> s.onUserDataRemovalRequest(request)); } + /** + * Called by {@link ContentCaptureServerSession} to notify a high-level activity event. + */ + public void onActivityLifecycleEvent(@NonNull ActivityEvent event) { + scheduleAsyncRequest((s) -> s.onActivityEvent(event)); + } + public interface ContentCaptureServiceCallbacks extends VultureCallback<RemoteContentCaptureService> { // NOTE: so far we don't need to notify the callback implementation diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 73884a000f44..f98327b3c1c1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -185,6 +185,7 @@ import android.app.WindowConfiguration.ActivityType; import android.app.WindowConfiguration.WindowingMode; import android.app.backup.IBackupManager; import android.app.usage.UsageEvents; +import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetManager; import android.content.AutofillOptions; @@ -1489,6 +1490,11 @@ public class ActivityManagerService extends IActivityManager.Stub private ParcelFileDescriptor[] mLifeMonitorFds; + /** + * Used to notify activity lifecycle events. + */ + @Nullable ContentCaptureManagerInternal mContentCaptureService; + final class UiHandler extends Handler { public UiHandler() { super(com.android.server.UiThread.get().getLooper(), null, true); @@ -1983,6 +1989,16 @@ public class ActivityManagerService extends IActivityManager.Stub mActivityTaskManager.setUsageStatsManager(usageStatsManager); } + /** + * Sets the internal content capture manager service. + * + * <p>It's called when {@code SystemServer} starts, so we don't really need to acquire the lock. + */ + public void setContentCaptureManager( + @Nullable ContentCaptureManagerInternal contentCaptureManager) { + mContentCaptureService = contentCaptureManager; + } + public void startObservingNativeCrashes() { final NativeCrashListener ncl = new NativeCrashListener(this); ncl.start(); @@ -2915,6 +2931,10 @@ public class ActivityManagerService extends IActivityManager.Stub taskRoot); } } + if (mContentCaptureService != null + && (event == Event.ACTIVITY_PAUSED || event == Event.ACTIVITY_RESUMED)) { + mContentCaptureService.notifyActivityEvent(userId, activity, event); + } } /** diff --git a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java index d04f92039f4c..fa7d3fca75b5 100644 --- a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java +++ b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java @@ -18,9 +18,11 @@ package com.android.server.contentcapture; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.os.Bundle; import android.os.IBinder; +import android.service.contentcapture.ActivityEvent.ActivityEventType; /** * ContentCapture Manager local system service interface. @@ -51,4 +53,10 @@ public abstract class ContentCaptureManagerInternal { @Nullable public abstract ContentCaptureOptions getOptionsForPackage(@UserIdInt int userId, @NonNull String packageName); + + /** + * Notifies the intelligence service of a high-level activity event for the given user. + */ + public abstract void notifyActivityEvent(@UserIdInt int userId, + @NonNull ComponentName activityComponent, @ActivityEventType int eventType); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index eba0081291ca..e73383013367 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -87,6 +87,7 @@ import com.android.server.broadcastradio.BroadcastRadioService; import com.android.server.camera.CameraServiceProxy; import com.android.server.clipboard.ClipboardService; import com.android.server.connectivity.IpConnectivityMetrics; +import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.coverage.CoverageService; import com.android.server.devicepolicy.DevicePolicyManagerService; import com.android.server.display.DisplayManagerService; @@ -2250,6 +2251,13 @@ public final class SystemServer { traceBeginAndSlog("StartContentCaptureService"); mSystemServiceManager.startService(CONTENT_CAPTURE_MANAGER_SERVICE_CLASS); + + ContentCaptureManagerInternal ccmi = + LocalServices.getService(ContentCaptureManagerInternal.class); + if (ccmi != null && mActivityManagerService != null) { + mActivityManagerService.setContentCaptureManager(ccmi); + } + traceEnd(); } |