/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You 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 java.lang;

// BEGIN android-added

import dalvik.system.VMStack;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FilePermission;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.net.InetAddress;
import java.net.SocketPermission;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.AllPermission;
import java.security.Permission;
import java.security.Security;
import java.security.SecurityPermission;
import java.util.PropertyPermission;
import org.apache.harmony.luni.util.PriviAction;

/**
 * <strong>Warning:</strong> security managers do <strong>not</strong> provide a
 * secure environment for executing untrusted code. Untrusted code cannot be
 * safely isolated within the Dalvik VM.
 *
 * <p>Provides security verification facilities for applications. {@code
 * SecurityManager} contains a set of {@code checkXXX} methods which determine
 * if it is safe to perform a specific operation such as establishing network
 * connections, modifying files, and many more. In general, these methods simply
 * return if they allow the application to perform the operation; if an
 * operation is not allowed, then they throw a {@link SecurityException}. The
 * only exception is {@link #checkTopLevelWindow(Object)}, which returns a
 * boolean to indicate permission.
 */
public class SecurityManager {

    private static final PropertyPermission READ_WRITE_ALL_PROPERTIES_PERMISSION = new PropertyPermission(
            "*", "read,write");

    private static final String PKG_ACC_KEY = "package.access";

    private static final String PKG_DEF_KEY = "package.definition";

    /**
     * Flag to indicate whether a security check is in progress.
     *
     * @deprecated Use {@link #checkPermission}
     */
    @Deprecated
    protected boolean inCheck;

