Merge "Avoid sync IPCs from TSMS to SpellCheckerService"
This commit is contained in:
@@ -370,6 +370,7 @@ LOCAL_SRC_FILES += \
|
||||
core/java/com/android/internal/statusbar/IStatusBar.aidl \
|
||||
core/java/com/android/internal/statusbar/IStatusBarService.aidl \
|
||||
core/java/com/android/internal/textservice/ISpellCheckerService.aidl \
|
||||
core/java/com/android/internal/textservice/ISpellCheckerServiceCallback.aidl \
|
||||
core/java/com/android/internal/textservice/ISpellCheckerSession.aidl \
|
||||
core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl \
|
||||
core/java/com/android/internal/textservice/ITextServicesManager.aidl \
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.service.textservice;
|
||||
|
||||
import com.android.internal.textservice.ISpellCheckerService;
|
||||
import com.android.internal.textservice.ISpellCheckerServiceCallback;
|
||||
import com.android.internal.textservice.ISpellCheckerSession;
|
||||
import com.android.internal.textservice.ISpellCheckerSessionListener;
|
||||
|
||||
@@ -311,16 +312,39 @@ public abstract class SpellCheckerService extends Service {
|
||||
mInternalServiceRef = new WeakReference<SpellCheckerService>(service);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the system when an application is requesting a new spell checker session.
|
||||
*
|
||||
* <p>Note: This is an internal protocol used by the system to establish spell checker
|
||||
* sessions, which is not guaranteed to be stable and is subject to change.</p>
|
||||
*
|
||||
* @param locale locale to be returned from {@link Session#getLocale()}
|
||||
* @param listener IPC channel object to be used to implement
|
||||
* {@link Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)} and
|
||||
* {@link Session#onGetSuggestions(TextInfo, int)}
|
||||
* @param bundle bundle to be returned from {@link Session#getBundle()}
|
||||
* @param callback IPC channel to return the result to the caller in an asynchronous manner
|
||||
*/
|
||||
@Override
|
||||
public ISpellCheckerSession getISpellCheckerSession(
|
||||
String locale, ISpellCheckerSessionListener listener, Bundle bundle) {
|
||||
public void getISpellCheckerSession(
|
||||
String locale, ISpellCheckerSessionListener listener, Bundle bundle,
|
||||
ISpellCheckerServiceCallback callback) {
|
||||
final SpellCheckerService service = mInternalServiceRef.get();
|
||||
if (service == null) return null;
|
||||
final Session session = service.createSession();
|
||||
final InternalISpellCheckerSession internalSession =
|
||||
new InternalISpellCheckerSession(locale, listener, bundle, session);
|
||||
session.onCreate();
|
||||
return internalSession;
|
||||
final InternalISpellCheckerSession internalSession;
|
||||
if (service == null) {
|
||||
// If the owner SpellCheckerService object was already destroyed and got GC-ed,
|
||||
// the weak-reference returns null and we should just ignore this request.
|
||||
internalSession = null;
|
||||
} else {
|
||||
final Session session = service.createSession();
|
||||
internalSession =
|
||||
new InternalISpellCheckerSession(locale, listener, bundle, session);
|
||||
session.onCreate();
|
||||
}
|
||||
try {
|
||||
callback.onSessionCreated(internalSession);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,16 +16,32 @@
|
||||
|
||||
package com.android.internal.textservice;
|
||||
|
||||
import com.android.internal.textservice.ISpellCheckerServiceCallback;
|
||||
import com.android.internal.textservice.ISpellCheckerSession;
|
||||
import com.android.internal.textservice.ISpellCheckerSessionListener;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* Public interface to the global spell checker.
|
||||
* IPC channels from TextServicesManagerService to SpellCheckerService.
|
||||
* @hide
|
||||
*/
|
||||
interface ISpellCheckerService {
|
||||
ISpellCheckerSession getISpellCheckerSession(
|
||||
String locale, ISpellCheckerSessionListener listener, in Bundle bundle);
|
||||
oneway interface ISpellCheckerService {
|
||||
/**
|
||||
* Called from the system when an application is requesting a new spell checker session.
|
||||
*
|
||||
* <p>Note: This is an internal protocol used by the system to establish spell checker sessions,
|
||||
* which is not guaranteed to be stable and is subject to change.</p>
|
||||
*
|
||||
* @param locale locale to be returned from
|
||||
* {@link android.service.textservice.SpellCheckerService.Session#getLocale()}
|
||||
* @param listener IPC channel object to be used to implement
|
||||
* {@link android.service.textservice.SpellCheckerService.Session#onGetSuggestionsMultiple(TextInfo[], int, boolean)} and
|
||||
* {@link android.service.textservice.SpellCheckerService.Session#onGetSuggestions(TextInfo, int)}
|
||||
* @param bundle bundle to be returned from {@link android.service.textservice.SpellCheckerService.Session#getBundle()}
|
||||
* @param callback IPC channel to return the result to the caller in an asynchronous manner
|
||||
*/
|
||||
void getISpellCheckerSession(
|
||||
String locale, ISpellCheckerSessionListener listener, in Bundle bundle,
|
||||
ISpellCheckerServiceCallback callback);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.internal.textservice;
|
||||
|
||||
import com.android.internal.textservice.ISpellCheckerSession;
|
||||
import com.android.internal.textservice.ISpellCheckerSessionListener;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* IPC channels from SpellCheckerService to TextServicesManagerService.
|
||||
* @hide
|
||||
*/
|
||||
oneway interface ISpellCheckerServiceCallback {
|
||||
// TODO: Currently SpellCheckerSession just ignores null newSession and continues waiting for
|
||||
// the next onSessionCreated with non-null newSession, which is supposed to never happen if
|
||||
// the system is working normally. We should at least free up resources in SpellCheckerSession.
|
||||
// Note: This method is called from non-system processes, in theory we cannot assume that
|
||||
// this method is always be called only once with non-null value.
|
||||
void onSessionCreated(ISpellCheckerSession newSession);
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import com.android.internal.textservice.ISpellCheckerSession;
|
||||
import android.view.textservice.SpellCheckerInfo;
|
||||
|
||||
/**
|
||||
* Interface to the text service session.
|
||||
* (Per-session) IPC channels from TextServicesManagerService to spell checker client applications.
|
||||
* @hide
|
||||
*/
|
||||
interface ITextServicesSessionListener {
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
import com.android.internal.inputmethod.InputMethodUtils;
|
||||
import com.android.internal.textservice.ISpellCheckerService;
|
||||
import com.android.internal.textservice.ISpellCheckerServiceCallback;
|
||||
import com.android.internal.textservice.ISpellCheckerSession;
|
||||
import com.android.internal.textservice.ISpellCheckerSessionListener;
|
||||
import com.android.internal.textservice.ITextServicesManager;
|
||||
@@ -68,7 +69,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
private static final String TAG = TextServicesManagerService.class.getSimpleName();
|
||||
@@ -549,56 +549,26 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
return;
|
||||
}
|
||||
final SpellCheckerInfo sci = mSpellCheckerMap.get(sciId);
|
||||
SpellCheckerBindGroup bindGroup = mSpellCheckerBindGroups.get(sciId);
|
||||
final int uid = Binder.getCallingUid();
|
||||
if (mSpellCheckerBindGroups.containsKey(sciId)) {
|
||||
final SpellCheckerBindGroup bindGroup = mSpellCheckerBindGroups.get(sciId);
|
||||
if (bindGroup != null) {
|
||||
final InternalDeathRecipient recipient =
|
||||
mSpellCheckerBindGroups.get(sciId).addListener(
|
||||
tsListener, locale, scListener, uid, bundle);
|
||||
if (recipient == null) {
|
||||
if (DBG) {
|
||||
Slog.w(TAG, "Didn't create a death recipient.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (bindGroup.mSpellChecker == null & bindGroup.mConnected) {
|
||||
Slog.e(TAG, "The state of the spell checker bind group is illegal.");
|
||||
bindGroup.removeAll();
|
||||
} else if (bindGroup.mSpellChecker != null) {
|
||||
if (DBG) {
|
||||
Slog.w(TAG, "Existing bind found. Return a spell checker session now. "
|
||||
+ "Listeners count = " + bindGroup.mListeners.size());
|
||||
}
|
||||
try {
|
||||
final ISpellCheckerSession session =
|
||||
bindGroup.mSpellChecker.getISpellCheckerSession(
|
||||
recipient.mScLocale, recipient.mScListener, bundle);
|
||||
if (session != null) {
|
||||
tsListener.onServiceConnected(session);
|
||||
return;
|
||||
} else {
|
||||
if (DBG) {
|
||||
Slog.w(TAG, "Existing bind already expired. ");
|
||||
}
|
||||
bindGroup.removeAll();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Exception in getting spell checker session: " + e);
|
||||
bindGroup.removeAll();
|
||||
}
|
||||
}
|
||||
if (bindGroup == null) {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
bindGroup = startSpellCheckerServiceInnerLocked(sci);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
if (bindGroup == null) {
|
||||
// startSpellCheckerServiceInnerLocked failed.
|
||||
return;
|
||||
}
|
||||
}
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
startSpellCheckerServiceInnerLocked(
|
||||
sci, locale, tsListener, scListener, uid, bundle);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
|
||||
// Start getISpellCheckerSession async IPC, or just queue the request until the spell
|
||||
// checker service is bound.
|
||||
bindGroup.getISpellCheckerSessionOrQueueLocked(
|
||||
new SessionRequest(uid, locale, tsListener, scListener, bundle));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -611,9 +581,8 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private void startSpellCheckerServiceInnerLocked(SpellCheckerInfo info, String locale,
|
||||
ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener,
|
||||
int uid, Bundle bundle) {
|
||||
@Nullable
|
||||
private SpellCheckerBindGroup startSpellCheckerServiceInnerLocked(SpellCheckerInfo info) {
|
||||
if (DBG) {
|
||||
Slog.w(TAG, "Start spell checker session inner locked.");
|
||||
}
|
||||
@@ -627,11 +596,11 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
if (!bindCurrentSpellCheckerService(serviceIntent, connection,
|
||||
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)) {
|
||||
Slog.e(TAG, "Failed to get a spell checker service.");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
final SpellCheckerBindGroup group = new SpellCheckerBindGroup(
|
||||
connection, tsListener, locale, scListener, uid, bundle);
|
||||
final SpellCheckerBindGroup group = new SpellCheckerBindGroup(connection);
|
||||
mSpellCheckerBindGroups.put(sciId, group);
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -814,16 +783,32 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
pw.println(" " + ent.getKey() + " " + grp + ":");
|
||||
pw.println(" " + "mInternalConnection=" + grp.mInternalConnection);
|
||||
pw.println(" " + "mSpellChecker=" + grp.mSpellChecker);
|
||||
pw.println(" " + "mBound=" + grp.mBound + " mConnected=" + grp.mConnected);
|
||||
pw.println(" " + "mUnbindCalled=" + grp.mUnbindCalled);
|
||||
pw.println(" " + "mConnected=" + grp.mConnected);
|
||||
final int numPendingSessionRequests = grp.mPendingSessionRequests.size();
|
||||
for (int i = 0; i < numPendingSessionRequests; i++) {
|
||||
final SessionRequest req = grp.mPendingSessionRequests.get(i);
|
||||
pw.println(" " + "Pending Request #" + i + ":");
|
||||
pw.println(" " + "mTsListener=" + req.mTsListener);
|
||||
pw.println(" " + "mScListener=" + req.mScListener);
|
||||
pw.println(" " + "mScLocale=" + req.mLocale + " mUid=" + req.mUserId);
|
||||
}
|
||||
final int numOnGoingSessionRequests = grp.mOnGoingSessionRequests.size();
|
||||
for (int i = 0; i < numOnGoingSessionRequests; i++) {
|
||||
final SessionRequest req = grp.mOnGoingSessionRequests.get(i);
|
||||
pw.println(" " + "On going Request #" + i + ":");
|
||||
++i;
|
||||
pw.println(" " + "mTsListener=" + req.mTsListener);
|
||||
pw.println(" " + "mScListener=" + req.mScListener);
|
||||
pw.println(
|
||||
" " + "mScLocale=" + req.mLocale + " mUid=" + req.mUserId);
|
||||
}
|
||||
final int N = grp.mListeners.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final InternalDeathRecipient listener = grp.mListeners.get(i);
|
||||
pw.println(" " + "Listener #" + i + ":");
|
||||
pw.println(" " + "mTsListener=" + listener.mTsListener);
|
||||
pw.println(" " + "mScListener=" + listener.mScListener);
|
||||
pw.println(" " + "mGroup=" + listener.mGroup);
|
||||
pw.println(" " + "mScLocale=" + listener.mScLocale
|
||||
+ " mUid=" + listener.mUid);
|
||||
}
|
||||
}
|
||||
pw.println("");
|
||||
@@ -832,25 +817,44 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SessionRequest {
|
||||
@UserIdInt
|
||||
public final int mUserId;
|
||||
@Nullable
|
||||
public final String mLocale;
|
||||
@NonNull
|
||||
public final ITextServicesSessionListener mTsListener;
|
||||
@NonNull
|
||||
public final ISpellCheckerSessionListener mScListener;
|
||||
@Nullable
|
||||
public final Bundle mBundle;
|
||||
|
||||
SessionRequest(@UserIdInt final int userId, @Nullable String locale,
|
||||
@NonNull ITextServicesSessionListener tsListener,
|
||||
@NonNull ISpellCheckerSessionListener scListener, @Nullable Bundle bundle) {
|
||||
mUserId = userId;
|
||||
mLocale = locale;
|
||||
mTsListener = tsListener;
|
||||
mScListener = scListener;
|
||||
mBundle = bundle;
|
||||
}
|
||||
}
|
||||
|
||||
// SpellCheckerBindGroup contains active text service session listeners.
|
||||
// If there are no listeners anymore, the SpellCheckerBindGroup instance will be removed from
|
||||
// mSpellCheckerBindGroups
|
||||
private final class SpellCheckerBindGroup {
|
||||
private final String TAG = SpellCheckerBindGroup.class.getSimpleName();
|
||||
private final InternalServiceConnection mInternalConnection;
|
||||
private final CopyOnWriteArrayList<InternalDeathRecipient> mListeners =
|
||||
new CopyOnWriteArrayList<>();
|
||||
public boolean mBound;
|
||||
public ISpellCheckerService mSpellChecker;
|
||||
public boolean mConnected;
|
||||
private final ArrayList<InternalDeathRecipient> mListeners = new ArrayList<>();
|
||||
private boolean mUnbindCalled;
|
||||
private ISpellCheckerService mSpellChecker;
|
||||
private boolean mConnected;
|
||||
private final ArrayList<SessionRequest> mPendingSessionRequests = new ArrayList<>();
|
||||
private final ArrayList<SessionRequest> mOnGoingSessionRequests = new ArrayList<>();
|
||||
|
||||
public SpellCheckerBindGroup(InternalServiceConnection connection,
|
||||
ITextServicesSessionListener listener, String locale,
|
||||
ISpellCheckerSessionListener scListener, int uid, Bundle bundle) {
|
||||
public SpellCheckerBindGroup(InternalServiceConnection connection) {
|
||||
mInternalConnection = connection;
|
||||
mBound = true;
|
||||
mConnected = false;
|
||||
addListener(listener, locale, scListener, uid, bundle);
|
||||
}
|
||||
|
||||
public void onServiceConnected(ISpellCheckerService spellChecker) {
|
||||
@@ -858,55 +862,15 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
Slog.d(TAG, "onServiceConnected");
|
||||
}
|
||||
|
||||
for (InternalDeathRecipient listener : mListeners) {
|
||||
try {
|
||||
final ISpellCheckerSession session = spellChecker.getISpellCheckerSession(
|
||||
listener.mScLocale, listener.mScListener, listener.mBundle);
|
||||
synchronized(mSpellCheckerMap) {
|
||||
if (mListeners.contains(listener)) {
|
||||
listener.mTsListener.onServiceConnected(session);
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Exception in getting the spell checker session."
|
||||
+ "Reconnect to the spellchecker. ", e);
|
||||
removeAll();
|
||||
return;
|
||||
}
|
||||
}
|
||||
synchronized(mSpellCheckerMap) {
|
||||
mSpellChecker = spellChecker;
|
||||
mConnected = true;
|
||||
// Dispatch pending getISpellCheckerSession requests.
|
||||
mPendingSessionRequests.forEach(this::getISpellCheckerSessionLocked);
|
||||
mPendingSessionRequests.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public InternalDeathRecipient addListener(ITextServicesSessionListener tsListener,
|
||||
String locale, ISpellCheckerSessionListener scListener, int uid, Bundle bundle) {
|
||||
if (DBG) {
|
||||
Slog.d(TAG, "addListener: " + locale);
|
||||
}
|
||||
InternalDeathRecipient recipient = null;
|
||||
synchronized(mSpellCheckerMap) {
|
||||
try {
|
||||
final int size = mListeners.size();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (mListeners.get(i).hasSpellCheckerListener(scListener)) {
|
||||
// do not add the lister if the group already contains this.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
recipient = new InternalDeathRecipient(
|
||||
this, tsListener, locale, scListener, uid, bundle);
|
||||
scListener.asBinder().linkToDeath(recipient, 0);
|
||||
mListeners.add(recipient);
|
||||
} catch(RemoteException e) {
|
||||
// do nothing
|
||||
}
|
||||
cleanLocked();
|
||||
}
|
||||
return recipient;
|
||||
}
|
||||
|
||||
public void removeListener(ISpellCheckerSessionListener listener) {
|
||||
if (DBG) {
|
||||
Slog.w(TAG, "remove listener: " + listener.hashCode());
|
||||
@@ -941,20 +905,29 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
if (DBG) {
|
||||
Slog.d(TAG, "cleanLocked");
|
||||
}
|
||||
// If there are no more active listeners, clean up. Only do this
|
||||
// once.
|
||||
if (mBound && mListeners.isEmpty()) {
|
||||
mBound = false;
|
||||
final String sciId = mInternalConnection.mSciId;
|
||||
SpellCheckerBindGroup cur = mSpellCheckerBindGroups.get(sciId);
|
||||
if (cur == this) {
|
||||
if (DBG) {
|
||||
Slog.d(TAG, "Remove bind group.");
|
||||
}
|
||||
mSpellCheckerBindGroups.remove(sciId);
|
||||
}
|
||||
mContext.unbindService(mInternalConnection);
|
||||
if (mUnbindCalled) {
|
||||
return;
|
||||
}
|
||||
// If there are no more active listeners, clean up. Only do this once.
|
||||
if (!mListeners.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!mPendingSessionRequests.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!mOnGoingSessionRequests.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
final String sciId = mInternalConnection.mSciId;
|
||||
final SpellCheckerBindGroup cur = mSpellCheckerBindGroups.get(sciId);
|
||||
if (cur == this) {
|
||||
if (DBG) {
|
||||
Slog.d(TAG, "Remove bind group.");
|
||||
}
|
||||
mSpellCheckerBindGroups.remove(sciId);
|
||||
}
|
||||
mContext.unbindService(mInternalConnection);
|
||||
mUnbindCalled = true;
|
||||
}
|
||||
|
||||
public void removeAll() {
|
||||
@@ -966,6 +939,59 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
idr.mScListener.asBinder().unlinkToDeath(idr, 0);
|
||||
}
|
||||
mListeners.clear();
|
||||
mPendingSessionRequests.clear();
|
||||
mOnGoingSessionRequests.clear();
|
||||
cleanLocked();
|
||||
}
|
||||
}
|
||||
|
||||
public void getISpellCheckerSessionOrQueueLocked(@NonNull SessionRequest request) {
|
||||
if (mUnbindCalled) {
|
||||
return;
|
||||
}
|
||||
if (!mConnected) {
|
||||
mPendingSessionRequests.add(request);
|
||||
return;
|
||||
}
|
||||
getISpellCheckerSessionLocked(request);
|
||||
}
|
||||
|
||||
private void getISpellCheckerSessionLocked(@NonNull SessionRequest request) {
|
||||
if (mUnbindCalled) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mSpellChecker.getISpellCheckerSession(
|
||||
request.mLocale, request.mScListener, request.mBundle,
|
||||
new ISpellCheckerServiceCallbackBinder(this, request));
|
||||
mOnGoingSessionRequests.add(request);
|
||||
} catch(RemoteException e) {
|
||||
// The target spell checker service is not available. Better to reset the state.
|
||||
removeAll();
|
||||
}
|
||||
cleanLocked();
|
||||
}
|
||||
|
||||
void onSessionCreated(@Nullable final ISpellCheckerSession newSession,
|
||||
@NonNull final SessionRequest request) {
|
||||
synchronized (mSpellCheckerMap) {
|
||||
if (mUnbindCalled) {
|
||||
return;
|
||||
}
|
||||
if (mOnGoingSessionRequests.remove(request)) {
|
||||
final InternalDeathRecipient recipient =
|
||||
new InternalDeathRecipient(this, request.mScListener);
|
||||
try {
|
||||
request.mTsListener.onServiceConnected(newSession);
|
||||
request.mScListener.asBinder().linkToDeath(recipient, 0);
|
||||
mListeners.add(recipient);
|
||||
} catch (RemoteException e) {
|
||||
// Technically this can happen if the spell checker client app is already
|
||||
// dead. We can just forget about this request; the request is already
|
||||
// removed from mOnGoingSessionRequests and the death recipient listener is
|
||||
// not yet added to mListeners. There is nothing to release further.
|
||||
}
|
||||
}
|
||||
cleanLocked();
|
||||
}
|
||||
}
|
||||
@@ -1008,21 +1034,13 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
}
|
||||
|
||||
private static final class InternalDeathRecipient implements IBinder.DeathRecipient {
|
||||
public final ITextServicesSessionListener mTsListener;
|
||||
public final ISpellCheckerSessionListener mScListener;
|
||||
public final String mScLocale;
|
||||
private final SpellCheckerBindGroup mGroup;
|
||||
public final int mUid;
|
||||
public final Bundle mBundle;
|
||||
|
||||
public InternalDeathRecipient(SpellCheckerBindGroup group,
|
||||
ITextServicesSessionListener tsListener, String scLocale,
|
||||
ISpellCheckerSessionListener scListener, int uid, Bundle bundle) {
|
||||
mTsListener = tsListener;
|
||||
ISpellCheckerSessionListener scListener) {
|
||||
mScListener = scListener;
|
||||
mScLocale = scLocale;
|
||||
mGroup = group;
|
||||
mUid = uid;
|
||||
mBundle = bundle;
|
||||
}
|
||||
|
||||
public boolean hasSpellCheckerListener(ISpellCheckerSessionListener listener) {
|
||||
@@ -1035,6 +1053,25 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ISpellCheckerServiceCallbackBinder
|
||||
extends ISpellCheckerServiceCallback.Stub {
|
||||
@NonNull
|
||||
private final SpellCheckerBindGroup mBindGroup;
|
||||
@NonNull
|
||||
private final SessionRequest mRequest;
|
||||
|
||||
ISpellCheckerServiceCallbackBinder(@NonNull final SpellCheckerBindGroup bindGroup,
|
||||
@NonNull final SessionRequest request) {
|
||||
mBindGroup = bindGroup;
|
||||
mRequest = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionCreated(@Nullable ISpellCheckerSession newSession) {
|
||||
mBindGroup.onSessionCreated(newSession, mRequest);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TextServicesSettings {
|
||||
private final ContentResolver mResolver;
|
||||
@UserIdInt
|
||||
|
||||
Reference in New Issue
Block a user