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:
Fabian Kozynski
2021-08-18 10:58:38 -04:00
parent 6b69f7799b
commit 1b6920d2ea
11 changed files with 408 additions and 11 deletions

View File

@@ -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";

View File

@@ -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";

View File

@@ -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));
}
}
}

View File

@@ -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";

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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)
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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);
}