Loading core/java/android/service/dreams/DreamService.java +99 −75 Original line number Diff line number Diff line Loading @@ -31,9 +31,9 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; Loading Loading @@ -68,6 +68,8 @@ import android.view.accessibility.AccessibilityEvent; import com.android.internal.R; import com.android.internal.util.DumpUtils; import com.android.internal.util.ObservableServiceConnection; import com.android.internal.util.PersistentServiceConnection; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; Loading @@ -75,7 +77,8 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.concurrent.Executor; import java.util.function.Consumer; /** Loading Loading @@ -225,6 +228,7 @@ public class DreamService extends Service implements Window.Callback { /** * The default value for whether to show complications on the overlay. * * @hide */ public static final boolean DEFAULT_SHOW_COMPLICATIONS = false; Loading @@ -251,77 +255,66 @@ public class DreamService extends Service implements Window.Callback { private DreamServiceWrapper mDreamServiceWrapper; private Runnable mDispatchAfterOnAttachedToWindow; private final OverlayConnection mOverlayConnection; private OverlayConnection mOverlayConnection; private static class OverlayConnection implements ServiceConnection { private static class OverlayConnection extends PersistentServiceConnection<IDreamOverlay> { // Overlay set during onBind. private IDreamOverlay mOverlay; // A Queue of pending requests to execute on the overlay. private final ArrayDeque<Consumer<IDreamOverlay>> mRequests; private boolean mBound; OverlayConnection() { mRequests = new ArrayDeque<>(); } // A list of pending requests to execute on the overlay. private final ArrayList<Consumer<IDreamOverlay>> mConsumers = new ArrayList<>(); public void bind(Context context, @Nullable ComponentName overlayService, ComponentName dreamService) { if (overlayService == null) { return; private final Callback<IDreamOverlay> mCallback = new Callback<IDreamOverlay>() { @Override public void onConnected(ObservableServiceConnection<IDreamOverlay> connection, IDreamOverlay service) { mOverlay = service; for (Consumer<IDreamOverlay> consumer : mConsumers) { consumer.accept(mOverlay); } final ServiceInfo serviceInfo = fetchServiceInfo(context, dreamService); final Intent overlayIntent = new Intent(); overlayIntent.setComponent(overlayService); overlayIntent.putExtra(EXTRA_SHOW_COMPLICATIONS, fetchShouldShowComplications(context, serviceInfo)); overlayIntent.putExtra(EXTRA_DREAM_COMPONENT, dreamService); context.bindService(overlayIntent, this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE); mBound = true; } public void unbind(Context context) { if (!mBound) { return; @Override public void onDisconnected(ObservableServiceConnection<IDreamOverlay> connection, int reason) { mOverlay = null; } }; context.unbindService(this); mBound = false; OverlayConnection(Context context, Executor executor, Handler handler, ServiceTransformer<IDreamOverlay> transformer, Intent serviceIntent, int flags, int minConnectionDurationMs, int maxReconnectAttempts, int baseReconnectDelayMs) { super(context, executor, handler, transformer, serviceIntent, flags, minConnectionDurationMs, maxReconnectAttempts, baseReconnectDelayMs); } public void request(Consumer<IDreamOverlay> request) { mRequests.push(request); evaluate(); @Override public boolean bind() { addCallback(mCallback); return super.bind(); } private void evaluate() { if (mOverlay == null) { return; @Override public void unbind() { removeCallback(mCallback); super.unbind(); } // Any new requests that arrive during this loop will be processed synchronously after // the loop exits. while (!mRequests.isEmpty()) { final Consumer<IDreamOverlay> request = mRequests.pop(); request.accept(mOverlay); public void addConsumer(Consumer<IDreamOverlay> consumer) { mConsumers.add(consumer); if (mOverlay != null) { consumer.accept(mOverlay); } } @Override public void onServiceConnected(ComponentName name, IBinder service) { // Store Overlay and execute pending requests. mOverlay = IDreamOverlay.Stub.asInterface(service); evaluate(); } @Override public void onServiceDisconnected(ComponentName name) { // Clear Overlay binder to prevent further request processing. mOverlay = null; public void removeConsumer(Consumer<IDreamOverlay> consumer) { mConsumers.remove(consumer); } } Loading @@ -336,7 +329,6 @@ public class DreamService extends Service implements Window.Callback { public DreamService() { mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); mOverlayConnection = new OverlayConnection(); } /** Loading Loading @@ -996,13 +988,33 @@ public class DreamService extends Service implements Window.Callback { public final IBinder onBind(Intent intent) { if (mDebug) Slog.v(mTag, "onBind() intent = " + intent); mDreamServiceWrapper = new DreamServiceWrapper(); final ComponentName overlayComponent = intent.getParcelableExtra( EXTRA_DREAM_OVERLAY_COMPONENT, ComponentName.class); // Connect to the overlay service if present. if (!mWindowless) { mOverlayConnection.bind( if (!mWindowless && overlayComponent != null) { final Resources resources = getResources(); final ComponentName dreamService = new ComponentName(this, getClass()); final ServiceInfo serviceInfo = fetchServiceInfo(this, dreamService); final Intent overlayIntent = new Intent() .setComponent(overlayComponent) .putExtra(EXTRA_SHOW_COMPLICATIONS, fetchShouldShowComplications(this, serviceInfo)) .putExtra(EXTRA_DREAM_COMPONENT, dreamService); mOverlayConnection = new OverlayConnection( /* context= */ this, intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT), new ComponentName(this, getClass())); getMainExecutor(), mHandler, IDreamOverlay.Stub::asInterface, overlayIntent, /* flags= */ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, resources.getInteger(R.integer.config_minDreamOverlayDurationMs), resources.getInteger(R.integer.config_dreamOverlayMaxReconnectAttempts), resources.getInteger(R.integer.config_dreamOverlayReconnectTimeoutMs)); mOverlayConnection.bind(); } return mDreamServiceWrapper; Loading @@ -1011,7 +1023,9 @@ public class DreamService extends Service implements Window.Callback { @Override public boolean onUnbind(Intent intent) { // We must unbind from any overlay connection if we are unbound before finishing. mOverlayConnection.unbind(this); if (mOverlayConnection != null) { mOverlayConnection.unbind(); } return super.onUnbind(intent); } Loading Loading @@ -1040,7 +1054,9 @@ public class DreamService extends Service implements Window.Callback { } mFinished = true; mOverlayConnection.unbind(this); if (mOverlayConnection != null) { mOverlayConnection.unbind(); } if (mDreamToken == null) { Slog.w(mTag, "Finish was called before the dream was attached."); Loading Loading @@ -1337,19 +1353,24 @@ public class DreamService extends Service implements Window.Callback { mWindow.getDecorView().addOnAttachStateChangeListener( new View.OnAttachStateChangeListener() { private Consumer<IDreamOverlay> mDreamStartOverlayConsumer; @Override public void onViewAttachedToWindow(View v) { mDispatchAfterOnAttachedToWindow.run(); // Request the DreamOverlay be told to dream with dream's window parameters // once the window has been attached. mOverlayConnection.request(overlay -> { if (mOverlayConnection != null) { // Request the DreamOverlay be told to dream with dream's window // parameters once the window has been attached. mDreamStartOverlayConsumer = overlay -> { try { overlay.startDream(mWindow.getAttributes(), mOverlayCallback); } catch (RemoteException e) { Log.e(mTag, "could not send window attributes:" + e); } }); }; mOverlayConnection.addConsumer(mDreamStartOverlayConsumer); } } @Override Loading @@ -1362,6 +1383,9 @@ public class DreamService extends Service implements Window.Callback { mActivity = null; finish(); } if (mOverlayConnection != null && mDreamStartOverlayConsumer != null) { mOverlayConnection.removeConsumer(mDreamStartOverlayConsumer); } } }); } Loading core/java/com/android/internal/util/ObservableServiceConnection.java 0 → 100644 +258 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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.util; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.CallbackRegistry.NotifierCallback; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.Executor; /** * {@link ObservableServiceConnection} is a concrete implementation of {@link ServiceConnection} * that enables monitoring the status of a binder connection. It also aides in automatically * converting a proxy into an internal wrapper type. * * @param <T> The type of the wrapper over the resulting service. */ public class ObservableServiceConnection<T> implements ServiceConnection { /** * An interface for converting the service proxy into a given internal wrapper type. * * @param <T> The type of the wrapper over the resulting service. */ public interface ServiceTransformer<T> { /** * Called to convert the service proxy to the wrapper type. * * @param service The service proxy to create the wrapper type from. * @return The wrapper type. */ T convert(IBinder service); } /** * An interface for listening to the connection status. * * @param <T> The wrapper type. */ public interface Callback<T> { /** * Invoked when the service has been successfully connected to. * * @param connection The {@link ObservableServiceConnection} instance that is now connected * @param service The service proxy converted into the typed wrapper. */ void onConnected(ObservableServiceConnection<T> connection, T service); /** * Invoked when the service has been disconnected. * * @param connection The {@link ObservableServiceConnection} that is now disconnected. * @param reason The reason for the disconnection. */ void onDisconnected(ObservableServiceConnection<T> connection, @DisconnectReason int reason); } /** * Default state, service has not yet disconnected. */ public static final int DISCONNECT_REASON_NONE = 0; /** * Disconnection was due to the resulting binding being {@code null}. */ public static final int DISCONNECT_REASON_NULL_BINDING = 1; /** * Disconnection was due to the remote end disconnecting. */ public static final int DISCONNECT_REASON_DISCONNECTED = 2; /** * Disconnection due to the binder dying. */ public static final int DISCONNECT_REASON_BINDING_DIED = 3; /** * Disconnection from an explicit unbinding. */ public static final int DISCONNECT_REASON_UNBIND = 4; @Retention(RetentionPolicy.SOURCE) @IntDef({ DISCONNECT_REASON_NONE, DISCONNECT_REASON_NULL_BINDING, DISCONNECT_REASON_DISCONNECTED, DISCONNECT_REASON_BINDING_DIED, DISCONNECT_REASON_UNBIND }) public @interface DisconnectReason { } private final Object mLock = new Object(); private final Context mContext; private final Executor mExecutor; private final ServiceTransformer<T> mTransformer; private final Intent mServiceIntent; private final int mFlags; @GuardedBy("mLock") private T mService; @GuardedBy("mLock") private boolean mBoundCalled = false; @GuardedBy("mLock") private int mLastDisconnectReason = DISCONNECT_REASON_NONE; private final CallbackRegistry<Callback<T>, ObservableServiceConnection<T>, T> mCallbackRegistry = new CallbackRegistry<>( new NotifierCallback<Callback<T>, ObservableServiceConnection<T>, T>() { @Override public void onNotifyCallback(Callback<T> callback, ObservableServiceConnection<T> sender, int disconnectReason, T service) { mExecutor.execute(() -> { synchronized (mLock) { if (service != null) { callback.onConnected(sender, service); } else if (mLastDisconnectReason != DISCONNECT_REASON_NONE) { callback.onDisconnected(sender, disconnectReason); } } }); } }); /** * Default constructor for {@link ObservableServiceConnection}. * * @param context The context from which the service will be bound with. * @param executor The executor for connection callbacks to be delivered on * @param transformer A {@link ObservableServiceConnection.ServiceTransformer} for transforming * the resulting service into a desired type. */ public ObservableServiceConnection(@NonNull Context context, @NonNull @CallbackExecutor Executor executor, @NonNull ServiceTransformer<T> transformer, Intent serviceIntent, int flags) { mContext = context; mExecutor = executor; mTransformer = transformer; mServiceIntent = serviceIntent; mFlags = flags; } /** * Initiate binding to the service. * * @return {@code true} if initiating binding succeed, {@code false} if the binding failed or * if this service is already bound. Regardless of the return value, you should later call * {@link #unbind()} to release the connection. */ public boolean bind() { synchronized (mLock) { if (mBoundCalled) { return false; } final boolean bindResult = mContext.bindService(mServiceIntent, mFlags, mExecutor, this); mBoundCalled = true; return bindResult; } } /** * Disconnect from the service if bound. */ public void unbind() { onDisconnected(DISCONNECT_REASON_UNBIND); } /** * Adds a callback for receiving connection updates. * * @param callback The {@link Callback} to receive future updates. */ public void addCallback(Callback<T> callback) { mCallbackRegistry.add(callback); mExecutor.execute(() -> { synchronized (mLock) { if (mService != null) { callback.onConnected(this, mService); } else if (mLastDisconnectReason != DISCONNECT_REASON_NONE) { callback.onDisconnected(this, mLastDisconnectReason); } } }); } /** * Removes previously added callback from receiving future connection updates. * * @param callback The {@link Callback} to be removed. */ public void removeCallback(Callback<T> callback) { synchronized (mLock) { mCallbackRegistry.remove(callback); } } private void onDisconnected(@DisconnectReason int reason) { synchronized (mLock) { if (!mBoundCalled) { return; } mBoundCalled = false; mLastDisconnectReason = reason; mContext.unbindService(this); mService = null; mCallbackRegistry.notifyCallbacks(this, reason, null); } } @Override public final void onServiceConnected(ComponentName name, IBinder service) { synchronized (mLock) { mService = mTransformer.convert(service); mLastDisconnectReason = DISCONNECT_REASON_NONE; mCallbackRegistry.notifyCallbacks(this, mLastDisconnectReason, mService); } } @Override public final void onServiceDisconnected(ComponentName name) { onDisconnected(DISCONNECT_REASON_DISCONNECTED); } @Override public final void onBindingDied(ComponentName name) { onDisconnected(DISCONNECT_REASON_BINDING_DIED); } @Override public final void onNullBinding(ComponentName name) { onDisconnected(DISCONNECT_REASON_NULL_BINDING); } } core/java/com/android/internal/util/PersistentServiceConnection.java 0 → 100644 +200 −0 File added.Preview size limit exceeded, changes collapsed. Show changes core/res/res/values/config.xml +8 −0 Original line number Diff line number Diff line Loading @@ -553,6 +553,14 @@ <!-- If this is true, then keep dreaming when undocking. --> <bool name="config_keepDreamingWhenUndocking">false</bool> <!-- The timeout (in ms) to wait before attempting to reconnect to the dream overlay service if it becomes disconnected --> <integer name="config_dreamOverlayReconnectTimeoutMs">1000</integer> <!-- 1 second --> <!-- The maximum number of times to attempt reconnecting to the dream overlay service --> <integer name="config_dreamOverlayMaxReconnectAttempts">3</integer> <!-- The duration after which the dream overlay connection should be considered stable --> <integer name="config_minDreamOverlayDurationMs">10000</integer> <!-- 10 seconds --> <!-- Auto-rotation behavior --> <!-- If true, enables auto-rotation features using the accelerometer. Loading core/res/res/values/symbols.xml +3 −0 Original line number Diff line number Diff line Loading @@ -2239,6 +2239,9 @@ <java-symbol type="array" name="config_supportedDreamComplications" /> <java-symbol type="array" name="config_disabledDreamComponents" /> <java-symbol type="bool" name="config_dismissDreamOnActivityStart" /> <java-symbol type="integer" name="config_dreamOverlayReconnectTimeoutMs" /> <java-symbol type="integer" name="config_dreamOverlayMaxReconnectAttempts" /> <java-symbol type="integer" name="config_minDreamOverlayDurationMs" /> <java-symbol type="string" name="config_loggable_dream_prefix" /> <java-symbol type="string" name="config_dozeComponent" /> <java-symbol type="string" name="enable_explore_by_touch_warning_title" /> Loading Loading
core/java/android/service/dreams/DreamService.java +99 −75 Original line number Diff line number Diff line Loading @@ -31,9 +31,9 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; Loading Loading @@ -68,6 +68,8 @@ import android.view.accessibility.AccessibilityEvent; import com.android.internal.R; import com.android.internal.util.DumpUtils; import com.android.internal.util.ObservableServiceConnection; import com.android.internal.util.PersistentServiceConnection; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; Loading @@ -75,7 +77,8 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.concurrent.Executor; import java.util.function.Consumer; /** Loading Loading @@ -225,6 +228,7 @@ public class DreamService extends Service implements Window.Callback { /** * The default value for whether to show complications on the overlay. * * @hide */ public static final boolean DEFAULT_SHOW_COMPLICATIONS = false; Loading @@ -251,77 +255,66 @@ public class DreamService extends Service implements Window.Callback { private DreamServiceWrapper mDreamServiceWrapper; private Runnable mDispatchAfterOnAttachedToWindow; private final OverlayConnection mOverlayConnection; private OverlayConnection mOverlayConnection; private static class OverlayConnection implements ServiceConnection { private static class OverlayConnection extends PersistentServiceConnection<IDreamOverlay> { // Overlay set during onBind. private IDreamOverlay mOverlay; // A Queue of pending requests to execute on the overlay. private final ArrayDeque<Consumer<IDreamOverlay>> mRequests; private boolean mBound; OverlayConnection() { mRequests = new ArrayDeque<>(); } // A list of pending requests to execute on the overlay. private final ArrayList<Consumer<IDreamOverlay>> mConsumers = new ArrayList<>(); public void bind(Context context, @Nullable ComponentName overlayService, ComponentName dreamService) { if (overlayService == null) { return; private final Callback<IDreamOverlay> mCallback = new Callback<IDreamOverlay>() { @Override public void onConnected(ObservableServiceConnection<IDreamOverlay> connection, IDreamOverlay service) { mOverlay = service; for (Consumer<IDreamOverlay> consumer : mConsumers) { consumer.accept(mOverlay); } final ServiceInfo serviceInfo = fetchServiceInfo(context, dreamService); final Intent overlayIntent = new Intent(); overlayIntent.setComponent(overlayService); overlayIntent.putExtra(EXTRA_SHOW_COMPLICATIONS, fetchShouldShowComplications(context, serviceInfo)); overlayIntent.putExtra(EXTRA_DREAM_COMPONENT, dreamService); context.bindService(overlayIntent, this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE); mBound = true; } public void unbind(Context context) { if (!mBound) { return; @Override public void onDisconnected(ObservableServiceConnection<IDreamOverlay> connection, int reason) { mOverlay = null; } }; context.unbindService(this); mBound = false; OverlayConnection(Context context, Executor executor, Handler handler, ServiceTransformer<IDreamOverlay> transformer, Intent serviceIntent, int flags, int minConnectionDurationMs, int maxReconnectAttempts, int baseReconnectDelayMs) { super(context, executor, handler, transformer, serviceIntent, flags, minConnectionDurationMs, maxReconnectAttempts, baseReconnectDelayMs); } public void request(Consumer<IDreamOverlay> request) { mRequests.push(request); evaluate(); @Override public boolean bind() { addCallback(mCallback); return super.bind(); } private void evaluate() { if (mOverlay == null) { return; @Override public void unbind() { removeCallback(mCallback); super.unbind(); } // Any new requests that arrive during this loop will be processed synchronously after // the loop exits. while (!mRequests.isEmpty()) { final Consumer<IDreamOverlay> request = mRequests.pop(); request.accept(mOverlay); public void addConsumer(Consumer<IDreamOverlay> consumer) { mConsumers.add(consumer); if (mOverlay != null) { consumer.accept(mOverlay); } } @Override public void onServiceConnected(ComponentName name, IBinder service) { // Store Overlay and execute pending requests. mOverlay = IDreamOverlay.Stub.asInterface(service); evaluate(); } @Override public void onServiceDisconnected(ComponentName name) { // Clear Overlay binder to prevent further request processing. mOverlay = null; public void removeConsumer(Consumer<IDreamOverlay> consumer) { mConsumers.remove(consumer); } } Loading @@ -336,7 +329,6 @@ public class DreamService extends Service implements Window.Callback { public DreamService() { mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); mOverlayConnection = new OverlayConnection(); } /** Loading Loading @@ -996,13 +988,33 @@ public class DreamService extends Service implements Window.Callback { public final IBinder onBind(Intent intent) { if (mDebug) Slog.v(mTag, "onBind() intent = " + intent); mDreamServiceWrapper = new DreamServiceWrapper(); final ComponentName overlayComponent = intent.getParcelableExtra( EXTRA_DREAM_OVERLAY_COMPONENT, ComponentName.class); // Connect to the overlay service if present. if (!mWindowless) { mOverlayConnection.bind( if (!mWindowless && overlayComponent != null) { final Resources resources = getResources(); final ComponentName dreamService = new ComponentName(this, getClass()); final ServiceInfo serviceInfo = fetchServiceInfo(this, dreamService); final Intent overlayIntent = new Intent() .setComponent(overlayComponent) .putExtra(EXTRA_SHOW_COMPLICATIONS, fetchShouldShowComplications(this, serviceInfo)) .putExtra(EXTRA_DREAM_COMPONENT, dreamService); mOverlayConnection = new OverlayConnection( /* context= */ this, intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT), new ComponentName(this, getClass())); getMainExecutor(), mHandler, IDreamOverlay.Stub::asInterface, overlayIntent, /* flags= */ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, resources.getInteger(R.integer.config_minDreamOverlayDurationMs), resources.getInteger(R.integer.config_dreamOverlayMaxReconnectAttempts), resources.getInteger(R.integer.config_dreamOverlayReconnectTimeoutMs)); mOverlayConnection.bind(); } return mDreamServiceWrapper; Loading @@ -1011,7 +1023,9 @@ public class DreamService extends Service implements Window.Callback { @Override public boolean onUnbind(Intent intent) { // We must unbind from any overlay connection if we are unbound before finishing. mOverlayConnection.unbind(this); if (mOverlayConnection != null) { mOverlayConnection.unbind(); } return super.onUnbind(intent); } Loading Loading @@ -1040,7 +1054,9 @@ public class DreamService extends Service implements Window.Callback { } mFinished = true; mOverlayConnection.unbind(this); if (mOverlayConnection != null) { mOverlayConnection.unbind(); } if (mDreamToken == null) { Slog.w(mTag, "Finish was called before the dream was attached."); Loading Loading @@ -1337,19 +1353,24 @@ public class DreamService extends Service implements Window.Callback { mWindow.getDecorView().addOnAttachStateChangeListener( new View.OnAttachStateChangeListener() { private Consumer<IDreamOverlay> mDreamStartOverlayConsumer; @Override public void onViewAttachedToWindow(View v) { mDispatchAfterOnAttachedToWindow.run(); // Request the DreamOverlay be told to dream with dream's window parameters // once the window has been attached. mOverlayConnection.request(overlay -> { if (mOverlayConnection != null) { // Request the DreamOverlay be told to dream with dream's window // parameters once the window has been attached. mDreamStartOverlayConsumer = overlay -> { try { overlay.startDream(mWindow.getAttributes(), mOverlayCallback); } catch (RemoteException e) { Log.e(mTag, "could not send window attributes:" + e); } }); }; mOverlayConnection.addConsumer(mDreamStartOverlayConsumer); } } @Override Loading @@ -1362,6 +1383,9 @@ public class DreamService extends Service implements Window.Callback { mActivity = null; finish(); } if (mOverlayConnection != null && mDreamStartOverlayConsumer != null) { mOverlayConnection.removeConsumer(mDreamStartOverlayConsumer); } } }); } Loading
core/java/com/android/internal/util/ObservableServiceConnection.java 0 → 100644 +258 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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.util; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.CallbackRegistry.NotifierCallback; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.Executor; /** * {@link ObservableServiceConnection} is a concrete implementation of {@link ServiceConnection} * that enables monitoring the status of a binder connection. It also aides in automatically * converting a proxy into an internal wrapper type. * * @param <T> The type of the wrapper over the resulting service. */ public class ObservableServiceConnection<T> implements ServiceConnection { /** * An interface for converting the service proxy into a given internal wrapper type. * * @param <T> The type of the wrapper over the resulting service. */ public interface ServiceTransformer<T> { /** * Called to convert the service proxy to the wrapper type. * * @param service The service proxy to create the wrapper type from. * @return The wrapper type. */ T convert(IBinder service); } /** * An interface for listening to the connection status. * * @param <T> The wrapper type. */ public interface Callback<T> { /** * Invoked when the service has been successfully connected to. * * @param connection The {@link ObservableServiceConnection} instance that is now connected * @param service The service proxy converted into the typed wrapper. */ void onConnected(ObservableServiceConnection<T> connection, T service); /** * Invoked when the service has been disconnected. * * @param connection The {@link ObservableServiceConnection} that is now disconnected. * @param reason The reason for the disconnection. */ void onDisconnected(ObservableServiceConnection<T> connection, @DisconnectReason int reason); } /** * Default state, service has not yet disconnected. */ public static final int DISCONNECT_REASON_NONE = 0; /** * Disconnection was due to the resulting binding being {@code null}. */ public static final int DISCONNECT_REASON_NULL_BINDING = 1; /** * Disconnection was due to the remote end disconnecting. */ public static final int DISCONNECT_REASON_DISCONNECTED = 2; /** * Disconnection due to the binder dying. */ public static final int DISCONNECT_REASON_BINDING_DIED = 3; /** * Disconnection from an explicit unbinding. */ public static final int DISCONNECT_REASON_UNBIND = 4; @Retention(RetentionPolicy.SOURCE) @IntDef({ DISCONNECT_REASON_NONE, DISCONNECT_REASON_NULL_BINDING, DISCONNECT_REASON_DISCONNECTED, DISCONNECT_REASON_BINDING_DIED, DISCONNECT_REASON_UNBIND }) public @interface DisconnectReason { } private final Object mLock = new Object(); private final Context mContext; private final Executor mExecutor; private final ServiceTransformer<T> mTransformer; private final Intent mServiceIntent; private final int mFlags; @GuardedBy("mLock") private T mService; @GuardedBy("mLock") private boolean mBoundCalled = false; @GuardedBy("mLock") private int mLastDisconnectReason = DISCONNECT_REASON_NONE; private final CallbackRegistry<Callback<T>, ObservableServiceConnection<T>, T> mCallbackRegistry = new CallbackRegistry<>( new NotifierCallback<Callback<T>, ObservableServiceConnection<T>, T>() { @Override public void onNotifyCallback(Callback<T> callback, ObservableServiceConnection<T> sender, int disconnectReason, T service) { mExecutor.execute(() -> { synchronized (mLock) { if (service != null) { callback.onConnected(sender, service); } else if (mLastDisconnectReason != DISCONNECT_REASON_NONE) { callback.onDisconnected(sender, disconnectReason); } } }); } }); /** * Default constructor for {@link ObservableServiceConnection}. * * @param context The context from which the service will be bound with. * @param executor The executor for connection callbacks to be delivered on * @param transformer A {@link ObservableServiceConnection.ServiceTransformer} for transforming * the resulting service into a desired type. */ public ObservableServiceConnection(@NonNull Context context, @NonNull @CallbackExecutor Executor executor, @NonNull ServiceTransformer<T> transformer, Intent serviceIntent, int flags) { mContext = context; mExecutor = executor; mTransformer = transformer; mServiceIntent = serviceIntent; mFlags = flags; } /** * Initiate binding to the service. * * @return {@code true} if initiating binding succeed, {@code false} if the binding failed or * if this service is already bound. Regardless of the return value, you should later call * {@link #unbind()} to release the connection. */ public boolean bind() { synchronized (mLock) { if (mBoundCalled) { return false; } final boolean bindResult = mContext.bindService(mServiceIntent, mFlags, mExecutor, this); mBoundCalled = true; return bindResult; } } /** * Disconnect from the service if bound. */ public void unbind() { onDisconnected(DISCONNECT_REASON_UNBIND); } /** * Adds a callback for receiving connection updates. * * @param callback The {@link Callback} to receive future updates. */ public void addCallback(Callback<T> callback) { mCallbackRegistry.add(callback); mExecutor.execute(() -> { synchronized (mLock) { if (mService != null) { callback.onConnected(this, mService); } else if (mLastDisconnectReason != DISCONNECT_REASON_NONE) { callback.onDisconnected(this, mLastDisconnectReason); } } }); } /** * Removes previously added callback from receiving future connection updates. * * @param callback The {@link Callback} to be removed. */ public void removeCallback(Callback<T> callback) { synchronized (mLock) { mCallbackRegistry.remove(callback); } } private void onDisconnected(@DisconnectReason int reason) { synchronized (mLock) { if (!mBoundCalled) { return; } mBoundCalled = false; mLastDisconnectReason = reason; mContext.unbindService(this); mService = null; mCallbackRegistry.notifyCallbacks(this, reason, null); } } @Override public final void onServiceConnected(ComponentName name, IBinder service) { synchronized (mLock) { mService = mTransformer.convert(service); mLastDisconnectReason = DISCONNECT_REASON_NONE; mCallbackRegistry.notifyCallbacks(this, mLastDisconnectReason, mService); } } @Override public final void onServiceDisconnected(ComponentName name) { onDisconnected(DISCONNECT_REASON_DISCONNECTED); } @Override public final void onBindingDied(ComponentName name) { onDisconnected(DISCONNECT_REASON_BINDING_DIED); } @Override public final void onNullBinding(ComponentName name) { onDisconnected(DISCONNECT_REASON_NULL_BINDING); } }
core/java/com/android/internal/util/PersistentServiceConnection.java 0 → 100644 +200 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
core/res/res/values/config.xml +8 −0 Original line number Diff line number Diff line Loading @@ -553,6 +553,14 @@ <!-- If this is true, then keep dreaming when undocking. --> <bool name="config_keepDreamingWhenUndocking">false</bool> <!-- The timeout (in ms) to wait before attempting to reconnect to the dream overlay service if it becomes disconnected --> <integer name="config_dreamOverlayReconnectTimeoutMs">1000</integer> <!-- 1 second --> <!-- The maximum number of times to attempt reconnecting to the dream overlay service --> <integer name="config_dreamOverlayMaxReconnectAttempts">3</integer> <!-- The duration after which the dream overlay connection should be considered stable --> <integer name="config_minDreamOverlayDurationMs">10000</integer> <!-- 10 seconds --> <!-- Auto-rotation behavior --> <!-- If true, enables auto-rotation features using the accelerometer. Loading
core/res/res/values/symbols.xml +3 −0 Original line number Diff line number Diff line Loading @@ -2239,6 +2239,9 @@ <java-symbol type="array" name="config_supportedDreamComplications" /> <java-symbol type="array" name="config_disabledDreamComponents" /> <java-symbol type="bool" name="config_dismissDreamOnActivityStart" /> <java-symbol type="integer" name="config_dreamOverlayReconnectTimeoutMs" /> <java-symbol type="integer" name="config_dreamOverlayMaxReconnectAttempts" /> <java-symbol type="integer" name="config_minDreamOverlayDurationMs" /> <java-symbol type="string" name="config_loggable_dream_prefix" /> <java-symbol type="string" name="config_dozeComponent" /> <java-symbol type="string" name="enable_explore_by_touch_warning_title" /> Loading