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

src.com.android.server.utils.UserTokenWatcher Maven / Gradle / Ivy

/*
 * 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.utils;

import android.annotation.UserIdInt;
import android.os.Handler;
import android.os.IBinder;
import android.os.TokenWatcher;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;

import java.io.PrintWriter;

/**
 * Multi-user aware {@link TokenWatcher}.
 *
 * {@link UserTokenWatcher} is thread-safe.
 */
public final class UserTokenWatcher {

    private final Callback mCallback;
    private final Handler mHandler;
    private final String mTag;

    @GuardedBy("mWatchers")
    private final SparseArray mWatchers = new SparseArray<>(1);

    public UserTokenWatcher(Callback callback, Handler handler, String tag) {
        mCallback = callback;
        mHandler = handler;
        mTag = tag;
    }

    /**
     * Record that this token has been acquired for the given user.  When acquire is called, and
     * the user's count goes from 0 to 1, the acquired callback is called on the given
     * handler.
     *
     * Note that the same {@code token} can only be acquired once per user. If this
     * {@code token} has already been acquired for the given user, no action is taken. The first
     * subsequent call to {@link #release} will release this {@code token}
     * immediately.
     *
     * @param token  An IBinder object.
     * @param tag    A string used by the {@link #dump} method for debugging,
     *               to see who has references.
     * @param userId A user id
     */
    public void acquire(IBinder token, String tag, @UserIdInt int userId) {
        synchronized (mWatchers) {
            TokenWatcher watcher = mWatchers.get(userId);
            if (watcher == null) {
                watcher = new InnerTokenWatcher(userId, mHandler, mTag);
                mWatchers.put(userId, watcher);
            }
            watcher.acquire(token, tag);
        }
    }

    /**
     * Record that this token has been released for the given user.  When release is called, and
     * the user's count goes from 1 to 0, the released callback is called on the given
     * handler.
     *
     * @param token  An IBinder object.
     * @param userId A user id
     */
    public void release(IBinder token, @UserIdInt int userId) {
        synchronized (mWatchers) {
            TokenWatcher watcher = mWatchers.get(userId);
            if (watcher != null) {
                watcher.release(token);
            }
        }
    }

    /**
     * Returns whether the given user has any registered tokens that have not been cleaned up.
     *
     * @return true, if the given user has registered tokens.
     */
    public boolean isAcquired(@UserIdInt int userId) {
        synchronized (mWatchers) {
            TokenWatcher watcher = mWatchers.get(userId);
            return watcher != null && watcher.isAcquired();
        }
    }

    /**
     * Dumps the current state.
     */
    public void dump(PrintWriter pw) {
        synchronized (mWatchers) {
            for (int i = 0; i < mWatchers.size(); i++) {
                int userId = mWatchers.keyAt(i);
                TokenWatcher watcher = mWatchers.valueAt(i);
                if (watcher.isAcquired()) {
                    pw.print("User ");
                    pw.print(userId);
                    pw.println(":");
                    watcher.dump(new IndentingPrintWriter(pw, " "));
                }
            }
        }
    }

    /**
     * Callback for {@link UserTokenWatcher}.
     */
    public interface Callback {

        /**
         * Reports that the first token has been acquired for the given user.
         */
        void acquired(@UserIdInt int userId);

        /**
         * Reports that the last token has been release for the given user.
         */
        void released(@UserIdInt int userId);
    }

    private final class InnerTokenWatcher extends TokenWatcher {
        private final int mUserId;

        private InnerTokenWatcher(int userId, Handler handler, String tag) {
            super(handler, tag);
            this.mUserId = userId;
        }

        @Override
        public void acquired() {
            // We MUST NOT hold any locks while invoking the callbacks.
            mCallback.acquired(mUserId);
        }

        @Override
        public void released() {
            // We MUST NOT hold any locks while invoking the callbacks.
            mCallback.released(mUserId);

            synchronized (mWatchers) {
                final TokenWatcher watcher = mWatchers.get(mUserId);
                if (watcher != null && !watcher.isAcquired()) {
                    mWatchers.remove(mUserId);
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy