App ops: cleanup, handle root and shell, perms.

Rework how the shell user is defined so that it is
associated with an actual apk, instead of being a free
roaming uid with special permissions assigned to it.
This allows us to correctly account for its operations
in app ops.

Implement a special case for the root user in app ops --
it is always allowed, always with the package name "root".

Add various code to take care of cleaning up package state
from app ops -- when packages are uninstalled, and during
boot if any packages currently being stored no longer exist.

Also fix a bug in the activity manager to correctly grant
permissions in all cases when onNewIntent() is being called.

Change-Id: Iae9f6d793ee48b93518c984ad957e46ae4582581
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index cf04b5c..d986c1e 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -122,71 +122,6 @@
          others should have a fairly open environment in which to
          interact with the system. -->
 
-    <!-- Standard permissions granted to the shell. -->
-    <assign-permission name="android.permission.WRITE_EXTERNAL_STORAGE" uid="shell" />
-    <assign-permission name="android.permission.SEND_SMS" uid="shell" />
-    <assign-permission name="android.permission.CALL_PHONE" uid="shell" />
-    <assign-permission name="android.permission.READ_CONTACTS" uid="shell" />
-    <assign-permission name="android.permission.WRITE_CONTACTS" uid="shell" />
-    <assign-permission name="android.permission.READ_CALENDAR" uid="shell" />
-    <assign-permission name="android.permission.WRITE_CALENDAR" uid="shell" />
-    <assign-permission name="android.permission.READ_USER_DICTIONARY" uid="shell" />
-    <assign-permission name="android.permission.WRITE_USER_DICTIONARY" uid="shell" />
-    <assign-permission name="android.permission.ACCESS_FINE_LOCATION" uid="shell" />
-    <assign-permission name="android.permission.ACCESS_COARSE_LOCATION" uid="shell" />
-    <assign-permission name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" uid="shell" />
-    <assign-permission name="android.permission.ACCESS_NETWORK_STATE" uid="shell" />
-    <assign-permission name="android.permission.ACCESS_WIFI_STATE" uid="shell" />
-    <assign-permission name="android.permission.BLUETOOTH" uid="shell" />
-    <assign-permission name="android.permission.EXPAND_STATUS_BAR" uid="shell" />
-    <!-- System tool permissions granted to the shell. -->
-    <assign-permission name="android.permission.GET_TASKS" uid="shell" />
-    <assign-permission name="android.permission.CHANGE_CONFIGURATION" uid="shell" />
-    <assign-permission name="android.permission.REORDER_TASKS" uid="shell" />
-    <assign-permission name="android.permission.SET_ANIMATION_SCALE" uid="shell" />
-    <assign-permission name="android.permission.SET_PREFERRED_APPLICATIONS" uid="shell" />
-    <assign-permission name="android.permission.WRITE_SETTINGS" uid="shell" />
-    <assign-permission name="android.permission.WRITE_SECURE_SETTINGS" uid="shell" />
-    <assign-permission name="android.permission.BROADCAST_STICKY" uid="shell" />
-    <!-- Development tool permissions granted to the shell. -->
-    <assign-permission name="android.permission.SET_DEBUG_APP" uid="shell" />
-    <assign-permission name="android.permission.SET_PROCESS_LIMIT" uid="shell" />
-    <assign-permission name="android.permission.SET_ALWAYS_FINISH" uid="shell" />
-    <assign-permission name="android.permission.DUMP" uid="shell" />
-    <assign-permission name="android.permission.SIGNAL_PERSISTENT_PROCESSES" uid="shell" />
-    <assign-permission name="android.permission.KILL_BACKGROUND_PROCESSES" uid="shell" />
-    <!-- Internal permissions granted to the shell. -->
-    <assign-permission name="android.permission.FORCE_BACK" uid="shell" />
-    <assign-permission name="android.permission.BATTERY_STATS" uid="shell" />
-    <assign-permission name="android.permission.INTERNAL_SYSTEM_WINDOW" uid="shell" />
-    <assign-permission name="android.permission.INJECT_EVENTS" uid="shell" />
-    <assign-permission name="android.permission.RETRIEVE_WINDOW_CONTENT" uid="shell" />
-    <assign-permission name="android.permission.SET_ACTIVITY_WATCHER" uid="shell" />
-    <assign-permission name="android.permission.READ_INPUT_STATE" uid="shell" />
-    <assign-permission name="android.permission.SET_ORIENTATION" uid="shell" />
-    <assign-permission name="android.permission.INSTALL_PACKAGES" uid="shell" />
-    <assign-permission name="android.permission.CLEAR_APP_USER_DATA" uid="shell" />
-    <assign-permission name="android.permission.DELETE_CACHE_FILES" uid="shell" />
-    <assign-permission name="android.permission.DELETE_PACKAGES" uid="shell" />
-    <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="shell" />
-    <assign-permission name="android.permission.READ_FRAME_BUFFER" uid="shell" />
-    <assign-permission name="android.permission.DEVICE_POWER" uid="shell" />
-    <assign-permission name="android.permission.INSTALL_LOCATION_PROVIDER" uid="shell" />
-    <assign-permission name="android.permission.BACKUP" uid="shell" />
-    <assign-permission name="android.permission.FORCE_STOP_PACKAGES" uid="shell" />
-    <assign-permission name="android.permission.STOP_APP_SWITCHES" uid="shell" />
-    <assign-permission name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY" uid="shell" />
-    <assign-permission name="android.permission.GRANT_REVOKE_PERMISSIONS" uid="shell" />
-    <assign-permission name="android.permission.SET_KEYBOARD_LAYOUT" uid="shell" />
-    <assign-permission name="android.permission.GET_DETAILED_TASKS" uid="shell" />
-    <assign-permission name="android.permission.SET_SCREEN_COMPATIBILITY" uid="shell" />
-    <assign-permission name="android.permission.READ_EXTERNAL_STORAGE" uid="shell" />
-    <assign-permission name="android.permission.WRITE_EXTERNAL_STORAGE" uid="shell" />
-    <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="shell" />
-    <assign-permission name="android.permission.INTERACT_ACROSS_USERS_FULL" uid="shell" />
-    <assign-permission name="android.permission.MANAGE_USERS" uid="shell" />
-    <assign-permission name="android.permission.BLUETOOTH_STACK" uid="shell" />
-    
     <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
     <assign-permission name="android.permission.ACCESS_DRM" uid="media" />
     <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
