Require that persistable Uri permissions be taken.

Change our Intent flag to indicate that a Uri permission grant is
persistable, but don't actually persist it until explicitly taken by
the receiving app.  This prevents apps from spamming each other if
persisted permissions aren't really required.

Remember the last time a persisted grant was taken by an app, and
use this to prune away the oldest grants when the number of grants
grows too large.  Allow apps to query persisted grants they are
holding, and allow them to release previously persisted grants. Add
public UriPermission class to return grant details and timestamp.

Track various permission strengths separately, and combine together
after each mutation pass.  Persistable grants are currently treated
like global grants, but they could be moved to have owners in the
future.  Require that grant holders trying to extend a persistable
permission actually hold a persistable permission themselves.

Bug: 10835779
Change-Id: I95b2f797c04ce7fd2612f9a644685dbd44e03759
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 7d4d57c..3b88973 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -23,9 +23,11 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
+import android.content.UriPermission;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
@@ -1130,6 +1132,32 @@
             return true;
         }
 
+        case TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            Uri uri = Uri.CREATOR.createFromParcel(data);
+            int mode = data.readInt();
+            takePersistableUriPermission(uri, mode);
+            reply.writeNoException();
+            return true;
+        }
+
+        case RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            Uri uri = Uri.CREATOR.createFromParcel(data);
+            int mode = data.readInt();
+            releasePersistableUriPermission(uri, mode);
+            reply.writeNoException();
+            return true;
+        }
+
+        case GET_PERSISTED_URI_PERMISSIONS_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final ParceledListSlice<UriPermission> perms = getPersistedUriPermissions();
+            reply.writeNoException();
+            perms.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+            return true;
+        }
+
         case SHOW_WAITING_FOR_DEBUGGER_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder b = data.readStrongBinder();
@@ -1983,19 +2011,6 @@
             return true;
         }
 
-        case GET_GRANTED_URI_PERMISSIONS_TRANSACTION: {
-            data.enforceInterface(IActivityManager.descriptor);
-            final String sourcePackage = data.readString();
-            final String targetPackage = data.readString();
-            final int modeFlags = data.readInt();
-            final int modeMask = data.readInt();
-            final Uri[] uris = getGrantedUriPermissions(
-                    sourcePackage, targetPackage, modeFlags, modeMask);
-            reply.writeNoException();
-            reply.writeParcelableArray(uris, 0);
-            return true;
-        }
-
         case PERFORM_IDLE_MAINTENANCE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             performIdleMaintenance();
@@ -3436,6 +3451,47 @@
         data.recycle();
         reply.recycle();
     }
+
+    @Override
+    public void takePersistableUriPermission(Uri uri, int mode) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        uri.writeToParcel(data, 0);
+        data.writeInt(mode);
+        mRemote.transact(TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    @Override
+    public void releasePersistableUriPermission(Uri uri, int mode) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        uri.writeToParcel(data, 0);
+        data.writeInt(mode);
+        mRemote.transact(RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    @Override
+    public ParceledListSlice<UriPermission> getPersistedUriPermissions() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        mRemote.transact(GET_PERSISTED_URI_PERMISSIONS_TRANSACTION, data, reply, 0);
+        reply.readException();
+        final ParceledListSlice<UriPermission> perms = ParceledListSlice.CREATOR.createFromParcel(
+                reply);
+        data.recycle();
+        reply.recycle();
+        return perms;
+    }
+
     public void showWaitingForDebugger(IApplicationThread who, boolean waiting)
             throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -4567,24 +4623,6 @@
         reply.recycle();
     }
 
-    public Uri[] getGrantedUriPermissions(
-            String sourcePackage, String targetPackage, int modeFlags, int modeMask)
-            throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken(IActivityManager.descriptor);
-        data.writeString(sourcePackage);
-        data.writeString(targetPackage);
-        data.writeInt(modeFlags);
-        data.writeInt(modeMask);
-        mRemote.transact(GET_GRANTED_URI_PERMISSIONS_TRANSACTION, data, reply, 0);
-        reply.readException();
-        final Uri[] uris = (Uri[]) reply.readParcelableArray(null);
-        data.recycle();
-        reply.recycle();
-        return uris;
-    }
-
     public void performIdleMaintenance() throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 25c02df..9a77377 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -27,9 +27,11 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
