chromium/components/policy/android/java/src/org/chromium/components/policy/PolicyService.java

// Copyright 2020 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.components.policy;

import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;
import org.jni_zero.NativeClassQualifiedName;
import org.jni_zero.NativeMethods;

import org.chromium.base.Log;
import org.chromium.base.ObserverList;

/**
 * Wrapper of the native PolicyService class in the Java layer.
 *
 * It only supports the Chrome policy domain but not any extension policy. More
 * documentation can be found in
 * //components/policy/core/common/policy_service.h
 *
 * Native pointer is owned by the C++ instance.
 *
 * The class only provides a subset of native class features for now, other
 * functions will be added once needed.
 */
@JNINamespace("policy::android")
public class PolicyService {
    private static final String TAG = "PolicyService";

    private long mNativePolicyService;
    private final ObserverList<Observer> mObservers = new ObserverList<Observer>();

    /**
     * Observer interface for observing PolicyService change for Chrome policy
     * domain.
     *
     * The default method below may increase method count with Desugar. If there
     * are more than 10+ observer implementations, please consider use
     * EmptyObserver instead for default behavior.
     */
    public interface Observer {
        /**
         * Invoked when Chrome policy is modified. The native class of both
         * |previous| and |current| become invalid once the method
         * returns. Do not use their references outside the method.
         * @param previous PolicyMap contains values before the update.
         * @param current PolicyMap contains values after the update.
         */
        default void onPolicyUpdated(PolicyMap previous, PolicyMap current) {}

        /**
         * Invoked when Chome policy domain is initialized. Observer must be
         * added before the naitve PolicyService initialization being finished.
         * Use {@link #isInitializationComplete} to check the initialization
         * state before listening to this event.
         */
        default void onPolicyServiceInitialized() {}
    }

    /**
     * @param observer The {@link Observer} to be notified for Chrome policy
     * update.
     */
    public void addObserver(Observer observer) {
        if (mObservers.isEmpty()) {
            PolicyServiceJni.get().addObserver(mNativePolicyService, PolicyService.this);
        }
        mObservers.addObserver(observer);
    }

    /**
     * @param observer The {@link Observer} to no longer be notified for Chrome
     * policy update.
     */
    public void removeObserver(Observer observer) {
        mObservers.removeObserver(observer);
        if (mObservers.isEmpty()) {
            PolicyServiceJni.get().removeObserver(mNativePolicyService, PolicyService.this);
        }
    }

    /** Returns true if Chrome policy domain has been initialized. */
    public boolean isInitializationComplete() {
        return PolicyServiceJni.get()
                .isInitializationComplete(mNativePolicyService, PolicyService.this);
    }

    /** Returns {@link PolicyMap} that contains all Chrome policies. */
    public PolicyMap getPolicies() {
        return PolicyServiceJni.get().getPolicies(mNativePolicyService, PolicyService.this);
    }

    /** Pass the onPolicyServiceInitialized event to the |mObservers|. */
    @CalledByNative
    private void onPolicyServiceInitialized() {
        Log.i(TAG, "#onPolicyServiceInitialized()");
        for (Observer observer : mObservers) {
            observer.onPolicyServiceInitialized();
        }
    }

    /** Pass the onPolicyUpdated event to the |mObservers|. */
    @CalledByNative
    private void onPolicyUpdated(PolicyMap previous, PolicyMap current) {
        for (Observer observer : mObservers) {
            observer.onPolicyUpdated(previous, current);
        }
    }

    @CalledByNative
    private PolicyService(long nativePolicyService) {
        mNativePolicyService = nativePolicyService;
    }

    @NativeMethods
    public interface Natives {
        @NativeClassQualifiedName("PolicyServiceAndroid")
        void addObserver(long nativePolicyService, PolicyService caller);

        @NativeClassQualifiedName("PolicyServiceAndroid")
        void removeObserver(long nativePolicyService, PolicyService caller);

        @NativeClassQualifiedName("PolicyServiceAndroid")
        boolean isInitializationComplete(long nativePolicyService, PolicyService caller);

        @NativeClassQualifiedName("PolicyServiceAndroid")
        PolicyMap getPolicies(long nativePolicyService, PolicyService caller);
    }
}