Parses share targets from shortcuts.xml
Test: atest com.android.server.pm.ShortcutManagerTest1
Test: atest com.android.server.pm.ShortcutManagerTest2
Test: atest com.android.server.pm.ShortcutManagerTest3
Test: atest com.android.server.pm.ShortcutManagerTest4
Test: atest com.android.server.pm.ShortcutManagerTest5
Test: atest com.android.server.pm.ShortcutManagerTest6
Test: atest com.android.server.pm.ShortcutManagerTest7
Test: atest com.android.server.pm.ShortcutManagerTest8
Test: atest com.android.server.pm.ShortcutManagerTest9
Test: atest com.android.server.pm.ShortcutManagerTest10
Test: atest CtsShortcutHostTestCases CtsShortcutManagerTestCases
Change-Id: I1ddcd4e689f5d76d68b5068629cbe2c35d0a2841
diff --git a/services/core/java/com/android/server/pm/ShareTargetInfo.java b/services/core/java/com/android/server/pm/ShareTargetInfo.java
new file mode 100644
index 0000000..9e8b73e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShareTargetInfo.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import android.text.TextUtils;
+
+/**
+ * Represents a Share Target definition, read from the application's manifest (shortcuts.xml)
+ */
+class ShareTargetInfo {
+ static class TargetData {
+ final String mScheme;
+ final String mHost;
+ final String mPort;
+ final String mPath;
+ final String mPathPattern;
+ final String mPathPrefix;
+ final String mMimeType;
+
+ TargetData(String scheme, String host, String port, String path, String pathPattern,
+ String pathPrefix, String mimeType) {
+ mScheme = scheme;
+ mHost = host;
+ mPort = port;
+ mPath = path;
+ mPathPattern = pathPattern;
+ mPathPrefix = pathPrefix;
+ mMimeType = mimeType;
+ }
+
+ public void toStringInner(StringBuilder strBuilder) {
+ if (!TextUtils.isEmpty(mScheme)) {
+ strBuilder.append(" scheme=").append(mScheme);
+ }
+ if (!TextUtils.isEmpty(mHost)) {
+ strBuilder.append(" host=").append(mHost);
+ }
+ if (!TextUtils.isEmpty(mPort)) {
+ strBuilder.append(" port=").append(mPort);
+ }
+ if (!TextUtils.isEmpty(mPath)) {
+ strBuilder.append(" path=").append(mPath);
+ }
+ if (!TextUtils.isEmpty(mPathPattern)) {
+ strBuilder.append(" pathPattern=").append(mPathPattern);
+ }
+ if (!TextUtils.isEmpty(mPathPrefix)) {
+ strBuilder.append(" pathPrefix=").append(mPathPrefix);
+ }
+ if (!TextUtils.isEmpty(mMimeType)) {
+ strBuilder.append(" mimeType=").append(mMimeType);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder strBuilder = new StringBuilder();
+ toStringInner(strBuilder);
+ return strBuilder.toString();
+ }
+ }
+
+ final TargetData[] mTargetData;
+ final String mTargetClass;
+ final String[] mCategories;
+
+ ShareTargetInfo(TargetData[] data, String targetClass, String[] categories) {
+ mTargetData = data;
+ mTargetClass = targetClass;
+ mCategories = categories;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder strBuilder = new StringBuilder();
+ strBuilder.append("targetClass=").append(mTargetClass);
+ for (int i = 0; i < mTargetData.length; i++) {
+ strBuilder.append(" data={");
+ mTargetData[i].toStringInner(strBuilder);
+ strBuilder.append("}");
+ }
+ for (int i = 0; i < mCategories.length; i++) {
+ strBuilder.append(" category=").append(mCategories[i]);
+ }
+
+ return strBuilder.toString();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 92e261a..83f0fde 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -111,6 +111,11 @@
final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
/**
+ * All the share targets from the package
+ */
+ private final ArrayList<ShareTargetInfo> mShareTargets = new ArrayList<>(0);
+
+ /**
* # of times the package has called rate-limited APIs.
*/
private int mApiCallCount;
@@ -739,15 +744,16 @@
List<ShortcutInfo> newManifestShortcutList = null;
try {
newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService,
- getPackageName(), getPackageUserId());
+ getPackageName(), getPackageUserId(), mShareTargets);
} catch (IOException|XmlPullParserException e) {
Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e);
}
final int manifestShortcutSize = newManifestShortcutList == null ? 0
: newManifestShortcutList.size();
if (ShortcutService.DEBUG) {
- Slog.d(TAG, String.format("Package %s has %d manifest shortcut(s)",
- getPackageName(), manifestShortcutSize));
+ Slog.d(TAG,
+ String.format("Package %s has %d manifest shortcut(s), and %d share target(s)",
+ getPackageName(), manifestShortcutSize, mShareTargets.size()));
}
if (isNewApp && (manifestShortcutSize == 0)) {
// If it's a new app, and it doesn't have manifest shortcuts, then nothing to do.
@@ -1657,6 +1663,11 @@
return new ArrayList<>(mShortcuts.values());
}
+ @VisibleForTesting
+ List<ShareTargetInfo> getAllShareTargetsForTest() {
+ return new ArrayList<>(mShareTargets);
+ }
+
@Override
public void verifyStates() {
super.verifyStates();
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index 866c46c..90f08c3 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -15,6 +15,7 @@
*/
package com.android.server.pm;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
@@ -55,10 +56,14 @@
private static final String TAG_SHORTCUT = "shortcut";
private static final String TAG_INTENT = "intent";
private static final String TAG_CATEGORIES = "categories";
+ private static final String TAG_SHARE_TARGET = "share-target";
+ private static final String TAG_DATA = "data";
+ private static final String TAG_CATEGORY = "category";
@Nullable
- public static List<ShortcutInfo> parseShortcuts(ShortcutService service,
- String packageName, @UserIdInt int userId) throws IOException, XmlPullParserException {
+ public static List<ShortcutInfo> parseShortcuts(ShortcutService service, String packageName,
+ @UserIdInt int userId, @NonNull List<ShareTargetInfo> outShareTargets)
+ throws IOException, XmlPullParserException {
if (ShortcutService.DEBUG) {
Slog.d(TAG, String.format("Scanning package %s for manifest shortcuts on user %d",
packageName, userId));
@@ -69,6 +74,7 @@
}
List<ShortcutInfo> result = null;
+ outShareTargets.clear();
try {
final int size = activities.size();
@@ -82,8 +88,8 @@
service.getActivityInfoWithMetadata(
activityInfoNoMetadata.getComponentName(), userId);
if (activityInfoWithMetadata != null) {
- result = parseShortcutsOneFile(
- service, activityInfoWithMetadata, packageName, userId, result);
+ result = parseShortcutsOneFile(service, activityInfoWithMetadata, packageName,
+ userId, result, outShareTargets);
}
}
} catch (RuntimeException e) {
@@ -99,7 +105,8 @@
private static List<ShortcutInfo> parseShortcutsOneFile(
ShortcutService service,
ActivityInfo activityInfo, String packageName, @UserIdInt int userId,
- List<ShortcutInfo> result) throws IOException, XmlPullParserException {
+ List<ShortcutInfo> result, @NonNull List<ShareTargetInfo> outShareTargets)
+ throws IOException, XmlPullParserException {
if (ShortcutService.DEBUG) {
Slog.d(TAG, String.format(
"Checking main activity %s", activityInfo.getComponentName()));
@@ -126,9 +133,19 @@
// after parsing <intent>. We keep the current one in here.
ShortcutInfo currentShortcut = null;
+ // We instantiate ShareTargetInfo at <share-target>, but add it to outShareTargets at
+ // </share-target>, after parsing <data> and <category>. We keep the current one here.
+ ShareTargetInfo currentShareTarget = null;
+
+ // Keeps parsed categories for both ShortcutInfo and ShareTargetInfo
Set<String> categories = null;
+
+ // Keeps parsed intents for ShortcutInfo
final ArrayList<Intent> intents = new ArrayList<>();
+ // Keeps parsed data fields for ShareTargetInfo
+ final ArrayList<ShareTargetInfo.TargetData> dataList = new ArrayList<>();
+
outer:
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > 0)) {
@@ -194,6 +211,32 @@
continue;
}
+ // When a share-target tag is closing, publish.
+ if ((type == XmlPullParser.END_TAG) && (depth == 2)
+ && (TAG_SHARE_TARGET.equals(tag))) {
+ if (currentShareTarget == null) {
+ // ShareTarget was invalid.
+ continue;
+ }
+ final ShareTargetInfo sti = currentShareTarget;
+ currentShareTarget = null; // Make sure to null out for the next iteration.
+
+ if (categories == null || categories.isEmpty() || dataList.isEmpty()) {
+ // Incomplete ShareTargetInfo.
+ continue;
+ }
+
+ final ShareTargetInfo newShareTarget = new ShareTargetInfo(
+ dataList.toArray(new ShareTargetInfo.TargetData[dataList.size()]),
+ sti.mTargetClass, categories.toArray(new String[categories.size()]));
+ outShareTargets.add(newShareTarget);
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, "ShareTarget added: " + newShareTarget.toString());
+ }
+ categories = null;
+ dataList.clear();
+ }
+
// Otherwise, just look at start tags.
if (type != XmlPullParser.START_TAG) {
continue;
@@ -224,6 +267,17 @@
categories = null;
continue;
}
+ if (depth == 2 && TAG_SHARE_TARGET.equals(tag)) {
+ final ShareTargetInfo sti = parseShareTargetAttributes(service, attrs);
+ if (sti == null) {
+ // ShareTarget was invalid.
+ continue;
+ }
+ currentShareTarget = sti;
+ categories = null;
+ dataList.clear();
+ continue;
+ }
if (depth == 3 && TAG_INTENT.equals(tag)) {
if ((currentShortcut == null)
|| !currentShortcut.isEnabled()) {
@@ -258,6 +312,34 @@
categories.add(name);
continue;
}
+ if (depth == 3 && TAG_CATEGORY.equals(tag)) {
+ if ((currentShareTarget == null)) {
+ continue;
+ }
+ final String name = parseCategory(service, attrs);
+ if (TextUtils.isEmpty(name)) {
+ Log.e(TAG, "Empty category found. activity=" + activity);
+ continue;
+ }
+
+ if (categories == null) {
+ categories = new ArraySet<>();
+ }
+ categories.add(name);
+ continue;
+ }
+ if (depth == 3 && TAG_DATA.equals(tag)) {
+ if ((currentShareTarget == null)) {
+ continue;
+ }
+ final ShareTargetInfo.TargetData data = parseShareTargetData(service, attrs);
+ if (data == null) {
+ Log.e(TAG, "Invalid data tag found. activity=" + activity);
+ continue;
+ }
+ dataList.add(data);
+ continue;
+ }
Log.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth));
}
@@ -369,4 +451,57 @@
null, // bitmap path
disabledReason);
}
+
+ private static String parseCategory(ShortcutService service, AttributeSet attrs) {
+ final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+ R.styleable.IntentCategory);
+ try {
+ if (sa.getType(R.styleable.IntentCategory_name) != TypedValue.TYPE_STRING) {
+ Log.w(TAG, "android:name must be string literal.");
+ return null;
+ }
+ return sa.getString(R.styleable.IntentCategory_name);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ShareTargetInfo parseShareTargetAttributes(ShortcutService service,
+ AttributeSet attrs) {
+ final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+ R.styleable.Intent);
+ try {
+ String targetClass = sa.getString(R.styleable.Intent_targetClass);
+ if (TextUtils.isEmpty(targetClass)) {
+ Log.w(TAG, "android:targetClass must be provided.");
+ return null;
+ }
+ return new ShareTargetInfo(null, targetClass, null);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ShareTargetInfo.TargetData parseShareTargetData(ShortcutService service,
+ AttributeSet attrs) {
+ final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+ R.styleable.AndroidManifestData);
+ try {
+ if (sa.getType(R.styleable.AndroidManifestData_mimeType) != TypedValue.TYPE_STRING) {
+ Log.w(TAG, "android:mimeType must be string literal.");
+ return null;
+ }
+ String scheme = sa.getString(R.styleable.AndroidManifestData_scheme);
+ String host = sa.getString(R.styleable.AndroidManifestData_host);
+ String port = sa.getString(R.styleable.AndroidManifestData_port);
+ String path = sa.getString(R.styleable.AndroidManifestData_path);
+ String pathPattern = sa.getString(R.styleable.AndroidManifestData_pathPattern);
+ String pathPrefix = sa.getString(R.styleable.AndroidManifestData_pathPrefix);
+ String mimeType = sa.getString(R.styleable.AndroidManifestData_mimeType);
+ return new ShareTargetInfo.TargetData(scheme, host, port, path, pathPattern, pathPrefix,
+ mimeType);
+ } finally {
+ sa.recycle();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index b9c3048..2b773f4 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -41,8 +41,8 @@
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
@@ -98,8 +98,8 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
-import com.android.server.LocalServices;
import com.android.internal.util.StatLogger;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.ShortcutUser.PackageWithUser;
@@ -1172,7 +1172,7 @@
return true;
}
}
-
+
// If the local copy says the user is locked, check with AM for the actual state, since
// the user might just have been unlocked.
// Note we just don't use isUserUnlockingOrUnlocked() here, because it'll return false
diff --git a/services/tests/servicestests/res/xml/shortcut_share_targets.xml b/services/tests/servicestests/res/xml/shortcut_share_targets.xml
new file mode 100644
index 0000000..ec696e9
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_share_targets.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- Test XML resource to read share-targets from, used in ShortcutManagerTest1.java -->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
+ <shortcut
+ android:shortcutId="dummy_shortcut1"
+ android:enabled="true"
+ android:shortcutShortLabel="@string/shortcut_title1">
+ <intent
+ android:action="android.intent.action.VIEW"
+ android:targetPackage="com.test.somepackage"
+ android:targetClass="com.test.somepackage.someclass" />
+ <categories android:name="android.shortcut.conversation" />
+ </shortcut>
+
+ <!-- Valid share target definition -->
+ <share-target android:targetClass="com.test.directshare.TestActivity1">
+ <data
+ android:scheme="http"
+ android:host="www.google.com"
+ android:port="1234"
+ android:path="somePath"
+ android:pathPrefix="somePathPrefix"
+ android:pathPattern="somePathPattern"
+ android:mimeType="text/plain"/>
+ <category android:name="com.test.category.CATEGORY1"/>
+ <category android:name="com.test.category.CATEGORY2"/>
+ </share-target>
+
+ <!-- Share target missing data tag, will be dropped -->
+ <share-target android:targetClass="com.test.directshare.TestActivity">
+ <category android:name="com.test.category.CATEGORY2"/>
+ </share-target>
+
+ <!-- Share target missing target class, will be dropped -->
+ <share-target>
+ <data
+ android:scheme="file"
+ android:host="www.somehost.com"
+ android:port="1234"
+ android:mimeType="video/*"/>
+ <category android:name="com.test.category.CATEGORY3"/>
+ </share-target>
+
+ <shortcut
+ android:shortcutId="dummy_shortcut2"
+ android:enabled="true"
+ android:shortcutShortLabel="@string/shortcut_title1">
+ <intent
+ android:action="android.intent.action.VIEW"
+ android:targetPackage="com.test.somepackage"
+ android:targetClass="com.test.somepackage.someclass" />
+ <categories android:name="android.shortcut.conversation" />
+ </shortcut>
+
+ <!-- Share target missing category, will be dropped -->
+ <share-target android:targetClass="com.test.directshare.TestActivity">
+ <data
+ android:scheme="content"
+ android:mimeType="text/plain"/>
+ </share-target>
+
+ <!-- Valid share target definition -->
+ <share-target android:targetClass="com.test.directshare.TestActivity5">
+ <category android:name="com.test.category.CATEGORY5"/>
+ <category android:name="com.test.category.CATEGORY6"/>
+ <data android:mimeType="video/mp4"/>
+ <data
+ android:scheme="content"
+ android:mimeType="video/*"/>
+ </share-target>
+</shortcuts>
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 32902a7..1f5c64e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -91,8 +91,8 @@
import com.android.server.SystemService;
import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
import com.android.server.pm.ShortcutUser.PackageWithUser;
-
import com.android.server.wm.ActivityTaskManagerInternal;
+
import org.junit.Assert;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
@@ -105,8 +105,6 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -762,6 +760,7 @@
LocalServices.addService(UsageStatsManagerInternal.class, mMockUsageStatsManagerInternal);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.addService(ActivityManagerInternal.class, mMockActivityManagerInternal);
+ LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
LocalServices.addService(ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
LocalServices.removeServiceForTest(UserManagerInternal.class);
LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
@@ -1796,6 +1795,15 @@
}
/**
+ * @return all share targets stored internally for the caller.
+ */
+ protected List<ShareTargetInfo> getCallerShareTargets() {
+ final ShortcutPackage p = mService.getPackageShortcutForTest(
+ getCallingPackage(), getCallingUserId());
+ return p == null ? null : p.getAllShareTargetsForTest();
+ }
+
+ /**
* @return all shortcuts owned by caller that are actually visible via ShortcutManager.
* See also {@link #getCallerShortcuts}.
*/
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index fa73447..3172afb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -106,6 +106,7 @@
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.BiConsumer;
@@ -398,7 +399,7 @@
assertEquals(3, mManager.getRemainingCallCount());
}
- public void testPublishWithNoActivity() {
+ public void testPublishWithNoActivity() {
// If activity is not explicitly set, use the default one.
mRunningUsers.put(USER_10, true);
@@ -8015,4 +8016,56 @@
assertFalse(mInternal.isForegroundDefaultLauncher("another", uid));
}
+
+ public void testParseShareTargetsFromManifest() {
+ // These values must exactly match the content of shortcuts_share_targets.xml resource
+ List<ShareTargetInfo> expectedValues = new ArrayList<>();
+ expectedValues.add(new ShareTargetInfo(
+ new ShareTargetInfo.TargetData[]{new ShareTargetInfo.TargetData(
+ "http", "www.google.com", "1234", "somePath", "somePathPattern",
+ "somePathPrefix", "text/plain")}, "com.test.directshare.TestActivity1",
+ new String[]{"com.test.category.CATEGORY1", "com.test.category.CATEGORY2"}));
+ expectedValues.add(new ShareTargetInfo(new ShareTargetInfo.TargetData[]{
+ new ShareTargetInfo.TargetData(null, null, null, null, null, null, "video/mp4"),
+ new ShareTargetInfo.TargetData("content", null, null, null, null, null, "video/*")},
+ "com.test.directshare.TestActivity5",
+ new String[]{"com.test.category.CATEGORY5", "com.test.category.CATEGORY6"}));
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_share_targets);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ List<ShareTargetInfo> shareTargets = getCallerShareTargets();
+
+ assertNotNull(shareTargets);
+ assertEquals(expectedValues.size(), shareTargets.size());
+
+ for (int i = 0; i < expectedValues.size(); i++) {
+ ShareTargetInfo expected = expectedValues.get(i);
+ ShareTargetInfo actual = shareTargets.get(i);
+
+ assertEquals(expected.mTargetData.length, actual.mTargetData.length);
+ for (int j = 0; j < expected.mTargetData.length; j++) {
+ assertEquals(expected.mTargetData[j].mScheme, actual.mTargetData[j].mScheme);
+ assertEquals(expected.mTargetData[j].mHost, actual.mTargetData[j].mHost);
+ assertEquals(expected.mTargetData[j].mPort, actual.mTargetData[j].mPort);
+ assertEquals(expected.mTargetData[j].mPath, actual.mTargetData[j].mPath);
+ assertEquals(expected.mTargetData[j].mPathPrefix,
+ actual.mTargetData[j].mPathPrefix);
+ assertEquals(expected.mTargetData[j].mPathPattern,
+ actual.mTargetData[j].mPathPattern);
+ assertEquals(expected.mTargetData[j].mMimeType, actual.mTargetData[j].mMimeType);
+ }
+
+ assertEquals(expected.mTargetClass, actual.mTargetClass);
+
+ assertEquals(expected.mCategories.length, actual.mCategories.length);
+ for (int j = 0; j < expected.mCategories.length; j++) {
+ assertEquals(expected.mCategories[j], actual.mCategories[j]);
+ }
+ }
+ }
}