chromium/chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/ProfileManager.java

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.profiles;

import androidx.annotation.VisibleForTesting;

import org.jni_zero.CalledByNative;
import org.jni_zero.JniType;
import org.jni_zero.NativeMethods;

import org.chromium.base.ObserverList;
import org.chromium.base.ResettersForTesting;
import org.chromium.base.ThreadUtils;

import java.util.List;

/** Java interface to the C++ ProfileManager. */
public class ProfileManager {
    private static Profile sLastUsedProfileForTesting;

    private static ObserverList<Observer> sObservers;
    private static boolean sInitialized;

    /** Observer for Profile creation. */
    public static interface Observer {
        /**
         * Called whenever a profile is created.
         * @param profile The profile that has just been created.
         */
        public void onProfileAdded(Profile profile);

        /**
         * Called whenever a profile is destroyed.
         * @param profile The profile that has just been created.
         */
        public void onProfileDestroyed(Profile profile);
    }

    /** Add an observer to be notified when profiles get created. */
    public static void addObserver(Observer observer) {
        // Lazily creating the ObserverList to avoid the internal ThreadChecker being assigned to
        // the first thread that accesses any ProfileManager method. Because the ObserverList
        // asserts thread consistency, this does not do additional locking on the ProfileManager
        // side because the internal ObserverList checks will crash if thread misuse happens.
        if (sObservers == null) {
            sObservers = new ObserverList<>();
        }
        sObservers.addObserver(observer);
    }

    /** Remove an observer of profiles changes. */
    public static void removeObserver(Observer observer) {
        if (sObservers == null) return;
        sObservers.removeObserver(observer);
    }

    /**
     * @return True iff any profile has been created.
     */
    public static boolean isInitialized() {
        return sInitialized;
    }

    @CalledByNative
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    public static void onProfileAdded(Profile profile) {
        // If a profile has been added, we know the ProfileManager has been initialized.
        sInitialized = true;

        if (sObservers == null) return;
        for (Observer observer : sObservers) {
            observer.onProfileAdded(profile);
        }
    }

    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
    public static void onProfileDestroyed(Profile profile) {
        if (sObservers == null) return;
        for (Observer observer : sObservers) {
            observer.onProfileDestroyed(profile);
        }
    }

    /**
     * Returns the regular (i.e., not off-the-record) profile.
     *
     * <p>Note: The function name uses the "last used" terminology for consistency with
     * profile_manager.cc which supports multiple regular profiles.
     */
    public static Profile getLastUsedRegularProfile() {
        if (sLastUsedProfileForTesting != null) {
            return sLastUsedProfileForTesting;
        }
        ThreadUtils.assertOnUiThread();
        if (!ProfileManager.isInitialized()) {
            throw new IllegalStateException("Browser hasn't finished initialization yet!");
        }
        return (Profile) ProfileManagerJni.get().getLastUsedRegularProfile();
    }

    /** Return the fully loaded and initialized Profiles (excluding off the record Profiles). */
    public static List<Profile> getLoadedProfiles() {
        return ProfileManagerJni.get().getLoadedProfiles();
    }

    public static void onProfileActivated(Profile profile) {
        ProfileManagerJni.get().onProfileActivated(profile);
    }

    /**
     * Destroys the Profile. Destruction is delayed until all associated renderers have been killed,
     * so the profile might not be destroyed upon returning from this call.
     */
    public static void destroyWhenAppropriate(Profile profile) {
        profile.ensureNativeInitialized();
        ProfileManagerJni.get().destroyWhenAppropriate(profile);
    }

    /** Sets for testing the profile to be returned by {@link #getLastUsedRegularProfile()}. */
    public static void setLastUsedProfileForTesting(Profile profile) {
        sLastUsedProfileForTesting = profile;
        ResettersForTesting.register(() -> sLastUsedProfileForTesting = null);
    }

    public static void resetForTesting() {
        sInitialized = false;
        if (sObservers != null) sObservers.clear();
    }

    @NativeMethods
    public interface Natives {
        Object getLastUsedRegularProfile();

        void onProfileActivated(@JniType("Profile*") Profile profile);

        void destroyWhenAppropriate(@JniType("Profile*") Profile caller);

        @JniType("std::vector<Profile*>")
        List<Profile> getLoadedProfiles();
    }
}