Added DeviceConfig properties to fine tune ContentCapture:
- DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE - DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY - DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY - DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE - DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL Test: manual verification Test: atest CtsContentCaptureServiceTestCases Bug: 123096662 Fixes: 121044064 Fixes: 121044306 Change-Id: I08623516bb7ebbe9c900987be853f4f395250016
This commit is contained in:
@@ -2748,7 +2748,15 @@ package android.view.contentcapture {
|
||||
public final class ContentCaptureManager {
|
||||
method public boolean isContentCaptureFeatureEnabled();
|
||||
method public void setContentCaptureFeatureEnabled(boolean);
|
||||
field public static final String DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY = "idle_flush_frequency";
|
||||
field public static final String DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL = "logging_level";
|
||||
field public static final String DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE = "log_history_size";
|
||||
field public static final String DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE = "max_buffer_size";
|
||||
field public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED = "service_explicitly_enabled";
|
||||
field public static final String DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY = "text_change_flush_frequency";
|
||||
field public static final int LOGGING_LEVEL_DEBUG = 1; // 0x1
|
||||
field public static final int LOGGING_LEVEL_OFF = 0; // 0x0
|
||||
field public static final int LOGGING_LEVEL_VERBOSE = 2; // 0x2
|
||||
}
|
||||
|
||||
public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
|
||||
|
||||
@@ -15,16 +15,29 @@
|
||||
*/
|
||||
package android.view.contentcapture;
|
||||
|
||||
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL;
|
||||
import static android.view.contentcapture.ContentCaptureManager.LOGGING_LEVEL_DEBUG;
|
||||
import static android.view.contentcapture.ContentCaptureManager.LOGGING_LEVEL_OFF;
|
||||
import static android.view.contentcapture.ContentCaptureManager.LOGGING_LEVEL_VERBOSE;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Build;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.util.Log;
|
||||
import android.view.contentcapture.ContentCaptureManager.LoggingLevel;
|
||||
|
||||
/**
|
||||
* Helpe class for this package.
|
||||
* Helper class for this package and server's.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
final class ContentCaptureHelper {
|
||||
public final class ContentCaptureHelper {
|
||||
|
||||
// TODO(b/121044306): define a way to dynamically set them(for example, using settings?)
|
||||
static final boolean VERBOSE = false;
|
||||
static final boolean DEBUG = true; // STOPSHIP if not set to false
|
||||
private static final String TAG = ContentCaptureHelper.class.getSimpleName();
|
||||
|
||||
public static boolean sVerbose = false;
|
||||
public static boolean sDebug = true;
|
||||
|
||||
/**
|
||||
* Used to log text that could contain PII.
|
||||
@@ -34,6 +47,61 @@ final class ContentCaptureHelper {
|
||||
return text == null ? null : text.length() + "_chars";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a device config property from the Content Capture namespace.
|
||||
*/
|
||||
public static int getIntDeviceConfigProperty(@NonNull String key, int defaultValue) {
|
||||
final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, key);
|
||||
if (value == null) return defaultValue;
|
||||
|
||||
try {
|
||||
return Integer.parseInt(value);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "error parsing value (" + value + ") of property " + key + ": " + e);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the static logging level constants based on device config.
|
||||
*/
|
||||
public static void setLoggingLevel() {
|
||||
final int defaultLevel = Build.IS_DEBUGGABLE ? LOGGING_LEVEL_DEBUG : LOGGING_LEVEL_OFF;
|
||||
final int level = getIntDeviceConfigProperty(DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL,
|
||||
defaultLevel);
|
||||
Log.i(TAG, "Setting logging level to " + getLoggingLevelAsString(level));
|
||||
sVerbose = sDebug = false;
|
||||
switch (level) {
|
||||
case LOGGING_LEVEL_VERBOSE:
|
||||
sVerbose = true;
|
||||
// fall through
|
||||
case LOGGING_LEVEL_DEBUG:
|
||||
sDebug = true;
|
||||
return;
|
||||
case LOGGING_LEVEL_OFF:
|
||||
// You log nothing, Jon Snow!
|
||||
return;
|
||||
default:
|
||||
Log.w(TAG, "setLoggingLevel(): invalud level: " + level);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a user-friendly value for a content capture logging level.
|
||||
*/
|
||||
public static String getLoggingLevelAsString(@LoggingLevel int level) {
|
||||
switch (level) {
|
||||
case LOGGING_LEVEL_OFF:
|
||||
return "OFF";
|
||||
case LOGGING_LEVEL_DEBUG:
|
||||
return "DEBUG";
|
||||
case LOGGING_LEVEL_VERBOSE:
|
||||
return "VERBOSE";
|
||||
default:
|
||||
return "UNKNOWN-" + level;
|
||||
}
|
||||
}
|
||||
|
||||
private ContentCaptureHelper() {
|
||||
throw new UnsupportedOperationException("contains only static methods");
|
||||
}
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
*/
|
||||
package android.view.contentcapture;
|
||||
|
||||
import static android.view.contentcapture.ContentCaptureHelper.DEBUG;
|
||||
import static android.view.contentcapture.ContentCaptureHelper.VERBOSE;
|
||||
import static android.view.contentcapture.ContentCaptureHelper.sDebug;
|
||||
import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
@@ -38,6 +39,8 @@ import com.android.internal.util.Preconditions;
|
||||
import com.android.internal.util.SyncResultReceiver;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* TODO(b/123577059): add javadocs / mention it can be null
|
||||
@@ -78,6 +81,75 @@ public final class ContentCaptureManager {
|
||||
public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED =
|
||||
"service_explicitly_enabled";
|
||||
|
||||
/**
|
||||
* Maximum number of events that are buffered before sent to the app.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public static final String DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE = "max_buffer_size";
|
||||
|
||||
/**
|
||||
* Frequency (in ms) of buffer flushes when no events are received.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public static final String DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY = "idle_flush_frequency";
|
||||
|
||||
/**
|
||||
* Frequency (in ms) of buffer flushes when no events are received and the last one was a
|
||||
* text change event.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public static final String DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY =
|
||||
"text_change_flush_frequency";
|
||||
|
||||
/**
|
||||
* Size of events that are logging on {@code dump}.
|
||||
*
|
||||
* <p>Set it to {@code 0} or less to disable history.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public static final String DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE = "log_history_size";
|
||||
|
||||
/**
|
||||
* Sets the logging level for {@code logcat} statements.
|
||||
*
|
||||
* <p>Valid values are: {@link #LOGGING_LEVEL_OFF}, {@value #LOGGING_LEVEL_DEBUG}, and
|
||||
* {@link #LOGGING_LEVEL_VERBOSE}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public static final String DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL = "logging_level";
|
||||
|
||||
|
||||
/** @hide */
|
||||
@TestApi
|
||||
public static final int LOGGING_LEVEL_OFF = 0;
|
||||
|
||||
/** @hide */
|
||||
@TestApi
|
||||
public static final int LOGGING_LEVEL_DEBUG = 1;
|
||||
|
||||
/** @hide */
|
||||
@TestApi
|
||||
public static final int LOGGING_LEVEL_VERBOSE = 2;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(flag = false, value = {
|
||||
LOGGING_LEVEL_OFF,
|
||||
LOGGING_LEVEL_DEBUG,
|
||||
LOGGING_LEVEL_VERBOSE
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface LoggingLevel {}
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
@NonNull
|
||||
@@ -101,10 +173,16 @@ public final class ContentCaptureManager {
|
||||
/** @hide */
|
||||
public ContentCaptureManager(@NonNull Context context,
|
||||
@NonNull IContentCaptureManager service) {
|
||||
if (VERBOSE) Log.v(TAG, "Constructor for " + context.getPackageName());
|
||||
mContext = Preconditions.checkNotNull(context, "context cannot be null");
|
||||
mService = Preconditions.checkNotNull(service, "service cannot be null");
|
||||
|
||||
// TODO(b/123096662): right now we're reading the device config values here, but ideally
|
||||
// it should be read on ContentCaptureManagerService and passed back when the activity
|
||||
// started.
|
||||
ContentCaptureHelper.setLoggingLevel();
|
||||
|
||||
if (sVerbose) Log.v(TAG, "Constructor for " + context.getPackageName());
|
||||
|
||||
// TODO(b/119220549): we might not even need a handler, as the IPCs are oneway. But if we
|
||||
// do, then we should optimize it to run the tests after the Choreographer finishes the most
|
||||
// important steps of the frame.
|
||||
@@ -126,7 +204,7 @@ public final class ContentCaptureManager {
|
||||
synchronized (mLock) {
|
||||
if (mMainSession == null) {
|
||||
mMainSession = new MainContentCaptureSession(mContext, this, mHandler, mService);
|
||||
if (VERBOSE) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
|
||||
if (sVerbose) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
|
||||
}
|
||||
return mMainSession;
|
||||
}
|
||||
@@ -210,7 +288,7 @@ public final class ContentCaptureManager {
|
||||
* it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
|
||||
*/
|
||||
public void setContentCaptureEnabled(boolean enabled) {
|
||||
if (DEBUG) {
|
||||
if (sDebug) {
|
||||
Log.d(TAG, "setContentCaptureEnabled(): setting to " + enabled + " for " + mContext);
|
||||
}
|
||||
|
||||
@@ -264,7 +342,7 @@ public final class ContentCaptureManager {
|
||||
@SystemApi
|
||||
@TestApi
|
||||
public void setContentCaptureFeatureEnabled(boolean enabled) {
|
||||
if (DEBUG) Log.d(TAG, "setContentCaptureFeatureEnabled(): setting to " + enabled);
|
||||
if (sDebug) Log.d(TAG, "setContentCaptureFeatureEnabled(): setting to " + enabled);
|
||||
|
||||
final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
|
||||
final int resultCode;
|
||||
@@ -308,10 +386,12 @@ public final class ContentCaptureManager {
|
||||
synchronized (mLock) {
|
||||
pw.print(prefix2); pw.print("isContentCaptureEnabled(): ");
|
||||
pw.println(isContentCaptureEnabled());
|
||||
pw.print(prefix2); pw.print("Context: "); pw.println(mContext);
|
||||
pw.print(prefix2); pw.print("User: "); pw.println(mContext.getUserId());
|
||||
pw.print(prefix2); pw.print("Service: "); pw.println(mService);
|
||||
pw.print(prefix2); pw.print("Flags: "); pw.println(mFlags);
|
||||
pw.print(prefix); pw.print("Debug: "); pw.print(sDebug);
|
||||
pw.print(" Verbose: "); pw.println(sVerbose);
|
||||
pw.print(prefix); pw.print("Context: "); pw.println(mContext);
|
||||
pw.print(prefix); pw.print("User: "); pw.println(mContext.getUserId());
|
||||
pw.print(prefix); pw.print("Service: "); pw.println(mService);
|
||||
pw.print(prefix); pw.print("Flags: "); pw.println(mFlags);
|
||||
if (mMainSession != null) {
|
||||
final String prefix3 = prefix2 + " ";
|
||||
pw.print(prefix2); pw.println("Main session:");
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
package android.view.contentcapture;
|
||||
|
||||
import static android.view.contentcapture.ContentCaptureHelper.DEBUG;
|
||||
import static android.view.contentcapture.ContentCaptureHelper.VERBOSE;
|
||||
import static android.view.contentcapture.ContentCaptureHelper.sDebug;
|
||||
import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
|
||||
|
||||
import android.annotation.CallSuper;
|
||||
import android.annotation.IntDef;
|
||||
@@ -233,7 +233,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
|
||||
public final ContentCaptureSession createContentCaptureSession(
|
||||
@NonNull ContentCaptureContext context) {
|
||||
final ContentCaptureSession child = newChild(context);
|
||||
if (DEBUG) {
|
||||
if (sDebug) {
|
||||
Log.d(TAG, "createContentCaptureSession(" + context + ": parent=" + mId + ", child="
|
||||
+ child.mId);
|
||||
}
|
||||
@@ -285,20 +285,20 @@ public abstract class ContentCaptureSession implements AutoCloseable {
|
||||
public final void destroy() {
|
||||
synchronized (mLock) {
|
||||
if (mDestroyed) {
|
||||
if (DEBUG) Log.d(TAG, "destroy(" + mId + "): already destroyed");
|
||||
if (sDebug) Log.d(TAG, "destroy(" + mId + "): already destroyed");
|
||||
return;
|
||||
}
|
||||
mDestroyed = true;
|
||||
|
||||
// TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
|
||||
// id) and send it to the cache of batched commands
|
||||
if (VERBOSE) {
|
||||
if (sVerbose) {
|
||||
Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
|
||||
}
|
||||
// Finish children first
|
||||
if (mChildren != null) {
|
||||
final int numberChildren = mChildren.size();
|
||||
if (VERBOSE) Log.v(TAG, "Destroying " + numberChildren + " children first");
|
||||
if (sVerbose) Log.v(TAG, "Destroying " + numberChildren + " children first");
|
||||
for (int i = 0; i < numberChildren; i++) {
|
||||
final ContentCaptureSession child = mChildren.get(i);
|
||||
try {
|
||||
|
||||
@@ -23,9 +23,13 @@ import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_START
|
||||
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
|
||||
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
|
||||
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
|
||||
import static android.view.contentcapture.ContentCaptureHelper.DEBUG;
|
||||
import static android.view.contentcapture.ContentCaptureHelper.VERBOSE;
|
||||
import static android.view.contentcapture.ContentCaptureHelper.getIntDeviceConfigProperty;
|
||||
import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
|
||||
import static android.view.contentcapture.ContentCaptureHelper.sDebug;
|
||||
import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
|
||||
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY;
|
||||
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE;
|
||||
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -64,6 +68,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
|
||||
private static final String TAG = MainContentCaptureSession.class.getSimpleName();
|
||||
|
||||
// For readability purposes...
|
||||
private static final boolean FORCE_FLUSH = true;
|
||||
|
||||
/**
|
||||
@@ -71,17 +76,9 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
*/
|
||||
private static final int MSG_FLUSH = 1;
|
||||
|
||||
/**
|
||||
* Maximum number of events that are buffered before sent to the app.
|
||||
*/
|
||||
// TODO(b/121044064): use settings
|
||||
private static final int MAX_BUFFER_SIZE = 100;
|
||||
|
||||
/**
|
||||
* Frequency the buffer is flushed if stale.
|
||||
*/
|
||||
// TODO(b/121044064): use settings
|
||||
private static final int FLUSHING_FREQUENCY_MS = 5_000;
|
||||
private static final int DEFAULT_MAX_BUFFER_SIZE = 100;
|
||||
private static final int DEFAULT_FLUSHING_FREQUENCY_MS = 5_000;
|
||||
private static final int DEFAULT_LOG_HISTORY_SIZE = 10;
|
||||
|
||||
/**
|
||||
* Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
|
||||
@@ -131,11 +128,21 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
@Nullable
|
||||
private ArrayList<ContentCaptureEvent> mEvents;
|
||||
|
||||
/**
|
||||
* Maximum number of events that are buffered before sent to the app.
|
||||
*/
|
||||
private final int mMaxBufferSize;
|
||||
|
||||
/**
|
||||
* Frequency the buffer is flushed if idle.
|
||||
*/
|
||||
private final int mIdleFlushingFrequencyMs;
|
||||
|
||||
// Used just for debugging purposes (on dump)
|
||||
private long mNextFlush;
|
||||
|
||||
// TODO(b/121044064): use settings to set size
|
||||
private final LocalLog mFlushHistory = new LocalLog(10);
|
||||
@Nullable
|
||||
private final LocalLog mFlushHistory;
|
||||
|
||||
/** @hide */
|
||||
protected MainContentCaptureSession(@NonNull Context context,
|
||||
@@ -145,6 +152,18 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
mManager = manager;
|
||||
mHandler = handler;
|
||||
mSystemServerInterface = systemServerInterface;
|
||||
|
||||
// TODO(b/123096662): right now we're reading the device config values here, but ideally
|
||||
// it should be read on ContentCaptureManagerService and passed back when the activity
|
||||
// started.
|
||||
mMaxBufferSize = getIntDeviceConfigProperty(DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE,
|
||||
DEFAULT_MAX_BUFFER_SIZE);
|
||||
mIdleFlushingFrequencyMs = getIntDeviceConfigProperty(
|
||||
DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY, DEFAULT_FLUSHING_FREQUENCY_MS);
|
||||
final int logHistorySize = getIntDeviceConfigProperty(
|
||||
DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, DEFAULT_LOG_HISTORY_SIZE);
|
||||
|
||||
mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,14 +188,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
int flags) {
|
||||
if (!isContentCaptureEnabled()) return;
|
||||
|
||||
if (VERBOSE) {
|
||||
if (sVerbose) {
|
||||
Log.v(TAG, "start(): token=" + token + ", comp="
|
||||
+ ComponentName.flattenToShortString(component));
|
||||
}
|
||||
|
||||
if (hasStarted()) {
|
||||
// TODO(b/122959591): make sure this is expected (and when), or use Log.w
|
||||
if (DEBUG) {
|
||||
if (sDebug) {
|
||||
Log.d(TAG, "ignoring handleStartSession(" + token + "/"
|
||||
+ ComponentName.flattenToShortString(component) + " while on state "
|
||||
+ getStateAsString(mState));
|
||||
@@ -187,7 +206,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
mApplicationToken = token;
|
||||
mComponentName = component;
|
||||
|
||||
if (VERBOSE) {
|
||||
if (sVerbose) {
|
||||
Log.v(TAG, "handleStartSession(): token=" + token + ", act="
|
||||
+ getDebugState() + ", id=" + mId);
|
||||
}
|
||||
@@ -252,7 +271,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
mState = resultCode;
|
||||
mDisabled.set(false);
|
||||
}
|
||||
if (VERBOSE) {
|
||||
if (sVerbose) {
|
||||
Log.v(TAG, "handleSessionStarted() result: id=" + mId + " resultCode=" + resultCode
|
||||
+ ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
|
||||
+ ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size()));
|
||||
@@ -267,7 +286,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
@UiThread
|
||||
private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
|
||||
final int eventType = event.getType();
|
||||
if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
|
||||
if (sVerbose) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
|
||||
if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED
|
||||
&& eventType != ContentCaptureEvent.TYPE_CONTEXT_UPDATED) {
|
||||
// TODO(b/120494182): comment when this could happen (dialogs?)
|
||||
@@ -280,14 +299,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
// This happens when the event was queued in the handler before the sesison was ready,
|
||||
// then handleSessionStarted() returned and set it as disabled - we need to drop it,
|
||||
// otherwise it will keep triggering handleScheduleFlush()
|
||||
if (VERBOSE) Log.v(TAG, "handleSendEvent(): ignoring when disabled");
|
||||
if (sVerbose) Log.v(TAG, "handleSendEvent(): ignoring when disabled");
|
||||
return;
|
||||
}
|
||||
if (mEvents == null) {
|
||||
if (VERBOSE) {
|
||||
Log.v(TAG, "handleSendEvent(): creating buffer for " + MAX_BUFFER_SIZE + " events");
|
||||
if (sVerbose) {
|
||||
Log.v(TAG, "handleSendEvent(): creating buffer for " + mMaxBufferSize + " events");
|
||||
}
|
||||
mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
|
||||
mEvents = new ArrayList<>(mMaxBufferSize);
|
||||
}
|
||||
|
||||
// Some type of events can be merged together
|
||||
@@ -299,7 +318,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
// TODO(b/121045053): check if flags match
|
||||
if (lastEvent.getType() == TYPE_VIEW_TEXT_CHANGED
|
||||
&& lastEvent.getId().equals(event.getId())) {
|
||||
if (VERBOSE) {
|
||||
if (sVerbose) {
|
||||
Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text="
|
||||
+ getSanitizedString(event.getText()));
|
||||
}
|
||||
@@ -313,7 +332,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1);
|
||||
if (lastEvent.getType() == TYPE_VIEW_DISAPPEARED
|
||||
&& event.getSessionId().equals(lastEvent.getSessionId())) {
|
||||
if (VERBOSE) {
|
||||
if (sVerbose) {
|
||||
Log.v(TAG, "Buffering TYPE_VIEW_DISAPPEARED events for session "
|
||||
+ lastEvent.getSessionId());
|
||||
}
|
||||
@@ -328,20 +347,20 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
|
||||
final int numberEvents = mEvents.size();
|
||||
|
||||
final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
|
||||
final boolean bufferEvent = numberEvents < mMaxBufferSize;
|
||||
|
||||
if (bufferEvent && !forceFlush) {
|
||||
scheduleFlush(FLUSH_REASON_IDLE_TIMEOUT, /* checkExisting= */ true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mState != STATE_ACTIVE && numberEvents >= MAX_BUFFER_SIZE) {
|
||||
if (mState != STATE_ACTIVE && numberEvents >= mMaxBufferSize) {
|
||||
// Callback from startSession hasn't been called yet - typically happens on system
|
||||
// apps that are started before the system service
|
||||
// TODO(b/122959591): try to ignore session while system is not ready / boot
|
||||
// not complete instead. Similarly, the manager service should return right away
|
||||
// when the user does not have a service set
|
||||
if (DEBUG) {
|
||||
if (sDebug) {
|
||||
Log.d(TAG, "Closing session for " + getDebugState()
|
||||
+ " after " + numberEvents + " delayed events");
|
||||
}
|
||||
@@ -396,12 +415,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
|
||||
@UiThread
|
||||
private void scheduleFlush(@FlushReason int reason, boolean checkExisting) {
|
||||
if (VERBOSE) {
|
||||
if (sVerbose) {
|
||||
Log.v(TAG, "handleScheduleFlush(" + getDebugState(reason)
|
||||
+ ", checkExisting=" + checkExisting);
|
||||
}
|
||||
if (!hasStarted()) {
|
||||
if (VERBOSE) Log.v(TAG, "handleScheduleFlush(): session not started yet");
|
||||
if (sVerbose) Log.v(TAG, "handleScheduleFlush(): session not started yet");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -416,19 +435,19 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
// "Renew" the flush message by removing the previous one
|
||||
mHandler.removeMessages(MSG_FLUSH);
|
||||
}
|
||||
mNextFlush = System.currentTimeMillis() + FLUSHING_FREQUENCY_MS;
|
||||
if (VERBOSE) {
|
||||
mNextFlush = System.currentTimeMillis() + mIdleFlushingFrequencyMs;
|
||||
if (sVerbose) {
|
||||
Log.v(TAG, "handleScheduleFlush(): scheduled to flush in "
|
||||
+ FLUSHING_FREQUENCY_MS + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
|
||||
+ mIdleFlushingFrequencyMs + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
|
||||
}
|
||||
// Post using a Runnable directly to trim a few μs from PooledLambda.obtainMessage()
|
||||
mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, FLUSHING_FREQUENCY_MS);
|
||||
mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, mIdleFlushingFrequencyMs);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void flushIfNeeded(@FlushReason int reason) {
|
||||
if (mEvents == null || mEvents.isEmpty()) {
|
||||
if (VERBOSE) Log.v(TAG, "Nothing to flush");
|
||||
if (sVerbose) Log.v(TAG, "Nothing to flush");
|
||||
return;
|
||||
}
|
||||
flush(reason);
|
||||
@@ -446,7 +465,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
}
|
||||
|
||||
if (mDirectServiceInterface == null) {
|
||||
if (VERBOSE) {
|
||||
if (sVerbose) {
|
||||
Log.v(TAG, "handleForceFlush(" + getDebugState(reason) + "): hold your horses, "
|
||||
+ "client not ready: " + mEvents);
|
||||
}
|
||||
@@ -458,14 +477,16 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
|
||||
final int numberEvents = mEvents.size();
|
||||
final String reasonString = getflushReasonAsString(reason);
|
||||
if (DEBUG) {
|
||||
if (sDebug) {
|
||||
Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState(reason));
|
||||
}
|
||||
// Logs reason, size, max size, idle timeout
|
||||
final String logRecord = "r=" + reasonString + " s=" + numberEvents
|
||||
+ " m=" + MAX_BUFFER_SIZE + " i=" + FLUSHING_FREQUENCY_MS;
|
||||
try {
|
||||
if (mFlushHistory != null) {
|
||||
// Logs reason, size, max size, idle timeout
|
||||
final String logRecord = "r=" + reasonString + " s=" + numberEvents
|
||||
+ " m=" + mMaxBufferSize + " i=" + mIdleFlushingFrequencyMs;
|
||||
mFlushHistory.log(logRecord);
|
||||
}
|
||||
try {
|
||||
mHandler.removeMessages(MSG_FLUSH);
|
||||
|
||||
final ParceledListSlice<ContentCaptureEvent> events = clearEvents();
|
||||
@@ -498,7 +519,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
|
||||
@UiThread
|
||||
private void destroySession() {
|
||||
if (DEBUG) {
|
||||
if (sDebug) {
|
||||
Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
|
||||
+ (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
|
||||
+ getDebugState());
|
||||
@@ -516,7 +537,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
// clearings out.
|
||||
@UiThread
|
||||
private void resetSession(int newState) {
|
||||
if (VERBOSE) {
|
||||
if (sVerbose) {
|
||||
Log.v(TAG, "handleResetSession(" + getActivityName() + "): from "
|
||||
+ getStateAsString(mState) + " to " + getStateAsString(newState));
|
||||
}
|
||||
@@ -646,8 +667,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
if (mEvents != null && !mEvents.isEmpty()) {
|
||||
final int numberEvents = mEvents.size();
|
||||
pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents);
|
||||
pw.print('/'); pw.println(MAX_BUFFER_SIZE);
|
||||
if (VERBOSE && numberEvents > 0) {
|
||||
pw.print('/'); pw.println(mMaxBufferSize);
|
||||
if (sVerbose && numberEvents > 0) {
|
||||
final String prefix3 = prefix + " ";
|
||||
for (int i = 0; i < numberEvents; i++) {
|
||||
final ContentCaptureEvent event = mEvents.get(i);
|
||||
@@ -655,13 +676,19 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
|
||||
pw.println();
|
||||
}
|
||||
}
|
||||
pw.print(prefix); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS);
|
||||
pw.print(prefix); pw.print("flush frequency: "); pw.println(mIdleFlushingFrequencyMs);
|
||||
pw.print(prefix); pw.print("next flush: ");
|
||||
TimeUtils.formatDuration(mNextFlush - System.currentTimeMillis(), pw);
|
||||
pw.print(" ("); pw.print(TimeUtils.logTimeOfDay(mNextFlush)); pw.println(")");
|
||||
}
|
||||
pw.print(prefix); pw.println("flush history:");
|
||||
mFlushHistory.reverseDump(/* fd= */ null, pw, /* args= */ null); pw.println();
|
||||
if (mFlushHistory != null) {
|
||||
pw.print(prefix); pw.println("flush history:");
|
||||
mFlushHistory.reverseDump(/* fd= */ null, pw, /* args= */ null); pw.println();
|
||||
} else {
|
||||
pw.print(prefix); pw.println("not logging flush history");
|
||||
}
|
||||
|
||||
super.dump(prefix, pw);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,6 +45,7 @@ import android.provider.Settings;
|
||||
import android.util.LocalLog;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.contentcapture.ContentCaptureHelper;
|
||||
import android.view.contentcapture.ContentCaptureManager;
|
||||
import android.view.contentcapture.IContentCaptureManager;
|
||||
import android.view.contentcapture.UserDataRemovalRequest;
|
||||
@@ -79,7 +80,8 @@ public final class ContentCaptureManagerService extends
|
||||
|
||||
private final LocalService mLocalService = new LocalService();
|
||||
|
||||
private final LocalLog mRequestsHistory = new LocalLog(20);
|
||||
@Nullable
|
||||
final LocalLog mRequestsHistory;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private ActivityManagerInternal mAm;
|
||||
@@ -105,15 +107,19 @@ public final class ContentCaptureManagerService extends
|
||||
UserManager.DISALLOW_CONTENT_CAPTURE);
|
||||
DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
|
||||
ActivityThread.currentApplication().getMainExecutor(),
|
||||
(namespace, key, value) -> {
|
||||
if (!ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED
|
||||
.equals(key)) {
|
||||
Slog.i(mTag, "Ignoring change on " + key);
|
||||
return;
|
||||
}
|
||||
setDisabledByDeviceConfig(value);
|
||||
});
|
||||
setDisabledByDeviceConfig();
|
||||
(namespace, key, value) -> onDeviceConfigChange(key, value));
|
||||
setLoggingLevelFromDeviceConfig();
|
||||
setDisabledFromDeviceConfig();
|
||||
|
||||
final int loggingSize = ContentCaptureHelper.getIntDeviceConfigProperty(
|
||||
ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, 20);
|
||||
if (loggingSize > 0) {
|
||||
if (debug) Slog.d(mTag, "log history size: " + loggingSize);
|
||||
mRequestsHistory = new LocalLog(loggingSize);
|
||||
} else {
|
||||
if (debug) Slog.d(mTag, "disabled log history because size is " + loggingSize);
|
||||
mRequestsHistory = null;
|
||||
}
|
||||
|
||||
// Sets which services are disabled
|
||||
final UserManager um = getContext().getSystemService(UserManager.class);
|
||||
@@ -213,7 +219,33 @@ public final class ContentCaptureManagerService extends
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setDisabledByDeviceConfig() {
|
||||
private void onDeviceConfigChange(@NonNull String key, @Nullable String value) {
|
||||
switch (key) {
|
||||
case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED:
|
||||
setDisabledByDeviceConfig(value);
|
||||
return;
|
||||
case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL:
|
||||
setLoggingLevelFromDeviceConfig();
|
||||
return;
|
||||
case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE:
|
||||
case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY:
|
||||
case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE:
|
||||
case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY:
|
||||
// TODO(b/123096662): implement it
|
||||
Slog.d(mTag, "changes on " + key + " not supported yet");
|
||||
return;
|
||||
default:
|
||||
Slog.i(mTag, "Ignoring change on " + key);
|
||||
}
|
||||
}
|
||||
|
||||
private void setLoggingLevelFromDeviceConfig() {
|
||||
ContentCaptureHelper.setLoggingLevel();
|
||||
verbose = ContentCaptureHelper.sVerbose;
|
||||
debug = ContentCaptureHelper.sDebug;
|
||||
}
|
||||
|
||||
private void setDisabledFromDeviceConfig() {
|
||||
final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
|
||||
ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED);
|
||||
setDisabledByDeviceConfig(value);
|
||||
@@ -327,13 +359,6 @@ public final class ContentCaptureManagerService extends
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a request so it's dumped later...
|
||||
*/
|
||||
void logRequestLocked(@NonNull String historyItem) {
|
||||
mRequestsHistory.log(historyItem);
|
||||
}
|
||||
|
||||
private ActivityManagerInternal getAmInternal() {
|
||||
synchronized (mLock) {
|
||||
if (mAm == null) {
|
||||
@@ -527,9 +552,13 @@ public final class ContentCaptureManagerService extends
|
||||
synchronized (mLock) {
|
||||
dumpLocked("", pw);
|
||||
}
|
||||
if (showHistory) {
|
||||
pw.println(); pw.println("Requests history:"); pw.println();
|
||||
pw.print("Requests history: ");
|
||||
if (mRequestsHistory == null) {
|
||||
pw.println("disabled by device config");
|
||||
} else if (showHistory) {
|
||||
pw.println();
|
||||
mRequestsHistory.reverseDump(fd, pw, args);
|
||||
pw.println();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -187,13 +187,15 @@ final class ContentCapturePerUserService
|
||||
final ComponentName componentName = activityPresentationInfo.componentName;
|
||||
final ComponentName serviceComponentName = getServiceComponentName();
|
||||
final boolean enabled = isEnabledLocked();
|
||||
final String historyItem =
|
||||
"id=" + sessionId + " uid=" + uid
|
||||
+ " a=" + ComponentName.flattenToShortString(componentName)
|
||||
+ " t=" + taskId + " d=" + displayId
|
||||
+ " s=" + ComponentName.flattenToShortString(serviceComponentName)
|
||||
+ " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)");
|
||||
mMaster.logRequestLocked(historyItem);
|
||||
if (mMaster.mRequestsHistory != null) {
|
||||
final String historyItem =
|
||||
"id=" + sessionId + " uid=" + uid
|
||||
+ " a=" + ComponentName.flattenToShortString(componentName)
|
||||
+ " t=" + taskId + " d=" + displayId
|
||||
+ " s=" + ComponentName.flattenToShortString(serviceComponentName)
|
||||
+ " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)");
|
||||
mMaster.mRequestsHistory.log(historyItem);
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
// TODO: it would be better to split in differet reasons, like
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.content.ComponentName;
|
||||
import android.os.IBinder;
|
||||
import android.service.contentcapture.ContentCaptureService;
|
||||
import android.service.contentcapture.SnapshotData;
|
||||
import android.util.LocalLog;
|
||||
import android.util.Slog;
|
||||
import android.view.contentcapture.ContentCaptureContext;
|
||||
import android.view.contentcapture.ContentCaptureSessionId;
|
||||
@@ -86,7 +87,10 @@ final class ContentCaptureServerSession {
|
||||
*/
|
||||
@GuardedBy("mLock")
|
||||
public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) {
|
||||
mService.getMaster().logRequestLocked("snapshot: id=" + mId);
|
||||
final LocalLog logHistory = mService.getMaster().mRequestsHistory;
|
||||
if (logHistory != null) {
|
||||
logHistory.log("snapshot: id=" + mId);
|
||||
}
|
||||
|
||||
mRemoteService.onActivitySnapshotRequest(mId, snapshotData);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user