Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad9cf217 authored by Bryce Lee's avatar Bryce Lee Committed by Android (Google) Code Review
Browse files

Merge changes Ib2e177f4,Icd9f17b4

* changes:
  Do not show communal view when occluded.
  Reconnect to communal source.
parents 6f1b5890 162f7218
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -691,4 +691,13 @@

    <!-- Timeout to idle mode duration in milliseconds. -->
    <integer name="config_idleModeTimeout">10000</integer>

    <!-- The maximum number of attempts to reconnect to the communal source target after failing
         to connect -->
    <integer name="config_communalSourceMaxReconnectAttempts">10</integer>

    <!-- The initial amount of time (in milliseconds) before attempting to reconnect to a communal
         source. This value is used as the base value in an exponential backoff in subsequent
         attempts. -->
    <integer name="config_communalSourceReconnectBaseDelay">1000</integer>
</resources>
+3 −0
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import android.os.Bundle;

import androidx.annotation.NonNull;

import com.android.internal.annotations.VisibleForTesting;

import java.io.FileDescriptor;
import java.io.PrintWriter;

@@ -48,6 +50,7 @@ public abstract class SystemUI implements Dumpable {
    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
    }

    @VisibleForTesting
    protected void onBootCompleted() {
    }

+80 −9
Original line number Diff line number Diff line
@@ -16,20 +16,23 @@

package com.android.systemui.communal;

import android.annotation.IntDef;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;

import com.google.common.util.concurrent.ListenableFuture;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.concurrent.Executor;

@@ -40,22 +43,50 @@ import javax.inject.Inject;
 */
public class CommunalHostViewController extends ViewController<CommunalHostView> {
    private static final String TAG = "CommunalController";
    private static final boolean DEBUG = false;
    private static final AnimationProperties ANIMATION_PROPERTIES =
            new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    private static final String STATE_LIST_FORMAT = "[%s]";

    private final Executor mMainExecutor;
    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
    private final KeyguardStateController mKeyguardStateController;
    private final StatusBarStateController mStatusBarStateController;
    private WeakReference<CommunalSource> mLastSource;
    private int mState;

    @Retention(RetentionPolicy.RUNTIME)
    @IntDef({STATE_KEYGUARD_SHOWING, STATE_DOZING, STATE_BOUNCER_SHOWING, STATE_KEYGUARD_OCCLUDED})
    public @interface State {}

    private static final int STATE_KEYGUARD_SHOWING = 1 << 0;
    private static final int STATE_DOZING = 1 << 1;
    private static final int STATE_BOUNCER_SHOWING = 1 << 2;
    private static final int STATE_KEYGUARD_OCCLUDED = 1 << 3;

    // Only show communal view when keyguard is showing and not dozing.
    private static final int SHOW_COMMUNAL_VIEW_REQUIRED_STATES = STATE_KEYGUARD_SHOWING;
    private static final int SHOW_COMMUNAL_VIEW_INVALID_STATES = STATE_DOZING;
    private static final int SHOW_COMMUNAL_VIEW_INVALID_STATES =
            STATE_DOZING | STATE_BOUNCER_SHOWING | STATE_KEYGUARD_OCCLUDED;

    private KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
            new KeyguardUpdateMonitorCallback() {
                @Override
                public void onKeyguardBouncerChanged(boolean bouncer) {
                    if (DEBUG) {
                        Log.d(TAG, "onKeyguardBouncerChanged:" + bouncer);
                    }

                    setState(STATE_BOUNCER_SHOWING, bouncer);
                }

                @Override
                public void onKeyguardOccludedChanged(boolean occluded) {
                    if (DEBUG) {
                        Log.d(TAG, "onKeyguardOccludedChanged" + occluded);
                    }

                    setState(STATE_KEYGUARD_OCCLUDED, occluded);
                }
            };