+import android.content.UriPermission;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.ProviderInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
@@ -210,7 +212,10 @@
             Uri uri, int mode) throws RemoteException;
     public void revokeUriPermission(IApplicationThread caller, Uri uri,
             int mode) throws RemoteException;
-    
+    public void takePersistableUriPermission(Uri uri, int modeFlags) throws RemoteException;
+    public void releasePersistableUriPermission(Uri uri, int modeFlags) throws RemoteException;
+    public ParceledListSlice<UriPermission> getPersistedUriPermissions() throws RemoteException;
+
     public void showWaitingForDebugger(IApplicationThread who, boolean waiting)
             throws RemoteException;
     
@@ -399,10 +404,6 @@
 
     public void restart() throws RemoteException;
 
-    public Uri[] getGrantedUriPermissions(
-            String sourcePackage, String targetPackage, int modeFlags, int modeMask)
-            throws RemoteException;
-
     public void performIdleMaintenance() throws RemoteException;
 
     /*
@@ -686,6 +687,8 @@
     int NOTIFY_ACTIVITY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+175;
     int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+176;
     int RESTART_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+177;
-    int GET_GRANTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+178;
-    int PERFORM_IDLE_MAINTENANCE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+179;
+    int PERFORM_IDLE_MAINTENANCE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+178;
+    int TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+179;
+    int RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+180;
+    int GET_PERSISTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+181;
 }
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 2750d68..95fb6858 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -266,12 +266,18 @@
 
     /** @hide */
     protected abstract IContentProvider acquireProvider(Context c, String name);
-    /** Providing a default implementation of this, to avoid having to change
-     * a lot of other things, but implementations of ContentResolver should
-     * implement it. @hide */
+
+    /**
+     * Providing a default implementation of this, to avoid having to change a
+     * lot of other things, but implementations of ContentResolver should
+     * implement it.
+     *
+     * @hide
+     */
     protected IContentProvider acquireExistingProvider(Context c, String name) {
         return acquireProvider(c, name);
     }
+
     /** @hide */
     public abstract boolean releaseProvider(IContentProvider icp);
     /** @hide */
@@ -1616,54 +1622,50 @@
     }
 
     /**
-     * Return list of all Uri permissions that have been granted <em>to</em> the
-     * calling package, and which exactly match the requested flags. For
-     * example, to return all Uris that the calling application has
-     * <em>non-persistent</em> read access to:
+     * Take a persistable Uri permission grant that has been offered. Once
+     * taken, the permission grant will be remembered across device reboots.
+     * Only Uri permissions granted with
+     * {@link Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION} can be persisted. If
+     * the grant has already been persisted, taking it again will touch
+     * {@link UriPermission#getPersistedTime()}.
      *
-     * <pre class="prettyprint">
-     * getIncomingUriPermissionGrants(Intent.FLAG_GRANT_READ_URI_PERMISSION,
-     *         Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
-     * </pre>
-     *
-     * @param modeFlags any combination of
-     *            {@link Intent#FLAG_GRANT_READ_URI_PERMISSION},
-     *            {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, or
-     *            {@link Intent#FLAG_PERSIST_GRANT_URI_PERMISSION}.
-     * @param modeMask mask indicating which flags must match.
+     * @see #getPersistedUriPermissions()
      */
