Merge changes from topic "update_mapping"
* changes: Rework how pinning works Follow prebuilt/API update
This commit is contained in:
committed by
Android (Google) Code Review
commit
2ca8fcec2d
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user