Adding initial implementation of Prediction client/service API
Test: Build sample app, ensure that app prediction service gets client
requests
Bug: 111701043
Change-Id: I33aceb2de31552b2d740dc333559d68728753e40
Signed-off-by: Winson Chung <winsonc@google.com>
diff --git a/services/Android.bp b/services/Android.bp
index 01734f4..8e96ccc 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -17,6 +17,7 @@
static_libs: [
"services.core",
"services.accessibility",
+ "services.appprediction",
"services.appwidget",
"services.autofill",
"services.backup",
diff --git a/services/appprediction/Android.bp b/services/appprediction/Android.bp
new file mode 100644
index 0000000..a7be587
--- /dev/null
+++ b/services/appprediction/Android.bp
@@ -0,0 +1,5 @@
+java_library_static {
+ name: "services.appprediction",
+ srcs: ["java/**/*.java"],
+ libs: ["services.core"],
+}
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
new file mode 100644
index 0000000..833eaa0
--- /dev/null
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2018 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.appprediction;
+
+import static android.Manifest.permission.MANAGE_APP_PREDICTIONS;
+import static android.content.Context.APP_PREDICTION_SERVICE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionSessionId;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.IPredictionCallback;
+import android.app.prediction.IPredictionManager;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.UserHandle;
+
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+
+import java.io.FileDescriptor;
+import java.util.function.Consumer;
+
+/**
+ * A service used to predict app and shortcut usage.
+ *
+ * <p>The data collected by this service can be analyzed and combined with other sources to provide
+ * predictions in different areas of the system such as Launcher and Share sheet.
+ */
+public class AppPredictionManagerService extends
+ AbstractMasterSystemService<AppPredictionManagerService, AppPredictionPerUserService> {
+
+ private static final String TAG = AppPredictionManagerService.class.getSimpleName();
+
+ private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+
+ public AppPredictionManagerService(Context context) {
+ super(context, new FrameworkResourcesServiceNameResolver(context,
+ com.android.internal.R.string.config_defaultAppPredictionService), null);
+ }
+
+ @Override
+ protected AppPredictionPerUserService newServiceLocked(int resolvedUserId, boolean disabled) {
+ return new AppPredictionPerUserService(this, mLock, resolvedUserId);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(APP_PREDICTION_SERVICE, new PredictionManagerServiceStub());
+ }
+
+ @Override
+ protected void enforceCallingPermissionForManagement() {
+ getContext().enforceCallingPermission(MANAGE_APP_PREDICTIONS, TAG);
+ }
+
+ @Override
+ protected int getMaximumTemporaryServiceDurationMs() {
+ return MAX_TEMP_SERVICE_DURATION_MS;
+ }
+
+ private class PredictionManagerServiceStub extends IPredictionManager.Stub {
+
+ @Override
+ public void createPredictionSession(@NonNull AppPredictionContext context,
+ @NonNull AppPredictionSessionId sessionId) {
+ runForUserLocked((service) ->
+ service.onCreatePredictionSessionLocked(context, sessionId));
+ }
+
+ @Override
+ public void notifyAppTargetEvent(@NonNull AppPredictionSessionId sessionId,
+ @NonNull AppTargetEvent event) {
+ runForUserLocked((service) -> service.notifyAppTargetEventLocked(sessionId, event));
+ }
+
+ @Override
+ public void notifyLocationShown(@NonNull AppPredictionSessionId sessionId,
+ @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
+ runForUserLocked((service) ->
+ service.notifyLocationShownLocked(sessionId, launchLocation, targetIds));
+ }
+
+ @Override
+ public void sortAppTargets(@NonNull AppPredictionSessionId sessionId,
+ @NonNull ParceledListSlice targets,
+ IPredictionCallback callback) {
+ runForUserLocked((service) ->
+ service.sortAppTargetsLocked(sessionId, targets, callback));
+ }
+
+ @Override
+ public void registerPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
+ @NonNull IPredictionCallback callback) {
+ runForUserLocked((service) ->
+ service.registerPredictionUpdatesLocked(sessionId, callback));
+ }
+
+ public void unregisterPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
+ @NonNull IPredictionCallback callback) {
+ runForUserLocked((service) ->
+ service.unregisterPredictionUpdatesLocked(sessionId, callback));
+ }
+
+ @Override
+ public void requestPredictionUpdate(@NonNull AppPredictionSessionId sessionId) {
+ runForUserLocked((service) -> service.requestPredictionUpdateLocked(sessionId));
+ }
+
+ @Override
+ public void onDestroyPredictionSession(@NonNull AppPredictionSessionId sessionId) {
+ runForUserLocked((service) -> service.onDestroyPredictionSessionLocked(sessionId));
+ }
+
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err,
+ @NonNull String[] args, @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) throws RemoteException {
+ new AppPredictionManagerServiceShellCommand(AppPredictionManagerService.this)
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ private void runForUserLocked(@NonNull Consumer<AppPredictionPerUserService> c) {
+ final int userId = UserHandle.getCallingUserId();
+ // TODO(b/111701043): Determine what permission model we want for this
+ long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ final AppPredictionPerUserService service = getServiceForUserLocked(userId);
+ c.accept(service);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+}
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerServiceShellCommand.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerServiceShellCommand.java
new file mode 100644
index 0000000..ed7cc67
--- /dev/null
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerServiceShellCommand.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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.appprediction;
+
+import android.annotation.NonNull;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/**
+ * The shell command implementation for the AppPredictionManagerService.
+ */
+public class AppPredictionManagerServiceShellCommand extends ShellCommand {
+
+ private static final String TAG =
+ AppPredictionManagerServiceShellCommand.class.getSimpleName();
+
+ private final AppPredictionManagerService mService;
+
+ public AppPredictionManagerServiceShellCommand(@NonNull AppPredictionManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ switch (cmd) {
+ case "set": {
+ final String what = getNextArgRequired();
+ switch (what) {
+ case "temporary-service": {
+ final int userId = Integer.parseInt(getNextArgRequired());
+ String serviceName = getNextArg();
+ if (serviceName == null) {
+ mService.resetTemporaryService(userId);
+ return 0;
+ }
+ final int duration = Integer.parseInt(getNextArgRequired());
+ mService.setTemporaryService(userId, serviceName, duration);
+ pw.println("AppPredictionService temporarily set to " + serviceName
+ + " for " + duration + "ms");
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ try (PrintWriter pw = getOutPrintWriter()) {
+ pw.println("AppPredictionManagerService commands:");
+ pw.println(" help");
+ pw.println(" Prints this help text.");
+ pw.println("");
+ pw.println(" set temporary-service USER_ID [COMPONENT_NAME DURATION]");
+ pw.println(" Temporarily (for DURATION ms) changes the service implemtation.");
+ pw.println(" To reset, call with just the USER_ID argument.");
+ pw.println("");
+ }
+ }
+}
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
new file mode 100644
index 0000000..24d592c
--- /dev/null
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2018 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.appprediction;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionSessionId;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.IPredictionCallback;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.service.appprediction.AppPredictionService;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+/**
+ * Per-user instance of {@link AppPredictionManagerService}.
+ */
+public class AppPredictionPerUserService extends
+ AbstractPerUserSystemService<AppPredictionPerUserService, AppPredictionManagerService>
+ implements RemoteAppPredictionService.RemoteAppPredictionServiceCallbacks {
+
+ private static final String TAG = AppPredictionPerUserService.class.getSimpleName();
+
+ @Nullable
+ @GuardedBy("mLock")
+ private RemoteAppPredictionService mRemoteService;
+
+ protected AppPredictionPerUserService(AppPredictionManagerService master,
+ Object lock, int userId) {
+ super(master, lock, userId);
+ }
+
+ @Override // from PerUserSystemService
+ protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+ throws NameNotFoundException {
+
+ ServiceInfo si;
+ try {
+ si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+ PackageManager.GET_META_DATA, mUserId);
+ } catch (RemoteException e) {
+ throw new NameNotFoundException("Could not get service for " + serviceComponent);
+ }
+ // TODO(b/111701043): must check that either the service is from a system component,
+ // or it matches a service set by shell cmd (so it can be used on CTS tests and when
+ // OEMs are implementing the real service and also verify the proper permissions
+ return si;
+ }
+
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ protected boolean updateLocked(boolean disabled) {
+ final boolean enabledChanged = super.updateLocked(disabled);
+ if (enabledChanged) {
+ if (!isEnabledLocked()) {
+ // Clear the remote service for the next call
+ mRemoteService = null;
+ }
+ }
+ return enabledChanged;
+ }
+
+ /**
+ * Notifies the service of a new prediction session.
+ */
+ @GuardedBy("mLock")
+ public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context,
+ @NonNull AppPredictionSessionId sessionId) {
+ final RemoteAppPredictionService service = getRemoteServiceLocked();
+ if (service != null) {
+ service.onCreatePredictionSession(context, sessionId);
+ }
+ }
+
+ /**
+ * Records an app target event to the service.
+ */
+ @GuardedBy("mLock")
+ public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId,
+ @NonNull AppTargetEvent event) {
+ final RemoteAppPredictionService service = getRemoteServiceLocked();
+ if (service != null) {
+ service.notifyAppTargetEvent(sessionId, event);
+ }
+ }
+
+ /**
+ * Records when a launch location is shown.
+ */
+ @GuardedBy("mLock")
+ public void notifyLocationShownLocked(@NonNull AppPredictionSessionId sessionId,
+ @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
+ final RemoteAppPredictionService service = getRemoteServiceLocked();
+ if (service != null) {
+ service.notifyLocationShown(sessionId, launchLocation, targetIds);
+ }
+ }
+
+ /**
+ * Requests the service to sort a list of apps or shortcuts.
+ */
+ @GuardedBy("mLock")
+ public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId,
+ @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
+ final RemoteAppPredictionService service = getRemoteServiceLocked();
+ if (service != null) {
+ service.sortAppTargets(sessionId, targets, callback);
+ }
+ }
+
+ /**
+ * Registers a callback for continuous updates of predicted apps or shortcuts.
+ */
+ @GuardedBy("mLock")
+ public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
+ @NonNull IPredictionCallback callback) {
+ final RemoteAppPredictionService service = getRemoteServiceLocked();
+ if (service != null) {
+ service.registerPredictionUpdates(sessionId, callback);
+ }
+ }
+
+ /**
+ * Unregisters a callback for continuous updates of predicted apps or shortcuts.
+ */
+ @GuardedBy("mLock")
+ public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
+ @NonNull IPredictionCallback callback) {
+ final RemoteAppPredictionService service = getRemoteServiceLocked();
+ if (service != null) {
+ service.unregisterPredictionUpdates(sessionId, callback);
+ }
+ }
+
+ /**
+ * Requests a new set of predicted apps or shortcuts.
+ */
+ @GuardedBy("mLock")
+ public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) {
+ final RemoteAppPredictionService service = getRemoteServiceLocked();
+ if (service != null) {
+ service.requestPredictionUpdate(sessionId);
+ }
+ }
+
+ /**
+ * Notifies the service of the end of an existing prediction session.
+ */
+ @GuardedBy("mLock")
+ public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) {
+ final RemoteAppPredictionService service = getRemoteServiceLocked();
+ if (service != null) {
+ service.onDestroyPredictionSession(sessionId);
+ }
+ }
+
+ @Override
+ public void onFailureOrTimeout(boolean timedOut) {
+ if (isDebug()) {
+ Slog.d(TAG, "onFailureOrTimeout(): timed out=" + timedOut);
+ }
+
+ // Do nothing, we are just proxying to the prediction service
+ }
+
+ @Override
+ public void onServiceDied(RemoteAppPredictionService service) {
+ if (isDebug()) {
+ Slog.d(TAG, "onServiceDied():");
+ }
+
+ // Do nothing, we are just proxying to the prediction service
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteAppPredictionService getRemoteServiceLocked() {
+ if (mRemoteService == null) {
+ final String serviceName = getComponentNameLocked();
+ if (serviceName == null) {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "getRemoteServiceLocked(): not set");
+ }
+ return null;
+ }
+ ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+
+ mRemoteService = new RemoteAppPredictionService(getContext(),
+ AppPredictionService.SERVICE_INTERFACE, serviceComponent, mUserId, this,
+ mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+ }
+
+ return mRemoteService;
+ }
+}
diff --git a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
new file mode 100644
index 0000000..45ea86f
--- /dev/null
+++ b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 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.appprediction;
+
+import android.annotation.NonNull;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionSessionId;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.IPredictionCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.IBinder;
+import android.service.appprediction.IPredictionService;
+import android.text.format.DateUtils;
+
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+
+
+/**
+ * Proxy to the {@link android.service.appprediction.AppPredictionService} implemention in another
+ * process.
+ */
+public class RemoteAppPredictionService extends
+ AbstractMultiplePendingRequestsRemoteService<RemoteAppPredictionService,
+ IPredictionService> {
+
+ private static final String TAG = "RemoteAppPredictionService";
+
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
+
+ public RemoteAppPredictionService(Context context, String serviceInterface,
+ ComponentName componentName, int userId,
+ RemoteAppPredictionServiceCallbacks callback, boolean bindInstantServiceAllowed,
+ boolean verbose) {
+ super(context, serviceInterface, componentName, userId, callback, bindInstantServiceAllowed,
+ verbose, /* initialCapacity= */ 1);
+ }
+
+ @Override
+ protected IPredictionService getServiceInterface(IBinder service) {
+ return IPredictionService.Stub.asInterface(service);
+ }
+
+ @Override
+ protected long getTimeoutIdleBindMillis() {
+ return PERMANENT_BOUND_TIMEOUT_MS;
+ }
+
+ @Override
+ protected long getRemoteRequestMillis() {
+ return TIMEOUT_REMOTE_REQUEST_MILLIS;
+ }
+
+ /**
+ * Notifies the service of a new prediction session.
+ */
+ public void onCreatePredictionSession(@NonNull AppPredictionContext context,
+ @NonNull AppPredictionSessionId sessionId) {
+ scheduleAsyncRequest((s) -> s.onCreatePredictionSession(context, sessionId));
+ }
+
+ /**
+ * Records an app target event to the service.
+ */
+ public void notifyAppTargetEvent(@NonNull AppPredictionSessionId sessionId,
+ @NonNull AppTargetEvent event) {
+ scheduleAsyncRequest((s) -> s.notifyAppTargetEvent(sessionId, event));
+ }
+
+ /**
+ * Records when a launch location is shown.
+ */
+ public void notifyLocationShown(@NonNull AppPredictionSessionId sessionId,
+ @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
+ scheduleAsyncRequest((s) -> s.notifyLocationShown(sessionId, launchLocation, targetIds));
+ }
+
+ /**
+ * Requests the service to sort a list of apps or shortcuts.
+ */
+ public void sortAppTargets(@NonNull AppPredictionSessionId sessionId,
+ @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
+ scheduleAsyncRequest((s) -> s.sortAppTargets(sessionId, targets, callback));
+ }
+
+
+ /**
+ * Registers a callback for continuous updates of predicted apps or shortcuts.
+ */
+ public void registerPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
+ @NonNull IPredictionCallback callback) {
+ scheduleAsyncRequest((s) -> s.registerPredictionUpdates(sessionId, callback));
+ }
+
+ /**
+ * Unregisters a callback for continuous updates of predicted apps or shortcuts.
+ */
+ public void unregisterPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
+ @NonNull IPredictionCallback callback) {
+ scheduleAsyncRequest((s) -> s.unregisterPredictionUpdates(sessionId, callback));
+ }
+
+ /**
+ * Requests a new set of predicted apps or shortcuts.
+ */
+ public void requestPredictionUpdate(@NonNull AppPredictionSessionId sessionId) {
+ scheduleAsyncRequest((s) -> s.requestPredictionUpdate(sessionId));
+ }
+
+ /**
+ * Notifies the service of the end of an existing prediction session.
+ */
+ public void onDestroyPredictionSession(@NonNull AppPredictionSessionId sessionId) {
+ scheduleAsyncRequest((s) -> s.onDestroyPredictionSession(sessionId));
+ }
+
+ /**
+ * Failure callback
+ */
+ public interface RemoteAppPredictionServiceCallbacks
+ extends VultureCallback<RemoteAppPredictionService> {
+
+ /**
+ * Notifies a the failure or timeout of a remote call.
+ */
+ void onFailureOrTimeout(boolean timedOut);
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4326c39..ced4261 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -260,6 +260,8 @@
"com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
private static final String ADB_SERVICE_CLASS =
"com.android.server.adb.AdbService$Lifecycle";
+ private static final String APP_PREDICTION_MANAGER_SERVICE_CLASS =
+ "com.android.server.appprediction.AppPredictionManagerService";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
@@ -1153,6 +1155,11 @@
startContentCaptureService(context);
+ // App prediction manager service
+ traceBeginAndSlog("StartAppPredictionService");
+ mSystemServiceManager.startService(APP_PREDICTION_MANAGER_SERVICE_CLASS);
+ traceEnd();
+
// NOTE: ClipboardService indirectly depends on IntelligenceService
traceBeginAndSlog("StartClipboardService");
mSystemServiceManager.startService(ClipboardService.class);