Merge changes from topic "update_mapping"

* changes:
  Rework how pinning works
  Follow prebuilt/API update
This commit is contained in:
TreeHugger Robot
2018-02-23 06:51:31 +00:00
committed by Android (Google) Code Review
7 changed files with 45 additions and 432 deletions

View File

@@ -16,17 +16,13 @@
package android.app.slice;
import android.app.slice.ISliceListener;
import android.app.slice.SliceSpec;
import android.net.Uri;
/** @hide */
interface ISliceManager {
void addSliceListener(in Uri uri, String pkg, in ISliceListener listener,
in SliceSpec[] specs);
void removeSliceListener(in Uri uri, String pkg, in ISliceListener listener);
void pinSlice(String pkg, in Uri uri, in SliceSpec[] specs);
void unpinSlice(String pkg, in Uri uri);
void pinSlice(String pkg, in Uri uri, in SliceSpec[] specs, in IBinder token);
void unpinSlice(String pkg, in Uri uri, in IBinder token);
boolean hasSliceAccess(String pkg);
SliceSpec[] getPinnedSpecs(in Uri uri, String pkg);
int checkSlicePermission(in Uri uri, String pkg, int pid, int uid);

View File

@@ -26,8 +26,10 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -73,6 +75,7 @@ public class SliceManager {
private final Context mContext;
private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup =
new ArrayMap<>();
private final IBinder mToken = new Binder();
/**
* Permission denied.
@@ -105,7 +108,6 @@ public class SliceManager {
@Deprecated
public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
@NonNull List<SliceSpec> specs) {
registerSliceCallback(uri, specs, mContext.getMainExecutor(), callback);
}
/**
@@ -114,7 +116,6 @@ public class SliceManager {
@Deprecated
public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
@NonNull List<SliceSpec> specs, Executor executor) {
registerSliceCallback(uri, specs, executor, callback);
}
/**
@@ -132,7 +133,6 @@ public class SliceManager {
*/
public void registerSliceCallback(@NonNull Uri uri, @NonNull List<SliceSpec> specs,
@NonNull SliceCallback callback) {
registerSliceCallback(uri, specs, mContext.getMainExecutor(), callback);
}
/**
@@ -150,32 +150,7 @@ public class SliceManager {
*/
public void registerSliceCallback(@NonNull Uri uri, @NonNull List<SliceSpec> specs,
@NonNull @CallbackExecutor Executor executor, @NonNull SliceCallback callback) {
try {
mService.addSliceListener(uri, mContext.getPackageName(),
getListener(uri, callback, new ISliceListener.Stub() {
@Override
public void onSliceUpdated(Slice s) throws RemoteException {
executor.execute(() -> callback.onSliceUpdated(s));
}
}), specs.toArray(new SliceSpec[specs.size()]));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private ISliceListener getListener(Uri uri, SliceCallback callback,
ISliceListener listener) {
Pair<Uri, SliceCallback> key = new Pair<>(uri, callback);
if (mListenerLookup.containsKey(key)) {
try {
mService.removeSliceListener(uri, mContext.getPackageName(),
mListenerLookup.get(key));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
mListenerLookup.put(key, listener);
return listener;
}
/**
@@ -189,12 +164,7 @@ public class SliceManager {
* @see #registerSliceCallback
*/
public void unregisterSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback) {
try {
mService.removeSliceListener(uri, mContext.getPackageName(),
mListenerLookup.remove(new Pair<>(uri, callback)));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
@@ -215,7 +185,7 @@ public class SliceManager {
public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
try {
mService.pinSlice(mContext.getPackageName(), uri,
specs.toArray(new SliceSpec[specs.size()]));
specs.toArray(new SliceSpec[specs.size()]), mToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -237,7 +207,7 @@ public class SliceManager {
*/
public void unpinSlice(@NonNull Uri uri) {
try {
mService.unpinSlice(mContext.getPackageName(), uri);
mService.unpinSlice(mContext.getPackageName(), uri, mToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@@ -160,8 +160,8 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe
mRow.addView(button);
PendingIntent pendingIntent = null;
if (rc.getContentIntent() != null) {
pendingIntent = rc.getContentIntent().getAction();
if (rc.getPrimaryAction() != null) {
pendingIntent = rc.getPrimaryAction().getAction();
}
mClickActions.put(button, pendingIntent);

View File

@@ -16,7 +16,6 @@ package com.android.server.slice;
import static android.app.slice.SliceManager.PERMISSION_GRANTED;
import android.app.slice.ISliceListener;
import android.app.slice.Slice;
import android.app.slice.SliceProvider;
import android.app.slice.SliceSpec;
@@ -106,10 +105,6 @@ public class PinnedSliceState {
setSlicePinned(false);
}
public void onChange() {
mService.getHandler().post(this::handleBind);
}
private void setSlicePinned(boolean pinned) {
synchronized (mLock) {
if (mSlicePinned == pinned) return;
@@ -122,45 +117,23 @@ public class PinnedSliceState {
}
}
public void addSliceListener(ISliceListener listener, String pkg, SliceSpec[] specs,
boolean hasPermission) {
public void pin(String pkg, SliceSpec[] specs, IBinder token) {
synchronized (mLock) {
if (mListeners.size() == 0) {
mService.listen(mUri);
}
mListeners.put(token, new ListenerInfo(token, pkg, true,
Binder.getCallingUid(), Binder.getCallingPid()));
try {
listener.asBinder().linkToDeath(mDeathRecipient, 0);
token.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
}
mListeners.put(listener.asBinder(), new ListenerInfo(listener, pkg, hasPermission,
Binder.getCallingUid(), Binder.getCallingPid()));
mergeSpecs(specs);
setSlicePinned(hasPermission);
}
}
public boolean removeSliceListener(ISliceListener listener) {
synchronized (mLock) {
listener.asBinder().unlinkToDeath(mDeathRecipient, 0);
if (mListeners.containsKey(listener.asBinder()) && mListeners.size() == 1) {
mService.unlisten(mUri);
}
mListeners.remove(listener.asBinder());
}
return !hasPinOrListener();
}
public void pin(String pkg, SliceSpec[] specs) {
synchronized (mLock) {
setSlicePinned(true);
mPinnedPkgs.add(pkg);
mergeSpecs(specs);
}
}
public boolean unpin(String pkg) {
public boolean unpin(String pkg, IBinder token) {
synchronized (mLock) {
mPinnedPkgs.remove(pkg);
token.unlinkToDeath(mDeathRecipient, 0);
mListeners.remove(token);
}
return !hasPinOrListener();
}
@@ -171,30 +144,6 @@ public class PinnedSliceState {
}
}
public void recheckPackage(String pkg) {
synchronized (mLock) {
for (int i = 0; i < mListeners.size(); i++) {
ListenerInfo info = mListeners.valueAt(i);
if (!info.hasPermission && Objects.equals(info.pkg, pkg)) {
mService.getHandler().post(() -> {
// This bind lets the app itself participate in the permission grant.
Slice s = doBind(info);
if (mService.checkAccess(info.pkg, mUri, info.callingUid, info.callingPid)
== PERMISSION_GRANTED) {
info.hasPermission = true;
setSlicePinned(true);
try {
info.listener.onSliceUpdated(s);
} catch (RemoteException e) {
checkSelfRemove();
}
}
});
}
}
}
}
@VisibleForTesting
public boolean hasPinOrListener() {
synchronized (mLock) {
@@ -213,7 +162,6 @@ public class PinnedSliceState {
private void checkSelfRemove() {
if (!hasPinOrListener()) {
// All the listeners died, remove from pinned state.
mService.unlisten(mUri);
mService.removePinnedSlice(mUri);
}
}
@@ -223,7 +171,7 @@ public class PinnedSliceState {
synchronized (mLock) {
for (int i = mListeners.size() - 1; i >= 0; i--) {
ListenerInfo l = mListeners.valueAt(i);
if (!l.listener.asBinder().isBinderAlive()) {
if (!l.token.isBinderAlive()) {
mListeners.removeAt(i);
}
}
@@ -231,62 +179,6 @@ public class PinnedSliceState {
}
}
private void handleBind() {
Slice cachedSlice = doBind(null);
synchronized (mLock) {
if (!hasPinOrListener()) return;
for (int i = mListeners.size() - 1; i >= 0; i--) {
ListenerInfo info = mListeners.valueAt(i);
Slice s = cachedSlice;
if (s == null || s.hasHint(Slice.HINT_CALLER_NEEDED)
|| !info.hasPermission) {
s = doBind(info);
}
if (s == null) {
mListeners.removeAt(i);
continue;
}
try {
info.listener.onSliceUpdated(s);
} catch (RemoteException e) {
Log.e(TAG, "Unable to notify slice " + mUri, e);
mListeners.removeAt(i);
continue;
}
}
checkSelfRemove();
}
}
private Slice doBind(ListenerInfo info) {
try (ContentProviderClient client = getClient()) {
if (client == null) return null;
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri);
extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
new ArrayList<>(Arrays.asList(mSupportedSpecs)));
if (info != null) {
extras.putString(SliceProvider.EXTRA_OVERRIDE_PKG, info.pkg);
extras.putInt(SliceProvider.EXTRA_OVERRIDE_UID, info.callingUid);
extras.putInt(SliceProvider.EXTRA_OVERRIDE_PID, info.callingPid);
}
final Bundle res;
try {
res = client.call(SliceProvider.METHOD_SLICE, null, extras);
} catch (RemoteException e) {
Log.e(TAG, "Unable to bind slice " + mUri, e);
return null;
}
if (res == null) return null;
Bundle.setDefusable(res, true);
return res.getParcelable(SliceProvider.EXTRA_SLICE);
} catch (Throwable t) {
// Calling out of the system process, make sure they don't throw anything at us.
Log.e(TAG, "Caught throwable while binding " + mUri, t);
return null;
}
}
private void handleSendPinned() {
try (ContentProviderClient client = getClient()) {
if (client == null) return;
@@ -315,15 +207,15 @@ public class PinnedSliceState {
private class ListenerInfo {
private ISliceListener listener;
private IBinder token;
private String pkg;
private boolean hasPermission;
private int callingUid;
private int callingPid;
public ListenerInfo(ISliceListener listener, String pkg, boolean hasPermission,
public ListenerInfo(IBinder token, String pkg, boolean hasPermission,
int callingUid, int callingPid) {
this.listener = listener;
this.token = token;
this.pkg = pkg;
this.hasPermission = hasPermission;
this.callingUid = callingUid;

View File

@@ -28,7 +28,6 @@ import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.ContentProviderHolder;
import android.app.IActivityManager;
import android.app.slice.ISliceListener;
import android.app.slice.ISliceManager;
import android.app.slice.SliceManager;
import android.app.slice.SliceSpec;
@@ -39,7 +38,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
@@ -52,7 +50,6 @@ import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
import android.util.Xml.Encoding;
@@ -94,7 +91,6 @@ public class SliceManagerService extends ISliceManager.Stub {
@GuardedBy("mLock")
private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>();
private final Handler mHandler;
private final ContentObserver mObserver;
@GuardedBy("mSliceAccessFile")
private final AtomicFile mSliceAccessFile;
@GuardedBy("mAccessList")
@@ -113,16 +109,6 @@ public class SliceManagerService extends ISliceManager.Stub {
mAssistUtils = new AssistUtils(context);
mHandler = new Handler(looper);
mObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
try {
getPinnedSlice(maybeAddUserId(uri, userId)).onChange();
} catch (IllegalStateException e) {
Log.e(TAG, "Received change for unpinned slice " + uri, e);
}
}
};
final File systemDir = new File(Environment.getDataDirectory(), "system");
mSliceAccessFile = new AtomicFile(new File(systemDir, "slice_access.xml"));
mAccessList = new SliceFullAccessList(mContext);
@@ -163,40 +149,19 @@ public class SliceManagerService extends ISliceManager.Stub {
/// ----- ISliceManager stuff -----
@Override
public void addSliceListener(Uri uri, String pkg, ISliceListener listener, SliceSpec[] specs)
throws RemoteException {
public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token) throws RemoteException {
verifyCaller(pkg);
enforceAccess(pkg, uri);
uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
enforceCrossUser(pkg, uri);
getOrCreatePinnedSlice(uri).addSliceListener(listener, pkg, specs,
checkAccess(pkg, uri, Binder.getCallingUid(), Binder.getCallingUid())
== PERMISSION_GRANTED);
getOrCreatePinnedSlice(uri).pin(pkg, specs, token);
}
@Override
public void removeSliceListener(Uri uri, String pkg, ISliceListener listener)
throws RemoteException {
public void unpinSlice(String pkg, Uri uri, IBinder token) throws RemoteException {
verifyCaller(pkg);
enforceAccess(pkg, uri);
uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
if (getPinnedSlice(uri).removeSliceListener(listener)) {
removePinnedSlice(uri);
}
}
@Override
public void pinSlice(String pkg, Uri uri, SliceSpec[] specs) throws RemoteException {
verifyCaller(pkg);
enforceFullAccess(pkg, "pinSlice", uri);
uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
getOrCreatePinnedSlice(uri).pin(pkg, specs);
}
@Override
public void unpinSlice(String pkg, Uri uri) throws RemoteException {
verifyCaller(pkg);
enforceFullAccess(pkg, "unpinSlice", uri);
uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
if (getPinnedSlice(uri).unpin(pkg)) {
if (getPinnedSlice(uri).unpin(pkg, token)) {
removePinnedSlice(uri);
}
}
@@ -253,11 +218,6 @@ public class SliceManagerService extends ISliceManager.Stub {
} finally {
Binder.restoreCallingIdentity(ident);
}
synchronized (mLock) {
for (PinnedSliceState p : mPinnedSlicesByUri.values()) {
p.recheckPackage(pkg);
}
}
}
// Backup/restore interface
@@ -457,21 +417,6 @@ public class SliceManagerService extends ISliceManager.Stub {
return cn.getPackageName().equals(pkg);
}
public void listen(Uri uri) {
mContext.getContentResolver().registerContentObserver(uri, true, mObserver);
}
public void unlisten(Uri uri) {
mContext.getContentResolver().unregisterContentObserver(mObserver);
synchronized (mLock) {
mPinnedSlicesByUri.forEach((u, s) -> {
if (s.isListening()) {
listen(u);
}
});
}
}
private boolean isDefaultHomeApp(String pkg, int userId) {
String defaultHome = getDefaultHome(userId);

View File

@@ -71,6 +71,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
private PinnedSliceState mPinnedSliceManager;
private IContentProvider mIContentProvider;
private ContentProvider mContentProvider;
private IBinder mToken = new Binder();
@Before
public void setup() {
@@ -108,7 +109,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
TestableLooper.get(this).processAllMessages();
// When pinned for the first time, a pinned message should be sent.
mPinnedSliceManager.pin("pkg", FIRST_SPECS);
mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
TestableLooper.get(this).processAllMessages();
verify(mIContentProvider).call(anyString(), eq(SliceProvider.METHOD_PIN), eq(null),
@@ -118,113 +119,28 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
}));
}
@Test
public void testSendPinnedOnListen() throws RemoteException {
TestableLooper.get(this).processAllMessages();
// When a listener is added for the first time, a pinned message should be sent.
ISliceListener listener = mock(ISliceListener.class);
when(listener.asBinder()).thenReturn(new Binder());
mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
true);
TestableLooper.get(this).processAllMessages();
verify(mIContentProvider).call(anyString(), eq(SliceProvider.METHOD_PIN), eq(null),
argThat(b -> {
assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
return true;
}));
}
@Test
public void testNoSendPinnedWithoutPermission() throws RemoteException {
TestableLooper.get(this).processAllMessages();
// When a listener is added for the first time, a pinned message should be sent.
ISliceListener listener = mock(ISliceListener.class);
when(listener.asBinder()).thenReturn(new Binder());
mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
false);
TestableLooper.get(this).processAllMessages();
verify(mIContentProvider, never()).call(anyString(), eq(SliceProvider.METHOD_PIN), eq(null),
any());
}
@Test
public void testSendUnpinnedOnDestroy() throws RemoteException {
TestableLooper.get(this).processAllMessages();
clearInvocations(mIContentProvider);
mPinnedSliceManager.pin("pkg", FIRST_SPECS);
mPinnedSliceManager.destroy();
TestableLooper.get(this).processAllMessages();
verify(mIContentProvider).call(anyString(), eq(SliceProvider.METHOD_UNPIN), eq(null),
argThat(b -> {
assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
return true;
}));
}
@Test
public void testPkgPin() {
assertFalse(mPinnedSliceManager.hasPinOrListener());
mPinnedSliceManager.pin("pkg", FIRST_SPECS);
mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
assertTrue(mPinnedSliceManager.hasPinOrListener());
assertTrue(mPinnedSliceManager.unpin("pkg"));
assertTrue(mPinnedSliceManager.unpin("pkg", mToken));
assertFalse(mPinnedSliceManager.hasPinOrListener());
}
@Test
public void testMultiPkgPin() {
IBinder t2 = new Binder();
assertFalse(mPinnedSliceManager.hasPinOrListener());
mPinnedSliceManager.pin("pkg", FIRST_SPECS);
mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
assertTrue(mPinnedSliceManager.hasPinOrListener());
mPinnedSliceManager.pin("pkg2", FIRST_SPECS);
mPinnedSliceManager.pin("pkg2", FIRST_SPECS, t2);
assertFalse(mPinnedSliceManager.unpin("pkg"));
assertTrue(mPinnedSliceManager.unpin("pkg2"));
assertFalse(mPinnedSliceManager.hasPinOrListener());
}
@Test
public void testListenerPin() {
ISliceListener listener = mock(ISliceListener.class);
when(listener.asBinder()).thenReturn(new Binder());
assertFalse(mPinnedSliceManager.hasPinOrListener());
mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
true);
assertTrue(mPinnedSliceManager.hasPinOrListener());
assertTrue(mPinnedSliceManager.removeSliceListener(listener));
assertFalse(mPinnedSliceManager.hasPinOrListener());
}
@Test
public void testMultiListenerPin() {
ISliceListener listener = mock(ISliceListener.class);
Binder value = new Binder();
when(listener.asBinder()).thenReturn(value);
ISliceListener listener2 = mock(ISliceListener.class);
Binder value2 = new Binder();
when(listener2.asBinder()).thenReturn(value2);
assertFalse(mPinnedSliceManager.hasPinOrListener());
mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
true);
assertTrue(mPinnedSliceManager.hasPinOrListener());
mPinnedSliceManager.addSliceListener(listener2, mContext.getPackageName(), FIRST_SPECS,
true);
assertFalse(mPinnedSliceManager.removeSliceListener(listener));
assertTrue(mPinnedSliceManager.removeSliceListener(listener2));
assertFalse(mPinnedSliceManager.unpin("pkg", mToken));
assertTrue(mPinnedSliceManager.unpin("pkg2", t2));
assertFalse(mPinnedSliceManager.hasPinOrListener());
}
@@ -236,8 +152,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
when(listener.asBinder()).thenReturn(binder);
assertFalse(mPinnedSliceManager.hasPinOrListener());
mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
true);
mPinnedSliceManager.pin(mContext.getPackageName(), FIRST_SPECS, binder);
assertTrue(mPinnedSliceManager.hasPinOrListener());
ArgumentCaptor<DeathRecipient> arg = ArgumentCaptor.forClass(DeathRecipient.class);
@@ -246,79 +161,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase {
when(binder.isBinderAlive()).thenReturn(false);
arg.getValue().binderDied();
verify(mSliceService).unlisten(eq(TEST_URI));
verify(mSliceService).removePinnedSlice(eq(TEST_URI));
assertFalse(mPinnedSliceManager.hasPinOrListener());
}
@Test
public void testPkgListenerPin() {
ISliceListener listener = mock(ISliceListener.class);
when(listener.asBinder()).thenReturn(new Binder());
assertFalse(mPinnedSliceManager.hasPinOrListener());
mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
true);
assertTrue(mPinnedSliceManager.hasPinOrListener());
mPinnedSliceManager.pin("pkg", FIRST_SPECS);
assertFalse(mPinnedSliceManager.removeSliceListener(listener));
assertTrue(mPinnedSliceManager.unpin("pkg"));
assertFalse(mPinnedSliceManager.hasPinOrListener());
}
@Test
public void testBind() throws RemoteException {
TestableLooper.get(this).processAllMessages();
clearInvocations(mIContentProvider);
ISliceListener listener = mock(ISliceListener.class);
when(listener.asBinder()).thenReturn(new Binder());
Slice s = new Slice.Builder(TEST_URI).build();
Bundle b = new Bundle();
b.putParcelable(SliceProvider.EXTRA_SLICE, s);
when(mIContentProvider.call(anyString(), eq(SliceProvider.METHOD_SLICE), eq(null),
any())).thenReturn(b);
assertFalse(mPinnedSliceManager.hasPinOrListener());
mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
true);
mPinnedSliceManager.onChange();
TestableLooper.get(this).processAllMessages();
verify(mIContentProvider).call(anyString(), eq(SliceProvider.METHOD_SLICE), eq(null),
argThat(bundle -> {
assertEquals(TEST_URI, bundle.getParcelable(SliceProvider.EXTRA_BIND_URI));
return true;
}));
verify(listener).onSliceUpdated(eq(s));
}
@Test
public void testRecheckPackage() throws RemoteException {
TestableLooper.get(this).processAllMessages();
ISliceListener listener = mock(ISliceListener.class);
when(listener.asBinder()).thenReturn(new Binder());
mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS,
false);
TestableLooper.get(this).processAllMessages();
verify(mIContentProvider, never()).call(anyString(), eq(SliceProvider.METHOD_PIN), eq(null),
any());
when(mSliceService.checkAccess(any(), any(), anyInt(), anyInt()))
.thenReturn(PERMISSION_GRANTED);
mPinnedSliceManager.recheckPackage(mContext.getPackageName());
TestableLooper.get(this).processAllMessages();
verify(mIContentProvider).call(anyString(), eq(SliceProvider.METHOD_PIN), eq(null),
argThat(b -> {
assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
return true;
}));
}
}

View File

@@ -32,6 +32,8 @@ import android.app.slice.ISliceListener;
import android.app.slice.SliceSpec;
import android.content.pm.PackageManagerInternal;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -59,6 +61,7 @@ public class SliceManagerServiceTest extends UiServiceTestCase {
private SliceManagerService mService;
private PinnedSliceState mCreatedSliceState;
private IBinder mToken = new Binder();
@Before
public void setup() {
@@ -76,44 +79,12 @@ public class SliceManagerServiceTest extends UiServiceTestCase {
LocalServices.removeServiceForTest(PackageManagerInternal.class);
}
@Test
public void testAddListenerCreatesPinned() throws RemoteException {
mService.addSliceListener(TEST_URI, "pkg", mock(ISliceListener.class), EMPTY_SPECS);
verify(mService, times(1)).createPinnedSlice(eq(TEST_URI));
}
@Test
public void testAddListenerCreatesOnePinned() throws RemoteException {
mService.addSliceListener(TEST_URI, "pkg", mock(ISliceListener.class), EMPTY_SPECS);
mService.addSliceListener(TEST_URI, "pkg", mock(ISliceListener.class), EMPTY_SPECS);
verify(mService, times(1)).createPinnedSlice(eq(TEST_URI));
}
@Test
public void testRemoveListenerDestroysPinned() throws RemoteException {
ISliceListener listener = mock(ISliceListener.class);
mService.addSliceListener(TEST_URI, "pkg", listener, EMPTY_SPECS);
when(mCreatedSliceState.removeSliceListener(eq(listener))).thenReturn(false);
mService.removeSliceListener(TEST_URI, "pkg", listener);
verify(mCreatedSliceState, never()).destroy();
when(mCreatedSliceState.removeSliceListener(eq(listener))).thenReturn(true);
mService.removeSliceListener(TEST_URI, "pkg", listener);
verify(mCreatedSliceState).destroy();
}
@Test(expected = IllegalStateException.class)
public void testUnrecognizedThrows() throws RemoteException {
mService.removeSliceListener(TEST_URI, "pkg", mock(ISliceListener.class));
}
@Test
public void testAddPinCreatesPinned() throws RemoteException {
doReturn("pkg").when(mService).getDefaultHome(anyInt());
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS);
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS);
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
verify(mService, times(1)).createPinnedSlice(eq(TEST_URI));
}
@@ -121,15 +92,11 @@ public class SliceManagerServiceTest extends UiServiceTestCase {
public void testRemovePinDestroysPinned() throws RemoteException {
doReturn("pkg").when(mService).getDefaultHome(anyInt());
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS);
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
when(mCreatedSliceState.unpin(eq("pkg"))).thenReturn(false);
mService.unpinSlice("pkg", TEST_URI);
when(mCreatedSliceState.unpin(eq("pkg"), eq(mToken))).thenReturn(false);
mService.unpinSlice("pkg", TEST_URI, mToken);
verify(mCreatedSliceState, never()).destroy();
when(mCreatedSliceState.unpin(eq("pkg"))).thenReturn(true);
mService.unpinSlice("pkg", TEST_URI);
verify(mCreatedSliceState).destroy();
}
}