script-astra/Android/Sdk/sources/android-35/android/service/dreams/DreamOverlayConnectionHandler.java

243 lines
8.3 KiB
Java
Raw Normal View History

2025-01-20 15:15:20 +00:00
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.service.dreams;
import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ObservableServiceConnection;
import com.android.internal.util.PersistentServiceConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
* Handles the service connection to {@link IDreamOverlay}
*
* @hide
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public final class DreamOverlayConnectionHandler {
private static final String TAG = "DreamOverlayConnection";
private static final int MSG_ADD_CONSUMER = 1;
private static final int MSG_REMOVE_CONSUMER = 2;
private static final int MSG_OVERLAY_CLIENT_READY = 3;
private final Handler mHandler;
private final PersistentServiceConnection<IDreamOverlay> mConnection;
// Retrieved Client
private IDreamOverlayClient mClient;
// A list of pending requests to execute on the overlay.
private final List<Consumer<IDreamOverlayClient>> mConsumers = new ArrayList<>();
private final OverlayConnectionCallback mCallback;
DreamOverlayConnectionHandler(
Context context,
Looper looper,
Intent serviceIntent,
int minConnectionDurationMs,
int maxReconnectAttempts,
int baseReconnectDelayMs) {
this(context, looper, serviceIntent, minConnectionDurationMs, maxReconnectAttempts,
baseReconnectDelayMs, new Injector());
}
@VisibleForTesting
public DreamOverlayConnectionHandler(
Context context,
Looper looper,
Intent serviceIntent,
int minConnectionDurationMs,
int maxReconnectAttempts,
int baseReconnectDelayMs,
Injector injector) {
mCallback = new OverlayConnectionCallback();
mHandler = new Handler(looper, new OverlayHandlerCallback());
mConnection = injector.buildConnection(
context,
mHandler,
serviceIntent,
minConnectionDurationMs,
maxReconnectAttempts,
baseReconnectDelayMs
);
}
/**
* Bind to the overlay service. If binding fails, we automatically call unbind to clean
* up resources.
*
* @return true if binding was successful, false otherwise.
*/
public boolean bind() {
mConnection.addCallback(mCallback);
final boolean success = mConnection.bind();
if (!success) {
unbind();
}
return success;
}
/**
* Unbind from the overlay service, clearing any pending callbacks.
*/
public void unbind() {
mConnection.removeCallback(mCallback);
// Remove any pending messages.
mHandler.removeCallbacksAndMessages(null);
mClient = null;
mConsumers.clear();
mConnection.unbind();
}
/**
* Adds a consumer to run once the overlay service has connected. If the overlay service
* disconnects (eg binding dies) and then reconnects, this consumer will be re-run unless
* removed.
*
* @param consumer The consumer to run. This consumer is always executed asynchronously.
*/
public void addConsumer(Consumer<IDreamOverlayClient> consumer) {
final Message msg = mHandler.obtainMessage(MSG_ADD_CONSUMER, consumer);
mHandler.sendMessage(msg);
}
/**
* Removes the consumer, preventing this consumer from being called again.
*
* @param consumer The consumer to remove.
*/
public void removeConsumer(Consumer<IDreamOverlayClient> consumer) {
final Message msg = mHandler.obtainMessage(MSG_REMOVE_CONSUMER, consumer);
mHandler.sendMessage(msg);
// Clear any pending messages to add this consumer
mHandler.removeMessages(MSG_ADD_CONSUMER, consumer);
}
private final class OverlayHandlerCallback implements Handler.Callback {
@Override
public boolean handleMessage(@NonNull Message msg) {
switch (msg.what) {
case MSG_OVERLAY_CLIENT_READY:
onOverlayClientReady((IDreamOverlayClient) msg.obj);
break;
case MSG_ADD_CONSUMER:
onAddConsumer((Consumer<IDreamOverlayClient>) msg.obj);
break;
case MSG_REMOVE_CONSUMER:
onRemoveConsumer((Consumer<IDreamOverlayClient>) msg.obj);
break;
}
return true;
}
}
private void onOverlayClientReady(IDreamOverlayClient client) {
mClient = client;
for (Consumer<IDreamOverlayClient> consumer : mConsumers) {
consumer.accept(mClient);
}
}
private void onAddConsumer(Consumer<IDreamOverlayClient> consumer) {
if (mClient != null) {
consumer.accept(mClient);
}
mConsumers.add(consumer);
}
private void onRemoveConsumer(Consumer<IDreamOverlayClient> consumer) {
mConsumers.remove(consumer);
}
private final class OverlayConnectionCallback implements
ObservableServiceConnection.Callback<IDreamOverlay> {
private final IDreamOverlayClientCallback mClientCallback =
new IDreamOverlayClientCallback.Stub() {
@Override
public void onDreamOverlayClient(IDreamOverlayClient client) {
final Message msg =
mHandler.obtainMessage(MSG_OVERLAY_CLIENT_READY, client);
mHandler.sendMessage(msg);
}
};
@Override
public void onConnected(
ObservableServiceConnection<IDreamOverlay> connection,
IDreamOverlay service) {
try {
service.getClient(mClientCallback);
} catch (RemoteException e) {
Log.e(TAG, "could not get DreamOverlayClient", e);
}
}
@Override
public void onDisconnected(ObservableServiceConnection<IDreamOverlay> connection,
int reason) {
mClient = null;
// Cancel any pending messages about the overlay being ready, since it is no
// longer ready.
mHandler.removeMessages(MSG_OVERLAY_CLIENT_READY);
}
}
/**
* Injector for testing
*/
@VisibleForTesting
public static class Injector {
/**
* Returns milliseconds since boot, not counting time spent in deep sleep. Can be overridden
* in tests with a fake clock.
*/
public PersistentServiceConnection<IDreamOverlay> buildConnection(
Context context,
Handler handler,
Intent serviceIntent,
int minConnectionDurationMs,
int maxReconnectAttempts,
int baseReconnectDelayMs) {
final Executor executor = handler::post;
final int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
return new PersistentServiceConnection<>(
context,
executor,
handler,
IDreamOverlay.Stub::asInterface,
serviceIntent,
flags,
minConnectionDurationMs,
maxReconnectAttempts,
baseReconnectDelayMs
);
}
}
}