Add new setTag(int, Object) API to allow applications to specify several tags.
diff --git a/api/current.xml b/api/current.xml
index 509abe1..c222d42 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -135944,6 +135944,19 @@
  visibility="public"
 >
 </method>
+<method name="getTag"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="int">
+</parameter>
+</method>
 <method name="getTop"
  return="int"
  abstract="false"
@@ -137843,6 +137856,21 @@
 <parameter name="tag" type="java.lang.Object">
 </parameter>
 </method>
+<method name="setTag"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="int">
+</parameter>
+<parameter name="tag" type="java.lang.Object">
+</parameter>
+</method>
 <method name="setTouchDelegate"
  return="void"
  abstract="false"
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 07c56ee..d042f28d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -61,6 +61,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.WeakHashMap;
 import java.lang.ref.SoftReference;
 import java.lang.reflect.Method;
 import java.lang.reflect.InvocationTargetException;
@@ -1287,7 +1288,17 @@
      * a Rect. :)
      */
     static final ThreadLocal<Rect> sThreadLocal = new ThreadLocal<Rect>();
-    
+
+    /**
+     * Map used to store views' tags.
+     */
+    private static WeakHashMap<View, SparseArray<Object>> sTags;
+
+    /**
+     * Lock used to access sTags.
+     */
+    private static final Object sTagsLock = new Object();
+
     /**
      * The animation currently associated with this view.
      * @hide
@@ -7000,6 +7011,9 @@
      * Returns this view's tag.
      *
      * @return the Object stored in this view as a tag
+     *
+     * @see #setTag(Object)
+     * @see #getTag(int)
      */
     @ViewDebug.ExportedProperty
     public Object getTag() {
@@ -7013,12 +7027,102 @@
      * resorting to another data structure.
      *
      * @param tag an Object to tag the view with
+     *
+     * @see #getTag()
+     * @see #setTag(int, Object)
      */
     public void setTag(final Object tag) {
         mTag = tag;
     }
 
     /**
+     * Returns the tag associated with this view and the specified key.
+     *
+     * @param key The key identifying the tag
+     *
+     * @return the Object stored in this view as a tag
+     *
+     * @see #setTag(int, Object)
+     * @see #getTag() 
+     */
+    public Object getTag(int key) {
+        SparseArray<Object> tags = null;
+        synchronized (sTagsLock) {
+            if (sTags != null) {
+                tags = sTags.get(this);
+            }
+        }
+
+        if (tags != null) return tags.get(key);
+        return null;
+    }
+
+    /**
+     * Sets a tag associated with this view and a key. A tag can be used
+     * to mark a view in its hierarchy and does not have to be unique within
+     * the hierarchy. Tags can also be used to store data within a view
+     * without resorting to another data structure.
+     *
+     * The specified key should be an id declared in the resources of the
+     * application to ensure it is unique. Keys identified as belonging to
+     * the Android framework or not associated with any package will cause
+     * an {@link IllegalArgumentException} to be thrown.
+     *
+     * @param key The key identifying the tag
+     * @param tag An Object to tag the view with
+     *
+     * @throws IllegalArgumentException If they specified key is not valid
+     *
+     * @see #setTag(Object)
+     * @see #getTag(int)
+     */
+    public void setTag(int key, final Object tag) {
+        // If the package id is 0x00 or 0x01, it's either an undefined package
+        // or a framework id
+        if ((key >>> 24) < 2) {
+            throw new IllegalArgumentException("The key must be an application-specific "
+                    + "resource id.");
+        }
+
+        setTagInternal(this, key, tag);
+    }
+
+    /**
+     * Variation of {@link #setTag(int, Object)} that enforces the key to be a
+     * framework id.
+     *
+     * @hide
+     */
+    public void setTagInternal(int key, Object tag) {
+        if ((key >>> 24) != 0x1) {
+            throw new IllegalArgumentException("The key must be a framework-specific "
+                    + "resource id.");
+        }
+
+        setTagInternal(this, key, tag);        
+    }
+
+    private static void setTagInternal(View view, int key, Object tag) {
+        SparseArray<Object> tags = null;
+        synchronized (sTagsLock) {
+            if (sTags == null) {
+                sTags = new WeakHashMap<View, SparseArray<Object>>();
+            } else {
+                tags = sTags.get(view);
+            }
+        }
+
+        if (tags == null) {
+            tags = new SparseArray<Object>(2);
+            synchronized (sTagsLock) {
+                sTags.put(view, tags);
+            }
+        }
+
+        tags.put(key, tag);
+    }
+
+    /**
      * Prints information about this view in the log output, with the tag
      * {@link #VIEW_LOG_TAG}.
      *
diff --git a/tests/FrameworkTest/tests/src/com/android/frameworktest/view/SetTagsTest.java b/tests/FrameworkTest/tests/src/com/android/frameworktest/view/SetTagsTest.java
new file mode 100644
index 0000000..523eeaf
--- /dev/null
+++ b/tests/FrameworkTest/tests/src/com/android/frameworktest/view/SetTagsTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2007 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.frameworktest.view;
+
+import com.android.frameworktest.R;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.widget.Button;
+
+/**
+ * Exercises {@link android.view.View}'s tags property.
+ */
+public class SetTagsTest extends ActivityInstrumentationTestCase2<Disabled> {
+    private Button mView;
+
+    public SetTagsTest() {
+        super("com.android.frameworktest", Disabled.class);
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mView = (Button) getActivity().findViewById(R.id.disabledButton);
+    }
+
+    @MediumTest
+    public void testSetUpConditions() throws Exception {
+        assertNotNull(mView);
+    }
+
+    @MediumTest
+    public void testSetTag() throws Exception {
+        mView.setTag("1");
+    }
+
+    @MediumTest
+    public void testGetTag() throws Exception {
+        Object o = new Object();
+        mView.setTag(o);
+
+        final Object stored = mView.getTag();
+        assertNotNull(stored);
+        assertSame("The stored tag is inccorect", o, stored);
+    }
+
+    @MediumTest
+    public void testSetTagWithKey() throws Exception {
+        mView.setTag(R.id.a, "2");
+    }
+
+    @MediumTest
+    public void testGetTagWithKey() throws Exception {
+        Object o = new Object();
+        mView.setTag(R.id.a, o);
+
+        final Object stored = mView.getTag(R.id.a);
+        assertNotNull(stored);
+        assertSame("The stored tag is inccorect", o, stored);
+    }
+
+    @MediumTest
+    public void testSetTagWithFrameworkId() throws Exception {
+        boolean result = false;
+        try {
+            mView.setTag(android.R.id.list, "2");
+        } catch (IllegalArgumentException e) {
+            result = true;
+        }
+        assertTrue("Setting a tag with a framework id did not throw an exception", result);
+    }
+
+    @MediumTest
+    public void testSetTagWithNoPackageId() throws Exception {
+        boolean result = false;
+        try {
+            mView.setTag(0x000000AA, "2");
+        } catch (IllegalArgumentException e) {
+            result = true;
+        }
+        assertTrue("Setting a tag with an id with no package did not throw an exception", result);
+    }
+
+    @MediumTest
+    public void testSetTagInternalWithFrameworkId() throws Exception {
+        mView.setTagInternal(android.R.id.list, "2");
+    }
+
+    @MediumTest
+    public void testSetTagInternalWithApplicationId() throws Exception {
+        boolean result = false;
+        try {
+            mView.setTagInternal(R.id.a, "2");
+        } catch (IllegalArgumentException e) {
+            result = true;
+        }
+        assertTrue("Setting a tag with an id with app package did not throw an exception", result);
+    }
+}