}
}
- // 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);
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;
}
final ActivityInfo activityInfoWithMetadata =
- service.injectGetActivityInfoWithMetadata(
+ service.getActivityInfoWithMetadata(
activityInfoNoMetadata.getComponentName(), userId);
if (activityInfoWithMetadata != null) {
result = parseShortcutsOneFile(
private final Object mLock = new Object();
+ private static List<ResolveInfo> EMPTY_RESOLVE_INFO = new ArrayList<>(0);
+
+ private static Predicate<ResolveInfo> ACTIVITY_NOT_EXPORTED =
+ ri -> !ri.activityInfo.exported;
+
+ private static Predicate<PackageInfo> PACKAGE_NOT_INSTALLED = pi -> !isInstalled(pi);
+
private final Handler mHandler;
@GuardedBy("mLock")
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();
}
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);
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
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());
} 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);
+ }
}
}
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));
}
});
// === 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);
}
}
+ /**
+ * 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);
}
}
+ /**
+ * 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 {
}
}
+ /**
+ * 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);
}
}
- @Nullable
+ /**
+ * Return all installed and enabled packages.
+ */
+ @NonNull
@VisibleForTesting
- List<PackageInfo> injectInstalledPackages(@UserIdInt int userId) {
+ final List<PackageInfo> getInstalledPackages(@UserIdInt int userId) {
final long start = injectElapsedRealtime();
final long token = injectClearCallingIdentity();
try {
- final ParceledListSlice<PackageInfo> parceledList =
- mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId);
- if (parceledList == null) {
- return Collections.emptyList();
- }
- return parceledList.getList();
+ final List<PackageInfo> all = injectGetPackagesWithUninstalled(userId);
+
+ all.removeIf(PACKAGE_NOT_INSTALLED);
+
+ return all;
} catch (RemoteException e) {
// Shouldn't happen.
Slog.wtf(TAG, "RemoteException", e);
}
}
+ /**
+ * Do not use directly; this returns uninstalled packages too.
+ */
+ @NonNull
+ @VisibleForTesting
+ List<PackageInfo> injectGetPackagesWithUninstalled(@UserIdInt int userId)
+ throws RemoteException {
+ final ParceledListSlice<PackageInfo> 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<ApplicationInfo> callback) {
if (DEBUG) {
Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime);
}
- final List<PackageInfo> list = injectInstalledPackages(userId);
+ final List<PackageInfo> 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);
}
}
}
- 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
return intent;
}
+ /**
+ * Same as queryIntentActivitiesAsUser, except it makes sure the package is installed,
+ * and only returns exported activities.
+ */
+ @NonNull
+ @VisibleForTesting
+ List<ResolveInfo> 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<ResolveInfo> 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<ResolveInfo> 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);
}
}
+ /**
+ * 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<ResolveInfo> 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);
}
}
+ /**
+ * Return all the enabled, exported and main activities from a package.
+ */
@NonNull
List<ResolveInfo> 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<ResolveInfo> 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);
}
}
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++) {
<service android:name="com.android.server.job.MockPriorityJobService"
android:permission="android.permission.BIND_JOB_SERVICE" />
- <activity android:name="com.android.server.pm.ShortcutManagerTest$ShortcutActivity" />
- <activity android:name="com.android.server.pm.ShortcutManagerTest$ShortcutActivity2" />
- <activity android:name="com.android.server.pm.ShortcutManagerTest$ShortcutActivity3" />
+ <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity" />
+ <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2" />
+ <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3" />
+
+ <activity android:name="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true" android:exported="true" />
+
+ <activity-alias android:name="a.ShortcutEnabled"
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true" android:exported="true">
+ </activity-alias>
+ <activity-alias android:name="a.ShortcutDisabled"
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="false" android:exported="true">
+ <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_5"/>
+ </activity-alias>
+ <activity-alias android:name="a.ShortcutUnexported"
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true" android:exported="false">
+ <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_5"/>
+ </activity-alias>
+ <activity-alias android:name="a.Shortcut1"
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true" android:exported="true">
+ <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_1"/>
+ </activity-alias>
+
+ <activity-alias android:name="a.DisabledMain"
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="false" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
+
+ <activity-alias android:name="a.UnexportedMain"
+ android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+ android:enabled="true" android:exported="false">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
+
</application>
<instrumentation
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
protected static final String TAG = "ShortcutManagerTest";
IUidObserver mUidObserver;
public ShortcutServiceTestable(ServiceContext context, Looper looper) {
- super(context, looper);
+ super(context, looper, /* onyForPackageManagerApis */ false);
mContext = context;
}
}
@Override
- PackageInfo injectPackageInfo(String packageName, @UserIdInt int userId,
+ PackageInfo injectPackageInfoWithUninstalled(String packageName, @UserIdInt int userId,
boolean getSignatures) {
return getInjectedPackageInfo(packageName, userId, getSignatures);
}
@Override
- ApplicationInfo injectApplicationInfo(String packageName, @UserIdInt int userId) {
- PackageInfo pi = injectPackageInfo(packageName, userId, /* getSignatures= */ false);
+ ApplicationInfo injectApplicationInfoWithUninstalled(
+ String packageName, @UserIdInt int userId) {
+ PackageInfo pi = injectPackageInfoWithUninstalled(
+ packageName, userId, /* getSignatures= */ false);
return pi != null ? pi.applicationInfo : null;
}
@Override
- List<PackageInfo> injectInstalledPackages(@UserIdInt int userId) {
- return getInstalledPackages(userId);
+ List<PackageInfo> 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);
}
@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);
}
}
}
- private List<PackageInfo> getInstalledPackages(int userId) {
+ private List<PackageInfo> getInstalledPackagesWithUninstalled(int userId) {
final ArrayList<PackageInfo> ret = new ArrayList<>();
addPackageInfo(getInjectedPackageInfo(CALLING_PACKAGE_1, userId, false), ret);
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()]);
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);
}
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);
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;
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;
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.
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(
--- /dev/null
+/*
+ * 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<PackageInfo> apks = mShortcutService.getInstalledPackages(mMyUserId);
+
+ Set<String> 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));
+
+ }
+}
--- /dev/null
+/*
+ * 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 {
+}