Add API for requesting add TileService
This CL adds the API to StatusBarManager, as well as the constants. It also wires things into SystemUI so the API actually works as intended. TODO: * Rate limit requests (b/197534249) Fixes: 197207978 Test: manual using test app Test: atest SystemUITests Change-Id: I2a9d18535fde1d77e9d6792bd45002ce04b49dab
This commit is contained in:
@@ -6693,6 +6693,16 @@ package android.app {
|
||||
}
|
||||
|
||||
public class StatusBarManager {
|
||||
method public int requestAddTileService(@NonNull android.content.ComponentName, @NonNull CharSequence, @NonNull android.graphics.drawable.Icon, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
|
||||
field public static final int TILE_ADD_REQUEST_ANSWER_FAILED_BAD_COMPONENT = 3; // 0x3
|
||||
field public static final int TILE_ADD_REQUEST_ANSWER_FAILED_MISMATCHED_PACKAGE = 1; // 0x1
|
||||
field public static final int TILE_ADD_REQUEST_ANSWER_FAILED_NOT_CURRENT_USER = 4; // 0x4
|
||||
field public static final int TILE_ADD_REQUEST_ANSWER_FAILED_REQUEST_IN_PROGRESS = 2; // 0x2
|
||||
field public static final int TILE_ADD_REQUEST_ANSWER_FAILED_UNKNOWN_REASON = 5; // 0x5
|
||||
field public static final int TILE_ADD_REQUEST_ANSWER_SUCCESS = 0; // 0x0
|
||||
field public static final int TILE_ADD_REQUEST_RESULT_TILE_ADDED = 2; // 0x2
|
||||
field public static final int TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED = 1; // 0x1
|
||||
field public static final int TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED = 0; // 0x0
|
||||
}
|
||||
|
||||
public final class SyncNotedAppOp implements android.os.Parcelable {
|
||||
@@ -10695,6 +10705,7 @@ package android.content {
|
||||
field public static final String SEARCH_SERVICE = "search";
|
||||
field public static final String SENSOR_SERVICE = "sensor";
|
||||
field public static final String SHORTCUT_SERVICE = "shortcut";
|
||||
field public static final String STATUS_BAR_SERVICE = "statusbar";
|
||||
field public static final String STORAGE_SERVICE = "storage";
|
||||
field public static final String STORAGE_STATS_SERVICE = "storagestats";
|
||||
field public static final String SYSTEM_HEALTH_SERVICE = "systemhealth";
|
||||
|
||||
@@ -2350,7 +2350,6 @@ package android.content {
|
||||
field public static final String SECURE_ELEMENT_SERVICE = "secure_element";
|
||||
field public static final String SMARTSPACE_SERVICE = "smartspace";
|
||||
field public static final String STATS_MANAGER = "stats";
|
||||
field public static final String STATUS_BAR_SERVICE = "statusbar";
|
||||
field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
|
||||
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
|
||||
field public static final String TETHERING_SERVICE = "tethering";
|
||||
|
||||
@@ -24,7 +24,9 @@ import android.annotation.SystemApi;
|
||||
import android.annotation.SystemService;
|
||||
import android.annotation.TestApi;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -35,11 +37,15 @@ import android.util.Pair;
|
||||
import android.util.Slog;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.internal.statusbar.IAddTileResultCallback;
|
||||
import com.android.internal.statusbar.IStatusBarService;
|
||||
import com.android.internal.statusbar.NotificationVisibility;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Allows an app to control the status bar.
|
||||
@@ -206,6 +212,71 @@ public class StatusBarManager {
|
||||
/** @hide */
|
||||
public static final int CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER = 2;
|
||||
|
||||
/**
|
||||
* Response indicating that the tile was not added.
|
||||
*/
|
||||
public static final int TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED = 0;
|
||||
/**
|
||||
* Response indicating that the tile was already added and the user was not prompted.
|
||||
*/
|
||||
public static final int TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED = 1;
|
||||
/**
|
||||
* Response indicating that the tile was added.
|
||||
*/
|
||||
public static final int TILE_ADD_REQUEST_RESULT_TILE_ADDED = 2;
|
||||
/** @hide */
|
||||
public static final int TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED = 3;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(prefix = {"TILE_ADD_REQUEST_RESULT_"}, value = {
|
||||
TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED,
|
||||
TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED,
|
||||
TILE_ADD_REQUEST_RESULT_TILE_ADDED,
|
||||
TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface RequestResult {}
|
||||
|
||||
/**
|
||||
* Indicates that the request was sent successfully. Does not indicate that the user
|
||||
* has accepted the request.
|
||||
*/
|
||||
public static final int TILE_ADD_REQUEST_ANSWER_SUCCESS = 0;
|
||||
/**
|
||||
* Indicates that this package does not match that of the
|
||||
* {@link android.service.quicksettings.TileService}.
|
||||
*/
|
||||
public static final int TILE_ADD_REQUEST_ANSWER_FAILED_MISMATCHED_PACKAGE = 1;
|
||||
/**
|
||||
* Indicates that there's a request in progress for this package.
|
||||
*/
|
||||
public static final int TILE_ADD_REQUEST_ANSWER_FAILED_REQUEST_IN_PROGRESS = 2;
|
||||
/**
|
||||
* Indicates that the component does not match an enabled
|
||||
* {@link android.service.quicksettings.TileService} for the current user.
|
||||
*/
|
||||
public static final int TILE_ADD_REQUEST_ANSWER_FAILED_BAD_COMPONENT = 3;
|
||||
/**
|
||||
* Indicates that the user is not the current user.
|
||||
*/
|
||||
public static final int TILE_ADD_REQUEST_ANSWER_FAILED_NOT_CURRENT_USER = 4;
|
||||
/**
|
||||
* The request could not be processed due to an unkonwn reason.
|
||||
*/
|
||||
public static final int TILE_ADD_REQUEST_ANSWER_FAILED_UNKNOWN_REASON = 5;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(prefix = {"TILE_ADD_REQUEST_ANSWER_"}, value = {
|
||||
TILE_ADD_REQUEST_ANSWER_SUCCESS,
|
||||
TILE_ADD_REQUEST_ANSWER_FAILED_MISMATCHED_PACKAGE,
|
||||
TILE_ADD_REQUEST_ANSWER_FAILED_REQUEST_IN_PROGRESS,
|
||||
TILE_ADD_REQUEST_ANSWER_FAILED_BAD_COMPONENT,
|
||||
TILE_ADD_REQUEST_ANSWER_FAILED_NOT_CURRENT_USER,
|
||||
TILE_ADD_REQUEST_ANSWER_FAILED_UNKNOWN_REASON
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface RequestAnswer {}
|
||||
|
||||
@UnsupportedAppUsage
|
||||
private Context mContext;
|
||||
private IStatusBarService mService;
|
||||
@@ -529,6 +600,69 @@ public class StatusBarManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to the user to add a {@link android.service.quicksettings.TileService}
|
||||
* to the set of current QS tiles.
|
||||
* <p>
|
||||
* Calling this will prompt the user to decide whether they want to add the shown
|
||||
* {@link android.service.quicksettings.TileService} to their current tiles. The user can
|
||||
* deny the request and the system can stop processing requests for a given
|
||||
* {@link ComponentName} after a number of requests.
|
||||
* <p>
|
||||
* The request will show to the user information about the tile:
|
||||
* <ul>
|
||||
* <li>Application name</li>
|
||||
* <li>Label for the tile</li>
|
||||
* <li>Icon for the tile</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* The user for which this will be added is determined from the {@link Context} used to retrieve
|
||||
* this service, and must match the current user.
|
||||
*
|
||||
* @param tileServiceComponentName {@link ComponentName} of the
|
||||
* {@link android.service.quicksettings.TileService} for the request.
|
||||
* @param tileLabel label of the tile to show to the user.
|
||||
* @param icon icon to use in the tile shown to the user.
|
||||
* @param resultExecutor an executor to run the callback on
|
||||
* @param resultCallback callback to indicate the {@link RequestResult}.
|
||||
* @return whether the request was successfully sent.
|
||||
*
|
||||
* @see android.service.quicksettings.TileService
|
||||
*/
|
||||
@RequestAnswer
|
||||
public int requestAddTileService(
|
||||
@NonNull ComponentName tileServiceComponentName,
|
||||
@NonNull CharSequence tileLabel,
|
||||
@NonNull Icon icon,
|
||||
@NonNull Executor resultExecutor,
|
||||
@NonNull Consumer<Integer> resultCallback
|
||||
) {
|
||||
Objects.requireNonNull(tileServiceComponentName);
|
||||
Objects.requireNonNull(tileLabel);
|
||||
Objects.requireNonNull(icon);
|
||||
Objects.requireNonNull(resultExecutor);
|
||||
Objects.requireNonNull(resultCallback);
|
||||
if (!tileServiceComponentName.getPackageName().equals(mContext.getPackageName())) {
|
||||
return TILE_ADD_REQUEST_ANSWER_FAILED_MISMATCHED_PACKAGE;
|
||||
}
|
||||
int userId = mContext.getUserId();
|
||||
RequestResultCallback callbackProxy = new RequestResultCallback(resultExecutor,
|
||||
resultCallback);
|
||||
IStatusBarService svc = getService();
|
||||
try {
|
||||
return svc.requestAddTile(
|
||||
tileServiceComponentName,
|
||||
tileLabel,
|
||||
icon,
|
||||
userId,
|
||||
callbackProxy
|
||||
);
|
||||
} catch (RemoteException ex) {
|
||||
ex.rethrowFromSystemServer();
|
||||
}
|
||||
return TILE_ADD_REQUEST_ANSWER_FAILED_UNKNOWN_REASON;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static String windowStateToString(int state) {
|
||||
if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING";
|
||||
@@ -773,4 +907,25 @@ public class StatusBarManager {
|
||||
return new Pair<Integer, Integer>(disable1, disable2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
static final class RequestResultCallback extends IAddTileResultCallback.Stub {
|
||||
|
||||
@NonNull
|
||||
private final Executor mExecutor;
|
||||
@NonNull
|
||||
private final Consumer<Integer> mCallback;
|
||||
|
||||
RequestResultCallback(@NonNull Executor executor, @NonNull Consumer<Integer> callback) {
|
||||
mExecutor = executor;
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTileRequest(int userResponse) {
|
||||
mExecutor.execute(() -> mCallback.accept(userResponse));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4224,14 +4224,12 @@ public abstract class Context {
|
||||
|
||||
/**
|
||||
* Use with {@link #getSystemService(String)} to retrieve a {@link
|
||||
* android.app.StatusBarManager} for interacting with the status bar.
|
||||
* android.app.StatusBarManager} for interacting with the status bar and quick settings.
|
||||
*
|
||||
* @see #getSystemService(String)
|
||||
* @see android.app.StatusBarManager
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@SuppressLint("ServiceName")
|
||||
public static final String STATUS_BAR_SERVICE = "statusbar";
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2021, 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.statusbar;
|
||||
|
||||
/** {@hide} */
|
||||
oneway interface IAddTileResultCallback {
|
||||
void onTileRequest(int userResponse);
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.android.internal.statusbar;
|
||||
|
||||
import android.app.ITransientNotificationCallback;
|
||||
import android.content.ComponentName;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.biometrics.IBiometricSysuiReceiver;
|
||||
import android.hardware.biometrics.PromptInfo;
|
||||
@@ -27,6 +28,7 @@ import android.os.ParcelFileDescriptor;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.view.InsetsVisibilities;
|
||||
|
||||
import com.android.internal.statusbar.IAddTileResultCallback;
|
||||
import com.android.internal.statusbar.StatusBarIcon;
|
||||
import com.android.internal.view.AppearanceRegion;
|
||||
|
||||
@@ -286,4 +288,6 @@ oneway interface IStatusBar
|
||||
* Triggers a GC in the system and status bar.
|
||||
*/
|
||||
void runGcForTest();
|
||||
|
||||
void requestAddTile(in ComponentName componentName, in CharSequence appName, in CharSequence label, in Icon icon, in IAddTileResultCallback callback);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.internal.statusbar;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.ComponentName;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.biometrics.IBiometricSysuiReceiver;
|
||||
import android.hardware.biometrics.PromptInfo;
|
||||
@@ -27,6 +28,7 @@ import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
|
||||
import com.android.internal.statusbar.IAddTileResultCallback;
|
||||
import com.android.internal.statusbar.IStatusBar;
|
||||
import com.android.internal.statusbar.RegisterStatusBarResult;
|
||||
import com.android.internal.statusbar.StatusBarIcon;
|
||||
@@ -156,4 +158,6 @@ interface IStatusBarService
|
||||
* display.
|
||||
*/
|
||||
void suppressAmbientDisplay(boolean suppress);
|
||||
|
||||
int requestAddTile(in ComponentName componentName, in CharSequence label, in Icon icon, int userId, in IAddTileResultCallback callback);
|
||||
}
|
||||
|
||||
@@ -17,17 +17,20 @@
|
||||
package com.android.systemui.qs.external
|
||||
|
||||
import android.app.Dialog
|
||||
import android.app.StatusBarManager
|
||||
import android.content.ComponentName
|
||||
import android.content.DialogInterface
|
||||
import android.graphics.drawable.Icon
|
||||
import android.util.Log
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.internal.statusbar.IAddTileResultCallback
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.qs.QSTileHost
|
||||
import com.android.systemui.statusbar.commandline.Command
|
||||
import com.android.systemui.statusbar.commandline.CommandRegistry
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialog
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import java.io.PrintWriter
|
||||
import java.util.function.Consumer
|
||||
import javax.inject.Inject
|
||||
@@ -39,24 +42,41 @@ private const val TAG = "TileServiceRequestController"
|
||||
*/
|
||||
class TileServiceRequestController constructor(
|
||||
private val qsTileHost: QSTileHost,
|
||||
private val commandQueue: CommandQueue,
|
||||
private val commandRegistry: CommandRegistry,
|
||||
private val dialogCreator: () -> TileRequestDialog = { TileRequestDialog(qsTileHost.context) }
|
||||
) {
|
||||
|
||||
companion object {
|
||||
// Temporary return values while there's no API
|
||||
internal const val ADD_TILE = 0
|
||||
internal const val DONT_ADD_TILE = 1
|
||||
internal const val TILE_ALREADY_ADDED = 2
|
||||
internal const val ADD_TILE = StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ADDED
|
||||
internal const val DONT_ADD_TILE = StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED
|
||||
internal const val TILE_ALREADY_ADDED =
|
||||
StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED
|
||||
internal const val DISMISSED = 3
|
||||
}
|
||||
|
||||
private val commandQueueCallback = object : CommandQueue.Callbacks {
|
||||
override fun requestAddTile(
|
||||
componentName: ComponentName,
|
||||
appName: CharSequence,
|
||||
label: CharSequence,
|
||||
icon: Icon,
|
||||
callback: IAddTileResultCallback
|
||||
) {
|
||||
requestTileAdd(componentName, appName, label, icon) {
|
||||
callback.onTileRequest(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun init() {
|
||||
commandRegistry.registerCommand("tile-service-add") { TileServiceRequestCommand() }
|
||||
commandQueue.addCallback(commandQueueCallback)
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
commandRegistry.unregisterCommand("tile-service-add")
|
||||
commandQueue.removeCallback(commandQueueCallback)
|
||||
}
|
||||
|
||||
private fun addTile(componentName: ComponentName) {
|
||||
@@ -133,10 +153,11 @@ class TileServiceRequestController constructor(
|
||||
|
||||
@SysUISingleton
|
||||
class Builder @Inject constructor(
|
||||
private val commandQueue: CommandQueue,
|
||||
private val commandRegistry: CommandRegistry
|
||||
) {
|
||||
fun create(qsTileHost: QSTileHost): TileServiceRequestController {
|
||||
return TileServiceRequestController(qsTileHost, commandRegistry)
|
||||
return TileServiceRequestController(qsTileHost, commandQueue, commandRegistry)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import android.app.StatusBarManager.WindowType;
|
||||
import android.app.StatusBarManager.WindowVisibleState;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.hardware.biometrics.BiometricAuthenticator.Modality;
|
||||
import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
|
||||
import android.hardware.biometrics.IBiometricSysuiReceiver;
|
||||
@@ -58,6 +59,7 @@ import android.view.WindowInsetsController.Behavior;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.internal.os.SomeArgs;
|
||||
import com.android.internal.statusbar.IAddTileResultCallback;
|
||||
import com.android.internal.statusbar.IStatusBar;
|
||||
import com.android.internal.statusbar.StatusBarIcon;
|
||||
import com.android.internal.util.GcUtils;
|
||||
@@ -148,6 +150,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
|
||||
private static final int MSG_EMERGENCY_ACTION_LAUNCH_GESTURE = 58 << MSG_SHIFT;
|
||||
private static final int MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED = 59 << MSG_SHIFT;
|
||||
private static final int MSG_SET_UDFPS_HBM_LISTENER = 60 << MSG_SHIFT;
|
||||
private static final int MSG_TILE_SERVICE_REQUEST_ADD = 61 << MSG_SHIFT;
|
||||
|
||||
public static final int FLAG_EXCLUDE_NONE = 0;
|
||||
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
|
||||
@@ -402,6 +405,16 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
|
||||
* @see IStatusBar#setNavigationBarLumaSamplingEnabled(int, boolean)
|
||||
*/
|
||||
default void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {}
|
||||
|
||||
/**
|
||||
* @see IStatusBar#requestAddTile
|
||||
*/
|
||||
default void requestAddTile(
|
||||
@NonNull ComponentName componentName,
|
||||
@NonNull CharSequence appName,
|
||||
@NonNull CharSequence label,
|
||||
@NonNull Icon icon,
|
||||
@NonNull IAddTileResultCallback callback) {}
|
||||
}
|
||||
|
||||
public CommandQueue(Context context) {
|
||||
@@ -1106,6 +1119,23 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
|
||||
GcUtils.runGcAndFinalizersSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestAddTile(
|
||||
@NonNull ComponentName componentName,
|
||||
@NonNull CharSequence appName,
|
||||
@NonNull CharSequence label,
|
||||
@NonNull Icon icon,
|
||||
@NonNull IAddTileResultCallback callback
|
||||
) {
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = componentName;
|
||||
args.arg2 = appName;
|
||||
args.arg3 = label;
|
||||
args.arg4 = icon;
|
||||
args.arg5 = callback;
|
||||
mHandler.obtainMessage(MSG_TILE_SERVICE_REQUEST_ADD, args).sendToTarget();
|
||||
}
|
||||
|
||||
private final class H extends Handler {
|
||||
private H(Looper l) {
|
||||
super(l);
|
||||
@@ -1478,6 +1508,19 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
|
||||
msg.arg2 != 0);
|
||||
}
|
||||
break;
|
||||
case MSG_TILE_SERVICE_REQUEST_ADD:
|
||||
args = (SomeArgs) msg.obj;
|
||||
ComponentName componentName = (ComponentName) args.arg1;
|
||||
CharSequence appName = (CharSequence) args.arg2;
|
||||
CharSequence label = (CharSequence) args.arg3;
|
||||
Icon icon = (Icon) args.arg4;
|
||||
IAddTileResultCallback callback = (IAddTileResultCallback) args.arg5;
|
||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||
mCallbacks.get(i).requestAddTile(
|
||||
componentName, appName, label, icon, callback);
|
||||
}
|
||||
args.recycle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,10 @@ import android.content.DialogInterface
|
||||
import android.graphics.drawable.Icon
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.statusbar.IAddTileResultCallback
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.qs.QSTileHost
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import com.android.systemui.statusbar.commandline.CommandRegistry
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.capture
|
||||
@@ -36,6 +38,7 @@ import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.anyBoolean
|
||||
import org.mockito.Mockito.anyInt
|
||||
import org.mockito.Mockito.anyString
|
||||
import org.mockito.Mockito.atLeastOnce
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
@@ -58,6 +61,8 @@ class TileServiceRequestControllerTest : SysuiTestCase() {
|
||||
@Mock
|
||||
private lateinit var commandRegistry: CommandRegistry
|
||||
@Mock
|
||||
private lateinit var commandQueue: CommandQueue
|
||||
@Mock
|
||||
private lateinit var icon: Icon
|
||||
|
||||
private lateinit var controller: TileServiceRequestController
|
||||
@@ -69,7 +74,7 @@ class TileServiceRequestControllerTest : SysuiTestCase() {
|
||||
// Tile not present by default
|
||||
`when`(qsTileHost.indexOf(anyString())).thenReturn(-1)
|
||||
|
||||
controller = TileServiceRequestController(qsTileHost, commandRegistry) {
|
||||
controller = TileServiceRequestController(qsTileHost, commandQueue, commandRegistry) {
|
||||
tileRequestDialog
|
||||
}
|
||||
|
||||
@@ -152,11 +157,44 @@ class TileServiceRequestControllerTest : SysuiTestCase() {
|
||||
verify(qsTileHost, never()).addTile(any(ComponentName::class.java), anyBoolean())
|
||||
}
|
||||
|
||||
private class Callback : Consumer<Int> {
|
||||
@Test
|
||||
fun commandQueueCallback_registered() {
|
||||
verify(commandQueue).addCallback(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun commandQueueCallback_dataPassedToDialog() {
|
||||
val captor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
|
||||
verify(commandQueue, atLeastOnce()).addCallback(capture(captor))
|
||||
|
||||
captor.value.requestAddTile(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, Callback())
|
||||
|
||||
verify(tileRequestDialog).setTileData(
|
||||
TileRequestDialog.TileData(TEST_APP_NAME, TEST_LABEL, icon)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun commandQueueCallback_callbackCalled() {
|
||||
`when`(qsTileHost.indexOf(CustomTile.toSpec(TEST_COMPONENT))).thenReturn(2)
|
||||
val captor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
|
||||
verify(commandQueue, atLeastOnce()).addCallback(capture(captor))
|
||||
val c = Callback()
|
||||
|
||||
captor.value.requestAddTile(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, c)
|
||||
|
||||
assertThat(c.lastAccepted).isEqualTo(TileServiceRequestController.TILE_ALREADY_ADDED)
|
||||
}
|
||||
|
||||
private class Callback : IAddTileResultCallback.Stub(), Consumer<Int> {
|
||||
var lastAccepted: Int? = null
|
||||
private set
|
||||
override fun accept(t: Int) {
|
||||
lastAccepted = t
|
||||
}
|
||||
|
||||
override fun onTileRequest(r: Int) {
|
||||
accept(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,9 @@ import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.ActivityThread;
|
||||
import android.app.ITransientNotificationCallback;
|
||||
import android.app.Notification;
|
||||
@@ -31,7 +33,11 @@ import android.compat.annotation.ChangeId;
|
||||
import android.compat.annotation.EnabledSince;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.hardware.biometrics.BiometricAuthenticator.Modality;
|
||||
import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode;
|
||||
import android.hardware.biometrics.IBiometricSysuiReceiver;
|
||||
@@ -53,6 +59,7 @@ import android.os.ResultReceiver;
|
||||
import android.os.ShellCallback;
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.NotificationStats;
|
||||
import android.service.quicksettings.TileService;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
@@ -68,6 +75,7 @@ import com.android.internal.R;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.inputmethod.SoftInputShowHideReason;
|
||||
import com.android.internal.os.TransferPipe;
|
||||
import com.android.internal.statusbar.IAddTileResultCallback;
|
||||
import com.android.internal.statusbar.IStatusBar;
|
||||
import com.android.internal.statusbar.IStatusBarService;
|
||||
import com.android.internal.statusbar.NotificationVisibility;
|
||||
@@ -123,7 +131,9 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final DeathRecipient mDeathRecipient = new DeathRecipient();
|
||||
private final ActivityManagerInternal mActivityManagerInternal;
|
||||
private final ActivityTaskManagerInternal mActivityTaskManager;
|
||||
private final PackageManagerInternal mPackageManagerInternal;
|
||||
private int mCurrentUserId;
|
||||
private boolean mTracingEnabled;
|
||||
|
||||
@@ -227,6 +237,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
|
||||
(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
|
||||
displayManager.registerDisplayListener(this, mHandler);
|
||||
mActivityTaskManager = LocalServices.getService(ActivityTaskManagerInternal.class);
|
||||
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
|
||||
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1639,6 +1651,96 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
|
||||
}
|
||||
}
|
||||
|
||||
private void checkCallingUidPackage(String packageName, int callingUid, int userId) {
|
||||
int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
|
||||
if (UserHandle.getAppId(callingUid) != UserHandle.getAppId(packageUid)) {
|
||||
throw new SecurityException("Package " + packageName
|
||||
+ " does not belong to the calling uid " + callingUid);
|
||||
}
|
||||
}
|
||||
|
||||
private ResolveInfo isComponentValidTileService(ComponentName componentName, int userId) {
|
||||
Intent intent = new Intent(TileService.ACTION_QS_TILE);
|
||||
intent.setComponent(componentName);
|
||||
ResolveInfo r = mPackageManagerInternal.resolveService(intent,
|
||||
intent.resolveTypeIfNeeded(mContext.getContentResolver()), 0, userId,
|
||||
Process.myUid());
|
||||
int enabled = mPackageManagerInternal.getComponentEnabledSetting(
|
||||
componentName, Process.myUid(), userId);
|
||||
if (r != null
|
||||
&& r.serviceInfo != null
|
||||
&& resolveEnabledComponent(r.serviceInfo.enabled, enabled)
|
||||
&& Manifest.permission.BIND_QUICK_SETTINGS_TILE.equals(r.serviceInfo.permission)) {
|
||||
return r;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean resolveEnabledComponent(boolean defaultValue, int pmResult) {
|
||||
if (pmResult == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
|
||||
return true;
|
||||
}
|
||||
if (pmResult == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
|
||||
return defaultValue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requestAddTile(
|
||||
@NonNull ComponentName componentName,
|
||||
@NonNull CharSequence label,
|
||||
@NonNull Icon icon,
|
||||
int userId,
|
||||
@NonNull IAddTileResultCallback callback
|
||||
) {
|
||||
int callingUid = Binder.getCallingUid();
|
||||
String packageName = componentName.getPackageName();
|
||||
|
||||
// Check calling user can act on behalf of current user
|
||||
mActivityManagerInternal.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
|
||||
false, ActivityManagerInternal.ALLOW_NON_FULL, "requestAddTile", packageName);
|
||||
|
||||
// Check calling uid matches package
|
||||
checkCallingUidPackage(packageName, callingUid, userId);
|
||||
|
||||
int currentUser = mActivityManagerInternal.getCurrentUserId();
|
||||
|
||||
// Check current user
|
||||
if (userId != currentUser) {
|
||||
return StatusBarManager.TILE_ADD_REQUEST_ANSWER_FAILED_NOT_CURRENT_USER;
|
||||
}
|
||||
|
||||
// We've checked that the package, component name and uid all match.
|
||||
ResolveInfo r = isComponentValidTileService(componentName, userId);
|
||||
if (r == null) {
|
||||
return StatusBarManager.TILE_ADD_REQUEST_ANSWER_FAILED_BAD_COMPONENT;
|
||||
}
|
||||
|
||||
IAddTileResultCallback proxyCallback = new IAddTileResultCallback.Stub() {
|
||||
@Override
|
||||
public void onTileRequest(int i) throws RemoteException {
|
||||
if (i == StatusBarManager.TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED) {
|
||||
i = StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED;
|
||||
}
|
||||
callback.onTileRequest(i);
|
||||
}
|
||||
};
|
||||
|
||||
CharSequence appName = r.serviceInfo.applicationInfo
|
||||
.loadLabel(mContext.getPackageManager());
|
||||
if (mBar != null) {
|
||||
try {
|
||||
mBar.requestAddTile(componentName, appName, label, icon, proxyCallback);
|
||||
return StatusBarManager.TILE_ADD_REQUEST_ANSWER_SUCCESS;
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "requestAddTile", e);
|
||||
}
|
||||
}
|
||||
return StatusBarManager.TILE_ADD_REQUEST_ANSWER_FAILED_UNKNOWN_REASON;
|
||||
}
|
||||
|
||||
public String[] getStatusBarIcons() {
|
||||
return mContext.getResources().getStringArray(R.array.config_statusBarIcons);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user