diff --git a/packages/Shell/Android.mk b/packages/Shell/Android.mk
new file mode 100644
index 0000000..f993ab5
--- /dev/null
+++ b/packages/Shell/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := Shell
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
new file mode 100644
index 0000000..b42db45
--- /dev/null
+++ b/packages/Shell/AndroidManifest.xml
@@ -0,0 +1,74 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.shell"
+        coreApp="true"
+        android:sharedUserId="android.uid.shell"
+        >
+
+    <!-- Standard permissions granted to the shell. -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.SEND_SMS" />
+    <uses-permission android:name="android.permission.CALL_PHONE" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+    <uses-permission android:name="android.permission.READ_CALENDAR" />
+    <uses-permission android:name="android.permission.WRITE_CALENDAR" />
+    <uses-permission android:name="android.permission.READ_USER_DICTIONARY" />
+    <uses-permission android:name="android.permission.WRITE_USER_DICTIONARY" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
+    <!-- System tool permissions granted to the shell. -->
+    <uses-permission android:name="android.permission.GET_TASKS" />
+    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
+    <uses-permission android:name="android.permission.REORDER_TASKS" />
+    <uses-permission android:name="android.permission.SET_ANIMATION_SCALE" />
+    <uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
+    <!-- Development tool permissions granted to the shell. -->
+    <uses-permission android:name="android.permission.SET_DEBUG_APP" />
+    <uses-permission android:name="android.permission.SET_PROCESS_LIMIT" />
+    <uses-permission android:name="android.permission.SET_ALWAYS_FINISH" />
+    <uses-permission android:name="android.permission.DUMP" />
+    <uses-permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES" />
+    <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
+    <!-- Internal permissions granted to the shell. -->
+    <uses-permission android:name="android.permission.FORCE_BACK" />
+    <uses-permission android:name="android.permission.BATTERY_STATS" />
+    <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
+    <uses-permission android:name="android.permission.INJECT_EVENTS" />
+    <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" />
+    <uses-permission android:name="android.permission.SET_ACTIVITY_WATCHER" />
+    <uses-permission android:name="android.permission.READ_INPUT_STATE" />
+    <uses-permission android:name="android.permission.SET_ORIENTATION" />
+    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
+    <uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
+    <uses-permission android:name="android.permission.DELETE_PACKAGES" />
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+    <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
+    <uses-permission android:name="android.permission.DEVICE_POWER" />
+    <uses-permission android:name="android.permission.INSTALL_LOCATION_PROVIDER" />
+    <uses-permission android:name="android.permission.BACKUP" />
+    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
+    <uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
+    <uses-permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY" />
+    <uses-permission android:name="android.permission.GRANT_REVOKE_PERMISSIONS" />
+    <uses-permission android:name="android.permission.SET_KEYBOARD_LAYOUT" />
+    <uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
+    <uses-permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+    <uses-permission android:name="android.permission.MANAGE_USERS" />
+    <uses-permission android:name="android.permission.BLUETOOTH_STACK" />
+    
+    <application android:hasCode="false" android:label="@string/app_label">
+    </application>
+</manifest>
diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml
new file mode 100644
index 0000000..50610d5
--- /dev/null
+++ b/packages/Shell/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <string name="app_label">Shell</string>
+</resources>
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
index e94d03c..a402642 100644
--- a/services/java/com/android/server/AppOpsService.java
+++ b/services/java/com/android/server/AppOpsService.java
@@ -149,6 +149,61 @@
         ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
     }
 
+    public void systemReady() {
+        synchronized (this) {
+            boolean changed = false;
+            for (int i=0; i<mUidOps.size(); i++) {
+                HashMap<String, Ops> pkgs = mUidOps.valueAt(i);
+                Iterator<Ops> it = pkgs.values().iterator();
+                while (it.hasNext()) {
+                    Ops ops = it.next();
+                    int curUid;
+                    try {
+                        curUid = mContext.getPackageManager().getPackageUid(ops.packageName,
+                                UserHandle.getUserId(ops.uid));
+                    } catch (NameNotFoundException e) {
+                        curUid = -1;
+                    }
+                    if (curUid != ops.uid) {
+                        Slog.i(TAG, "Pruning old package " + ops.packageName
+                                + "/" + ops.uid + ": new uid=" + curUid);
+                        it.remove();
+                        changed = true;
+                    }
+                }
+                if (pkgs.size() <= 0) {
+                    mUidOps.removeAt(i);
+                }
+            }
+            if (changed) {
+                scheduleWriteLocked();
+            }
+        }
+    }
+
+    public void packageRemoved(int uid, String packageName) {
+        synchronized (this) {
+            HashMap<String, Ops> pkgs = mUidOps.get(uid);
+            if (pkgs != null) {
+                if (pkgs.remove(packageName) != null) {
+                    if (pkgs.size() <= 0) {
+                        mUidOps.remove(uid);
+                    }
+                    scheduleWriteLocked();
+                }
+            }
+        }
+    }
+
+    public void uidRemoved(int uid) {
+        synchronized (this) {
+            if (mUidOps.indexOfKey(uid) >= 0) {
+                mUidOps.remove(uid);
+                scheduleWriteLocked();
+            }
+        }
+    }
+
     public void shutdown() {
         Slog.w(TAG, "Writing app ops before shutdown...");
         boolean doWrite = false;
@@ -258,6 +313,25 @@
                         }
                         repCbs.addAll(cbs);
                     }
+                    if (mode == AppOpsManager.MODE_ALLOWED) {
+                        // If going into the default mode, prune this op
+                        // if there is nothing else interesting in it.
+                        if (op.time == 0 && op.rejectTime == 0) {
+                            Ops ops = getOpsLocked(uid, packageName, false);
+                            if (ops != null) {
+                                ops.remove(op.op);
+                                if (ops.size() <= 0) {
+                                    HashMap<String, Ops> pkgOps = mUidOps.get(uid);
+                                    if (pkgOps != null) {
+                                        pkgOps.remove(ops.packageName);
+                                        if (pkgOps.size() <= 0) {
+                                            mUidOps.remove(uid);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
                     scheduleWriteNowLocked();
                 }
             }
@@ -368,6 +442,7 @@
             if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
                     + " package " + packageName);
             op.time = System.currentTimeMillis();
+            op.rejectTime = 0;
             return AppOpsManager.MODE_ALLOWED;
         }
     }
@@ -396,6 +471,7 @@
                     + " package " + packageName);
             if (op.nesting == 0) {
                 op.time = System.currentTimeMillis();
+                op.rejectTime = 0;
                 op.duration = -1;
             }
             op.nesting++;
@@ -415,6 +491,7 @@
             if (op.nesting <= 1) {
                 if (op.nesting == 1) {
                     op.duration = (int)(System.currentTimeMillis() - op.time);
+                    op.time += op.duration;
                 } else {
                     Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + packageName
                         + " code " + code + " time=" + op.time + " duration=" + op.duration
@@ -454,6 +531,11 @@
             pkgOps = new HashMap<String, Ops>();
             mUidOps.put(uid, pkgOps);
         }
+        if (uid == 0) {
+            packageName = "root";
+        } else if (uid == Process.SHELL_UID) {
+            packageName = "com.android.shell";
+        }
         Ops ops = pkgOps.get(packageName);
         if (ops == null) {
             if (!edit) {
@@ -461,23 +543,25 @@
             }
             // This is the first time we have seen this package name under this uid,
             // so let's make sure it is valid.
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                int pkgUid = -1;
+            if (uid != 0) {
+                final long ident = Binder.clearCallingIdentity();
                 try {
-                    pkgUid = mContext.getPackageManager().getPackageUid(packageName,
-                            UserHandle.getUserId(uid));
-                } catch (NameNotFoundException e) {
+                    int pkgUid = -1;
+                    try {
+                        pkgUid = mContext.getPackageManager().getPackageUid(packageName,
+                                UserHandle.getUserId(uid));
+                    } catch (NameNotFoundException e) {
+                    }
+                    if (pkgUid != uid) {
+                        // Oops!  The package name is not valid for the uid they are calling
+                        // under.  Abort.
+                        Slog.w(TAG, "Bad call: specified package " + packageName
+                                + " under uid " + uid + " but it is really " + pkgUid);
+                        return null;
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
                 }
-                if (pkgUid != uid) {
-                    // Oops!  The package name is not valid for the uid they are calling
-                    // under.  Abort.
-                    Slog.w(TAG, "Bad call: specified package " + packageName
-                            + " under uid " + uid + " but it is really " + pkgUid);
-                    return null;
-                }
-            } finally {
-                Binder.restoreCallingIdentity(ident);
             }
             ops = new Ops(packageName, uid);
             pkgOps.put(packageName, ops);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 7b6e79e..4fbacb8 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -7925,7 +7925,8 @@
                 }
                 mDidUpdate = true;
             }
-            
+
+            mAppOpsService.systemReady();
             mSystemReady = true;
             if (!mStartRunning) {
                 return;
@@ -11779,6 +11780,7 @@
                         synchronized (bs) {
                             bs.removeUidStatsLocked(uid);
                         }
+                        mAppOpsService.uidRemoved(uid);
                     }
                 } else {
                     // If resources are unavailable just force stop all
@@ -11804,6 +11806,10 @@
                             if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
                                 sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,
                                         new String[] {ssp}, userId);
+                                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                                    mAppOpsService.packageRemoved(
+                                            intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);
+                                }
                             }
                         }
                     }
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index ba2e47a..cde17c9 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -574,6 +574,9 @@
      */
     final void deliverNewIntentLocked(int callingUid, Intent intent) {
         boolean sent = false;
+        // The activity now gets access to the data associated with this Intent.
+        service.grantUriPermissionFromIntentLocked(callingUid, packageName,
+                intent, getUriPermissionsLocked());
         // We want to immediately deliver the intent to the activity if
         // it is currently the top resumed activity...  however, if the
         // device is sleeping, then all activities are stopped, so in that
@@ -586,8 +589,6 @@
                 ArrayList<Intent> ar = new ArrayList<Intent>();
                 intent = new Intent(intent);
                 ar.add(intent);
-                service.grantUriPermissionFromIntentLocked(callingUid, packageName,
-                        intent, getUriPermissionsLocked());
                 app.thread.scheduleNewIntent(ar, appToken);
                 sent = true;
             } catch (RemoteException e) {
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 46d2cca..e26f8fd 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -185,6 +185,7 @@
     private static final int LOG_UID = Process.LOG_UID;
     private static final int NFC_UID = Process.NFC_UID;
     private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
+    private static final int SHELL_UID = Process.SHELL_UID;
 
     private static final boolean GET_CERTIFICATES = true;
 
@@ -974,6 +975,7 @@
         mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM);
         mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM);
         mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM);
+        mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, ApplicationInfo.FLAG_SYSTEM);
 
         String separateProcesses = SystemProperties.get("debug.separate_processes");
         if (separateProcesses != null && separateProcesses.length() > 0) {