Merge "Use the PlatformLogger in the platform" into sc-dev
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
//
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user