am 39792d22: Fix bugs with granting permissions through onNewIntent().

Merge commit '39792d2262352ae775091876d5488d2412a2ff92' into gingerbread-plus-aosp

* commit '39792d2262352ae775091876d5488d2412a2ff92':
  Fix bugs with granting permissions through onNewIntent().
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 7901b155..244d126 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -389,15 +389,17 @@
         if (permission != null) {
             pw.println(prefix + "permission=" + permission);
         }
-        pw.println(prefix + "uid=" + uid + " taskAffinity=" + taskAffinity);
-        if (theme != 0) {
-            pw.println(prefix + "theme=0x" + Integer.toHexString(theme));
-        }
-        pw.println(prefix + "flags=0x" + Integer.toHexString(flags)
-                + " processName=" + processName);
+        pw.println(prefix + "processName=" + processName);
+        pw.println(prefix + "taskAffinity=" + taskAffinity);
+        pw.println(prefix + "uid=" + uid + " flags=0x" + Integer.toHexString(flags)
+                + " theme=0x" + Integer.toHexString(theme));
         pw.println(prefix + "sourceDir=" + sourceDir);
-        pw.println(prefix + "publicSourceDir=" + publicSourceDir);
-        pw.println(prefix + "resourceDirs=" + resourceDirs);
+        if (!sourceDir.equals(publicSourceDir)) {
+            pw.println(prefix + "publicSourceDir=" + publicSourceDir);
+        }
+        if (resourceDirs != null) {
+            pw.println(prefix + "resourceDirs=" + resourceDirs);
+        }
         pw.println(prefix + "dataDir=" + dataDir);
         if (sharedLibraryFiles != null) {
             pw.println(prefix + "sharedLibraryFiles=" + sharedLibraryFiles);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index a3ba4ed..21df14a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -4096,16 +4096,23 @@
         }
     }
 
-    void grantUriPermissionLocked(int callingUid,
-            String targetPkg, Uri uri, int modeFlags, ActivityRecord activity) {
+    /**
+     * Check if the targetPkg can be granted permission to access uri by
+     * the callingUid using the given modeFlags.  Throws a security exception
+     * if callingUid is not allowed to do this.  Returns the uid of the target
+     * if the URI permission grant should be performed; returns -1 if it is not
+     * needed (for example targetPkg already has permission to access the URI).
+     */
+    int checkGrantUriPermissionLocked(int callingUid, String targetPkg,
+            Uri uri, int modeFlags) {
         modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
                 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
         if (modeFlags == 0) {
-            return;
+            return -1;
         }
 
         if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
-                "Requested grant " + targetPkg + " permission to " + uri);
+                "Checking grant " + targetPkg + " permission to " + uri);
         
         final IPackageManager pm = AppGlobals.getPackageManager();
 
@@ -4113,7 +4120,7 @@
         if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
             if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
                     "Can't grant URI permission for non-content URI: " + uri);
-            return;
+            return -1;
         }
 
         String name = uri.getAuthority();
@@ -4130,7 +4137,7 @@
         }
         if (pi == null) {
             Slog.w(TAG, "No content provider found for: " + name);
-            return;
+            return -1;
         }
 
         int targetUid;
@@ -4139,10 +4146,10 @@
             if (targetUid < 0) {
                 if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
                         "Can't grant URI permission no uid for: " + targetPkg);
-                return;
+                return -1;
             }
         } catch (RemoteException ex) {
-            return;
+            return -1;
         }
 
         // First...  does the target actually need this permission?
@@ -4150,7 +4157,7 @@
             // No need to grant the target this permission.
             if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
                     "Target " + targetPkg + " already has full permission to " + uri);
-            return;
+            return -1;
         }
 
         // Second...  is the provider allowing granting of URI permissions?
@@ -4187,12 +4194,23 @@
             }
         }
 
-        // Okay!  So here we are: the caller has the assumed permission
+        return targetUid;
+    }
+
+    void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg,
+            Uri uri, int modeFlags, UriPermissionOwner owner) {
+        modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        if (modeFlags == 0) {
+            return;
+        }
+
+        // So here we are: the caller has the assumed permission
         // to the uri, and the target doesn't.  Let's now give this to
         // the target.
 
         if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
-                "Granting " + targetPkg + " permission to " + uri);
+                "Granting " + targetPkg + "/" + targetUid + " permission to " + uri);
         
         HashMap<Uri, UriPermission> targetUris
                 = mGrantedUriPermissions.get(targetUid);
@@ -4208,39 +4226,65 @@
         }
 
         perm.modeFlags |= modeFlags;
-        if (activity == null) {
+        if (owner == null) {
             perm.globalModeFlags |= modeFlags;
         } else if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
-            perm.readActivities.add(activity);
-            if (activity.readUriPermissions == null) {
-                activity.readUriPermissions = new HashSet<UriPermission>();
-            }
-            activity.readUriPermissions.add(perm);
+            perm.readOwners.add(owner);
+            owner.addReadPermission(perm);
         } else if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
-            perm.writeActivities.add(activity);
-            if (activity.writeUriPermissions == null) {
-                activity.writeUriPermissions = new HashSet<UriPermission>();
-            }
-            activity.writeUriPermissions.add(perm);
+            perm.writeOwners.add(owner);
+            owner.addWritePermission(perm);
         }
     }
 
-    void grantUriPermissionFromIntentLocked(int callingUid,
-            String targetPkg, Intent intent, ActivityRecord activity) {
+    void grantUriPermissionLocked(int callingUid,
+            String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) {
+        int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags);
+        if (targetUid < 0) {
+            return;
+        }
+
+        grantUriPermissionUncheckedLocked(targetUid, targetPkg, uri, modeFlags, owner);
+    }
+
+    /**
+     * Like checkGrantUriPermissionLocked, but takes an Intent.
+     */
+    int checkGrantUriPermissionFromIntentLocked(int callingUid,
+            String targetPkg, Intent intent) {
         if (DEBUG_URI_PERMISSION) Slog.v(TAG,
-                "Grant URI perm to " + (intent != null ? intent.getData() : null)
+                "Checking URI perm to " + (intent != null ? intent.getData() : null)
                 + " from " + intent + "; flags=0x"
                 + Integer.toHexString(intent != null ? intent.getFlags() : 0));
 
         if (intent == null) {
-            return;
+            return -1;
         }
         Uri data = intent.getData();
         if (data == null) {
+            return -1;
+        }
+        return checkGrantUriPermissionLocked(callingUid, targetPkg, data,
+                intent.getFlags());
+    }
+
+    /**
+     * Like grantUriPermissionUncheckedLocked, but takes an Intent.
+     */
+    void grantUriPermissionUncheckedFromIntentLocked(int targetUid,
+            String targetPkg, Intent intent, UriPermissionOwner owner) {
+        grantUriPermissionUncheckedLocked(targetUid, targetPkg, intent.getData(),
+                intent.getFlags(), owner);
+    }
+
+    void grantUriPermissionFromIntentLocked(int callingUid,
+            String targetPkg, Intent intent, UriPermissionOwner owner) {
+        int targetUid = checkGrantUriPermissionFromIntentLocked(callingUid, targetPkg, intent);
+        if (targetUid < 0) {
             return;
         }
-        grantUriPermissionLocked(callingUid, targetPkg, data,
-                intent.getFlags(), activity);
+
+        grantUriPermissionUncheckedFromIntentLocked(targetUid, targetPkg, intent, owner);
     }
 
     public void grantUriPermission(IApplicationThread caller, String targetPkg,
@@ -8211,18 +8255,23 @@
             return;
         }
 
-        int i = 0;
-        while (i < N) {
+        while (r.pendingStarts.size() > 0) {
             try {
-                ServiceRecord.StartItem si = r.pendingStarts.get(i);
+                ServiceRecord.StartItem si = r.pendingStarts.remove(0);
                 if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to service: "
                         + r.name + " " + r.intent + " args=" + si.intent);
-                if (si.intent == null && N > 1) {
+                if (si.intent == null) {
                     // If somehow we got a dummy start at the front, then
                     // just drop it here.
-                    i++;
                     continue;
                 }
+                si.deliveredTime = SystemClock.uptimeMillis();
+                r.deliveredStarts.add(si);
+                si.deliveryCount++;
+                if (si.targetPermissionUid >= 0) {
+                    grantUriPermissionUncheckedFromIntentLocked(si.targetPermissionUid,
+                            r.packageName, si.intent, si);
+                }
                 bumpServiceExecutingLocked(r);
                 if (!oomAdjusted) {
                     oomAdjusted = true;
@@ -8236,10 +8285,6 @@
                     flags |= Service.START_FLAG_REDELIVERY;
                 }
                 r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent);
-                si.deliveredTime = SystemClock.uptimeMillis();
-                r.deliveredStarts.add(si);
-                si.deliveryCount++;
-                i++;
             } catch (RemoteException e) {
                 // Remote process gone...  we'll let the normal cleanup take
                 // care of this.
@@ -8249,14 +8294,6 @@
                 break;
             }
         }
-        if (i == N) {
-            r.pendingStarts.clear();
-        } else {
-            while (i > 0) {
-                i--;
-                r.pendingStarts.remove(i);
-            }
-        }
     }
 
     private final boolean requestServiceBindingLocked(ServiceRecord r,
@@ -8339,7 +8376,7 @@
             if (r.lastStartId < 1) {
                 r.lastStartId = 1;
             }
-            r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, null));
+            r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, null, -1));
         }
         
         sendServiceArgsLocked(r, true);
