Loading cmds/statsd/src/atoms.proto +93 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -4830,6 +4834,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. */ Loading core/java/android/service/contentcapture/ContentCaptureService.java +67 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -176,9 +181,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)); } }; Loading Loading @@ -424,14 +430,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; Loading @@ -439,22 +454,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) { Loading Loading @@ -499,7 +536,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; Loading Loading @@ -530,4 +573,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); } } } core/java/android/service/contentcapture/FlushMetrics.aidl 0 → 100644 +20 −0 Original line number Diff line number Diff line /** * 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; core/java/android/service/contentcapture/FlushMetrics.java 0 → 100644 +79 −0 Original line number Diff line number Diff line /* * 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]; } }; } core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl +7 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); } Loading
cmds/statsd/src/atoms.proto +93 −0 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -4830,6 +4834,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. */ Loading
core/java/android/service/contentcapture/ContentCaptureService.java +67 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -176,9 +181,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)); } }; Loading Loading @@ -424,14 +430,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; Loading @@ -439,22 +454,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) { Loading Loading @@ -499,7 +536,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; Loading Loading @@ -530,4 +573,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); } } }
core/java/android/service/contentcapture/FlushMetrics.aidl 0 → 100644 +20 −0 Original line number Diff line number Diff line /** * 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;
core/java/android/service/contentcapture/FlushMetrics.java 0 → 100644 +79 −0 Original line number Diff line number Diff line /* * 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]; } }; }
core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl +7 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); }