-    public Uri[] getIncomingUriPermissionGrants(int modeFlags, int modeMask) {
+    public void takePersistableUriPermission(Uri uri, int modeFlags) {
         try {
-            return ActivityManagerNative.getDefault()
-                    .getGrantedUriPermissions(null, getPackageName(), modeFlags, modeMask);
+            ActivityManagerNative.getDefault().takePersistableUriPermission(uri, modeFlags);
         } catch (RemoteException e) {
-            return new Uri[0];
         }
     }
 
     /**
-     * Return list of all Uri permissions that have been granted <em>from</em> the
-     * calling package, and which exactly match the requested flags. For
-     * example, to return all Uris that the calling application has granted
-     * <em>non-persistent</em> read access to:
+     * Relinquish a persisted Uri permission grant. The Uri must have been
+     * previously made persistent with
+     * {@link #takePersistableUriPermission(Uri, int)}. Any non-persistent
+     * grants to the calling package will remain intact.
      *
-     * <pre class="prettyprint">
-     * getOutgoingUriPermissionGrants(Intent.FLAG_GRANT_READ_URI_PERMISSION,
-     *         Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
-     * </pre>
-     *
-     * @param modeFlags any combination of
-     *            {@link Intent#FLAG_GRANT_READ_URI_PERMISSION},
-     *            {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, or
-     *            {@link Intent#FLAG_PERSIST_GRANT_URI_PERMISSION}.
-     * @param modeMask mask indicating which flags must match.
+     * @see #getPersistedUriPermissions()
      */
-    public Uri[] getOutgoingUriPermissionGrants(int modeFlags, int modeMask) {
+    public void releasePersistableUriPermission(Uri uri, int modeFlags) {
         try {
-            return ActivityManagerNative.getDefault()
-                    .getGrantedUriPermissions(getPackageName(), null, modeFlags, modeMask);
+            ActivityManagerNative.getDefault().releasePersistableUriPermission(uri, modeFlags);
         } catch (RemoteException e) {
-            return new Uri[0];
+        }
+    }
+
+    /**
+     * Return list of all Uri permission grants that have been persisted for the
+     * calling app. Only persistable grants taken with
+     * {@link #takePersistableUriPermission(Uri, int)} are returned.
+     *
+     * @see #takePersistableUriPermission(Uri, int)
+     * @see #releasePersistableUriPermission(Uri, int)
+     */
+    public List<UriPermission> getPersistedUriPermissions() {
+        try {
+            return ActivityManagerNative.getDefault().getPersistedUriPermissions().getList();
+        } catch (RemoteException e) {
+            throw new RuntimeException("Activity manager has died", e);
         }
     }
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index dcc6328..047f175 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3339,12 +3339,18 @@
 
     /**
      * When combined with {@link #FLAG_GRANT_READ_URI_PERMISSION} and/or
-     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, the grant will be remembered
-     * until explicitly revoked with
-     * {@link Context#revokeUriPermission(Uri, int)}. These grants persist
-     * across device reboots.
+     * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, the Uri permission grant can be
+     * persisted across device reboots until explicitly revoked with
+     * {@link Context#revokeUriPermission(Uri, int)}. This flag only offers the
+     * grant for possible persisting; the receiving application must call
+     * {@link ContentResolver#takePersistableUriPermission(Uri, int)} to
+     * actually persist.
+     *
+     * @see ContentResolver#takePersistableUriPermission(Uri, int)
+     * @see ContentResolver#releasePersistableUriPermission(Uri, int)
+     * @see ContentResolver#getPersistedUriPermissions()
      */
-    public static final int FLAG_PERSIST_GRANT_URI_PERMISSION = 0x00000040;
+    public static final int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 0x00000040;
 
     /**
      * If set, the new activity is not kept in the history stack.  As soon as
@@ -7173,7 +7179,7 @@
                     setClipData(target.getClipData());
                     addFlags(target.getFlags()
                             & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION
-                                    | FLAG_PERSIST_GRANT_URI_PERMISSION));
+                                    | FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
                     return true;
                 } else {
                     return false;
diff --git a/core/java/android/content/UriPermission.java b/core/java/android/content/UriPermission.java
new file mode 100644
index 0000000..df9200d
--- /dev/null
+++ b/core/java/android/content/UriPermission.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 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 android.content;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Description of a single Uri permission grant. This grants may have been
+ * created via {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}, etc when sending
+ * an {@link Intent}, or explicitly through
+ * {@link Context#grantUriPermission(String, android.net.Uri, int)}.
+ *
+ * @see ContentResolver#getPersistedUriPermissions()
+ */
+public final class UriPermission implements Parcelable {
+    private final Uri mUri;
+    private final int mModeFlags;
+    private final long mPersistedTime;
+
+    /**
+     * Value returned when a permission has not been persisted.
+     */
+    public static final long INVALID_TIME = Long.MIN_VALUE;
+
+    /** {@hide} */
+    public UriPermission(Uri uri, int modeFlags, long persistedTime) {
+        mUri = uri;
+        mModeFlags = modeFlags;
+        mPersistedTime = persistedTime;
+    }
+
+    /** {@hide} */
+    public UriPermission(Parcel in) {
+        mUri = in.readParcelable(null);
+        mModeFlags = in.readInt();
+        mPersistedTime = in.readLong();
+    }
+
+    /**
+     * Return the Uri this permission pertains to.
+     */
+    public Uri getUri() {
+        return mUri;
+    }
+
+    /**
+     * Returns if this permission offers read access.
+     */
+    public boolean isReadPermission() {
+        return (mModeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0;
+    }
+
+    /**
+     * Returns if this permission offers write access.
+     */
+    public boolean isWritePermission() {
+        return (mModeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0;
+    }
+
+    /**
+     * Return the time when this permission was first persisted, in milliseconds
+     * since January 1, 1970 00:00:00.0 UTC. Returns {@link #INVALID_TIME} if
+     * not persisted.
+     *
+     * @see ContentResolver#takePersistableUriPermission(Uri, int)
+     * @see System#currentTimeMillis()
+     */
+    public long getPersistedTime() {
+        return mPersistedTime;
+    }
+
+    @Override
+    public String toString() {
+        return "UriPermission {uri=" + mUri + ", modeFlags=" + mModeFlags + ", persistedTime="
+                + mPersistedTime + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mUri, flags);
+        dest.writeInt(mModeFlags);
+        dest.writeLong(mPersistedTime);
+    }
+
+    public static final Creator<UriPermission> CREATOR = new Creator<UriPermission>() {
+        @Override
+        public UriPermission createFromParcel(Parcel source) {
+            return new UriPermission(source);
+        }
+
+        @Override
+        public UriPermission[] newArray(int size) {
+            return new UriPermission[size];
+        }
+    };
+}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 3f33e80..8f22312 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -21,10 +21,7 @@
 
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
-import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.graphics.Bitmap;
@@ -38,8 +35,6 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import com.google.android.collect.Lists;
-
 import libcore.io.ErrnoException;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
@@ -624,37 +619,6 @@
     }
 
     /**
-     * Return list of all documents that the calling package has "open." These
-     * are Uris matching {@link DocumentsContract} to which persistent
-     * read/write access has been granted, usually through
-     * {@link Intent#ACTION_OPEN_DOCUMENT} or
-     * {@link Intent#ACTION_CREATE_DOCUMENT}.
-     *
-     * @see Context#grantUriPermission(String, Uri, int)
-     * @see Context#revokeUriPermission(Uri, int)
-     * @see ContentResolver#getIncomingUriPermissionGrants(int, int)
-     */
-    public static Uri[] getOpenDocuments(Context context) {
-        final int openedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION;
-        final Uri[] uris = context.getContentResolver()
-                .getIncomingUriPermissionGrants(openedFlags, openedFlags);
-
-        // Filter to only include document providers
-        final PackageManager pm = context.getPackageManager();
-        final List<Uri> result = Lists.newArrayList();
-        for (Uri uri : uris) {
-            final ProviderInfo info = pm.resolveContentProvider(
-                    uri.getAuthority(), PackageManager.GET_META_DATA);
-            if (info.metaData.containsKey(META_DATA_DOCUMENT_PROVIDER)) {
-                result.add(uri);
-            }
-        }
-
-        return result.toArray(new Uri[result.size()]);
-    }
-
-    /**
      * Return thumbnail representing the document at the given Uri. Callers are
      * responsible for their own in-memory caching.
      *
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index bc4e28b..337b735 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -387,7 +387,7 @@
                     context.grantUriPermission(getCallingPackage(), newDocumentUri,
                             Intent.FLAG_GRANT_READ_URI_PERMISSION
                             | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                            | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
+                            | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
                 }
 
             } else if (METHOD_DELETE_DOCUMENT.equals(method)) {
@@ -396,7 +396,7 @@
                 // Document no longer exists, clean up any grants
                 context.revokeUriPermission(documentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION
                         | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                        | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
+                        | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
 
             } else {
                 throw new UnsupportedOperationException("Method not supported " + method);
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index f40f48c..0b74cf3 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -931,6 +931,15 @@
         out.attribute(null, name, Integer.toString(value));
     }
 
+    public static long readLongAttribute(XmlPullParser in, String name, long defaultValue) {
+        final String value = in.getAttributeValue(null, name);
+        try {
+            return Long.parseLong(value);
+        } catch (NumberFormatException e) {
+            return defaultValue;
+        }
+    }
+
     public static long readLongAttribute(XmlPullParser in, String name) throws IOException {
         final String value = in.getAttributeValue(null, name);
         try {