All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.contentcapture.ContentCaptureServerSession Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 15-robolectric-12650502
Show newest version
/*
 * 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.contentcapture;

import static android.service.contentcapture.ContentCaptureService.setClientState;
import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_TRUE;
import static android.view.contentcapture.ContentCaptureSession.STATE_ACTIVE;
import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED;
import static android.view.contentcapture.ContentCaptureSession.STATE_SERVICE_RESURRECTED;
import static android.view.contentcapture.ContentCaptureSession.STATE_SERVICE_UPDATING;

import android.annotation.NonNull;
import android.app.assist.ActivityId;
import android.content.ComponentName;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.service.contentcapture.ContentCaptureService;
import android.service.contentcapture.SnapshotData;
import android.util.LocalLog;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureSessionId;
import android.view.contentcapture.MainContentCaptureSession;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.Preconditions;

import java.io.PrintWriter;

final class ContentCaptureServerSession {

    private static final String TAG = ContentCaptureServerSession.class.getSimpleName();

    final IBinder mActivityToken;
    private final ContentCapturePerUserService mService;

    // NOTE: this is the "internal" context (like package and taskId), not the explicit content
    // set by apps - those are only send to the ContentCaptureService.
    private final ContentCaptureContext mContentCaptureContext;

    /**
     * Reference to the binder object help at the client-side process and used to set its state.
     */
    @NonNull
    private final IResultReceiver mSessionStateReceiver;

    /**
     * Canonical session id.
     */
    private final int mId;

    /**
     * UID of the app whose contents is being captured.
     */
    private final int mUid;

    private final Object mLock;

    public final ComponentName appComponentName;

    ContentCaptureServerSession(@NonNull Object lock, @NonNull IBinder activityToken,
            @NonNull ActivityId activityId, @NonNull ContentCapturePerUserService service,
            @NonNull ComponentName appComponentName, @NonNull IResultReceiver sessionStateReceiver,
            int taskId, int displayId, int sessionId, int uid, int flags) {
        Preconditions.checkArgument(sessionId != NO_SESSION_ID);
        mLock = lock;
        mActivityToken = activityToken;
        this.appComponentName = appComponentName;
        mService = service;
        mId = sessionId;
        mUid = uid;
        mContentCaptureContext = new ContentCaptureContext(/* clientContext= */ null,
                activityId, appComponentName, displayId, flags);
        mSessionStateReceiver = sessionStateReceiver;
        try {
            sessionStateReceiver.asBinder().linkToDeath(() -> onClientDeath(), 0);
        } catch (Exception e) {
            Slog.w(TAG, "could not register DeathRecipient for " + activityToken);
        }
    }

    /**
     * Returns whether this session is for the given activity.
     */
    boolean isActivitySession(@NonNull IBinder activityToken) {
        return mActivityToken.equals(activityToken);
    }

    /**
     * Notifies the {@link ContentCaptureService} that the service started.
     */
    @GuardedBy("mLock")
    public void notifySessionStartedLocked(@NonNull IResultReceiver clientReceiver) {
        if (mService.mRemoteService == null) {
            Slog.w(TAG, "notifySessionStartedLocked(): no remote service");
            return;
        }
        mService.mRemoteService.onSessionStarted(mContentCaptureContext, mId, mUid, clientReceiver,
                STATE_ACTIVE);
    }

    /**
     * Changes the {@link ContentCaptureService} enabled state.
     */
    @GuardedBy("mLock")
    public void setContentCaptureEnabledLocked(boolean enabled) {
        try {
            final Bundle extras = new Bundle();
            extras.putBoolean(MainContentCaptureSession.EXTRA_ENABLED_STATE, true);
            mSessionStateReceiver.send(enabled ? RESULT_CODE_TRUE : RESULT_CODE_FALSE, extras);
        } catch (RemoteException e) {
            Slog.w(TAG, "Error async reporting result to client: " + e);
        }
    }

    /**
     * Notifies the {@link ContentCaptureService} of a snapshot of an activity.
     */
    @GuardedBy("mLock")
    public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) {
        final LocalLog logHistory = mService.getMaster().mRequestsHistory;
        if (logHistory != null) {
            logHistory.log("snapshot: id=" + mId);
        }

        if (mService.mRemoteService == null) {
            Slog.w(TAG, "sendActivitySnapshotLocked(): no remote service");
            return;
        }
        mService.mRemoteService.onActivitySnapshotRequest(mId, snapshotData);
    }

    /**
     * Cleans up the session and removes it from the service.
     *
     * @param notifyRemoteService whether it should trigger a {@link
     * ContentCaptureService#onDestroyContentCaptureSession(ContentCaptureSessionId)}
     * request.
     */
    @GuardedBy("mLock")
    public void removeSelfLocked(boolean notifyRemoteService) {
        try {
            destroyLocked(notifyRemoteService);
        } finally {
            mService.removeSessionLocked(mId);
        }
    }

    /**
     * Cleans up the session, but not removes it from the service.
     *
     * @param notifyRemoteService whether it should trigger a {@link
     * ContentCaptureService#onDestroyContentCaptureSession(ContentCaptureSessionId)}
     * request.
     */
    @GuardedBy("mLock")
    public void destroyLocked(boolean notifyRemoteService) {
        if (mService.isVerbose()) {
            Slog.v(TAG, "destroy(notifyRemoteService=" + notifyRemoteService + ")");
        }
        // TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
        if (notifyRemoteService) {
            if (mService.mRemoteService == null) {
                Slog.w(TAG, "destroyLocked(): no remote service");
                return;
            }
            mService.mRemoteService.onSessionFinished(mId);
        }
    }

    /**
     * Called to restore the active state of a session that was paused while the service died.
     */
    @GuardedBy("mLock")
    public void resurrectLocked() {
        final RemoteContentCaptureService remoteService = mService.mRemoteService;
        if (remoteService == null) {
            Slog.w(TAG, "destroyLocked(: no remote service");
            return;
        }
        if (mService.isVerbose()) {
            Slog.v(TAG, "resurrecting " + mActivityToken + " on " + remoteService);
        }
        remoteService.onSessionStarted(new ContentCaptureContext(mContentCaptureContext,
                ContentCaptureContext.FLAG_RECONNECTED), mId, mUid, mSessionStateReceiver,
                STATE_ACTIVE | STATE_SERVICE_RESURRECTED);
    }

    /**
     * Called to pause the session while the service is being updated.
     */
    @GuardedBy("mLock")
    public void pauseLocked() {
        if (mService.isVerbose()) Slog.v(TAG, "pausing " + mActivityToken);
        setClientState(mSessionStateReceiver, STATE_DISABLED | STATE_SERVICE_UPDATING,
                /* binder= */ null);
    }

    /**
     * Called when the session client binder object died - typically when its process was killed
     * and the activity was not properly destroyed.
     */
    private void onClientDeath() {
        if (mService.isVerbose()) {
            Slog.v(TAG, "onClientDeath(" + mActivityToken + "): removing session " + mId);
        }
        synchronized (mLock) {
            removeSelfLocked(/* notifyRemoteService= */ true);
        }
    }

    @GuardedBy("mLock")
    public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
        pw.print(prefix); pw.print("id: ");  pw.print(mId); pw.println();
        pw.print(prefix); pw.print("uid: ");  pw.print(mUid); pw.println();
        pw.print(prefix); pw.print("context: ");  mContentCaptureContext.dump(pw); pw.println();
        pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
        pw.print(prefix); pw.print("app component: "); pw.println(appComponentName);
        pw.print(prefix); pw.print("has autofill callback: ");
    }

    String toShortString() {
        return mId  + ":" + mActivityToken;
    }

    @Override
    public String toString() {
        return "ContentCaptureSession[id=" + mId + ", act=" + mActivityToken + "]";
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy