Merge "TextClassifierService.onSelectionEvent" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
197ea9e3a5
@@ -51069,7 +51069,8 @@ package android.view.textclassifier {
|
||||
method public java.lang.String getWidgetVersion();
|
||||
}
|
||||
|
||||
public final class SelectionEvent {
|
||||
public final class SelectionEvent implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public long getDurationSincePreviousEvent();
|
||||
method public long getDurationSinceSessionStart();
|
||||
method public int getEnd();
|
||||
@@ -51086,6 +51087,7 @@ package android.view.textclassifier {
|
||||
method public int getStart();
|
||||
method public java.lang.String getWidgetType();
|
||||
method public java.lang.String getWidgetVersion();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final int ACTION_ABANDON = 107; // 0x6b
|
||||
field public static final int ACTION_COPY = 101; // 0x65
|
||||
field public static final int ACTION_CUT = 103; // 0x67
|
||||
@@ -51097,6 +51099,7 @@ package android.view.textclassifier {
|
||||
field public static final int ACTION_SELECT_ALL = 200; // 0xc8
|
||||
field public static final int ACTION_SHARE = 104; // 0x68
|
||||
field public static final int ACTION_SMART_SHARE = 105; // 0x69
|
||||
field public static final android.os.Parcelable.Creator<android.view.textclassifier.SelectionEvent> CREATOR;
|
||||
field public static final int EVENT_AUTO_SELECTION = 5; // 0x5
|
||||
field public static final int EVENT_SELECTION_MODIFIED = 2; // 0x2
|
||||
field public static final int EVENT_SELECTION_STARTED = 1; // 0x1
|
||||
|
||||
@@ -4710,6 +4710,7 @@ package android.service.textclassifier {
|
||||
method public final android.os.IBinder onBind(android.content.Intent);
|
||||
method public abstract void onClassifyText(java.lang.CharSequence, int, int, android.view.textclassifier.TextClassification.Options, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextClassification>);
|
||||
method public abstract void onGenerateLinks(java.lang.CharSequence, android.view.textclassifier.TextLinks.Options, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLinks>);
|
||||
method public void onSelectionEvent(android.view.textclassifier.SelectionEvent);
|
||||
method public abstract void onSuggestSelection(java.lang.CharSequence, int, int, android.view.textclassifier.TextSelection.Options, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextSelection>);
|
||||
field public static final java.lang.String SERVICE_INTERFACE = "android.service.textclassifier.TextClassifierService";
|
||||
}
|
||||
|
||||
@@ -19,13 +19,14 @@ package android.service.textclassifier;
|
||||
import android.service.textclassifier.ITextClassificationCallback;
|
||||
import android.service.textclassifier.ITextLinksCallback;
|
||||
import android.service.textclassifier.ITextSelectionCallback;
|
||||
import android.view.textclassifier.SelectionEvent;
|
||||
import android.view.textclassifier.TextClassification;
|
||||
import android.view.textclassifier.TextLinks;
|
||||
import android.view.textclassifier.TextSelection;
|
||||
|
||||
/**
|
||||
* TextClassifierService binder interface.
|
||||
* See TextClassifier for interface documentation.
|
||||
* See TextClassifier (and TextClassifier.Logger) for interface documentation.
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface ITextClassifierService {
|
||||
@@ -44,4 +45,6 @@ oneway interface ITextClassifierService {
|
||||
in CharSequence text,
|
||||
in TextLinks.Options options,
|
||||
in ITextLinksCallback c);
|
||||
|
||||
void onSelectionEvent(in SelectionEvent event);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Slog;
|
||||
import android.view.textclassifier.SelectionEvent;
|
||||
import android.view.textclassifier.TextClassification;
|
||||
import android.view.textclassifier.TextClassificationManager;
|
||||
import android.view.textclassifier.TextClassifier;
|
||||
@@ -171,6 +172,12 @@ public abstract class TextClassifierService extends Service {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void onSelectionEvent(SelectionEvent event) throws RemoteException {
|
||||
TextClassifierService.this.onSelectionEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
@Nullable
|
||||
@@ -237,6 +244,15 @@ public abstract class TextClassifierService extends Service {
|
||||
@NonNull CancellationSignal cancellationSignal,
|
||||
@NonNull Callback<TextLinks> callback);
|
||||
|
||||
/**
|
||||
* Writes the selection event.
|
||||
* This is called when a selection event occurs. e.g. user changed selection; or smart selection
|
||||
* happened.
|
||||
*
|
||||
* <p>The default implementation ignores the event.
|
||||
*/
|
||||
public void onSelectionEvent(@NonNull SelectionEvent event) {}
|
||||
|
||||
/**
|
||||
* Returns a TextClassifier that runs in this service's process.
|
||||
* If the local TextClassifier is disabled, this returns {@link TextClassifier#NO_OP}.
|
||||
|
||||
@@ -94,10 +94,7 @@ public abstract class Logger {
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the selection event.
|
||||
*
|
||||
* <p><strong>NOTE: </strong>This method is designed for subclasses.
|
||||
* Apps should not call it directly.
|
||||
* Writes the selection event to a log.
|
||||
*/
|
||||
public abstract void writeEvent(@NonNull SelectionEvent event);
|
||||
|
||||
|
||||
19
core/java/android/view/textclassifier/SelectionEvent.aidl
Normal file
19
core/java/android/view/textclassifier/SelectionEvent.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.view.textclassifier;
|
||||
|
||||
parcelable SelectionEvent;
|
||||
@@ -18,6 +18,8 @@ package android.view.textclassifier;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.view.textclassifier.TextClassifier.EntityType;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
@@ -25,12 +27,13 @@ import com.android.internal.util.Preconditions;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A selection event.
|
||||
* Specify index parameters as word token indices.
|
||||
*/
|
||||
public final class SelectionEvent {
|
||||
public final class SelectionEvent implements Parcelable {
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@@ -121,9 +124,9 @@ public final class SelectionEvent {
|
||||
private String mSignature;
|
||||
private long mEventTime;
|
||||
private long mDurationSinceSessionStart;
|
||||
private long mDurationSinceLastEvent;
|
||||
private long mDurationSincePreviousEvent;
|
||||
private int mEventIndex;
|
||||
private String mSessionId;
|
||||
@Nullable private String mSessionId;
|
||||
private int mStart;
|
||||
private int mEnd;
|
||||
private int mSmartStart;
|
||||
@@ -146,6 +149,60 @@ public final class SelectionEvent {
|
||||
mInvocationMethod = invocationMethod;
|
||||
}
|
||||
|
||||
private SelectionEvent(Parcel in) {
|
||||
mAbsoluteStart = in.readInt();
|
||||
mAbsoluteEnd = in.readInt();
|
||||
mEventType = in.readInt();
|
||||
mEntityType = in.readString();
|
||||
mWidgetVersion = in.readInt() > 0 ? in.readString() : null;
|
||||
mPackageName = in.readString();
|
||||
mWidgetType = in.readString();
|
||||
mInvocationMethod = in.readInt();
|
||||
mSignature = in.readString();
|
||||
mEventTime = in.readLong();
|
||||
mDurationSinceSessionStart = in.readLong();
|
||||
mDurationSincePreviousEvent = in.readLong();
|
||||
mEventIndex = in.readInt();
|
||||
mSessionId = in.readInt() > 0 ? in.readString() : null;
|
||||
mStart = in.readInt();
|
||||
mEnd = in.readInt();
|
||||
mSmartStart = in.readInt();
|
||||
mSmartEnd = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mAbsoluteStart);
|
||||
dest.writeInt(mAbsoluteEnd);
|
||||
dest.writeInt(mEventType);
|
||||
dest.writeString(mEntityType);
|
||||
dest.writeInt(mWidgetVersion != null ? 1 : 0);
|
||||
if (mWidgetVersion != null) {
|
||||
dest.writeString(mWidgetVersion);
|
||||
}
|
||||
dest.writeString(mPackageName);
|
||||
dest.writeString(mWidgetType);
|
||||
dest.writeInt(mInvocationMethod);
|
||||
dest.writeString(mSignature);
|
||||
dest.writeLong(mEventTime);
|
||||
dest.writeLong(mDurationSinceSessionStart);
|
||||
dest.writeLong(mDurationSincePreviousEvent);
|
||||
dest.writeInt(mEventIndex);
|
||||
dest.writeInt(mSessionId != null ? 1 : 0);
|
||||
if (mSessionId != null) {
|
||||
dest.writeString(mSessionId);
|
||||
}
|
||||
dest.writeInt(mStart);
|
||||
dest.writeInt(mEnd);
|
||||
dest.writeInt(mSmartStart);
|
||||
dest.writeInt(mSmartEnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getAbsoluteStart() {
|
||||
return mAbsoluteStart;
|
||||
}
|
||||
@@ -240,11 +297,11 @@ public final class SelectionEvent {
|
||||
* in the selection session was triggered.
|
||||
*/
|
||||
public long getDurationSincePreviousEvent() {
|
||||
return mDurationSinceLastEvent;
|
||||
return mDurationSincePreviousEvent;
|
||||
}
|
||||
|
||||
SelectionEvent setDurationSincePreviousEvent(long durationMs) {
|
||||
this.mDurationSinceLastEvent = durationMs;
|
||||
this.mDurationSincePreviousEvent = durationMs;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -341,16 +398,67 @@ public final class SelectionEvent {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
|
||||
mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, mSignature,
|
||||
mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
|
||||
mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof SelectionEvent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final SelectionEvent other = (SelectionEvent) obj;
|
||||
return mAbsoluteStart == other.mAbsoluteStart
|
||||
&& mAbsoluteEnd == other.mAbsoluteEnd
|
||||
&& mEventType == other.mEventType
|
||||
&& Objects.equals(mEntityType, other.mEntityType)
|
||||
&& Objects.equals(mWidgetVersion, other.mWidgetVersion)
|
||||
&& Objects.equals(mPackageName, other.mPackageName)
|
||||
&& Objects.equals(mWidgetType, other.mWidgetType)
|
||||
&& mInvocationMethod == other.mInvocationMethod
|
||||
&& Objects.equals(mSignature, other.mSignature)
|
||||
&& mEventTime == other.mEventTime
|
||||
&& mDurationSinceSessionStart == other.mDurationSinceSessionStart
|
||||
&& mDurationSincePreviousEvent == other.mDurationSincePreviousEvent
|
||||
&& mEventIndex == other.mEventIndex
|
||||
&& Objects.equals(mSessionId, other.mSessionId)
|
||||
&& mStart == other.mStart
|
||||
&& mEnd == other.mEnd
|
||||
&& mSmartStart == other.mSmartStart
|
||||
&& mSmartEnd == other.mSmartEnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(Locale.US,
|
||||
"SelectionEvent {absoluteStart=%d, absoluteEnd=%d, eventType=%d, entityType=%s, "
|
||||
+ "widgetVersion=%s, packageName=%s, widgetType=%s, signature=%s, "
|
||||
+ "eventTime=%d, durationSinceSessionStart=%d, durationSinceLastEvent=%d, "
|
||||
+ "eventIndex=%d, sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d}",
|
||||
+ "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, "
|
||||
+ "signature=%s, eventTime=%d, durationSinceSessionStart=%d, "
|
||||
+ "durationSincePreviousEvent=%d, eventIndex=%d, sessionId=%s, start=%d, end=%d, "
|
||||
+ "smartStart=%d, smartEnd=%d}",
|
||||
mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
|
||||
mWidgetVersion, mPackageName, mWidgetType, mSignature,
|
||||
mEventTime, mDurationSinceSessionStart, mDurationSinceLastEvent,
|
||||
mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, mSignature,
|
||||
mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
|
||||
mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
|
||||
}
|
||||
|
||||
public static final Creator<SelectionEvent> CREATOR = new Creator<SelectionEvent>() {
|
||||
@Override
|
||||
public SelectionEvent createFromParcel(Parcel in) {
|
||||
return new SelectionEvent(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectionEvent[] newArray(int size) {
|
||||
return new SelectionEvent[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.service.textclassifier.ITextClassifierService;
|
||||
import android.service.textclassifier.ITextLinksCallback;
|
||||
import android.service.textclassifier.ITextSelectionCallback;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
@@ -46,6 +47,12 @@ final class SystemTextClassifier implements TextClassifier {
|
||||
private final TextClassifier mFallback;
|
||||
private final String mPackageName;
|
||||
|
||||
private final Object mLoggerLock = new Object();
|
||||
@GuardedBy("mLoggerLock")
|
||||
private Logger.Config mLoggerConfig;
|
||||
@GuardedBy("mLoggerLock")
|
||||
private Logger mLogger;
|
||||
|
||||
SystemTextClassifier(Context context, TextClassificationConstants settings)
|
||||
throws ServiceManager.ServiceNotFoundException {
|
||||
mManagerService = ITextClassifierService.Stub.asInterface(
|
||||
@@ -58,6 +65,7 @@ final class SystemTextClassifier implements TextClassifier {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
@WorkerThread
|
||||
public TextSelection suggestSelection(
|
||||
@NonNull CharSequence text,
|
||||
@@ -84,6 +92,7 @@ final class SystemTextClassifier implements TextClassifier {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
@WorkerThread
|
||||
public TextClassification classifyText(
|
||||
@NonNull CharSequence text,
|
||||
@@ -109,6 +118,7 @@ final class SystemTextClassifier implements TextClassifier {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
@WorkerThread
|
||||
public TextLinks generateLinks(
|
||||
@NonNull CharSequence text, @Nullable TextLinks.Options options) {
|
||||
@@ -142,11 +152,33 @@ final class SystemTextClassifier implements TextClassifier {
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
@WorkerThread
|
||||
public int getMaxGenerateLinksTextLength() {
|
||||
// TODO: retrieve this from the bound service.
|
||||
return mFallback.getMaxGenerateLinksTextLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLogger(@NonNull Logger.Config config) {
|
||||
Preconditions.checkNotNull(config);
|
||||
synchronized (mLoggerLock) {
|
||||
if (mLogger == null || !config.equals(mLoggerConfig)) {
|
||||
mLoggerConfig = config;
|
||||
mLogger = new Logger(config) {
|
||||
@Override
|
||||
public void writeEvent(SelectionEvent event) {
|
||||
try {
|
||||
mManagerService.onSelectionEvent(event);
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowAsRuntimeException();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return mLogger;
|
||||
}
|
||||
|
||||
private static final class TextSelectionCallback extends ITextSelectionCallback.Stub {
|
||||
|
||||
final ResponseReceiver<TextSelection> mReceiver = new ResponseReceiver<>();
|
||||
|
||||
@@ -323,6 +323,7 @@ public interface TextClassifier {
|
||||
* @see #generateLinks(CharSequence)
|
||||
* @see #generateLinks(CharSequence, TextLinks.Options)
|
||||
*/
|
||||
@WorkerThread
|
||||
default int getMaxGenerateLinksTextLength() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.view.textclassifier;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.WorkerThread;
|
||||
import android.app.SearchManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentUris;
|
||||
@@ -42,7 +43,6 @@ import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -78,7 +78,6 @@ public final class TextClassifierImpl implements TextClassifier {
|
||||
|
||||
private final Context mContext;
|
||||
private final TextClassifier mFallback;
|
||||
|
||||
private final GenerateLinksLogger mGenerateLinksLogger;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
@@ -91,9 +90,9 @@ public final class TextClassifierImpl implements TextClassifier {
|
||||
|
||||
private final Object mLoggerLock = new Object();
|
||||
@GuardedBy("mLoggerLock") // Do not access outside this lock.
|
||||
private WeakReference<Logger.Config> mLoggerConfig = new WeakReference<>(null);
|
||||
private Logger.Config mLoggerConfig;
|
||||
@GuardedBy("mLoggerLock") // Do not access outside this lock.
|
||||
private Logger mLogger; // Should never be null if mLoggerConfig.get() is not null.
|
||||
private Logger mLogger;
|
||||
|
||||
private final TextClassificationConstants mSettings;
|
||||
|
||||
@@ -106,6 +105,7 @@ public final class TextClassifierImpl implements TextClassifier {
|
||||
|
||||
/** @inheritDoc */
|
||||
@Override
|
||||
@WorkerThread
|
||||
public TextSelection suggestSelection(
|
||||
@NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex,
|
||||
@Nullable TextSelection.Options options) {
|
||||
@@ -169,6 +169,7 @@ public final class TextClassifierImpl implements TextClassifier {
|
||||
|
||||
/** @inheritDoc */
|
||||
@Override
|
||||
@WorkerThread
|
||||
public TextClassification classifyText(
|
||||
@NonNull CharSequence text, int startIndex, int endIndex,
|
||||
@Nullable TextClassification.Options options) {
|
||||
@@ -204,6 +205,7 @@ public final class TextClassifierImpl implements TextClassifier {
|
||||
|
||||
/** @inheritDoc */
|
||||
@Override
|
||||
@WorkerThread
|
||||
public TextLinks generateLinks(
|
||||
@NonNull CharSequence text, @Nullable TextLinks.Options options) {
|
||||
Utils.validate(text, getMaxGenerateLinksTextLength(), false /* allowInMainThread */);
|
||||
@@ -282,16 +284,17 @@ public final class TextClassifierImpl implements TextClassifier {
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
@Override
|
||||
public Logger getLogger(@NonNull Logger.Config config) {
|
||||
Preconditions.checkNotNull(config);
|
||||
synchronized (mLoggerLock) {
|
||||
if (mLoggerConfig.get() == null || !mLoggerConfig.get().equals(config)) {
|
||||
mLoggerConfig = new WeakReference<>(config);
|
||||
if (mLogger == null || !config.equals(mLoggerConfig)) {
|
||||
mLoggerConfig = config;
|
||||
mLogger = new DefaultLogger(config);
|
||||
}
|
||||
return mLogger;
|
||||
}
|
||||
return mLogger;
|
||||
}
|
||||
|
||||
private TextClassifierImplNative getNative(LocaleList localeList)
|
||||
|
||||
@@ -648,6 +648,9 @@ public final class SelectionActionModeHelper {
|
||||
* Part selection of a word e.g. "or" is counted as selecting the
|
||||
* entire word i.e. equivalent to "York", and each special character is counted as a word, e.g.
|
||||
* "," is at [2, 3). Whitespaces are ignored.
|
||||
*
|
||||
* NOTE that the definition of a word is defined by the TextClassifier's Logger's token
|
||||
* iterator.
|
||||
*/
|
||||
private static final class SelectionMetricsLogger {
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.view.textclassifier;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SelectionEventTest {
|
||||
|
||||
@Test
|
||||
public void testParcel() {
|
||||
final SelectionEvent[] captured = new SelectionEvent[1];
|
||||
final Logger logger = new Logger(new Logger.Config(
|
||||
InstrumentationRegistry.getTargetContext(), Logger.WIDGET_TEXTVIEW, null)) {
|
||||
@Override
|
||||
public void writeEvent(SelectionEvent event) {
|
||||
captured[0] = event;
|
||||
}
|
||||
};
|
||||
logger.logSelectionStartedEvent(SelectionEvent.INVOCATION_MANUAL, 0);
|
||||
final SelectionEvent event = captured[0];
|
||||
final Parcel parcel = Parcel.obtain();
|
||||
event.writeToParcel(parcel, event.describeContents());
|
||||
parcel.setDataPosition(0);
|
||||
assertEquals(event, SelectionEvent.CREATOR.createFromParcel(parcel));
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,12 @@
|
||||
|
||||
package com.android.server.textclassifier;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
@@ -30,6 +31,7 @@ import android.service.textclassifier.ITextClassificationCallback;
|
||||
import android.service.textclassifier.ITextLinksCallback;
|
||||
import android.service.textclassifier.ITextSelectionCallback;
|
||||
import android.service.textclassifier.TextClassifierService;
|
||||
import android.view.textclassifier.SelectionEvent;
|
||||
import android.view.textclassifier.TextClassification;
|
||||
import android.view.textclassifier.TextClassifier;
|
||||
import android.view.textclassifier.TextLinks;
|
||||
@@ -216,6 +218,23 @@ public final class TextClassificationManagerService extends ITextClassifierServi
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionEvent(SelectionEvent event) throws RemoteException {
|
||||
validateInput(event, mContext);
|
||||
|
||||
synchronized (mLock) {
|
||||
if (isBoundLocked()) {
|
||||
mService.onSelectionEvent(event);
|
||||
} else {
|
||||
final Callable<Void> request = () -> {
|
||||
onSelectionEvent(event);
|
||||
return null;
|
||||
};
|
||||
enqueueRequestLocked(request, null /* onServiceFailure */, null /* binder */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the service is bound or in the process of being bound.
|
||||
* Returns false otherwise.
|
||||
@@ -281,8 +300,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi
|
||||
private final class PendingRequest implements IBinder.DeathRecipient {
|
||||
|
||||
private final Callable<Void> mRequest;
|
||||
private final Callable<Void> mOnServiceFailure;
|
||||
private final IBinder mBinder;
|
||||
@Nullable private final Callable<Void> mOnServiceFailure;
|
||||
@Nullable private final IBinder mBinder;
|
||||
|
||||
/**
|
||||
* Initializes a new pending request.
|
||||
@@ -292,15 +311,17 @@ public final class TextClassificationManagerService extends ITextClassifierServi
|
||||
* @param binder binder to the process that made this pending request
|
||||
*/
|
||||
PendingRequest(
|
||||
@NonNull Callable<Void> request, @NonNull Callable<Void> onServiceFailure,
|
||||
@NonNull IBinder binder) {
|
||||
Callable<Void> request, @Nullable Callable<Void> onServiceFailure,
|
||||
@Nullable IBinder binder) {
|
||||
mRequest = Preconditions.checkNotNull(request);
|
||||
mOnServiceFailure = Preconditions.checkNotNull(onServiceFailure);
|
||||
mBinder = Preconditions.checkNotNull(binder);
|
||||
try {
|
||||
mBinder.linkToDeath(this, 0);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
mOnServiceFailure = onServiceFailure;
|
||||
mBinder = binder;
|
||||
if (mBinder != null) {
|
||||
try {
|
||||
mBinder.linkToDeath(this, 0);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,11 +338,13 @@ public final class TextClassificationManagerService extends ITextClassifierServi
|
||||
@GuardedBy("mLock")
|
||||
void notifyServiceFailureLocked() {
|
||||
removeLocked();
|
||||
try {
|
||||
mOnServiceFailure.call();
|
||||
} catch (Exception e) {
|
||||
Slog.d(LOG_TAG, "Error notifying callback of service failure: "
|
||||
+ e.getMessage());
|
||||
if (mOnServiceFailure != null) {
|
||||
try {
|
||||
mOnServiceFailure.call();
|
||||
} catch (Exception e) {
|
||||
Slog.d(LOG_TAG, "Error notifying callback of service failure: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +359,9 @@ public final class TextClassificationManagerService extends ITextClassifierServi
|
||||
@GuardedBy("mLock")
|
||||
private void removeLocked() {
|
||||
mPendingRequests.remove(this);
|
||||
mBinder.unlinkToDeath(this, 0);
|
||||
if (mBinder != null) {
|
||||
mBinder.unlinkToDeath(this, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,4 +384,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi
|
||||
throw new RemoteException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateInput(SelectionEvent event, Context context)
|
||||
throws RemoteException {
|
||||
try {
|
||||
final int uid = context.getPackageManager()
|
||||
.getPackageUid(event.getPackageName(), 0);
|
||||
Preconditions.checkArgument(Binder.getCallingUid() == uid);
|
||||
} catch (IllegalArgumentException | NullPointerException |
|
||||
PackageManager.NameNotFoundException e) {
|
||||
throw new RemoteException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user