Per package read timeouts.
Bug: 162345970 Test: atest PackageManagerShellCommandTest PackageManagerShellCommandIncrementalTest IncrementalServiceTest PackageManagerServiceTest Change-Id: I2599db1ed8827fff16387c11254a5d607f27ea46
This commit is contained in:
@@ -1115,6 +1115,7 @@ filegroup {
|
||||
"core/java/android/os/incremental/IStorageLoadingProgressListener.aidl",
|
||||
"core/java/android/os/incremental/IncrementalNewFileParams.aidl",
|
||||
"core/java/android/os/incremental/IStorageHealthListener.aidl",
|
||||
"core/java/android/os/incremental/PerUidReadTimeouts.aidl",
|
||||
"core/java/android/os/incremental/StorageHealthCheckParams.aidl",
|
||||
],
|
||||
path: "core/java",
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.pm.IDataLoaderStatusListener;
|
||||
import android.os.incremental.IncrementalNewFileParams;
|
||||
import android.os.incremental.IStorageLoadingProgressListener;
|
||||
import android.os.incremental.IStorageHealthListener;
|
||||
import android.os.incremental.PerUidReadTimeouts;
|
||||
import android.os.incremental.StorageHealthCheckParams;
|
||||
|
||||
/** @hide */
|
||||
@@ -40,7 +41,8 @@ interface IIncrementalService {
|
||||
int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode,
|
||||
in IDataLoaderStatusListener statusListener,
|
||||
in StorageHealthCheckParams healthCheckParams,
|
||||
in IStorageHealthListener healthListener);
|
||||
in IStorageHealthListener healthListener,
|
||||
in PerUidReadTimeouts[] perUidReadTimeouts);
|
||||
int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode);
|
||||
|
||||
/**
|
||||
@@ -123,7 +125,7 @@ interface IIncrementalService {
|
||||
/**
|
||||
* Permanently disable readlogs reporting for a storage given its ID.
|
||||
*/
|
||||
void disableReadLogs(int storageId);
|
||||
void disallowReadLogs(int storageId);
|
||||
|
||||
/**
|
||||
* Setting up native library directories and extract native libs onto a storage if needed.
|
||||
|
||||
@@ -69,7 +69,8 @@ public final class IncrementalFileStorages {
|
||||
@Nullable IDataLoaderStatusListener statusListener,
|
||||
@Nullable StorageHealthCheckParams healthCheckParams,
|
||||
@Nullable IStorageHealthListener healthListener,
|
||||
List<InstallationFileParcel> addedFiles) throws IOException {
|
||||
@NonNull List<InstallationFileParcel> addedFiles,
|
||||
@NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
|
||||
// TODO(b/136132412): validity check if session should not be incremental
|
||||
IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
|
||||
Context.INCREMENTAL_SERVICE);
|
||||
@@ -80,7 +81,7 @@ public final class IncrementalFileStorages {
|
||||
|
||||
final IncrementalFileStorages result = new IncrementalFileStorages(stageDir,
|
||||
incrementalManager, dataLoaderParams, statusListener, healthCheckParams,
|
||||
healthListener);
|
||||
healthListener, perUidReadTimeouts);
|
||||
for (InstallationFileParcel file : addedFiles) {
|
||||
if (file.location == LOCATION_DATA_APP) {
|
||||
try {
|
||||
@@ -105,7 +106,8 @@ public final class IncrementalFileStorages {
|
||||
@NonNull DataLoaderParams dataLoaderParams,
|
||||
@Nullable IDataLoaderStatusListener statusListener,
|
||||
@Nullable StorageHealthCheckParams healthCheckParams,
|
||||
@Nullable IStorageHealthListener healthListener) throws IOException {
|
||||
@Nullable IStorageHealthListener healthListener,
|
||||
@NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
|
||||
try {
|
||||
mStageDir = stageDir;
|
||||
mIncrementalManager = incrementalManager;
|
||||
@@ -124,7 +126,7 @@ public final class IncrementalFileStorages {
|
||||
mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
|
||||
dataLoaderParams, IncrementalManager.CREATE_MODE_CREATE
|
||||
| IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false,
|
||||
statusListener, healthCheckParams, healthListener);
|
||||
statusListener, healthCheckParams, healthListener, perUidReadTimeouts);
|
||||
if (mDefaultStorage == null) {
|
||||
throw new IOException(
|
||||
"Couldn't create incremental storage at " + stageDir);
|
||||
@@ -163,8 +165,8 @@ public final class IncrementalFileStorages {
|
||||
/**
|
||||
* Permanently disables readlogs.
|
||||
*/
|
||||
public void disableReadLogs() {
|
||||
mDefaultStorage.disableReadLogs();
|
||||
public void disallowReadLogs() {
|
||||
mDefaultStorage.disallowReadLogs();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,6 +40,7 @@ import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Provides operations to open or create an IncrementalStorage, using IIncrementalService
|
||||
@@ -104,10 +105,14 @@ public final class IncrementalManager {
|
||||
boolean autoStartDataLoader,
|
||||
@Nullable IDataLoaderStatusListener statusListener,
|
||||
@Nullable StorageHealthCheckParams healthCheckParams,
|
||||
@Nullable IStorageHealthListener healthListener) {
|
||||
@Nullable IStorageHealthListener healthListener,
|
||||
@NonNull PerUidReadTimeouts[] perUidReadTimeouts) {
|
||||
Objects.requireNonNull(path);
|
||||
Objects.requireNonNull(params);
|
||||
Objects.requireNonNull(perUidReadTimeouts);
|
||||
try {
|
||||
final int id = mService.createStorage(path, params.getData(), createMode,
|
||||
statusListener, healthCheckParams, healthListener);
|
||||
statusListener, healthCheckParams, healthListener, perUidReadTimeouts);
|
||||
if (id < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -432,9 +432,9 @@ public final class IncrementalStorage {
|
||||
/**
|
||||
* Permanently disable readlogs collection.
|
||||
*/
|
||||
public void disableReadLogs() {
|
||||
public void disallowReadLogs() {
|
||||
try {
|
||||
mService.disableReadLogs(mId);
|
||||
mService.disallowReadLogs(mId);
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
45
core/java/android/os/incremental/PerUidReadTimeouts.aidl
Normal file
45
core/java/android/os/incremental/PerUidReadTimeouts.aidl
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.os.incremental;
|
||||
|
||||
/**
|
||||
* Max value is ~1hr = 3600s = 3600000ms = 3600000000us
|
||||
* @hide
|
||||
*/
|
||||
parcelable PerUidReadTimeouts {
|
||||
/** UID to apply these timeouts to */
|
||||
int uid;
|
||||
|
||||
/**
|
||||
* Min time to read any block. Note that this doesn't apply to reads
|
||||
* which are satisfied from the page cache.
|
||||
*/
|
||||
long minTimeUs;
|
||||
|
||||
/**
|
||||
* Min time to satisfy a pending read. Must be >= min_time_us. Any
|
||||
* pending read which is filled before this time will be delayed so
|
||||
* that the total read time >= this value.
|
||||
*/
|
||||
long minPendingTimeUs;
|
||||
|
||||
/**
|
||||
* Max time to satisfy a pending read before the read times out.
|
||||
* Must be >= min_pending_time_us
|
||||
*/
|
||||
long maxPendingTimeUs;
|
||||
}
|
||||
@@ -44,6 +44,7 @@ public final class DumpState {
|
||||
public static final int DUMP_APEX = 1 << 25;
|
||||
public static final int DUMP_QUERIES = 1 << 26;
|
||||
public static final int DUMP_KNOWN_PACKAGES = 1 << 27;
|
||||
public static final int DUMP_PER_UID_READ_TIMEOUTS = 1 << 28;
|
||||
|
||||
public static final int OPTION_SHOW_FILTERS = 1 << 0;
|
||||
public static final int OPTION_DUMP_ALL_COMPONENTS = 1 << 1;
|
||||
|
||||
@@ -111,6 +111,7 @@ import android.os.UserHandle;
|
||||
import android.os.incremental.IStorageHealthListener;
|
||||
import android.os.incremental.IncrementalFileStorages;
|
||||
import android.os.incremental.IncrementalManager;
|
||||
import android.os.incremental.PerUidReadTimeouts;
|
||||
import android.os.incremental.StorageHealthCheckParams;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.provider.Settings.Secure;
|
||||
@@ -3006,7 +3007,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID);
|
||||
if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) {
|
||||
if (!packageLite.debuggable && !packageLite.profilableByShell) {
|
||||
mIncrementalFileStorages.disableReadLogs();
|
||||
mIncrementalFileStorages.disallowReadLogs();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3720,12 +3721,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
};
|
||||
|
||||
if (!manualStartAndDestroy) {
|
||||
final PerUidReadTimeouts[] perUidReadTimeouts = mPm.getPerUidReadTimeouts();
|
||||
|
||||
final StorageHealthCheckParams healthCheckParams = new StorageHealthCheckParams();
|
||||
healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS;
|
||||
healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
|
||||
healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
|
||||
|
||||
final boolean systemDataLoader =
|
||||
params.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE;
|
||||
|
||||
final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() {
|
||||
@Override
|
||||
public void onHealthStatus(int storageId, int status) {
|
||||
@@ -3760,7 +3765,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
|
||||
try {
|
||||
mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir,
|
||||
params, statusListener, healthCheckParams, healthListener, addedFiles);
|
||||
params, statusListener, healthCheckParams, healthListener, addedFiles,
|
||||
perUidReadTimeouts);
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
|
||||
|
||||
@@ -107,6 +107,7 @@ import static android.os.incremental.IncrementalManager.isIncrementalPath;
|
||||
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
|
||||
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
|
||||
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
|
||||
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
|
||||
|
||||
import static com.android.internal.annotations.VisibleForTesting.Visibility;
|
||||
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
|
||||
@@ -287,6 +288,7 @@ import android.os.UserManager;
|
||||
import android.os.incremental.IStorageHealthListener;
|
||||
import android.os.incremental.IncrementalManager;
|
||||
import android.os.incremental.IncrementalStorage;
|
||||
import android.os.incremental.PerUidReadTimeouts;
|
||||
import android.os.incremental.StorageHealthCheckParams;
|
||||
import android.os.storage.DiskInfo;
|
||||
import android.os.storage.IStorageManager;
|
||||
@@ -512,6 +514,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
public static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE;
|
||||
public static final boolean DEBUG_CACHES = false;
|
||||
public static final boolean TRACE_CACHES = false;
|
||||
private static final boolean DEBUG_PER_UID_READ_TIMEOUTS = false;
|
||||
|
||||
// Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService
|
||||
// and PackageDexOptimizer. All these classes have their own flag to allow switching a single
|
||||
@@ -646,6 +649,24 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
*/
|
||||
private static final long DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS = 10 * 1000;
|
||||
|
||||
/**
|
||||
* Default IncFs timeouts. Maximum values in IncFs is 1hr.
|
||||
*
|
||||
* <p>If flag value is empty, the default value will be assigned.
|
||||
*
|
||||
* Flag type: {@code String}
|
||||
* Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE
|
||||
*/
|
||||
private static final String PROPERTY_INCFS_DEFAULT_TIMEOUTS = "incfs_default_timeouts";
|
||||
|
||||
/**
|
||||
* Known digesters with optional timeouts.
|
||||
*
|
||||
* Flag type: {@code String}
|
||||
* Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE
|
||||
*/
|
||||
private static final String PROPERTY_KNOWN_DIGESTERS_LIST = "known_digesters_list";
|
||||
|
||||
/**
|
||||
* The default response for package verification timeout.
|
||||
*
|
||||
@@ -909,6 +930,11 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
final private ArrayList<IPackageChangeObserver> mPackageChangeObservers =
|
||||
new ArrayList<>();
|
||||
|
||||
// Cached parsed flag value. Invalidated on each flag change.
|
||||
private PerUidReadTimeouts[] mPerUidReadTimeoutsCache;
|
||||
|
||||
private static final PerUidReadTimeouts[] EMPTY_PER_UID_READ_TIMEOUTS_ARRAY = {};
|
||||
|
||||
/**
|
||||
* Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
|
||||
*
|
||||
@@ -23738,6 +23764,17 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
mInstallerService.restoreAndApplyStagedSessionIfNeeded();
|
||||
|
||||
mExistingPackages = null;
|
||||
|
||||
// Clear cache on flags changes.
|
||||
DeviceConfig.addOnPropertiesChangedListener(
|
||||
NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(),
|
||||
properties -> {
|
||||
final Set<String> keyset = properties.getKeyset();
|
||||
if (keyset.contains(PROPERTY_INCFS_DEFAULT_TIMEOUTS) || keyset.contains(
|
||||
PROPERTY_KNOWN_DIGESTERS_LIST)) {
|
||||
mPerUidReadTimeoutsCache = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void waitForAppDataPrepared() {
|
||||
@@ -23828,6 +23865,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
pw.println(" v[erifiers]: print package verifier info");
|
||||
pw.println(" d[omain-preferred-apps]: print domains preferred apps");
|
||||
pw.println(" i[ntent-filter-verifiers]|ifv: print intent filter verifier info");
|
||||
pw.println(" t[imeouts]: print read timeouts for known digesters");
|
||||
pw.println(" version: print database version info");
|
||||
pw.println(" write: write current settings now");
|
||||
pw.println(" installs: details about install sessions");
|
||||
@@ -23982,6 +24020,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS);
|
||||
} else if ("known-packages".equals(cmd)) {
|
||||
dumpState.setDump(DumpState.DUMP_KNOWN_PACKAGES);
|
||||
} else if ("t".equals(cmd) || "timeouts".equals(cmd)) {
|
||||
dumpState.setDump(DumpState.DUMP_PER_UID_READ_TIMEOUTS);
|
||||
} else if ("write".equals(cmd)) {
|
||||
synchronized (mLock) {
|
||||
writeSettingsLPrTEMP();
|
||||
@@ -24380,6 +24420,25 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) {
|
||||
mApexManager.dump(pw, packageName);
|
||||
}
|
||||
|
||||
if (!checkin && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
|
||||
&& packageName == null) {
|
||||
pw.println();
|
||||
pw.println("Per UID read timeouts:");
|
||||
pw.println(" Default timeouts flag: " + getDefaultTimeouts());
|
||||
pw.println(" Known digesters list flag: " + getKnownDigestersList());
|
||||
|
||||
PerUidReadTimeouts[] items = getPerUidReadTimeouts();
|
||||
pw.println(" Timeouts (" + items.length + "):");
|
||||
for (PerUidReadTimeouts item : items) {
|
||||
pw.print(" (");
|
||||
pw.print("uid=" + item.uid + ", ");
|
||||
pw.print("minTimeUs=" + item.minTimeUs + ", ");
|
||||
pw.print("minPendingTimeUs=" + item.minPendingTimeUs + ", ");
|
||||
pw.print("maxPendingTimeUs=" + item.maxPendingTimeUs);
|
||||
pw.println(")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: b/111402650
|
||||
@@ -27967,6 +28026,88 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
SystemClock.sleep(durationMs);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getDefaultTimeouts() {
|
||||
return DeviceConfig.getString(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
|
||||
PROPERTY_INCFS_DEFAULT_TIMEOUTS, "");
|
||||
}
|
||||
|
||||
private static String getKnownDigestersList() {
|
||||
return DeviceConfig.getString(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
|
||||
PROPERTY_KNOWN_DIGESTERS_LIST, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array containing per-uid timeout configuration.
|
||||
* This is derived from DeviceConfig flags.
|
||||
*/
|
||||
public @NonNull PerUidReadTimeouts[] getPerUidReadTimeouts() {
|
||||
PerUidReadTimeouts[] result = mPerUidReadTimeoutsCache;
|
||||
if (result == null) {
|
||||
result = parsePerUidReadTimeouts();
|
||||
mPerUidReadTimeoutsCache = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private @NonNull PerUidReadTimeouts[] parsePerUidReadTimeouts() {
|
||||
final String defaultTimeouts = getDefaultTimeouts();
|
||||
final String knownDigestersList = getKnownDigestersList();
|
||||
final List<PerPackageReadTimeouts> perPackageReadTimeouts =
|
||||
PerPackageReadTimeouts.parseDigestersList(defaultTimeouts, knownDigestersList);
|
||||
|
||||
if (perPackageReadTimeouts.size() == 0) {
|
||||
return EMPTY_PER_UID_READ_TIMEOUTS_ARRAY;
|
||||
}
|
||||
|
||||
final int[] allUsers = mInjector.getUserManagerService().getUserIds();
|
||||
|
||||
List<PerUidReadTimeouts> result = new ArrayList<>(perPackageReadTimeouts.size());
|
||||
synchronized (mLock) {
|
||||
for (int i = 0, size = perPackageReadTimeouts.size(); i < size; ++i) {
|
||||
final PerPackageReadTimeouts perPackage = perPackageReadTimeouts.get(i);
|
||||
final PackageSetting ps = mSettings.mPackages.get(perPackage.packageName);
|
||||
if (ps == null) {
|
||||
if (DEBUG_PER_UID_READ_TIMEOUTS) {
|
||||
Slog.i(TAG, "PerUidReadTimeouts: package not found = "
|
||||
+ perPackage.packageName);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
final AndroidPackage pkg = ps.getPkg();
|
||||
if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode
|
||||
|| pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) {
|
||||
if (DEBUG_PER_UID_READ_TIMEOUTS) {
|
||||
Slog.i(TAG, "PerUidReadTimeouts: version code is not in range = "
|
||||
+ perPackage.packageName + ":" + pkg.getLongVersionCode());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (perPackage.sha256certificate != null
|
||||
&& !pkg.getSigningDetails().hasSha256Certificate(
|
||||
perPackage.sha256certificate)) {
|
||||
if (DEBUG_PER_UID_READ_TIMEOUTS) {
|
||||
Slog.i(TAG, "PerUidReadTimeouts: invalid certificate = "
|
||||
+ perPackage.packageName + ":" + pkg.getLongVersionCode());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (int userId : allUsers) {
|
||||
if (!ps.getInstalled(userId)) {
|
||||
continue;
|
||||
}
|
||||
final int uid = UserHandle.getUid(userId, ps.appId);
|
||||
final PerUidReadTimeouts perUid = new PerUidReadTimeouts();
|
||||
perUid.uid = uid;
|
||||
perUid.minTimeUs = perPackage.timeouts.minTimeUs;
|
||||
perUid.minPendingTimeUs = perPackage.timeouts.minPendingTimeUs;
|
||||
perUid.maxPendingTimeUs = perPackage.timeouts.maxPendingTimeUs;
|
||||
result.add(perUid);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toArray(new PerUidReadTimeouts[result.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
interface PackageSender {
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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 com.android.server.pm;
|
||||
|
||||
import android.annotation.NonNull;;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.internal.util.HexDump;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
class PerPackageReadTimeouts {
|
||||
static long tryParseLong(String str, long defaultValue) {
|
||||
try {
|
||||
return Long.parseLong(str);
|
||||
} catch (NumberFormatException nfe) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] tryParseSha256(String str) {
|
||||
if (TextUtils.isEmpty(str)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return HexDump.hexStringToByteArray(str);
|
||||
} catch (RuntimeException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class Timeouts {
|
||||
public final long minTimeUs;
|
||||
public final long minPendingTimeUs;
|
||||
public final long maxPendingTimeUs;
|
||||
|
||||
// 3600000000us == 1hr
|
||||
public static final Timeouts DEFAULT = new Timeouts(3600000000L, 3600000000L, 3600000000L);
|
||||
|
||||
private Timeouts(long minTimeUs, long minPendingTimeUs, long maxPendingTimeUs) {
|
||||
this.minTimeUs = minTimeUs;
|
||||
this.minPendingTimeUs = minPendingTimeUs;
|
||||
this.maxPendingTimeUs = maxPendingTimeUs;
|
||||
}
|
||||
|
||||
static Timeouts parse(String timeouts) {
|
||||
String[] splits = timeouts.split(":", 3);
|
||||
if (splits.length != 3) {
|
||||
return DEFAULT;
|
||||
}
|
||||
final long minTimeUs = tryParseLong(splits[0], DEFAULT.minTimeUs);
|
||||
final long minPendingTimeUs = tryParseLong(splits[1], DEFAULT.minPendingTimeUs);
|
||||
final long maxPendingTimeUs = tryParseLong(splits[2], DEFAULT.maxPendingTimeUs);
|
||||
if (0 <= minTimeUs && minTimeUs <= minPendingTimeUs
|
||||
&& minPendingTimeUs <= maxPendingTimeUs) {
|
||||
// validity check
|
||||
return new Timeouts(minTimeUs, minPendingTimeUs, maxPendingTimeUs);
|
||||
}
|
||||
return DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
static class VersionCodes {
|
||||
public final long minVersionCode;
|
||||
public final long maxVersionCode;
|
||||
|
||||
public static final VersionCodes ALL_VERSION_CODES = new VersionCodes(Long.MIN_VALUE,
|
||||
Long.MAX_VALUE);
|
||||
|
||||
private VersionCodes(long minVersionCode, long maxVersionCode) {
|
||||
this.minVersionCode = minVersionCode;
|
||||
this.maxVersionCode = maxVersionCode;
|
||||
}
|
||||
|
||||
static VersionCodes parse(String codes) {
|
||||
if (TextUtils.isEmpty(codes)) {
|
||||
return ALL_VERSION_CODES;
|
||||
}
|
||||
String[] splits = codes.split("-", 2);
|
||||
switch (splits.length) {
|
||||
case 1: {
|
||||
// single version code
|
||||
try {
|
||||
final long versionCode = Long.parseLong(splits[0]);
|
||||
return new VersionCodes(versionCode, versionCode);
|
||||
} catch (NumberFormatException nfe) {
|
||||
return ALL_VERSION_CODES;
|
||||
}
|
||||
}
|
||||
case 2: {
|
||||
final long minVersionCode = tryParseLong(splits[0],
|
||||
ALL_VERSION_CODES.minVersionCode);
|
||||
final long maxVersionCode = tryParseLong(splits[1],
|
||||
ALL_VERSION_CODES.maxVersionCode);
|
||||
if (minVersionCode <= maxVersionCode) {
|
||||
return new VersionCodes(minVersionCode, maxVersionCode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ALL_VERSION_CODES;
|
||||
}
|
||||
}
|
||||
|
||||
public final String packageName;
|
||||
public final byte[] sha256certificate;
|
||||
public final VersionCodes versionCodes;
|
||||
public final Timeouts timeouts;
|
||||
|
||||
private PerPackageReadTimeouts(String packageName, byte[] sha256certificate,
|
||||
VersionCodes versionCodes, Timeouts timeouts) {
|
||||
this.packageName = packageName;
|
||||
this.sha256certificate = sha256certificate;
|
||||
this.versionCodes = versionCodes;
|
||||
this.timeouts = timeouts;
|
||||
}
|
||||
|
||||
@SuppressWarnings("fallthrough")
|
||||
static PerPackageReadTimeouts parse(String timeoutsStr, VersionCodes defaultVersionCodes,
|
||||
Timeouts defaultTimeouts) {
|
||||
String packageName = null;
|
||||
byte[] sha256certificate = null;
|
||||
VersionCodes versionCodes = defaultVersionCodes;
|
||||
Timeouts timeouts = defaultTimeouts;
|
||||
|
||||
final String[] splits = timeoutsStr.split(":", 4);
|
||||
switch (splits.length) {
|
||||
case 4:
|
||||
timeouts = Timeouts.parse(splits[3]);
|
||||
// fall through
|
||||
case 3:
|
||||
versionCodes = VersionCodes.parse(splits[2]);
|
||||
// fall through
|
||||
case 2:
|
||||
sha256certificate = tryParseSha256(splits[1]);
|
||||
// fall through
|
||||
case 1:
|
||||
packageName = splits[0];
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
if (TextUtils.isEmpty(packageName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PerPackageReadTimeouts(packageName, sha256certificate, versionCodes,
|
||||
timeouts);
|
||||
}
|
||||
|
||||
static @NonNull List<PerPackageReadTimeouts> parseDigestersList(String defaultTimeoutsStr,
|
||||
String knownDigestersList) {
|
||||
if (TextUtils.isEmpty(knownDigestersList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final VersionCodes defaultVersionCodes = VersionCodes.ALL_VERSION_CODES;
|
||||
final Timeouts defaultTimeouts = Timeouts.parse(defaultTimeoutsStr);
|
||||
|
||||
String[] packages = knownDigestersList.split(",");
|
||||
List<PerPackageReadTimeouts> result = new ArrayList<>(packages.length);
|
||||
for (int i = 0, size = packages.length; i < size; ++i) {
|
||||
PerPackageReadTimeouts timeouts = PerPackageReadTimeouts.parse(packages[i],
|
||||
defaultVersionCodes, defaultTimeouts);
|
||||
if (timeouts != null) {
|
||||
result.add(timeouts);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -122,13 +122,14 @@ binder::Status BinderIncrementalService::createStorage(
|
||||
const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener,
|
||||
const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
|
||||
const ::android::sp<::android::os::incremental::IStorageHealthListener>& healthListener,
|
||||
const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts,
|
||||
int32_t* _aidl_return) {
|
||||
*_aidl_return =
|
||||
mImpl.createStorage(path, const_cast<content::pm::DataLoaderParamsParcel&&>(params),
|
||||
android::incremental::IncrementalService::CreateOptions(createMode),
|
||||
statusListener,
|
||||
const_cast<StorageHealthCheckParams&&>(healthCheckParams),
|
||||
healthListener);
|
||||
healthListener, perUidReadTimeouts);
|
||||
return ok();
|
||||
}
|
||||
|
||||
@@ -164,8 +165,8 @@ binder::Status BinderIncrementalService::deleteStorage(int32_t storageId) {
|
||||
return ok();
|
||||
}
|
||||
|
||||
binder::Status BinderIncrementalService::disableReadLogs(int32_t storageId) {
|
||||
mImpl.disableReadLogs(storageId);
|
||||
binder::Status BinderIncrementalService::disallowReadLogs(int32_t storageId) {
|
||||
mImpl.disallowReadLogs(storageId);
|
||||
return ok();
|
||||
}
|
||||
|
||||
@@ -254,7 +255,7 @@ binder::Status BinderIncrementalService::isFileFullyLoaded(int32_t storageId,
|
||||
|
||||
binder::Status BinderIncrementalService::getLoadingProgress(int32_t storageId,
|
||||
float* _aidl_return) {
|
||||
*_aidl_return = mImpl.getLoadingProgress(storageId);
|
||||
*_aidl_return = mImpl.getLoadingProgress(storageId).getProgress();
|
||||
return ok();
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener,
|
||||
const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
|
||||
const ::android::sp<IStorageHealthListener>& healthListener,
|
||||
const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts,
|
||||
int32_t* _aidl_return) final;
|
||||
binder::Status createLinkedStorage(const std::string& path, int32_t otherStorageId,
|
||||
int32_t createMode, int32_t* _aidl_return) final;
|
||||
@@ -77,7 +78,7 @@ public:
|
||||
std::vector<uint8_t>* _aidl_return) final;
|
||||
binder::Status startLoading(int32_t storageId, bool* _aidl_return) final;
|
||||
binder::Status deleteStorage(int32_t storageId) final;
|
||||
binder::Status disableReadLogs(int32_t storageId) final;
|
||||
binder::Status disallowReadLogs(int32_t storageId) final;
|
||||
binder::Status configureNativeBinaries(int32_t storageId, const std::string& apkFullPath,
|
||||
const std::string& libDirRelativePath,
|
||||
const std::string& abi, bool extractNativeLibs,
|
||||
|
||||
@@ -63,6 +63,10 @@ struct Constants {
|
||||
static constexpr auto libSuffix = ".so"sv;
|
||||
static constexpr auto blockSize = 4096;
|
||||
static constexpr auto systemPackage = "android"sv;
|
||||
|
||||
static constexpr auto progressUpdateInterval = 1000ms;
|
||||
static constexpr auto perUidTimeoutOffset = progressUpdateInterval * 2;
|
||||
static constexpr auto minPerUidTimeout = progressUpdateInterval * 3;
|
||||
};
|
||||
|
||||
static const Constants& constants() {
|
||||
@@ -350,7 +354,8 @@ void IncrementalService::onDump(int fd) {
|
||||
dprintf(fd, " storages (%d): {\n", int(mnt.storages.size()));
|
||||
for (auto&& [storageId, storage] : mnt.storages) {
|
||||
dprintf(fd, " [%d] -> [%s] (%d %% loaded) \n", storageId, storage.name.c_str(),
|
||||
(int)(getLoadingProgressFromPath(mnt, storage.name.c_str()) * 100));
|
||||
(int)(getLoadingProgressFromPath(mnt, storage.name.c_str()).getProgress() *
|
||||
100));
|
||||
}
|
||||
dprintf(fd, " }\n");
|
||||
|
||||
@@ -419,12 +424,11 @@ auto IncrementalService::getStorageSlotLocked() -> MountMap::iterator {
|
||||
}
|
||||
}
|
||||
|
||||
StorageId IncrementalService::createStorage(std::string_view mountPoint,
|
||||
content::pm::DataLoaderParamsParcel&& dataLoaderParams,
|
||||
CreateOptions options,
|
||||
const DataLoaderStatusListener& statusListener,
|
||||
StorageHealthCheckParams&& healthCheckParams,
|
||||
const StorageHealthListener& healthListener) {
|
||||
StorageId IncrementalService::createStorage(
|
||||
std::string_view mountPoint, content::pm::DataLoaderParamsParcel&& dataLoaderParams,
|
||||
CreateOptions options, const DataLoaderStatusListener& statusListener,
|
||||
StorageHealthCheckParams&& healthCheckParams, const StorageHealthListener& healthListener,
|
||||
const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
|
||||
LOG(INFO) << "createStorage: " << mountPoint << " | " << int(options);
|
||||
if (!path::isAbsolute(mountPoint)) {
|
||||
LOG(ERROR) << "path is not absolute: " << mountPoint;
|
||||
@@ -553,13 +557,14 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint,
|
||||
if (auto err = addBindMount(*ifs, storageIt->first, storageIt->second.name,
|
||||
std::string(storageIt->second.name), std::move(mountNorm), bk, l);
|
||||
err < 0) {
|
||||
LOG(ERROR) << "adding bind mount failed: " << -err;
|
||||
LOG(ERROR) << "Adding bind mount failed: " << -err;
|
||||
return kInvalidStorageId;
|
||||
}
|
||||
|
||||
// Done here as well, all data structures are in good state.
|
||||
secondCleanupOnFailure.release();
|
||||
|
||||
// DataLoader.
|
||||
auto dataLoaderStub = prepareDataLoader(*ifs, std::move(dataLoaderParams), &statusListener,
|
||||
std::move(healthCheckParams), &healthListener);
|
||||
CHECK(dataLoaderStub);
|
||||
@@ -567,6 +572,11 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint,
|
||||
mountIt->second = std::move(ifs);
|
||||
l.unlock();
|
||||
|
||||
// Per Uid timeouts.
|
||||
if (!perUidReadTimeouts.empty()) {
|
||||
setUidReadTimeouts(mountId, perUidReadTimeouts);
|
||||
}
|
||||
|
||||
if (mSystemReady.load(std::memory_order_relaxed) && !dataLoaderStub->requestCreate()) {
|
||||
// failed to create data loader
|
||||
LOG(ERROR) << "initializeDataLoader() failed";
|
||||
@@ -634,17 +644,17 @@ StorageId IncrementalService::findStorageId(std::string_view path) const {
|
||||
return it->second->second.storage;
|
||||
}
|
||||
|
||||
void IncrementalService::disableReadLogs(StorageId storageId) {
|
||||
void IncrementalService::disallowReadLogs(StorageId storageId) {
|
||||
std::unique_lock l(mLock);
|
||||
const auto ifs = getIfsLocked(storageId);
|
||||
if (!ifs) {
|
||||
LOG(ERROR) << "disableReadLogs failed, invalid storageId: " << storageId;
|
||||
LOG(ERROR) << "disallowReadLogs failed, invalid storageId: " << storageId;
|
||||
return;
|
||||
}
|
||||
if (!ifs->readLogsEnabled()) {
|
||||
if (!ifs->readLogsAllowed()) {
|
||||
return;
|
||||
}
|
||||
ifs->disableReadLogs();
|
||||
ifs->disallowReadLogs();
|
||||
l.unlock();
|
||||
|
||||
const auto metadata = constants().readLogsDisabledMarkerName;
|
||||
@@ -669,7 +679,7 @@ int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLog
|
||||
|
||||
const auto& params = ifs->dataLoaderStub->params();
|
||||
if (enableReadLogs) {
|
||||
if (!ifs->readLogsEnabled()) {
|
||||
if (!ifs->readLogsAllowed()) {
|
||||
LOG(ERROR) << "setStorageParams failed, readlogs disabled for storageId: " << storageId;
|
||||
return -EPERM;
|
||||
}
|
||||
@@ -704,7 +714,12 @@ binder::Status IncrementalService::applyStorageParams(IncFsMount& ifs, bool enab
|
||||
}
|
||||
|
||||
std::lock_guard l(mMountOperationLock);
|
||||
return mVold->setIncFsMountOptions(control, enableReadLogs);
|
||||
const auto status = mVold->setIncFsMountOptions(control, enableReadLogs);
|
||||
if (status.isOk()) {
|
||||
// Store enabled state.
|
||||
ifs.setReadLogsEnabled(enableReadLogs);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void IncrementalService::deleteStorage(StorageId storageId) {
|
||||
@@ -1052,6 +1067,74 @@ bool IncrementalService::startLoading(StorageId storage) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void IncrementalService::setUidReadTimeouts(
|
||||
StorageId storage, const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
|
||||
using microseconds = std::chrono::microseconds;
|
||||
using milliseconds = std::chrono::milliseconds;
|
||||
|
||||
auto maxPendingTimeUs = microseconds(0);
|
||||
for (const auto& timeouts : perUidReadTimeouts) {
|
||||
maxPendingTimeUs = std::max(maxPendingTimeUs, microseconds(timeouts.maxPendingTimeUs));
|
||||
}
|
||||
if (maxPendingTimeUs < Constants::minPerUidTimeout) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto ifs = getIfs(storage);
|
||||
if (!ifs) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto err = mIncFs->setUidReadTimeouts(ifs->control, perUidReadTimeouts); err < 0) {
|
||||
LOG(ERROR) << "Setting read timeouts failed: " << -err;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto timeout = std::chrono::duration_cast<milliseconds>(maxPendingTimeUs) -
|
||||
Constants::perUidTimeoutOffset;
|
||||
updateUidReadTimeouts(storage, Clock::now() + timeout);
|
||||
}
|
||||
|
||||
void IncrementalService::clearUidReadTimeouts(StorageId storage) {
|
||||
const auto ifs = getIfs(storage);
|
||||
if (!ifs) {
|
||||
return;
|
||||
}
|
||||
|
||||
mIncFs->setUidReadTimeouts(ifs->control, {});
|
||||
}
|
||||
|
||||
void IncrementalService::updateUidReadTimeouts(StorageId storage, Clock::time_point timeLimit) {
|
||||
// Reached maximum timeout.
|
||||
if (Clock::now() >= timeLimit) {
|
||||
return clearUidReadTimeouts(storage);
|
||||
}
|
||||
|
||||
// Still loading?
|
||||
const auto progress = getLoadingProgress(storage);
|
||||
if (progress.isError()) {
|
||||
// Something is wrong, abort.
|
||||
return clearUidReadTimeouts(storage);
|
||||
}
|
||||
|
||||
if (progress.started() && progress.fullyLoaded()) {
|
||||
// Fully loaded, check readLogs collection.
|
||||
const auto ifs = getIfs(storage);
|
||||
if (!ifs->readLogsEnabled()) {
|
||||
return clearUidReadTimeouts(storage);
|
||||
}
|
||||
}
|
||||
|
||||
const auto timeLeft = timeLimit - Clock::now();
|
||||
if (timeLeft < Constants::progressUpdateInterval) {
|
||||
// Don't bother.
|
||||
return clearUidReadTimeouts(storage);
|
||||
}
|
||||
|
||||
addTimedJob(*mTimedQueue, storage, Constants::progressUpdateInterval,
|
||||
[this, storage, timeLimit]() { updateUidReadTimeouts(storage, timeLimit); });
|
||||
}
|
||||
|
||||
std::unordered_set<std::string_view> IncrementalService::adoptMountedInstances() {
|
||||
std::unordered_set<std::string_view> mountedRootNames;
|
||||
mIncFs->listExistingMounts([this, &mountedRootNames](auto root, auto backingDir, auto binds) {
|
||||
@@ -1125,7 +1208,7 @@ std::unordered_set<std::string_view> IncrementalService::adoptMountedInstances()
|
||||
|
||||
// Check if marker file present.
|
||||
if (checkReadLogsDisabledMarker(root)) {
|
||||
ifs->disableReadLogs();
|
||||
ifs->disallowReadLogs();
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, metadata::BindPoint>> permanentBindPoints;
|
||||
@@ -1301,7 +1384,7 @@ bool IncrementalService::mountExistingImage(std::string_view root) {
|
||||
|
||||
// Check if marker file present.
|
||||
if (checkReadLogsDisabledMarker(mountTarget)) {
|
||||
ifs->disableReadLogs();
|
||||
ifs->disallowReadLogs();
|
||||
}
|
||||
|
||||
// DataLoader params
|
||||
@@ -1705,7 +1788,7 @@ int IncrementalService::setFileContent(const IfsMountPtr& ifs, const incfs::File
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IncrementalService::isFileFullyLoaded(StorageId storage, const std::string& path) const {
|
||||
int IncrementalService::isFileFullyLoaded(StorageId storage, std::string_view filePath) const {
|
||||
std::unique_lock l(mLock);
|
||||
const auto ifs = getIfsLocked(storage);
|
||||
if (!ifs) {
|
||||
@@ -1718,7 +1801,7 @@ int IncrementalService::isFileFullyLoaded(StorageId storage, const std::string&
|
||||
return -EINVAL;
|
||||
}
|
||||
l.unlock();
|
||||
return isFileFullyLoadedFromPath(*ifs, path);
|
||||
return isFileFullyLoadedFromPath(*ifs, filePath);
|
||||
}
|
||||
|
||||
int IncrementalService::isFileFullyLoadedFromPath(const IncFsMount& ifs,
|
||||
@@ -1736,25 +1819,26 @@ int IncrementalService::isFileFullyLoadedFromPath(const IncFsMount& ifs,
|
||||
return totalBlocks - filledBlocks;
|
||||
}
|
||||
|
||||
float IncrementalService::getLoadingProgress(StorageId storage) const {
|
||||
IncrementalService::LoadingProgress IncrementalService::getLoadingProgress(
|
||||
StorageId storage) const {
|
||||
std::unique_lock l(mLock);
|
||||
const auto ifs = getIfsLocked(storage);
|
||||
if (!ifs) {
|
||||
LOG(ERROR) << "getLoadingProgress failed, invalid storageId: " << storage;
|
||||
return -EINVAL;
|
||||
return {-EINVAL, -EINVAL};
|
||||
}
|
||||
const auto storageInfo = ifs->storages.find(storage);
|
||||
if (storageInfo == ifs->storages.end()) {
|
||||
LOG(ERROR) << "getLoadingProgress failed, no storage: " << storage;
|
||||
return -EINVAL;
|
||||
return {-EINVAL, -EINVAL};
|
||||
}
|
||||
l.unlock();
|
||||
return getLoadingProgressFromPath(*ifs, storageInfo->second.name);
|
||||
}
|
||||
|
||||
float IncrementalService::getLoadingProgressFromPath(const IncFsMount& ifs,
|
||||
std::string_view storagePath) const {
|
||||
size_t totalBlocks = 0, filledBlocks = 0;
|
||||
IncrementalService::LoadingProgress IncrementalService::getLoadingProgressFromPath(
|
||||
const IncFsMount& ifs, std::string_view storagePath) const {
|
||||
ssize_t totalBlocks = 0, filledBlocks = 0;
|
||||
const auto filePaths = mFs->listFilesRecursive(storagePath);
|
||||
for (const auto& filePath : filePaths) {
|
||||
const auto [filledBlocksCount, totalBlocksCount] =
|
||||
@@ -1762,33 +1846,29 @@ float IncrementalService::getLoadingProgressFromPath(const IncFsMount& ifs,
|
||||
if (filledBlocksCount < 0) {
|
||||
LOG(ERROR) << "getLoadingProgress failed to get filled blocks count for: " << filePath
|
||||
<< " errno: " << filledBlocksCount;
|
||||
return filledBlocksCount;
|
||||
return {filledBlocksCount, filledBlocksCount};
|
||||
}
|
||||
totalBlocks += totalBlocksCount;
|
||||
filledBlocks += filledBlocksCount;
|
||||
}
|
||||
|
||||
if (totalBlocks == 0) {
|
||||
// No file in the storage or files are empty; regarded as fully loaded
|
||||
return 1;
|
||||
}
|
||||
return (float)filledBlocks / (float)totalBlocks;
|
||||
return {filledBlocks, totalBlocks};
|
||||
}
|
||||
|
||||
bool IncrementalService::updateLoadingProgress(
|
||||
StorageId storage, const StorageLoadingProgressListener& progressListener) {
|
||||
const auto progress = getLoadingProgress(storage);
|
||||
if (progress < 0) {
|
||||
if (progress.isError()) {
|
||||
// Failed to get progress from incfs, abort.
|
||||
return false;
|
||||
}
|
||||
progressListener->onStorageLoadingProgressChanged(storage, progress);
|
||||
if (progress > 1 - 0.001f) {
|
||||
progressListener->onStorageLoadingProgressChanged(storage, progress.getProgress());
|
||||
if (progress.fullyLoaded()) {
|
||||
// Stop updating progress once it is fully loaded
|
||||
return true;
|
||||
}
|
||||
static constexpr auto kProgressUpdateInterval = 1000ms;
|
||||
addTimedJob(*mProgressUpdateJobQueue, storage, kProgressUpdateInterval /* repeat after 1s */,
|
||||
addTimedJob(*mProgressUpdateJobQueue, storage,
|
||||
Constants::progressUpdateInterval /* repeat after 1s */,
|
||||
[storage, progressListener, this]() {
|
||||
updateLoadingProgress(storage, progressListener);
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <android/os/incremental/BnIncrementalServiceConnector.h>
|
||||
#include <android/os/incremental/BnStorageHealthListener.h>
|
||||
#include <android/os/incremental/BnStorageLoadingProgressListener.h>
|
||||
#include <android/os/incremental/PerUidReadTimeouts.h>
|
||||
#include <android/os/incremental/StorageHealthCheckParams.h>
|
||||
#include <binder/IAppOpsCallback.h>
|
||||
#include <utils/String16.h>
|
||||
@@ -69,6 +70,8 @@ using StorageHealthListener = ::android::sp<IStorageHealthListener>;
|
||||
using IStorageLoadingProgressListener = ::android::os::incremental::IStorageLoadingProgressListener;
|
||||
using StorageLoadingProgressListener = ::android::sp<IStorageLoadingProgressListener>;
|
||||
|
||||
using PerUidReadTimeouts = ::android::os::incremental::PerUidReadTimeouts;
|
||||
|
||||
class IncrementalService final {
|
||||
public:
|
||||
explicit IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir);
|
||||
@@ -98,7 +101,23 @@ public:
|
||||
};
|
||||
|
||||
enum StorageFlags {
|
||||
ReadLogsEnabled = 1,
|
||||
ReadLogsAllowed = 1 << 0,
|
||||
ReadLogsEnabled = 1 << 1,
|
||||
};
|
||||
|
||||
struct LoadingProgress {
|
||||
ssize_t filledBlocks;
|
||||
ssize_t totalBlocks;
|
||||
|
||||
bool isError() const { return totalBlocks < 0; }
|
||||
bool started() const { return totalBlocks > 0; }
|
||||
bool fullyLoaded() const { return !isError() && (totalBlocks == filledBlocks); }
|
||||
|
||||
float getProgress() const {
|
||||
return totalBlocks < 0
|
||||
? totalBlocks
|
||||
: totalBlocks > 0 ? double(filledBlocks) / double(totalBlocks) : 1.f;
|
||||
}
|
||||
};
|
||||
|
||||
static FileId idFromMetadata(std::span<const uint8_t> metadata);
|
||||
@@ -114,7 +133,8 @@ public:
|
||||
content::pm::DataLoaderParamsParcel&& dataLoaderParams,
|
||||
CreateOptions options, const DataLoaderStatusListener& statusListener,
|
||||
StorageHealthCheckParams&& healthCheckParams,
|
||||
const StorageHealthListener& healthListener);
|
||||
const StorageHealthListener& healthListener,
|
||||
const std::vector<PerUidReadTimeouts>& perUidReadTimeouts);
|
||||
StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage,
|
||||
CreateOptions options = CreateOptions::Default);
|
||||
StorageId openStorage(std::string_view path);
|
||||
@@ -123,7 +143,7 @@ public:
|
||||
int unbind(StorageId storage, std::string_view target);
|
||||
void deleteStorage(StorageId storage);
|
||||
|
||||
void disableReadLogs(StorageId storage);
|
||||
void disallowReadLogs(StorageId storage);
|
||||
int setStorageParams(StorageId storage, bool enableReadLogs);
|
||||
|
||||
int makeFile(StorageId storage, std::string_view path, int mode, FileId id,
|
||||
@@ -135,8 +155,8 @@ public:
|
||||
std::string_view newPath);
|
||||
int unlink(StorageId storage, std::string_view path);
|
||||
|
||||
int isFileFullyLoaded(StorageId storage, const std::string& path) const;
|
||||
float getLoadingProgress(StorageId storage) const;
|
||||
int isFileFullyLoaded(StorageId storage, std::string_view filePath) const;
|
||||
LoadingProgress getLoadingProgress(StorageId storage) const;
|
||||
bool registerLoadingProgressListener(StorageId storage,
|
||||
const StorageLoadingProgressListener& progressListener);
|
||||
bool unregisterLoadingProgressListener(StorageId storage);
|
||||
@@ -282,7 +302,7 @@ private:
|
||||
const std::string root;
|
||||
Control control;
|
||||
/*const*/ MountId mountId;
|
||||
int32_t flags = StorageFlags::ReadLogsEnabled;
|
||||
int32_t flags = StorageFlags::ReadLogsAllowed;
|
||||
StorageMap storages;
|
||||
BindMap bindPoints;
|
||||
DataLoaderStubPtr dataLoaderStub;
|
||||
@@ -301,7 +321,15 @@ private:
|
||||
|
||||
StorageMap::iterator makeStorage(StorageId id);
|
||||
|
||||
void disableReadLogs() { flags &= ~StorageFlags::ReadLogsEnabled; }
|
||||
void disallowReadLogs() { flags &= ~StorageFlags::ReadLogsAllowed; }
|
||||
int32_t readLogsAllowed() const { return (flags & StorageFlags::ReadLogsAllowed); }
|
||||
|
||||
void setReadLogsEnabled(bool value) {
|
||||
if (value)
|
||||
flags |= StorageFlags::ReadLogsEnabled;
|
||||
else
|
||||
flags &= ~StorageFlags::ReadLogsEnabled;
|
||||
}
|
||||
int32_t readLogsEnabled() const { return (flags & StorageFlags::ReadLogsEnabled); }
|
||||
|
||||
static void cleanupFilesystem(std::string_view root);
|
||||
@@ -313,6 +341,11 @@ private:
|
||||
|
||||
static bool perfLoggingEnabled();
|
||||
|
||||
void setUidReadTimeouts(StorageId storage,
|
||||
const std::vector<PerUidReadTimeouts>& perUidReadTimeouts);
|
||||
void clearUidReadTimeouts(StorageId storage);
|
||||
void updateUidReadTimeouts(StorageId storage, Clock::time_point timeLimit);
|
||||
|
||||
std::unordered_set<std::string_view> adoptMountedInstances();
|
||||
void mountExistingImages(const std::unordered_set<std::string_view>& mountedRootNames);
|
||||
bool mountExistingImage(std::string_view root);
|
||||
@@ -355,7 +388,7 @@ private:
|
||||
binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);
|
||||
|
||||
int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const;
|
||||
float getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
|
||||
LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
|
||||
|
||||
int setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId,
|
||||
std::string_view debugFilePath, std::span<const uint8_t> data) const;
|
||||
|
||||
@@ -206,6 +206,11 @@ public:
|
||||
std::vector<incfs::ReadInfo>* pendingReadsBuffer) const final {
|
||||
return incfs::waitForPendingReads(control, timeout, pendingReadsBuffer);
|
||||
}
|
||||
ErrorCode setUidReadTimeouts(const Control& control,
|
||||
const std::vector<android::os::incremental::PerUidReadTimeouts>&
|
||||
perUidReadTimeouts) const final {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
};
|
||||
|
||||
static JNIEnv* getOrAttachJniEnv(JavaVM* jvm);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <android/content/pm/FileSystemControlParcel.h>
|
||||
#include <android/content/pm/IDataLoader.h>
|
||||
#include <android/content/pm/IDataLoaderStatusListener.h>
|
||||
#include <android/os/incremental/PerUidReadTimeouts.h>
|
||||
#include <binder/IAppOpsCallback.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <binder/Status.h>
|
||||
@@ -103,6 +104,10 @@ public:
|
||||
virtual WaitResult waitForPendingReads(
|
||||
const Control& control, std::chrono::milliseconds timeout,
|
||||
std::vector<incfs::ReadInfo>* pendingReadsBuffer) const = 0;
|
||||
virtual ErrorCode setUidReadTimeouts(
|
||||
const Control& control,
|
||||
const std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts)
|
||||
const = 0;
|
||||
};
|
||||
|
||||
class AppOpsManagerWrapper {
|
||||
|
||||
@@ -42,6 +42,7 @@ using testing::NiceMock;
|
||||
|
||||
using namespace android::incfs;
|
||||
using namespace android::content::pm;
|
||||
using PerUidReadTimeouts = android::os::incremental::PerUidReadTimeouts;
|
||||
|
||||
namespace android::os::incremental {
|
||||
|
||||
@@ -307,6 +308,9 @@ public:
|
||||
MOCK_CONST_METHOD3(waitForPendingReads,
|
||||
WaitResult(const Control& control, std::chrono::milliseconds timeout,
|
||||
std::vector<incfs::ReadInfo>* pendingReadsBuffer));
|
||||
MOCK_CONST_METHOD2(setUidReadTimeouts,
|
||||
ErrorCode(const Control& control,
|
||||
const std::vector<PerUidReadTimeouts>& perUidReadTimeouts));
|
||||
|
||||
MockIncFs() { ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return()); }
|
||||
|
||||
@@ -665,7 +669,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsFails) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_LT(storageId, 0);
|
||||
}
|
||||
|
||||
@@ -676,7 +680,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsInvalidControlParcel)
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_LT(storageId, 0);
|
||||
}
|
||||
|
||||
@@ -689,7 +693,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMakeFileFails) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_LT(storageId, 0);
|
||||
}
|
||||
|
||||
@@ -703,7 +707,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageBindMountFails) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_LT(storageId, 0);
|
||||
}
|
||||
|
||||
@@ -721,7 +725,7 @@ TEST_F(IncrementalServiceTest, testCreateStoragePrepareDataLoaderFails) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_LT(storageId, 0);
|
||||
}
|
||||
|
||||
@@ -735,7 +739,7 @@ TEST_F(IncrementalServiceTest, testDeleteStorageSuccess) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_GE(storageId, 0);
|
||||
mIncrementalService->deleteStorage(storageId);
|
||||
}
|
||||
@@ -750,7 +754,7 @@ TEST_F(IncrementalServiceTest, testDataLoaderDestroyed) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_GE(storageId, 0);
|
||||
// Simulated crash/other connection breakage.
|
||||
mDataLoaderManager->setDataLoaderStatusDestroyed();
|
||||
@@ -767,7 +771,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderCreate) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_GE(storageId, 0);
|
||||
mDataLoaderManager->setDataLoaderStatusCreated();
|
||||
ASSERT_TRUE(mIncrementalService->startLoading(storageId));
|
||||
@@ -785,7 +789,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderPendingStart) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_GE(storageId, 0);
|
||||
ASSERT_TRUE(mIncrementalService->startLoading(storageId));
|
||||
mDataLoaderManager->setDataLoaderStatusCreated();
|
||||
@@ -802,7 +806,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderCreateUnavailable) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_GE(storageId, 0);
|
||||
mDataLoaderManager->setDataLoaderStatusUnavailable();
|
||||
}
|
||||
@@ -823,7 +827,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderRecreateOnPendingReads) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_GE(storageId, 0);
|
||||
mDataLoaderManager->setDataLoaderStatusUnavailable();
|
||||
ASSERT_NE(nullptr, mLooper->mCallback);
|
||||
@@ -877,7 +881,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, std::move(params), listener);
|
||||
{}, std::move(params), listener, {});
|
||||
ASSERT_GE(storageId, 0);
|
||||
|
||||
// Healthy state, registered for pending reads.
|
||||
@@ -972,7 +976,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_GE(storageId, 0);
|
||||
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
|
||||
}
|
||||
@@ -993,11 +997,11 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndDisabled) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_GE(storageId, 0);
|
||||
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
|
||||
// Now disable.
|
||||
mIncrementalService->disableReadLogs(storageId);
|
||||
mIncrementalService->disallowReadLogs(storageId);
|
||||
ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM);
|
||||
}
|
||||
|
||||
@@ -1019,7 +1023,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChang
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_GE(storageId, 0);
|
||||
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
|
||||
ASSERT_NE(nullptr, mAppOpsManager->mStoredCallback.get());
|
||||
@@ -1038,7 +1042,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsCheckPermissionFails) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_GE(storageId, 0);
|
||||
ASSERT_LT(mDataLoader->setStorageParams(true), 0);
|
||||
}
|
||||
@@ -1057,7 +1061,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsFails) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_GE(storageId, 0);
|
||||
ASSERT_LT(mDataLoader->setStorageParams(true), 0);
|
||||
}
|
||||
@@ -1066,7 +1070,7 @@ TEST_F(IncrementalServiceTest, testMakeDirectory) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
std::string dir_path("test");
|
||||
|
||||
// Expecting incfs to call makeDir on a path like:
|
||||
@@ -1085,7 +1089,7 @@ TEST_F(IncrementalServiceTest, testMakeDirectories) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
auto first = "first"sv;
|
||||
auto second = "second"sv;
|
||||
auto third = "third"sv;
|
||||
@@ -1108,7 +1112,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithNoFile) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
|
||||
}
|
||||
|
||||
@@ -1119,7 +1123,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithFailedRanges) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
|
||||
ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
|
||||
}
|
||||
@@ -1131,7 +1135,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccessWithEmptyRanges) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
|
||||
ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
|
||||
}
|
||||
@@ -1143,7 +1147,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccess) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
|
||||
ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
|
||||
}
|
||||
@@ -1155,8 +1159,8 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithNoFile) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId));
|
||||
{}, {}, {}, {});
|
||||
ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId).getProgress());
|
||||
}
|
||||
|
||||
TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) {
|
||||
@@ -1166,9 +1170,9 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
|
||||
ASSERT_EQ(-1, mIncrementalService->getLoadingProgress(storageId));
|
||||
ASSERT_EQ(-1, mIncrementalService->getLoadingProgress(storageId).getProgress());
|
||||
}
|
||||
|
||||
TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithEmptyRanges) {
|
||||
@@ -1178,9 +1182,9 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithEmptyRanges) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3);
|
||||
ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId));
|
||||
ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId).getProgress());
|
||||
}
|
||||
|
||||
TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) {
|
||||
@@ -1190,9 +1194,9 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3);
|
||||
ASSERT_EQ(0.5, mIncrementalService->getLoadingProgress(storageId));
|
||||
ASSERT_EQ(0.5, mIncrementalService->getLoadingProgress(storageId).getProgress());
|
||||
}
|
||||
|
||||
TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerSuccess) {
|
||||
@@ -1202,7 +1206,7 @@ TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerSuccess) {
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
sp<NiceMock<MockStorageLoadingProgressListener>> listener{
|
||||
new NiceMock<MockStorageLoadingProgressListener>};
|
||||
NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get();
|
||||
@@ -1227,7 +1231,7 @@ TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerFailsToGetProg
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, {}, {});
|
||||
{}, {}, {}, {});
|
||||
sp<NiceMock<MockStorageLoadingProgressListener>> listener{
|
||||
new NiceMock<MockStorageLoadingProgressListener>};
|
||||
NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get();
|
||||
@@ -1242,9 +1246,10 @@ TEST_F(IncrementalServiceTest, testRegisterStorageHealthListenerSuccess) {
|
||||
NiceMock<MockStorageHealthListener>* newListenerMock = newListener.get();
|
||||
|
||||
TemporaryDir tempDir;
|
||||
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew,
|
||||
{}, StorageHealthCheckParams{}, listener);
|
||||
int storageId =
|
||||
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew, {},
|
||||
StorageHealthCheckParams{}, listener, {});
|
||||
ASSERT_GE(storageId, 0);
|
||||
StorageHealthCheckParams newParams;
|
||||
newParams.blockedTimeoutMs = 10000;
|
||||
@@ -1313,4 +1318,123 @@ TEST_F(IncrementalServiceTest, testRegisterStorageHealthListenerSuccess) {
|
||||
mTimedQueue->clearJob(storageId);
|
||||
}
|
||||
|
||||
static std::vector<PerUidReadTimeouts> createPerUidTimeouts(
|
||||
std::initializer_list<std::tuple<int, int, int, int>> tuples) {
|
||||
std::vector<PerUidReadTimeouts> result;
|
||||
for (auto&& tuple : tuples) {
|
||||
result.emplace_back();
|
||||
auto& timeouts = result.back();
|
||||
timeouts.uid = std::get<0>(tuple);
|
||||
timeouts.minTimeUs = std::get<1>(tuple);
|
||||
timeouts.minPendingTimeUs = std::get<2>(tuple);
|
||||
timeouts.maxPendingTimeUs = std::get<3>(tuple);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static ErrorCode checkPerUidTimeouts(const Control& control,
|
||||
const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
|
||||
std::vector<PerUidReadTimeouts> expected =
|
||||
createPerUidTimeouts({{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 100000000}});
|
||||
EXPECT_EQ(expected, perUidReadTimeouts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ErrorCode checkPerUidTimeoutsEmpty(
|
||||
const Control& control, const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
|
||||
EXPECT_EQ(0u, perUidReadTimeouts.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST_F(IncrementalServiceTest, testPerUidTimeoutsTooShort) {
|
||||
EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
|
||||
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
|
||||
EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
|
||||
EXPECT_CALL(*mDataLoader, start(_)).Times(0);
|
||||
EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
|
||||
EXPECT_CALL(*mIncFs, setUidReadTimeouts(_, _)).Times(0);
|
||||
EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(0);
|
||||
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
|
||||
TemporaryDir tempDir;
|
||||
int storageId =
|
||||
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew, {}, {},
|
||||
{},
|
||||
createPerUidTimeouts(
|
||||
{{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 5}}));
|
||||
ASSERT_GE(storageId, 0);
|
||||
}
|
||||
|
||||
TEST_F(IncrementalServiceTest, testPerUidTimeoutsSuccess) {
|
||||
mVold->setIncFsMountOptionsSuccess();
|
||||
mAppOpsManager->checkPermissionSuccess();
|
||||
mFs->hasFiles();
|
||||
|
||||
EXPECT_CALL(*mIncFs, setUidReadTimeouts(_, _))
|
||||
// First call.
|
||||
.WillOnce(Invoke(&checkPerUidTimeouts))
|
||||
// Fully loaded and no readlogs.
|
||||
.WillOnce(Invoke(&checkPerUidTimeoutsEmpty));
|
||||
EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(3);
|
||||
|
||||
// Empty storage.
|
||||
mIncFs->countFilledBlocksEmpty();
|
||||
|
||||
TemporaryDir tempDir;
|
||||
int storageId =
|
||||
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
|
||||
IncrementalService::CreateOptions::CreateNew, {}, {},
|
||||
{},
|
||||
createPerUidTimeouts({{0, 1, 2, 3},
|
||||
{1, 2, 3, 4},
|
||||
{2, 3, 4, 100000000}}));
|
||||
ASSERT_GE(storageId, 0);
|
||||
|
||||
{
|
||||
// Timed callback present -> 0 progress.
|
||||
ASSERT_EQ(storageId, mTimedQueue->mId);
|
||||
ASSERT_GE(mTimedQueue->mAfter, std::chrono::seconds(1));
|
||||
const auto timedCallback = mTimedQueue->mWhat;
|
||||
mTimedQueue->clearJob(storageId);
|
||||
|
||||
// Still loading.
|
||||
mIncFs->countFilledBlocksSuccess();
|
||||
|
||||
// Call it again.
|
||||
timedCallback();
|
||||
}
|
||||
|
||||
{
|
||||
// Still present -> 0.5 progress.
|
||||
ASSERT_EQ(storageId, mTimedQueue->mId);
|
||||
ASSERT_GE(mTimedQueue->mAfter, std::chrono::seconds(1));
|
||||
const auto timedCallback = mTimedQueue->mWhat;
|
||||
mTimedQueue->clearJob(storageId);
|
||||
|
||||
// Fully loaded but readlogs collection enabled.
|
||||
mIncFs->countFilledBlocksFullyLoaded();
|
||||
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
|
||||
|
||||
// Call it again.
|
||||
timedCallback();
|
||||
}
|
||||
|
||||
{
|
||||
// Still present -> fully loaded + readlogs.
|
||||
ASSERT_EQ(storageId, mTimedQueue->mId);
|
||||
ASSERT_GE(mTimedQueue->mAfter, std::chrono::seconds(1));
|
||||
const auto timedCallback = mTimedQueue->mWhat;
|
||||
mTimedQueue->clearJob(storageId);
|
||||
|
||||
// Now disable readlogs.
|
||||
ASSERT_GE(mDataLoader->setStorageParams(false), 0);
|
||||
|
||||
// Call it again.
|
||||
timedCallback();
|
||||
}
|
||||
|
||||
// No callbacks anymore -> fully loaded and no readlogs.
|
||||
ASSERT_EQ(mTimedQueue->mAfter, Milliseconds());
|
||||
}
|
||||
|
||||
} // namespace android::os::incremental
|
||||
|
||||
@@ -29,6 +29,10 @@ import android.util.SparseArray;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.util.HexDump;
|
||||
import com.android.server.pm.PerPackageReadTimeouts.Timeouts;
|
||||
import com.android.server.pm.PerPackageReadTimeouts.VersionCodes;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -45,6 +49,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
// atest PackageManagerServiceTest
|
||||
// runtest -c com.android.server.pm.PackageManagerServiceTest frameworks-services
|
||||
// bit FrameworksServicesTests:com.android.server.pm.PackageManagerServiceTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@@ -182,6 +187,219 @@ public class PackageManagerServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeouts() {
|
||||
Timeouts defaults = Timeouts.parse("3600000001:3600000002:3600000003");
|
||||
Assert.assertEquals(3600000001L, defaults.minTimeUs);
|
||||
Assert.assertEquals(3600000002L, defaults.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000003L, defaults.maxPendingTimeUs);
|
||||
|
||||
Timeouts empty = Timeouts.parse("");
|
||||
Assert.assertEquals(3600000000L, empty.minTimeUs);
|
||||
Assert.assertEquals(3600000000L, empty.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000000L, empty.maxPendingTimeUs);
|
||||
|
||||
Timeouts partial0 = Timeouts.parse("10000::");
|
||||
Assert.assertEquals(10000L, partial0.minTimeUs);
|
||||
Assert.assertEquals(3600000000L, partial0.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000000L, partial0.maxPendingTimeUs);
|
||||
|
||||
Timeouts partial1 = Timeouts.parse("10000:10001:");
|
||||
Assert.assertEquals(10000L, partial1.minTimeUs);
|
||||
Assert.assertEquals(10001L, partial1.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000000L, partial1.maxPendingTimeUs);
|
||||
|
||||
Timeouts fullDefault = Timeouts.parse("3600000000:3600000000:3600000000");
|
||||
Assert.assertEquals(3600000000L, fullDefault.minTimeUs);
|
||||
Assert.assertEquals(3600000000L, fullDefault.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000000L, fullDefault.maxPendingTimeUs);
|
||||
|
||||
Timeouts full = Timeouts.parse("10000:10001:10002");
|
||||
Assert.assertEquals(10000L, full.minTimeUs);
|
||||
Assert.assertEquals(10001L, full.minPendingTimeUs);
|
||||
Assert.assertEquals(10002L, full.maxPendingTimeUs);
|
||||
|
||||
Timeouts invalid0 = Timeouts.parse(":10000");
|
||||
Assert.assertEquals(3600000000L, invalid0.minTimeUs);
|
||||
Assert.assertEquals(3600000000L, invalid0.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000000L, invalid0.maxPendingTimeUs);
|
||||
|
||||
Timeouts invalid1 = Timeouts.parse(":10000::");
|
||||
Assert.assertEquals(3600000000L, invalid1.minTimeUs);
|
||||
Assert.assertEquals(3600000000L, invalid1.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000000L, invalid1.maxPendingTimeUs);
|
||||
|
||||
Timeouts invalid2 = Timeouts.parse("10000:10001:abcd");
|
||||
Assert.assertEquals(10000L, invalid2.minTimeUs);
|
||||
Assert.assertEquals(10001L, invalid2.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000000L, invalid2.maxPendingTimeUs);
|
||||
|
||||
Timeouts invalid3 = Timeouts.parse(":10000:");
|
||||
Assert.assertEquals(3600000000L, invalid3.minTimeUs);
|
||||
Assert.assertEquals(3600000000L, invalid3.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000000L, invalid3.maxPendingTimeUs);
|
||||
|
||||
Timeouts invalid4 = Timeouts.parse("abcd:10001:10002");
|
||||
Assert.assertEquals(3600000000L, invalid4.minTimeUs);
|
||||
Assert.assertEquals(3600000000L, invalid4.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000000L, invalid4.maxPendingTimeUs);
|
||||
|
||||
Timeouts invalid5 = Timeouts.parse("::1000000000000000000000000");
|
||||
Assert.assertEquals(3600000000L, invalid5.minTimeUs);
|
||||
Assert.assertEquals(3600000000L, invalid5.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000000L, invalid5.maxPendingTimeUs);
|
||||
|
||||
Timeouts invalid6 = Timeouts.parse("-10000:10001:10002");
|
||||
Assert.assertEquals(3600000000L, invalid6.minTimeUs);
|
||||
Assert.assertEquals(3600000000L, invalid6.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000000L, invalid6.maxPendingTimeUs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVersionCodes() {
|
||||
final VersionCodes defaults = VersionCodes.parse("");
|
||||
Assert.assertEquals(Long.MIN_VALUE, defaults.minVersionCode);
|
||||
Assert.assertEquals(Long.MAX_VALUE, defaults.maxVersionCode);
|
||||
|
||||
VersionCodes single = VersionCodes.parse("191000070");
|
||||
Assert.assertEquals(191000070, single.minVersionCode);
|
||||
Assert.assertEquals(191000070, single.maxVersionCode);
|
||||
|
||||
VersionCodes single2 = VersionCodes.parse("191000070-191000070");
|
||||
Assert.assertEquals(191000070, single2.minVersionCode);
|
||||
Assert.assertEquals(191000070, single2.maxVersionCode);
|
||||
|
||||
VersionCodes upto = VersionCodes.parse("-191000070");
|
||||
Assert.assertEquals(Long.MIN_VALUE, upto.minVersionCode);
|
||||
Assert.assertEquals(191000070, upto.maxVersionCode);
|
||||
|
||||
VersionCodes andabove = VersionCodes.parse("191000070-");
|
||||
Assert.assertEquals(191000070, andabove.minVersionCode);
|
||||
Assert.assertEquals(Long.MAX_VALUE, andabove.maxVersionCode);
|
||||
|
||||
VersionCodes range = VersionCodes.parse("191000070-201000070");
|
||||
Assert.assertEquals(191000070, range.minVersionCode);
|
||||
Assert.assertEquals(201000070, range.maxVersionCode);
|
||||
|
||||
VersionCodes invalid0 = VersionCodes.parse("201000070-191000070");
|
||||
Assert.assertEquals(Long.MIN_VALUE, invalid0.minVersionCode);
|
||||
Assert.assertEquals(Long.MAX_VALUE, invalid0.maxVersionCode);
|
||||
|
||||
VersionCodes invalid1 = VersionCodes.parse("abcd-191000070");
|
||||
Assert.assertEquals(Long.MIN_VALUE, invalid1.minVersionCode);
|
||||
Assert.assertEquals(191000070, invalid1.maxVersionCode);
|
||||
|
||||
VersionCodes invalid2 = VersionCodes.parse("abcd");
|
||||
Assert.assertEquals(Long.MIN_VALUE, invalid2.minVersionCode);
|
||||
Assert.assertEquals(Long.MAX_VALUE, invalid2.maxVersionCode);
|
||||
|
||||
VersionCodes invalid3 = VersionCodes.parse("191000070-abcd");
|
||||
Assert.assertEquals(191000070, invalid3.minVersionCode);
|
||||
Assert.assertEquals(Long.MAX_VALUE, invalid3.maxVersionCode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerPackageReadTimeouts() {
|
||||
final String sha256 = "336faefc91bb2dddf9b21829106fbc607b862132fecd273e1b6b3ea55f09d4e1";
|
||||
final VersionCodes defVCs = VersionCodes.parse("");
|
||||
final Timeouts defTs = Timeouts.parse("3600000001:3600000002:3600000003");
|
||||
|
||||
PerPackageReadTimeouts empty = PerPackageReadTimeouts.parse("", defVCs, defTs);
|
||||
Assert.assertNull(empty);
|
||||
|
||||
PerPackageReadTimeouts packageOnly = PerPackageReadTimeouts.parse("package.com", defVCs,
|
||||
defTs);
|
||||
Assert.assertEquals("package.com", packageOnly.packageName);
|
||||
Assert.assertEquals(null, packageOnly.sha256certificate);
|
||||
Assert.assertEquals(Long.MIN_VALUE, packageOnly.versionCodes.minVersionCode);
|
||||
Assert.assertEquals(Long.MAX_VALUE, packageOnly.versionCodes.maxVersionCode);
|
||||
Assert.assertEquals(3600000001L, packageOnly.timeouts.minTimeUs);
|
||||
Assert.assertEquals(3600000002L, packageOnly.timeouts.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000003L, packageOnly.timeouts.maxPendingTimeUs);
|
||||
|
||||
PerPackageReadTimeouts packageHash = PerPackageReadTimeouts.parse(
|
||||
"package.com:" + sha256, defVCs, defTs);
|
||||
Assert.assertEquals("package.com", packageHash.packageName);
|
||||
Assert.assertEquals(sha256, bytesToHexString(packageHash.sha256certificate));
|
||||
Assert.assertEquals(Long.MIN_VALUE, packageHash.versionCodes.minVersionCode);
|
||||
Assert.assertEquals(Long.MAX_VALUE, packageHash.versionCodes.maxVersionCode);
|
||||
Assert.assertEquals(3600000001L, packageHash.timeouts.minTimeUs);
|
||||
Assert.assertEquals(3600000002L, packageHash.timeouts.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000003L, packageHash.timeouts.maxPendingTimeUs);
|
||||
|
||||
PerPackageReadTimeouts packageVersionCode = PerPackageReadTimeouts.parse(
|
||||
"package.com::191000070", defVCs, defTs);
|
||||
Assert.assertEquals("package.com", packageVersionCode.packageName);
|
||||
Assert.assertEquals(null, packageVersionCode.sha256certificate);
|
||||
Assert.assertEquals(191000070, packageVersionCode.versionCodes.minVersionCode);
|
||||
Assert.assertEquals(191000070, packageVersionCode.versionCodes.maxVersionCode);
|
||||
Assert.assertEquals(3600000001L, packageVersionCode.timeouts.minTimeUs);
|
||||
Assert.assertEquals(3600000002L, packageVersionCode.timeouts.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000003L, packageVersionCode.timeouts.maxPendingTimeUs);
|
||||
|
||||
PerPackageReadTimeouts full = PerPackageReadTimeouts.parse(
|
||||
"package.com:" + sha256 + ":191000070-201000070:10001:10002:10003", defVCs, defTs);
|
||||
Assert.assertEquals("package.com", full.packageName);
|
||||
Assert.assertEquals(sha256, bytesToHexString(full.sha256certificate));
|
||||
Assert.assertEquals(191000070, full.versionCodes.minVersionCode);
|
||||
Assert.assertEquals(201000070, full.versionCodes.maxVersionCode);
|
||||
Assert.assertEquals(10001L, full.timeouts.minTimeUs);
|
||||
Assert.assertEquals(10002L, full.timeouts.minPendingTimeUs);
|
||||
Assert.assertEquals(10003L, full.timeouts.maxPendingTimeUs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPerPackageReadTimeouts() {
|
||||
Assert.assertEquals(0, getPerPackageReadTimeouts(null).length);
|
||||
Assert.assertEquals(0, getPerPackageReadTimeouts("").length);
|
||||
Assert.assertEquals(0, getPerPackageReadTimeouts(",,,,").length);
|
||||
|
||||
final String sha256 = "0fae93f1a7925b4c68bbea80ad3eaa41acfc9bc6f10bf1054f5d93a2bd556093";
|
||||
|
||||
PerPackageReadTimeouts[] singlePackage = getPerPackageReadTimeouts(
|
||||
"package.com:" + sha256 + ":191000070-201000070:10001:10002:10003");
|
||||
Assert.assertEquals(1, singlePackage.length);
|
||||
Assert.assertEquals("package.com", singlePackage[0].packageName);
|
||||
Assert.assertEquals(sha256, bytesToHexString(singlePackage[0].sha256certificate));
|
||||
Assert.assertEquals(191000070, singlePackage[0].versionCodes.minVersionCode);
|
||||
Assert.assertEquals(201000070, singlePackage[0].versionCodes.maxVersionCode);
|
||||
Assert.assertEquals(10001L, singlePackage[0].timeouts.minTimeUs);
|
||||
Assert.assertEquals(10002L, singlePackage[0].timeouts.minPendingTimeUs);
|
||||
Assert.assertEquals(10003L, singlePackage[0].timeouts.maxPendingTimeUs);
|
||||
|
||||
PerPackageReadTimeouts[] multiPackage = getPerPackageReadTimeouts("package.com:" + sha256
|
||||
+ ":191000070-201000070:10001:10002:10003,package1.com::123456");
|
||||
Assert.assertEquals(2, multiPackage.length);
|
||||
Assert.assertEquals("package.com", multiPackage[0].packageName);
|
||||
Assert.assertEquals(sha256, bytesToHexString(multiPackage[0].sha256certificate));
|
||||
Assert.assertEquals(191000070, multiPackage[0].versionCodes.minVersionCode);
|
||||
Assert.assertEquals(201000070, multiPackage[0].versionCodes.maxVersionCode);
|
||||
Assert.assertEquals(10001L, multiPackage[0].timeouts.minTimeUs);
|
||||
Assert.assertEquals(10002L, multiPackage[0].timeouts.minPendingTimeUs);
|
||||
Assert.assertEquals(10003L, multiPackage[0].timeouts.maxPendingTimeUs);
|
||||
Assert.assertEquals("package1.com", multiPackage[1].packageName);
|
||||
Assert.assertEquals(null, multiPackage[1].sha256certificate);
|
||||
Assert.assertEquals(123456, multiPackage[1].versionCodes.minVersionCode);
|
||||
Assert.assertEquals(123456, multiPackage[1].versionCodes.maxVersionCode);
|
||||
Assert.assertEquals(3600000001L, multiPackage[1].timeouts.minTimeUs);
|
||||
Assert.assertEquals(3600000002L, multiPackage[1].timeouts.minPendingTimeUs);
|
||||
Assert.assertEquals(3600000003L, multiPackage[1].timeouts.maxPendingTimeUs);
|
||||
}
|
||||
|
||||
private static PerPackageReadTimeouts[] getPerPackageReadTimeouts(String knownDigestersList) {
|
||||
final String defaultTimeouts = "3600000001:3600000002:3600000003";
|
||||
List<PerPackageReadTimeouts> result = PerPackageReadTimeouts.parseDigestersList(
|
||||
defaultTimeouts, knownDigestersList);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
return result.toArray(new PerPackageReadTimeouts[result.size()]);
|
||||
}
|
||||
|
||||
private static String bytesToHexString(byte[] bytes) {
|
||||
return HexDump.toHexString(bytes, 0, bytes.length, /*upperCase=*/ false);
|
||||
}
|
||||
|
||||
private List<Integer> getKnownPackageIdsList() throws IllegalAccessException {
|
||||
final ArrayList<Integer> knownPackageIds = new ArrayList<>();
|
||||
final Field[] allFields = PackageManagerInternal.class.getDeclaredFields();
|
||||
|
||||
Reference in New Issue
Block a user