diff --git a/Android.bp b/Android.bp index 44f7edd98e0a6..83f2006e75918 100644 --- a/Android.bp +++ b/Android.bp @@ -287,6 +287,8 @@ java_defaults { "core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl", "core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl", "core/java/android/service/gatekeeper/IGateKeeperService.aidl", + "core/java/android/service/intelligence/IIntelligenceService.aidl", + "core/java/android/service/notification/INotificationListener.aidl", "core/java/android/service/notification/IStatusBarNotificationHolder.aidl", "core/java/android/service/notification/IConditionListener.aidl", @@ -343,6 +345,7 @@ java_defaults { "core/java/android/view/autofill/IAutoFillManager.aidl", "core/java/android/view/autofill/IAutoFillManagerClient.aidl", "core/java/android/view/autofill/IAutofillWindowPresenter.aidl", + "core/java/android/view/intelligence/IIntelligenceManager.aidl", "core/java/android/view/IApplicationToken.aidl", "core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl", "core/java/android/view/IDockedStackListener.aidl", diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index a666819562335..8d54e91b93e45 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -120,6 +120,7 @@ import android.view.autofill.AutofillManager; import android.view.autofill.AutofillManager.AutofillClient; import android.view.autofill.AutofillPopupWindow; import android.view.autofill.IAutofillWindowPresenter; +import android.view.intelligence.IntelligenceManager; import android.widget.AdapterView; import android.widget.Toast; import android.widget.Toolbar; @@ -821,6 +822,10 @@ public class Activity extends ContextThemeWrapper /** The autofill manager. Always access via {@link #getAutofillManager()}. */ @Nullable private AutofillManager mAutofillManager; + /** The screen observation manager. Always access via {@link #getIntelligenceManager()}. */ + @Nullable private IntelligenceManager mIntelligenceManager; + + static final class NonConfigurationInstances { Object activity; HashMap children; @@ -994,7 +999,7 @@ public class Activity extends ContextThemeWrapper } /** - * (Create and) return the autofill manager + * (Creates, sets and) returns the autofill manager * * @return The autofill manager */ @@ -1006,6 +1011,18 @@ public class Activity extends ContextThemeWrapper return mAutofillManager; } + /** + * (Creates, sets, and ) returns the intelligence manager + * + * @return The intelligence manager + */ + @NonNull private IntelligenceManager getIntelligenceManager() { + if (mIntelligenceManager == null) { + mIntelligenceManager = getSystemService(IntelligenceManager.class); + } + return mIntelligenceManager; + } + @Override protected void attachBaseContext(Context newBase) { super.attachBaseContext(newBase); @@ -1081,6 +1098,12 @@ public class Activity extends ContextThemeWrapper } mRestoredFromBundle = savedInstanceState != null; mCalled = true; + + if (getIntelligenceManager() != null) { + //TODO(b/111276913): decide whether the screen_obs session id should be saved / restored + // in the activity bundle. + mIntelligenceManager.onActivityCreated(mToken, getComponentName()); + } } /** @@ -2047,6 +2070,10 @@ public class Activity extends ContextThemeWrapper } getApplication().dispatchActivityDestroyed(this); + + if (getIntelligenceManager() != null) { + mIntelligenceManager.onActivityDestroyed(); + } } /** @@ -6403,9 +6430,16 @@ public class Activity extends ContextThemeWrapper void dumpInner(@NonNull String prefix, @Nullable FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) { - if (args != null && args.length > 0 && args[0].equals("--autofill")) { - dumpAutofillManager(prefix, writer); - return; + if (args != null && args.length > 0) { + // Handle special cases + switch (args[0]) { + case "--autofill": + dumpAutofillManager(prefix, writer); + return; + case "--intelligence": + dumpIntelligenceManager(prefix, writer); + return; + } } writer.print(prefix); writer.print("Local Activity "); writer.print(Integer.toHexString(System.identityHashCode(this))); @@ -6435,6 +6469,7 @@ public class Activity extends ContextThemeWrapper mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix); dumpAutofillManager(prefix, writer); + dumpIntelligenceManager(prefix, writer); ResourcesManager.getInstance().dump(prefix, writer); } @@ -6450,6 +6485,15 @@ public class Activity extends ContextThemeWrapper } } + void dumpIntelligenceManager(String prefix, PrintWriter writer) { + final IntelligenceManager im = getIntelligenceManager(); + if (im != null) { + im.dump(prefix, writer); + } else { + writer.print(prefix); writer.println("No IntelligenceManager"); + } + } + /** * Bit indicating that this activity is "immersive" and should not be * interrupted by notifications if possible. diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index f26716987d4a7..e95f9abed9089 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -66,8 +66,8 @@ import android.hardware.fingerprint.IFingerprintService; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.IHdmiControlService; import android.hardware.input.InputManager; -import android.hardware.iris.IrisManager; import android.hardware.iris.IIrisService; +import android.hardware.iris.IrisManager; import android.hardware.location.ContextHubManager; import android.hardware.radio.RadioManager; import android.hardware.usb.IUsbManager; @@ -163,6 +163,8 @@ import android.view.accessibility.CaptioningManager; import android.view.autofill.AutofillManager; import android.view.autofill.IAutoFillManager; import android.view.inputmethod.InputMethodManager; +import android.view.intelligence.IIntelligenceManager; +import android.view.intelligence.IntelligenceManager; import android.view.textclassifier.TextClassificationManager; import android.view.textservice.TextServicesManager; @@ -1032,6 +1034,17 @@ final class SystemServiceRegistry { return new AutofillManager(ctx.getOuterContext(), service); }}); + registerService(Context.INTELLIGENCE_MANAGER_SERVICE, IntelligenceManager.class, + new CachedServiceFetcher() { + @Override + public IntelligenceManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + // Get the services without throwing as this is an optional feature + IBinder b = ServiceManager.getService(Context.INTELLIGENCE_MANAGER_SERVICE); + IIntelligenceManager service = IIntelligenceManager.Stub.asInterface(b); + return new IntelligenceManager(ctx.getOuterContext(), service); + }}); + registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher() { @Override public VrManager createService(ContextImpl ctx) throws ServiceNotFoundException { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 6a7829b9aa61b..ccf8417bee56b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3864,6 +3864,14 @@ public abstract class Context { */ public static final String AUTOFILL_MANAGER_SERVICE = "autofill"; + /** + * Official published name of the intelligence service. + * + * @hide + * @see #getSystemService(String) + */ + public static final String INTELLIGENCE_MANAGER_SERVICE = "intelligence"; + /** * Use with {@link #getSystemService(String)} to access the * {@link com.android.server.voiceinteraction.SoundTriggerService}. diff --git a/core/java/android/service/intelligence/IIntelligenceService.aidl b/core/java/android/service/intelligence/IIntelligenceService.aidl new file mode 100644 index 0000000000000..ee93326d2e3cf --- /dev/null +++ b/core/java/android/service/intelligence/IIntelligenceService.aidl @@ -0,0 +1,31 @@ +/* + * 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 android.service.intelligence; + +import android.service.intelligence.InteractionSessionId; +import android.service.intelligence.InteractionContext; + +/** + * Interface from the system to an intelligence service. + * + * @hide + */ +oneway interface IIntelligenceService { + + // Called when session is created (context not null) or destroyed (context null) + void onSessionLifecycle(in InteractionContext context, in InteractionSessionId sessionId); +} diff --git a/core/java/android/service/intelligence/IntelligenceService.java b/core/java/android/service/intelligence/IntelligenceService.java index 4b8825d645596..ce0a88a3faca6 100644 --- a/core/java/android/service/intelligence/IntelligenceService.java +++ b/core/java/android/service/intelligence/IntelligenceService.java @@ -15,16 +15,24 @@ */ package android.service.intelligence; +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; + +import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; import android.view.intelligence.ContentCaptureEvent; import java.util.List; /** - * A service used to captures the content of the screen. + * A service used to capture the content of the screen. * *

The data collected by this service can be analyzed and combined with other sources to provide * contextual data in other areas of the system such as Autofill. @@ -34,6 +42,8 @@ import java.util.List; @SystemApi public abstract class IntelligenceService extends Service { + private static final String TAG = "IntelligenceService"; + /** * The {@link Intent} that must be declared as handled by the service. * To be supported, the service must also require the @@ -43,6 +53,42 @@ public abstract class IntelligenceService extends Service { public static final String SERVICE_INTERFACE = "android.service.intelligence.IntelligenceService"; + private Handler mHandler; + + private final IIntelligenceService mInterface = new IIntelligenceService.Stub() { + + @Override + public void onSessionLifecycle(InteractionContext context, InteractionSessionId sessionId) + throws RemoteException { + if (context != null) { + mHandler.sendMessage( + obtainMessage(IntelligenceService::onCreateInteractionSession, + IntelligenceService.this, context, sessionId)); + } else { + mHandler.sendMessage( + obtainMessage(IntelligenceService::onDestroyInteractionSession, + IntelligenceService.this, sessionId)); + } + } + }; + + @CallSuper + @Override + public void onCreate() { + super.onCreate(); + mHandler = new Handler(Looper.getMainLooper(), null, true); + } + + /** @hide */ + @Override + public final IBinder onBind(Intent intent) { + if (SERVICE_INTERFACE.equals(intent.getAction())) { + return mInterface.asBinder(); + } + Log.w(TAG, "Tried to bind to wrong intent: " + intent); + return null; + } + /** * Creates a new interaction session. * @@ -63,7 +109,7 @@ public abstract class IntelligenceService extends Service { @NonNull List events); /** - * Destroys the content capture session identified by the specified {@code sessionId}. + * Destroys the interaction session. * * @param sessionId the id of the session to destroy */ diff --git a/core/java/android/service/intelligence/InteractionContext.aidl b/core/java/android/service/intelligence/InteractionContext.aidl new file mode 100644 index 0000000000000..4ce6aa45d4a16 --- /dev/null +++ b/core/java/android/service/intelligence/InteractionContext.aidl @@ -0,0 +1,19 @@ +/** + * 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 android.service.intelligence; + +parcelable InteractionContext; diff --git a/core/java/android/service/intelligence/InteractionContext.java b/core/java/android/service/intelligence/InteractionContext.java index 4d83820f2a2a0..c1803ad259d5d 100644 --- a/core/java/android/service/intelligence/InteractionContext.java +++ b/core/java/android/service/intelligence/InteractionContext.java @@ -23,6 +23,9 @@ import android.content.ComponentName; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.util.Preconditions; + +import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -32,7 +35,7 @@ import java.lang.annotation.RetentionPolicy; public final class InteractionContext implements Parcelable { /** - * Flag used to indicate that the app explicitly disabled contents capture for the activity + * Flag used to indicate that the app explicitly disabled content capture for the activity * (using * {@link android.view.intelligence.IntelligenceManager#disableContentCapture()}), * in which case the service will just receive activity-level events. @@ -54,24 +57,34 @@ public final class InteractionContext implements Parcelable { @Retention(RetentionPolicy.SOURCE) @interface ContextCreationFlags{} + // TODO(b/111276913): create new object for taskId + componentName / reuse on other places + private final @NonNull ComponentName mComponentName; + private final int mTaskId; + private final int mDisplayId; + private final int mFlags; + + /** @hide */ - InteractionContext() { + public InteractionContext(@NonNull ComponentName componentName, int taskId, int displayId, + int flags) { + mComponentName = Preconditions.checkNotNull(componentName); + mTaskId = taskId; + mDisplayId = displayId; + mFlags = flags; } /** * Gets the id of the {@link TaskInfo task} associated with this context. */ public int getTaskId() { - //TODO(b/111276913): implement - return 108; + return mTaskId; } /** * Gets the activity associated with this context. */ public @NonNull ComponentName getActivityComponent() { - //TODO(b/111276913): implement - return null; + return mComponentName; } /** @@ -79,8 +92,7 @@ public final class InteractionContext implements Parcelable { * {G android.hardware.display.DisplayManager#getDisplay(int) DisplayManager.getDisplay()}. */ public int getDisplayId() { - //TODO(b/111276913): implement - return 42; + return mDisplayId; } /** @@ -90,8 +102,26 @@ public final class InteractionContext implements Parcelable { * {@link #FLAG_DISABLED_BY_APP}. */ public @ContextCreationFlags int getFlags() { - //TODO(b/111276913): implement - return 42; + return mFlags; + } + + /** + * @hide + */ + // TODO(b/111276913): dump to proto as well + public void dump(PrintWriter pw) { + pw.print("comp="); pw.print(mComponentName.flattenToShortString()); + pw.print(", taskId="); pw.print(mTaskId); + pw.print(", displayId="); pw.print(mDisplayId); + if (mFlags > 0) { + pw.print(", flags="); pw.print(mFlags); + } + } + + @Override + public String toString() { + return "Context[act=" + mComponentName.flattenToShortString() + ", taskId=" + mTaskId + + ", displayId=" + mDisplayId + ", flags=" + mFlags + "]"; } @Override @@ -101,6 +131,10 @@ public final class InteractionContext implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { + parcel.writeParcelable(mComponentName, flags); + parcel.writeInt(mTaskId); + parcel.writeInt(mDisplayId); + parcel.writeInt(mFlags); } public static final Parcelable.Creator CREATOR = @@ -108,8 +142,11 @@ public final class InteractionContext implements Parcelable { @Override public InteractionContext createFromParcel(Parcel parcel) { - // TODO(b/111276913): implement - return null; + final ComponentName componentName = parcel.readParcelable(null); + final int taskId = parcel.readInt(); + final int displayId = parcel.readInt(); + final int flags = parcel.readInt(); + return new InteractionContext(componentName, taskId, displayId, flags); } @Override diff --git a/core/java/android/service/intelligence/InteractionSessionId.aidl b/core/java/android/service/intelligence/InteractionSessionId.aidl new file mode 100644 index 0000000000000..a5392b684a110 --- /dev/null +++ b/core/java/android/service/intelligence/InteractionSessionId.aidl @@ -0,0 +1,19 @@ +/** + * 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 android.service.intelligence; + +parcelable InteractionSessionId; diff --git a/core/java/android/service/intelligence/InteractionSessionId.java b/core/java/android/service/intelligence/InteractionSessionId.java index 4c9d706b0b408..ca68f8efdbd3d 100644 --- a/core/java/android/service/intelligence/InteractionSessionId.java +++ b/core/java/android/service/intelligence/InteractionSessionId.java @@ -20,13 +20,39 @@ import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import java.io.PrintWriter; + // TODO(b/111276913): add javadocs / implement equals/hashcode/string /** @hide */ @SystemApi public final class InteractionSessionId implements Parcelable { + private final int mGlobalId; + + // TODO(b/111276913): remove if not needed + private final int mLocalId; + /** @hide */ - public InteractionSessionId() { + public InteractionSessionId(int globalId, int localId) { + mGlobalId = globalId; + mLocalId = localId; + } + + /** @hide */ + public int getGlobalId() { + return mGlobalId; + } + + /** @hide */ + // TODO(b/111276913): dump to proto as well + public void dump(PrintWriter pw) { + pw.print("globalId="); pw.print(mGlobalId); + pw.print("localId="); pw.print(mLocalId); + } + + @Override + public String toString() { + return "SessionId[globalId=" + mGlobalId + ", localId=" + mLocalId + "]"; } @Override @@ -36,6 +62,8 @@ public final class InteractionSessionId implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mGlobalId); + parcel.writeInt(mLocalId); } public static final Parcelable.Creator CREATOR = @@ -43,8 +71,9 @@ public final class InteractionSessionId implements Parcelable { @Override public InteractionSessionId createFromParcel(Parcel parcel) { - // TODO(b/111276913): implement - return null; + final int globalId = parcel.readInt(); + final int localId = parcel.readInt(); + return new InteractionSessionId(globalId, localId); } @Override diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.java b/core/java/android/view/intelligence/ContentCaptureEvent.java index b8330e5568fb4..d6aec344171d0 100644 --- a/core/java/android/view/intelligence/ContentCaptureEvent.java +++ b/core/java/android/view/intelligence/ContentCaptureEvent.java @@ -149,7 +149,6 @@ public final class ContentCaptureEvent implements Parcelable { return null; } - @Override public int describeContents() { return 0; @@ -157,6 +156,7 @@ public final class ContentCaptureEvent implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { + // TODO(b/111276913): implement } public static final Parcelable.Creator CREATOR = diff --git a/core/java/android/view/intelligence/IIntelligenceManager.aidl b/core/java/android/view/intelligence/IIntelligenceManager.aidl new file mode 100644 index 0000000000000..f4901c371614a --- /dev/null +++ b/core/java/android/view/intelligence/IIntelligenceManager.aidl @@ -0,0 +1,38 @@ +/* + * 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 android.view.intelligence; + +import android.content.ComponentName; +import android.os.IBinder; +import com.android.internal.os.IResultReceiver; + +/** + * {@hide} + */ +oneway interface IIntelligenceManager { + /** + * Starts a session, sending the "remote" sessionId to the receiver. + */ + void startSession(int userId, IBinder activityToken, in ComponentName componentName, + int localSessionId, int flags, in IResultReceiver result); + + /** + * Finishes a session. + */ + void finishSession(int userId, IBinder activityToken, in ComponentName componentName, + int localSessionId, int globalSessionId); +} diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java index 5513ce2f6732f..b1d06f7fccfdf 100644 --- a/core/java/android/view/intelligence/IntelligenceManager.java +++ b/core/java/android/view/intelligence/IntelligenceManager.java @@ -18,29 +18,174 @@ package android.view.intelligence; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.ComponentName; import android.content.Context; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.IResultReceiver; import com.android.internal.util.Preconditions; +import java.io.PrintWriter; import java.util.Set; /** - * TODO(b/111276913): add javadocs / implement / add SystemService / PackageFeature + * TODO(b/111276913): add javadocs / implement */ +@SystemService(Context.INTELLIGENCE_MANAGER_SERVICE) public final class IntelligenceManager { + private static final String TAG = "IntelligenceManager"; + + // TODO(b/111276913): define a way to dynamically set it (for example, using settings?) + private static final boolean VERBOSE = false; + /** * Used to indicate that a text change was caused by user input (for example, through IME). */ //TODO(b/111276913): link to notifyTextChanged() method once available public static final int FLAG_USER_INPUT = 0x1; - private final Context mContext; /** @hide */ - public IntelligenceManager(@NonNull Context context) { + public static final int NO_SESSION = 0; + + /** + * Initial state, when there is no session. + * + * @hide + */ + public static final int STATE_UNKNOWN = 0; + + /** + * Service's startSession() was called, but remote session id was not returned yet. + * + * @hide + */ + public static final int STATE_WAITING_FOR_SESSION_ID = 1; + + /** + * Session is active. + * + * @hide + */ + public static final int STATE_ACTIVE = 2; + + private static int sNextSessionId; + + private final Context mContext; + + @Nullable + private final IIntelligenceManager mService; + + private final Object mLock = new Object(); + + // TODO(b/111276913): localSessionId might be an overkill, perhaps just the global id is enough. + // Let's keep both for now, and revisit once we decide whether the session id will be persisted + // when the activity's process is killed + @GuardedBy("mLock") + private int mLocalSessionId = NO_SESSION; + + @GuardedBy("mLock") + private int mRemoteSessionId = NO_SESSION; + + @GuardedBy("mLock") + private int mState = STATE_UNKNOWN; + + @GuardedBy("mLock") + private IBinder mApplicationToken; + + // TODO(b/111276913): replace by an interface name implemented by Activity, similar to + // AutofillClient + @GuardedBy("mLock") + private ComponentName mComponentName; + + /** @hide */ + public IntelligenceManager(@NonNull Context context, @Nullable IIntelligenceManager service) { mContext = Preconditions.checkNotNull(context, "context cannot be null"); + mService = service; + } + + /** @hide */ + public void onActivityCreated(@NonNull IBinder token, @NonNull ComponentName componentName) { + if (!isContentCaptureEnabled()) return; + + synchronized (mLock) { + if (mState != STATE_UNKNOWN) { + Log.w(TAG, "ignoring onActivityStarted(" + token + ") while on state " + + getStateAsStringLocked()); + return; + } + mState = STATE_WAITING_FOR_SESSION_ID; + mLocalSessionId = ++sNextSessionId; + mRemoteSessionId = NO_SESSION; + mApplicationToken = token; + mComponentName = componentName; + + if (VERBOSE) { + Log.v(TAG, "onActivityStarted(): token=" + token + ", act=" + componentName + + ", localSessionId=" + mLocalSessionId); + } + final int flags = 0; // TODO(b/111276913): get proper flags + + try { + mService.startSession(mContext.getUserId(), mApplicationToken, componentName, + mLocalSessionId, flags, new IResultReceiver.Stub() { + @Override + public void send(int resultCode, Bundle resultData) + throws RemoteException { + synchronized (mLock) { + if (resultCode > 0) { + mRemoteSessionId = resultCode; + mState = STATE_ACTIVE; + } else { + // TODO(b/111276913): handle other cases like disabled by + // service + mState = STATE_UNKNOWN; + } + if (VERBOSE) { + Log.v(TAG, "onActivityStarted() result: code=" + resultCode + + ", remoteSession=" + mRemoteSessionId + + ", state=" + getStateAsStringLocked()); + } + } + } + }); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** @hide */ + public void onActivityDestroyed() { + if (!isContentCaptureEnabled()) return; + + synchronized (mLock) { + //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote + // id) and send it to the cache of batched commands + + if (VERBOSE) { + Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsStringLocked() + + ", localSessionId=" + mLocalSessionId + + ", mRemoteSessionId=" + mRemoteSessionId); + } + + try { + mService.finishSession(mContext.getUserId(), mApplicationToken, mComponentName, + mLocalSessionId, mRemoteSessionId); + mState = STATE_UNKNOWN; + mLocalSessionId = mRemoteSessionId = NO_SESSION; + mApplicationToken = null; + mComponentName = null; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } /** @@ -54,11 +199,13 @@ public final class IntelligenceManager { } /** - * Checks whether contents capture is enabled for this activity. + * Checks whether content capture is enabled for this activity. */ public boolean isContentCaptureEnabled() { - //TODO(b/111276913): implement - return false; + //TODO(b/111276913): properly implement by checking if it was explicitly disabled by + // service, or if service is not set + // (and probably renamign to isEnabledLocked() + return mService != null; } /** @@ -68,6 +215,7 @@ public final class IntelligenceManager { * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}. */ public void disableContentCapture() { + //TODO(b/111276913): implement } /** @@ -140,4 +288,41 @@ public final class IntelligenceManager { //TODO(b/111276913): implement return null; } + + /** @hide */ + public void dump(String prefix, PrintWriter pw) { + pw.print(prefix); pw.println("IntelligenceManager"); + final String prefix2 = prefix + " "; + synchronized (mLock) { + pw.print(prefix2); pw.print("mContext: "); pw.println(mContext); + pw.print(prefix2); pw.print("mService: "); pw.println(mService); + pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId()); + pw.print(prefix2); pw.print("enabled: "); pw.println(isContentCaptureEnabled()); + pw.print(prefix2); pw.print("mLocalSessionId: "); pw.println(mLocalSessionId); + pw.print(prefix2); pw.print("mRemoteSessionId: "); pw.println(mRemoteSessionId); + pw.print(prefix2); pw.print("mState: "); pw.print(mState); pw.print(" ("); + pw.print(getStateAsStringLocked()); pw.println(")"); + pw.print(prefix2); pw.print("mAppToken: "); pw.println(mApplicationToken); + pw.print(prefix2); pw.print("mComponentName: "); pw.println(mComponentName); + } + } + + @GuardedBy("mLock") + private String getStateAsStringLocked() { + return getStateAsString(mState); + } + + @NonNull + private static String getStateAsString(int state) { + switch (state) { + case STATE_UNKNOWN: + return "UNKNOWN"; + case STATE_WAITING_FOR_SESSION_ID: + return "WAITING_FOR_SESSION_ID"; + case STATE_ACTIVE: + return "ACTIVE"; + default: + return "INVALID:" + state; + } + } } diff --git a/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java b/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java new file mode 100644 index 0000000000000..a437a390a0e67 --- /dev/null +++ b/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java @@ -0,0 +1,121 @@ +/* + * 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.intelligence; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.Context; +import android.os.IBinder; +import android.service.intelligence.IntelligenceService; +import android.service.intelligence.InteractionContext; +import android.service.intelligence.InteractionSessionId; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.AbstractRemoteService; +import com.android.server.intelligence.RemoteIntelligenceService.RemoteIntelligenceServiceCallbacks; + +import java.io.PrintWriter; + +final class ContentCaptureSession implements RemoteIntelligenceServiceCallbacks { + + private static final String TAG = "ContentCaptureSession"; + + private final Object mLock; + private final IBinder mActivityToken; + + private final IntelligencePerUserService mService; + private final RemoteIntelligenceService mRemoteService; + private final InteractionContext mInterationContext; + private final InteractionSessionId mId; + + ContentCaptureSession(@NonNull Context context, int userId, @NonNull Object lock, + @NonNull IBinder activityToken, @NonNull IntelligencePerUserService service, + @NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName, + int taskId, int displayId, int localSessionId, int globalSessionId, int flags, + boolean bindInstantServiceAllowed, boolean verbose) { + mLock = lock; + mActivityToken = activityToken; + mService = service; + mRemoteService = new RemoteIntelligenceService(context, + IntelligenceService.SERVICE_INTERFACE, serviceComponentName, userId, this, + bindInstantServiceAllowed, verbose); + mId = new InteractionSessionId(globalSessionId, localSessionId); + mInterationContext = new InteractionContext(appComponentName, taskId, displayId, flags); + } + + /** + * Notifies the {@link IntelligenceService} that the service started. + */ + @GuardedBy("mLock") + public void notifySessionStartedLocked() { + mRemoteService.onSessionLifecycleRequest(mInterationContext, mId); + } + + /** + * Cleans up the session and remove itself from the service. + * + * @param notifyRemoteService whether it should trigger a {@link + * IntelligenceService#onDestroyInteractionSession(InteractionSessionId)} + * request. + */ + @GuardedBy("mLock") + public void removeSelfLocked(boolean notifyRemoteService) { + try { + if (notifyRemoteService) { + mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId); + } + } finally { + mService.removeSessionLocked(mInterationContext.getActivityComponent()); + } + } + + @Override // from RemoteScreenObservationServiceCallbacks + public void onServiceDied(AbstractRemoteService service) { + // TODO(b/111276913): implement (remove session from PerUserSession?) + if (mService.isDebug()) { + Slog.d(TAG, "onServiceDied() for " + mId); + } + synchronized (mLock) { + removeSelfLocked(/* notifyRemoteService= */ false); + } + } + + @Override // from RemoteScreenObservationServiceCallbacks + public void onSessionLifecycleRequestFailureOrTimeout(boolean timedOut) { + // TODO(b/111276913): log metrics on whether timed out or not + if (mService.isDebug()) { + Slog.d(TAG, "onSessionLifecycleRequestFailure(" + mId + "): timed out=" + timedOut); + } + synchronized (mLock) { + removeSelfLocked(/* notifyRemoteService= */ false); + } + } + + /** + * Gets global id, unique per {@link IntelligencePerUserService}. + */ + public int getGlobalSessionId() { + return mId.getGlobalId(); + } + + @GuardedBy("mLock") + public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) { + pw.print(prefix); pw.print("id: "); mId.dump(pw); pw.println(); + pw.print(prefix); pw.print("context: "); mInterationContext.dump(pw); pw.println(); + pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken); + } +} diff --git a/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java new file mode 100644 index 0000000000000..4ea90364f9364 --- /dev/null +++ b/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java @@ -0,0 +1,127 @@ +/* + * 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.intelligence; + +import static android.content.Context.INTELLIGENCE_MANAGER_SERVICE; + +import android.annotation.NonNull; +import android.app.ActivityManagerInternal; +import android.content.ComponentName; +import android.content.Context; +import android.os.IBinder; +import android.os.UserManager; +import android.view.intelligence.IIntelligenceManager; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.IResultReceiver; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.Preconditions; +import com.android.server.AbstractMasterSystemService; +import com.android.server.LocalServices; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * A service used to observe the contents of the screen. + * + *

The data collected by this service can be analyzed and combined with other sources to provide + * contextual data in other areas of the system such as Autofill. + */ +public final class IntelligenceManagerService + extends AbstractMasterSystemService { + + private static final String TAG = "IntelligenceManagerService"; + + @GuardedBy("mLock") + private ActivityManagerInternal mAm; + + public IntelligenceManagerService(Context context) { + super(context, UserManager.DISALLOW_INTELLIGENCE_CAPTURE); + } + + @Override // from MasterSystemService + protected String getServiceSettingsProperty() { + // TODO(b/111276913): STOPSHIP temporary settings, until it's set by resourcs + cmd + return "intel_service"; + } + + @Override // from MasterSystemService + protected IntelligencePerUserService newServiceLocked(int resolvedUserId, + boolean disabled) { + return new IntelligencePerUserService(this, mLock, resolvedUserId); + } + + @Override // from SystemService + public void onStart() { + publishBinderService(INTELLIGENCE_MANAGER_SERVICE, + new IntelligenceManagerServiceStub()); + } + + private ActivityManagerInternal getAmInternal() { + synchronized (mLock) { + if (mAm == null) { + mAm = LocalServices.getService(ActivityManagerInternal.class); + } + } + return mAm; + } + + final class IntelligenceManagerServiceStub extends IIntelligenceManager.Stub { + + @Override + public void startSession(int userId, @NonNull IBinder activityToken, + @NonNull ComponentName componentName, int localSessionId, int flags, + @NonNull IResultReceiver result) { + Preconditions.checkNotNull(activityToken); + + // TODO(b/111276913): refactor getTaskIdForActivity() to also return ComponentName, + // so we don't pass it on startSession (same for Autofill) + final int taskId = getAmInternal().getTaskIdForActivity(activityToken, false); + + // TODO(b/111276913): get from AM as well + final int displayId = 0; + + synchronized (mLock) { + final IntelligencePerUserService service = getServiceForUserLocked(userId); + service.startSessionLocked(activityToken, componentName, taskId, displayId, + localSessionId, flags, result); + } + } + + @Override + public void finishSession(int userId, @NonNull IBinder activityToken, + @NonNull ComponentName componentName, int localSessionId, int globalSessionId) { + Preconditions.checkNotNull(activityToken); + + synchronized (mLock) { + final IntelligencePerUserService service = getServiceForUserLocked(userId); + service.finishSessionLocked(activityToken, componentName, localSessionId, + globalSessionId); + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return; + + synchronized (mLock) { + dumpLocked("", pw); + } + } + } +} diff --git a/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java new file mode 100644 index 0000000000000..b62b239ab83ef --- /dev/null +++ b/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java @@ -0,0 +1,174 @@ +/* + * 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.intelligence; + +import android.Manifest; +import android.annotation.NonNull; +import android.app.AppGlobals; +import android.content.ComponentName; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ServiceInfo; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.IResultReceiver; +import com.android.server.AbstractPerUserSystemService; + +import java.io.PrintWriter; + +/** + * Per-user instance of {@link IntelligenceManagerService}. + */ +final class IntelligencePerUserService + extends AbstractPerUserSystemService { + + private static final String TAG = "IntelligencePerUserService"; + + private static int sNextSessionId; + + // TODO(b/111276913): should key by componentName + taskId or ActivityToken + @GuardedBy("mLock") + private final ArrayMap mSessions = new ArrayMap<>(); + + // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's + + protected IntelligencePerUserService( + IntelligenceManagerService master, Object lock, int userId) { + super(master, lock, userId); + } + + @Override // from PerUserSystemService + protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent) + throws NameNotFoundException { + + 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); + } catch (RemoteException e) { + Slog.w(TAG, "Could not get service for " + serviceComponent + ": " + e); + return null; + } + if (!Manifest.permission.BIND_INTELLIGENCE_SERVICE.equals(si.permission)) { + Slog.w(TAG, "IntelligenceService from '" + si.packageName + + "' does not require permission " + + Manifest.permission.BIND_INTELLIGENCE_SERVICE); + throw new SecurityException("Service does not require permission " + + Manifest.permission.BIND_INTELLIGENCE_SERVICE); + } + return si; + } + + // TODO(b/111276913): log metrics + @GuardedBy("mLock") + public void startSessionLocked(@NonNull IBinder activityToken, + @NonNull ComponentName componentName, int taskId, int displayId, int localSessionId, + int flags, @NonNull IResultReceiver resultReceiver) { + final ComponentName serviceComponentName = getServiceComponentName(); + if (serviceComponentName == null) { + // TODO(b/111276913): this happens when the system service is starting, we should + // probably handle it in a more elegant way (like waiting for boot_complete or + // something like that + Slog.w(TAG, "startSession(" + activityToken + "): hold your horses"); + return; + } + + ContentCaptureSession session = mSessions.get(componentName); + if (session != null) { + if (mMaster.debug) { + Slog.d(TAG, "startSession(): reusing session " + session.getGlobalSessionId() + + " for " + componentName); + } + // TODO(b/111276913): check if local ids match and decide what to do if they don't + // TODO(b/111276913): should we call session.notifySessionStartedLocked() again?? + // if not, move notifySessionStartedLocked() into session constructor + sendToClient(resultReceiver, session.getGlobalSessionId()); + return; + } + + // TODO(b/117779333): get from mMaster once it's moved to superclass + final boolean bindInstantServiceAllowed = false; + + session = new ContentCaptureSession(getContext(), mUserId, mLock, activityToken, + this, serviceComponentName, componentName, taskId, displayId, localSessionId, + ++sNextSessionId, flags, bindInstantServiceAllowed, mMaster.verbose); + if (mMaster.verbose) { + Slog.v(TAG, "startSession(): new session for " + componentName + "; globalId =" + + session.getGlobalSessionId()); + } + mSessions.put(componentName, session); + session.notifySessionStartedLocked(); + sendToClient(resultReceiver, session.getGlobalSessionId()); + } + + // TODO(b/111276913): log metrics + @GuardedBy("mLock") + public void finishSessionLocked(@NonNull IBinder activityToken, + @NonNull ComponentName componentName, int localSessionId, int globalSessionId) { + final ContentCaptureSession session = mSessions.get(componentName); + if (session == null) { + Slog.w(TAG, "finishSession(): no session for " + componentName); + return; + } + if (mMaster.verbose) { + Slog.v(TAG, "finishSession(): comp=" + componentName + "; globalId =" + + session.getGlobalSessionId()); + } + // TODO(b/111276913): check if all arguments match existing session and throw exception if + // not. Or just use componentName if we change AIDL to pass just ApplicationToken and + // retrieve componentName from AMInternal + session.removeSelfLocked(true); + } + + @GuardedBy("mLock") + public void removeSessionLocked(@NonNull ComponentName key) { + mSessions.remove(key); + } + + @Override + protected void dumpLocked(String prefix, PrintWriter pw) { + super.dumpLocked(prefix, pw); + pw.print(prefix); pw.print("next id: "); pw.println(sNextSessionId); + if (mSessions.isEmpty()) { + pw.print(prefix); pw.println("no sessions"); + } else { + final int size = mSessions.size(); + pw.print(prefix); pw.print("number sessions: "); pw.println(size); + final String prefix2 = prefix + " "; + for (int i = 0; i < size; i++) { + pw.print(prefix); pw.print("session@"); pw.println(i); + final ContentCaptureSession session = mSessions.valueAt(i); + session.dumpLocked(prefix2, pw); + } + } + } + + private static void sendToClient(@NonNull IResultReceiver resultReceiver, int value) { + try { + resultReceiver.send(value, null); + } catch (RemoteException e) { + Slog.w(TAG, "Error async reporting result to client: " + e); + } + } +} diff --git a/services/autofill/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/autofill/java/com/android/server/intelligence/RemoteIntelligenceService.java new file mode 100644 index 0000000000000..ee66b4e741299 --- /dev/null +++ b/services/autofill/java/com/android/server/intelligence/RemoteIntelligenceService.java @@ -0,0 +1,122 @@ +/* + * 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.intelligence; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.os.IBinder; +import android.os.IInterface; +import android.os.RemoteException; +import android.service.intelligence.IIntelligenceService; +import android.service.intelligence.InteractionContext; +import android.service.intelligence.InteractionSessionId; +import android.text.format.DateUtils; +import android.util.Slog; + +import com.android.server.AbstractRemoteService; + +final class RemoteIntelligenceService extends AbstractRemoteService { + + private static final String TAG = "RemoteIntelligenceService"; + + private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS; + private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS; + + private final RemoteIntelligenceServiceCallbacks mCallbacks; + private IIntelligenceService mService; + + RemoteIntelligenceService(Context context, String serviceInterface, + ComponentName componentName, int userId, + RemoteIntelligenceServiceCallbacks callbacks, boolean bindInstantServiceAllowed, + boolean verbose) { + super(context, serviceInterface, componentName, userId, callbacks, + bindInstantServiceAllowed, verbose); + mCallbacks = callbacks; + } + + @Override // from RemoteService + protected IInterface getServiceInterface(@NonNull IBinder service) { + mService = IIntelligenceService.Stub.asInterface(service); + return mService; + } + + // TODO(b/111276913): modify super class to allow permanent binding when value is 0 or negative + @Override // from RemoteService + protected long getTimeoutIdleBindMillis() { + // TODO(b/111276913): read from Settings so it can be changed in the field + return TIMEOUT_IDLE_BIND_MILLIS; + } + + @Override // from RemoteService + protected long getRemoteRequestMillis() { + // TODO(b/111276913): read from Settings so it can be changed in the field + return TIMEOUT_REMOTE_REQUEST_MILLIS; + } + + /** + * Called by {@link ContentCaptureSession} to generate a call to the + * {@link RemoteIntelligenceService} to indicate the session was created (when {@code context} + * is not {@code null} or destroyed (when {@code context} is {@code null}). + */ + public void onSessionLifecycleRequest(@Nullable InteractionContext context, + @NonNull InteractionSessionId sessionId) { + cancelScheduledUnbind(); + scheduleRequest(new PendingSessionLifecycleRequest(this, context, sessionId)); + } + + private static final class PendingSessionLifecycleRequest + extends PendingRequest { + + private final InteractionContext mContext; + private final InteractionSessionId mSessionId; + + protected PendingSessionLifecycleRequest(@NonNull RemoteIntelligenceService service, + @Nullable InteractionContext context, @NonNull InteractionSessionId sessionId) { + super(service); + mContext = context; + mSessionId = sessionId; + } + + @Override // from PendingRequest + public void run() { + final RemoteIntelligenceService remoteService = getService(); + if (remoteService != null) { + try { + remoteService.mService.onSessionLifecycle(mContext, mSessionId); + } catch (RemoteException e) { + Slog.w(TAG, "exception handling PendingSessionLifecycleRequest for " + + mSessionId + ": " + e); + remoteService.mCallbacks + .onSessionLifecycleRequestFailureOrTimeout(/* timedOut= */ false); + } + } + } + + @Override // from PendingRequest + protected void onTimeout(RemoteIntelligenceService remoteService) { + Slog.w(TAG, "timed out handling PendingSessionLifecycleRequest for " + + mSessionId); + remoteService.mCallbacks + .onSessionLifecycleRequestFailureOrTimeout(/* timedOut= */ true); + } + } + + public interface RemoteIntelligenceServiceCallbacks extends VultureCallback { + void onSessionLifecycleRequestFailureOrTimeout(boolean timedOut); + } +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index f432c8d8046ea..54a140dec713e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -16,6 +16,12 @@ package com.android.server; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; +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.app.ActivityThread; import android.app.INotificationManager; import android.app.usage.UsageStatsManagerInternal; @@ -64,10 +70,11 @@ import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.EmergencyAffordanceManager; import com.android.internal.widget.ILockSettings; import com.android.server.am.ActivityManagerService; -import com.android.server.wm.ActivityTaskManagerService; import com.android.server.appbinding.AppBindingService; import com.android.server.audio.AudioService; import com.android.server.biometrics.BiometricService; +import com.android.server.biometrics.face.FaceService; +import com.android.server.biometrics.fingerprint.FingerprintService; import com.android.server.biometrics.iris.IrisService; import com.android.server.broadcastradio.BroadcastRadioService; import com.android.server.camera.CameraServiceProxy; @@ -79,8 +86,6 @@ import com.android.server.display.ColorDisplayService; import com.android.server.display.DisplayManagerService; import com.android.server.dreams.DreamManagerService; import com.android.server.emergency.EmergencyAffordanceService; -import com.android.server.biometrics.face.FaceService; -import com.android.server.biometrics.fingerprint.FingerprintService; import com.android.server.hdmi.HdmiControlService; import com.android.server.input.InputManagerService; import com.android.server.inputmethod.InputMethodManagerService; @@ -88,8 +93,8 @@ import com.android.server.job.JobSchedulerService; import com.android.server.lights.LightsService; import com.android.server.media.MediaResourceMonitorService; import com.android.server.media.MediaRouterService; -import com.android.server.media.MediaUpdateService; import com.android.server.media.MediaSessionService; +import com.android.server.media.MediaUpdateService; import com.android.server.media.projection.MediaProjectionManagerService; import com.android.server.net.NetworkPolicyManagerService; import com.android.server.net.NetworkStatsService; @@ -128,6 +133,7 @@ import com.android.server.uri.UriGrantsManagerService; import com.android.server.usage.UsageStatsService; import com.android.server.vr.VrManagerService; import com.android.server.webkit.WebViewUpdateService; +import com.android.server.wm.ActivityTaskManagerService; import com.android.server.wm.WindowManagerService; import dalvik.system.VMRuntime; @@ -139,12 +145,6 @@ import java.util.Timer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; -import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; -import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; -import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; -import static android.os.IServiceManager.DUMP_FLAG_PROTO; -import static android.view.Display.DEFAULT_DISPLAY; - public final class SystemServer { private static final String TAG = "SystemServer"; @@ -230,6 +230,8 @@ public final class SystemServer { "com.android.server.wallpaper.WallpaperManagerService$Lifecycle"; private static final String AUTO_FILL_MANAGER_SERVICE_CLASS = "com.android.server.autofill.AutofillManagerService"; + private static final String INTELLIGENCE_MANAGER_SERVICE_CLASS = + "com.android.server.intelligence.IntelligenceManagerService"; private static final String TIME_ZONE_RULES_MANAGER_SERVICE_CLASS = "com.android.server.timezone.RulesManagerService$Lifecycle"; private static final String IOT_SERVICE_CLASS = @@ -795,6 +797,8 @@ public final class SystemServer { boolean disableSystemTextClassifier = SystemProperties.getBoolean( "config.disable_systemtextclassifier", false); + boolean disableIntelligence = SystemProperties.getBoolean( + "config.disable_intelligence", false); boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime", false); boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice", @@ -1737,6 +1741,12 @@ public final class SystemServer { traceEnd(); } + if (!disableIntelligence) { + traceBeginAndSlog("StartIntelligenceService"); + mSystemServiceManager.startService(INTELLIGENCE_MANAGER_SERVICE_CLASS); + traceEnd(); + } + traceBeginAndSlog("AppServiceManager"); mSystemServiceManager.startService(AppBindingService.Lifecycle.class); traceEnd();