Work on issue #9586838: Crash after waking up Hammerhead device

I made the power manager more rigid, not allowing different uids
to use the same wake lock.  This never should happen.  I would
guess there is somewhere that the activity manager is acquiring
the wake lock without clearing the calling identity...  but it is
hard to follow all the paths this may happen in.  So here we add
some checks when acquiring/releasing the wake lock to make sure
it is being done as the system uid.

Also:

- Protect the new activity stack calls with a permission, and
make sure to clear the calling uid once past that.
- Collect uid data from process stats so we can correctly
associate CPU use with a uid even if we don't know about the
pid for some reason.
- Fix battery stats dump commands to clear calling uid before
executing so they aren't broken.

Change-Id: I0030d4f7b614e3270d794ecfc3669139a5703ce9
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 9666d9a..97ea99d 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -60,6 +60,8 @@
 
     public static native int setPermissions(String file, int mode, int uid, int gid);
 
+    public static native int getUid(String file);
+
     /** returns the FAT file system volume ID for the volume mounted 
      * at the given mount point, or -1 for failure
      * @param mountPoint point for FAT volume
diff --git a/core/java/com/android/internal/os/ProcessStats.java b/core/java/com/android/internal/os/ProcessStats.java
index b63dce5..874bc0e 100644
--- a/core/java/com/android/internal/os/ProcessStats.java
+++ b/core/java/com/android/internal/os/ProcessStats.java
@@ -18,6 +18,7 @@
 
 import static android.os.Process.*;
 
+import android.os.FileUtils;
 import android.os.Process;
 import android.os.StrictMode;
 import android.os.SystemClock;
@@ -174,12 +175,15 @@
 
     public static class Stats {
         public final int pid;
+        public final int uid;
         final String statFile;
         final String cmdlineFile;
         final String threadsDir;
         final ArrayList<Stats> threadStats;
         final ArrayList<Stats> workingThreads;
-        
+
+        public BatteryStatsImpl.Uid.Proc batteryStats;
+
         public boolean interesting;
 
         public String baseName;
@@ -229,6 +233,7 @@
                 threadStats = null;
                 workingThreads = null;
             }
+            uid = FileUtils.getUid(statFile.toString());
         }
     }
 
diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp
index a07f5b7..0aaa2b1 100644
--- a/core/jni/android_os_FileUtils.cpp
+++ b/core/jni/android_os_FileUtils.cpp
@@ -55,6 +55,24 @@
     return chmod(file8.string(), mode) == 0 ? 0 : errno;
 }
 
+jint android_os_FileUtils_getUid(JNIEnv* env, jobject clazz, jstring file)
+{
+    struct stat stats;
+    const jchar* str = env->GetStringCritical(file, 0);
+    String8 file8;
+    if (str) {
+        file8 = String8(str, env->GetStringLength(file));
+        env->ReleaseStringCritical(file, str);
+    }
+    if (file8.size() <= 0) {
+        return ENOENT;
+    }
+    if (stat(file8.string(), &stats) < 0) {
+        return -1;
+    }
+    return stats.st_uid;
+}
+
 jint android_os_FileUtils_getFatVolumeId(JNIEnv* env, jobject clazz, jstring path)
 {
     if (path == NULL) {
@@ -78,6 +96,7 @@
 
 static const JNINativeMethod methods[] = {
     {"setPermissions",  "(Ljava/lang/String;III)I", (void*)android_os_FileUtils_setPermissions},
+    {"getUid",          "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getUid},
     {"getFatVolumeId",  "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getFatVolumeId},
 };
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ae6d141..7ddb0dc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1222,6 +1222,13 @@
         android:label="@string/permlab_removeTasks"
         android:description="@string/permdesc_removeTasks" />
 
+    <!-- @hide Allows an application to create/manage/remove stacks -->
+    <permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
+        android:permissionGroup="android.permission-group.APP_INFO"
+        android:protectionLevel="signature"
+        android:label="@string/permlab_manageActivityStacks"
+        android:description="@string/permdesc_manageActivityStacks" />
+
     <!-- Allows an application to start any activity, regardless of permission
          protection or exported state. @hide -->
     <permission android:name="android.permission.START_ANY_ACTIVITY"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4b02a7d..75ff8b2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -712,6 +712,15 @@
         tasks and kill their apps. Malicious apps may disrupt
         the behavior of other apps.</string>
 
+    <!-- [CHAR LIMIT=NONE] Title of an application permission, allowing an application to create,
+         change, remove activity stacks. -->
+    <string name="permlab_manageActivityStacks">manage activity stacks</string>
+    <!-- [CHAR LIMIT=NONE] Description of an application permission, allowing an application to create,
+             change, remove activity stacks. -->
+    <string name="permdesc_manageActivityStacks">Allows the app to add, remove, and
+        modify the activity stacks in which other apps run.  Malicious apps may disrupt
+        the behavior of other apps.</string>
+
     <!-- Title of an application permission, allowing an application to start any activity, regardless of permission protection or exported state. -->
     <string name="permlab_startAnyActivity">start any activity</string>
     <!-- Description of an application permission, allowing an application to start any activity, regardless of permission protection or exported state. -->
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 12a0549..de58496 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -642,15 +642,25 @@
                         update = false;
                     }
                     if (update) {
-                        mUpdatesStopped = true;
-                        processValuesLocked();
+                        long ident = Binder.clearCallingIdentity();
+                        try {
+                            mUpdatesStopped = true;
+                            processValuesLocked();
+                        } finally {
+                            Binder.restoreCallingIdentity(ident);
+                        }
                     }
                 } catch (NumberFormatException ex) {
                     pw.println("Bad value: " + value);
                 }
             } else if (args.length == 1 && "reset".equals(args[0])) {
-                mUpdatesStopped = false;
-                updateLocked();
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    mUpdatesStopped = false;
+                    updateLocked();
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             } else {
                 pw.println("Dump current battery state, or:");
                 pw.println("  set ac|usb|wireless|status|level|invalid <value>");
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 6f367e0..475092a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1923,6 +1923,16 @@
                                             st.rel_stime-otherSTime);
                                     ps.addSpeedStepTimes(cpuSpeedTimes);
                                     pr.curCpuTime += (st.rel_utime+st.rel_stime) * 10;
+                                } else if (st.uid >= Process.FIRST_APPLICATION_UID) {
+                                    BatteryStatsImpl.Uid.Proc ps = st.batteryStats;
+                                    if (ps == null) {
+                                        st.batteryStats = ps = bstats.getProcessStatsLocked(st.uid,
+                                                "(Unknown)");
+                                    }
+                                    ps.addCpuTimeLocked(st.rel_utime-otherUTime,
+                                            st.rel_stime-otherSTime);
+                                    ps.addSpeedStepTimes(cpuSpeedTimes);
+                                    pr.curCpuTime += (st.rel_utime+st.rel_stime) * 10;
                                 } else {
                                     BatteryStatsImpl.Uid.Proc ps =
                                             bstats.getProcessStatsLocked(st.name, st.pid);
@@ -6370,34 +6380,55 @@
 
     @Override
     public int createStack(int taskId, int relativeStackBoxId, int position, float weight) {
+        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "createStack()");
         if (DEBUG_STACK) Slog.d(TAG, "createStack: taskId=" + taskId + " relStackBoxId=" +
                 relativeStackBoxId + " position=" + position + " weight=" + weight);
         synchronized (this) {
-            int stackId = mStackSupervisor.createStack();
-            mWindowManager.createStack(stackId, relativeStackBoxId, position, weight);
-            if (taskId > 0) {
-                moveTaskToStack(taskId, stackId, true);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                int stackId = mStackSupervisor.createStack();
+                mWindowManager.createStack(stackId, relativeStackBoxId, position, weight);
+                if (taskId > 0) {
+                    moveTaskToStack(taskId, stackId, true);
+                }
+                return stackId;
+            } finally {
+                Binder.restoreCallingIdentity(ident);
             }
-            return stackId;
         }
     }
 
     @Override
     public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
+        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "moveTaskToStack()");
         if (stackId == HOME_STACK_ID) {
             Slog.e(TAG, "moveTaskToStack: Attempt to move task " + taskId + " to home stack",
                     new RuntimeException("here").fillInStackTrace());
         }
         synchronized (this) {
-            if (DEBUG_STACK) Slog.d(TAG, "moveTaskToStack: moving task=" + taskId + " to stackId="
-                    + stackId + " toTop=" + toTop);
-            mStackSupervisor.moveTaskToStack(taskId, stackId, toTop);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG_STACK) Slog.d(TAG, "moveTaskToStack: moving task=" + taskId + " to stackId="
+                        + stackId + " toTop=" + toTop);
+                mStackSupervisor.moveTaskToStack(taskId, stackId, toTop);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
     }
 
     @Override
     public void resizeStackBox(int stackBoxId, float weight) {
-        mWindowManager.resizeStackBox(stackBoxId, weight);
+        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "resizeStackBox()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            mWindowManager.resizeStackBox(stackBoxId, weight);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
     }
 
     private ArrayList<StackInfo> getStacks() {
@@ -6447,30 +6478,44 @@
 
     @Override
     public List<StackBoxInfo> getStackBoxes() {
-        List<StackBoxInfo> stackBoxInfos = mWindowManager.getStackBoxInfos();
-        synchronized (this) {
-            List<StackInfo> stackInfos = getStacks();
-            for (StackBoxInfo stackBoxInfo : stackBoxInfos) {
-                addStackInfoToStackBoxInfo(stackBoxInfo, stackInfos);
+        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "getStackBoxes()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            List<StackBoxInfo> stackBoxInfos = mWindowManager.getStackBoxInfos();
+            synchronized (this) {
+                List<StackInfo> stackInfos = getStacks();
+                for (StackBoxInfo stackBoxInfo : stackBoxInfos) {
+                    addStackInfoToStackBoxInfo(stackBoxInfo, stackInfos);
+                }
             }
+            return stackBoxInfos;
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
-        return stackBoxInfos;
     }
 
     @Override
     public StackBoxInfo getStackBoxInfo(int stackBoxId) {
-        List<StackBoxInfo> stackBoxInfos = mWindowManager.getStackBoxInfos();
-        StackBoxInfo info = null;
-        synchronized (this) {
-            List<StackInfo> stackInfos = getStacks();
-            for (StackBoxInfo stackBoxInfo : stackBoxInfos) {
-                addStackInfoToStackBoxInfo(stackBoxInfo, stackInfos);
-                if (stackBoxInfo.stackBoxId == stackBoxId) {
-                    info = stackBoxInfo;
+        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "getStackBoxInfo()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            List<StackBoxInfo> stackBoxInfos = mWindowManager.getStackBoxInfos();
+            StackBoxInfo info = null;
+            synchronized (this) {
+                List<StackInfo> stackInfos = getStacks();
+                for (StackBoxInfo stackBoxInfo : stackBoxInfos) {
+                    addStackInfoToStackBoxInfo(stackBoxInfo, stackInfos);
+                    if (stackBoxInfo.stackBoxId == stackBoxId) {
+                        info = stackBoxInfo;
+                    }
                 }
             }
+            return info;
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
-        return info;
     }
 
     @Override
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 1e19989..a9ab53d 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -69,6 +69,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -120,6 +121,10 @@
     // is being started.
     static final boolean SHOW_APP_STARTING_PREVIEW = true;
 
+    // For debugging to make sure the caller when acquiring/releasing our
+    // wake lock is the system process.
+    static final boolean VALIDATE_WAKE_LOCK_CALLER = true;
+
     enum ActivityState {
         INITIALIZING,
         RESUMED,
@@ -288,6 +293,10 @@
                     synchronized (mService) {
                         if (mLaunchingActivity.isHeld()) {
                             Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
+                            if (VALIDATE_WAKE_LOCK_CALLER
+                                    && Binder.getCallingUid() != Process.myUid()) {
+                                throw new IllegalStateException("Calling must be system uid");
+                            }
                             mLaunchingActivity.release();
                         }
                     }
@@ -328,6 +337,9 @@
         mStackSupervisor = service.mStackSupervisor;
         mContext = context;
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
+            throw new IllegalStateException("Calling must be system uid");
+        }
         mLaunchingActivity =
                 pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
         mLaunchingActivity.setReferenceCounted(false);
@@ -595,6 +607,9 @@
 
     void stopIfSleepingLocked() {
         if (mLaunchingActivity.isHeld()) {
+            if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
+                throw new IllegalStateException("Calling must be system uid");
+            }
             mLaunchingActivity.release();
             mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
         }
@@ -722,6 +737,9 @@
         // If we are not going to sleep, we want to ensure the device is
         // awake until the next activity is started.
         if (!mService.isSleepingOrShuttingDown()) {
+            if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
+                throw new IllegalStateException("Calling must be system uid");
+            }
             mLaunchingActivity.acquire();
             if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) {
                 // To be safe, don't allow the wake lock to be held for too long.
@@ -2092,6 +2110,9 @@
             // No longer need to keep the device awake.
             if (mResumedActivity == res && mLaunchingActivity.isHeld()) {
                 mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+                if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
+                    throw new IllegalStateException("Calling must be system uid");
+                }
                 mLaunchingActivity.release();
             }