CompatChange to use hidden API in TileService
This adds a new hidden API in StatusBarManager for active TileService#requestListeningState. That way, we can verify that the calling package is the same as the one belonging to the tile. This is enforced using CompatChange for apps that target T+. Test: atest StatusBarManagerServiceTest Test: atest TileServicesTest Test: atest CtsSystemUiHostTestCases Test: sample app targetting T Fixes: 172251878 Change-Id: Ic9b8aca55dcfc2b32b018a5308f19e49a933c4c9
This commit is contained in:
@@ -843,6 +843,24 @@ public class StatusBarManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an active {@link android.service.quicksettings.TileService} to listening state
|
||||
*
|
||||
* The {@code componentName}'s package must match the calling package.
|
||||
*
|
||||
* @param componentName the tile to set into listening state
|
||||
* @see android.service.quicksettings.TileService#requestListeningState
|
||||
* @hide
|
||||
*/
|
||||
public void requestTileServiceListeningState(@NonNull ComponentName componentName) {
|
||||
Objects.requireNonNull(componentName);
|
||||
try {
|
||||
getService().requestTileServiceListeningState(componentName, mContext.getUserId());
|
||||
} catch (RemoteException ex) {
|
||||
throw ex.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to the user to add a {@link android.service.quicksettings.TileService}
|
||||
* to the set of current QS tiles.
|
||||
|
||||
@@ -15,18 +15,19 @@
|
||||
*/
|
||||
package android.service.quicksettings;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SdkConstant;
|
||||
import android.annotation.SdkConstant.SdkConstantType;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.TestApi;
|
||||
import android.app.Dialog;
|
||||
import android.app.Service;
|
||||
import android.app.StatusBarManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
@@ -146,13 +147,6 @@ public class TileService extends Service {
|
||||
public static final String META_DATA_TOGGLEABLE_TILE =
|
||||
"android.service.quicksettings.TOGGLEABLE_TILE";
|
||||
|
||||
/**
|
||||
* Used to notify SysUI that Listening has be requested.
|
||||
* @hide
|
||||
*/
|
||||
public static final String ACTION_REQUEST_LISTENING =
|
||||
"android.service.quicksettings.action.REQUEST_LISTENING";
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -482,14 +476,24 @@ public class TileService extends Service {
|
||||
*
|
||||
* This method is only applicable to tiles that have {@link #META_DATA_ACTIVE_TILE} defined
|
||||
* as true on their TileService Manifest declaration, and will do nothing otherwise.
|
||||
*
|
||||
* For apps targeting {@link Build.VERSION_CODES#TIRAMISU} or later, this call may throw
|
||||
* the following exceptions if the request is not valid:
|
||||
* <ul>
|
||||
* <li> {@link NullPointerException} if {@code component} is {@code null}.</li>
|
||||
* <li> {@link SecurityException} if the package of {@code component} does not match
|
||||
* the calling package or if the calling user cannot act on behalf of the user from the
|
||||
* {@code context}.</li>
|
||||
* <li> {@link IllegalArgumentException} if the user of the {@code context} is not the
|
||||
* current user.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final void requestListeningState(Context context, ComponentName component) {
|
||||
final ComponentName sysuiComponent = ComponentName.unflattenFromString(
|
||||
context.getResources().getString(
|
||||
com.android.internal.R.string.config_systemUIServiceComponent));
|
||||
Intent intent = new Intent(ACTION_REQUEST_LISTENING);
|
||||
intent.putExtra(Intent.EXTRA_COMPONENT_NAME, component);
|
||||
intent.setPackage(sysuiComponent.getPackageName());
|
||||
context.sendBroadcast(intent, Manifest.permission.BIND_QUICK_SETTINGS_TILE);
|
||||
StatusBarManager sbm = context.getSystemService(StatusBarManager.class);
|
||||
if (sbm == null) {
|
||||
Log.e(TAG, "No StatusBarManager service found");
|
||||
return;
|
||||
}
|
||||
sbm.requestTileServiceListeningState(component);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,6 +297,11 @@ oneway interface IStatusBar
|
||||
*/
|
||||
void runGcForTest();
|
||||
|
||||
/**
|
||||
* Send a request to SystemUI to put a given active tile in listening state
|
||||
*/
|
||||
void requestTileServiceListeningState(in ComponentName componentName);
|
||||
|
||||
void requestAddTile(in ComponentName componentName, in CharSequence appName, in CharSequence label, in Icon icon, in IAddTileResultCallback callback);
|
||||
void cancelRequestAddTile(in String packageName);
|
||||
|
||||
|
||||
@@ -171,6 +171,11 @@ interface IStatusBarService
|
||||
*/
|
||||
void suppressAmbientDisplay(boolean suppress);
|
||||
|
||||
/**
|
||||
* Send a request to SystemUI to put a given active tile in listening state
|
||||
*/
|
||||
void requestTileServiceListeningState(in ComponentName componentName, int userId);
|
||||
|
||||
void requestAddTile(in ComponentName componentName, in CharSequence label, in Icon icon, int userId, in IAddTileResultCallback callback);
|
||||
void cancelRequestAddTile(in String packageName);
|
||||
|
||||
|
||||
@@ -15,11 +15,8 @@
|
||||
*/
|
||||
package com.android.systemui.qs.external;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Icon;
|
||||
@@ -30,10 +27,10 @@ import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.service.quicksettings.IQSService;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.service.quicksettings.TileService;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.statusbar.StatusBarIcon;
|
||||
@@ -41,6 +38,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.qs.QSTileHost;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.statusbar.CommandQueue;
|
||||
import com.android.systemui.statusbar.phone.StatusBarIconController;
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||
|
||||
@@ -69,6 +67,7 @@ public class TileServices extends IQSService.Stub {
|
||||
private final QSTileHost mHost;
|
||||
private final KeyguardStateController mKeyguardStateController;
|
||||
private final BroadcastDispatcher mBroadcastDispatcher;
|
||||
private final CommandQueue mCommandQueue;
|
||||
private final UserTracker mUserTracker;
|
||||
|
||||
private int mMaxBound = DEFAULT_MAX_BOUND;
|
||||
@@ -79,7 +78,8 @@ public class TileServices extends IQSService.Stub {
|
||||
@Main Provider<Handler> handlerProvider,
|
||||
BroadcastDispatcher broadcastDispatcher,
|
||||
UserTracker userTracker,
|
||||
KeyguardStateController keyguardStateController) {
|
||||
KeyguardStateController keyguardStateController,
|
||||
CommandQueue commandQueue) {
|
||||
mHost = host;
|
||||
mKeyguardStateController = keyguardStateController;
|
||||
mContext = mHost.getContext();
|
||||
@@ -87,12 +87,8 @@ public class TileServices extends IQSService.Stub {
|
||||
mHandlerProvider = handlerProvider;
|
||||
mMainHandler = mHandlerProvider.get();
|
||||
mUserTracker = userTracker;
|
||||
mBroadcastDispatcher.registerReceiver(
|
||||
mRequestListeningReceiver,
|
||||
new IntentFilter(TileService.ACTION_REQUEST_LISTENING),
|
||||
null, // Use the default Executor
|
||||
UserHandle.ALL
|
||||
);
|
||||
mCommandQueue = commandQueue;
|
||||
mCommandQueue.addCallback(mRequestListeningCallback);
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
@@ -354,21 +350,14 @@ public class TileServices extends IQSService.Stub {
|
||||
public void destroy() {
|
||||
synchronized (mServices) {
|
||||
mServices.values().forEach(service -> service.handleDestroy());
|
||||
mBroadcastDispatcher.unregisterReceiver(mRequestListeningReceiver);
|
||||
}
|
||||
mCommandQueue.removeCallback(mRequestListeningCallback);
|
||||
}
|
||||
|
||||
private final BroadcastReceiver mRequestListeningReceiver = new BroadcastReceiver() {
|
||||
private final CommandQueue.Callbacks mRequestListeningCallback = new CommandQueue.Callbacks() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (TileService.ACTION_REQUEST_LISTENING.equals(intent.getAction())) {
|
||||
try {
|
||||
ComponentName c = intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME);
|
||||
requestListening(c);
|
||||
} catch (ClassCastException ex) {
|
||||
Log.e(TAG, "Bad component name", ex);
|
||||
}
|
||||
}
|
||||
public void requestTileServiceListeningState(@NonNull ComponentName componentName) {
|
||||
mMainHandler.post(() -> requestListening(componentName));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -163,6 +163,7 @@ public class CommandQueue extends IStatusBar.Stub implements
|
||||
private static final int MSG_MEDIA_TRANSFER_RECEIVER_STATE = 65 << MSG_SHIFT;
|
||||
private static final int MSG_REGISTER_NEARBY_MEDIA_DEVICE_PROVIDER = 66 << MSG_SHIFT;
|
||||
private static final int MSG_UNREGISTER_NEARBY_MEDIA_DEVICE_PROVIDER = 67 << MSG_SHIFT;
|
||||
private static final int MSG_TILE_SERVICE_REQUEST_LISTENING_STATE = 68 << MSG_SHIFT;
|
||||
|
||||
public static final int FLAG_EXCLUDE_NONE = 0;
|
||||
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
|
||||
@@ -432,6 +433,11 @@ public class CommandQueue extends IStatusBar.Stub implements
|
||||
*/
|
||||
default void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {}
|
||||
|
||||
/**
|
||||
* @see IStatusBar#requestTileServiceListeningState
|
||||
*/
|
||||
default void requestTileServiceListeningState(@NonNull ComponentName componentName) {}
|
||||
|
||||
/**
|
||||
* @see IStatusBar#requestAddTile
|
||||
*/
|
||||
@@ -1189,6 +1195,12 @@ public class CommandQueue extends IStatusBar.Stub implements
|
||||
GcUtils.runGcAndFinalizersSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestTileServiceListeningState(@NonNull ComponentName componentName) {
|
||||
mHandler.obtainMessage(MSG_TILE_SERVICE_REQUEST_LISTENING_STATE, componentName)
|
||||
.sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestAddTile(
|
||||
@NonNull ComponentName componentName,
|
||||
@@ -1686,6 +1698,12 @@ public class CommandQueue extends IStatusBar.Stub implements
|
||||
mCallbacks.get(i).unregisterNearbyMediaDevicesProvider(provider);
|
||||
}
|
||||
break;
|
||||
case MSG_TILE_SERVICE_REQUEST_LISTENING_STATE:
|
||||
ComponentName component = (ComponentName) msg.obj;
|
||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||
mCallbacks.get(i).requestTileServiceListeningState(component);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,20 +20,18 @@ import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.service.quicksettings.TileService;
|
||||
import android.service.quicksettings.IQSTileService;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
@@ -48,6 +46,7 @@ import com.android.systemui.qs.logging.QSLogger;
|
||||
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.shared.plugins.PluginManager;
|
||||
import com.android.systemui.statusbar.CommandQueue;
|
||||
import com.android.systemui.statusbar.phone.AutoTileManager;
|
||||
import com.android.systemui.statusbar.phone.CentralSurfaces;
|
||||
import com.android.systemui.statusbar.phone.StatusBarIconController;
|
||||
@@ -75,12 +74,17 @@ import javax.inject.Provider;
|
||||
public class TileServicesTest extends SysuiTestCase {
|
||||
private static int NUM_FAKES = TileServices.DEFAULT_MAX_BOUND * 2;
|
||||
|
||||
private static final ComponentName TEST_COMPONENT =
|
||||
ComponentName.unflattenFromString("pkg/.cls");
|
||||
|
||||
private TileServices mTileService;
|
||||
private TestableLooper mTestableLooper;
|
||||
private ArrayList<TileServiceManager> mManagers;
|
||||
@Mock
|
||||
private BroadcastDispatcher mBroadcastDispatcher;
|
||||
@Mock
|
||||
private CommandQueue mCommandQueue;
|
||||
@Mock
|
||||
private StatusBarIconController mStatusBarIconController;
|
||||
@Mock
|
||||
private QSFactoryImpl mQSFactory;
|
||||
@@ -146,7 +150,7 @@ public class TileServicesTest extends SysuiTestCase {
|
||||
mTileServiceRequestControllerBuilder,
|
||||
mTileLifecycleManagerFactory);
|
||||
mTileService = new TestTileServices(host, provider, mBroadcastDispatcher,
|
||||
mUserTracker, mKeyguardStateController);
|
||||
mUserTracker, mKeyguardStateController, mCommandQueue);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -156,24 +160,6 @@ public class TileServicesTest extends SysuiTestCase {
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActiveTileListenerRegisteredOnAllUsers() {
|
||||
ArgumentCaptor<IntentFilter> captor = ArgumentCaptor.forClass(IntentFilter.class);
|
||||
verify(mBroadcastDispatcher).registerReceiver(any(), captor.capture(), any(), eq(
|
||||
UserHandle.ALL));
|
||||
assertTrue(captor.getValue().hasAction(TileService.ACTION_REQUEST_LISTENING));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadComponentName_doesntCrash() {
|
||||
ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
|
||||
verify(mBroadcastDispatcher).registerReceiver(captor.capture(), any(), any(), eq(
|
||||
UserHandle.ALL));
|
||||
Intent intent = new Intent(TileService.ACTION_REQUEST_LISTENING)
|
||||
.putExtra(Intent.EXTRA_COMPONENT_NAME, "abc");
|
||||
captor.getValue().onReceive(mContext, intent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecalculateBindAllowance() {
|
||||
// Add some fake tiles.
|
||||
@@ -230,11 +216,36 @@ public class TileServicesTest extends SysuiTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterCommand() {
|
||||
verify(mCommandQueue).addCallback(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestListeningStatusCommand() throws RemoteException {
|
||||
ArgumentCaptor<CommandQueue.Callbacks> captor =
|
||||
ArgumentCaptor.forClass(CommandQueue.Callbacks.class);
|
||||
verify(mCommandQueue).addCallback(captor.capture());
|
||||
|
||||
CustomTile mockTile = mock(CustomTile.class);
|
||||
when(mockTile.getComponent()).thenReturn(TEST_COMPONENT);
|
||||
|
||||
TileServiceManager manager = mTileService.getTileWrapper(mockTile);
|
||||
when(manager.isActiveTile()).thenReturn(true);
|
||||
when(manager.getTileService()).thenReturn(mock(IQSTileService.class));
|
||||
|
||||
captor.getValue().requestTileServiceListeningState(TEST_COMPONENT);
|
||||
mTestableLooper.processAllMessages();
|
||||
verify(manager).setBindRequested(true);
|
||||
verify(manager.getTileService()).onStartListening();
|
||||
}
|
||||
|
||||
private class TestTileServices extends TileServices {
|
||||
TestTileServices(QSTileHost host, Provider<Handler> handlerProvider,
|
||||
BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
|
||||
KeyguardStateController keyguardStateController) {
|
||||
super(host, handlerProvider, broadcastDispatcher, userTracker, keyguardStateController);
|
||||
KeyguardStateController keyguardStateController, CommandQueue commandQueue) {
|
||||
super(host, handlerProvider, broadcastDispatcher, userTracker, keyguardStateController,
|
||||
commandQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -36,6 +36,7 @@ import android.app.Notification;
|
||||
import android.app.StatusBarManager;
|
||||
import android.app.compat.CompatChanges;
|
||||
import android.compat.annotation.ChangeId;
|
||||
import android.compat.annotation.EnabledAfter;
|
||||
import android.compat.annotation.EnabledSince;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -135,6 +136,17 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
|
||||
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
|
||||
private static final long LOCK_DOWN_COLLAPSE_STATUS_BAR = 173031413L;
|
||||
|
||||
/**
|
||||
* In apps targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or higher, calling
|
||||
* {@link android.service.quicksettings.TileService#requestListeningState} will check that the
|
||||
* calling package (uid) and the package of the target {@link android.content.ComponentName}
|
||||
* match. It'll also make sure that the context used can take actions on behalf of the current
|
||||
* user.
|
||||
*/
|
||||
@ChangeId
|
||||
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2)
|
||||
static final long REQUEST_LISTENING_MUST_MATCH_PACKAGE = 172251878L;
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
private final Handler mHandler = new Handler();
|
||||
@@ -1775,6 +1787,42 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestTileServiceListeningState(
|
||||
@NonNull ComponentName componentName,
|
||||
int userId
|
||||
) {
|
||||
int callingUid = Binder.getCallingUid();
|
||||
String packageName = componentName.getPackageName();
|
||||
|
||||
boolean mustPerformChecks = CompatChanges.isChangeEnabled(
|
||||
REQUEST_LISTENING_MUST_MATCH_PACKAGE, callingUid);
|
||||
|
||||
if (mustPerformChecks) {
|
||||
// Check calling user can act on behalf of current user
|
||||
userId = mActivityManagerInternal.handleIncomingUser(Binder.getCallingPid(), callingUid,
|
||||
userId, false, ActivityManagerInternal.ALLOW_NON_FULL,
|
||||
"requestTileServiceListeningState", packageName);
|
||||
|
||||
// Check calling uid matches package
|
||||
checkCallingUidPackage(packageName, callingUid, userId);
|
||||
|
||||
int currentUser = mActivityManagerInternal.getCurrentUserId();
|
||||
|
||||
// Check current user
|
||||
if (userId != currentUser) {
|
||||
throw new IllegalArgumentException("User " + userId + " is not the current user.");
|
||||
}
|
||||
}
|
||||
if (mBar != null) {
|
||||
try {
|
||||
mBar.requestTileServiceListeningState(componentName);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "requestTileServiceListeningState", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestAddTile(
|
||||
@NonNull ComponentName componentName,
|
||||
|
||||
@@ -23,9 +23,11 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.anyLong;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.mockito.Mockito.argThat;
|
||||
import static org.mockito.Mockito.eq;
|
||||
@@ -37,6 +39,7 @@ import static org.mockito.Mockito.when;
|
||||
import android.Manifest;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.StatusBarManager;
|
||||
import android.compat.testing.PlatformCompatChangeRule;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.om.IOverlayManager;
|
||||
@@ -62,10 +65,13 @@ import com.android.server.LocalServices;
|
||||
import com.android.server.policy.GlobalActionsProvider;
|
||||
import com.android.server.wm.ActivityTaskManagerInternal;
|
||||
|
||||
import libcore.junit.util.compat.CoreCompatChangeRule;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
@@ -73,6 +79,7 @@ import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class StatusBarManagerServiceTest {
|
||||
@@ -88,6 +95,9 @@ public class StatusBarManagerServiceTest {
|
||||
public final TestableContext mContext =
|
||||
new NoBroadcastContextWrapper(InstrumentationRegistry.getContext());
|
||||
|
||||
@Rule
|
||||
public TestRule mCompatChangeRule = new PlatformCompatChangeRule();
|
||||
|
||||
@Mock
|
||||
private ActivityTaskManagerInternal mActivityTaskManagerInternal;
|
||||
@Mock
|
||||
@@ -127,6 +137,7 @@ public class StatusBarManagerServiceTest {
|
||||
|
||||
when(mMockStatusBar.asBinder()).thenReturn(mMockStatusBar);
|
||||
when(mApplicationInfo.loadLabel(any())).thenReturn(APP_NAME);
|
||||
mockHandleIncomingUser();
|
||||
|
||||
mStatusBarManagerService = new StatusBarManagerService(mContext);
|
||||
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
|
||||
@@ -141,6 +152,80 @@ public class StatusBarManagerServiceTest {
|
||||
mIcon = Icon.createWithResource(mContext, android.R.drawable.btn_plus);
|
||||
}
|
||||
|
||||
@Test
|
||||
@CoreCompatChangeRule.EnableCompatChanges(
|
||||
{StatusBarManagerService.REQUEST_LISTENING_MUST_MATCH_PACKAGE})
|
||||
public void testRequestActive_changeEnabled_OKCall() throws RemoteException {
|
||||
int user = 0;
|
||||
mockEverything(user);
|
||||
mStatusBarManagerService.requestTileServiceListeningState(TEST_COMPONENT, user);
|
||||
|
||||
verify(mMockStatusBar).requestTileServiceListeningState(TEST_COMPONENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@CoreCompatChangeRule.EnableCompatChanges(
|
||||
{StatusBarManagerService.REQUEST_LISTENING_MUST_MATCH_PACKAGE})
|
||||
public void testRequestActive_changeEnabled_differentPackage_fail() throws RemoteException {
|
||||
when(mPackageManagerInternal.getPackageUid(TEST_PACKAGE, 0L, mContext.getUserId()))
|
||||
.thenReturn(Binder.getCallingUid() + 1);
|
||||
try {
|
||||
mStatusBarManagerService.requestTileServiceListeningState(TEST_COMPONENT, 0);
|
||||
fail("Should cause security exception");
|
||||
} catch (SecurityException e) { }
|
||||
verify(mMockStatusBar, never()).requestTileServiceListeningState(TEST_COMPONENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@CoreCompatChangeRule.EnableCompatChanges(
|
||||
{StatusBarManagerService.REQUEST_LISTENING_MUST_MATCH_PACKAGE})
|
||||
public void testRequestActive_changeEnabled_notCurrentUser_fail() throws RemoteException {
|
||||
mockUidCheck();
|
||||
int user = 0;
|
||||
mockCurrentUserCheck(user);
|
||||
try {
|
||||
mStatusBarManagerService.requestTileServiceListeningState(TEST_COMPONENT, user + 1);
|
||||
fail("Should cause illegal argument exception");
|
||||
} catch (IllegalArgumentException e) { }
|
||||
|
||||
// Do not call into SystemUI
|
||||
verify(mMockStatusBar, never()).requestTileServiceListeningState(TEST_COMPONENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@CoreCompatChangeRule.DisableCompatChanges(
|
||||
{StatusBarManagerService.REQUEST_LISTENING_MUST_MATCH_PACKAGE})
|
||||
public void testRequestActive_changeDisabled_pass() throws RemoteException {
|
||||
int user = 0;
|
||||
mockEverything(user);
|
||||
mStatusBarManagerService.requestTileServiceListeningState(TEST_COMPONENT, user);
|
||||
|
||||
verify(mMockStatusBar).requestTileServiceListeningState(TEST_COMPONENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@CoreCompatChangeRule.DisableCompatChanges(
|
||||
{StatusBarManagerService.REQUEST_LISTENING_MUST_MATCH_PACKAGE})
|
||||
public void testRequestActive_changeDisabled_differentPackage_pass() throws RemoteException {
|
||||
when(mPackageManagerInternal.getPackageUid(TEST_PACKAGE, 0L, mContext.getUserId()))
|
||||
.thenReturn(Binder.getCallingUid() + 1);
|
||||
mStatusBarManagerService.requestTileServiceListeningState(TEST_COMPONENT, 0);
|
||||
|
||||
verify(mMockStatusBar).requestTileServiceListeningState(TEST_COMPONENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@CoreCompatChangeRule.DisableCompatChanges(
|
||||
{StatusBarManagerService.REQUEST_LISTENING_MUST_MATCH_PACKAGE})
|
||||
public void testRequestActive_changeDisabled_notCurrentUser_pass() throws RemoteException {
|
||||
mockUidCheck();
|
||||
int user = 0;
|
||||
mockCurrentUserCheck(user);
|
||||
mStatusBarManagerService.requestTileServiceListeningState(TEST_COMPONENT, user + 1);
|
||||
|
||||
verify(mMockStatusBar).requestTileServiceListeningState(TEST_COMPONENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleIncomingUserCalled() {
|
||||
int fakeUser = 17;
|
||||
@@ -252,7 +337,7 @@ public class StatusBarManagerServiceTest {
|
||||
mockCurrentUserCheck(user);
|
||||
IntentMatcher im = new IntentMatcher(
|
||||
new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT));
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0),
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0L),
|
||||
eq(user), anyInt())).thenReturn(null);
|
||||
|
||||
Callback callback = new Callback();
|
||||
@@ -272,7 +357,7 @@ public class StatusBarManagerServiceTest {
|
||||
|
||||
IntentMatcher im = new IntentMatcher(
|
||||
new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT));
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0),
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0L),
|
||||
eq(user), anyInt())).thenReturn(r);
|
||||
when(mPackageManagerInternal.getComponentEnabledSetting(TEST_COMPONENT,
|
||||
Binder.getCallingUid(), user)).thenReturn(
|
||||
@@ -294,7 +379,7 @@ public class StatusBarManagerServiceTest {
|
||||
|
||||
IntentMatcher im = new IntentMatcher(
|
||||
new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT));
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0),
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0L),
|
||||
eq(user), anyInt())).thenReturn(r);
|
||||
when(mPackageManagerInternal.getComponentEnabledSetting(TEST_COMPONENT,
|
||||
Binder.getCallingUid(), user)).thenReturn(
|
||||
@@ -318,7 +403,7 @@ public class StatusBarManagerServiceTest {
|
||||
|
||||
IntentMatcher im = new IntentMatcher(
|
||||
new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT));
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0),
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0L),
|
||||
eq(user), anyInt())).thenReturn(r);
|
||||
when(mPackageManagerInternal.getComponentEnabledSetting(TEST_COMPONENT,
|
||||
Binder.getCallingUid(), user)).thenReturn(
|
||||
@@ -342,7 +427,7 @@ public class StatusBarManagerServiceTest {
|
||||
|
||||
IntentMatcher im = new IntentMatcher(
|
||||
new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT));
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0),
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0L),
|
||||
eq(user), anyInt())).thenReturn(r);
|
||||
when(mPackageManagerInternal.getComponentEnabledSetting(TEST_COMPONENT,
|
||||
Binder.getCallingUid(), user)).thenReturn(
|
||||
@@ -641,7 +726,7 @@ public class StatusBarManagerServiceTest {
|
||||
}
|
||||
|
||||
private void mockUidCheck(String packageName) {
|
||||
when(mPackageManagerInternal.getPackageUid(eq(packageName), anyInt(), anyInt()))
|
||||
when(mPackageManagerInternal.getPackageUid(eq(packageName), anyLong(), anyInt()))
|
||||
.thenReturn(Binder.getCallingUid());
|
||||
}
|
||||
|
||||
@@ -667,7 +752,7 @@ public class StatusBarManagerServiceTest {
|
||||
|
||||
IntentMatcher im = new IntentMatcher(
|
||||
new Intent(TileService.ACTION_QS_TILE).setComponent(componentName));
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0),
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0L),
|
||||
eq(user), anyInt())).thenReturn(r);
|
||||
when(mPackageManagerInternal.getComponentEnabledSetting(componentName,
|
||||
Binder.getCallingUid(), user)).thenReturn(
|
||||
@@ -679,6 +764,15 @@ public class StatusBarManagerServiceTest {
|
||||
PROCESS_STATE_TOP);
|
||||
}
|
||||
|
||||
private void mockHandleIncomingUser() {
|
||||
when(mActivityManagerInternal.handleIncomingUser(anyInt(), anyInt(), anyInt(), anyBoolean(),
|
||||
anyInt(), anyString(), anyString())).thenAnswer(
|
||||
(Answer<Integer>) invocation -> {
|
||||
return invocation.getArgument(2); // same user
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void mockEverything(int user) {
|
||||
mockUidCheck();
|
||||
mockCurrentUserCheck(user);
|
||||
|
||||
Reference in New Issue
Block a user