@@ -8359,6 +8396,7 @@
         if (N > 0) {
             for (int i=N-1; i>=0; i--) {
                 ServiceRecord.StartItem si = r.deliveredStarts.get(i);
+                si.removeUriPermissionsLocked();
                 if (si.intent == null) {
                     // We'll generate this again if needed.
                 } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
@@ -8598,7 +8636,7 @@
         r.foregroundNoti = null;
         
         // Clear start entries.
-        r.deliveredStarts.clear();
+        r.clearDeliveredStartsLocked();
         r.pendingStarts.clear();
         
         if (r.app != null) {
@@ -8658,6 +8696,8 @@
                         ? res.permission : "private to package");
             }
             ServiceRecord r = res.record;
+            int targetPermissionUid = checkGrantUriPermissionFromIntentLocked(
+                    callingUid, r.packageName, service);
             if (unscheduleServiceRestartLocked(r)) {
                 if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: "
                         + r.shortName);
@@ -8668,7 +8708,8 @@
             if (r.lastStartId < 1) {
                 r.lastStartId = 1;
             }
-            r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, service));
+            r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId,
+                    service, targetPermissionUid));
             r.lastActivity = SystemClock.uptimeMillis();
             synchronized (r.stats.getBatteryStats()) {
                 r.stats.startRunningLocked();
@@ -8793,7 +8834,9 @@
                     ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
                     if (si != null) {
                         while (r.deliveredStarts.size() > 0) {
-                            if (r.deliveredStarts.remove(0) == si) {
+                            ServiceRecord.StartItem cur = r.deliveredStarts.remove(0);
+                            cur.removeUriPermissionsLocked();
+                            if (cur == si) {
                                 break;
                             }
                         }
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 79756a7..80a41b7 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -29,6 +29,7 @@
 import android.os.Bundle;
 import android.os.Message;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.EventLog;
 import android.util.Log;
@@ -43,7 +44,7 @@
 /**
  * An entry in the history stack, representing an activity.
  */
-class ActivityRecord extends IApplicationToken.Stub {
+class ActivityRecord extends IApplicationToken.Stub implements UriPermissionOwner {
     final ActivityManagerService service; // owner
     final ActivityStack stack; // owner
     final ActivityInfo info; // all about me
@@ -340,16 +341,22 @@
      * Deliver a new Intent to an existing activity, so that its onNewIntent()
      * method will be called at the proper time.
      */
-    final void deliverNewIntentLocked(Intent intent) {
+    final void deliverNewIntentLocked(int callingUid, Intent intent) {
         boolean sent = false;
         if (state == ActivityState.RESUMED
                 && app != null && app.thread != null) {
             try {
                 ArrayList<Intent> ar = new ArrayList<Intent>();
-                ar.add(new Intent(intent));
+                intent = new Intent(intent);
+                ar.add(intent);
+                service.grantUriPermissionFromIntentLocked(callingUid, packageName,
+                        intent, this);
                 app.thread.scheduleNewIntent(ar, this);
                 sent = true;
-            } catch (Exception e) {
+            } catch (RemoteException e) {
+                Slog.w(ActivityManagerService.TAG,
+                        "Exception thrown sending new intent to " + this, e);
+            } catch (NullPointerException e) {
                 Slog.w(ActivityManagerService.TAG,
                         "Exception thrown sending new intent to " + this, e);
             }
@@ -362,23 +369,25 @@
     void removeUriPermissionsLocked() {
         if (readUriPermissions != null) {
             for (UriPermission perm : readUriPermissions) {
-                perm.readActivities.remove(this);
-                if (perm.readActivities.size() == 0 && (perm.globalModeFlags
+                perm.readOwners.remove(this);
+                if (perm.readOwners.size() == 0 && (perm.globalModeFlags
                         &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
                     perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-                   service.removeUriPermissionIfNeededLocked(perm);
+                    service.removeUriPermissionIfNeededLocked(perm);
                 }
             }
+            readUriPermissions = null;
         }
         if (writeUriPermissions != null) {
             for (UriPermission perm : writeUriPermissions) {
-                perm.writeActivities.remove(this);
-                if (perm.writeActivities.size() == 0 && (perm.globalModeFlags
+                perm.writeOwners.remove(this);
+                if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
                         &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
                     perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
                     service.removeUriPermissionIfNeededLocked(perm);
                 }
             }
+            writeUriPermissions = null;
         }
     }
 
@@ -569,6 +578,37 @@
                 state == ActivityState.RESUMED;
      }
     
+    @Override
+    public void addReadPermission(UriPermission perm) {
+        if (readUriPermissions == null) {
+            readUriPermissions = new HashSet<UriPermission>();
+        }
+        readUriPermissions.add(perm);
+    }
+
+    @Override
+    public void addWritePermission(UriPermission perm) {
+        if (writeUriPermissions == null) {
+            writeUriPermissions = new HashSet<UriPermission>();
+        }
+        writeUriPermissions.add(perm);
+    }
+
+    @Override
+    public void removeReadPermission(UriPermission perm) {
+        readUriPermissions.remove(perm);
+        if (readUriPermissions.size() == 0) {
+            readUriPermissions = null;
+        }
+    }
+
+    @Override
+    public void removeWritePermission(UriPermission perm) {
+        writeUriPermissions.remove(perm);
+        if (writeUriPermissions.size() == 0) {
+            writeUriPermissions = null;
+        }
+    }
     
     public String toString() {
         if (stringName != null) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index de7b15c..a5f7e96 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -2033,16 +2033,6 @@
             }
         }
 
-        if (grantedUriPermissions != null && callingUid > 0) {
-            for (int i=0; i<grantedUriPermissions.length; i++) {
-                mService.grantUriPermissionLocked(callingUid, r.packageName,
-                        grantedUriPermissions[i], grantedMode, r);
-            }
-        }
-
-        mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
-                intent, r);
-
         if (sourceRecord == null) {
             // This activity is not being started from another...  in this
             // case we -always- start a new task.
@@ -2150,7 +2140,7 @@
                                 top.task.setIntent(r.intent, r.info);
                             }
                             logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                            top.deliverNewIntentLocked(r.intent);
+                            top.deliverNewIntentLocked(callingUid, r.intent);
                         } else {
                             // A special case: we need to
                             // start the activity because it is not currently
@@ -2175,7 +2165,7 @@
                             if (taskTop.frontOfTask) {
                                 taskTop.task.setIntent(r.intent, r.info);
                             }
-                            taskTop.deliverNewIntentLocked(r.intent);
+                            taskTop.deliverNewIntentLocked(callingUid, r.intent);
                         } else if (!r.intent.filterEquals(taskTop.task.intent)) {
                             // In this case we are launching the root activity
                             // of the task, but with a different intent.  We
@@ -2243,7 +2233,7 @@
                                 // is the case, so this is it!
                                 return START_RETURN_INTENT_TO_CALLER;
                             }
-                            top.deliverNewIntentLocked(r.intent);
+                            top.deliverNewIntentLocked(callingUid, r.intent);
                             return START_DELIVERED_TO_TOP;
                         }
                     }
@@ -2288,7 +2278,7 @@
                         sourceRecord.task.taskId, r, launchFlags, true);
                 if (top != null) {
                     logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                    top.deliverNewIntentLocked(r.intent);
+                    top.deliverNewIntentLocked(callingUid, r.intent);
                     // For paranoia, make sure we have correctly
                     // resumed the top activity.
                     if (doResume) {
@@ -2305,7 +2295,7 @@
                 if (where >= 0) {
                     ActivityRecord top = moveActivityToFrontLocked(where);
                     logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                    top.deliverNewIntentLocked(r.intent);
+                    top.deliverNewIntentLocked(callingUid, r.intent);
                     if (doResume) {
                         resumeTopActivityLocked(null);
                     }
@@ -2333,6 +2323,17 @@
             if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                     + " in new guessed " + r.task);
         }
+
+        if (grantedUriPermissions != null && callingUid > 0) {
+            for (int i=0; i<grantedUriPermissions.length; i++) {
+                mService.grantUriPermissionLocked(callingUid, r.packageName,
+                        grantedUriPermissions[i], grantedMode, r);
+            }
+        }
+
+        mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+                intent, r);
+
         if (newTask) {
             EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId);
         }
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index c3f0b3e..bac21b1 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -26,6 +26,7 @@
 import android.os.IBinder;
 import android.os.SystemClock;
 import android.util.PrintWriterPrinter;
+import android.util.TimeUtils;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -73,61 +74,65 @@
     ActivityInfo curReceiver;   // info about the receiver that is currently running.
 
     void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + this);
-        pw.println(prefix + intent);
+        final long now = SystemClock.uptimeMillis();
+
+        pw.print(prefix); pw.println(this);
+        pw.print(prefix); pw.println(intent);
         if (sticky) {
             Bundle bundle = intent.getExtras();
             if (bundle != null) {
-                pw.println(prefix + "extras: " + bundle.toString());
+                pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString());
             }
         }
-        pw.println(prefix + "proc=" + callerApp);
-        pw.println(prefix + "caller=" + callerPackage
-                + " callingPid=" + callingPid
-                + " callingUid=" + callingUid);
+        pw.print(prefix); pw.print("caller="); pw.print(callerPackage); pw.println(" ");
+                pw.println(callerApp != null ? callerApp.toShortString() : "null");
+                pw.print(" pid="); pw.print(callingPid);
+                pw.print(" uid="); pw.println(callingUid);
         if (requiredPermission != null) {
-            pw.println(prefix + "requiredPermission=" + requiredPermission);
+            pw.print(prefix); pw.print("requiredPermission="); pw.println(requiredPermission);
         }
-        pw.println(prefix + "dispatchTime=" + dispatchTime + " ("
-                + (SystemClock.uptimeMillis()-dispatchTime) + "ms since now)");
+        pw.print(prefix); pw.print("dispatchTime=");
+                TimeUtils.formatDuration(dispatchTime, now, pw);
         if (finishTime != 0) {
-            pw.println(prefix + "finishTime=" + finishTime + " ("
-                    + (SystemClock.uptimeMillis()-finishTime) + "ms since now)");
+            pw.print(" finishTime="); TimeUtils.formatDuration(finishTime, now, pw);
         } else {
-            pw.println(prefix + "receiverTime=" + receiverTime + " ("
-                    + (SystemClock.uptimeMillis()-receiverTime) + "ms since now)");
+            pw.print(" receiverTime="); TimeUtils.formatDuration(receiverTime, now, pw);
         }
+        pw.println("");
         if (anrCount != 0) {
-            pw.println(prefix + "anrCount=" + anrCount);
+            pw.print(prefix); pw.print("anrCount="); pw.println(anrCount);
         }
         if (resultTo != null || resultCode != -1 || resultData != null) {
-            pw.println(prefix + "resultTo=" + resultTo
-                  + " resultCode=" + resultCode + " resultData=" + resultData);
+            pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
+                    pw.print(" resultCode="); pw.print(resultCode);
+                    pw.print(" resultData="); pw.println(resultData);
         }
         if (resultExtras != null) {
-            pw.println(prefix + "resultExtras=" + resultExtras);
+            pw.print(prefix); pw.print("resultExtras="); pw.println(resultExtras);
         }
         if (resultAbort || ordered || sticky || initialSticky) {
-            pw.println(prefix + "resultAbort=" + resultAbort
-                    + " ordered=" + ordered + " sticky=" + sticky
-                    + " initialSticky=" + initialSticky);
+            pw.print(prefix); pw.print("resultAbort="); pw.print(resultAbort);
+                    pw.print(" ordered="); pw.print(ordered);
+                    pw.print(" sticky="); pw.print(sticky);
+                    pw.print(" initialSticky="); pw.println(initialSticky);
         }
         if (nextReceiver != 0 || receiver != null) {
-            pw.println(prefix + "nextReceiver=" + nextReceiver
-                  + " receiver=" + receiver);
+            pw.print(prefix); pw.print("nextReceiver="); pw.print(nextReceiver);
+                    pw.print(" receiver="); pw.println(receiver);
         }
         if (curFilter != null) {
-            pw.println(prefix + "curFilter=" + curFilter);
+            pw.print(prefix); pw.print("curFilter="); pw.println(curFilter);
         }
         if (curReceiver != null) {
-            pw.println(prefix + "curReceiver=" + curReceiver);
+            pw.print(prefix); pw.print("curReceiver="); pw.println(curReceiver);
         }
         if (curApp != null) {
-            pw.println(prefix + "curApp=" + curApp);
-            pw.println(prefix + "curComponent="
-                    + (curComponent != null ? curComponent.toShortString() : "--"));
+            pw.print(prefix); pw.print("curApp="); pw.println(curApp);
+            pw.print(prefix); pw.print("curComponent=");
+                    pw.println((curComponent != null ? curComponent.toShortString() : "--"));
             if (curReceiver != null && curReceiver.applicationInfo != null) {
-                pw.println(prefix + "curSourceDir=" + curReceiver.applicationInfo.sourceDir);
+                pw.print(prefix); pw.print("curSourceDir=");
+                        pw.println(curReceiver.applicationInfo.sourceDir);
             }
         }
         String stateStr = " (?)";
@@ -137,13 +142,14 @@
             case CALL_IN_RECEIVE:   stateStr=" (CALL_IN_RECEIVE)"; break;
             case CALL_DONE_RECEIVE: stateStr=" (CALL_DONE_RECEIVE)"; break;
         }
-        pw.println(prefix + "state=" + state + stateStr);
+        pw.print(prefix); pw.print("state="); pw.print(state); pw.println(stateStr);
         final int N = receivers != null ? receivers.size() : 0;
         String p2 = prefix + "  ";
         PrintWriterPrinter printer = new PrintWriterPrinter(pw);
         for (int i=0; i<N; i++) {
             Object o = receivers.get(i);
-            pw.println(prefix + "Receiver #" + i + ": " + o);
+            pw.print(prefix); pw.print("Receiver #"); pw.print(i);
+                    pw.print(": "); pw.println(o);
             if (o instanceof BroadcastFilter)
                 ((BroadcastFilter)o).dumpBrief(pw, p2);
             else if (o instanceof ResolveInfo)
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index ab5a78d..255fbe3 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -36,6 +36,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 
@@ -43,6 +44,12 @@
  * A running application service.
  */
 class ServiceRecord extends Binder {
+    // Maximum number of delivery attempts before giving up.
+    static final int MAX_DELIVERY_COUNT = 3;
+
+    // Maximum number of times it can fail during execution before giving up.
+    static final int MAX_DONE_EXECUTING_COUNT = 6;
+
     final ActivityManagerService ams;
     final BatteryStatsImpl.Uid.Pkg.Serv stats;
     final ComponentName name; // service component.
@@ -68,29 +75,6 @@
     final HashMap<IBinder, ConnectionRecord> connections
             = new HashMap<IBinder, ConnectionRecord>();
                             // IBinder -> ConnectionRecord of all bound clients
-    
-    // Maximum number of delivery attempts before giving up.
-    static final int MAX_DELIVERY_COUNT = 3;
-    
-    // Maximum number of times it can fail during execution before giving up.
-    static final int MAX_DONE_EXECUTING_COUNT = 6;
-    
-    static class StartItem {
-        final int id;
-        final Intent intent;
-        long deliveredTime;
-        int deliveryCount;
-        int doneExecutingCount;
-        
-        StartItem(int _id, Intent _intent) {
-            id = _id;
-            intent = _intent;
-        }
-    }
-    final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
-                            // start() arguments which been delivered.
-    final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
-                            // start() arguments that haven't yet been delivered.
 
     ProcessRecord app;      // where this service is running or null.
     boolean isForeground;   // is service currently in foreground mode?
@@ -112,6 +96,104 @@
 
     String stringName;      // caching of toString
     
+    static class StartItem implements UriPermissionOwner {
+        final ServiceRecord sr;
+        final int id;
+        final Intent intent;
+        final int targetPermissionUid;
+        long deliveredTime;
+        int deliveryCount;
+        int doneExecutingCount;
+
+        String stringName;      // caching of toString
+
+        HashSet<UriPermission> readUriPermissions; // special access to reading uris.
+        HashSet<UriPermission> writeUriPermissions; // special access to writing uris.
+
+        StartItem(ServiceRecord _sr, int _id, Intent _intent, int _targetPermissionUid) {
+            sr = _sr;
+            id = _id;
+            intent = _intent;
+            targetPermissionUid = _targetPermissionUid;
+        }
+
+        void removeUriPermissionsLocked() {
+            if (readUriPermissions != null) {
+                for (UriPermission perm : readUriPermissions) {
+                    perm.readOwners.remove(this);
+                    if (perm.readOwners.size() == 0 && (perm.globalModeFlags
+                            &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+                        perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
+                        sr.ams.removeUriPermissionIfNeededLocked(perm);
+                    }
+                }
+                readUriPermissions = null;
+            }
+            if (writeUriPermissions != null) {
+                for (UriPermission perm : writeUriPermissions) {
+                    perm.writeOwners.remove(this);
+                    if (perm.writeOwners.size() == 0 && (perm.globalModeFlags
+                            &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) {
+                        perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+                        sr.ams.removeUriPermissionIfNeededLocked(perm);
+                    }
+                }
+                writeUriPermissions = null;
+            }
+        }
+
+        @Override
+        public void addReadPermission(UriPermission perm) {
+            if (readUriPermissions == null) {
+                readUriPermissions = new HashSet<UriPermission>();
+            }
+            readUriPermissions.add(perm);
+        }
+
+        @Override
+        public void addWritePermission(UriPermission perm) {
+            if (writeUriPermissions == null) {
+                writeUriPermissions = new HashSet<UriPermission>();
+            }
+            writeUriPermissions.add(perm);
+        }
+
+        @Override
+        public void removeReadPermission(UriPermission perm) {
+            readUriPermissions.remove(perm);
+            if (readUriPermissions.size() == 0) {
+                readUriPermissions = null;
+            }
+        }
+
+        @Override
+        public void removeWritePermission(UriPermission perm) {
+            writeUriPermissions.remove(perm);
+            if (writeUriPermissions.size() == 0) {
+                writeUriPermissions = null;
+            }
+        }
+
+        public String toString() {
+            if (stringName != null) {
+                return stringName;
+            }
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("ServiceRecord{")
+                .append(Integer.toHexString(System.identityHashCode(sr)))
+                .append(' ').append(sr.shortName)
+                .append(" StartItem ")
+                .append(Integer.toHexString(System.identityHashCode(this)))
+                .append(" id=").append(id).append('}');
+            return stringName = sb.toString();
+        }
+    }
+
+    final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
+                            // start() arguments which been delivered.
+    final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
+                            // start() arguments that haven't yet been delivered.
+
     void dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now) {
         final int N = list.size();
         for (int i=0; i<N; i++) {
@@ -128,9 +210,22 @@
                     if (si.doneExecutingCount != 0) {
                         pw.print(" dxc="); pw.print(si.doneExecutingCount);
                     }
-                    pw.print(" ");
+                    pw.println("");
+            pw.print(prefix); pw.print("  intent=");
                     if (si.intent != null) pw.println(si.intent.toString());
                     else pw.println("null");
+            if (si.targetPermissionUid >= 0) {
+                pw.print(prefix); pw.print("  targetPermissionUid=");
+                        pw.println(si.targetPermissionUid);
+            }
+            if (si.readUriPermissions != null) {
+                pw.print(prefix); pw.print("  readUriPermissions=");
+                        pw.println(si.readUriPermissions);
+            }
+            if (si.writeUriPermissions != null) {
+                pw.print(prefix); pw.print("  writeUriPermissions=");
+                        pw.println(si.writeUriPermissions);
+            }
         }
     }
     
@@ -324,6 +419,13 @@
         }
     }
     
+    public void clearDeliveredStartsLocked() {
+        for (int i=deliveredStarts.size()-1; i>=0; i--) {
+            deliveredStarts.get(i).removeUriPermissionsLocked();
+        }
+        deliveredStarts.clear();
+    }
+
     public String toString() {
         if (stringName != null) {
             return stringName;
diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index 81450c5..93c59cc 100644
--- a/services/java/com/android/server/am/UriPermission.java
+++ b/services/java/com/android/server/am/UriPermission.java
@@ -22,13 +22,20 @@
 import java.io.PrintWriter;
 import java.util.HashSet;
 
+interface UriPermissionOwner {
+    void addReadPermission(UriPermission perm);
+    void addWritePermission(UriPermission perm);
+    void removeReadPermission(UriPermission perm);
+    void removeWritePermission(UriPermission perm);
+}
+
 class UriPermission {
     final int uid;
     final Uri uri;
     int modeFlags = 0;
     int globalModeFlags = 0;
-    final HashSet<ActivityRecord> readActivities = new HashSet<ActivityRecord>();
-    final HashSet<ActivityRecord> writeActivities = new HashSet<ActivityRecord>();
+    final HashSet<UriPermissionOwner> readOwners = new HashSet<UriPermissionOwner>();
+    final HashSet<UriPermissionOwner> writeOwners = new HashSet<UriPermissionOwner>();
     
     String stringName;
     
@@ -41,27 +48,21 @@
         if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
             globalModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
             modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
-            if (readActivities.size() > 0) {
-                for (ActivityRecord r : readActivities) {
-                    r.readUriPermissions.remove(this);
-                    if (r.readUriPermissions.size() == 0) {
-                        r.readUriPermissions = null;
-                    }
+            if (readOwners.size() > 0) {
+                for (UriPermissionOwner r : readOwners) {
+                    r.removeReadPermission(this);
                 }
-                readActivities.clear();
+                readOwners.clear();
             }
         }
         if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
             globalModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
             modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
-            if (readActivities.size() > 0) {
-                for (ActivityRecord r : readActivities) {
-                    r.writeUriPermissions.remove(this);
-                    if (r.writeUriPermissions.size() == 0) {
-                        r.writeUriPermissions = null;
-                    }
+            if (readOwners.size() > 0) {
+                for (UriPermissionOwner r : writeOwners) {
+                    r.removeWritePermission(this);
                 }
-                readActivities.clear();
+                readOwners.clear();
             }
         }
     }
@@ -85,11 +86,17 @@
                 pw.print(" uid="); pw.print(uid); 
                 pw.print(" globalModeFlags=0x");
                 pw.println(Integer.toHexString(globalModeFlags));
-        if (readActivities.size() != 0) {
-            pw.print(prefix); pw.print("readActivities="); pw.println(readActivities);
+        if (readOwners.size() != 0) {
+            pw.print(prefix); pw.println("readOwners:");
+            for (UriPermissionOwner owner : readOwners) {
+                pw.print(prefix); pw.print("  * "); pw.println(owner);
+            }
         }
-        if (writeActivities.size() != 0) {
-            pw.print(prefix); pw.print("writeActivities="); pw.println(writeActivities);
+        if (writeOwners.size() != 0) {
+            pw.print(prefix); pw.println("writeOwners:");
+            for (UriPermissionOwner owner : writeOwners) {
+                pw.print(prefix); pw.print("  * "); pw.println(owner);
+            }
         }
     }
 }