    /**
     * Constructs a new {@code SecurityManager} instance.
     * <p>
     * The {@code RuntimePermission("createSecurityManager")} is checked if a
     * security manager is installed.
     */
    public SecurityManager() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security
                    .checkPermission(RuntimePermission.permissionToCreateSecurityManager);
        }
        Class<?> type = Security.class; // initialize Security properties
        if (type == null) {
            throw new AssertionError();
        }
    }

    /**
     * Checks whether the calling thread is allowed to accept socket
     * connections.
     *
     * @param host
     *            the address of the host that attempts to connect.
     * @param port
     *            the port number to check.
     * @throws NullPointerException
     *             if {@code host} is {@code null}.
     * @throws SecurityException
     *             if the calling thread is not allowed to accept socket
     *             connections from {@code host} through {@code port}.
     */
    public void checkAccept(String host, int port) {
        if (host == null) {
            throw new NullPointerException();
        }
        checkPermission(new SocketPermission(host + ':' + port, "accept"));
    }

    /**
     * Checks whether the calling thread is allowed to modify the specified
     * thread.
     *
     * @param thread
     *            the thread to access.
     * @throws SecurityException
     *             if the calling thread is not allowed to access {@code thread}.
     */
    public void checkAccess(Thread thread) {
        // Only worry about system threads. Dead threads have a null group.
        ThreadGroup group = thread.getThreadGroup();
        if ((group != null) && (group.parent == null)) {
            checkPermission(RuntimePermission.permissionToModifyThread);
        }
    }

    /**
     * Checks whether the calling thread is allowed to modify the specified
     * thread group.
     *
     * @param group
     *            the thread group to access.
     * @throws NullPointerException
     *             if {@code group} is {@code null}.
     * @throws SecurityException
     *             if the calling thread is not allowed to access {@code group}.
     */
    public void checkAccess(ThreadGroup group) {
        // Only worry about system threads.
        if (group == null) {
            throw new NullPointerException();
        }
        if (group.parent == null) {
            checkPermission(RuntimePermission.permissionToModifyThreadGroup);
        }
    }

    /**
     * Checks whether the calling thread is allowed to establish socket
     * connections. A -1 port indicates the caller is trying to resolve the
     * hostname.
     *
     * @param host
     *            the address of the host to connect to.
     * @param port
     *            the port number to check, or -1 for resolve.
     * @throws NullPointerException
     *             if {@code host} is {@code null}.
     * @throws SecurityException
     *             if the calling thread is not allowed to connect to {@code
     *             host} through {@code port}.
     */
    public void checkConnect(String host, int port) {
        if (host == null) {
            throw new NullPointerException();
        }
        if (port > 0) {
            checkPermission(new SocketPermission(host + ':' + port, "connect"));
        } else {
            checkPermission(new SocketPermission(host, "resolve"));
        }
    }

    /**
     * Checks whether the specified security context is allowed to establish
     * socket connections. A -1 port indicates the caller is trying to resolve
     * the hostname.
     *
     * @param host
     *            the address of the host to connect to.
     * @param port
     *            the port number to check, or -1 for resolve.
     * @param context
     *            the security context to use for the check.
     * @throws NullPointerException
     *             if {@code host} is {@code null}.
     * @throws SecurityException
     *             if {@code context} is not allowed to connect to {@code host}
     *             through {@code port}.
     */
    public void checkConnect(String host, int port, Object context) {
        // BEGIN android-added
        if (host == null) {
            throw new NullPointerException();
        }
        // END android-added
        if (port > 0) {
            checkPermission(new SocketPermission(host + ':' + port, "connect"),
                    context);
        } else {
            checkPermission(new SocketPermission(host, "resolve"), context);
        }
    }

    /**
     * Checks whether the calling thread is allowed to create a class loader.
     *
     * @throws SecurityException
     *             if the calling thread is not allowed to create a class
     *             loader.
     */
    public void checkCreateClassLoader() {
        checkPermission(RuntimePermission.permissionToCreateClassLoader);
    }

    /**
     * Checks whether the calling thread is allowed to delete the file with the
     * specified name, which should be passed in canonical form.
     *
     * @param file
     *            the name of the file to delete.
     * @throws SecurityException
     *             if the calling thread is not allowed to delete {@code file}.
     */
    public void checkDelete(String file) {
        checkPermission(new FilePermission(file, "delete"));
    }

    /**
     * Checks whether the calling thread is allowed to execute the specified
     * platform specific command.
     *
     * @param cmd
     *            the command line to execute.
     * @throws SecurityException
     *             if the calling thread is not allowed to execute {@code cmd}.
     */
    public void checkExec(String cmd) {
        checkPermission(new FilePermission(new File(cmd).isAbsolute() ? cmd
                : "<<ALL FILES>>", "execute"));
    }

    /**
     * Checks whether the calling thread is allowed to terminate the virtual
     * machine.
     *
     * @param status
     *            the status that the virtual machine returns when it is
     *            terminated.
     * @throws SecurityException
     *             if the calling thread is not allowed to terminate the virtual
     *             machine with {@code status}.
     */
    public void checkExit(int status) {
        checkPermission(RuntimePermission.permissionToExitVM);
    }

    /**
     * Checks whether the calling thread is allowed to load the specified native
     * library.
     *
     * @param libName
     *            the name of the library to load.
     * @throws SecurityException
     *             if the calling thread is not allowed to load {@code libName}.
     */
    public void checkLink(String libName) {
        if (libName == null) {
            throw new NullPointerException();
        }
        checkPermission(new RuntimePermission("loadLibrary." + libName));
    }

    /**
     * Checks whether the calling thread is allowed to listen on the specified
     * port.
     *
     * @param port
     *            the port number to check.
     * @throws SecurityException
     *             if the calling thread is not allowed listen on {@code port}.
     */
    public void checkListen(int port) {
        if (port == 0) {
            checkPermission(new SocketPermission("localhost:1024-", "listen"));
        } else {
            checkPermission(new SocketPermission("localhost:" + port, "listen"));
        }
    }

    /**
     * Checks whether the calling thread is allowed to access members. The
     * default is to allow access to public members (that is, {@code
     * java.lang.reflect.Member.PUBLIC}) and to classes loaded by the same
     * loader as the original caller (that is, the method that called the
     * reflect API). Due to the nature of the check, overriding implementations
     * cannot call {@code super.checkMemberAccess()} since the stack would no
     * longer be of the expected shape.
     *
     * @param cls
     *            the class of which members are accessed.
     * @param type
     *            the access type, either {@code
     *            java.lang.reflect.Member.PUBLIC} or {@code
     *            java.lang.reflect.Member.DECLARED}.
     * @throws SecurityException
     *             if the calling thread is not allowed to access members of
     *             {@code cls}.
     */
    public void checkMemberAccess(Class<?> cls, int type) {
        if (cls == null) {
            throw new NullPointerException();
        }
        if (type == Member.PUBLIC) {
            return;
        }
        //
        // Need to compare the classloaders.
        // Stack shape is
        // <user code> <- want this class
        // Class.getDeclared*();
        // Class.checkMemberAccess();
        // SecurityManager.checkMemberAccess(); <- current frame
        //
        // Use getClassLoaderImpl() since getClassLoader()
        // returns null for the bootstrap class loader.
        if (ClassLoader.getStackClassLoader(3) == cls.getClassLoaderImpl()) {
            return;
        }

        // Forward off to the permission mechanism.
        checkPermission(new RuntimePermission("accessDeclaredMembers"));
    }

    /**
     * Checks whether the calling thread is allowed to use the specified IP
     * multicast group address.
     *
     * @param maddr
     *            the internet group address to use.
     * @throws SecurityException
     *             if the calling thread is not allowed to use {@code maddr}.
     */
    public void checkMulticast(InetAddress maddr) {
        checkPermission(new SocketPermission(maddr.getHostAddress(),
                "accept,connect"));
    }

    /**
     * Checks whether the calling thread is allowed to use the specified IP
     * multicast group address.
     *
     * @param maddr
     *            the internet group address to use.
     * @param ttl
     *            the value in use for multicast send. This parameter is
     *            ignored.
     * @throws SecurityException
     *             if the calling thread is not allowed to use {@code maddr}.
     * @deprecated use {@link #checkMulticast(java.net.InetAddress)}
     */
    @Deprecated
    public void checkMulticast(InetAddress maddr, byte ttl) {
        checkPermission(new SocketPermission(maddr.getHostAddress(),
                "accept,connect"));
    }

    /**
     * Checks whether the calling thread is allowed to access the specified
     * package.
     *
     * @param packageName
     *            the name of the package to access.
     * @throws SecurityException
     *             if the calling thread is not allowed to access {@code
     *             packageName}.
     */
    public void checkPackageAccess(String packageName) {
        if (packageName == null) {
            throw new NullPointerException();
        }
        if (checkPackageProperty(PKG_ACC_KEY, packageName)) {
            checkPermission(new RuntimePermission("accessClassInPackage."
                    + packageName));
        }
    }

    /**
     * Checks whether the calling thread is allowed to define new classes in the
     * specified package.
     *
     * @param packageName
     *            the name of the package to add a class to.
     * @throws SecurityException
     *             if the calling thread is not allowed to add classes to
     *             {@code packageName}.
     */
    public void checkPackageDefinition(String packageName) {
        if (packageName == null) {
            throw new NullPointerException();
        }
        if (checkPackageProperty(PKG_DEF_KEY, packageName)) {
            checkPermission(new RuntimePermission("defineClassInPackage."
                    + packageName));
        }
    }

    /**
     * Returns true if the package name is restricted by the specified security
     * property.
     */
    private static boolean checkPackageProperty(final String property,
            final String pkg) {
        String list = AccessController.doPrivileged(PriviAction
                .getSecurityProperty(property));
        if (list != null) {
            int plen = pkg.length();
            String[] tokens = list.split(", *");
            for (String token : tokens) {
                int tlen = token.length();
                if (plen > tlen
                        && pkg.startsWith(token)
                        && (token.charAt(tlen - 1) == '.' || pkg.charAt(tlen) == '.')) {
                    return true;
                } else if (plen == tlen && token.startsWith(pkg)) {
                    return true;
                } else if (plen + 1 == tlen && token.startsWith(pkg)
                        && token.charAt(tlen - 1) == '.') {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Checks whether the calling thread is allowed to access the system
     * properties.
     *
     * @throws SecurityException
     *             if the calling thread is not allowed to access system
     *             properties.
     */
    public void checkPropertiesAccess() {
        checkPermission(READ_WRITE_ALL_PROPERTIES_PERMISSION);
    }

    /**
     * Checks whether the calling thread is allowed to access a particular
     * system property.
     *
     * @param key
     *            the name of the property to access.
     * @throws SecurityException
     *             if the calling thread is not allowed to access the {@code
     *             key} system property.
     */
    public void checkPropertyAccess(String key) {
        checkPermission(new PropertyPermission(key, "read"));
    }

    /**
     * Checks whether the calling thread is allowed to read from the file with
     * the specified file descriptor.
     *
     * @param fd
     *            the file descriptor of the file to read from.
     * @throws SecurityException
     *             if the calling thread is not allowed to read from {@code fd}.
     */
    public void checkRead(FileDescriptor fd) {
        if (fd == null) {
            throw new NullPointerException();
        }
        checkPermission(RuntimePermission.permissionToReadFileDescriptor);
    }

    /**
     * Checks whether the calling thread is allowed to read from the file with
     * the specified name, which should be passed in canonical form.
     *
     * @param file
     *            the name of the file or directory to read from.
     * @throws SecurityException
     *             if the calling thread is not allowed to read from {@code
     *             file}.
     */
    public void checkRead(String file) {
        checkPermission(new FilePermission(file, "read"));
    }

    /**
     * Checks whether the given security context is allowed to read from the
     * file named by the argument, which should be passed in canonical form.
     *
     * @param file
     *            the name of the file or directory to check.
     * @param context
     *            the security context to use for the check.
     * @throws SecurityException
     *             if {@code context} is not allowed to read from {@code file}.
     */
    public void checkRead(String file, Object context) {
        checkPermission(new FilePermission(file, "read"), context);
    }

    /**
     * Checks whether the calling thread is allowed to perform the security
     * operation named by the target.
     *
     * @param target
     *            the name of the operation to perform.
     * @throws SecurityException
     *             if the calling thread is not allowed to perform
     *             {@code target}.
     */
    public void checkSecurityAccess(String target) {
        checkPermission(new SecurityPermission(target));
    }

    /**
     * Checks whether the calling thread is allowed to set the net object
     * factories.
     *
     * @throws SecurityException
     *             if the calling thread is not allowed to set the net object
     *             factories.
     */
    public void checkSetFactory() {
        checkPermission(RuntimePermission.permissionToSetFactory);
    }

    /**
     * Checks whether the calling thread is trusted to show the specified top
     * level window.
     *
     * @param window
     *            the window to show.
     * @return {@code true} if the calling thread is allowed to show {@code
     *         window}; {@code false} otherwise.
     * @throws NullPointerException
     *             if {@code window} is {@code null}.
     */
    public boolean checkTopLevelWindow(Object window) {
        if (window == null) {
            throw new NullPointerException();
        }
        try {
            Class<?> awtPermission = Class.forName("java.awt.AWTPermission");
            Constructor<?> constructor = awtPermission
                    .getConstructor(String.class);
            Object perm = constructor
                    .newInstance("showWindowWithoutWarningBanner");
            checkPermission((Permission) perm);
        } catch (ClassNotFoundException e) {
        } catch (NoSuchMethodException e) {
        } catch (InstantiationException e) {
        } catch (IllegalAccessException e) {
        } catch (InvocationTargetException e) {
        } catch (SecurityException e) {
            return false;
        }
        return true;
    }

    /**
     * Checks whether the calling thread is allowed to access the system
     * clipboard.
     *
     * @throws SecurityException
     *             if the calling thread is not allowed to access the system
     *             clipboard.
     */
    public void checkSystemClipboardAccess() {
        try {
            Class<?> awtPermission = Class.forName("java.awt.AWTPermission");
            Constructor<?> constructor = awtPermission
                    .getConstructor(String.class);
            Object perm = constructor.newInstance("accessClipboard");
            checkPermission((Permission) perm);
            return;
        } catch (ClassNotFoundException e) {
        } catch (NoSuchMethodException e) {
        } catch (InstantiationException e) {
        } catch (IllegalAccessException e) {
        } catch (InvocationTargetException e) {
        }
        throw new SecurityException();
    }

    /**
     * Checks whether the calling thread is allowed to access the AWT event
     * queue.
     *
     * @throws SecurityException
     *             if the calling thread is not allowed to access the AWT event
     *             queue.
     */
    public void checkAwtEventQueueAccess() {
        try {
            Class<?> awtPermission = Class.forName("java.awt.AWTPermission");
            Constructor<?> constructor = awtPermission
                    .getConstructor(String.class);
            Object perm = constructor.newInstance("accessEventQueue");
            checkPermission((Permission) perm);
            return;
        } catch (ClassNotFoundException e) {
        } catch (NoSuchMethodException e) {
        } catch (InstantiationException e) {
        } catch (IllegalAccessException e) {
        } catch (InvocationTargetException e) {
        }
        throw new SecurityException();
    }

    /**
     * Checks whether the calling thread is allowed to start a new print job.
     *
     * @throws SecurityException
     *             if the calling thread is not allowed to start a new print
     *             job.
     */
    public void checkPrintJobAccess() {
        checkPermission(RuntimePermission.permissionToQueuePrintJob);
    }

    /**
     * Checks whether the calling thread is allowed to write to the file with
     * the specified file descriptor.
     *
     * @param fd
     *            the file descriptor of the file to write to.
     * @throws SecurityException
     *             if the calling thread is not allowed to write to {@code fd}.
     */
    public void checkWrite(FileDescriptor fd) {
        if (fd == null) {
            throw new NullPointerException();
        }
        checkPermission(RuntimePermission.permissionToWriteFileDescriptor);
    }

    /**
     * Checks whether the calling thread is allowed to write to the file with
     * the specified name, which should be passed in canonical form.
     *
     * @param file
     *            the name of the file or directory to write to.
     * @throws SecurityException
     *             if the calling thread is not allowed to write to
     *             {@code file}.
     */
    public void checkWrite(String file) {
        checkPermission(new FilePermission(file, "write"));
    }

    /**
     * Indicates if this security manager is currently checking something.
     *
     * @return {@code true} if this security manager is executing a security
     *         check method; {@code false} otherwise.
     * @deprecated Use {@link #checkPermission}.
     */
    @Deprecated
    public boolean getInCheck() {
        return inCheck;
    }

    /**
     * Returns an array containing one entry for each method in the current
     * execution stack. Each entry is the {@code java.lang.Class} which
     * represents the class in which the method is defined.
     *
     * @return all classes in the execution stack.
     */
    @SuppressWarnings("unchecked")
    protected Class[] getClassContext() {
        return VMStack.getClasses(-1, false);
    }

    /**
     * Returns the class loader of the first class in the execution stack whose
     * class loader is not a system class loader.
     *
     * @return the most recent non-system class loader.
     * @deprecated Use {@link #checkPermission}.
     */
    @Deprecated
    protected ClassLoader currentClassLoader() {

        /*
         * First, check if AllPermission is allowed. If so, then we are
         * effectively running in an unsafe environment, so just answer null
         * (==> everything is a system class).
         */
        try {
            checkPermission(new AllPermission());
            return null;
        } catch (SecurityException ex) {
        }

        /*
         * Now, check if there are any non-system class loaders in the stack up
         * to the first privileged method (or the end of the stack.
         */
        Class<?>[] classes = Class.getStackClasses(-1, true);
        for (int i = 0; i < classes.length; i++) {
            ClassLoader cl = classes[i].getClassLoaderImpl();
            if (!cl.isSystemClassLoader()) {
                return cl;
            }
        }
        return null;
    }

    /**
     * Returns the index in the call stack of the first class whose class loader
     * is not a system class loader.
     *
     * @return the frame index of the first method whose class was loaded by a
     *         non-system class loader.
     * @deprecated Use {@link #checkPermission}.
     */
    @Deprecated
    protected int classLoaderDepth() {
        /*
         * First, check if AllPermission is allowed. If so, then we are
         * effectively running in an unsafe environment, so just answer -1 (==>
         * everything is a system class).
         */
        try {
            checkPermission(new AllPermission());
            return -1;
        } catch (SecurityException ex) {
        }

        /*
         * Now, check if there are any non-system class loaders in the stack up
         * to the first privileged method (or the end of the stack.
         */
        Class<?>[] classes = Class.getStackClasses(-1, true);
        for (int i = 0; i < classes.length; i++) {
            ClassLoader cl = classes[i].getClassLoaderImpl();
            if (!cl.isSystemClassLoader()) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Returns the first class in the call stack that was loaded by a class
     * loader which is not a system class loader.
     *
     * @return the most recent class loaded by a non-system class loader.
     * @deprecated Use {@link #checkPermission}.
     */
    @Deprecated
    protected Class<?> currentLoadedClass() {
        /*
         * First, check if AllPermission is allowed. If so, then we are
         * effectively running in an unsafe environment, so just answer null
         * (==> everything is a system class).
         */
        try {
            checkPermission(new AllPermission());
            return null;
        } catch (SecurityException ex) {
        }

        /*
         * Now, check if there are any non-system class loaders in the stack up
         * to the first privileged method (or the end of the stack.
         */
        Class<?>[] classes = Class.getStackClasses(-1, true);
        for (int i = 0; i < classes.length; i++) {
            ClassLoader cl = classes[i].getClassLoaderImpl();
            if (!cl.isSystemClassLoader()) {
                return classes[i];
            }
        }
        return null;
    }

    /**
     * Returns the index in the call stack of the first method which is
     * contained in the class with the specified name. Returns -1 if no methods
     * from this class are in the stack.
     *
     * @param name
     *            the name of the class to look for.
     * @return the frame index of the first method found is contained in the
     *         class identified by {@code name}.
     * @deprecated Use {@link #checkPermission}.
     */
    @Deprecated
    protected int classDepth(String name) {
        Class<?>[] classes = Class.getStackClasses(-1, false);
        for (int i = 0; i < classes.length; i++) {
            if (classes[i].getName().equals(name)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Indicates whether there is a method in the call stack from the class with
     * the specified name.
     *
     * @param name
     *            the name of the class to look for.
     * @return {@code true} if a method from the class identified by {@code
     *         name} is executing; {@code false} otherwise.
     * @deprecated Use {@link #checkPermission}.
     */
    @Deprecated
    protected boolean inClass(String name) {
        return classDepth(name) != -1;
    }

    /**
     * Indicates whether there is a method in the call stack from a class which
     * was defined by a non-system class loader.
     *
     * @return {@code true} if a method from a class that was defined by a
     *         non-system class loader is executing; {@code false} otherwise.
     * @deprecated Use {@link #checkPermission}
     */
    @Deprecated
    protected boolean inClassLoader() {
        return currentClassLoader() != null;
    }

    /**
     * Returns the thread group which should be used to instantiate new threads.
     * By default, this is the same as the thread group of the thread running
     * this method.
     *
     * @return ThreadGroup the thread group to create new threads in.
     */
    public ThreadGroup getThreadGroup() {
        return Thread.currentThread().getThreadGroup();
    }

    /**
     * Returns an object which encapsulates the security state of the current
     * point in the execution. In our case, this is an {@link
     * java.security.AccessControlContext}.
     *
     * @return an object that encapsulates information about the current
     *         execution environment.
     */
    public Object getSecurityContext() {
        return AccessController.getContext();
    }

    /**
     * Checks whether the calling thread is allowed to access the resource being
     * guarded by the specified permission object.
     *
     * @param permission
     *            the permission to check.
     * @throws SecurityException
     *             if the requested {@code permission} is denied according to
     *             the current security policy.
     */
    public void checkPermission(Permission permission) {
        try {
            inCheck = true;
            AccessController.checkPermission(permission);
        } finally {
            inCheck = false;
        }
    }

    /**
     * Checks whether the specified security context is allowed to access the
     * resource being guarded by the specified permission object.
     *
     * @param permission
     *            the permission to check.
     * @param context
     *            the security context for which to check permission.
     * @throws SecurityException
     *             if {@code context} is not an instance of {@code
     *             AccessControlContext} or if the requested {@code permission}
     *             is denied for {@code context} according to the current
     *             security policy.
     */
    public void checkPermission(Permission permission, Object context) {
        try {
            inCheck = true;
            // Must be an AccessControlContext. If we don't check
            // this, then applications could pass in an arbitrary
            // object which circumvents the security check.
            if (context instanceof AccessControlContext) {
                ((AccessControlContext) context).checkPermission(permission);
            } else {
                throw new SecurityException();
            }
        } finally {
            inCheck = false;
        }
    }
}
