Merge "Changed how the Smart Suggestions service is defined."
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d367afc..74ec0b9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12838,6 +12838,23 @@
"max_sound_trigger_detection_service_ops_per_day";
/**
+ * Property used by {@code com.android.server.SystemServer} on start to decide whether
+ * the Smart Suggestions service should be created or not
+ *
+ * <p>By default it should *NOT* be set (in which case the decision is based on whether
+ * the OEM provides an implementation for the service), but it can be overridden to:
+ *
+ * <ul>
+ * <li>Provide a "kill switch" so OEMs can disable it remotely in case of emergency.
+ * <li>Enable the CTS tests to be run on AOSP builds
+ * </ul>
+ *
+ * @hide
+ */
+ public static final String SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED =
+ "smart_suggestions_service_explicitly_enabled";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index f22d57f..4c1fc5c 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -761,6 +761,13 @@
}
optional SmartSelection smart_selection = 108;
+ message SmartSuggestions {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto service_explicitly_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional SmartSuggestions smart_suggestions = 145;
+
message Sms {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -993,5 +1000,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 145;
+ // Next tag = 146;
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0cd6bc5..c62071b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3350,6 +3350,14 @@
-->
<string name="config_defaultTextClassifierPackage" translatable="false"></string>
+ <!-- The package name for the system's smart suggestion service.
+ This service must be trusted, as it can be activated without explicit consent of the user.
+ If no service with the specified name exists on the device, content capture and
+ smart suggestions will be disabled.
+ Example: "com.android.intelligence/.SmartSuggestionsService"
+ -->
+ <string name="config_defaultSmartSuggestionsService" translatable="false"></string>
+
<!-- Whether the device uses the default focus highlight when focus state isn't specified. -->
<bool name="config_useDefaultFocusHighlight">true</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 01422c8..6854a84e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3265,6 +3265,7 @@
<java-symbol type="string" name="notification_channel_do_not_disturb" />
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="config_defaultTextClassifierPackage" />
+ <java-symbol type="string" name="config_defaultSmartSuggestionsService" />
<java-symbol type="string" name="notification_channel_foreground_service" />
<java-symbol type="string" name="foreground_service_app_in_background" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 8a27de4b..ed9c3d5 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -415,6 +415,7 @@
Settings.Global.SHOW_TEMPERATURE_WARNING,
Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
+ Settings.Global.SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED,
Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED,
Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS,
Settings.Global.SMS_OUTGOING_CHECK_MAX_COUNT,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index d1824d7..df5b146 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1165,6 +1165,12 @@
GlobalSettingsProto.SmartSelection.UPDATE_METADATA_URL);
p.end(smartSelectToken);
+ final long smartSuggestionsToken = p.start(GlobalSettingsProto.SMART_SUGGESTIONS);
+ dumpSetting(s, p,
+ Settings.Global.SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED,
+ GlobalSettingsProto.SmartSuggestions.SERVICE_EXPLICITLY_ENABLED);
+ p.end(smartSuggestionsToken);
+
final long smsToken = p.start(GlobalSettingsProto.SMS);
dumpSetting(s, p,
Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS,
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 0df99d4..18bc856 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -190,6 +190,11 @@
return mInfo.getServiceInfo();
}
+ @Override // from PerUserSystemService
+ protected String getDefaultComponentName() {
+ return getComponentNameFromSettings();
+ }
+
@Nullable
String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) {
return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId);
@@ -369,7 +374,7 @@
final long identity = Binder.clearCallingIdentity();
try {
- final String autoFillService = getComponentNameFromSettings();
+ final String autoFillService = getComponentNameLocked();
final ComponentName componentName = serviceInfo.getComponentName();
if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) {
mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF,
diff --git a/services/core/java/com/android/server/AbstractMasterSystemService.java b/services/core/java/com/android/server/AbstractMasterSystemService.java
index 1759ce1..76010b3 100644
--- a/services/core/java/com/android/server/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/AbstractMasterSystemService.java
@@ -39,6 +39,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
import java.io.PrintWriter;
import java.util.List;
@@ -211,6 +212,66 @@
}
/**
+ * Temporary sets the service implementation.
+ *
+ * <p>Typically used by Shell command and/or CTS tests.
+ *
+ * @param componentName name of the new component
+ * @param durationMs how long the change will be valid (the service will be automatically reset
+ * to the default component after this timeout expires).
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ * @throws IllegalArgumentException if value of {@code durationMs} is higher than
+ * {@link #getMaximumTemporaryServiceDurationMs()}.
+ */
+ public final void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
+ int durationMs) {
+ Slog.i(mTag, "setTemporaryService(" + userId + ") to " + componentName + " for "
+ + durationMs + "ms");
+ enforceCallingPermissionForManagement();
+
+ Preconditions.checkNotNull(componentName);
+ final int maxDurationMs = getMaximumTemporaryServiceDurationMs();
+ if (durationMs > maxDurationMs) {
+ throw new IllegalArgumentException(
+ "Max duration is " + maxDurationMs + " (called with " + durationMs + ")");
+ }
+
+ synchronized (mLock) {
+ final S service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.setTemporaryServiceLocked(componentName, durationMs);
+ }
+ }
+ }
+
+ /**
+ * Gets the maximum time the service implementation can be changed.
+ *
+ * @throws UnsupportedOperationException if subclass doesn't override it.
+ */
+ protected int getMaximumTemporaryServiceDurationMs() {
+ throw new UnsupportedOperationException("Not implemented by " + getClass());
+ }
+
+ /**
+ * Resets the temporary service implementation to the default component.
+ *
+ * <p>Typically used by Shell command and/or CTS tests.
+ *
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ */
+ public final void resetTemporaryService(@UserIdInt int userId) {
+ Slog.i(mTag, "resetTemporaryService(): " + userId);
+ enforceCallingPermissionForManagement();
+ synchronized (mLock) {
+ final S service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.resetTemporaryServiceLocked();
+ }
+ }
+ }
+
+ /**
* Asserts that the caller has permissions to manage this service.
*
* <p>Typically called by {@code ShellCommand} implementations.
diff --git a/services/core/java/com/android/server/AbstractPerUserSystemService.java b/services/core/java/com/android/server/AbstractPerUserSystemService.java
index 001d85f..a26102d 100644
--- a/services/core/java/com/android/server/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/AbstractPerUserSystemService.java
@@ -26,12 +26,17 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
+import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
@@ -49,6 +54,9 @@
public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSystemService<S, M>,
M extends AbstractMasterSystemService<M, S>> {
+ /** Handler message to {@link #resetTemporaryServiceLocked()} */
+ private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
+
protected final @UserIdInt int mUserId;
protected final Object mLock;
protected final String mTag = getClass().getSimpleName();
@@ -70,6 +78,26 @@
@GuardedBy("mLock")
private ServiceInfo mServiceInfo;
+ /**
+ * Temporary service name set by {@link #setTemporaryServiceLocked(String, int)}.
+ *
+ * <p>Typically used by Shell command and/or CTS tests.
+ */
+ @GuardedBy("mLock")
+ private String mTemporaryServiceName;
+
+ /**
+ * When the temporary service will expire (and reset back to the default).
+ */
+ @GuardedBy("mLock")
+ private long mTemporaryServiceExpiration;
+
+ /**
+ * Handler used to reset the temporary service name.
+ */
+ @GuardedBy("mLock")
+ private Handler mTemporaryHandler;
+
protected AbstractPerUserSystemService(@NonNull M master, @NonNull Object lock,
@UserIdInt int userId) {
mMaster = master;
@@ -130,7 +158,7 @@
mDisabled = disabled;
ComponentName serviceComponent = null;
ServiceInfo serviceInfo = null;
- final String componentName = getComponentNameFromSettings();
+ final String componentName = getComponentNameLocked();
if (!TextUtils.isEmpty(componentName)) {
try {
serviceComponent = ComponentName.unflattenFromString(componentName);
@@ -191,6 +219,29 @@
}
/**
+ * Gets the current name of the service, which is either the
+ * {@link #getDefaultComponentName() default service} or the
+ * {@link #setTemporaryServiceLocked(String, int) temporary one}.
+ */
+ protected final String getComponentNameLocked() {
+ if (mTemporaryServiceName != null) {
+ // Always log it, as it should only be used on CTS or during development
+ Slog.w(mTag, "getComponentName(): using temporary name " + mTemporaryServiceName);
+ return mTemporaryServiceName;
+ }
+ return getDefaultComponentName();
+ }
+
+ /**
+ * Gets the name of the default component for the service.
+ *
+ * <p>Typically implemented by returning {@link #getComponentNameFromSettings()} or by using
+ * a string from the system resources.
+ */
+ @Nullable
+ protected abstract String getDefaultComponentName();
+
+ /**
* Gets this name of the remote service this service binds to as defined by {@link Settings}.
*/
@Nullable
@@ -201,6 +252,66 @@
}
/**
+ * Checks whether the current service for the user was temporarily set.
+ */
+ public final boolean isTemporaryServiceSetLocked() {
+ return mTemporaryServiceName != null;
+ }
+
+ /**
+ * Temporary sets the service implementation.
+ *
+ * @param componentName name of the new component
+ * @param durationMs how long the change will be valid (the service will be automatically reset
+ * to the default component after this timeout expires).
+ */
+ protected final void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
+ mTemporaryServiceName = componentName;
+
+ if (mTemporaryHandler == null) {
+ mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
+ synchronized (mLock) {
+ resetTemporaryServiceLocked();
+ }
+ } else {
+ Slog.wtf(mTag, "invalid handler msg: " + msg);
+ }
+ }
+ };
+ } else {
+ removeResetTemporaryServiceMessageLocked();
+ }
+ mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
+ mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
+
+ updateLocked(mDisabled);
+ }
+
+ private void removeResetTemporaryServiceMessageLocked() {
+ if (mMaster.verbose) {
+ Slog.v(mTag, "setTemporaryServiceLocked(): removing old message");
+ }
+ // NOTE: caller should already have checked it
+ mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
+ }
+
+ /**
+ * Resets the temporary service implementation to the default component.
+ */
+ protected final void resetTemporaryServiceLocked() {
+ Slog.i(mTag, "resetting temporary service from " + mTemporaryServiceName);
+ mTemporaryServiceName = null;
+ if (mTemporaryHandler != null) {
+ removeResetTemporaryServiceMessageLocked();
+ mTemporaryHandler = null;
+ }
+ updateLocked(mDisabled);
+ }
+
+ /**
* Gets the {@link ComponentName} of the remote service this service binds to, or {@code null}
* if the service is disabled.
*/
@@ -290,12 +401,14 @@
pw.print(prefix); pw.print("Service UID: ");
pw.println(mServiceInfo.applicationInfo.uid);
}
- final String componentName = getComponentNameFromSettings();
- if (componentName != null) {
- pw.print(prefix); pw.print("Service name: ");
- pw.println(componentName);
+ if (mTemporaryServiceName != null) {
+ pw.print(prefix); pw.print("Temporary service name: "); pw.print(mTemporaryServiceName);
+ final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
+ pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.println(")");
+ pw.print(prefix); pw.print(prefix);
+ pw.print("Default service name: "); pw.println(getDefaultComponentName());
} else {
- pw.println("No service package set");
+ pw.print(prefix); pw.print("Service name: "); pw.println(getDefaultComponentName());
}
}
}
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
index 4c68064..b8f2ad0 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -65,6 +65,8 @@
static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
+ private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+
@GuardedBy("mLock")
private ActivityManagerInternal mAm;
@@ -75,12 +77,6 @@
}
@Override // from AbstractMasterSystemService
- protected String getServiceSettingsProperty() {
- // TODO(b/111276913): STOPSHIP temporary settings, until it's set by resourcs + cmd
- return "smart_suggestions_service";
- }
-
- @Override // from AbstractMasterSystemService
protected IntelligencePerUserService newServiceLocked(@UserIdInt int resolvedUserId,
boolean disabled) {
return new IntelligencePerUserService(this, mLock, resolvedUserId);
@@ -104,6 +100,11 @@
getContext().enforceCallingPermission(MANAGE_SMART_SUGGESTIONS, TAG);
}
+ @Override // from AbstractMasterSystemService
+ protected int getMaximumTemporaryServiceDurationMs() {
+ return MAX_TEMP_SERVICE_DURATION_MS;
+ }
+
// Called by Shell command.
void destroySessions(@UserIdInt int userId, @NonNull IResultReceiver receiver) {
Slog.i(TAG, "destroySessions() for userId " + userId);
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
index dbf8601..6f047c5 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
@@ -36,6 +36,7 @@
import android.os.RemoteException;
import android.service.intelligence.InteractionSessionId;
import android.service.intelligence.SnapshotData;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.autofill.AutofillId;
@@ -77,17 +78,24 @@
protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
throws NameNotFoundException {
+ int flags = PackageManager.GET_META_DATA;
+ final boolean isTemp = isTemporaryServiceSetLocked();
+ if (!isTemp) {
+ flags |= PackageManager.MATCH_SYSTEM_ONLY;
+ }
+
ServiceInfo si;
try {
- // TODO(b/111276913): 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
- si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
- PackageManager.GET_META_DATA, mUserId);
+ si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, flags, mUserId);
} catch (RemoteException e) {
Slog.w(TAG, "Could not get service for " + serviceComponent + ": " + e);
return null;
}
+ if (si == null) {
+ Slog.w(TAG, "Could not get serviceInfo for " + (isTemp ? " (temp)" : "(default system)")
+ + " " + serviceComponent.flattenToShortString());
+ return null;
+ }
if (!Manifest.permission.BIND_SMART_SUGGESTIONS_SERVICE.equals(si.permission)) {
Slog.w(TAG, "SmartSuggestionsService from '" + si.packageName
+ "' does not require permission "
@@ -105,6 +113,13 @@
return super.updateLocked(disabled);
}
+ @Override // from PerUserSystemService
+ protected String getDefaultComponentName() {
+ final String name = getContext()
+ .getString(com.android.internal.R.string.config_defaultSmartSuggestionsService);
+ return TextUtils.isEmpty(name) ? null : name;
+ }
+
// TODO(b/111276913): log metrics
@GuardedBy("mLock")
public void startSessionLocked(@NonNull IBinder activityToken,
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java
index b7c1f78..0d92a972 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java
@@ -75,6 +75,10 @@
pw.println(" set bind-instant-service-allowed [true | false]");
pw.println(" Sets whether binding to services provided by instant apps is allowed");
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("");
pw.println(" list sessions [--user USER_ID]");
pw.println(" Lists all pending sessions.");
pw.println("");
@@ -101,6 +105,8 @@
switch(what) {
case "bind-instant-service-allowed":
return setBindInstantService(pw);
+ case "temporary-service":
+ return setTemporaryService();
default:
pw.println("Invalid set: " + what);
return -1;
@@ -131,6 +137,18 @@
}
}
+ private int setTemporaryService() {
+ final int userId = getNextIntArgRequired();
+ final String serviceName = getNextArg();
+ if (serviceName == null) {
+ mService.resetTemporaryService(userId);
+ return 0;
+ }
+ final int duration = getNextIntArgRequired();
+ mService.setTemporaryService(userId, serviceName, duration);
+ return 0;
+ }
+
private int requestDestroy(PrintWriter pw) {
if (!isNextArgSessions(pw)) {
return -1;
@@ -204,4 +222,8 @@
}
return UserHandle.USER_ALL;
}
+
+ private int getNextIntArgRequired() {
+ return Integer.parseInt(getNextArgRequired());
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index dadaf2a..05ff660 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -22,6 +22,7 @@
import static android.os.IServiceManager.DUMP_FLAG_PROTO;
import static android.view.Display.DEFAULT_DISPLAY;
+import android.annotation.NonNull;
import android.app.ActivityThread;
import android.app.INotificationManager;
import android.app.usage.UsageStatsManagerInternal;
@@ -55,7 +56,9 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.IStorageManager;
+import android.provider.Settings;
import android.sysprop.VoldProperties;
+import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Slog;
@@ -798,10 +801,6 @@
boolean disableSystemTextClassifier = SystemProperties.getBoolean(
"config.disable_systemtextclassifier", false);
- //TODO(b/111276913): temporarily disabled until the manager is properly implemented to
- // ignore events when disabled and buffer when enabled
- boolean disableIntelligence = SystemProperties.getBoolean(
- "config.disable_intelligence", true);
boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime",
false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
@@ -1137,13 +1136,7 @@
traceEnd();
}
- if (!disableIntelligence) {
- traceBeginAndSlog("StartIntelligenceService");
- mSystemServiceManager.startService(INTELLIGENCE_MANAGER_SERVICE_CLASS);
- traceEnd();
- } else {
- Slog.d(TAG, "IntelligenceService disabled");
- }
+ startIntelligenceService(context);
// NOTE: ClipboardService indirectly depends on IntelligenceService
traceBeginAndSlog("StartClipboardService");
@@ -2107,6 +2100,37 @@
}, BOOT_TIMINGS_TRACE_LOG);
}
+ private void startIntelligenceService(@NonNull Context context) {
+
+ // First check if it was explicitly enabled by Settings
+ boolean explicitlySupported = false;
+ final String settings = Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.SMART_SUGGESTIONS_SERVICE_EXPLICITLY_ENABLED);
+ if (settings != null) {
+ explicitlySupported = Boolean.parseBoolean(settings);
+ if (explicitlySupported) {
+ Slog.d(TAG, "IntelligenceService explicitly enabled by Settings");
+ } else {
+ Slog.d(TAG, "IntelligenceService explicitly disabled by Settings");
+ return;
+ }
+ }
+
+ // Then check if OEM overlaid the resource that defines the service.
+ if (!explicitlySupported) {
+ final String serviceName = context
+ .getString(com.android.internal.R.string.config_defaultSmartSuggestionsService);
+ if (TextUtils.isEmpty(serviceName)) {
+ Slog.d(TAG, "IntelligenceService disabled because config resource is not overlaid");
+ return;
+ }
+ }
+
+ traceBeginAndSlog("StartIntelligenceService");
+ mSystemServiceManager.startService(INTELLIGENCE_MANAGER_SERVICE_CLASS);
+ traceEnd();
+ }
+
static final void startSystemUi(Context context, WindowManagerService windowManager) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",