Merge "Use the PlatformLogger in the platform" into sc-dev

This commit is contained in:
Xiaoyu Jin
2021-04-12 18:04:09 +00:00
committed by Android (Google) Code Review
8 changed files with 368 additions and 33 deletions

View File

@@ -23,6 +23,7 @@ import android.app.appsearch.util.SchemaMigrationUtil;
import android.os.Bundle;
import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -270,8 +271,8 @@ public final class AppSearchSession implements Closeable {
documentBundles.add(documents.get(i).getBundle());
}
try {
// TODO(b/173532925) a timestamp needs to be sent here to calculate binder latency
mService.putDocuments(mPackageName, mDatabaseName, documentBundles, mUserId,
/*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
new IAppSearchBatchResultCallback.Stub() {
public void onResult(AppSearchBatchResult result) {
executor.execute(() -> callback.onResult(result));

View File

@@ -94,6 +94,7 @@ interface IAppSearchManager {
* @param databaseName The name of the database where this document lives.
* @param documentBundes List of GenericDocument bundles.
* @param userId Id of the calling user
* @param binderCallStartTimeMillis start timestamp of binder call in Millis
* @param callback
* If the call fails to start, {@link IAppSearchBatchResultCallback#onSystemError}
* will be called with the cause throwable. Otherwise,
@@ -106,6 +107,7 @@ interface IAppSearchManager {
in String databaseName,
in List<Bundle> documentBundles,
in int userId,
in long binderCallStartTimeMillis,
in IAppSearchBatchResultCallback callback);
/**

View File

@@ -18,6 +18,7 @@ package com.android.server.appsearch;
import static android.app.appsearch.AppSearchResult.throwableToFailedResult;
import static android.os.UserHandle.USER_NULL;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -45,6 +46,7 @@ import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
@@ -57,6 +59,9 @@ import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.appsearch.external.localstorage.AppSearchImpl;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
import com.android.server.appsearch.stats.LoggerInstanceManager;
import com.android.server.appsearch.stats.PlatformLogger;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -80,6 +85,7 @@ public class AppSearchManagerService extends SystemService {
private PackageManagerInternal mPackageManagerInternal;
private ImplInstanceManager mImplInstanceManager;
private UserManager mUserManager;
private LoggerInstanceManager mLoggerInstanceManager;
// Never call shutdownNow(). It will cancel the futures it's returned. And since
// Executor#execute won't return anything, we will hang forever waiting for the execution.
@@ -106,6 +112,7 @@ public class AppSearchManagerService extends SystemService {
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mImplInstanceManager = ImplInstanceManager.getInstance(mContext);
mUserManager = mContext.getSystemService(UserManager.class);
mLoggerInstanceManager = LoggerInstanceManager.getInstance();
registerReceivers();
}
@@ -147,6 +154,7 @@ public class AppSearchManagerService extends SystemService {
private void handleUserRemoved(@UserIdInt int userId) {
try {
mImplInstanceManager.removeAppSearchImplForUser(userId);
mLoggerInstanceManager.removePlatformLoggerForUser(userId);
Slog.i(TAG, "Removed AppSearchImpl instance for user: " + userId);
} catch (Throwable t) {
Slog.e(TAG, "Unable to remove data for user: " + userId, t);
@@ -274,6 +282,7 @@ public class AppSearchManagerService extends SystemService {
@NonNull String databaseName,
@NonNull List<Bundle> documentBundles,
@UserIdInt int userId,
@ElapsedRealtimeLong long binderCallStartTimeMillis,
@NonNull IAppSearchBatchResultCallback callback) {
Preconditions.checkNotNull(packageName);
Preconditions.checkNotNull(databaseName);
@@ -282,6 +291,11 @@ public class AppSearchManagerService extends SystemService {
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
PlatformLogger logger = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
verifyUserUnlocked(callingUserId);
verifyCallingPackage(callingUid, packageName);
@@ -289,20 +303,46 @@ public class AppSearchManagerService extends SystemService {
new AppSearchBatchResult.Builder<>();
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(callingUserId);
logger = mLoggerInstanceManager.getPlatformLogger(callingUserId);
for (int i = 0; i < documentBundles.size(); i++) {
GenericDocument document = new GenericDocument(documentBundles.get(i));
try {
impl.putDocument(packageName, databaseName, document,
/*logger=*/ null);
impl.putDocument(packageName, databaseName, document, logger);
resultBuilder.setSuccess(document.getUri(), /*result=*/ null);
++operationSuccessCount;
} catch (Throwable t) {
resultBuilder.setResult(document.getUri(),
throwableToFailedResult(t));
AppSearchResult<Void> result = throwableToFailedResult(t);
resultBuilder.setResult(document.getUri(), result);
// for failures, we would just log the one for last failure
statusCode = result.getResultCode();
++operationFailureCount;
}
}
invokeCallbackOnResult(callback, resultBuilder.build());
} catch (Throwable t) {
invokeCallbackOnError(callback, t);
} finally {
if (logger != null) {
CallStats.Builder cBuilder = new CallStats.Builder(packageName,
databaseName)
.setCallType(CallStats.CALL_TYPE_PUT_DOCUMENTS)
// TODO(b/173532925) check the existing binder call latency chart
// is good enough for us:
// http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
.setEstimatedBinderLatencyMillis(
2 * (int) (totalLatencyStartTimeMillis
- binderCallStartTimeMillis))
.setNumOperationsSucceeded(operationSuccessCount)
.setNumOperationsFailed(operationFailureCount);
cBuilder.getGeneralStatsBuilder()
.setStatusCode(statusCode)
.setTotalLatencyMillis(
(int) (SystemClock.elapsedRealtime()
- totalLatencyStartTimeMillis));
logger.logStats(cBuilder.build());
}
}
});
}
@@ -717,6 +757,7 @@ public class AppSearchManagerService extends SystemService {
try {
verifyUserUnlocked(callingUserId);
mImplInstanceManager.getOrCreateAppSearchImpl(mContext, callingUserId);
mLoggerInstanceManager.getOrCreatePlatformLogger(getContext(), callingUserId);
invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
} catch (Throwable t) {
invokeCallbackOnError(callback, t);

View File

@@ -106,7 +106,6 @@ public final class ImplInstanceManager {
*
* @param userId The multi-user userId of the user that need to be removed.
*/
@NonNull
public void removeAppSearchImplForUser(@UserIdInt int userId) {
synchronized (mInstancesLocked) {
mInstancesLocked.remove(userId);

View File

@@ -0,0 +1,131 @@
/*
* 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.appsearch.stats;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.server.appsearch.AppSearchManagerService;
/**
* Manages the lifecycle of instances of {@link PlatformLogger}.
*
* <p>These instances are managed per unique device-user.
*/
public final class LoggerInstanceManager {
// TODO(b/173532925) flags to control those three
// So probably we can't pass those three in the constructor but need to fetch the latest value
// every time we need them in the logger.
private static final int MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 100;
private static final int DEFAULT_SAMPLING_RATIO = 10;
private static volatile LoggerInstanceManager sLoggerInstanceManager;
@GuardedBy("mInstancesLocked")
private final SparseArray<PlatformLogger> mInstancesLocked = new SparseArray<>();
private LoggerInstanceManager() {
}
/**
* Gets an instance of {@link LoggerInstanceManager} to be used.
*
* <p>If no instance has been initialized yet, a new one will be created. Otherwise, the
* existing instance will be returned.
*/
@NonNull
public static LoggerInstanceManager getInstance() {
if (sLoggerInstanceManager == null) {
synchronized (LoggerInstanceManager.class) {
if (sLoggerInstanceManager == null) {
sLoggerInstanceManager =
new LoggerInstanceManager();
}
}
}
return sLoggerInstanceManager;
}
/**
* Gets an instance of PlatformLogger for the given user, or creates one if none exists.
*
* @param context The context
* @param userId The multi-user userId of the device user calling AppSearch
* @return An initialized {@link PlatformLogger} for this user
*/
@NonNull
public PlatformLogger getOrCreatePlatformLogger(
@NonNull Context context, @UserIdInt int userId) {
synchronized (mInstancesLocked) {
PlatformLogger instance = mInstancesLocked.get(userId);
if (instance == null) {
instance = new PlatformLogger(context, userId, new PlatformLogger.Config(
MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
DEFAULT_SAMPLING_RATIO,
// TODO(b/173532925) re-enable sampling ratios for different stats types
// once we have P/H flag manager setup in ag/13977824
/*samplingRatios=*/ new SparseIntArray()));
mInstancesLocked.put(userId, instance);
}
return instance;
}
}
/**
* Gets an instance of PlatformLogger for the given user.
*
* <p>This method should only be called by an initialized SearchSession, which has been already
* created the PlatformLogger instance for the given user.
*
* @param userId The multi-user userId of the device user calling AppSearch
* @return An initialized {@link PlatformLogger} for this user
* @throws IllegalStateException if {@link PlatformLogger} haven't created for the given user.
*/
@NonNull
public PlatformLogger getPlatformLogger(@UserIdInt int userId) {
synchronized (mInstancesLocked) {
PlatformLogger instance = mInstancesLocked.get(userId);
if (instance == null) {
// Impossible scenario, user cannot call an uninitialized SearchSession,
// getInstance should always find the instance for the given user and never try to
// create an instance for this user again.
throw new IllegalStateException(
"PlatformLogger has never been created for this user: " + userId);
}
return instance;
}
}
/**
* Remove an instance of {@link PlatformLogger} for the given user.
*
* <p>This method should only be called if {@link AppSearchManagerService} receives an
* ACTION_USER_REMOVED, which the logger instance of given user should be removed.
*
* @param userId The multi-user userId of the user that need to be removed.
*/
public void removePlatformLoggerForUser(@UserIdInt int userId) {
synchronized (mInstancesLocked) {
mInstancesLocked.remove(userId);
}
}
}

View File

@@ -17,21 +17,26 @@
package com.android.server.appsearch.stats;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.appsearch.exceptions.AppSearchException;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Process;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.server.appsearch.external.localstorage.AppSearchLogger;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.Random;
@@ -120,19 +125,18 @@ public final class PlatformLogger implements AppSearchLogger {
* @param minTimeIntervalBetweenSamplesMillis minimum time interval apart in Milliseconds
* required for two consecutive stats logged
* @param defaultSamplingRatio default sampling ratio
* @param samplingRatios SparseArray to customize sampling ratio for
* @param samplingRatios SparseArray to customize sampling ratio for
* different stat types
*/
public Config(long minTimeIntervalBetweenSamplesMillis,
int defaultSamplingRatio,
@Nullable SparseIntArray samplingRatios) {
@NonNull SparseIntArray samplingRatios) {
// TODO(b/173532925) Probably we can get rid of those three after we have p/h flags
// for them.
// e.g. we can just call DeviceConfig.get(SAMPLING_RATIO_FOR_PUT_DOCUMENTS).
mMinTimeIntervalBetweenSamplesMillis = minTimeIntervalBetweenSamplesMillis;
mDefaultSamplingRatio = defaultSamplingRatio;
if (samplingRatios != null) {
mSamplingRatios = samplingRatios;
} else {
mSamplingRatios = new SparseIntArray();
}
mSamplingRatios = samplingRatios;
}
}
@@ -169,7 +173,7 @@ public final class PlatformLogger implements AppSearchLogger {
Preconditions.checkNotNull(stats);
synchronized (mLock) {
if (shouldLogForTypeLocked(stats.getCallType())) {
logToWestworldLocked(stats);
logStatsImplLocked(stats);
}
}
}
@@ -180,7 +184,7 @@ public final class PlatformLogger implements AppSearchLogger {
Preconditions.checkNotNull(stats);
synchronized (mLock) {
if (shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)) {
logToWestworldLocked(stats);
logStatsImplLocked(stats);
}
}
}
@@ -201,25 +205,103 @@ public final class PlatformLogger implements AppSearchLogger {
}
@GuardedBy("mLock")
private void logToWestworldLocked(@NonNull CallStats stats) {
private void logStatsImplLocked(@NonNull CallStats stats) {
mLastPushTimeMillisLocked = SystemClock.elapsedRealtime();
ExtraStats extraStats = createExtraStatsLocked(stats.getGeneralStats().getPackageName(),
stats.getCallType());
/* TODO(b/173532925) Log the CallStats to Westworld
stats.log(..., samplingRatio, skippedSampleCount, ...)
*/
String database = stats.getGeneralStats().getDatabase();
try {
int hashCodeForDatabase = calculateHashCodeMd5(database);
FrameworkStatsLog.write(FrameworkStatsLog.APP_SEARCH_CALL_STATS_REPORTED,
extraStats.mSamplingRatio,
extraStats.mSkippedSampleCount,
extraStats.mPackageUid,
hashCodeForDatabase,
stats.getGeneralStats().getStatusCode(),
stats.getGeneralStats().getTotalLatencyMillis(),
stats.getCallType(),
stats.getEstimatedBinderLatencyMillis(),
stats.getNumOperationsSucceeded(),
stats.getNumOperationsFailed());
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
// TODO(b/184204720) report hashing error to Westworld
// We need to set a special value(e.g. 0xFFFFFFFF) for the hashing of the database,
// so in the dashboard we know there is some error for hashing.
//
// Something is wrong while calculating the hash code for database
// this shouldn't happen since we always use "MD5" and "UTF-8"
Log.e(TAG, "Error calculating hash code for database " + database, e);
}
}
@GuardedBy("mLock")
private void logToWestworldLocked(@NonNull PutDocumentStats stats) {
private void logStatsImplLocked(@NonNull PutDocumentStats stats) {
mLastPushTimeMillisLocked = SystemClock.elapsedRealtime();
ExtraStats extraStats = createExtraStatsLocked(stats.getGeneralStats().getPackageName(),
CallStats.CALL_TYPE_PUT_DOCUMENT);
/* TODO(b/173532925) Log the PutDocumentStats to Westworld
stats.log(..., samplingRatio, skippedSampleCount, ...)
*/
String database = stats.getGeneralStats().getDatabase();
try {
int hashCodeForDatabase = calculateHashCodeMd5(database);
FrameworkStatsLog.write(FrameworkStatsLog.APP_SEARCH_PUT_DOCUMENT_STATS_REPORTED,
extraStats.mSamplingRatio,
extraStats.mSkippedSampleCount,
extraStats.mPackageUid,
hashCodeForDatabase,
stats.getGeneralStats().getStatusCode(),
stats.getGeneralStats().getTotalLatencyMillis(),
stats.getGenerateDocumentProtoLatencyMillis(),
stats.getRewriteDocumentTypesLatencyMillis(),
stats.getNativeLatencyMillis(),
stats.getNativeDocumentStoreLatencyMillis(),
stats.getNativeIndexLatencyMillis(),
stats.getNativeIndexMergeLatencyMillis(),
stats.getNativeDocumentSizeBytes(),
stats.getNativeNumTokensIndexed(),
stats.getNativeExceededMaxNumTokens());
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
// TODO(b/184204720) report hashing error to Westworld
// We need to set a special value(e.g. 0xFFFFFFFF) for the hashing of the database,
// so in the dashboard we know there is some error for hashing.
//
// Something is wrong while calculating the hash code for database
// this shouldn't happen since we always use "MD5" and "UTF-8"
Log.e(TAG, "Error calculating hash code for database " + database, e);
}
}
/**
* Calculate the hash code as an integer by returning the last four bytes of its MD5.
*
* @param str a string
* @return hash code as an integer
* @throws AppSearchException if either algorithm or encoding does not exist.
*/
@VisibleForTesting
@NonNull
static int calculateHashCodeMd5(@NonNull String str) throws
NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes(/*charsetName=*/ "UTF-8"));
byte[] digest = md.digest();
// Since MD5 generates 16 bytes digest, we don't need to check the length here to see
// if it is smaller than sizeof(int)(4).
//
// We generate the same value as BigInteger(digest).intValue().
// BigInteger takes bytes[] and treat it as big endian. And its intValue() would get the
// lower 4 bytes. So here we take the last 4 bytes and treat them as big endian.
return (digest[12] & 0xFF) << 24
| (digest[13] & 0xFF) << 16
| (digest[14] & 0xFF) << 8
| (digest[15] & 0xFF);
}
/**
* Creates {@link ExtraStats} to hold additional information generated for logging.
*
* <p>This method is called by most of logToWestworldLocked functions to reduce code
* duplication.
*/
@VisibleForTesting
@GuardedBy("mLock")
@NonNull

View File

@@ -23,6 +23,7 @@ import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
@@ -38,6 +39,11 @@ import com.android.server.appsearch.external.localstorage.stats.CallStats;
import org.junit.Before;
import org.junit.Test;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class PlatformLoggerTest {
private static final int TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 100;
private static final int TEST_DEFAULT_SAMPLING_RATIO = 10;
@@ -57,15 +63,23 @@ public class PlatformLoggerTest {
};
}
static int calculateHashCodeMd5withBigInteger(@NonNull String str) throws
NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes(/*charsetName=*/ "UTF-8"));
byte[] digest = md.digest();
return new BigInteger(digest).intValue();
}
@Test
public void testcreateExtraStatsLocked_nullSamplingRatioMap_returnsDefaultSamplingRatio() {
public void testCreateExtraStatsLocked_nullSamplingRatioMap_returnsDefaultSamplingRatio() {
PlatformLogger logger = new PlatformLogger(
ApplicationProvider.getApplicationContext(),
UserHandle.USER_NULL,
new PlatformLogger.Config(
TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
TEST_DEFAULT_SAMPLING_RATIO,
/*samplingRatioMap=*/ null));
/*samplingRatios=*/ new SparseIntArray()));
// Make sure default sampling ratio is used if samplingMap is not provided.
assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
@@ -84,7 +98,7 @@ public class PlatformLoggerTest {
@Test
public void testcreateExtraStatsLocked_with_samplingRatioMap_returnsConfiguredSamplingRatio() {
public void testCreateExtraStatsLocked_with_samplingRatioMap_returnsConfiguredSamplingRatio() {
int putDocumentSamplingRatio = 1;
int querySamplingRatio = 2;
final SparseIntArray samplingRatios = new SparseIntArray();
@@ -98,8 +112,8 @@ public class PlatformLoggerTest {
TEST_DEFAULT_SAMPLING_RATIO,
samplingRatios));
// The default sampling ratio should be used if no sampling ratio is
// provided for certain call type.
// The default sampling ratio should be used if no sampling ratio is
// provided for certain call type.
assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
CallStats.CALL_TYPE_INITIALIZE).mSamplingRatio).isEqualTo(
TEST_DEFAULT_SAMPLING_RATIO);
@@ -117,6 +131,70 @@ public class PlatformLoggerTest {
querySamplingRatio);
}
@Test
public void testCalculateHashCode_MD5_int32_shortString()
throws NoSuchAlgorithmException, UnsupportedEncodingException {
final String str1 = "d1";
final String str2 = "d2";
int hashCodeForStr1 = PlatformLogger.calculateHashCodeMd5(str1);
// hashing should be stable
assertThat(hashCodeForStr1).isEqualTo(
PlatformLogger.calculateHashCodeMd5(str1));
assertThat(hashCodeForStr1).isNotEqualTo(
PlatformLogger.calculateHashCodeMd5(str2));
}
@Test
public void testGetCalculateCode_MD5_int32_mediumString()
throws NoSuchAlgorithmException, UnsupportedEncodingException {
final String str1 = "Siblings";
final String str2 = "Teheran";
int hashCodeForStr1 = PlatformLogger.calculateHashCodeMd5(str1);
// hashing should be stable
assertThat(hashCodeForStr1).isEqualTo(
PlatformLogger.calculateHashCodeMd5(str1));
assertThat(hashCodeForStr1).isNotEqualTo(
PlatformLogger.calculateHashCodeMd5(str2));
}
@Test
public void testCalculateHashCode_MD5_int32_longString() throws NoSuchAlgorithmException,
UnsupportedEncodingException {
final String str1 = "abcdefghijkl-mnopqrstuvwxyz";
final String str2 = "abcdefghijkl-mnopqrstuvwxy123";
int hashCodeForStr1 = PlatformLogger.calculateHashCodeMd5(str1);
// hashing should be stable
assertThat(hashCodeForStr1).isEqualTo(
PlatformLogger.calculateHashCodeMd5(str1));
assertThat(hashCodeForStr1).isNotEqualTo(
PlatformLogger.calculateHashCodeMd5(str2));
}
@Test
public void testCalculateHashCode_MD5_int32_sameAsBigInteger_intValue() throws
NoSuchAlgorithmException, UnsupportedEncodingException {
final String emptyStr = "";
final String shortStr = "a";
final String mediumStr = "Teheran";
final String longStr = "abcd-efgh-ijkl-mnop-qrst-uvwx-yz";
int emptyHashCode = PlatformLogger.calculateHashCodeMd5(emptyStr);
int shortHashCode = PlatformLogger.calculateHashCodeMd5(shortStr);
int mediumHashCode = PlatformLogger.calculateHashCodeMd5(mediumStr);
int longHashCode = PlatformLogger.calculateHashCodeMd5(longStr);
assertThat(emptyHashCode).isEqualTo(calculateHashCodeMd5withBigInteger(emptyStr));
assertThat(shortHashCode).isEqualTo(calculateHashCodeMd5withBigInteger(shortStr));
assertThat(mediumHashCode).isEqualTo(calculateHashCodeMd5withBigInteger(mediumStr));
assertThat(longHashCode).isEqualTo(calculateHashCodeMd5withBigInteger(longStr));
}
@Test
public void testShouldLogForTypeLocked_trueWhenSampleRatioIsOne() {
final int samplingRatio = 1;
@@ -127,7 +205,7 @@ public class PlatformLoggerTest {
new PlatformLogger.Config(
TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
samplingRatio,
/* samplingMap=*/ null));
/*samplingRatios=*/ new SparseIntArray()));
// Sample should always be logged for the first time if sampling is disabled(value is one).
assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isTrue();
@@ -145,7 +223,7 @@ public class PlatformLoggerTest {
new PlatformLogger.Config(
TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
samplingRatio,
/* samplingMap=*/ null));
/*samplingRatios=*/ new SparseIntArray()));
// Makes sure sample will be excluded due to sampling if sample ratio is negative.
assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isFalse();
@@ -167,7 +245,7 @@ public class PlatformLoggerTest {
new PlatformLogger.Config(
minTimeIntervalBetweenSamplesMillis,
samplingRatio,
/* samplingMap=*/ null));
/*samplingRatios=*/ new SparseIntArray()));
logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime());
// Makes sure sample will be excluded due to rate limiting if samples are too close.
@@ -189,7 +267,7 @@ public class PlatformLoggerTest {
new PlatformLogger.Config(
minTimeIntervalBetweenSamplesMillis,
samplingRatio,
/* samplingMap=*/ null));
/*samplingRatios=*/ new SparseIntArray()));
logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime());
// Makes sure sample will be logged if it is not too close to previous sample.
@@ -209,7 +287,7 @@ public class PlatformLoggerTest {
new PlatformLogger.Config(
TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
TEST_DEFAULT_SAMPLING_RATIO,
/* samplingMap=*/ null));
/*samplingRatios=*/ new SparseIntArray()));
mMockPackageManager.mockGetPackageUidAsUser(testPackageName, mContext.getUserId(), testUid);
//

View File

@@ -691,7 +691,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
@Override
public void putDocuments(String packageName, String databaseName,
List<Bundle> documentBundles, int userId, IAppSearchBatchResultCallback callback)
List<Bundle> documentBundles, int userId, long binderCallStartTimeMillis,
IAppSearchBatchResultCallback callback)
throws RemoteException {
final List<GenericDocument> docs = new ArrayList<>(documentBundles.size());
for (Bundle bundle : documentBundles) {