Merge "Add API for requesting add TileService"

This commit is contained in:
Fabian Kozynski
2021-09-09 17:35:26 +00:00
committed by Android (Google) Code Review
11 changed files with 408 additions and 11 deletions

View File

@@ -6694,6 +6694,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 {
@@ -10696,6 +10706,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

@@ -2353,7 +2353,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

@@ -4295,14 +4295,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;
@@ -157,4 +159,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;
@@ -149,6 +151,7 @@ public class CommandQueue extends IStatusBar.Stub implements
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;
@@ -403,6 +406,16 @@ public class CommandQueue extends IStatusBar.Stub implements
* @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) {
@@ -1108,6 +1121,23 @@ public class CommandQueue extends IStatusBar.Stub implements
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);
@@ -1481,6 +1511,19 @@ public class CommandQueue extends IStatusBar.Stub implements
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
@@ -1640,6 +1652,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);
}