Merge "ShortcutManager: Make sure persisted default launcher still exists." into nyc-mr1-dev
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -232,6 +232,13 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
|
||||
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")
|
||||
@@ -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
|
||||
ActivityInfo injectGetActivityInfoWithMetadata(ComponentName activity, @UserIdInt int userId) {
|
||||
final ActivityInfo getActivityInfoWithMetadata(ComponentName activity, @UserIdInt int userId) {
|
||||
return isInstalledOrNull(injectGetActivityInfoWithMetadataWithUninstalled(
|
||||
activity, userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use directly; this returns uninstalled packages too.
|
||||
*/
|
||||
@Nullable
|
||||
@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<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);
|
||||
@@ -2563,17 +2629,31 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@@ -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<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);
|
||||
|
||||
@@ -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<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);
|
||||
|
||||
@@ -2660,19 +2788,15 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
final List<ResolveInfo> resolved =
|
||||
mContext.getPackageManager().queryIntentActivitiesAsUser(
|
||||
intent, PACKAGE_MATCH_FLAGS, userId);
|
||||
|
||||
return (resolved != null) ? resolved : new ArrayList<>(0);
|
||||
return queryActivities(getMainActivityIntent(), packageName, null, userId);
|
||||
} finally {
|
||||
injectRestoreCallingIdentity(token);
|
||||
|
||||
@@ -2680,6 +2804,24 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.IS_ACTIVITY_ENABLED, start);
|
||||
}
|
||||
}
|
||||
|
||||
boolean injectIsSafeModeEnabled() {
|
||||
final long token = injectClearCallingIdentity();
|
||||
try {
|
||||
@@ -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++) {
|
||||
|
||||
@@ -108,9 +108,53 @@
|
||||
<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
|
||||
|
||||
@@ -103,8 +103,6 @@ import java.util.Set;
|
||||
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";
|
||||
@@ -206,7 +204,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
|
||||
IUidObserver mUidObserver;
|
||||
|
||||
public ShortcutServiceTestable(ServiceContext context, Looper looper) {
|
||||
super(context, looper);
|
||||
super(context, looper, /* onyForPackageManagerApis */ false);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@@ -301,24 +299,26 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
@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);
|
||||
@@ -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<PackageInfo> getInstalledPackages(int userId) {
|
||||
private List<PackageInfo> getInstalledPackagesWithUninstalled(int userId) {
|
||||
final ArrayList<PackageInfo> 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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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<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));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
Reference in New Issue
Block a user