diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java index 5910130e6069e..564a55c2853ed 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java @@ -350,6 +350,7 @@ public final class AppSearchSession implements Closeable { new ArrayList<>(request.getIds()), request.getProjectionsInternal(), mUserId, + /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), new IAppSearchBatchResultCallback.Stub() { @Override public void onResult(AppSearchBatchResultParcel resultParcel) { @@ -563,6 +564,7 @@ public final class AppSearchSession implements Closeable { try { mService.removeByDocumentId(mPackageName, mDatabaseName, request.getNamespace(), new ArrayList<>(request.getIds()), mUserId, + /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), new IAppSearchBatchResultCallback.Stub() { @Override public void onResult(AppSearchBatchResultParcel resultParcel) { @@ -613,6 +615,7 @@ public final class AppSearchSession implements Closeable { try { mService.removeByQuery(mPackageName, mDatabaseName, queryExpression, searchSpec.getBundle(), mUserId, + /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), new IAppSearchResultCallback.Stub() { @Override public void onResult(AppSearchResultParcel resultParcel) { @@ -672,7 +675,8 @@ public final class AppSearchSession implements Closeable { public void close() { if (mIsMutated && !mIsClosed) { try { - mService.persistToDisk(mUserId); + mService.persistToDisk(mUserId, + /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime()); mIsClosed = true; } catch (RemoteException e) { Log.e(TAG, "Unable to close the AppSearchSession", e); @@ -702,6 +706,7 @@ public final class AppSearchSession implements Closeable { request.isForceOverride(), request.getVersion(), mUserId, + /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), new IAppSearchResultCallback.Stub() { @Override public void onResult(AppSearchResultParcel resultParcel) { @@ -789,6 +794,7 @@ public final class AppSearchSession implements Closeable { /*forceOverride=*/ false, request.getVersion(), mUserId, + /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), new IAppSearchResultCallback.Stub() { @Override public void onResult(AppSearchResultParcel resultParcel) { @@ -840,6 +846,7 @@ public final class AppSearchSession implements Closeable { /*forceOverride=*/ true, request.getVersion(), mUserId, + /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), new IAppSearchResultCallback.Stub() { @Override public void onResult(AppSearchResultParcel resultParcel) { diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java index 7d246c20251be..06411b1e51677 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java @@ -23,6 +23,7 @@ import android.app.appsearch.aidl.AppSearchResultParcel; import android.app.appsearch.aidl.IAppSearchManager; import android.app.appsearch.aidl.IAppSearchResultCallback; import android.os.RemoteException; +import android.os.SystemClock; import android.util.Log; import com.android.internal.util.Preconditions; @@ -182,7 +183,8 @@ public class GlobalSearchSession implements Closeable { public void close() { if (mIsMutated && !mIsClosed) { try { - mService.persistToDisk(mUserId); + mService.persistToDisk(mUserId, + /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime()); mIsClosed = true; } catch (RemoteException e) { Log.e(TAG, "Unable to close the GlobalSearchSession", e); diff --git a/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl index 6f7e82e8126c9..9e077ee4df343 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl +++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl @@ -37,6 +37,7 @@ interface IAppSearchManager { * incompatible documents will be deleted. * @param schemaVersion The overall schema version number of the request. * @param userId Id of the calling user + * @param binderCallStartTimeMillis start timestamp of binder call in Millis * @param callback {@link IAppSearchResultCallback#onResult} will be called with an * {@link AppSearchResult}<{@link Bundle}>, where the value are * {@link SetSchemaResponse} bundle. @@ -50,6 +51,7 @@ interface IAppSearchManager { boolean forceOverride, in int schemaVersion, in int userId, + in long binderCallStartTimeMillis, in IAppSearchResultCallback callback); /** @@ -115,6 +117,7 @@ interface IAppSearchManager { * @param typePropertyPaths A map of schema type to a list of property paths to return in the * result. * @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, @@ -129,6 +132,7 @@ interface IAppSearchManager { in List ids, in Map> typePropertyPaths, in int userId, + in long binderCallStartTimeMillis, in IAppSearchBatchResultCallback callback); /** @@ -269,6 +273,7 @@ interface IAppSearchManager { * @param namespace Namespace of the document to remove. * @param ids The IDs of the documents to delete * @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, @@ -283,6 +288,7 @@ interface IAppSearchManager { in String namespace, in List ids, in int userId, + in long binderCallStartTimeMillis, in IAppSearchBatchResultCallback callback); /** @@ -293,6 +299,7 @@ interface IAppSearchManager { * @param queryExpression String to search for * @param searchSpecBundle SearchSpec bundle * @param userId Id of the calling user + * @param binderCallStartTimeMillis start timestamp of binder call in Millis * @param callback {@link IAppSearchResultCallback#onResult} will be called with an * {@link AppSearchResult}<{@link Void}>. */ @@ -302,6 +309,7 @@ interface IAppSearchManager { in String queryExpression, in Bundle searchSpecBundle, in int userId, + in long binderCallStartTimeMillis, in IAppSearchResultCallback callback); /** @@ -324,8 +332,9 @@ interface IAppSearchManager { * Persists all update/delete requests to the disk. * * @param userId Id of the calling user + * @param binderCallStartTimeMillis start timestamp of binder call in Millis */ - void persistToDisk(in int userId); + void persistToDisk(in int userId, in long binderCallStartTimeMillis); /** * Creates and initializes AppSearchImpl for the calling app. diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index 777f9fe00bcad..58ce6e56de595 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -275,14 +275,20 @@ public class AppSearchManagerService extends SystemService { boolean forceOverride, int schemaVersion, @UserIdInt int userId, + @ElapsedRealtimeLong long binderCallStartTimeMillis, @NonNull IAppSearchResultCallback callback) { Objects.requireNonNull(packageName); Objects.requireNonNull(databaseName); Objects.requireNonNull(schemaBundles); Objects.requireNonNull(callback); + long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); int callingUid = Binder.getCallingUid(); int callingUserId = handleIncomingUser(userId, callingUid); EXECUTOR.execute(() -> { + @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; + PlatformLogger logger = null; + int operationSuccessCount = 0; + int operationFailureCount = 0; try { verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); @@ -303,6 +309,7 @@ public class AppSearchManagerService extends SystemService { schemasPackageAccessible.put(entry.getKey(), packageIdentifiers); } AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId); + logger = mLoggerInstanceManager.getPlatformLogger(callingUserId); SetSchemaResponse setSchemaResponse = impl.setSchema( packageName, databaseName, @@ -311,10 +318,33 @@ public class AppSearchManagerService extends SystemService { schemasPackageAccessible, forceOverride, schemaVersion); + ++operationSuccessCount; invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(setSchemaResponse.getBundle())); } catch (Throwable t) { + ++operationFailureCount; + statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); + } finally { + if (logger != null) { + int estimatedBinderLatencyMillis = + 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); + int totalLatencyMillis = + (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); + CallStats.Builder cBuilder = new CallStats.Builder(packageName, + databaseName) + .setCallType(CallStats.CALL_TYPE_SET_SCHEMA) + // TODO(b/173532925) check the existing binder call latency chart + // is good enough for us: + // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 + .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) + .setNumOperationsSucceeded(operationSuccessCount) + .setNumOperationsFailed(operationFailureCount); + cBuilder.getGeneralStatsBuilder() + .setStatusCode(statusCode) + .setTotalLatencyMillis(totalLatencyMillis); + logger.logStats(cBuilder.build()); + } } }); } @@ -411,7 +441,8 @@ public class AppSearchManagerService extends SystemService { throwableToFailedResult(t)); AppSearchResult result = throwableToFailedResult(t); resultBuilder.setResult(document.getId(), result); - // for failures, we would just log the one for last failure + // Since we can only include one status code in the atom, + // for failures, we would just save the one for the last failure statusCode = result.getResultCode(); ++operationFailureCount; } @@ -420,6 +451,8 @@ public class AppSearchManagerService extends SystemService { impl.persistToDisk(PersistType.Code.LITE); invokeCallbackOnResult(callback, resultBuilder.build()); } catch (Throwable t) { + ++operationFailureCount; + statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { if (logger != null) { @@ -453,15 +486,21 @@ public class AppSearchManagerService extends SystemService { @NonNull List ids, @NonNull Map> typePropertyPaths, @UserIdInt int userId, + @ElapsedRealtimeLong long binderCallStartTimeMillis, @NonNull IAppSearchBatchResultCallback callback) { Objects.requireNonNull(packageName); Objects.requireNonNull(databaseName); Objects.requireNonNull(namespace); Objects.requireNonNull(ids); Objects.requireNonNull(callback); + long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); int callingUid = Binder.getCallingUid(); int callingUserId = handleIncomingUser(userId, callingUid); EXECUTOR.execute(() -> { + @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; + PlatformLogger logger = null; + int operationSuccessCount = 0; + int operationFailureCount = 0; try { verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); @@ -469,6 +508,7 @@ public class AppSearchManagerService extends SystemService { new AppSearchBatchResult.Builder<>(); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId); + logger = mLoggerInstanceManager.getPlatformLogger(callingUserId); for (int i = 0; i < ids.size(); i++) { String id = ids.get(i); try { @@ -479,14 +519,42 @@ public class AppSearchManagerService extends SystemService { namespace, id, typePropertyPaths); + ++operationSuccessCount; resultBuilder.setSuccess(id, document.getBundle()); } catch (Throwable t) { - resultBuilder.setResult(id, throwableToFailedResult(t)); + // Since we can only include one status code in the atom, + // for failures, we would just save the one for the last failure + AppSearchResult result = throwableToFailedResult(t); + resultBuilder.setResult(id, result); + statusCode = result.getResultCode(); + ++operationFailureCount; } } invokeCallbackOnResult(callback, resultBuilder.build()); } catch (Throwable t) { + ++operationFailureCount; + statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); + } finally { + if (logger != null) { + int estimatedBinderLatencyMillis = + 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); + int totalLatencyMillis = + (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); + CallStats.Builder cBuilder = new CallStats.Builder(packageName, + databaseName) + .setCallType(CallStats.CALL_TYPE_GET_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(estimatedBinderLatencyMillis) + .setNumOperationsSucceeded(operationSuccessCount) + .setNumOperationsFailed(operationFailureCount); + cBuilder.getGeneralStatsBuilder() + .setStatusCode(statusCode) + .setTotalLatencyMillis(totalLatencyMillis); + logger.logStats(cBuilder.build()); + } } }); } @@ -739,14 +807,20 @@ public class AppSearchManagerService extends SystemService { @NonNull String namespace, @NonNull List ids, @UserIdInt int userId, + @ElapsedRealtimeLong long binderCallStartTimeMillis, @NonNull IAppSearchBatchResultCallback callback) { Objects.requireNonNull(packageName); Objects.requireNonNull(databaseName); Objects.requireNonNull(ids); Objects.requireNonNull(callback); + long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); int callingUid = Binder.getCallingUid(); int callingUserId = handleIncomingUser(userId, callingUid); EXECUTOR.execute(() -> { + @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; + PlatformLogger logger = null; + int operationSuccessCount = 0; + int operationFailureCount = 0; try { verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); @@ -754,20 +828,49 @@ public class AppSearchManagerService extends SystemService { new AppSearchBatchResult.Builder<>(); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId); + logger = mLoggerInstanceManager.getPlatformLogger(callingUserId); for (int i = 0; i < ids.size(); i++) { String id = ids.get(i); try { impl.remove(packageName, databaseName, namespace, id); + ++operationSuccessCount; resultBuilder.setSuccess(id, /*result= */ null); } catch (Throwable t) { - resultBuilder.setResult(id, throwableToFailedResult(t)); + AppSearchResult result = throwableToFailedResult(t); + resultBuilder.setResult(id, result); + // Since we can only include one status code in the atom, + // for failures, we would just save the one for the last failure + statusCode = result.getResultCode(); + ++operationFailureCount; } } // Now that the batch has been written. Persist the newly written data. impl.persistToDisk(PersistType.Code.LITE); invokeCallbackOnResult(callback, resultBuilder.build()); } catch (Throwable t) { + ++operationFailureCount; + statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); + } finally { + if (logger != null) { + int estimatedBinderLatencyMillis = + 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); + int totalLatencyMillis = + (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); + CallStats.Builder cBuilder = new CallStats.Builder(packageName, + databaseName) + .setCallType(CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_ID) + // TODO(b/173532925) check the existing binder call latency chart + // is good enough for us: + // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 + .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) + .setNumOperationsSucceeded(operationSuccessCount) + .setNumOperationsFailed(operationFailureCount); + cBuilder.getGeneralStatsBuilder() + .setStatusCode(statusCode) + .setTotalLatencyMillis(totalLatencyMillis); + logger.logStats(cBuilder.build()); + } } }); } @@ -779,20 +882,28 @@ public class AppSearchManagerService extends SystemService { @NonNull String queryExpression, @NonNull Bundle searchSpecBundle, @UserIdInt int userId, + @ElapsedRealtimeLong long binderCallStartTimeMillis, @NonNull IAppSearchResultCallback callback) { + // TODO(b/173532925) log CallStats once we have CALL_TYPE_REMOVE_BY_QUERY added Objects.requireNonNull(packageName); Objects.requireNonNull(databaseName); Objects.requireNonNull(queryExpression); Objects.requireNonNull(searchSpecBundle); Objects.requireNonNull(callback); + long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); int callingUid = Binder.getCallingUid(); int callingUserId = handleIncomingUser(userId, callingUid); EXECUTOR.execute(() -> { + @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; + PlatformLogger logger = null; + int operationSuccessCount = 0; + int operationFailureCount = 0; try { verifyUserUnlocked(callingUserId); verifyCallingPackage(callingUid, packageName); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId); + logger = mLoggerInstanceManager.getPlatformLogger(callingUserId); impl.removeByQuery( packageName, databaseName, @@ -800,9 +911,32 @@ public class AppSearchManagerService extends SystemService { new SearchSpec(searchSpecBundle)); // Now that the batch has been written. Persist the newly written data. impl.persistToDisk(PersistType.Code.LITE); + ++operationSuccessCount; invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { + ++operationFailureCount; + statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); + } finally { + if (logger != null) { + int estimatedBinderLatencyMillis = + 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); + int totalLatencyMillis = + (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); + CallStats.Builder cBuilder = new CallStats.Builder(packageName, + databaseName) + .setCallType(CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH) + // TODO(b/173532925) check the existing binder call latency chart + // is good enough for us: + // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 + .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) + .setNumOperationsSucceeded(operationSuccessCount) + .setNumOperationsFailed(operationFailureCount); + cBuilder.getGeneralStatsBuilder() + .setStatusCode(statusCode) + .setTotalLatencyMillis(totalLatencyMillis); + logger.logStats(cBuilder.build()); + } } }); } @@ -836,17 +970,47 @@ public class AppSearchManagerService extends SystemService { } @Override - public void persistToDisk(@UserIdInt int userId) { + public void persistToDisk(@UserIdInt int userId, + @ElapsedRealtimeLong long binderCallStartTimeMillis) { + long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); int callingUid = Binder.getCallingUid(); int callingUserId = handleIncomingUser(userId, callingUid); EXECUTOR.execute(() -> { + @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; + PlatformLogger logger = null; + int operationSuccessCount = 0; + int operationFailureCount = 0; try { verifyUserUnlocked(callingUserId); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId); + logger = mLoggerInstanceManager.getPlatformLogger(callingUserId); impl.persistToDisk(PersistType.Code.FULL); + ++operationSuccessCount; } catch (Throwable t) { + ++operationFailureCount; + statusCode = throwableToFailedResult(t).getResultCode(); Log.e(TAG, "Unable to persist the data to disk", t); + } finally { + if (logger != null) { + int estimatedBinderLatencyMillis = + 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); + int totalLatencyMillis = + (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); + CallStats.Builder cBuilder = new CallStats.Builder(/*packageName=*/ "", + /*databaseName=*/ "") + .setCallType(CallStats.CALL_TYPE_FLUSH) + // TODO(b/173532925) check the existing binder call latency chart + // is good enough for us: + // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 + .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) + .setNumOperationsSucceeded(operationSuccessCount) + .setNumOperationsFailed(operationFailureCount); + cBuilder.getGeneralStatsBuilder() + .setStatusCode(statusCode) + .setTotalLatencyMillis(totalLatencyMillis); + logger.logStats(cBuilder.build()); + } } }); } diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 4a09cf8b6c268..15b8e359baa29 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -661,7 +661,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { public void setSchema(String packageName, String databaseName, List schemaBundles, List schemasNotPlatformSurfaceable, Map> schemasPackageAccessibleBundles, boolean forceOverride, - int userId, int version, IAppSearchResultCallback callback) throws RemoteException { + int userId, int version, long binderCallStartTimeMillis, + IAppSearchResultCallback callback) throws RemoteException { for (Map.Entry> entry : schemasPackageAccessibleBundles.entrySet()) { final String key = entry.getKey(); @@ -721,6 +722,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { @Override public void getDocuments(String packageName, String databaseName, String namespace, List ids, Map> typePropertyPaths, int userId, + long binderCallStartTimeMillis, IAppSearchBatchResultCallback callback) throws RemoteException { final AppSearchBatchResult.Builder builder = new AppSearchBatchResult.Builder<>(); @@ -820,7 +822,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { @Override public void removeByDocumentId(String packageName, String databaseName, String namespace, - List ids, int userId, IAppSearchBatchResultCallback callback) + List ids, int userId, long binderCallStartTimeMillis, + IAppSearchBatchResultCallback callback) throws RemoteException { final AppSearchBatchResult.Builder builder = new AppSearchBatchResult.Builder<>(); @@ -847,7 +850,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { @Override public void removeByQuery(String packageName, String databaseName, String queryExpression, - Bundle searchSpecBundle, int userId, IAppSearchResultCallback callback) + Bundle searchSpecBundle, int userId, long binderCallStartTimeMillis, + IAppSearchResultCallback callback) throws RemoteException { final String key = getKey(userId, databaseName); if (!mDocumentMap.containsKey(key)) { @@ -867,7 +871,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } @Override - public void persistToDisk(int userId) throws RemoteException { + public void persistToDisk(int userId, long binderCallStartTimeMillis) + throws RemoteException { }