From ee6b6e4a1861336f8db52d8c93dbf32ee9615cfa Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Wed, 29 Jun 2016 17:34:02 -0700 Subject: ShortcutManager: Make sure persisted default launcher still exists. - Also make sure to ignore unexported activities. (e.g. unexported activities shouldn't have shortcuts.) - Also add unit tests for package manager related operations. - Also remove stale TODOs (per-activity shortcut count check is implemented already.) Bug 29699769 Bug 29516954 Change-Id: Ia18301baf6bec1ad71ae195d9ae3d10bd8386fc4 --- .../com/android/server/pm/ShortcutPackage.java | 3 - .../java/com/android/server/pm/ShortcutParser.java | 3 +- .../com/android/server/pm/ShortcutService.java | 265 ++++++++++++++++----- services/tests/servicestests/AndroidManifest.xml | 50 +++- .../android/server/pm/BaseShortcutManagerTest.java | 36 ++- .../android/server/pm/ShortcutManagerTest1.java | 5 - .../android/server/pm/ShortcutManagerTest4.java | 2 + .../android/server/pm/ShortcutManagerTest5.java | 202 ++++++++++++++++ .../android/server/pm/ShortcutTestActivity.java | 21 ++ 9 files changed, 501 insertions(+), 86 deletions(-) create mode 100644 services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java create mode 100644 services/tests/servicestests/src/com/android/server/pm/ShortcutTestActivity.java diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 30283862a793..1528eb554b39 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -260,9 +260,6 @@ class ShortcutPackage extends ShortcutPackageItem { } } - // TODO Check max dynamic count. - // mShortcutUser.mService.enforceMaxDynamicShortcuts(newDynamicCount); - // If it was originally pinned, the new one should be pinned too. if (wasPinned) { newShortcut.addFlags(ShortcutInfo.FLAG_PINNED); diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java index 858e1cd9284b..0762c0b21678 100644 --- a/services/core/java/com/android/server/pm/ShortcutParser.java +++ b/services/core/java/com/android/server/pm/ShortcutParser.java @@ -20,7 +20,6 @@ import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; -import android.content.pm.PackageInfo; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; import android.content.res.TypedArray; @@ -79,7 +78,7 @@ public class ShortcutParser { } final ActivityInfo activityInfoWithMetadata = - service.injectGetActivityInfoWithMetadata( + service.getActivityInfoWithMetadata( activityInfoNoMetadata.getComponentName(), userId); if (activityInfoWithMetadata != null) { result = parseShortcutsOneFile( diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 22d0d3ccb501..c6949e499021 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -232,6 +232,13 @@ public class ShortcutService extends IShortcutService.Stub { private final Object mLock = new Object(); + private static List EMPTY_RESOLVE_INFO = new ArrayList<>(0); + + private static Predicate ACTIVITY_NOT_EXPORTED = + ri -> !ri.activityInfo.exported; + + private static Predicate PACKAGE_NOT_INSTALLED = pi -> !isInstalled(pi); + private final Handler mHandler; @GuardedBy("mLock") @@ -318,8 +325,9 @@ public class ShortcutService extends IShortcutService.Stub { int RESOURCE_NAME_LOOKUP = 10; int GET_LAUNCHER_ACTIVITY = 11; int CHECK_LAUNCHER_ACTIVITY = 12; + int IS_ACTIVITY_ENABLED = 13; - int COUNT = CHECK_LAUNCHER_ACTIVITY + 1; + int COUNT = IS_ACTIVITY_ENABLED + 1; } final Object mStatLock = new Object(); @@ -348,11 +356,11 @@ public class ShortcutService extends IShortcutService.Stub { } public ShortcutService(Context context) { - this(context, BackgroundThread.get().getLooper()); + this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false); } @VisibleForTesting - ShortcutService(Context context, Looper looper) { + ShortcutService(Context context, Looper looper, boolean onlyForPackageManagerApis) { mContext = Preconditions.checkNotNull(context); LocalServices.addService(ShortcutServiceInternal.class, new LocalService()); mHandler = new Handler(looper); @@ -363,6 +371,10 @@ public class ShortcutService extends IShortcutService.Stub { mUsageStatsManagerInternal = Preconditions.checkNotNull( LocalServices.getService(UsageStatsManagerInternal.class)); + if (onlyForPackageManagerApis) { + return; // Don't do anything further. For unit tests only. + } + mPackageMonitor.register(context, looper, UserHandle.ALL, /* externalStorage= */ false); injectRegisterUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE @@ -1571,11 +1583,6 @@ public class ShortcutService extends IShortcutService.Stub { removeIcon(userId, target); } - if (source.getActivity() != null && - !source.getActivity().equals(target.getActivity())) { - // TODO When activity is changing, check the dynamic count. - } - // Note copyNonNullFieldsFrom() does the "updatable with?" check too. target.copyNonNullFieldsFrom(source); target.setTimestamp(injectCurrentTimeMillis()); @@ -1915,9 +1922,16 @@ public class ShortcutService extends IShortcutService.Stub { } else { detected = user.getDefaultLauncherComponent(); - // TODO: Make sure it's still enabled. - if (DEBUG) { - Slog.v(TAG, "Cached launcher: " + detected); + if (detected != null) { + if (injectIsActivityEnabledAndExported(detected, userId)) { + if (DEBUG) { + Slog.v(TAG, "Cached launcher: " + detected); + } + } else { + Slog.w(TAG, "Cached launcher " + detected + " no longer exists"); + detected = null; + user.setDefaultLauncherComponent(null); + } } } @@ -2363,6 +2377,10 @@ public class ShortcutService extends IShortcutService.Stub { return; // Don't delete shadow information. } if (!isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) { + if (DEBUG) { + Slog.d(TAG, "Uninstalled: " + spi.getPackageName() + + " user " + spi.getPackageUserId()); + } gonePackages.add(PackageWithUser.of(spi)); } }); @@ -2457,21 +2475,26 @@ public class ShortcutService extends IShortcutService.Stub { // === PackageManager interaction === + /** + * Returns {@link PackageInfo} unless it's uninstalled or disabled. + */ @Nullable - PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) { - return injectPackageInfo(packageName, userId, true); + final PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) { + return getPackageInfo(packageName, userId, true); } + /** + * Returns {@link PackageInfo} unless it's uninstalled or disabled. + */ @Nullable - PackageInfo getPackageInfo(String packageName, @UserIdInt int userId) { - return injectPackageInfo(packageName, userId, false); + final PackageInfo getPackageInfo(String packageName, @UserIdInt int userId) { + return getPackageInfo(packageName, userId, false); } int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) { final long token = injectClearCallingIdentity(); try { - return mIPackageManager.getPackageUid(packageName, PACKAGE_MATCH_FLAGS - , userId); + return mIPackageManager.getPackageUid(packageName, PACKAGE_MATCH_FLAGS, userId); } catch (RemoteException e) { // Shouldn't happen. Slog.wtf(TAG, "RemoteException", e); @@ -2481,16 +2504,30 @@ public class ShortcutService extends IShortcutService.Stub { } } + /** + * Returns {@link PackageInfo} unless it's uninstalled or disabled. + */ @Nullable @VisibleForTesting - PackageInfo injectPackageInfo(String packageName, @UserIdInt int userId, + final PackageInfo getPackageInfo(String packageName, @UserIdInt int userId, + boolean getSignatures) { + return isInstalledOrNull(injectPackageInfoWithUninstalled( + packageName, userId, getSignatures)); + } + + /** + * Do not use directly; this returns uninstalled packages too. + */ + @Nullable + @VisibleForTesting + PackageInfo injectPackageInfoWithUninstalled(String packageName, @UserIdInt int userId, boolean getSignatures) { final long start = injectElapsedRealtime(); final long token = injectClearCallingIdentity(); try { - return mIPackageManager.getPackageInfo(packageName, PACKAGE_MATCH_FLAGS - | (getSignatures ? PackageManager.GET_SIGNATURES : 0) - , userId); + return mIPackageManager.getPackageInfo( + packageName, PACKAGE_MATCH_FLAGS + | (getSignatures ? PackageManager.GET_SIGNATURES : 0), userId); } catch (RemoteException e) { // Shouldn't happen. Slog.wtf(TAG, "RemoteException", e); @@ -2504,9 +2541,22 @@ public class ShortcutService extends IShortcutService.Stub { } } + /** + * Returns {@link ApplicationInfo} unless it's uninstalled or disabled. + */ @Nullable @VisibleForTesting - ApplicationInfo injectApplicationInfo(String packageName, @UserIdInt int userId) { + final ApplicationInfo getApplicationInfo(String packageName, @UserIdInt int userId) { + return isInstalledOrNull(injectApplicationInfoWithUninstalled(packageName, userId)); + } + + /** + * Do not use directly; this returns uninstalled packages too. + */ + @Nullable + @VisibleForTesting + ApplicationInfo injectApplicationInfoWithUninstalled( + String packageName, @UserIdInt int userId) { final long start = injectElapsedRealtime(); final long token = injectClearCallingIdentity(); try { @@ -2522,13 +2572,27 @@ public class ShortcutService extends IShortcutService.Stub { } } + /** + * Returns {@link ActivityInfo} with its metadata unless it's uninstalled or disabled. + */ + @Nullable + final ActivityInfo getActivityInfoWithMetadata(ComponentName activity, @UserIdInt int userId) { + return isInstalledOrNull(injectGetActivityInfoWithMetadataWithUninstalled( + activity, userId)); + } + + /** + * Do not use directly; this returns uninstalled packages too. + */ @Nullable - ActivityInfo injectGetActivityInfoWithMetadata(ComponentName activity, @UserIdInt int userId) { + @VisibleForTesting + ActivityInfo injectGetActivityInfoWithMetadataWithUninstalled( + ComponentName activity, @UserIdInt int userId) { final long start = injectElapsedRealtime(); final long token = injectClearCallingIdentity(); try { return mIPackageManager.getActivityInfo(activity, - PACKAGE_MATCH_FLAGS | PackageManager.GET_META_DATA, userId); + (PACKAGE_MATCH_FLAGS | PackageManager.GET_META_DATA), userId); } catch (RemoteException e) { // Shouldn't happen. Slog.wtf(TAG, "RemoteException", e); @@ -2540,18 +2604,20 @@ public class ShortcutService extends IShortcutService.Stub { } } - @Nullable + /** + * Return all installed and enabled packages. + */ + @NonNull @VisibleForTesting - List injectInstalledPackages(@UserIdInt int userId) { + final List getInstalledPackages(@UserIdInt int userId) { final long start = injectElapsedRealtime(); final long token = injectClearCallingIdentity(); try { - final ParceledListSlice parceledList = - mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId); - if (parceledList == null) { - return Collections.emptyList(); - } - return parceledList.getList(); + final List all = injectGetPackagesWithUninstalled(userId); + + all.removeIf(PACKAGE_NOT_INSTALLED); + + return all; } catch (RemoteException e) { // Shouldn't happen. Slog.wtf(TAG, "RemoteException", e); @@ -2563,17 +2629,31 @@ public class ShortcutService extends IShortcutService.Stub { } } + /** + * Do not use directly; this returns uninstalled packages too. + */ + @NonNull + @VisibleForTesting + List injectGetPackagesWithUninstalled(@UserIdInt int userId) + throws RemoteException { + final ParceledListSlice parceledList = + mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } + private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime, Consumer callback) { if (DEBUG) { Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime); } - final List list = injectInstalledPackages(userId); + final List list = getInstalledPackages(userId); for (int i = list.size() - 1; i >= 0; i--) { final PackageInfo pi = list.get(i); - if (((pi.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0) - && (pi.lastUpdateTime >= lastScanTime)) { + if (pi.lastUpdateTime >= lastScanTime) { if (DEBUG) { Slog.d(TAG, "Found updated package " + pi.packageName); } @@ -2582,13 +2662,37 @@ public class ShortcutService extends IShortcutService.Stub { } } - private boolean isApplicationFlagSet(String packageName, int userId, int flags) { - final ApplicationInfo ai = injectApplicationInfo(packageName, userId); + private boolean isApplicationFlagSet(@NonNull String packageName, int userId, int flags) { + final ApplicationInfo ai = injectApplicationInfoWithUninstalled(packageName, userId); return (ai != null) && ((ai.flags & flags) == flags); } + private static boolean isInstalled(@Nullable ApplicationInfo ai) { + return (ai != null) && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0; + } + + private static boolean isInstalled(@Nullable PackageInfo pi) { + return (pi != null) && isInstalled(pi.applicationInfo); + } + + private static boolean isInstalled(@Nullable ActivityInfo ai) { + return (ai != null) && isInstalled(ai.applicationInfo); + } + + private static ApplicationInfo isInstalledOrNull(ApplicationInfo ai) { + return isInstalled(ai) ? ai : null; + } + + private static PackageInfo isInstalledOrNull(PackageInfo pi) { + return isInstalled(pi) ? pi : null; + } + + private static ActivityInfo isInstalledOrNull(ActivityInfo ai) { + return isInstalled(ai) ? ai : null; + } + boolean isPackageInstalled(String packageName, int userId) { - return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_INSTALLED); + return getApplicationInfo(packageName, userId) != null; } @Nullable @@ -2619,20 +2723,46 @@ public class ShortcutService extends IShortcutService.Stub { return intent; } + /** + * Same as queryIntentActivitiesAsUser, except it makes sure the package is installed, + * and only returns exported activities. + */ + @NonNull + @VisibleForTesting + List queryActivities(@NonNull Intent baseIntent, + @NonNull String packageName, @Nullable ComponentName activity, int userId) { + + baseIntent.setPackage(Preconditions.checkNotNull(packageName)); + if (activity != null) { + baseIntent.setComponent(activity); + } + + final List resolved = + mContext.getPackageManager().queryIntentActivitiesAsUser( + baseIntent, PACKAGE_MATCH_FLAGS, userId); + if (resolved == null || resolved.size() == 0) { + return EMPTY_RESOLVE_INFO; + } + // Make sure the package is installed. + if (!isInstalled(resolved.get(0).activityInfo)) { + return EMPTY_RESOLVE_INFO; + } + resolved.removeIf(ACTIVITY_NOT_EXPORTED); + return resolved; + } + + /** + * Return the main activity that is enabled and exported. If multiple activities are found, + * return the first one. + */ @Nullable ComponentName injectGetDefaultMainActivity(@NonNull String packageName, int userId) { final long start = injectElapsedRealtime(); final long token = injectClearCallingIdentity(); try { - final Intent intent = getMainActivityIntent(); - intent.setPackage(packageName); - final List resolved = - mContext.getPackageManager().queryIntentActivitiesAsUser( - intent, PACKAGE_MATCH_FLAGS, userId); - - return (resolved == null || resolved.size() == 0) - ? null : resolved.get(0).activityInfo.getComponentName(); + queryActivities(getMainActivityIntent(), packageName, null, userId); + return resolved.size() == 0 ? null : resolved.get(0).activityInfo.getComponentName(); } finally { injectRestoreCallingIdentity(token); @@ -2640,19 +2770,17 @@ public class ShortcutService extends IShortcutService.Stub { } } + /** + * Return whether an activity is enabled, exported and main. + */ boolean injectIsMainActivity(@NonNull ComponentName activity, int userId) { final long start = injectElapsedRealtime(); final long token = injectClearCallingIdentity(); try { - final Intent intent = getMainActivityIntent(); - intent.setPackage(activity.getPackageName()); - intent.setComponent(activity); - final List resolved = - mContext.getPackageManager().queryIntentActivitiesAsUser( - intent, PACKAGE_MATCH_FLAGS, userId); - - return resolved != null && resolved.size() > 0; + queryActivities(getMainActivityIntent(), activity.getPackageName(), + activity, userId); + return resolved.size() > 0; } finally { injectRestoreCallingIdentity(token); @@ -2660,23 +2788,37 @@ public class ShortcutService extends IShortcutService.Stub { } } + /** + * Return all the enabled, exported and main activities from a package. + */ @NonNull List injectGetMainActivities(@NonNull String packageName, int userId) { final long start = injectElapsedRealtime(); final long token = injectClearCallingIdentity(); try { - final Intent intent = getMainActivityIntent(); - intent.setPackage(packageName); + return queryActivities(getMainActivityIntent(), packageName, null, userId); + } finally { + injectRestoreCallingIdentity(token); - final List resolved = - mContext.getPackageManager().queryIntentActivitiesAsUser( - intent, PACKAGE_MATCH_FLAGS, userId); + logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start); + } + } - return (resolved != null) ? resolved : new ArrayList<>(0); + /** + * Return whether an activity is enabled and exported. + */ + @VisibleForTesting + boolean injectIsActivityEnabledAndExported( + @NonNull ComponentName activity, @UserIdInt int userId) { + final long start = injectElapsedRealtime(); + final long token = injectClearCallingIdentity(); + try { + return queryActivities(new Intent(), activity.getPackageName(), activity, userId) + .size() > 0; } finally { injectRestoreCallingIdentity(token); - logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start); + logDurationStat(Stats.IS_ACTIVITY_ENABLED, start); } } @@ -2839,6 +2981,7 @@ public class ShortcutService extends IShortcutService.Stub { dumpStatLS(pw, p, Stats.RESOURCE_NAME_LOOKUP, "resourceNameLookup"); dumpStatLS(pw, p, Stats.GET_LAUNCHER_ACTIVITY, "getLauncherActivity"); dumpStatLS(pw, p, Stats.CHECK_LAUNCHER_ACTIVITY, "checkLauncherActivity"); + dumpStatLS(pw, p, Stats.IS_ACTIVITY_ENABLED, "isActivityEnabled"); } for (int i = 0; i < mUsers.size(); i++) { diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 7017d81e43bb..b8ace28fc093 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -108,9 +108,53 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + injectInstalledPackages(@UserIdInt int userId) { - return getInstalledPackages(userId); + List injectGetPackagesWithUninstalled(@UserIdInt int userId) { + return BaseShortcutManagerTest.this.getInstalledPackagesWithUninstalled(userId); } @Override - ActivityInfo injectGetActivityInfoWithMetadata(ComponentName activity, + ActivityInfo injectGetActivityInfoWithMetadataWithUninstalled(ComponentName activity, @UserIdInt int userId) { final PackageInfo pi = mContext.injectGetActivitiesWithMetadata( activity.getPackageName(), userId); @@ -369,6 +369,11 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { return mMainActivityFetcher.apply(packageName, userId); } + @Override + boolean injectIsActivityEnabledAndExported(ComponentName activity, @UserIdInt int userId) { + return mEnabledActivityChecker.test(activity, userId); + } + @Override XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) { return mContext.injectXmlMetaData(activityInfo, key); @@ -951,7 +956,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } } - private List getInstalledPackages(int userId) { + private List getInstalledPackagesWithUninstalled(int userId) { final ArrayList ret = new ArrayList<>(); addPackageInfo(getInjectedPackageInfo(CALLING_PACKAGE_1, userId, false), ret); @@ -990,6 +995,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { ai.name = cn.getClassName(); ai.metaData = new Bundle(); ai.metaData.putInt(ShortcutParser.METADATA_KEY, activities.get(cn)); + ai.applicationInfo = ret.applicationInfo; list.add(ai); } ret.activities = list.toArray(new ActivityInfo[list.size()]); @@ -1462,14 +1468,18 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { return infoList.get(0); } - protected Intent genPackageAddIntent(String pakcageName, int userId) { + protected Intent genPackageAddIntent(String packageName, int userId) { + installPackage(userId, packageName); + Intent i = new Intent(Intent.ACTION_PACKAGE_ADDED); - i.setData(Uri.parse("package:" + pakcageName)); + i.setData(Uri.parse("package:" + packageName)); i.putExtra(Intent.EXTRA_USER_HANDLE, userId); return i; } protected Intent genPackageDeleteIntent(String pakcageName, int userId) { + uninstallPackage(userId, pakcageName); + Intent i = new Intent(Intent.ACTION_PACKAGE_REMOVED); i.setData(Uri.parse("package:" + pakcageName)); i.putExtra(Intent.EXTRA_USER_HANDLE, userId); @@ -1477,6 +1487,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } protected Intent genPackageUpdateIntent(String pakcageName, int userId) { + installPackage(userId, pakcageName); + Intent i = new Intent(Intent.ACTION_PACKAGE_ADDED); i.setData(Uri.parse("package:" + pakcageName)); i.putExtra(Intent.EXTRA_USER_HANDLE, userId); 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 8e26c109269d..e2e31e99e32b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -55,7 +55,6 @@ import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -86,7 +85,6 @@ import com.android.frameworks.servicestests.R; import com.android.server.pm.ShortcutService.ConfigConstants; import com.android.server.pm.ShortcutService.FileOutputStreamWithPath; import com.android.server.pm.ShortcutUser.PackageWithUser; -import com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.ShortcutListAsserter; import org.mockito.ArgumentCaptor; @@ -94,9 +92,6 @@ import java.io.File; import java.io.IOException; import java.util.List; import java.util.Locale; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Supplier; /** * Tests for ShortcutService and ShortcutManager. diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java index a36c0adea5dc..54c4b22494f1 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java @@ -24,7 +24,9 @@ import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils import android.content.Intent; import android.os.Bundle; import android.os.PersistableBundle; +import android.test.suitebuilder.annotation.SmallTest; +@SmallTest public class ShortcutManagerTest4 extends BaseShortcutManagerTest { private static Bundle sIntentExtras = makeBundle( diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java new file mode 100644 index 000000000000..29c98dcf5228 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2016 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 static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.ShortcutServiceInternal; +import android.content.res.XmlResourceParser; +import android.os.Looper; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.server.LocalServices; + +import java.util.List; +import java.util.Set; + +/** + * Unit tests for all the IPackageManager related methods in {@link ShortcutService}. + * + * All the tests here actually talks to the real IPackageManager, so we can't test complicated + * cases. Instead we just make sure they all work reasonably without at least crashing. + */ +@SmallTest +public class ShortcutManagerTest5 extends BaseShortcutManagerTest { + private ShortcutService mShortcutService; + + private String mMyPackage; + private int mMyUserId; + + public static class ShortcutEnabled extends Activity { + } + + public static class ShortcutDisabled extends Activity { + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + LocalServices.removeServiceForTest(ShortcutServiceInternal.class); + mShortcutService = new ShortcutService(getTestContext(), Looper.getMainLooper(), + /* onyForPackageManagerApis */ true); + + mMyPackage = getTestContext().getPackageName(); + mMyUserId = android.os.Process.myUserHandle().getIdentifier(); + } + + public void testGetPackageUid() { + assertTrue(mShortcutService.injectGetPackageUid( + mMyPackage, mMyUserId) != 0); + + assertEquals(-1, mShortcutService.injectGetPackageUid( + "no.such.package", mMyUserId)); + } + + public void testGetPackageInfo() { + PackageInfo pi = mShortcutService.getPackageInfo( + mMyPackage, mMyUserId, /*signature*/ false); + assertEquals(mMyPackage, pi.packageName); + assertNull(pi.signatures); + + pi = mShortcutService.getPackageInfo( + mMyPackage, mMyUserId, /*signature*/ true); + assertEquals(mMyPackage, pi.packageName); + assertNotNull(pi.signatures); + + pi = mShortcutService.getPackageInfo( + "no.such.package", mMyUserId, /*signature*/ true); + assertNull(pi); + } + + public void testGetApplicationInfo() { + ApplicationInfo ai = mShortcutService.getApplicationInfo( + mMyPackage, mMyUserId); + assertEquals(mMyPackage, ai.packageName); + + ai = mShortcutService.getApplicationInfo( + "no.such.package", mMyUserId); + assertNull(ai); + } + + public void testGetActivityInfoWithMetadata() { + // Disabled activity + ActivityInfo ai = mShortcutService.getActivityInfoWithMetadata( + new ComponentName(mMyPackage, "ShortcutDisabled"), mMyUserId); + assertNull(ai); + + // Nonexistent + ai = mShortcutService.getActivityInfoWithMetadata( + new ComponentName("no.such.package", "ShortcutDisabled"), mMyUserId); + assertNull(ai); + + // Existent, with no metadata. + ai = mShortcutService.getActivityInfoWithMetadata( + new ComponentName(mMyPackage, "a.ShortcutEnabled"), mMyUserId); + assertEquals(mMyPackage, ai.packageName); + assertEquals("a.ShortcutEnabled", ai.name); + assertNull(ai.loadXmlMetaData(getTestContext().getPackageManager(), + "android.app.shortcuts")); + + // Existent, with a shortcut metadata. + ai = mShortcutService.getActivityInfoWithMetadata( + new ComponentName(mMyPackage, "a.Shortcut1"), mMyUserId); + assertEquals(mMyPackage, ai.packageName); + assertEquals("a.Shortcut1", ai.name); + XmlResourceParser meta = ai.loadXmlMetaData(getTestContext().getPackageManager(), + "android.app.shortcuts"); + assertNotNull(meta); + meta.close(); + } + + public void testGetInstalledPackages() { + List apks = mShortcutService.getInstalledPackages(mMyUserId); + + Set expectedPackages = set("com.android.settings", mMyPackage); + for (PackageInfo pi : apks) { + expectedPackages.remove(pi.packageName); + } + assertEquals(set(), expectedPackages); + } + + public void testGetDefaultMainActivity() { + ComponentName cn = mShortcutService.injectGetDefaultMainActivity( + "com.android.settings", mMyUserId); + + assertEquals( + ComponentName.unflattenFromString("com.android.settings/.Settings"), + cn); + + // This package has no main activity. + assertNull(mShortcutService.injectGetDefaultMainActivity( + mMyPackage, mMyUserId)); + + // Nonexistent. + assertNull(mShortcutService.injectGetDefaultMainActivity( + "no.such.package", mMyUserId)); + } + + public void testIsMainActivity() { + assertTrue(mShortcutService.injectIsMainActivity( + ComponentName.unflattenFromString("com.android.settings/.Settings"), mMyUserId)); + assertFalse(mShortcutService.injectIsMainActivity( + ComponentName.unflattenFromString("com.android.settings/.xxx"), mMyUserId)); + assertFalse(mShortcutService.injectIsMainActivity( + ComponentName.unflattenFromString("no.such.package/.xxx"), mMyUserId)); + + assertFalse(mShortcutService.injectIsMainActivity( + new ComponentName(mMyPackage, "a.DisabledMain"), mMyUserId)); + assertFalse(mShortcutService.injectIsMainActivity( + new ComponentName(mMyPackage, "a.UnexportedMain"), mMyUserId)); + + } + + public void testGetMainActivities() { + assertEquals(1, mShortcutService.injectGetMainActivities( + "com.android.settings", mMyUserId).size()); + + // This APK has no main activities. + assertEquals(0, mShortcutService.injectGetMainActivities( + mMyPackage, mMyUserId).size()); + } + + public void testIsActivityEnabledAndExported() { + assertTrue(mShortcutService.injectIsActivityEnabledAndExported( + ComponentName.unflattenFromString("com.android.settings/.Settings"), mMyUserId)); + assertFalse(mShortcutService.injectIsActivityEnabledAndExported( + ComponentName.unflattenFromString("com.android.settings/.xxx"), mMyUserId)); + assertFalse(mShortcutService.injectIsActivityEnabledAndExported( + ComponentName.unflattenFromString("no.such.package/.xxx"), mMyUserId)); + + assertTrue(mShortcutService.injectIsActivityEnabledAndExported( + new ComponentName(mMyPackage, "com.android.server.pm.ShortcutTestActivity"), + mMyUserId)); + + assertTrue(mShortcutService.injectIsActivityEnabledAndExported( + new ComponentName(mMyPackage, "a.ShortcutEnabled"), mMyUserId)); + + assertFalse(mShortcutService.injectIsActivityEnabledAndExported( + new ComponentName(mMyPackage, "a.ShortcutDisabled"), mMyUserId)); + assertFalse(mShortcutService.injectIsActivityEnabledAndExported( + new ComponentName(mMyPackage, "a.ShortcutUnexported"), mMyUserId)); + + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutTestActivity.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutTestActivity.java new file mode 100644 index 000000000000..d82b0d55f2aa --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutTestActivity.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016 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.app.Activity; + +public class ShortcutTestActivity extends Activity { +} -- cgit v1.2.3-59-g8ed1b