Oracle, Android and the copy claims: SCO all over again?


Second important update: After quite some digging, I have finally found out that the file had the following history:

  • An initial import, from a private branch to the public git repo, with this commitdiff, at Sat, 10 Jan 2009 01:50:54
  • A deletion of the file and of most of the imported branch at Wed, 4 Mar 2009 02:28:14, with this commitdiff
  • The branch got re-imported, with all new files, at Wed, 4 Mar 2009 03:28:47, with this commitdiff, without PolicyNodeImpl.java

So the file was present in the git repo from Jan, 10 to March, 4; after checking the test execution code, it is clear that the code itself was not included in the final build (delivered to handsets) but was part of a test harness, that (funnily) was mostly silenced during the development period due to the majority of tests failing :-) It is however clear that the code itself was distributed, as it was freely accessible online and through the git tree (and – funnily – it is still available under the same means). It was, however, not part of the Android SDK release, as Android 1.6 rel2 was released in December 2009, while rel3 was released in May, with the commit diff already applied.

Important update: I have verified that the code included in Android is actually a decompilation of an old Java 1.5 class file, and my own comparison is invalid, as it was done with a recent Java edition (that does have more changes – thus suggesting a reimplementation). Also, it seems that Harmony actually has not that code in its repo – despite the fact that in Android is part of the initial import under the “Harmony” subproject. My apologies.

Sigh. I had higher hopes for Oracle – while the initial complaint was dull and unsubstantial, the claims of actual copying of source code was an interesting twist (apart from the patents, of course – but that is a totally different world). Now, as ZDnet promptly writes, Oracle amended the complaint, and published the scribd link with the entire text (by the way: the HTML5 version of Scribd really, really rocks. End of parenthesis.) The relevant parts are:

