Merge "Add async version of "canonicalize""
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index e1942da..bd3298c 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -580,6 +580,15 @@
}
@Override
+ public void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri,
+ RemoteCallback callback) {
+ final Bundle result = new Bundle();
+ result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
+ canonicalize(callingPkg, featureId, uri));
+ callback.sendResult(result);
+ }
+
+ @Override
public Uri uncanonicalize(String callingPkg, String featureId, Uri uri) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 0f1442d..7bc5901 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -359,6 +359,16 @@
return true;
}
+ case CANONICALIZE_ASYNC_TRANSACTION: {
+ data.enforceInterface(IContentProvider.descriptor);
+ String callingPkg = data.readString();
+ String featureId = data.readString();
+ Uri uri = Uri.CREATOR.createFromParcel(data);
+ RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data);
+ canonicalizeAsync(callingPkg, featureId, uri, callback);
+ return true;
+ }
+
case UNCANONICALIZE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
@@ -823,6 +833,25 @@
}
@Override
+ /* oneway */ public void canonicalizeAsync(String callingPkg, @Nullable String featureId,
+ Uri uri, RemoteCallback callback) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ try {
+ data.writeInterfaceToken(IContentProvider.descriptor);
+
+ data.writeString(callingPkg);
+ data.writeString(featureId);
+ uri.writeToParcel(data, 0);
+ callback.writeToParcel(data, 0);
+
+ mRemote.transact(IContentProvider.CANONICALIZE_ASYNC_TRANSACTION, data, null,
+ Binder.FLAG_ONEWAY);
+ } finally {
+ data.recycle();
+ }
+ }
+
+ @Override
public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri url)
throws RemoteException {
Parcel data = Parcel.obtain();
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 0e0161f..b748cfa 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -712,14 +712,17 @@
* {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS}.
* @hide
*/
- public static final int CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS =
+ public static final int CONTENT_PROVIDER_READY_TIMEOUT_MILLIS =
CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS + 10 * 1000;
+ // Timeout given a ContentProvider that has already been started and connected to.
+ private static final int CONTENT_PROVIDER_TIMEOUT_MILLIS = 3 * 1000;
+
// Should be >= {@link #CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS}, because that's how
// long ActivityManagerService is giving a content provider to get published if a new process
// needs to be started for that.
- private static final int GET_TYPE_TIMEOUT_MILLIS =
- CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS + 5 * 1000;
+ private static final int REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS =
+ CONTENT_PROVIDER_READY_TIMEOUT_MILLIS + CONTENT_PROVIDER_TIMEOUT_MILLIS;
public ContentResolver(@Nullable Context context) {
this(context, null);
@@ -833,10 +836,10 @@
IContentProvider provider = acquireExistingProvider(url);
if (provider != null) {
try {
- final GetTypeResultListener resultListener = new GetTypeResultListener();
+ final StringResultListener resultListener = new StringResultListener();
provider.getTypeAsync(url, new RemoteCallback(resultListener));
- resultListener.waitForResult();
- return resultListener.type;
+ resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
+ return resultListener.result;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
@@ -854,13 +857,13 @@
}
try {
- GetTypeResultListener resultListener = new GetTypeResultListener();
+ final StringResultListener resultListener = new StringResultListener();
ActivityManager.getService().getProviderMimeTypeAsync(
ContentProvider.getUriWithoutUserId(url),
resolveUserId(url),
new RemoteCallback(resultListener));
- resultListener.waitForResult();
- return resultListener.type;
+ resultListener.waitForResult(REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS);
+ return resultListener.result;
} catch (RemoteException e) {
// We just failed to send a oneway request to the System Server. Nothing to do.
return null;
@@ -870,27 +873,29 @@
}
}
- private static class GetTypeResultListener implements RemoteCallback.OnResultListener {
+ private abstract static class ResultListener<T> implements RemoteCallback.OnResultListener {
@GuardedBy("this")
public boolean done;
@GuardedBy("this")
- public String type;
+ public T result;
@Override
public void onResult(Bundle result) {
synchronized (this) {
- type = result.getString(REMOTE_CALLBACK_RESULT);
+ this.result = getResultFromBundle(result);
done = true;
notifyAll();
}
}
- public void waitForResult() {
+ protected abstract T getResultFromBundle(Bundle result);
+
+ public void waitForResult(long timeout) {
synchronized (this) {
if (!done) {
try {
- wait(GET_TYPE_TIMEOUT_MILLIS);
+ wait(timeout);
} catch (InterruptedException e) {
// Ignore
}
@@ -899,6 +904,20 @@
}
}
+ private static class StringResultListener extends ResultListener<String> {
+ @Override
+ protected String getResultFromBundle(Bundle result) {
+ return result.getString(REMOTE_CALLBACK_RESULT);
+ }
+ }
+
+ private static class UriResultListener extends ResultListener<Uri> {
+ @Override
+ protected Uri getResultFromBundle(Bundle result) {
+ return result.getParcelable(REMOTE_CALLBACK_RESULT);
+ }
+ }
+
/**
* Query for the possible MIME types for the representations the given
* content URL can be returned when opened as as stream with
@@ -1192,7 +1211,11 @@
}
try {
- return provider.canonicalize(mPackageName, mFeatureId, url);
+ final UriResultListener resultListener = new UriResultListener();
+ provider.canonicalizeAsync(mPackageName, mFeatureId, url,
+ new RemoteCallback(resultListener));
+ resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
+ return resultListener.result;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 4658ba1..37643da 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -45,7 +45,7 @@
public String getType(Uri url) throws RemoteException;
/**
- * An oneway version of getType. The functionality is exactly the same, except that the
+ * A oneway version of getType. The functionality is exactly the same, except that the
* call returns immediately, and the resulting type is returned when available via
* a binder callback.
*/
@@ -126,6 +126,14 @@
public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
throws RemoteException;
+ /**
+ * A oneway version of canonicalize. The functionality is exactly the same, except that the
+ * call returns immediately, and the resulting type is returned when available via
+ * a binder callback.
+ */
+ void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri,
+ RemoteCallback callback) throws RemoteException;
+
public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
throws RemoteException;
@@ -162,4 +170,5 @@
static final int REFRESH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 26;
static final int CHECK_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 27;
int GET_TYPE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 28;
+ int CANONICALIZE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 29;
}
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 78c4420..1737bd0 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -234,4 +234,12 @@
assertThat(type).isNull();
assertThat(end).isLessThan(start + 5000);
}
+
+ @Test
+ public void testCanonicalize() {
+ Uri canonical = mResolver.canonicalize(
+ Uri.parse("content://android.content.FakeProviderRemote/something"));
+ assertThat(canonical).isEqualTo(
+ Uri.parse("content://android.content.FakeProviderRemote/canonical"));
+ }
}
diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java
index 7b9bdbc..1d7ba5d 100644
--- a/core/tests/coretests/src/android/content/FakeProviderRemote.java
+++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java
@@ -54,4 +54,10 @@
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
+
+ @Override
+ public Uri canonicalize(Uri uri) {
+ return new Uri.Builder().scheme(uri.getScheme()).authority(uri.getAuthority())
+ .appendPath("canonical").build();
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0d8eff5..a529f24 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7271,7 +7271,7 @@
// Wait for the provider to be published...
final long timeout =
- SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS;
+ SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS;
boolean timedOut = false;
synchronized (cpr) {
while (cpr.provider == null) {
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 3f0cda3..b7199f7 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -29,6 +29,7 @@
libs: [
"android.test.runner",
"android.test.base",
+ "android.test.mock",
],
dxflags: ["--multi-dex"],
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 7f9732b..5829961 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -36,9 +36,10 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -67,6 +68,7 @@
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.service.notification.ConversationChannelWrapper;
+import android.test.mock.MockIContentProvider;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContentResolver;
import android.util.ArrayMap;
@@ -87,6 +89,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -123,7 +126,7 @@
@Mock NotificationUsageStats mUsageStats;
@Mock RankingHandler mHandler;
@Mock PackageManager mPm;
- @Mock IContentProvider mTestIContentProvider;
+ @Spy IContentProvider mTestIContentProvider = new MockIContentProvider();
@Mock Context mContext;
@Mock ZenModeHelper mMockZenModeHelper;
@@ -170,12 +173,12 @@
when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
- when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI)))
- .thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(SOUND_URI);
+ doReturn(CANONICAL_SOUND_URI)
+ .when(mTestIContentProvider).canonicalize(any(), any(), eq(SOUND_URI));
+ doReturn(CANONICAL_SOUND_URI)
+ .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ doReturn(SOUND_URI)
+ .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
@@ -506,12 +509,13 @@
.appendQueryParameter("title", "Test")
.appendQueryParameter("canonical", "1")
.build();
- when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(canonicalBasedOnLocal);
- when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(localUri);
- when(mTestIContentProvider.uncanonicalize(any(), any(), eq(canonicalBasedOnLocal)))
- .thenReturn(localUri);
+ doReturn(canonicalBasedOnLocal)
+ .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ doReturn(localUri)
+ .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ doReturn(localUri)
+ .when(mTestIContentProvider).uncanonicalize(any(), any(),
+ eq(canonicalBasedOnLocal));
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -530,10 +534,10 @@
@Test
public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
Thread.sleep(3000);
- when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(null);
- when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(null);
+ doReturn(null)
+ .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ doReturn(null)
+ .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -557,7 +561,8 @@
@Test
public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
// Not a local uncanonicalized uri, simulating that it fails to exist locally
- when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI))).thenReturn(null);
+ doReturn(null)
+ .when(mTestIContentProvider).canonicalize(any(), any(), eq(SOUND_URI));
String id = "id";
String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index f7ec11c..d1d64d3 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -156,6 +156,12 @@
}
@Override
+ public void canonicalizeAsync(String callingPkg, String featureId, Uri uri,
+ RemoteCallback callback) {
+ MockContentProvider.this.canonicalizeAsync(uri, callback);
+ }
+
+ @Override
public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
throws RemoteException {
return MockContentProvider.this.uncanonicalize(uri);
@@ -292,6 +298,18 @@
/**
* @hide
*/
+ @SuppressWarnings("deprecation")
+ public void canonicalizeAsync(Uri uri, RemoteCallback callback) {
+ AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+ final Bundle bundle = new Bundle();
+ bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, canonicalize(uri));
+ callback.sendResult(bundle);
+ });
+ }
+
+ /**
+ * @hide
+ */
public boolean refresh(Uri url, Bundle args) {
throw new UnsupportedOperationException("unimplemented mock method call");
}
diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
index 1831bcd..223bcc5 100644
--- a/test-mock/src/android/test/mock/MockIContentProvider.java
+++ b/test-mock/src/android/test/mock/MockIContentProvider.java
@@ -145,12 +145,23 @@
}
@Override
- public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
- throws RemoteException {
+ public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
+ @SuppressWarnings("deprecation")
+ public void canonicalizeAsync(String callingPkg, String featureId, Uri uri,
+ RemoteCallback remoteCallback) {
+ AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+ final Bundle bundle = new Bundle();
+ bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
+ canonicalize(callingPkg, featureId, uri));
+ remoteCallback.sendResult(bundle);
+ });
+ }
+
+ @Override
public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");