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

Commit d6f48402 authored by Fabian Kozynski's avatar Fabian Kozynski
Browse files

Add API for requesting add controls from Provider

This API allows ControlsProviderService to request SystemUI to show UI
to the user to add a favorite. This can be called at any time (not just
when bound).

Test: atest
Fixes: 148936288
Change-Id: I8740b6a105ad64994e6df701f2604a4234a49154
parent 8887c6da
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ package android {
    field public static final String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES";
    field @Deprecated public static final String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
    field public static final String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE";
    field public static final String BIND_CONTROLS = "android.permission.BIND_CONTROLS";
    field public static final String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
    field public static final String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
    field public static final String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";
@@ -43410,6 +43411,7 @@ package android.service.controls {
    method @NonNull public abstract java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherFor(@NonNull java.util.List<java.lang.String>);
    method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherForAllAvailable();
    method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherForSuggested();
    method public static void requestAddControl(@NonNull android.content.Context, @NonNull android.content.ComponentName, @NonNull android.service.controls.Control);
    field public static final String SERVICE_CONTROLS = "android.service.controls.ControlsProviderService";
    field @NonNull public static final String TAG = "ControlsProviderService";
  }
+44 −0
Original line number Diff line number Diff line
@@ -15,11 +15,14 @@
 */
package android.service.controls;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
@@ -51,6 +54,20 @@ public abstract class ControlsProviderService extends Service {
    @SdkConstant(SdkConstantType.SERVICE_ACTION)
    public static final String SERVICE_CONTROLS =
            "android.service.controls.ControlsProviderService";

    /**
     * @hide
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_ADD_CONTROL =
            "android.service.controls.action.ADD_CONTROL";

    /**
     * @hide
     */
    public static final String EXTRA_CONTROL =
            "android.service.controls.extra.CONTROL";

    /**
     * @hide
     */
@@ -325,6 +342,33 @@ public abstract class ControlsProviderService extends Service {
        }
    }

    /**
     * Request SystemUI to prompt the user to add a control to favorites.
     *
     * @param context A context
     * @param componentName Component name of the {@link ControlsProviderService}
     * @param control A stateless control to show to the user
     */
    public static void requestAddControl(@NonNull Context context,
            @NonNull ComponentName componentName,
            @NonNull Control control) {
        Preconditions.checkNotNull(context);
        Preconditions.checkNotNull(componentName);
        Preconditions.checkNotNull(control);
        final ComponentName sysuiComponent = ComponentName.unflattenFromString(
                context.getResources().getString(
                        com.android.internal.R.string.config_systemUIServiceComponent));
        Intent intent = new Intent(ACTION_ADD_CONTROL);
        intent.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName);
        intent.setPackage(sysuiComponent.getPackageName());
        if (isStatelessControl(control)) {
            intent.putExtra(EXTRA_CONTROL, control);
        } else {
            intent.putExtra(EXTRA_CONTROL, new Control.StatelessBuilder(control).build());
        }
        context.sendBroadcast(intent, Manifest.permission.BIND_CONTROLS);
    }

    private static class SubscriptionAdapter extends IControlsSubscription.Stub {
        final Subscription mSubscription;

+1 −2
Original line number Diff line number Diff line
@@ -3081,8 +3081,7 @@

    <!-- Allows SystemUI to request third party controls.
         <p>Should only be requested by the System and required by
         ControlsService declarations.
         @hide
         {@link android.service.controls.ControlsProviderService} declarations.
    -->
    <permission android:name="android.permission.BIND_CONTROLS"
        android:protectionLevel="signature" />
+35 −0
Original line number Diff line number Diff line
@@ -25,9 +25,13 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.Manifest;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -44,6 +48,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@@ -59,6 +64,11 @@ import java.util.function.Consumer;
@RunWith(AndroidJUnit4.class)
public class ControlProviderServiceTest {

    private static final ComponentName TEST_SYSUI_COMPONENT =
            ComponentName.unflattenFromString("sysui/.test.cls");
    private static final ComponentName TEST_COMPONENT =
            ComponentName.unflattenFromString("test.pkg/.test.cls");

    private IBinder mToken = new Binder();
    @Mock
    private IControlsActionCallback.Stub mActionCallback;
@@ -66,6 +76,12 @@ public class ControlProviderServiceTest {
    private IControlsSubscriber.Stub mSubscriber;
    @Mock
    private IIntentSender mIIntentSender;
    @Mock
    private Resources mResources;
    @Mock
    private Context mContext;
    @Captor
    private ArgumentCaptor<Intent> mIntentArgumentCaptor;

    private PendingIntent mPendingIntent;
    private FakeControlsProviderService mControlsProviderService;
@@ -81,6 +97,10 @@ public class ControlProviderServiceTest {
        when(mSubscriber.asBinder()).thenCallRealMethod();
        when(mSubscriber.queryLocalInterface(any())).thenReturn(mSubscriber);

        when(mResources.getString(com.android.internal.R.string.config_systemUIServiceComponent))
                .thenReturn(TEST_SYSUI_COMPONENT.flattenToString());
        when(mContext.getResources()).thenReturn(mResources);

        Bundle b = new Bundle();
        b.putBinder(ControlsProviderService.CALLBACK_TOKEN, mToken);
        Intent intent = new Intent();
@@ -223,6 +243,21 @@ public class ControlProviderServiceTest {
                ControlAction.RESPONSE_OK);
    }

    @Test
    public void testRequestAdd() {
        Control control = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build();
        ControlsProviderService.requestAddControl(mContext, TEST_COMPONENT, control);

        verify(mContext).sendBroadcast(mIntentArgumentCaptor.capture(),
                eq(Manifest.permission.BIND_CONTROLS));
        Intent intent = mIntentArgumentCaptor.getValue();
        assertEquals(ControlsProviderService.ACTION_ADD_CONTROL, intent.getAction());
        assertEquals(TEST_SYSUI_COMPONENT.getPackageName(), intent.getPackage());
        assertEquals(TEST_COMPONENT, intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME));
        assertTrue(equals(control,
                intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL)));
    }

    private static boolean equals(Control c1, Control c2) {
        if (c1 == c2) return true;
        if (c1 == null || c2 == null) return false;