diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2018-12-05 16:15:50 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2018-12-05 16:15:50 +0000 |
commit | 0492440cd9cb181c567442dd21e53b624caeba29 (patch) | |
tree | 87968f05218727d2c1ca6b12bfab1321f6949f41 | |
parent | e95bb70598faa3738c696621303ede566cf632ba (diff) | |
parent | 658c8e423fc4269d293eecbce8c1a99ed270ff76 (diff) |
Merge "Changed how the Smart Suggestions service is defined."
13 files changed, 311 insertions, 30 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d367afcaa309..74ec0b9c8085 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12838,6 +12838,23 @@ public final class Settings { "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 f22d57f817e8..4c1fc5c89b0d 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -761,6 +761,13 @@ message GlobalSettingsProto { } 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 @@ message GlobalSettingsProto { // 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 0cd6bc5cd700..c62071bdf552 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 01422c8ffd97..6854a84e950a 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 8a27de4bbdc3..ed9c3d5b2b70 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -415,6 +415,7 @@ public class SettingsBackupTest { 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 d1824d78d664..df5b1467f9d9 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 @@ class SettingsProtoDumpUtil { 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 0df99d4b6642..18bc856700f7 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 @@ final class AutofillManagerServiceImpl 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 class AutofillManagerServiceImpl 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 1759ce195485..76010b346a3b 100644 --- a/services/core/java/com/android/server/AbstractMasterSystemService.java +++ b/services/core/java/com/android/server/AbstractMasterSystemService.java @@ -39,6 +39,7 @@ import android.util.SparseBooleanArray; 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 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem } /** + * 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 001d85f1b798..a26102d57297 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.PackageItemInfo; 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 @@ import java.io.PrintWriter; 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 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst @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 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst 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 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst } /** + * 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 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst } /** + * 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 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst 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 4c68064b46f6..b8f2ad01d502 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 @@ public final class IntelligenceManagerService extends 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 @@ public final class IntelligenceManagerService extends } @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 @@ public final class IntelligenceManagerService extends 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 dbf8601849bb..6f047c51633a 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.IBinder; 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 @@ final class IntelligencePerUserService 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 @@ final class IntelligencePerUserService 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 b7c1f789e46c..0d92a972aa96 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 @@ public final class IntelligenceServiceShellCommand extends ShellCommand { 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 @@ public final class IntelligenceServiceShellCommand extends ShellCommand { 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 @@ public final class IntelligenceServiceShellCommand extends ShellCommand { } } + 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 @@ public final class IntelligenceServiceShellCommand extends ShellCommand { } 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 dadaf2ad67f4..05ff6601a477 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_PRIORITY_NORMAL; 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.SystemProperties; 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { 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 @@ public final class SystemServer { }, 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", |