Changing IntentSender to become a listener.
Listener is more secure as nobody can send a broadcast with invalid digests. Bug: 160605420 Test: atest PackageManagerShellCommandTest PackageManagerShellCommandIncrementalTest IncrementalServiceTest PackageManagerServiceTest ChecksumsTest Change-Id: I5f6ec3f0fa975d274d456d11d57be61d0b245e80
This commit is contained in:
@@ -12298,7 +12298,7 @@ package android.content.pm {
|
||||
method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
|
||||
method public abstract void removePermission(@NonNull String);
|
||||
method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
|
||||
method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
|
||||
method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
|
||||
method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
|
||||
method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
|
||||
method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
|
||||
@@ -12318,7 +12318,6 @@ package android.content.pm {
|
||||
field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
|
||||
field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
|
||||
field public static final int DONT_KILL_APP = 1; // 0x1
|
||||
field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
|
||||
field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
|
||||
field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
|
||||
field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
|
||||
@@ -12504,6 +12503,10 @@ package android.content.pm {
|
||||
ctor public PackageManager.NameNotFoundException(String);
|
||||
}
|
||||
|
||||
@java.lang.FunctionalInterface public static interface PackageManager.OnChecksumsReadyListener {
|
||||
method public void onChecksumsReady(@NonNull java.util.List<android.content.pm.ApkChecksum>);
|
||||
}
|
||||
|
||||
public static final class PackageManager.Property implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public boolean getBoolean();
|
||||
|
||||
@@ -39,11 +39,13 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApkChecksum;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ChangedPackages;
|
||||
import android.content.pm.Checksum;
|
||||
import android.content.pm.ComponentInfo;
|
||||
import android.content.pm.FeatureInfo;
|
||||
import android.content.pm.IOnChecksumsReadyListener;
|
||||
import android.content.pm.IPackageDataObserver;
|
||||
import android.content.pm.IPackageDeleteObserver;
|
||||
import android.content.pm.IPackageManager;
|
||||
@@ -880,10 +882,10 @@ public class ApplicationPackageManager extends PackageManager {
|
||||
@Override
|
||||
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
|
||||
@Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
|
||||
@NonNull IntentSender statusReceiver)
|
||||
@NonNull OnChecksumsReadyListener onChecksumsReadyListener)
|
||||
throws CertificateEncodingException, NameNotFoundException {
|
||||
Objects.requireNonNull(packageName);
|
||||
Objects.requireNonNull(statusReceiver);
|
||||
Objects.requireNonNull(onChecksumsReadyListener);
|
||||
Objects.requireNonNull(trustedInstallers);
|
||||
try {
|
||||
if (trustedInstallers == TRUST_ALL) {
|
||||
@@ -895,8 +897,17 @@ public class ApplicationPackageManager extends PackageManager {
|
||||
"trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty "
|
||||
+ "list of certificates.");
|
||||
}
|
||||
IOnChecksumsReadyListener onChecksumsReadyListenerDelegate =
|
||||
new IOnChecksumsReadyListener.Stub() {
|
||||
@Override
|
||||
public void onChecksumsReady(List<ApkChecksum> checksums)
|
||||
throws RemoteException {
|
||||
onChecksumsReadyListener.onChecksumsReady(checksums);
|
||||
}
|
||||
};
|
||||
mPM.requestChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
|
||||
encodeCertificates(trustedInstallers), statusReceiver, getUserId());
|
||||
encodeCertificates(trustedInstallers), onChecksumsReadyListenerDelegate,
|
||||
getUserId());
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(PackageManager.NameNotFoundException.class);
|
||||
throw new RuntimeException(e);
|
||||
|
||||
27
core/java/android/content/pm/IOnChecksumsReadyListener.aidl
Normal file
27
core/java/android/content/pm/IOnChecksumsReadyListener.aidl
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 android.content.pm;
|
||||
|
||||
import android.content.pm.ApkChecksum;
|
||||
|
||||
/**
|
||||
* Listener that gets notified when checksums are available.
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface IOnChecksumsReadyListener {
|
||||
void onChecksumsReady(in List<ApkChecksum> checksums);
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import android.content.pm.InstantAppInfo;
|
||||
import android.content.pm.FeatureInfo;
|
||||
import android.content.pm.IDexModuleRegisterCallback;
|
||||
import android.content.pm.InstallSourceInfo;
|
||||
import android.content.pm.IOnChecksumsReadyListener;
|
||||
import android.content.pm.IPackageInstaller;
|
||||
import android.content.pm.IPackageDeleteObserver;
|
||||
import android.content.pm.IPackageDeleteObserver2;
|
||||
@@ -754,7 +755,7 @@ interface IPackageManager {
|
||||
|
||||
void notifyPackagesReplacedReceived(in String[] packages);
|
||||
|
||||
void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
|
||||
void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
@@ -3858,13 +3858,6 @@ public abstract class PackageManager {
|
||||
public static final String EXTRA_FAILURE_EXISTING_PERMISSION
|
||||
= "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
|
||||
|
||||
/**
|
||||
* Extra field name for the ID of a package pending verification. Passed to
|
||||
* a package verifier and is used to call back to
|
||||
* @see #requestChecksums
|
||||
*/
|
||||
public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
|
||||
|
||||
/**
|
||||
* Permission flag: The permission is set in its current state
|
||||
* by the user and apps can still request it at runtime.
|
||||
@@ -8709,9 +8702,20 @@ public abstract class PackageManager {
|
||||
*/
|
||||
public static final @NonNull List<Certificate> TRUST_NONE = Collections.singletonList(null);
|
||||
|
||||
/** Listener that gets notified when checksums are available. */
|
||||
@FunctionalInterface
|
||||
public interface OnChecksumsReadyListener {
|
||||
/**
|
||||
* Called when the checksums are available.
|
||||
*
|
||||
* @param checksums array of checksums.
|
||||
*/
|
||||
void onChecksumsReady(@NonNull List<ApkChecksum> checksums);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requesting the checksums for APKs within a package.
|
||||
* The checksums will be returned asynchronously via statusReceiver.
|
||||
* The checksums will be returned asynchronously via onChecksumsReadyListener.
|
||||
*
|
||||
* By default returns all readily available checksums:
|
||||
* - enforced by platform,
|
||||
@@ -8730,15 +8734,14 @@ public abstract class PackageManager {
|
||||
* {@link #TRUST_ALL} will return checksums from any installer,
|
||||
* {@link #TRUST_NONE} disables optimized installer-enforced checksums,
|
||||
* otherwise the list has to be non-empty list of certificates.
|
||||
* @param statusReceiver called once when the results are available as
|
||||
* {@link #EXTRA_CHECKSUMS} of type {@link ApkChecksum}[].
|
||||
* @param onChecksumsReadyListener called once when the results are available.
|
||||
* @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
|
||||
* @throws IllegalArgumentException if the list of trusted installer certificates is empty.
|
||||
* @throws NameNotFoundException if a package with the given name cannot be found on the system.
|
||||
*/
|
||||
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
|
||||
@Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
|
||||
@NonNull IntentSender statusReceiver)
|
||||
@NonNull OnChecksumsReadyListener onChecksumsReadyListener)
|
||||
throws CertificateEncodingException, NameNotFoundException {
|
||||
throw new UnsupportedOperationException("requestChecksums not implemented in subclass");
|
||||
}
|
||||
|
||||
@@ -1137,7 +1137,8 @@ public abstract class PackageManagerInternal {
|
||||
*/
|
||||
public abstract void requestChecksums(@NonNull String packageName, boolean includeSplits,
|
||||
@Checksum.Type int optional, @Checksum.Type int required,
|
||||
@Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
|
||||
@Nullable List trustedInstallers,
|
||||
@NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
|
||||
@NonNull Executor executor, @NonNull Handler handler);
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,6 @@ import static android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256;
|
||||
import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
|
||||
import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
|
||||
import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
|
||||
import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
|
||||
import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
|
||||
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
|
||||
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
|
||||
@@ -32,15 +31,15 @@ import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKE
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.ApkChecksum;
|
||||
import android.content.pm.Checksum;
|
||||
import android.content.pm.IOnChecksumsReadyListener;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.PackageParser;
|
||||
import android.content.pm.Signature;
|
||||
import android.content.pm.parsing.ApkLiteParseUtils;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.os.incremental.IncrementalManager;
|
||||
import android.os.incremental.IncrementalStorage;
|
||||
@@ -294,21 +293,21 @@ public class ApkChecksums {
|
||||
/**
|
||||
* Fetch or calculate checksums for the collection of files.
|
||||
*
|
||||
* @param filesToChecksum split name, null for base and File to fetch checksums for
|
||||
* @param optional mask to fetch readily available checksums
|
||||
* @param required mask to forcefully calculate if not available
|
||||
* @param installerPackageName package name of the installer of the packages
|
||||
* @param trustedInstallers array of certificate to trust, two specific cases:
|
||||
* null - trust anybody,
|
||||
* [] - trust nobody.
|
||||
* @param statusReceiver to receive the resulting checksums
|
||||
* @param filesToChecksum split name, null for base and File to fetch checksums for
|
||||
* @param optional mask to fetch readily available checksums
|
||||
* @param required mask to forcefully calculate if not available
|
||||
* @param installerPackageName package name of the installer of the packages
|
||||
* @param trustedInstallers array of certificate to trust, two specific cases:
|
||||
* null - trust anybody,
|
||||
* [] - trust nobody.
|
||||
* @param onChecksumsReadyListener to receive the resulting checksums
|
||||
*/
|
||||
public static void getChecksums(List<Pair<String, File>> filesToChecksum,
|
||||
@Checksum.Type int optional,
|
||||
@Checksum.Type int required,
|
||||
@Nullable String installerPackageName,
|
||||
@Nullable Certificate[] trustedInstallers,
|
||||
@NonNull IntentSender statusReceiver,
|
||||
@NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
|
||||
@NonNull Injector injector) {
|
||||
List<Map<Integer, ApkChecksum>> result = new ArrayList<>(filesToChecksum.size());
|
||||
for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
|
||||
@@ -326,14 +325,14 @@ public class ApkChecksums {
|
||||
}
|
||||
|
||||
long startTime = SystemClock.uptimeMillis();
|
||||
processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector,
|
||||
startTime);
|
||||
processRequiredChecksums(filesToChecksum, result, required, onChecksumsReadyListener,
|
||||
injector, startTime);
|
||||
}
|
||||
|
||||
private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum,
|
||||
List<Map<Integer, ApkChecksum>> result,
|
||||
@Checksum.Type int required,
|
||||
@NonNull IntentSender statusReceiver,
|
||||
@NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
|
||||
@NonNull Injector injector,
|
||||
long startTime) {
|
||||
final boolean timeout =
|
||||
@@ -350,7 +349,7 @@ public class ApkChecksums {
|
||||
// Not ready, come back later.
|
||||
injector.getHandler().postDelayed(() -> {
|
||||
processRequiredChecksums(filesToChecksum, result, required,
|
||||
statusReceiver, injector, startTime);
|
||||
onChecksumsReadyListener, injector, startTime);
|
||||
}, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS);
|
||||
return;
|
||||
}
|
||||
@@ -363,13 +362,9 @@ public class ApkChecksums {
|
||||
}
|
||||
}
|
||||
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra(EXTRA_CHECKSUMS,
|
||||
allChecksums.toArray(new ApkChecksum[allChecksums.size()]));
|
||||
|
||||
try {
|
||||
statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null);
|
||||
} catch (IntentSender.SendIntentException e) {
|
||||
onChecksumsReadyListener.onChecksumsReady(allChecksums);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +177,7 @@ import android.content.pm.DataLoaderType;
|
||||
import android.content.pm.FallbackCategoryProvider;
|
||||
import android.content.pm.FeatureInfo;
|
||||
import android.content.pm.IDexModuleRegisterCallback;
|
||||
import android.content.pm.IOnChecksumsReadyListener;
|
||||
import android.content.pm.IPackageChangeObserver;
|
||||
import android.content.pm.IPackageDataObserver;
|
||||
import android.content.pm.IPackageDeleteObserver;
|
||||
@@ -5521,19 +5522,19 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
|
||||
@Checksum.Type int optional,
|
||||
@Checksum.Type int required, @Nullable List trustedInstallers,
|
||||
@NonNull IntentSender statusReceiver, int userId) {
|
||||
@NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) {
|
||||
requestChecksumsInternal(packageName, includeSplits, optional, required, trustedInstallers,
|
||||
statusReceiver, userId, mInjector.getBackgroundExecutor(),
|
||||
onChecksumsReadyListener, userId, mInjector.getBackgroundExecutor(),
|
||||
mInjector.getBackgroundHandler());
|
||||
}
|
||||
|
||||
private void requestChecksumsInternal(@NonNull String packageName, boolean includeSplits,
|
||||
@Checksum.Type int optional,
|
||||
@Checksum.Type int required, @Nullable List trustedInstallers,
|
||||
@NonNull IntentSender statusReceiver, int userId, @NonNull Executor executor,
|
||||
@NonNull Handler handler) {
|
||||
@Checksum.Type int optional, @Checksum.Type int required,
|
||||
@Nullable List trustedInstallers,
|
||||
@NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
|
||||
@NonNull Executor executor, @NonNull Handler handler) {
|
||||
Objects.requireNonNull(packageName);
|
||||
Objects.requireNonNull(statusReceiver);
|
||||
Objects.requireNonNull(onChecksumsReadyListener);
|
||||
Objects.requireNonNull(executor);
|
||||
Objects.requireNonNull(handler);
|
||||
|
||||
@@ -5569,7 +5570,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
() -> mInjector.getIncrementalManager(),
|
||||
() -> mPmInternal);
|
||||
ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName,
|
||||
trustedCerts, statusReceiver, injector);
|
||||
trustedCerts, onChecksumsReadyListener, injector);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27108,12 +27109,14 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
ps.setStatesOnCrashOrAnr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
|
||||
@Checksum.Type int optional, @Checksum.Type int required,
|
||||
@Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
|
||||
@Nullable List trustedInstallers,
|
||||
@NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
|
||||
@NonNull Executor executor, @NonNull Handler handler) {
|
||||
requestChecksumsInternal(packageName, includeSplits, optional, required,
|
||||
trustedInstallers, statusReceiver, userId, executor, handler);
|
||||
trustedInstallers, onChecksumsReadyListener, userId, executor, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,22 +16,16 @@
|
||||
|
||||
package com.android.server.pm;
|
||||
|
||||
import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
|
||||
|
||||
import android.app.admin.SecurityLog;
|
||||
import android.content.Context;
|
||||
import android.content.IIntentReceiver;
|
||||
import android.content.IIntentSender;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.ApkChecksum;
|
||||
import android.content.pm.Checksum;
|
||||
import android.content.pm.IOnChecksumsReadyListener;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
@@ -39,7 +33,6 @@ import android.util.Slog;
|
||||
import com.android.internal.os.BackgroundThread;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -109,26 +102,23 @@ public final class ProcessLoggingHandler extends Handler {
|
||||
// Capturing local loggingInfo to still log even if hash was invalidated.
|
||||
try {
|
||||
pmi.requestChecksums(packageName, false, 0, CHECKSUM_TYPE, null,
|
||||
new IntentSender((IIntentSender) new IIntentSender.Stub() {
|
||||
new IOnChecksumsReadyListener.Stub() {
|
||||
@Override
|
||||
public void send(int code, Intent intent, String resolvedType,
|
||||
IBinder allowlistToken, IIntentReceiver finishedReceiver,
|
||||
String requiredPermission, Bundle options) {
|
||||
processChecksums(loggingInfo, intent);
|
||||
public void onChecksumsReady(List<ApkChecksum> checksums)
|
||||
throws RemoteException {
|
||||
processChecksums(loggingInfo, checksums);
|
||||
}
|
||||
}), context.getUserId(), mExecutor, this);
|
||||
}, context.getUserId(),
|
||||
mExecutor, this);
|
||||
} catch (Throwable t) {
|
||||
Slog.e(TAG, "requestChecksums() failed", t);
|
||||
enqueueProcessChecksum(loggingInfo, null);
|
||||
}
|
||||
}
|
||||
|
||||
void processChecksums(final LoggingInfo loggingInfo, Intent intent) {
|
||||
Parcelable[] parcelables = intent.getParcelableArrayExtra(EXTRA_CHECKSUMS);
|
||||
ApkChecksum[] checksums = Arrays.copyOf(parcelables, parcelables.length,
|
||||
ApkChecksum[].class);
|
||||
|
||||
for (ApkChecksum checksum : checksums) {
|
||||
void processChecksums(final LoggingInfo loggingInfo, List<ApkChecksum> checksums) {
|
||||
for (int i = 0, size = checksums.size(); i < size; ++i) {
|
||||
ApkChecksum checksum = checksums.get(i);
|
||||
if (checksum.getType() == CHECKSUM_TYPE) {
|
||||
processChecksum(loggingInfo, checksum.getValue());
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user