Metrics for content capture.
Bug: 119613670 Test: statsd_testdrive & manual test Change-Id: If43465ccee7454a7ebf9e15caa23fce7bae33cfe
This commit is contained in:
@@ -299,6 +299,10 @@ message Atom {
|
||||
CarPowerStateChanged car_power_state_changed = 203;
|
||||
GarageModeInfo garage_mode_info = 204;
|
||||
TestAtomReported test_atom_reported = 205 [(log_from_module) = "cts"];
|
||||
ContentCaptureCallerMismatchReported content_capture_caller_mismatch_reported = 206;
|
||||
ContentCaptureServiceEvents content_capture_service_events = 207;
|
||||
ContentCaptureSessionEvents content_capture_session_events = 208;
|
||||
ContentCaptureFlushed content_capture_flushed = 209;
|
||||
}
|
||||
|
||||
// Pulled events will start at field 10000.
|
||||
@@ -4829,6 +4833,95 @@ message BuildInformation {
|
||||
optional string tags = 9;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs information about mismatched caller for content capture.
|
||||
*
|
||||
* Logged from:
|
||||
* frameworks/base/core/java/android/service/contentcapture/ContentCaptureService.java
|
||||
*/
|
||||
message ContentCaptureCallerMismatchReported {
|
||||
optional string intended_package = 1;
|
||||
optional string calling_package = 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs information about content capture service events.
|
||||
*
|
||||
* Logged from:
|
||||
* frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
|
||||
*/
|
||||
message ContentCaptureServiceEvents {
|
||||
// The type of event.
|
||||
enum Event {
|
||||
UNKNOWN = 0;
|
||||
ON_CONNECTED = 1;
|
||||
ON_DISCONNECTED = 2;
|
||||
SET_WHITELIST = 3;
|
||||
SET_DISABLED = 4;
|
||||
ON_USER_DATA_REMOVED = 5;
|
||||
}
|
||||
optional Event event = 1;
|
||||
// component/package of content capture service.
|
||||
optional string service_info = 2;
|
||||
// component/package of target.
|
||||
// it's a concatenated list of component/package for SET_WHITELIST event
|
||||
// separated by " ".
|
||||
optional string target_info = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs information about content capture session events.
|
||||
*
|
||||
* Logged from:
|
||||
* frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
|
||||
*/
|
||||
message ContentCaptureSessionEvents {
|
||||
// The type of event.
|
||||
enum Event {
|
||||
UNKNOWN = 0;
|
||||
ON_SESSION_STARTED = 1;
|
||||
ON_SESSION_FINISHED = 2;
|
||||
SESSION_NOT_CREATED = 3;
|
||||
}
|
||||
optional int32 session_id = 1;
|
||||
optional Event event = 2;
|
||||
// (n/a on session finished)
|
||||
optional int32 state_flags = 3;
|
||||
// component/package of content capture service.
|
||||
optional string service_info = 4;
|
||||
// component/package of app.
|
||||
// (n/a on session finished)
|
||||
optional string app_info = 5;
|
||||
optional bool is_child_session = 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs information about session being flushed.
|
||||
*
|
||||
* Logged from:
|
||||
* frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
|
||||
*/
|
||||
message ContentCaptureFlushed {
|
||||
optional int32 session_id = 1;
|
||||
// component/package of content capture service.
|
||||
optional string service_info = 2;
|
||||
// component/package of app.
|
||||
optional string app_info = 3;
|
||||
// session start/finish events
|
||||
optional int32 child_session_started = 4;
|
||||
optional int32 child_session_finished = 5;
|
||||
// count of view events.
|
||||
optional int32 view_appeared_count = 6;
|
||||
optional int32 view_disappeared_count = 7;
|
||||
optional int32 view_text_changed_count = 8;
|
||||
|
||||
// Flush stats.
|
||||
optional int32 max_events = 9;
|
||||
optional int32 idle_flush_freq = 10;
|
||||
optional int32 text_flush_freq = 11;
|
||||
optional int32 flush_reason = 12;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls on-device BatteryStats power use calculations for the overall device.
|
||||
*/
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.annotation.SystemApi;
|
||||
import android.annotation.TestApi;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentCaptureOptions;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.os.Binder;
|
||||
@@ -40,6 +41,7 @@ import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseIntArray;
|
||||
import android.util.StatsLog;
|
||||
import android.view.contentcapture.ContentCaptureCondition;
|
||||
import android.view.contentcapture.ContentCaptureContext;
|
||||
import android.view.contentcapture.ContentCaptureEvent;
|
||||
@@ -114,6 +116,9 @@ public abstract class ContentCaptureService extends Service {
|
||||
private Handler mHandler;
|
||||
private IContentCaptureServiceCallback mCallback;
|
||||
|
||||
private long mCallerMismatchTimeout = 1000;
|
||||
private long mLastCallerMismatchLog;
|
||||
|
||||
/**
|
||||
* Binder that receives calls from the system server.
|
||||
*/
|
||||
@@ -174,9 +179,10 @@ public abstract class ContentCaptureService extends Service {
|
||||
new IContentCaptureDirectManager.Stub() {
|
||||
|
||||
@Override
|
||||
public void sendEvents(@SuppressWarnings("rawtypes") ParceledListSlice events) {
|
||||
public void sendEvents(@SuppressWarnings("rawtypes") ParceledListSlice events, int reason,
|
||||
ContentCaptureOptions options) {
|
||||
mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents,
|
||||
ContentCaptureService.this, Binder.getCallingUid(), events));
|
||||
ContentCaptureService.this, Binder.getCallingUid(), events, reason, options));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -422,14 +428,23 @@ public abstract class ContentCaptureService extends Service {
|
||||
}
|
||||
|
||||
private void handleSendEvents(int uid,
|
||||
@NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents) {
|
||||
@NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents, int reason,
|
||||
@Nullable ContentCaptureOptions options) {
|
||||
final List<ContentCaptureEvent> events = parceledEvents.getList();
|
||||
if (events.isEmpty()) {
|
||||
Log.w(TAG, "handleSendEvents() received empty list of events");
|
||||
return;
|
||||
}
|
||||
|
||||
// Metrics.
|
||||
final FlushMetrics metrics = new FlushMetrics();
|
||||
ComponentName activityComponent = null;
|
||||
|
||||
// Most events belong to the same session, so we can keep a reference to the last one
|
||||
// to avoid creating too many ContentCaptureSessionId objects
|
||||
int lastSessionId = NO_SESSION_ID;
|
||||
ContentCaptureSessionId sessionId = null;
|
||||
|
||||
final List<ContentCaptureEvent> events = parceledEvents.getList();
|
||||
for (int i = 0; i < events.size(); i++) {
|
||||
final ContentCaptureEvent event = events.get(i);
|
||||
if (!handleIsRightCallerFor(event, uid)) continue;
|
||||
@@ -437,22 +452,44 @@ public abstract class ContentCaptureService extends Service {
|
||||
if (sessionIdInt != lastSessionId) {
|
||||
sessionId = new ContentCaptureSessionId(sessionIdInt);
|
||||
lastSessionId = sessionIdInt;
|
||||
if (i != 0) {
|
||||
writeFlushMetrics(lastSessionId, activityComponent, metrics, options, reason);
|
||||
metrics.reset();
|
||||
}
|
||||
}
|
||||
final ContentCaptureContext clientContext = event.getContentCaptureContext();
|
||||
if (activityComponent == null && clientContext != null) {
|
||||
activityComponent = clientContext.getActivityComponent();
|
||||
}
|
||||
switch (event.getType()) {
|
||||
case ContentCaptureEvent.TYPE_SESSION_STARTED:
|
||||
final ContentCaptureContext clientContext = event.getContentCaptureContext();
|
||||
clientContext.setParentSessionId(event.getParentSessionId());
|
||||
mSessionUids.put(sessionIdInt, uid);
|
||||
onCreateContentCaptureSession(clientContext, sessionId);
|
||||
metrics.sessionStarted++;
|
||||
break;
|
||||
case ContentCaptureEvent.TYPE_SESSION_FINISHED:
|
||||
mSessionUids.delete(sessionIdInt);
|
||||
onDestroyContentCaptureSession(sessionId);
|
||||
metrics.sessionFinished++;
|
||||
break;
|
||||
case ContentCaptureEvent.TYPE_VIEW_APPEARED:
|
||||
onContentCaptureEvent(sessionId, event);
|
||||
metrics.viewAppearedCount++;
|
||||
break;
|
||||
case ContentCaptureEvent.TYPE_VIEW_DISAPPEARED:
|
||||
onContentCaptureEvent(sessionId, event);
|
||||
metrics.viewDisappearedCount++;
|
||||
break;
|
||||
case ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED:
|
||||
onContentCaptureEvent(sessionId, event);
|
||||
metrics.viewTextChangedCount++;
|
||||
break;
|
||||
default:
|
||||
onContentCaptureEvent(sessionId, event);
|
||||
}
|
||||
}
|
||||
writeFlushMetrics(lastSessionId, activityComponent, metrics, options, reason);
|
||||
}
|
||||
|
||||
private void handleOnActivitySnapshot(int sessionId, @NonNull SnapshotData snapshotData) {
|
||||
@@ -497,7 +534,13 @@ public abstract class ContentCaptureService extends Service {
|
||||
if (rightUid != uid) {
|
||||
Log.e(TAG, "invalid call from UID " + uid + ": session " + sessionId + " belongs to "
|
||||
+ rightUid);
|
||||
//TODO(b/111276913): log metrics as this could be a malicious app forging a sessionId
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - mLastCallerMismatchLog > mCallerMismatchTimeout) {
|
||||
StatsLog.write(StatsLog.CONTENT_CAPTURE_CALLER_MISMATCH_REPORTED,
|
||||
getPackageManager().getNameForUid(rightUid),
|
||||
getPackageManager().getNameForUid(uid));
|
||||
mLastCallerMismatchLog = now;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -528,4 +571,22 @@ public abstract class ContentCaptureService extends Service {
|
||||
Slog.w(TAG, "Error async reporting result to client: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the metrics for content capture events flushing.
|
||||
*/
|
||||
private void writeFlushMetrics(int sessionId, @Nullable ComponentName app,
|
||||
@NonNull FlushMetrics flushMetrics, @Nullable ContentCaptureOptions options,
|
||||
int flushReason) {
|
||||
if (mCallback == null) {
|
||||
Log.w(TAG, "writeSessionFlush(): no server callback");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mCallback.writeSessionFlush(sessionId, app, flushMetrics, options, flushReason);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "failed to write flush metrics: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
core/java/android/service/contentcapture/FlushMetrics.aidl
Normal file
20
core/java/android/service/contentcapture/FlushMetrics.aidl
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/* @hide */
|
||||
parcelable FlushMetrics;
|
||||
79
core/java/android/service/contentcapture/FlushMetrics.java
Normal file
79
core/java/android/service/contentcapture/FlushMetrics.java
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.NonNull;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Holds metrics for content capture events flushing.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class FlushMetrics implements Parcelable {
|
||||
public int viewAppearedCount;
|
||||
public int viewDisappearedCount;
|
||||
public int viewTextChangedCount;
|
||||
public int sessionStarted;
|
||||
public int sessionFinished;
|
||||
|
||||
/**
|
||||
* Resets all flush metrics.
|
||||
*/
|
||||
public void reset() {
|
||||
viewAppearedCount = 0;
|
||||
viewDisappearedCount = 0;
|
||||
viewTextChangedCount = 0;
|
||||
sessionStarted = 0;
|
||||
sessionFinished = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeInt(sessionStarted);
|
||||
out.writeInt(sessionFinished);
|
||||
out.writeInt(viewAppearedCount);
|
||||
out.writeInt(viewDisappearedCount);
|
||||
out.writeInt(viewTextChangedCount);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<FlushMetrics> CREATOR = new Creator<FlushMetrics>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public FlushMetrics createFromParcel(Parcel in) {
|
||||
final FlushMetrics flushMetrics = new FlushMetrics();
|
||||
flushMetrics.sessionStarted = in.readInt();
|
||||
flushMetrics.sessionFinished = in.readInt();
|
||||
flushMetrics.viewAppearedCount = in.readInt();
|
||||
flushMetrics.viewDisappearedCount = in.readInt();
|
||||
flushMetrics.viewTextChangedCount = in.readInt();
|
||||
return flushMetrics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlushMetrics[] newArray(int size) {
|
||||
return new FlushMetrics[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -18,6 +18,8 @@ package android.service.contentcapture;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.view.contentcapture.ContentCaptureCondition;
|
||||
import android.service.contentcapture.FlushMetrics;
|
||||
import android.content.ContentCaptureOptions;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -30,4 +32,8 @@ oneway interface IContentCaptureServiceCallback {
|
||||
void setContentCaptureWhitelist(in List<String> packages, in List<ComponentName> activities);
|
||||
void setContentCaptureConditions(String packageName, in List<ContentCaptureCondition> conditions);
|
||||
void disableSelf();
|
||||
}
|
||||
|
||||
// Logs aggregated content capture flush metrics to Statsd
|
||||
void writeSessionFlush(int sessionId, in ComponentName app, in FlushMetrics flushMetrics,
|
||||
in ContentCaptureOptions options, int flushReason);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.view.contentcapture;
|
||||
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.view.contentcapture.ContentCaptureEvent;
|
||||
import android.content.ContentCaptureOptions;
|
||||
|
||||
/**
|
||||
* Interface between an app (ContentCaptureManager / ContentCaptureSession) and the app providing
|
||||
@@ -26,5 +27,6 @@ import android.view.contentcapture.ContentCaptureEvent;
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IContentCaptureDirectManager {
|
||||
void sendEvents(in ParceledListSlice events);
|
||||
// reason and options are used only for metrics logging.
|
||||
void sendEvents(in ParceledListSlice events, int reason, in ContentCaptureOptions options);
|
||||
}
|
||||
|
||||
@@ -498,7 +498,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
}
|
||||
|
||||
final ParceledListSlice<ContentCaptureEvent> events = clearEvents();
|
||||
mDirectServiceInterface.sendEvents(events);
|
||||
mDirectServiceInterface.sendEvents(events, reason, mManager.mOptions);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Error sending " + numberEvents + " for " + getDebugState()
|
||||
+ ": " + e);
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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 com.android.server.contentcapture;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentCaptureOptions;
|
||||
import android.service.contentcapture.FlushMetrics;
|
||||
import android.util.StatsLog;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** @hide */
|
||||
public final class ContentCaptureMetricsLogger {
|
||||
/**
|
||||
* Class only contains static utility functions, and should not be instantiated
|
||||
*/
|
||||
private ContentCaptureMetricsLogger() {
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static void writeServiceEvent(int eventType, @NonNull String serviceName,
|
||||
@Nullable String targetPackage) {
|
||||
StatsLog.write(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS, eventType, serviceName,
|
||||
targetPackage);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static void writeServiceEvent(int eventType, @NonNull ComponentName service,
|
||||
@Nullable ComponentName target) {
|
||||
writeServiceEvent(eventType, ComponentName.flattenToShortString(service),
|
||||
ComponentName.flattenToShortString(target));
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static void writeServiceEvent(int eventType, @NonNull ComponentName service,
|
||||
@Nullable String targetPackage) {
|
||||
writeServiceEvent(eventType, ComponentName.flattenToShortString(service), targetPackage);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static void writeServiceEvent(int eventType, @NonNull ComponentName service) {
|
||||
writeServiceEvent(eventType, ComponentName.flattenToShortString(service), null);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static void writeSetWhitelistEvent(@Nullable ComponentName service,
|
||||
@Nullable List<String> packages, @Nullable List<ComponentName> activities) {
|
||||
final String serviceName = ComponentName.flattenToShortString(service);
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
if (packages != null && packages.size() > 0) {
|
||||
final int size = packages.size();
|
||||
stringBuilder.append(packages.get(0));
|
||||
for (int i = 1; i < size; i++) {
|
||||
stringBuilder.append(" ");
|
||||
stringBuilder.append(packages.get(i));
|
||||
}
|
||||
}
|
||||
if (activities != null && activities.size() > 0) {
|
||||
stringBuilder.append(" ");
|
||||
stringBuilder.append(activities.get(0).flattenToShortString());
|
||||
final int size = activities.size();
|
||||
for (int i = 1; i < size; i++) {
|
||||
stringBuilder.append(" ");
|
||||
stringBuilder.append(activities.get(i).flattenToShortString());
|
||||
}
|
||||
}
|
||||
StatsLog.write(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS,
|
||||
StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__SET_WHITELIST,
|
||||
serviceName, stringBuilder.toString());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static void writeSessionEvent(int sessionId, int event, int flags,
|
||||
@NonNull ComponentName service, @Nullable ComponentName app, boolean isChildSession) {
|
||||
StatsLog.write(StatsLog.CONTENT_CAPTURE_SESSION_EVENTS, sessionId, event, flags,
|
||||
ComponentName.flattenToShortString(service),
|
||||
ComponentName.flattenToShortString(app), isChildSession);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static void writeSessionFlush(int sessionId, @NonNull ComponentName service,
|
||||
@Nullable ComponentName app, @NonNull FlushMetrics fm,
|
||||
@NonNull ContentCaptureOptions options, int flushReason) {
|
||||
StatsLog.write(StatsLog.CONTENT_CAPTURE_FLUSHED, sessionId,
|
||||
ComponentName.flattenToShortString(service),
|
||||
ComponentName.flattenToShortString(app), fm.sessionStarted, fm.sessionFinished,
|
||||
fm.viewAppearedCount, fm.viewDisappearedCount, fm.viewTextChangedCount,
|
||||
options.maxBufferSize, options.idleFlushingFrequencyMs,
|
||||
options.textChangeFlushingFrequencyMs, flushReason);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,9 @@ import static android.view.contentcapture.ContentCaptureSession.STATE_INTERNAL_E
|
||||
import static android.view.contentcapture.ContentCaptureSession.STATE_NOT_WHITELISTED;
|
||||
import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE;
|
||||
|
||||
import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeServiceEvent;
|
||||
import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSessionEvent;
|
||||
import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSetWhitelistEvent;
|
||||
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
|
||||
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
|
||||
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
|
||||
@@ -35,6 +38,7 @@ import android.app.ActivityManagerInternal;
|
||||
import android.app.assist.AssistContent;
|
||||
import android.app.assist.AssistStructure;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentCaptureOptions;
|
||||
import android.content.pm.ActivityPresentationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
@@ -48,6 +52,7 @@ import android.service.contentcapture.ActivityEvent;
|
||||
import android.service.contentcapture.ActivityEvent.ActivityEventType;
|
||||
import android.service.contentcapture.ContentCaptureService;
|
||||
import android.service.contentcapture.ContentCaptureServiceInfo;
|
||||
import android.service.contentcapture.FlushMetrics;
|
||||
import android.service.contentcapture.IContentCaptureServiceCallback;
|
||||
import android.service.contentcapture.SnapshotData;
|
||||
import android.util.ArrayMap;
|
||||
@@ -55,6 +60,7 @@ import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.util.StatsLog;
|
||||
import android.view.contentcapture.ContentCaptureCondition;
|
||||
import android.view.contentcapture.DataRemovalRequest;
|
||||
|
||||
@@ -231,7 +237,6 @@ final class ContentCapturePerUserService
|
||||
resurrectSessionsLocked();
|
||||
}
|
||||
|
||||
// TODO(b/119613670): log metrics
|
||||
@GuardedBy("mLock")
|
||||
public void startSessionLocked(@NonNull IBinder activityToken,
|
||||
@NonNull ActivityPresentationInfo activityPresentationInfo, int sessionId, int uid,
|
||||
@@ -263,9 +268,14 @@ final class ContentCapturePerUserService
|
||||
|
||||
if (!enabled) {
|
||||
// TODO: it would be better to split in differet reasons, like
|
||||
// STATE_DISABLED_NO_SERVICE and STATE_DISABLED_BY_DEVICE_POLICY
|
||||
// STATE_DISABLED_NO and STATE_DISABLED_BY_DEVICE_POLICY
|
||||
setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
|
||||
/* binder= */ null);
|
||||
// Log metrics.
|
||||
writeSessionEvent(sessionId,
|
||||
StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
|
||||
STATE_DISABLED | STATE_NO_SERVICE, serviceComponentName,
|
||||
componentName, /* isChildSession= */ false);
|
||||
return;
|
||||
}
|
||||
if (serviceComponentName == null) {
|
||||
@@ -285,6 +295,11 @@ final class ContentCapturePerUserService
|
||||
}
|
||||
setClientState(clientReceiver, STATE_DISABLED | STATE_NOT_WHITELISTED,
|
||||
/* binder= */ null);
|
||||
// Log metrics.
|
||||
writeSessionEvent(sessionId,
|
||||
StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
|
||||
STATE_DISABLED | STATE_NOT_WHITELISTED, serviceComponentName,
|
||||
componentName, /* isChildSession= */ false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -294,6 +309,11 @@ final class ContentCapturePerUserService
|
||||
+ ": ignoring because it already exists for " + existingSession.mActivityToken);
|
||||
setClientState(clientReceiver, STATE_DISABLED | STATE_DUPLICATED_ID,
|
||||
/* binder=*/ null);
|
||||
// Log metrics.
|
||||
writeSessionEvent(sessionId,
|
||||
StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
|
||||
STATE_DISABLED | STATE_DUPLICATED_ID,
|
||||
serviceComponentName, componentName, /* isChildSession= */ false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -302,11 +322,15 @@ final class ContentCapturePerUserService
|
||||
}
|
||||
|
||||
if (mRemoteService == null) {
|
||||
// TODO(b/119613670): log metrics
|
||||
Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
|
||||
+ ": ignoring because service is not set");
|
||||
setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
|
||||
/* binder= */ null);
|
||||
// Log metrics.
|
||||
writeSessionEvent(sessionId,
|
||||
StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
|
||||
STATE_DISABLED | STATE_NO_SERVICE, serviceComponentName,
|
||||
componentName, /* isChildSession= */ false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -324,7 +348,6 @@ final class ContentCapturePerUserService
|
||||
newSession.notifySessionStartedLocked(clientReceiver);
|
||||
}
|
||||
|
||||
// TODO(b/119613670): log metrics
|
||||
@GuardedBy("mLock")
|
||||
public void finishSessionLocked(int sessionId) {
|
||||
if (!isEnabledLocked()) {
|
||||
@@ -553,6 +576,7 @@ final class ContentCapturePerUserService
|
||||
+ " for user " + mUserId);
|
||||
}
|
||||
mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities);
|
||||
writeSetWhitelistEvent(getServiceComponentName(), packages, activities);
|
||||
|
||||
// Must disable session that are not the whitelist anymore...
|
||||
final int numSessions = mSessions.size();
|
||||
@@ -602,7 +626,6 @@ final class ContentCapturePerUserService
|
||||
mConditionsByPkg.put(packageName, new ArraySet<>(conditions));
|
||||
}
|
||||
}
|
||||
// TODO(b/119613670): log metrics
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -616,6 +639,15 @@ final class ContentCapturePerUserService
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__SET_DISABLED,
|
||||
getServiceComponentName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSessionFlush(int sessionId, ComponentName app, FlushMetrics flushMetrics,
|
||||
ContentCaptureOptions options, int flushReason) {
|
||||
ContentCaptureMetricsLogger.writeSessionFlush(sessionId, getServiceComponentName(), app,
|
||||
flushMetrics, options, flushReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ package com.android.server.contentcapture;
|
||||
import static android.view.contentcapture.ContentCaptureHelper.sDebug;
|
||||
import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
|
||||
|
||||
import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeServiceEvent;
|
||||
import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSessionEvent;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ComponentName;
|
||||
@@ -28,6 +31,7 @@ import android.service.contentcapture.IContentCaptureService;
|
||||
import android.service.contentcapture.IContentCaptureServiceCallback;
|
||||
import android.service.contentcapture.SnapshotData;
|
||||
import android.util.Slog;
|
||||
import android.util.StatsLog;
|
||||
import android.view.contentcapture.ContentCaptureContext;
|
||||
import android.view.contentcapture.DataRemovalRequest;
|
||||
|
||||
@@ -77,6 +81,8 @@ final class RemoteContentCaptureService
|
||||
if (connected) {
|
||||
try {
|
||||
mService.onConnected(mServerCallback, sVerbose, sDebug);
|
||||
writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_CONNECTED,
|
||||
mComponentName);
|
||||
} finally {
|
||||
// Update the system-service state, in case the service reconnected after
|
||||
// dying
|
||||
@@ -84,6 +90,8 @@ final class RemoteContentCaptureService
|
||||
}
|
||||
} else {
|
||||
mService.onDisconnected();
|
||||
writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_DISCONNECTED,
|
||||
mComponentName);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Slog.w(mTag, "Exception calling onConnectedStateChanged(" + connected + "): " + e);
|
||||
@@ -102,6 +110,10 @@ final class RemoteContentCaptureService
|
||||
@NonNull IResultReceiver clientReceiver, int initialState) {
|
||||
scheduleAsyncRequest(
|
||||
(s) -> s.onSessionStarted(context, sessionId, uid, clientReceiver, initialState));
|
||||
// Metrics logging.
|
||||
writeSessionEvent(sessionId,
|
||||
StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__ON_SESSION_STARTED, initialState,
|
||||
getComponentName(), context.getActivityComponent(), /* is_child_session= */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,6 +122,11 @@ final class RemoteContentCaptureService
|
||||
*/
|
||||
public void onSessionFinished(int sessionId) {
|
||||
scheduleAsyncRequest((s) -> s.onSessionFinished(sessionId));
|
||||
// Metrics logging.
|
||||
writeSessionEvent(sessionId,
|
||||
StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__ON_SESSION_FINISHED,
|
||||
/* flags= */ 0, getComponentName(), /* app= */ null,
|
||||
/* is_child_session= */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,6 +141,8 @@ final class RemoteContentCaptureService
|
||||
*/
|
||||
public void onDataRemovalRequest(@NonNull DataRemovalRequest request) {
|
||||
scheduleAsyncRequest((s) -> s.onDataRemovalRequest(request));
|
||||
writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_USER_DATA_REMOVED,
|
||||
mComponentName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user