Merge "Address API council feedback" into qt-dev

This commit is contained in:
Svetoslav Ganov
2019-04-29 16:59:13 +00:00
committed by Android (Google) Code Review
9 changed files with 225 additions and 107 deletions

View File

@@ -3763,7 +3763,7 @@ package android.app {
method public void onDetachedFromWindow();
method public void onEnterAnimationComplete();
method public boolean onGenericMotionEvent(android.view.MotionEvent);
method @NonNull public java.util.List<android.app.DirectAction> onGetDirectActions();
method public void onGetDirectActions(@NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<java.util.List<android.app.DirectAction>>);
method public boolean onKeyDown(int, android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
@@ -3783,7 +3783,7 @@ package android.app {
method public void onOptionsMenuClosed(android.view.Menu);
method public void onPanelClosed(int, @NonNull android.view.Menu);
method @CallSuper protected void onPause();
method public void onPerformDirectAction(@NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.os.Bundle>);
method public void onPerformDirectAction(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.os.Bundle>);
method public void onPictureInPictureModeChanged(boolean, android.content.res.Configuration);
method @Deprecated public void onPictureInPictureModeChanged(boolean);
method @CallSuper protected void onPostCreate(@Nullable android.os.Bundle);
@@ -41833,7 +41833,7 @@ package android.service.voice {
method public void onTaskStarted(android.content.Intent, int);
method public void onTrimMemory(int);
method public final void performDirectAction(@NonNull android.app.DirectAction, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.os.Bundle>);
method public final void requestDirectActions(@NonNull android.service.voice.VoiceInteractionSession.ActivityId, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.DirectAction>>);
method public final void requestDirectActions(@NonNull android.service.voice.VoiceInteractionSession.ActivityId, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.DirectAction>>);
method public void setContentView(android.view.View);
method public void setDisabledShowContext(int);
method public void setKeepAwake(boolean);

View File

@@ -2340,12 +2340,20 @@ public class Activity extends ContextThemeWrapper
* <p>This method will be called only after {@link #onStart()} is being called and
* before {@link #onStop()} is being called.
*
* @return The currently supported direct actions which cannot be <code>null</code>
* or contain <code>null</null> elements.
* <p>You should pass to the callback the currently supported direct actions which
* cannot be <code>null</code> or contain <code>null</null> elements.
*
* <p>You should return the action list as soon as possible to ensure the consumer,
* for example the assistant, is as responsive as possible which would improve user
* experience of your app.
*
* @param cancellationSignal A signal to cancel the operation in progress.
* @param callback The callback to send the action list. The actions list cannot
* contain <code>null</code> elements.
*/
@NonNull
public List<DirectAction> onGetDirectActions() {
return Collections.emptyList();
public void onGetDirectActions(@NonNull CancellationSignal cancellationSignal,
@NonNull Consumer<List<DirectAction>> callback) {
callback.accept(Collections.emptyList());
}
/**
@@ -2354,14 +2362,13 @@ public class Activity extends ContextThemeWrapper
*
* @param actionId The ID for the action
* @param arguments Any additional arguments provided by the caller
* @param cancellationSignal A signal to cancel the operation in progress, or {@code null}
* if none.
* @param cancellationSignal A signal to cancel the operation in progress.
* @param resultListener The callback to provide the result back to the caller
*
* @see #onGetDirectActions()
* @see #onGetDirectActions(CancellationSignal, Consumer)
*/
public void onPerformDirectAction(@NonNull String actionId,
@Nullable Bundle arguments, @Nullable CancellationSignal cancellationSignal,
@NonNull Bundle arguments, @NonNull CancellationSignal cancellationSignal,
@NonNull Consumer<Bundle> resultListener) { }
/**

View File

@@ -392,6 +392,10 @@ public final class ActivityThread extends ClientTransactionHandler {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final ResourcesManager mResourcesManager;
// Registry of remote cancellation transports pending a reply with reply handles.
@GuardedBy("this")
private @Nullable Map<SafeCancellationTransport, CancellationSignal> mRemoteCancellations;
private static final class ProviderKey {
final String authority;
final int userId;
@@ -1657,32 +1661,85 @@ public final class ActivityThread extends ClientTransactionHandler {
@Override
public void requestDirectActions(@NonNull IBinder activityToken,
@NonNull IVoiceInteractor interactor, @NonNull RemoteCallback callback) {
mH.sendMessage(PooledLambda.obtainMessage(ActivityThread::handleRequestDirectActions,
ActivityThread.this, activityToken, interactor, callback));
}
@Override
public void performDirectAction(IBinder activityToken, String actionId, Bundle arguments,
RemoteCallback cancellationCallback, RemoteCallback resultCallback) {
final CancellationSignal cancellationSignal;
@NonNull IVoiceInteractor interactor, @Nullable RemoteCallback cancellationCallback,
@NonNull RemoteCallback callback) {
final CancellationSignal cancellationSignal = new CancellationSignal();
if (cancellationCallback != null) {
final ICancellationSignal transport = CancellationSignal.createTransport();
cancellationSignal = CancellationSignal.fromTransport(transport);
final ICancellationSignal transport = createSafeCancellationTransport(
cancellationSignal);
final Bundle cancellationResult = new Bundle();
cancellationResult.putBinder(VoiceInteractor.KEY_CANCELLATION_SIGNAL,
transport.asBinder());
cancellationCallback.sendResult(cancellationResult);
} else {
cancellationSignal = new CancellationSignal();
}
mH.sendMessage(PooledLambda.obtainMessage(ActivityThread::handleRequestDirectActions,
ActivityThread.this, activityToken, interactor, cancellationSignal, callback));
}
@Override
public void performDirectAction(@NonNull IBinder activityToken, @NonNull String actionId,
@Nullable Bundle arguments, @Nullable RemoteCallback cancellationCallback,
@NonNull RemoteCallback resultCallback) {
final CancellationSignal cancellationSignal = new CancellationSignal();
if (cancellationCallback != null) {
final ICancellationSignal transport = createSafeCancellationTransport(
cancellationSignal);
final Bundle cancellationResult = new Bundle();
cancellationResult.putBinder(VoiceInteractor.KEY_CANCELLATION_SIGNAL,
transport.asBinder());
cancellationCallback.sendResult(cancellationResult);
}
mH.sendMessage(PooledLambda.obtainMessage(ActivityThread::handlePerformDirectAction,
ActivityThread.this, activityToken, actionId, arguments,
cancellationSignal, resultCallback));
}
}
private @NonNull SafeCancellationTransport createSafeCancellationTransport(
@NonNull CancellationSignal cancellationSignal) {
synchronized (ActivityThread.this) {
if (mRemoteCancellations == null) {
mRemoteCancellations = new ArrayMap<>();
}
final SafeCancellationTransport transport = new SafeCancellationTransport(
this, cancellationSignal);
mRemoteCancellations.put(transport, cancellationSignal);
return transport;
}
}
private @NonNull CancellationSignal removeSafeCancellationTransport(
@NonNull SafeCancellationTransport transport) {
synchronized (ActivityThread.this) {
final CancellationSignal cancellation = mRemoteCancellations.remove(transport);
if (mRemoteCancellations.isEmpty()) {
mRemoteCancellations = null;
}
return cancellation;
}
}
private static final class SafeCancellationTransport extends ICancellationSignal.Stub {
private final @NonNull WeakReference<ActivityThread> mWeakActivityThread;
SafeCancellationTransport(@NonNull ActivityThread activityThread,
@NonNull CancellationSignal cancellation) {
mWeakActivityThread = new WeakReference<>(activityThread);
}
@Override
public void cancel() {
final ActivityThread activityThread = mWeakActivityThread.get();
if (activityThread != null) {
final CancellationSignal cancellation = activityThread
.removeSafeCancellationTransport(this);
if (cancellation != null) {
cancellation.cancel();
}
}
}
}
class H extends Handler {
public static final int BIND_APPLICATION = 110;
@UnsupportedAppUsage
@@ -3491,27 +3548,31 @@ public final class ActivityThread extends ClientTransactionHandler {
/** Fetches the user actions for the corresponding activity */
private void handleRequestDirectActions(@NonNull IBinder activityToken,
@NonNull IVoiceInteractor interactor, @NonNull RemoteCallback callback) {
@NonNull IVoiceInteractor interactor, @NonNull CancellationSignal cancellationSignal,
@NonNull RemoteCallback callback) {
final ActivityClientRecord r = mActivities.get(activityToken);
if (r != null) {
final int lifecycleState = r.getLifecycleState();
if (lifecycleState < ON_START || lifecycleState >= ON_STOP) {
callback.sendResult(null);
return;
if (r == null) {
callback.sendResult(null);
return;
}
final int lifecycleState = r.getLifecycleState();
if (lifecycleState < ON_START || lifecycleState >= ON_STOP) {
callback.sendResult(null);
return;
}
if (r.activity.mVoiceInteractor == null
|| r.activity.mVoiceInteractor.mInteractor.asBinder()
!= interactor.asBinder()) {
if (r.activity.mVoiceInteractor != null) {
r.activity.mVoiceInteractor.destroy();
}
if (r.activity.mVoiceInteractor == null
|| r.activity.mVoiceInteractor.mInteractor.asBinder()
!= interactor.asBinder()) {
if (r.activity.mVoiceInteractor != null) {
r.activity.mVoiceInteractor.destroy();
}
r.activity.mVoiceInteractor = new VoiceInteractor(interactor, r.activity,
r.activity, Looper.myLooper());
}
final List<DirectAction> actions = r.activity.onGetDirectActions();
r.activity.mVoiceInteractor = new VoiceInteractor(interactor, r.activity,
r.activity, Looper.myLooper());
}
r.activity.onGetDirectActions(cancellationSignal, (actions) -> {
Preconditions.checkNotNull(actions);
Preconditions.checkCollectionElementsNotNull(actions, "actions");
if (actions != null && !actions.isEmpty()) {
if (!actions.isEmpty()) {
final int actionCount = actions.size();
for (int i = 0; i < actionCount; i++) {
final DirectAction action = actions.get(i);
@@ -3521,9 +3582,10 @@ public final class ActivityThread extends ClientTransactionHandler {
result.putParcelable(DirectAction.KEY_ACTIONS_LIST,
new ParceledListSlice<>(actions));
callback.sendResult(result);
} else {
callback.sendResult(null);
}
}
callback.sendResult(null);
});
}
/** Performs an actions in the corresponding activity */
@@ -3539,16 +3601,11 @@ public final class ActivityThread extends ClientTransactionHandler {
return;
}
final Bundle nonNullArguments = (arguments != null) ? arguments : Bundle.EMPTY;
final WeakReference<RemoteCallback> weakCallback = new WeakReference<>(resultCallback);
r.activity.onPerformDirectAction(actionId, nonNullArguments, cancellationSignal,
(b) -> {
final RemoteCallback strongCallback = weakCallback.get();
if (strongCallback != null) {
strongCallback.sendResult(b);
}
});
resultCallback::sendResult);
} else {
resultCallback.sendResult(null);
}
resultCallback.sendResult(null);
}
public void handleTranslucentConversionComplete(IBinder token, boolean drawComplete) {

View File

@@ -141,7 +141,7 @@ oneway interface IApplicationThread {
void setNetworkBlockSeq(long procStateSeq);
void scheduleTransaction(in ClientTransaction transaction);
void requestDirectActions(IBinder activityToken, IVoiceInteractor intractor,
in RemoteCallback callback);
in RemoteCallback cancellationCallback, in RemoteCallback callback);
void performDirectAction(IBinder activityToken, String actionId,
in Bundle arguments, in RemoteCallback cancellationCallback,
in RemoteCallback resultCallback);

View File

@@ -76,6 +76,7 @@ import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -171,6 +172,9 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
final WeakReference<VoiceInteractionSession> mWeakRef
= new WeakReference<VoiceInteractionSession>(this);
// Registry of remote callbacks pending a reply with reply handles.
final Map<SafeResultListener, Consumer<Bundle>> mRemoteCallbacks = new ArrayMap<>();
ICancellationSignal mKillCallback;
final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
@@ -1105,6 +1109,7 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
} catch (RemoteException e) {
/* ignore */
}
mKillCallback = null;
}
if (mInitialized) {
mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
@@ -1314,8 +1319,6 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
}
}
/**
* <p>Ask that a new assistant activity be started. This will create a new task in the
* in activity manager: this means that
@@ -1349,19 +1352,57 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
*
* @param activityId Ths activity id of the app to get the actions from.
* @param resultExecutor The handler to receive the callback
* @param cancellationSignal A signal to cancel the operation in progress,
* or {@code null} if none.
* @param callback The callback to receive the response
*/
public final void requestDirectActions(@NonNull ActivityId activityId,
@Nullable CancellationSignal cancellationSignal,
@NonNull @CallbackExecutor Executor resultExecutor,
@NonNull Consumer<List<DirectAction>> callback) {
Preconditions.checkNotNull(activityId);
Preconditions.checkNotNull(resultExecutor);
Preconditions.checkNotNull(callback);
if (mToken == null) {
throw new IllegalStateException("Can't call before onCreate()");
}
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
}
final RemoteCallback cancellationCallback = (cancellationSignal != null)
? new RemoteCallback(b -> {
if (b != null) {
final IBinder cancellation = b.getBinder(
VoiceInteractor.KEY_CANCELLATION_SIGNAL);
if (cancellation != null) {
cancellationSignal.setRemote(ICancellationSignal.Stub.asInterface(
cancellation));
}
}
})
: null;
try {
mSystemService.requestDirectActions(mToken, activityId.getTaskId(),
activityId.getAssistToken(), new RemoteCallback(new DirectActionsReceiver(
Preconditions.checkNotNull(resultExecutor),
Preconditions.checkNotNull(callback))));
activityId.getAssistToken(), cancellationCallback,
new RemoteCallback(createSafeResultListener((result) -> {
List<DirectAction> list;
if (result == null) {
list = Collections.emptyList();
} else {
final ParceledListSlice<DirectAction> pls = result.getParcelable(
DirectAction.KEY_ACTIONS_LIST);
if (pls != null) {
final List<DirectAction> receivedList = pls.getList();
list = (receivedList != null) ? receivedList : Collections.emptyList();
} else {
list = Collections.emptyList();
}
}
resultExecutor.execute(() -> callback.accept(list));
})));
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -1390,8 +1431,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
* @param resultExecutor The handler to receive the callback.
* @param resultListener The callback to receive the response.
*
* @see #requestDirectActions(ActivityId, Executor, Consumer)
* @see Activity#onGetDirectActions()
* @see #requestDirectActions(ActivityId, CancellationSignal, Executor, Consumer)
* @see Activity#onGetDirectActions(CancellationSignal, Consumer)
*/
public final void performDirectAction(@NonNull DirectAction action, @Nullable Bundle extras,
@Nullable CancellationSignal cancellationSignal,
@@ -1407,26 +1448,31 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
cancellationSignal.throwIfCanceled();
}
final RemoteCallback remoteCallback = new RemoteCallback(b -> {
if (b != null) {
final IBinder cancellation = b.getBinder(VoiceInteractor.KEY_CANCELLATION_SIGNAL);
if (cancellation != null) {
if (cancellationSignal != null) {
cancellationSignal.setRemote(ICancellationSignal.Stub.asInterface(
cancellation));
final RemoteCallback cancellationCallback = (cancellationSignal != null)
? new RemoteCallback(createSafeResultListener(b -> {
if (b != null) {
final IBinder cancellation = b.getBinder(
VoiceInteractor.KEY_CANCELLATION_SIGNAL);
if (cancellation != null) {
cancellationSignal.setRemote(ICancellationSignal.Stub.asInterface(
cancellation));
}
}
} else {
resultExecutor.execute(() -> resultListener.accept(b));
}
}))
: null;
final RemoteCallback resultCallback = new RemoteCallback(createSafeResultListener(b -> {
if (b != null) {
resultExecutor.execute(() -> resultListener.accept(b));
} else {
resultExecutor.execute(() -> resultListener.accept(Bundle.EMPTY));
}
});
}));
try {
mSystemService.performDirectAction(mToken, action.getId(), extras,
action.getTaskId(), action.getActivityId(), remoteCallback,
remoteCallback);
action.getTaskId(), action.getActivityId(), cancellationCallback,
resultCallback);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -1901,33 +1947,18 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
}
}
private static class DirectActionsReceiver implements RemoteCallback.OnResultListener {
@NonNull
private final Executor mResultExecutor;
private final Consumer<List<DirectAction>> mCallback;
DirectActionsReceiver(Executor resultExecutor, Consumer<List<DirectAction>> callback) {
mResultExecutor = resultExecutor;
mCallback = callback;
private SafeResultListener createSafeResultListener(
@NonNull Consumer<Bundle> consumer) {
synchronized (this) {
final SafeResultListener listener = new SafeResultListener(consumer, this);
mRemoteCallbacks.put(listener, consumer);
return listener;
}
}
@Override
public void onResult(Bundle result) {
final List<DirectAction> list;
if (result == null) {
list = Collections.emptyList();
} else {
final ParceledListSlice<DirectAction> pls = result.getParcelable(
DirectAction.KEY_ACTIONS_LIST);
if (pls != null) {
final List<DirectAction> receivedList = pls.getList();
list = (receivedList != null) ? receivedList : Collections.emptyList();
} else {
list = Collections.emptyList();
}
}
mResultExecutor.execute(() -> mCallback.accept(list));
private Consumer<Bundle> removeSafeResultListener(@NonNull SafeResultListener listener) {
synchronized (this) {
return mRemoteCallbacks.remove(listener);
}
}
@@ -2062,4 +2093,24 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
return result;
}
}
private static class SafeResultListener implements RemoteCallback.OnResultListener {
private final @NonNull WeakReference<VoiceInteractionSession> mWeakSession;
SafeResultListener(@NonNull Consumer<Bundle> action,
@NonNull VoiceInteractionSession session) {
mWeakSession = new WeakReference<>(session);
}
@Override
public void onResult(Bundle result) {
final VoiceInteractionSession session = mWeakSession.get();
if (session != null) {
final Consumer<Bundle> consumer = session.removeSafeResultListener(this);
if (consumer != null) {
consumer.accept(result);
}
}
}
}
}

View File

@@ -163,7 +163,7 @@ interface IVoiceInteractionManagerService {
* Requests a list of supported actions from a specific activity.
*/
void requestDirectActions(in IBinder token, int taskId, IBinder assistToken,
in RemoteCallback callback);
in RemoteCallback cancellationCallback, in RemoteCallback callback);
/**
* Requests performing an action from a specific activity.

View File

@@ -628,8 +628,8 @@ public class TransactionParcelTests {
}
@Override
public void requestDirectActions(IBinder activityToken, IVoiceInteractor intractor,
RemoteCallback callback) {
public void requestDirectActions(IBinder activityToken, IVoiceInteractor interactor,
RemoteCallback cancellationCallback, RemoteCallback resultCallback) {
}
@Override

View File

@@ -714,17 +714,19 @@ public class VoiceInteractionManagerService extends SystemService {
}
@Override
public void requestDirectActions(@NonNull IBinder token, int taskId, IBinder assistToken,
@NonNull RemoteCallback callback) {
public void requestDirectActions(@NonNull IBinder token, int taskId,
@NonNull IBinder assistToken, @Nullable RemoteCallback cancellationCallback,
@NonNull RemoteCallback resultCallback) {
synchronized (this) {
if (mImpl == null) {
Slog.w(TAG, "requestDirectActions without running voice interaction service");
callback.sendResult(null);
resultCallback.sendResult(null);
return;
}
final long caller = Binder.clearCallingIdentity();
try {
mImpl.requestDirectActionsLocked(token, taskId, assistToken, callback);
mImpl.requestDirectActionsLocked(token, taskId, assistToken,
cancellationCallback, resultCallback);
} finally {
Binder.restoreCallingIdentity(caller);
}

View File

@@ -263,7 +263,8 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
}
public void requestDirectActionsLocked(@NonNull IBinder token, int taskId,
IBinder assistToken, @NonNull RemoteCallback callback) {
@NonNull IBinder assistToken, @Nullable RemoteCallback cancellationCallback,
@NonNull RemoteCallback callback) {
if (mActiveSession == null || token != mActiveSession.mToken) {
Slog.w(TAG, "requestDirectActionsLocked does not match active session");
callback.sendResult(null);
@@ -277,7 +278,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
} else {
try {
tokens.getApplicationThread().requestDirectActions(tokens.getActivityToken(),
mActiveSession.mInteractor, callback);
mActiveSession.mInteractor, cancellationCallback, callback);
} catch (RemoteException e) {
Slog.w("Unexpected remote error", e);
callback.sendResult(null);