Merge "Validate Computer functions" into sc-dev

This commit is contained in:
Lee Shombert
2021-06-22 00:11:11 +00:00
committed by Android (Google) Code Review
2 changed files with 391 additions and 114 deletions

View File

@@ -436,8 +436,10 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.nio.charset.StandardCharsets;
import java.security.DigestException;
import java.security.DigestInputStream;
@@ -1918,14 +1920,65 @@ public class PackageManagerService extends IPackageManager.Stub
}
/**
* A computer provides the functional interface to the snapshot.
* A {@link Computer} provides a set of functions that can operate on live data or snapshot
* data. At this time, the {@link Computer} is implemented by the
* {@link ComputerEngine}, which is in turn extended by {@link ComputerLocked}.
*
* New functions must be added carefully.
* <ol>
* <li> New functions must be true functions with respect to data collected in a
* {@link Snapshot}. Such data may never be modified from inside a {@link Computer}
* function.
* </li>
*
* <li> A new function must be implemented in {@link ComputerEngine}.
* </li>
*
* <li> A new function must be overridden in {@link ComputerLocked} if the function
* cannot safely access live data without holding the PackageManagerService lock. The
* form of the {@link ComputerLocked} function must be a single call to the
* {@link ComputerEngine} implementation, wrapped in a <code>synchronized</code>
* block. Functions in {@link ComputerLocked} should never include any other code.
* </li>
*
* Care must be taken when deciding if a function should be overridden in
* {@link ComputerLocked}. The complex lock relationships of PackageManagerService
* and other managers (like PermissionManager) mean deadlock is possible. On the
* other hand, not overriding in {@link ComputerLocked} may leave a function walking
* unstable data.
*
* To coax developers to consider such issues carefully, all methods in
* {@link Computer} must be annotated with <code>@LiveImplementation(override =
* MANDATORY)</code> or <code>LiveImplementation(locked = NOT_ALLOWED)</code>. A unit
* test verifies the annotation and that the annotation corresponds to the code in
* {@link ComputerEngine} and {@link ComputerLocked}.
*/
private interface Computer {
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
protected interface Computer {
/**
* Every method must be annotated.
*/
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface LiveImplementation {
// A Computer method must be annotated with one of the following values:
// MANDATORY - the method must be overridden in ComputerEngineLive. The
// format of the override is a call to the super method, wrapped in a
// synchronization block.
// NOT_ALLOWED - the method may not appear in the live computer. It must
// be final in the ComputerEngine.
int MANDATORY = 1;
int NOT_ALLOWED = 2;
int override() default MANDATORY;
String rationale() default "";
}
/**
* Administrative statistics: record that the snapshot has been used. Every call
* to use() increments the usage counter.
*/
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
default void use() {
}
@@ -1933,95 +1986,154 @@ public class PackageManagerService extends IPackageManager.Stub
* Fetch the snapshot usage counter.
* @return The number of times this snapshot was used.
*/
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
default int getUsed() {
return 0;
}
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
@NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
int flags, @PrivateResolveFlags int privateResolveFlags, int filterCallingUid,
int userId, boolean resolveForStart, boolean allowDynamicSplits);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
@NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
int flags, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
@NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, String resolvedType,
int flags, int userId, int callingUid, boolean includeInstantApps);
@LiveImplementation(override = LiveImplementation.MANDATORY)
@NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent,
String resolvedType, int flags, int filterCallingUid, int userId,
boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
String instantAppPkgName);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ActivityInfo getActivityInfo(ComponentName component, int flags, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
int filterCallingUid, int userId);
@LiveImplementation(override = LiveImplementation.MANDATORY)
AndroidPackage getPackage(String packageName);
@LiveImplementation(override = LiveImplementation.MANDATORY)
AndroidPackage getPackage(int uid);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
int filterCallingUid, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ApplicationInfo getApplicationInfo(String packageName, int flags, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
int filterCallingUid, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ComponentName getDefaultHomeActivity(int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType,
int flags, int sourceUserId, int parentUserId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
Intent getHomeIntent();
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
String resolvedType, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
boolean resolveForStart, int userId, Intent intent);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
PackageInfo getPackageInfo(String packageName, int flags, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags,
int filterCallingUid, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
PackageSetting getPackageSetting(String packageName);
@LiveImplementation(override = LiveImplementation.MANDATORY)
PackageSetting getPackageSettingInternal(String packageName, int callingUid);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
int sourceUserId, int targetUserId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ServiceInfo getServiceInfo(ComponentName component, int flags, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version);
@LiveImplementation(override = LiveImplementation.MANDATORY)
String getInstantAppPackageName(int callingUid);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
String resolveExternalPackageNameLPr(AndroidPackage pkg);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
String resolveInternalPackageNameLPr(String packageName, long versionCode);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
String[] getPackagesForUid(int uid);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
UserInfo getProfileParent(int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean canViewInstantApps(int callingUid, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
int flags);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean isCallerSameApp(String packageName, int uid);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean isComponentVisibleToInstantApp(@Nullable ComponentName component);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean isComponentVisibleToInstantApp(@Nullable ComponentName component,
@ComponentType int type);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
String resolvedType, int flags);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean isInstantApp(String packageName, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean isInstantAppInternal(String packageName, @UserIdInt int userId, int callingUid);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
@Nullable ComponentName component, @ComponentType int componentType, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid,
int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
int checkUidPermission(String permName, int uid);
@LiveImplementation(override = LiveImplementation.MANDATORY)
int getPackageUidInternal(String packageName, int flags, int userId, int callingUid);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
int updateFlagsForApplication(int flags, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
int updateFlagsForComponent(int flags, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
int updateFlagsForPackage(int flags, int userId);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
boolean isImplicitImageCaptureIntentAndNotSetByDpc);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
boolean onlyExposedExplicitly, boolean isImplicitImageCaptureIntentAndNotSetByDpc);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell, String message);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell, String message);
@LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell,
boolean requirePermissionWhenSameUser, String message);
@LiveImplementation(override = LiveImplementation.MANDATORY)
SigningDetails getSigningDetails(@NonNull String packageName);
@LiveImplementation(override = LiveImplementation.MANDATORY)
SigningDetails getSigningDetails(int uid);
@LiveImplementation(override = LiveImplementation.MANDATORY)
boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId);
@LiveImplementation(override = LiveImplementation.MANDATORY)
boolean filterAppAccess(String packageName, int callingUid, int userId);
@LiveImplementation(override = LiveImplementation.MANDATORY)
void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState);
}
@@ -2030,7 +2142,8 @@ public class PackageManagerService extends IPackageManager.Stub
* is entirely self-contained - it has no implicit access to
* PackageManagerService.
*/
private static class ComputerEngine implements Computer {
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
protected static class ComputerEngine implements Computer {
// The administrative use counter.
private int mUsed = 0;
@@ -2076,7 +2189,7 @@ public class PackageManagerService extends IPackageManager.Stub
// PackageManagerService attributes that are primitives are referenced through the
// pms object directly. Primitives are the only attributes so referenced.
protected final PackageManagerService mService;
protected boolean safeMode() {
private boolean safeMode() {
return mService.mSafeMode;
}
protected ComponentName resolveComponentName() {
@@ -2130,18 +2243,18 @@ public class PackageManagerService extends IPackageManager.Stub
/**
* Record that the snapshot was used.
*/
public void use() {
public final void use() {
mUsed++;
}
/**
* Return the usage counter.
*/
public int getUsed() {
public final int getUsed() {
return mUsed;
}
public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags,
int filterCallingUid, int userId, boolean resolveForStart,
boolean allowDynamicSplits) {
@@ -2240,14 +2353,14 @@ public class PackageManagerService extends IPackageManager.Stub
resolveForStart, userId, intent);
}
public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int userId) {
return queryIntentActivitiesInternal(
intent, resolvedType, flags, 0 /*privateResolveFlags*/, Binder.getCallingUid(),
userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
}
public @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
public final @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
String resolvedType, int flags, int userId, int callingUid,
boolean includeInstantApps) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
@@ -2471,7 +2584,7 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
public final ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
}
@@ -2481,7 +2594,7 @@ public class PackageManagerService extends IPackageManager.Stub
* to clearing. Because it can only be provided by trusted code, its value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
public ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
public final ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId);
@@ -2535,8 +2648,8 @@ public class PackageManagerService extends IPackageManager.Stub
return pkg;
}
public ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
int filterCallingUid, int userId) {
public final ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName,
int flags, int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null) {
@@ -2563,7 +2676,7 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
public final ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId);
}
@@ -2573,7 +2686,7 @@ public class PackageManagerService extends IPackageManager.Stub
* to clearing. Because it can only be provided by trusted code, its value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
public ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
public final ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForApplication(flags, userId);
@@ -2765,7 +2878,7 @@ public class PackageManagerService extends IPackageManager.Stub
* Report the 'Home' activity which is currently set as "always use this one". If non is set
* then reports the most likely home activity or null if there are more than one.
*/
public ComponentName getDefaultHomeActivity(int userId) {
public final ComponentName getDefaultHomeActivity(int userId) {
List<ResolveInfo> allHomeCandidates = new ArrayList<>();
ComponentName cn = getHomeActivitiesAsUser(allHomeCandidates, userId);
if (cn != null) {
@@ -2793,7 +2906,7 @@ public class PackageManagerService extends IPackageManager.Stub
return lastComponent;
}
public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
public final ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
int userId) {
Intent intent = getHomeIntent();
List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
@@ -2822,7 +2935,7 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
public CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
public final CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
String resolvedType, int flags, int sourceUserId, int parentUserId) {
if (!mUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
sourceUserId)) {
@@ -2868,15 +2981,15 @@ public class PackageManagerService extends IPackageManager.Stub
return result;
}
public Intent getHomeIntent() {
public final Intent getHomeIntent() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.addCategory(Intent.CATEGORY_DEFAULT);
return intent;
}
public List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
String resolvedType, int userId) {
public final List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(
Intent intent, String resolvedType, int userId) {
CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId);
if (resolver != null) {
return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
@@ -2895,7 +3008,8 @@ public class PackageManagerService extends IPackageManager.Stub
* @param intent
* @return A filtered list of resolved activities.
*/
public List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
public final List<ResolveInfo> applyPostResolutionFilter(
@NonNull List<ResolveInfo> resolveInfos,
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
boolean resolveForStart, int userId, Intent intent) {
final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
@@ -3041,7 +3155,7 @@ public class PackageManagerService extends IPackageManager.Stub
return resolveInfos;
}
public List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
int userId) {
final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0;
@@ -3188,7 +3302,7 @@ public class PackageManagerService extends IPackageManager.Stub
return result;
}
public PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
public final PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
if (ps == null) {
return null;
@@ -3258,7 +3372,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
public final PackageInfo getPackageInfo(String packageName, int flags, int userId) {
return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
flags, Binder.getCallingUid(), userId);
}
@@ -3269,7 +3383,7 @@ public class PackageManagerService extends IPackageManager.Stub
* to clearing. Because it can only be provided by trusted code, its value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
public PackageInfo getPackageInfoInternal(String packageName, long versionCode,
public final PackageInfo getPackageInfoInternal(String packageName, long versionCode,
int flags, int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForPackage(flags, userId);
@@ -3340,7 +3454,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Nullable
public PackageSetting getPackageSetting(String packageName) {
public final PackageSetting getPackageSetting(String packageName) {
return getPackageSettingInternal(packageName, Binder.getCallingUid());
}
@@ -3350,7 +3464,7 @@ public class PackageManagerService extends IPackageManager.Stub
return mSettings.getPackageLPr(packageName);
}
public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
public final ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return ParceledListSlice.emptyList();
@@ -3489,7 +3603,7 @@ public class PackageManagerService extends IPackageManager.Stub
return new CrossProfileDomainInfo(forwardingInfo, highestApprovalLevel);
}
public ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
public final ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
int sourceUserId, int targetUserId) {
ResolveInfo forwardingResolveInfo = new ResolveInfo();
final long ident = Binder.clearCallingIdentity();
@@ -3601,7 +3715,7 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
public final ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
@@ -3635,7 +3749,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Nullable
public SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
public final SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
return getSharedLibraryInfo(name, version, mSharedLibraries, null);
}
@@ -3671,7 +3785,7 @@ public class PackageManagerService extends IPackageManager.Stub
return ownerUid;
}
public String resolveExternalPackageNameLPr(AndroidPackage pkg) {
public final String resolveExternalPackageNameLPr(AndroidPackage pkg) {
if (pkg.getStaticSharedLibName() != null) {
return pkg.getManifestPackageName();
}
@@ -3745,7 +3859,7 @@ public class PackageManagerService extends IPackageManager.Stub
return packageName;
}
public String resolveInternalPackageNameLPr(String packageName, long versionCode) {
public final String resolveInternalPackageNameLPr(String packageName, long versionCode) {
final int callingUid = Binder.getCallingUid();
return resolveInternalPackageNameInternalLocked(packageName, versionCode,
callingUid);
@@ -3766,7 +3880,7 @@ public class PackageManagerService extends IPackageManager.Stub
* calls to invalidateGetPackagesForUidCache() to locate the points at
* which the cache is invalidated.
*/
public String[] getPackagesForUid(int uid) {
public final String[] getPackagesForUid(int uid) {
return getPackagesForUidInternal(uid, Binder.getCallingUid());
}
@@ -3807,7 +3921,7 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
public UserInfo getProfileParent(int userId) {
public final UserInfo getProfileParent(int userId) {
final long identity = Binder.clearCallingIdentity();
try {
return mUserManager.getProfileParent(userId);
@@ -3837,7 +3951,7 @@ public class PackageManagerService extends IPackageManager.Stub
* <li>The calling application is the default app prediction service.</li>
* </ol>
*/
public boolean canViewInstantApps(int callingUid, int userId) {
public final boolean canViewInstantApps(int callingUid, int userId) {
if (callingUid < Process.FIRST_APPLICATION_UID) {
return true;
}
@@ -3861,8 +3975,8 @@ public class PackageManagerService extends IPackageManager.Stub
return false;
}
public boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
int flags) {
public final boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid,
int userId, int flags) {
// Callers can access only the libs they depend on, otherwise they need to explicitly
// ask for the shared libraries given the caller is allowed to access all static libs.
if ((flags & PackageManager.MATCH_STATIC_SHARED_LIBRARIES) != 0) {
@@ -3945,13 +4059,13 @@ public class PackageManagerService extends IPackageManager.Stub
== PackageManager.PERMISSION_GRANTED;
}
public boolean isCallerSameApp(String packageName, int uid) {
public final boolean isCallerSameApp(String packageName, int uid) {
AndroidPackage pkg = mPackages.get(packageName);
return pkg != null
&& UserHandle.getAppId(uid) == pkg.getUid();
}
public boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
public final boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
if (isComponentVisibleToInstantApp(component, TYPE_ACTIVITY)) {
return true;
}
@@ -3964,7 +4078,7 @@ public class PackageManagerService extends IPackageManager.Stub
return false;
}
public boolean isComponentVisibleToInstantApp(
public final boolean isComponentVisibleToInstantApp(
@Nullable ComponentName component, @ComponentType int type) {
if (type == TYPE_ACTIVITY) {
final ParsedActivity activity = mComponentResolver.getActivity(component);
@@ -4012,13 +4126,13 @@ public class PackageManagerService extends IPackageManager.Stub
* @return {@code true} if the intent is a camera intent and the persistent preferred
* activity was not set by the DPC.
*/
public boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
String resolvedType, int flags) {
public final boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent,
int userId, String resolvedType, int flags) {
return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm(
intent, userId, resolvedType, flags);
}
public boolean isInstantApp(String packageName, int userId) {
public final boolean isInstantApp(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
false /* checkShell */, "isInstantApp");
@@ -4026,7 +4140,7 @@ public class PackageManagerService extends IPackageManager.Stub
return isInstantAppInternal(packageName, userId, callingUid);
}
public boolean isInstantAppInternal(String packageName, @UserIdInt int userId,
public final boolean isInstantAppInternal(String packageName, @UserIdInt int userId,
int callingUid) {
if (HIDE_EPHEMERAL_APIS) {
return false;
@@ -4160,7 +4274,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
public boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
public final boolean isSameProfileGroup(@UserIdInt int callerUserId,
@UserIdInt int userId) {
final long identity = Binder.clearCallingIdentity();
try {
return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId);
@@ -4187,7 +4302,8 @@ public class PackageManagerService extends IPackageManager.Stub
*
* @see #canViewInstantApps(int, int)
*/
public boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
public final boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps,
int callingUid,
@Nullable ComponentName component, @ComponentType int componentType, int userId) {
// if we're in an isolated process, get the real calling UID
if (Process.isIsolated(callingUid)) {
@@ -4247,7 +4363,7 @@ public class PackageManagerService extends IPackageManager.Stub
/**
* @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
*/
public boolean shouldFilterApplicationLocked(
public final boolean shouldFilterApplicationLocked(
@Nullable PackageSetting ps, int callingUid, int userId) {
return shouldFilterApplicationLocked(ps, callingUid, null, TYPE_UNKNOWN, userId);
}
@@ -4255,8 +4371,8 @@ public class PackageManagerService extends IPackageManager.Stub
/**
* @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
*/
public boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid,
int userId) {
public final boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus,
int callingUid, int userId) {
boolean filterApp = true;
for (int index = sus.packages.size() - 1; index >= 0 && filterApp; index--) {
filterApp &= shouldFilterApplicationLocked(sus.packages.valueAt(index),
@@ -4280,7 +4396,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
// NOTE: Can't remove without a major refactor. Keep around for now.
public int checkUidPermission(String permName, int uid) {
public final int checkUidPermission(String permName, int uid) {
return mPermissionManager.checkUidPermission(uid, permName);
}
@@ -4330,21 +4446,21 @@ public class PackageManagerService extends IPackageManager.Stub
/**
* Update given flags when being used to request {@link ApplicationInfo}.
*/
public int updateFlagsForApplication(int flags, int userId) {
public final int updateFlagsForApplication(int flags, int userId) {
return updateFlagsForPackage(flags, userId);
}
/**
* Update given flags when being used to request {@link ComponentInfo}.
*/
public int updateFlagsForComponent(int flags, int userId) {
public final int updateFlagsForComponent(int flags, int userId) {
return updateFlags(flags, userId);
}
/**
* Update given flags when being used to request {@link PackageInfo}.
*/
public int updateFlagsForPackage(int flags, int userId) {
public final int updateFlagsForPackage(int flags, int userId) {
final boolean isCallerSystemUser = UserHandle.getCallingUserId()
== UserHandle.USER_SYSTEM;
if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
@@ -4380,14 +4496,14 @@ public class PackageManagerService extends IPackageManager.Stub
* action and a {@code android.intent.category.BROWSABLE} category</li>
* </ul>
*/
public int updateFlagsForResolve(int flags, int userId, int callingUid,
public final int updateFlagsForResolve(int flags, int userId, int callingUid,
boolean wantInstantApps, boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
return updateFlagsForResolve(flags, userId, callingUid,
wantInstantApps, false /*onlyExposedExplicitly*/,
isImplicitImageCaptureIntentAndNotSetByDpc);
}
public int updateFlagsForResolve(int flags, int userId, int callingUid,
public final int updateFlagsForResolve(int flags, int userId, int callingUid,
boolean wantInstantApps, boolean onlyExposedExplicitly,
boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
// Safe mode means we shouldn't match any third-party components
@@ -4429,7 +4545,7 @@ public class PackageManagerService extends IPackageManager.Stub
* @param checkShell whether to prevent shell from access if there's a debugging restriction
* @param message the message to log on security exception
*/
public void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
public final void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell, String message) {
if (userId < 0) {
throw new IllegalArgumentException("Invalid userId " + userId);
@@ -4467,7 +4583,7 @@ public class PackageManagerService extends IPackageManager.Stub
* @param checkShell whether to prevent shell from access if there's a debugging restriction
* @param message the message to log on security exception
*/
public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
public final void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell, String message) {
enforceCrossUserPermission(callingUid, userId, requireFullPermission, checkShell, false,
message);
@@ -4484,7 +4600,7 @@ public class PackageManagerService extends IPackageManager.Stub
* reference the same user.
* @param message the message to log on security exception
*/
public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
public final void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell,
boolean requirePermissionWhenSameUser, String message) {
if (userId < 0) {
@@ -4736,31 +4852,14 @@ public class PackageManagerService extends IPackageManager.Stub
}
/**
* The live computer differs from the ComputerEngine in the methods that fetch data
* from PackageManagerService.
**/
private static class ComputerEngineLive extends ComputerEngine {
ComputerEngineLive(Snapshot args) {
super(args);
}
protected ComponentName resolveComponentName() {
return mService.mResolveComponentName;
}
protected ActivityInfo instantAppInstallerActivity() {
return mService.mInstantAppInstallerActivity;
}
protected ApplicationInfo androidApplication() {
return mService.mAndroidApplication;
}
}
/**
* This subclass is the external interface to the live computer. For each
* interface, it takes the PM lock and then delegates to the live
* computer engine. This is required because there are no locks taken in
* the engine itself.
* This subclass is the external interface to the live computer. Some internal helper
* methods are overridden to fetch live data instead of snapshot data. For each
* Computer interface that is overridden in this class, the override takes the PM lock
* and then delegates to the live computer engine. This is required because there are
* no locks taken in the engine itself.
*/
private static class ComputerLocked extends ComputerEngineLive {
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
protected static class ComputerLocked extends ComputerEngine {
private final Object mLock;
ComputerLocked(Snapshot args) {
@@ -4768,18 +4867,17 @@ public class PackageManagerService extends IPackageManager.Stub
mLock = mService.mLock;
}
/**
* Explicilty snapshot {@link Settings#mPackages} for cases where the caller must not lock
* in order to get package data. It is expected that the caller locks itself to be able
* to block on changes to the package data and bring itself up to date once the change
* propagates to it. Use with heavy caution.
* @return
*/
private Map<String, PackageSetting> snapshotPackageSettings() {
return mSettings.snapshot().mPackages;
protected final ComponentName resolveComponentName() {
return mService.mResolveComponentName;
}
protected final ActivityInfo instantAppInstallerActivity() {
return mService.mInstantAppInstallerActivity;
}
protected final ApplicationInfo androidApplication() {
return mService.mAndroidApplication;
}
public @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
public final @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
String resolvedType, int flags, int userId, int callingUid,
String instantAppPkgName) {
synchronized (mLock) {
@@ -4787,8 +4885,8 @@ public class PackageManagerService extends IPackageManager.Stub
callingUid, instantAppPkgName);
}
}
public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent,
String resolvedType, int flags, int filterCallingUid, int userId,
public final @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(
Intent intent, String resolvedType, int flags, int filterCallingUid, int userId,
boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
String instantAppPkgName) {
synchronized (mLock) {
@@ -4797,31 +4895,31 @@ public class PackageManagerService extends IPackageManager.Stub
instantAppPkgName);
}
}
public ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
public final ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
int filterCallingUid, int userId) {
synchronized (mLock) {
return super.getActivityInfoInternalBody(component, flags, filterCallingUid,
userId);
}
}
public AndroidPackage getPackage(String packageName) {
public final AndroidPackage getPackage(String packageName) {
synchronized (mLock) {
return super.getPackage(packageName);
}
}
public AndroidPackage getPackage(int uid) {
public final AndroidPackage getPackage(int uid) {
synchronized (mLock) {
return super.getPackage(uid);
}
}
public ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
public final ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
int filterCallingUid, int userId) {
synchronized (mLock) {
return super.getApplicationInfoInternalBody(packageName, flags, filterCallingUid,
userId);
}
}
public ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
public final ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
Intent intent, int matchFlags, List<ResolveInfo> candidates,
CrossProfileDomainInfo xpDomainInfo, int userId, boolean debug) {
synchronized (mLock) {
@@ -4829,49 +4927,49 @@ public class PackageManagerService extends IPackageManager.Stub
matchFlags, candidates, xpDomainInfo, userId, debug);
}
}
public PackageInfo getPackageInfoInternalBody(String packageName, long versionCode,
public final PackageInfo getPackageInfoInternalBody(String packageName, long versionCode,
int flags, int filterCallingUid, int userId) {
synchronized (mLock) {
return super.getPackageInfoInternalBody(packageName, versionCode, flags,
filterCallingUid, userId);
}
}
public PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
public final PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
synchronized (mLock) {
return super.getPackageSettingInternal(packageName, callingUid);
}
}
public ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
public final ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
int callingUid) {
synchronized (mLock) {
return super.getInstalledPackagesBody(flags, userId, callingUid);
}
}
public ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
public final ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
int callingUid) {
synchronized (mLock) {
return super.getServiceInfoBody(component, flags, userId, callingUid);
}
}
public String getInstantAppPackageName(int callingUid) {
public final String getInstantAppPackageName(int callingUid) {
synchronized (mLock) {
return super.getInstantAppPackageName(callingUid);
}
}
public String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
public final String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
boolean isCallerInstantApp) {
synchronized (mLock) {
return super.getPackagesForUidInternalBody(callingUid, userId, appId,
isCallerInstantApp);
}
}
public boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
public final boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
int callingUid) {
synchronized (mLock) {
return super.isInstantAppInternalBody(packageName, userId, callingUid);
}
}
public boolean isInstantAppResolutionAllowedBody(Intent intent,
public final boolean isInstantAppResolutionAllowedBody(Intent intent,
List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck,
int flags) {
synchronized (mLock) {
@@ -4879,33 +4977,33 @@ public class PackageManagerService extends IPackageManager.Stub
skipPackageCheck, flags);
}
}
public int getPackageUidInternal(String packageName, int flags, int userId,
public final int getPackageUidInternal(String packageName, int flags, int userId,
int callingUid) {
synchronized (mLock) {
return super.getPackageUidInternal(packageName, flags, userId, callingUid);
}
}
public SigningDetails getSigningDetails(@NonNull String packageName) {
public final SigningDetails getSigningDetails(@NonNull String packageName) {
synchronized (mLock) {
return super.getSigningDetails(packageName);
}
}
public SigningDetails getSigningDetails(int uid) {
public final SigningDetails getSigningDetails(int uid) {
synchronized (mLock) {
return super.getSigningDetails(uid);
}
}
public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
public final boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
synchronized (mLock) {
return super.filterAppAccess(pkg, callingUid, userId);
}
}
public boolean filterAppAccess(String packageName, int callingUid, int userId) {
public final boolean filterAppAccess(String packageName, int callingUid, int userId) {
synchronized (mLock) {
return super.filterAppAccess(packageName, callingUid, userId);
}
}
public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
public final void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
synchronized (mLock) {
super.dump(type, fd, pw, dumpState);
}

View File

@@ -18,7 +18,11 @@ package com.android.server.pm;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
import static java.lang.reflect.Modifier.isFinal;
import static java.lang.reflect.Modifier.isPrivate;
import static java.lang.reflect.Modifier.isProtected;
import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
@@ -44,9 +48,12 @@ import org.junit.runner.RunWith;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;
@@ -393,6 +400,178 @@ public class PackageManagerServiceTest {
Assert.assertEquals(3600000003L, multiPackage[1].timeouts.maxPendingTimeUs);
}
// Report an error from the Computer structure validation test.
private void flag(String name, String msg) {
fail(name + " " + msg);
}
// Return a string that identifies a Method. This is not very efficient but it is not
// called very often.
private String displayName(Method m) {
String r = m.getName();
String p = Arrays.toString(m.getGenericParameterTypes())
.replaceAll("([a-zA-Z0-9]+\\.)+", "")
.replace("class ", "")
.replaceAll("^\\[", "(")
.replaceAll("\\]$", ")");
return r + p;
}
// Match a method to an array of Methods. Matching is on method signature: name and
// parameter types. If a method in the declared array matches, return it. Otherwise
// return null.
private Method matchMethod(Method m, Method[] declared) {
String n = m.getName();
Type[] t = m.getGenericParameterTypes();
for (int i = 0; i < declared.length; i++) {
Method l = declared[i];
if (l != null && l.getName().equals(n)
&& Arrays.equals(l.getGenericParameterTypes(), t)) {
Method result = l;
// Set the method to null since it has been visited already.
declared[i] = null;
return result;
}
}
return null;
}
// Return the boolean locked value. A null return means the annotation was not
// found. This method will fail if the annotation is found but is not one of the
// known constants.
private Boolean getOverride(Method m) {
final String name = "Computer." + displayName(m);
final PackageManagerService.Computer.LiveImplementation annotation =
m.getAnnotation(PackageManagerService.Computer.LiveImplementation.class);
if (annotation == null) {
return null;
}
final int override = annotation.override();
if (override == PackageManagerService.Computer.LiveImplementation.MANDATORY) {
return true;
} else if (override == PackageManagerService.Computer.LiveImplementation.NOT_ALLOWED) {
return false;
} else {
flag(name, "invalid Live value: " + override);
return null;
}
}
@Test
public void testComputerStructure() {
// Verify that Copmuter methods are properly annotated and that ComputerLocked is
// properly populated per annotations.
// Call PackageManagerService.validateComputer();
Class base = PackageManagerService.Computer.class;
HashMap<Method, Boolean> methodType = new HashMap<>();
// Verify that all Computer methods are annotated and that the annotation
// parameter locked() is valid.
for (Method m : base.getDeclaredMethods()) {
final String name = "Computer." + displayName(m);
Boolean override = getOverride(m);
if (override == null) {
flag(name, "missing required Live annotation");
}
methodType.put(m, override);
}
Class coreClass = PackageManagerService.ComputerEngine.class;
final Method[] coreMethods = coreClass.getDeclaredMethods();
// Examine every method in the core. If it inherits from a base method it must be
// "public final" if the base is NOT_ALLOWED or "public" if the base is MANDATORY.
// If the core method does not inherit from the base then it must be either
// private or protected.
for (Method m : base.getDeclaredMethods()) {
String name = "Computer." + displayName(m);
final boolean locked = methodType.get(m);
final Method core = matchMethod(m, coreMethods);
if (core == null) {
flag(name, "not overridden in ComputerEngine");
continue;
}
name = "ComputerEngine." + displayName(m);
final int modifiers = core.getModifiers();
if (!locked) {
if (!isPublic(modifiers)) {
flag(name, "is not public");
}
if (!isFinal(modifiers)) {
flag(name, "is not final");
}
}
}
// Any methods left in the coreMethods array must be private or protected.
// Protected methods must be overridden (and final) in the live list.
Method[] coreHelpers = new Method[coreMethods.length];
int coreIndex = 0;
for (Method m : coreMethods) {
if (m != null) {
final String name = "ComputerEngine." + displayName(m);
final int modifiers = m.getModifiers();
if (isPrivate(modifiers)) {
// Okay
} else if (isProtected(modifiers)) {
coreHelpers[coreIndex++] = m;
} else {
flag(name, "is neither private nor protected");
}
}
}
Class liveClass = PackageManagerService.ComputerLocked.class;
final Method[] liveMethods = liveClass.getDeclaredMethods();
// Examine every method in the live list. Every method must be final and must
// inherit either from base or core. If the method inherits from a base method
// then the base must be MANDATORY.
for (Method m : base.getDeclaredMethods()) {
String name = "Computer." + displayName(m);
final boolean locked = methodType.get(m);
final Method live = matchMethod(m, liveMethods);
if (live == null) {
if (locked) {
flag(name, "not overridden in ComputerLocked");
}
continue;
}
if (!locked) {
flag(name, "improperly overridden in ComputerLocked");
continue;
}
name = "ComputerLocked." + displayName(m);
final int modifiers = live.getModifiers();
if (!locked) {
if (!isPublic(modifiers)) {
flag(name, "is not public");
}
if (!isFinal(modifiers)) {
flag(name, "is not final");
}
}
}
for (Method m : coreHelpers) {
if (m == null) {
continue;
}
String name = "ComputerLocked." + displayName(m);
final Method live = matchMethod(m, liveMethods);
if (live == null) {
flag(name, "is not overridden in ComputerLocked");
continue;
}
}
for (Method m : liveMethods) {
if (m != null) {
String name = "ComputerLocked." + displayName(m);
flag(name, "illegal local method");
}
}
}
private static PerPackageReadTimeouts[] getPerPackageReadTimeouts(String knownDigestersList) {
final String defaultTimeouts = "3600000001:3600000002:3600000003";
List<PerPackageReadTimeouts> result = PerPackageReadTimeouts.parseDigestersList(