    private KeyguardStateController.Callback mKeyguardCallback =
            new KeyguardStateController.Callback() {
@@ -84,9 +115,11 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>

    @Inject
    protected CommunalHostViewController(@Main Executor mainExecutor,
            KeyguardUpdateMonitor keyguardUpdateMonitor,
            KeyguardStateController keyguardStateController,
            StatusBarStateController statusBarStateController, CommunalHostView view) {
        super(view);
        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
        mMainExecutor = mainExecutor;
        mKeyguardStateController = keyguardStateController;
        mStatusBarStateController = statusBarStateController;
@@ -104,18 +137,20 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
    protected void onViewAttached() {
        mKeyguardStateController.addCallback(mKeyguardCallback);
        mStatusBarStateController.addCallback(mDozeCallback);
        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
    }

    @Override
    protected void onViewDetached() {
        mKeyguardStateController.removeCallback(mKeyguardCallback);
        mStatusBarStateController.removeCallback(mDozeCallback);
        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
    }

    private void setState(int stateFlag, boolean enabled) {
    private void setState(@State int stateFlag, boolean enabled) {
        final int existingState = mState;
        if (DEBUG) {
            Log.d(TAG, "setState flag:" + stateFlag + " enabled:" + enabled);
            Log.d(TAG, "setState flag:" + describeState(stateFlag) + " enabled:" + enabled);
        }

        if (enabled) {
@@ -125,7 +160,7 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
        }

        if (DEBUG) {
            Log.d(TAG, "updated state:" + mState);
            Log.d(TAG, "updated state:" + describeState());
        }

        if (existingState != mState) {
@@ -133,12 +168,48 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
        }
    }

    private String describeState(@State int stateFlag) {
        switch(stateFlag) {
            case STATE_DOZING:
                return "dozing";
            case STATE_BOUNCER_SHOWING:
                return "bouncer_showing";
            case STATE_KEYGUARD_SHOWING:
                return "keyguard_showing";
            default:
                return "UNDEFINED_STATE";
        }
    }

    private String describeState() {
        StringBuilder stringBuilder = new StringBuilder();

        if ((mState & STATE_KEYGUARD_SHOWING) == STATE_KEYGUARD_SHOWING) {
            stringBuilder.append(String.format(STATE_LIST_FORMAT,
                    describeState(STATE_KEYGUARD_SHOWING)));
        }
        if ((mState & STATE_DOZING) == STATE_DOZING) {
            stringBuilder.append(String.format(STATE_LIST_FORMAT,
                    describeState(STATE_DOZING)));
        }
        if ((mState & STATE_BOUNCER_SHOWING) == STATE_BOUNCER_SHOWING) {
            stringBuilder.append(String.format(STATE_LIST_FORMAT,
                    describeState(STATE_BOUNCER_SHOWING)));
        }

        return stringBuilder.toString();
    }

    private void showSource() {
        // Make sure all necessary states are present for showing communal and all invalid states
        // are absent
        mMainExecutor.execute(() -> {
            final CommunalSource currentSource = mLastSource != null ? mLastSource.get() : null;

            if (DEBUG) {
                Log.d(TAG, "showSource. currentSource:" + currentSource);
            }

            if ((mState & SHOW_COMMUNAL_VIEW_REQUIRED_STATES) == SHOW_COMMUNAL_VIEW_REQUIRED_STATES
                    && (mState & SHOW_COMMUNAL_VIEW_INVALID_STATES) == 0
                    && currentSource != null) {
+16 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.view.View;
import androidx.concurrent.futures.CallbackToFutureAdapter;

import com.android.systemui.communal.CommunalSource;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.communal.ICommunalSource;
import com.android.systemui.shared.communal.ICommunalSurfaceCallback;

@@ -36,6 +37,8 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.concurrent.Executor;

import javax.inject.Inject;

/**
 * {@link CommunalSourceImpl} provides a wrapper around {@link ICommunalSource} proxies as an
 * implementation of {@link CommunalSource}. Requests and responses for communal surfaces are
@@ -47,6 +50,19 @@ public class CommunalSourceImpl implements CommunalSource {
    private final ICommunalSource mSourceProxy;
    private final Executor mMainExecutor;

    static class Factory {
        private final Executor mExecutor;

        @Inject
        Factory(@Main Executor executor) {
            mExecutor = executor;
        }

        public CommunalSource create(ICommunalSource source) {
            return new CommunalSourceImpl(mExecutor, source);
        }
    }

    // mConnected is initialized to true as it is presumed instances are constructed with valid
    // proxies. The source can never be reconnected once the proxy has died. Once this value
    // becomes false, the source will always report disconnected to registering callbacks.
+146 −16
Original line number Diff line number Diff line
@@ -16,11 +16,15 @@

package com.android.systemui.communal.service;

import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.res.Resources;
import android.os.IBinder;
import android.os.PatternMatcher;
import android.util.Log;

import com.android.systemui.R;
@@ -29,8 +33,7 @@ import com.android.systemui.communal.CommunalSourceMonitor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.communal.ICommunalSource;

import java.util.concurrent.Executor;
import com.android.systemui.util.concurrency.DelayableExecutor;

import javax.inject.Inject;

@@ -43,63 +46,190 @@ import javax.inject.Inject;
@SysUISingleton
public class CommunalSourcePrimer extends SystemUI {
    private static final String TAG = "CommunalSourcePrimer";
    private static final boolean DEBUG = false;
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    private static final String ACTION_COMMUNAL_SOURCE = "android.intent.action.COMMUNAL_SOURCE";

    private final Context mContext;
    private final Executor mMainExecutor;
    private final DelayableExecutor mMainExecutor;
    private final CommunalSourceMonitor mMonitor;
    private static final String ACTION_COMMUNAL_SOURCE = "android.intent.action.COMMUNAL_SOURCE";
    private final CommunalSourceImpl.Factory mSourceFactory;
    private final ComponentName mComponentName;
    private final int mBaseReconnectDelayMs;
    private final int mMaxReconnectAttempts;

    private int mReconnectAttempts = 0;
    private Runnable mCurrentReconnectCancelable;

    private final Runnable mConnectRunnable = new Runnable() {
        @Override
        public void run() {
            mCurrentReconnectCancelable = null;
            bindToService();
        }
    };


    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (DEBUG) {
                Log.d(TAG, "package added receiver - onReceive");
            }

            initiateConnectionAttempt();
        }
    };

    private final ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            final ICommunalSource source = ICommunalSource.Stub.asInterface(service);
            if (DEBUG) {
                Log.d(TAG, "onServiceConnected. source;" + source);
                Log.d(TAG, "onServiceConnected. source:" + source);
            }

            mMonitor.setSource(
                    source != null ? new CommunalSourceImpl(mMainExecutor, source) : null);
            if (source == null) {
                if (DEBUG) {
                    Log.d(TAG, "onServiceConnected. invalid source");
                    // Since the service could just repeatedly return null, the primer chooses
                    // to schedule rather than initiate a new connection attempt sequence.
                    scheduleConnectionAttempt();
                }
                return;
            }

            mMonitor.setSource(mSourceFactory.create(source));
        }

        @Override
        public void onBindingDied(ComponentName name) {
            if (DEBUG) {
                Log.d(TAG, "onBindingDied. lost communal source. initiating reconnect");
            }

            initiateConnectionAttempt();
        }

        @Override
        public void onServiceDisconnected(ComponentName className) {
            if (DEBUG) {
                Log.d(TAG,
                        "onServiceDisconnected. lost communal source. initiating reconnect");
            }

            initiateConnectionAttempt();
        }
    };

    @Inject
    public CommunalSourcePrimer(Context context, @Main Executor mainExecutor,
            CommunalSourceMonitor monitor) {
    public CommunalSourcePrimer(Context context, @Main Resources resources,
            DelayableExecutor mainExecutor,
            CommunalSourceMonitor monitor,
            CommunalSourceImpl.Factory sourceFactory) {
        super(context);
        mContext = context;
        mMainExecutor = mainExecutor;
        mMonitor = monitor;
        mSourceFactory = sourceFactory;
        mMaxReconnectAttempts = resources.getInteger(
                R.integer.config_communalSourceMaxReconnectAttempts);
        mBaseReconnectDelayMs = resources.getInteger(
                R.integer.config_communalSourceReconnectBaseDelay);

        final String component = resources.getString(R.string.config_communalSourceComponent);
        mComponentName = component != null && !component.isEmpty()
                ? ComponentName.unflattenFromString(component) : null;
    }

    @Override
    public void start() {
    }

    private void initiateConnectionAttempt() {
        // Reset attempts
        mReconnectAttempts = 0;
        mMonitor.setSource(null);

        // The first attempt is always a direct invocation rather than delayed.
        bindToService();
    }

    private void registerPackageListening() {
        if (mComponentName == null) {
            return;
        }

        final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
        filter.addDataScheme("package");
        filter.addDataSchemeSpecificPart(mComponentName.getPackageName(),
                PatternMatcher.PATTERN_LITERAL);
        // Note that we directly register the receiver here as data schemes are not supported by
        // BroadcastDispatcher.
        mContext.registerReceiver(mReceiver, filter);
    }

    private void scheduleConnectionAttempt() {
        // always clear cancelable if present.
        if (mCurrentReconnectCancelable != null) {
            mCurrentReconnectCancelable.run();
            mCurrentReconnectCancelable = null;
        }

        if (mReconnectAttempts >= mMaxReconnectAttempts) {
            if (DEBUG) {
                Log.d(TAG, "exceeded max connection attempts.");
            }
            return;
        }

        final long reconnectDelayMs =
                (long) Math.scalb(mBaseReconnectDelayMs, mReconnectAttempts);

        if (DEBUG) {
            Log.d(TAG, "start");
            Log.d(TAG,
                    "scheduling connection attempt in " + reconnectDelayMs + "milliseconds");
        }

        mCurrentReconnectCancelable = mMainExecutor.executeDelayed(mConnectRunnable,
                reconnectDelayMs);

        mReconnectAttempts++;
    }

    @Override
    protected void onBootCompleted() {
        super.onBootCompleted();
        final String serviceComponent = mContext.getString(R.string.config_communalSourceComponent);

        if (DEBUG) {
            Log.d(TAG, "onBootCompleted. communal source component:" + serviceComponent);
            Log.d(TAG, "onBootCompleted. communal source component:" + mComponentName);
        }

        registerPackageListening();
        initiateConnectionAttempt();
    }

        if (serviceComponent == null || serviceComponent.isEmpty()) {
    private void bindToService() {
        if (mComponentName == null) {
            return;
        }

        if (DEBUG) {
            Log.d(TAG, "attempting to bind to communal source");
        }

        final Intent intent = new Intent();
        intent.setAction(ACTION_COMMUNAL_SOURCE);
        intent.setComponent(ComponentName.unflattenFromString(serviceComponent));
        intent.setComponent(mComponentName);

        mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
        final boolean binding = mContext.bindService(intent, Context.BIND_AUTO_CREATE,
                mMainExecutor, mConnection);

        if (!binding) {
            if (DEBUG) {
                Log.d(TAG, "bindService failed, rescheduling");
            }

            scheduleConnectionAttempt();
        }
    }
}
Loading