40. Android includes infringing class libraries and documentation. Approximately one third of Android’s Application Programmer Interface (API) packages (available at http://developer.android.com/reference/packages.html) are derivative of Oracle America’s copyrighted Java API packages (available at http://download-llnw.oracle.com/javase/1.5.0/-docs/api/ and http://download-llnw.oracle.com/javase/1.4.2/docs/api/) and corresponding documents. The infringed elements of Oracle America’s copyrighted work include Java method and class names, definitions, organization, and parameters; the structure, organization and content of Java class libraries; and the content and organization of Java’s documentation. Examples of this copying are illustrated in Exhibit I to this complaint. In at least several instances, Android computer program code also was directly copied from copyrighted Oracle America code. For example, as may be readily seen in Exhibit J, the source code in Android’s “PolicyNodeImpl.java” class is nearly identical to “PolicyNodeImpl.java” in Oracle America’s Java, not just in name, but in the source code on a line-for-line basis.”

Hm. First of all, the definition of line-for-line equality is not correct here, as the lines are different (but quite similar). I have not developed for quite some time, but I would say that it is not strange to see similarities within the API constraints. Second, while technically part of Android, the code is Apache Harmony, a reimplementation of J2SE that tried (for many, many years) to get the compliance toolkit from Sun, but never did (and now will never do).

But the relevant point is different: the PolicyNodeImpl.java that is presented comes from the OpenJDK distribution, and was as such released under the GPL+ClassPath exception (something that is not mentioned anywhere within the complaint, by the way). Here, the claims are two and different: the first is that Android (actually, Harmony) copied its API that Oracle claims is copyrighted. The second claim is that the actual source code of the PolicyNodeImpl.java file has been copied verbatim.

Let’s start with the first one: the claim that Oracle Java APIs are protected and copyrighted. On this, it seem to me that the interface definition themselves (not the actual source code) as a mere interface does not fall within the copyright provisions, unless the actual names are trademarked, and thus its implementation requires the actual copying of a protected name in a way that is deemed incompatible by its licensee (something similar was done by Autodesk, embedding a copyrighted phrase that if not included in the file prevented the application from opening it directly). I believe that this is not the case, as the names within the source code seem to lack any “TM” or the direct use of “Java” in a way that may suggest its direct protection as a trademark.I(t should be noted that both “Java” and “OpenJDK” are trademarked, and its use is explicitly forbidden unless the code is a

And now, for the claim that the code is actually copied, here is in its glory the full diff (note: if you download the OpenJDK source you don’t get the file; I had to grab it from here in the Mercurial publication site, the raw file is this one). It seem to me that this is actually a reimplementation and not a straight copy as Oracle claims – but I would like to ask my readers for their opinion.

A disclaimer: I am not a Oracle hater, a Google or Apache fanboy, and I have no relationship with them all.

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or moreCopyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
 *  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 atDO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 *     http://www.apache.org/licenses/LICENSE-2.0This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the LicenseThis code is distributed on an "AS IS" BASIS,in the hope that it will be useful, but WITHOUT
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressWARRANTY; without even the implied warranty of MERCHANTABILITY or implied.
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the specific language governing permissionsLICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package sun.security.provider.certpath;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.security.cert.*;
/**
 * Implements the <code>PolicyNode</code> interface.
 * <p>
 * This class provides an implementation of the <code>PolicyNode</code>
 * interface, and is used internally to build and search Policy Trees.
 *  limitations underWhile the License.implementation is mutable during construction, it is immutable
 * before returning to a client and no mutable public or protected methods
 * are exposed by this implementation, as per the contract of PolicyNode.
 *
 * @since       1.4
 * @author      Seth Proctor
 * @author      Sean Mullan
 */
package org.apache.harmony.security.tests.support.cert;
import java.security.cert.PolicyNode;
import java.util.*;
publicfinal class PolicyNodeImpl implements PolicyNode {
    /**
     * Use to specify the special policy "Any Policy"
     */
    private static final String ANY_POLICY = "2.5.29.32.0";
    // every node has one parent, and zero or more children
    private PolicyNodeImpl mParent;
    private HashSetHashSet<PolicyNodeImpl> mChildren;
    // the 4 fields specified by RFC 3280
    private String mValidPolicy;
    private HashSetHashSet<PolicyQualifierInfo> mQualifierSet;
    private boolean mCriticalityIndicator;
    private HashSetHashSet<String> mExpectedPolicySet;
    private boolean mOriginalExpectedPolicySet;
    // the tree depth
    private int mDepth;
    private// immutability flag
    private boolean isImmutable;
    public PolicyNodeImpl(PolicyNodeImpl policynodeimpl,isImmutable = false;
    /**
     * Constructor which takes a <code>PolicyNodeImpl</code> representing the
     * parent in the Policy Tree to this node. If null, this is the
     * root of the tree. The constructor also takes the associated data
     * for this node, as found in the certificate. It also takes a boolean
     * argument specifying whether this node is being created as a result
     * of policy mapping.
     *
     * @param parent the PolicyNode above this in the tree, or null if this
     *               node is the tree's root node
     * @param validPolicy a String s,representing this node's valid policy OID
     * @param qualifierSet the Set set,
                   boolean flag,of qualifiers for this policy
     * @param criticalityIndicator a boolean representing whether or not the
     *                             extension is critical
     * @param expectedPolicySet a Set set1,of expected policies
     * @param generatedByPolicyMapping a boolean flag1)indicating whether this
     * node was generated by a policy mapping
     */
    PolicyNodeImpl(PolicyNodeImpl parent, String validPolicy,
                Set<PolicyQualifierInfo> qualifierSet,
                boolean criticalityIndicator, Set<String> expectedPolicySet,
                boolean generatedByPolicyMapping) {
        isImmutable = false;
        mParent = policynodeimpl;parent;
        mChildren = new HashSet();
        if(sHashSet<PolicyNodeImpl>();
        if (validPolicy != null) {
            mValidPolicy = s;
        } else {validPolicy;
        else
            mValidPolicy = "";
        }
        if(setif (qualifierSet != null) {
            mQualifierSet = new HashSet(set);
        } else {HashSet<PolicyQualifierInfo>(qualifierSet);
        else
            mQualifierSet = new HashSet();
        }HashSet<PolicyQualifierInfo>();
        mCriticalityIndicator = flag;
        if(set1criticalityIndicator;
        if (expectedPolicySet != null) {
            mExpectedPolicySet = new HashSet(set1);
        } else {HashSet<String>(expectedPolicySet);
        else
            mExpectedPolicySet = new HashSet();
        }HashSet<String>();
        mOriginalExpectedPolicySet = !flag1;
        if(mParent!generatedByPolicyMapping;
        // see if we're the root, and act appropriately
        if (mParent != null) {
            mDepth = mParent.getDepth() + 1;
            mParent.addChild(this);
        } else {
            mDepth = 0;
        }
    }
    PolicyNodeImpl(PolicyNodeImpl policynodeimpl,
                   PolicyNodeImpl policynodeimpl1)/**
     * Alternate constructor which makes a new node with the policy data
     * in an existing <code>PolicyNodeImpl</code>.
     *
     * @param parent a PolicyNode that's the new parent of the node, or
     *               null if this is the root node
     * @param node a PolicyNode containing the policy data to copy
     */
    PolicyNodeImpl(PolicyNodeImpl parent, PolicyNodeImpl node) {
        this(policynodeimpl, policynodeimpl1.mValidPolicy, ((Set) (policynodeimpl1.mQualifierSet)), policynodeimpl1.mCriticalityIndicator, ((Set) (policynodeimpl1.mExpectedPolicySet)),
        this(parent, node.mValidPolicy, node.mQualifierSet,
             node.mCriticalityIndicator, node.mExpectedPolicySet, false);
    }
    public PolicyNode getParent() {
        return mParent;
    }
    public IteratorIterator<PolicyNodeImpl> getChildren() {
        return Collections.unmodifiableSet(mChildren).iterator();
    }
    public int getDepth() {
        return mDepth;
    }
    public String getValidPolicy() {
        return mValidPolicy;
    }
    public SetSet<PolicyQualifierInfo> getPolicyQualifiers() {
        return Collections.unmodifiableSet(mQualifierSet);
    }
    public SetSet<String> getExpectedPolicies() {
        return Collections.unmodifiableSet(mExpectedPolicySet);
    }
    public boolean isCritical() {
        return mCriticalityIndicator;
    }
    /**
     * Return a printable representation of the PolicyNode.
     * Starting at the node on which this method is called,
     * it recurses through the tree and prints out each node.
     *
     * @return a String describing the contents of the Policy Node
     */
    public String toString() {
        StringBuffer stringbufferbuffer = new StringBuffer(asString());
        for(Iterator iteratorStringBuffer(this.asString());
        Iterator<PolicyNodeImpl> it = getChildren(); iterator.hasNext(); stringbuffer.append((PolicyNodeImpl)iterator.next()));
        while (it.hasNext()) {
            buffer.append(it.next());
        }
        return stringbuffer.toString();buffer.toString();
    }
    // private methods and package private operations
    boolean isImmutable() {
        return isImmutable;
    }
    /**
     * Sets the immutability flag of this node and all of its children
     * to true.
     */
    void setImmutable() {
        if(isImmutable)  return;
        PolicyNodeImpl policynodeimpl;
        for(Iterator iterator = mChildren.iterator(); iterator.hasNext(); policynodeimpl.setImmutable())
            policynodeimpl = (PolicyNodeImpl)iterator.next();if (isImmutable)
            return;
        for (PolicyNodeImpl node : mChildren) {
            node.setImmutable();
        }
        isImmutable = true;
    }
    private/**
     * Private method sets a child node. This is called from the child's
     * constructor.
     *
     * @param child new <code>PolicyNodeImpl</code> child node
     */
    private void addChild(PolicyNodeImpl policynodeimpl)child) {
        if(isImmutable)
        if (isImmutable) {
            throw new IllegalStateException("PolicyNode is immutable");
        } else {
            mChildren.add(policynodeimpl);
            return;
        }
        mChildren.add(child);
    }
    void/**
     * Adds an expectedPolicy to the expected policy set.
     * If this is the original expected policy set initialized
     * by the constructor, then the expected policy set is cleared
     * before the expected policy is added.
     *
     * @param expectedPolicy a String representing an expected policy.
     */
    void addExpectedPolicy(String s)expectedPolicy) {
        if (isImmutable) {
        if(isImmutable)
            throw new IllegalStateException("PolicyNode is immutable");
        if(mOriginalExpectedPolicySet)}
        if (mOriginalExpectedPolicySet) {
            mExpectedPolicySet.clear();
            mOriginalExpectedPolicySet = false;
        }
        mExpectedPolicySet.add(s);mExpectedPolicySet.add(expectedPolicy);
    }
    void/**
     * Removes all paths which don't reach the specified depth.
     *
     * @param depth an int representing the desired minimum depth of all paths
     */
    void prune(int i)depth) {
        if(isImmutable)
        if (isImmutable)
            throw new IllegalStateException("PolicyNode is immutable");
        if(mChildren.size()// if we have no children, we can't prune below us...
        if (mChildren.size() == 0)
            return;
        Iterator iteratorIterator<PolicyNodeImpl> it = mChildren.iterator();
        do
        while (it.hasNext()) {
            if(!iterator.hasNext())  break;
            PolicyNodeImpl policynodeimplnode = (PolicyNodeImpl)iterator.next();
            policynodeimpl.prune(i);
            if(policynodeimpl.mChildren.size()it.next();
            node.prune(depth);
            // now that we've called prune on the child, see if we should
            // remove it from the tree
            if ((node.mChildren.size() == 00) && i(depth > mDepth + 1)
                iterator.remove();1))
                it.remove();
        } while(true);
    }
    void/**
     * Deletes the specified child node of this node, if it exists.
     *
     * @param childNode the child node to be deleted
     */
    void deleteChild(PolicyNode policynode)childNode) {
        if(isImmutable)
        if (isImmutable) {
            throw new IllegalStateException("PolicyNode is immutable");
        } else {
            mChildren.remove(policynode);
            return;
        }
        mChildren.remove(childNode);
    }
    /**
     * Returns a copy of the tree, without copying the policy-related data,
     * rooted at the node on which this was called.
     *
     * @return a copy of the tree
     */
    PolicyNodeImpl copyTree() {
        return copyTree(null);
    }
    private PolicyNodeImpl copyTree(PolicyNodeImpl policynodeimpl)parent) {
        PolicyNodeImpl policynodeimpl1newNode = new PolicyNodeImpl(policynodeimpl,PolicyNodeImpl(parent, this);
        PolicyNodeImpl policynodeimpl2;
        for(Iterator iterator = mChildren.iterator(); iterator.hasNext(); policynodeimpl2.copyTree(policynodeimpl1))
            policynodeimpl2 = (PolicyNodeImpl)iterator.next();
        for (PolicyNodeImpl node : mChildren) {
            node.copyTree(newNode);
        }
        return policynodeimpl1;newNode;
    }
    Set/**
     * Returns all nodes at the specified depth in the tree.
     *
     * @param depth an int representing the depth of the desired nodes
     * @return a <code>Set</code> of all nodes at the specified depth
     */
    Set<PolicyNodeImpl> getPolicyNodes(int i)depth) {
        HashSet hashset
        Set<PolicyNodeImpl> set = new HashSet();
        getPolicyNodes(i, ((Set) (hashset)));HashSet<PolicyNodeImpl>();
        getPolicyNodes(depth, set);
        return hashset;set;
    }
    private/**
     * Add all nodes at depth depth to set and return the Set.
     * Internal recursion helper.
     */
    private void getPolicyNodes(int i, Setdepth, Set<PolicyNodeImpl> set) {
        if(mDepth
        // if we've reached the desired depth, then return ourself
        if (mDepth == i)depth) {
            set.add(this);
        } else {
            PolicyNodeImpl policynodeimpl;
            for(Iterator iterator = mChildren.iterator(); iterator.hasNext(); policynodeimpl.getPolicyNodes(i, set))
                policynodeimpl = (PolicyNodeImpl)iterator.next();for (PolicyNodeImpl node : mChildren) {
                node.getPolicyNodes(depth, set);
            }
        }
    }
    Set getPolicyNodesExpected(int i,/**
     * Finds all nodes at the specified depth whose expected_policy_set
     * contains the specified expected OID (if matchAny is false)
     * or the special OID "any value" (if matchAny is true).
     *
     * @param depth an int representing the desired depth
     * @param expectedOID a String s,encoding the valid OID to match
     * @param matchAny a boolean flag)indicating whether an expected_policy_set
     * containing ANY_POLICY should be considered a match
     * @return a Set of matched <code>PolicyNode</code>s
     */
    Set<PolicyNodeImpl> getPolicyNodesExpected(int depth,
        String expectedOID, boolean matchAny) {
        if (expectedOID.equals(ANY_POLICY)) {
        if(s.equals("2.5.29.32.0"))
            return getPolicyNodes(i);
        elsegetPolicyNodes(depth);
        } else {
            return getPolicyNodesExpectedHelper(i, s, flag);getPolicyNodesExpectedHelper(depth, expectedOID, matchAny);
        }
    }
    private SetSet<PolicyNodeImpl> getPolicyNodesExpectedHelper(int i, String s,depth,
        String expectedOID, boolean flag)matchAny) {
        HashSet hashset
        HashSet<PolicyNodeImpl> set = new HashSet();
        if(mDepthHashSet<PolicyNodeImpl>();
        if (mDepth < i)depth) {
            PolicyNodeImpl policynodeimpl;
            for(Iterator iterator = mChildren.iterator(); iterator.hasNext(); hashset.addAll(policynodeimpl.getPolicyNodesExpectedHelper(i, s, flag)))
                policynodeimpl = (PolicyNodeImpl)iterator.next();
            for (PolicyNodeImpl node : mChildren) {
                set.addAll(node.getPolicyNodesExpectedHelper(depth,
                                                             expectedOID,
                                                             matchAny));
            }
        } else if(flag) {
            if(mExpectedPolicySet.contains("2.5.29.32.0"))
                hashset.add(this);
        }
            if (matchAny) {
                if (mExpectedPolicySet.contains(ANY_POLICY))
                    set.add(this);
            } else if(mExpectedPolicySet.contains(s)) {
            hashset.add(this);
                if (mExpectedPolicySet.contains(expectedOID))
                    set.add(this);
            }
        }
        return hashset;set;
    }
    Set/**
     * Finds all nodes at the specified depth that contains the
     * specified valid OID
     *
     * @param depth an int representing the desired depth
     * @param validOID a String encoding the valid OID to match
     * @return a Set of matched <code>PolicyNode</code>s
     */
    Set<PolicyNodeImpl> getPolicyNodesValid(int i,depth, String s)validOID) {
        HashSet hashset
        HashSet<PolicyNodeImpl> set = new HashSet();
        if(mDepthHashSet<PolicyNodeImpl>();
        if (mDepth < i)depth) {
            PolicyNodeImpl policynodeimpl;
            for(Iterator iterator = mChildren.iterator(); iterator.hasNext(); hashset.addAll(policynodeimpl.getPolicyNodesValid(i, s)))
                policynodeimpl = (PolicyNodeImpl)iterator.next();
            for (PolicyNodeImpl node : mChildren) {
                set.addAll(node.getPolicyNodesValid(depth, validOID));
            }
        } else if(mValidPolicy.equals(s)) {
            hashset.add(this);
            if (mValidPolicy.equals(validOID))
                set.add(this);
        }
        return hashset;set;
    }
    private static String policyToString(String s)oid) {
        if(s.equals("2.5.29.32.0"))
        if (oid.equals(ANY_POLICY)) {
            return "anyPolicy";
        } else {
            return s;oid;
        }
    }
    /**
     * Prints out some data on this node.
     */
    String asString() {
        if(mParentif (mParent == null) {
            return "anyPolicy  ROOT\n";
        StringBuffer stringbuffer} else {
            StringBuffer sb = new StringBuffer();
        int
            for (int i = 0;
        for(int j0, n = getDepth(); i < j;n; i++)
            stringbuffer.append(" {
                sb.append("  ");
        stringbuffer.append(policyToString(getValidPolicy()));
        stringbuffer.append("
            }
            sb.append(policyToString(getValidPolicy()));
            sb.append("  CRIT: ");
        stringbuffer.append(isCritical());
        stringbuffer.append("
            sb.append(isCritical());
            sb.append("  EP: ");
        for(Iterator iterator = getExpectedPolicies().iterator(); iterator.hasNext(); stringbuffer.append(" "))
            for (String policy : getExpectedPolicies()) {
            String s = (String)iterator.next();
            stringbuffer.append(policyToString(s));
                sb.append(policyToString(policy));
                sb.append(" ");
            }
            sb.append(" (");
            sb.append(getDepth());
            sb.append(")\n");
            return sb.toString();
        }
        stringbuffer.append(" (");
        stringbuffer.append(getDepth());
        stringbuffer.append(")\n");
        return stringbuffer.toString();
    }
}

,

  1. #1 by Petter - October 28th, 2010 at 23:31

    The Harmony code is probably IBM donated code from their own JVM and class library – J9. So any if at all infringment is done by IBM, which has written large parts of Hotspot/OpenJDK too, so the ownership issue won’t be easy. Java or Hotspot isn’t a proprietary closed developed JVM it’s developed by the industry since the 90′s no single vendor. Code comes from a lot of guys. An IBM reimplementation a partner with good status as a licensee and developer of Java probably isn’t wrong at all. When it comes to reimplementations we wouldn’t even have PC’s if they couldn’t get around the licensing issue with IBM BIOS by doing their implementations in a clean room environment. But with Java it’s quite a difference since there are so many developers JVMs and licensees which have right to it all.

  2. #2 by Kieran Elby - October 28th, 2010 at 23:35

    The Harmony/Android version was almost certainly generated by decompiling the Oracle/Sun class file.

    You can see this for yourself with the following steps:

    Install the Oracle JRE.
    Locate the lib\rt.jar file.
    Extract the sun\security\provider\certpath\PolicyNodeImpl.class from the jar file.
    Run jad v1.5.8g (from http://www.varaneckas.com/jad) on that file with the -ff and -nonlb options.
    The resulting source is identical to the Harmony implementation (barring some minor whitespace / bracing differences).

  3. #3 by Bobby Powers - October 29th, 2010 at 00:45

    you are confusing copyright and trademark. The GPL _depends_ on copyright to work; something released under the GPL is necessarily copyrighted (look at the top of Oracle version – it starts with a copyright notice). http://en.wikipedia.org/wiki/Software_copyright seems like a decent jumping off point for more info.

  4. #4 by Alan - October 29th, 2010 at 03:42

    Thanks for doing the diff. The output in my browser (Chrome) looks strange. It may be useful to describe what we’re seeing (strikethrough vs. non-strikethrough text) for those non-technical people.

  5. #5 by trashhalo - October 29th, 2010 at 05:48

    Some people over at reddit have pointed out that the harmony version is a very close match to what comes out of the java decompiler (jad)

    http://www.reddit.com/r/programming/comments/dxmvr/oracle_google_directly_copied_our_java_code/c13qfov

  6. #6 by Paul Hands - October 29th, 2010 at 12:18

    Hi,

    A bit OT, but the page renders really badly, with the Java code getting mixed up with the Twitter sidebar. I tried it on Firefox, Google Chrome and Konqueror on Linux, and IE on windows, all with the same problem.

  7. #7 by Robin - October 29th, 2010 at 16:02

    The diff is useless because it is very influenced by returns and spacing and nameshortening

    For example your diff show these 4 lines of code
    sb.append(” (“);
    sb.append(getDepth());
    sb.append(“)\n”);
    return sb.toString();

    to be completly different to this code
    stringbuffer.append(” (“);
    stringbuffer.append(getDepth());
    stringbuffer.append(“)\n”);
    return stringbuffer.toString();

    whereas a human comparison shows it to be virtually identical sourcecode where only “stringbuffer” is shortened to “sb”

    So your diff makes it look A lot more different than it actually is.

  8. #8 by Keith - October 29th, 2010 at 19:14

    Apparently PolicyNodeImpl.java is not a Harmony class:

    https://blogs.apache.org/foundation/entry/read_beyond_the_headers

  9. #9 by Mike Amling - October 29th, 2010 at 19:33

    The blog posting has a link to the OpenJDK source file. Where is the Harmony source file?

  10. #10 by Mike Amling - October 29th, 2010 at 19:42

    The blog posting has a link to the OpenJDK source file. Where is the Android source file? (I previously asked where the “Harmony” source file is, duh. That was an error because Harmony does not include PolicyNodeImpl.)

  11. #11 by Jeff - October 29th, 2010 at 22:42

  12. #12 by cdaffara - October 30th, 2010 at 17:12

    One reply for all my commenters: you are right – it seems that the code is not from Harmony, but got included by Google within the Harmony project tree of Android 1.6 (it is not there in the later versions). It may be code that was submitted to harmony but not accepted – I don’t know. It is however true that it is not in harmony, and that is generated from a decompiler. I have updated the article to correct my post.

  13. #13 by dog - November 1st, 2010 at 23:45

    Ok.. I have quite a bit of experience dealing w/copies in the context of classrooms. So this is what I can glean from this code diff:

    1. One version does seem to be derived from the other.
    2. It is somewhat suspicious that the methods appear exactly in the same order
    3. It is very suspicious that members like mChildren are exactly the same in both versions.
    4. Both seem to have EXACTLY the same number of member variables.. also very suspicious
    5. There seems to be transformation from non-generic to generic styles.

    Conclusion: The structures seem to be suspiciously similar in more than coincidental ways. I’m 60% certain that one version was derived from the other. I’d look for more “coincidences” elsewhere.. If there are too many I’d say the implementation is not cleanroom.

  14. #14 by Alrick - November 16th, 2010 at 06:33

    Isn’t the “PolicyNodeImpl.java” calss apart of a unit test? Does Google include it in the final build of Android at all?

  15. #15 by cdaffara - November 16th, 2010 at 08:02

    No, it is not included in the final build. In fact, I believe that the claims that the code got included in phones is bogus.
    It is true, however, that the code is made available through the Android GIT web site.

(will not be published)