Merge "Revert "App home directories are now 0700 for targetSdkVersion > 17""
diff --git a/Android.mk b/Android.mk
index 294a2fe..462d6ac 100644
--- a/Android.mk
+++ b/Android.mk
@@ -166,6 +166,7 @@
 	core/java/android/speech/IRecognitionService.aidl \
 	core/java/android/speech/tts/ITextToSpeechCallback.aidl \
 	core/java/android/speech/tts/ITextToSpeechService.aidl \
+	core/java/com/android/internal/app/IAppOpsService.aidl \
 	core/java/com/android/internal/app/IBatteryStats.aidl \
 	core/java/com/android/internal/app/IUsageStats.aidl \
 	core/java/com/android/internal/app/IMediaContainerService.aidl \
diff --git a/api/current.txt b/api/current.txt
index cf24f13..632d779 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13785,12 +13785,18 @@
     method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
     method public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...);
     method public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
+    field public static final java.lang.String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
     field public static final java.lang.String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
     field public static final java.lang.String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
     field public static final java.lang.String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED";
+    field public static final java.lang.String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE";
     field public static final java.lang.String EXTRA_ID = "android.nfc.extra.ID";
     field public static final java.lang.String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
     field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG";
+    field public static final int STATE_OFF = 1; // 0x1
+    field public static final int STATE_ON = 3; // 0x3
+    field public static final int STATE_TURNING_OFF = 4; // 0x4
+    field public static final int STATE_TURNING_ON = 2; // 0x2
   }
 
   public static abstract interface NfcAdapter.CreateBeamUrisCallback {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
new file mode 100644
index 0000000..7210df4
--- /dev/null
+++ b/core/java/android/app/AppOpsManager.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2012 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.app;
+
+import com.android.internal.app.IAppOpsService;
+
+import android.content.Context;
+import android.os.Process;
+import android.os.RemoteException;
+
+/** @hide */
+public class AppOpsManager {
+    final Context mContext;
+    final IAppOpsService mService;
+
+    public static final int MODE_ALLOWED = 0;
+    public static final int MODE_IGNORED = 1;
+    public static final int MODE_ERRORED = 2;
+
+    public static final int OP_LOCATION = 0;
+    public static final int OP_GPS = 1;
+    public static final int OP_VIBRATE = 2;
+
+    public static String opToString(int op) {
+        switch (op) {
+            case OP_LOCATION: return "LOCATION";
+            case OP_GPS: return "GPS";
+            case OP_VIBRATE: return "VIBRATE";
+            default: return "Unknown(" + op + ")";
+        }
+    }
+
+    public AppOpsManager(Context context, IAppOpsService service) {
+        mContext = context;
+        mService = service;
+    }
+
+    public int noteOp(int op, int uid, String packageName) {
+        try {
+            int mode = mService.noteOperation(op, uid, packageName);
+            if (mode == MODE_ERRORED) {
+                throw new SecurityException("Operation not allowed");
+            }
+            return mode;
+        } catch (RemoteException e) {
+        }
+        return MODE_IGNORED;
+    }
+
+    public int noteOpNoThrow(int op, int uid, String packageName) {
+        try {
+            return mService.noteOperation(op, uid, packageName);
+        } catch (RemoteException e) {
+        }
+        return MODE_IGNORED;
+    }
+
+    public int noteOp(int op) {
+        return noteOp(op, Process.myUid(), mContext.getPackageName());
+    }
+
+    public int startOp(int op, int uid, String packageName) {
+        try {
+            int mode = mService.startOperation(op, uid, packageName);
+            if (mode == MODE_ERRORED) {
+                throw new SecurityException("Operation not allowed");
+            }
+            return mode;
+        } catch (RemoteException e) {
+        }
+        return MODE_IGNORED;
+    }
+
+    public int startOpNoThrow(int op, int uid, String packageName) {
+        try {
+            return mService.startOperation(op, uid, packageName);
+        } catch (RemoteException e) {
+        }
+        return MODE_IGNORED;
+    }
+
+    public int startOp(int op) {
+        return startOp(op, Process.myUid(), mContext.getPackageName());
+    }
+
+    public void finishOp(int op, int uid, String packageName) {
+        try {
+            mService.finishOperation(op, uid, packageName);
+        } catch (RemoteException e) {
+        }
+    }
+
+    public void finishOp(int op) {
+        finishOp(op, Process.myUid(), mContext.getPackageName());
+    }
+}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7431765..03d1a3f 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -142,6 +142,21 @@
     }
 
     @Override
+    public int getPackageUid(String packageName, int userHandle)
+            throws NameNotFoundException {
+        try {
+            int uid = mPM.getPackageUid(packageName, userHandle);
+            if (uid >= 0) {
+                return uid;
+            }
+        } catch (RemoteException e) {
+            throw new RuntimeException("Package manager has died", e);
+        }
+
+        throw new NameNotFoundException(packageName);
+    }
+
+    @Override
     public PermissionInfo getPermissionInfo(String name, int flags)
             throws NameNotFoundException {
         try {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f895ccc..8ef708c 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -47,11 +47,9 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.hardware.ISerialManager;
-import android.hardware.SensorManager;
 import android.hardware.SerialManager;
 import android.hardware.SystemSensorManager;
 import android.hardware.display.DisplayManager;
-import android.hardware.input.IInputManager;
 import android.hardware.input.InputManager;
 import android.hardware.usb.IUsbManager;
 import android.hardware.usb.UsbManager;
@@ -109,6 +107,8 @@
 import android.accounts.AccountManager;
 import android.accounts.IAccountManager;
 import android.app.admin.DevicePolicyManager;
+
+import com.android.internal.app.IAppOpsService;
 import com.android.internal.os.IDropBoxManagerService;
 
 import java.io.File;
@@ -499,7 +499,7 @@
 
         registerService(VIBRATOR_SERVICE, new ServiceFetcher() {
                 public Object createService(ContextImpl ctx) {
-                    return new SystemVibrator();
+                    return new SystemVibrator(ctx);
                 }});
 
         registerService(WALLPAPER_SERVICE, WALLPAPER_FETCHER);
@@ -530,11 +530,18 @@
                 }});
 
         registerService(USER_SERVICE, new ServiceFetcher() {
-            public Object getService(ContextImpl ctx) {
+            public Object createService(ContextImpl ctx) {
                 IBinder b = ServiceManager.getService(USER_SERVICE);
                 IUserManager service = IUserManager.Stub.asInterface(b);
                 return new UserManager(ctx, service);
             }});
+
+        registerService(APP_OPS_SERVICE, new ServiceFetcher() {
+            public Object createService(ContextImpl ctx) {
+                IBinder b = ServiceManager.getService(APP_OPS_SERVICE);
+                IAppOpsService service = IAppOpsService.Stub.asInterface(b);
+                return new AppOpsManager(ctx, service);
+            }});
     }
 
     static ContextImpl getImpl(Context context) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 7aa2507..41d470b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2255,6 +2255,18 @@
     public static final String USER_SERVICE = "user";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.app.AppOpsManager} for tracking application operations
+     * on the device.
+     *
+     * @see #getSystemService
+     * @see android.app.AppOpsManager
+     *
+     * @hide
+     */
+    public static final String APP_OPS_SERVICE = "appops";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8ba1988..cdd9195 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1280,6 +1280,22 @@
             throws NameNotFoundException;
 
     /**
+     * @hide Return the uid associated with the given package name for the
+     * given user.
+     *
+     * <p>Throws {@link NameNotFoundException} if a package with the given
+     * name can not be found on the system.
+     *
+     * @param packageName The full name (i.e. com.google.apps.contacts) of the
+     *                    desired package.
+     * @param userHandle The user handle identifier to look up the package under.
+     *
+     * @return Returns an integer uid who owns the given package name.
+     */
+    public abstract int getPackageUid(String packageName, int userHandle)
+            throws NameNotFoundException;
+
+    /**
      * Retrieve all of the information we know about a particular permission.
      *
      * <p>Throws {@link NameNotFoundException} if a permission with the given
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 4baceed..6ad382b 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -174,31 +174,25 @@
      * Broadcast Action: The state of the local NFC adapter has been
      * changed.
      * <p>For example, NFC has been turned on or off.
-     * <p>Always contains the extra field {@link #EXTRA_STATE}
-     * @hide
+     * <p>Always contains the extra field {@link #EXTRA_ADAPTER_STATE}
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_ADAPTER_STATE_CHANGED =
             "android.nfc.action.ADAPTER_STATE_CHANGED";
 
     /**
-     * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
+     * Used as an int extra field in {@link #ACTION_ADAPTER_STATE_CHANGED}
      * intents to request the current power state. Possible values are:
      * {@link #STATE_OFF},
      * {@link #STATE_TURNING_ON},
      * {@link #STATE_ON},
      * {@link #STATE_TURNING_OFF},
-     * @hide
      */
     public static final String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE";
 
-    /** @hide */
     public static final int STATE_OFF = 1;
-    /** @hide */
     public static final int STATE_TURNING_ON = 2;
-    /** @hide */
     public static final int STATE_ON = 3;
-    /** @hide */
     public static final int STATE_TURNING_OFF = 4;
 
     /** @hide */
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 9821824..abbb6a1 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -93,6 +93,11 @@
     public static final int VIDEO_TURNED_ON = 8;
 
     /**
+     * A constant indicating a vibrator on timer
+     */
+    public static final int VIBRATOR_ON = 9;
+
+    /**
      * Include all of the data in the stats, including previously saved data.
      */
     public static final int STATS_SINCE_CHARGED = 0;
@@ -131,6 +136,7 @@
     private static final String APK_DATA = "apk";
     private static final String PROCESS_DATA = "pr";
     private static final String SENSOR_DATA = "sr";
+    private static final String VIBRATOR_DATA = "vib";
     private static final String WAKELOCK_DATA = "wl";
     private static final String KERNEL_WAKELOCK_DATA = "kwl";
     private static final String NETWORK_DATA = "nt";
@@ -277,6 +283,7 @@
                                                   int which);
         public abstract long getAudioTurnedOnTime(long batteryRealtime, int which);
         public abstract long getVideoTurnedOnTime(long batteryRealtime, int which);
+        public abstract Timer getVibratorOnTimer();
 
         /**
          * Note that these must match the constants in android.os.PowerManager.
@@ -1395,6 +1402,16 @@
                 }
             }
 
+            Timer vibTimer = u.getVibratorOnTimer();
+            if (vibTimer != null) {
+                // Convert from microseconds to milliseconds with rounding
+                long totalTime = (vibTimer.getTotalTimeLocked(batteryRealtime, which) + 500) / 1000;
+                int count = vibTimer.getCountLocked(which);
+                if (totalTime != 0) {
+                    dumpLine(pw, uid, category, VIBRATOR_DATA, totalTime, count);
+                }
+            }
+
             Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
             if (processStats.size() > 0) {
                 for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
@@ -1919,6 +1936,26 @@
                 }
             }
 
+            Timer vibTimer = u.getVibratorOnTimer();
+            if (vibTimer != null) {
+                // Convert from microseconds to milliseconds with rounding
+                long totalTime = (vibTimer.getTotalTimeLocked(
+                        batteryRealtime, which) + 500) / 1000;
+                int count = vibTimer.getCountLocked(which);
+                //timer.logState();
+                if (totalTime != 0) {
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("    Vibrator: ");
+                    formatTimeMs(sb, totalTime);
+                    sb.append("realtime (");
+                    sb.append(count);
+                    sb.append(" times)");
+                    pw.println(sb.toString());
+                    uidActivity = true;
+                }
+            }
+
             Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
             if (processStats.size() > 0) {
                 for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index 2c2fe8a..15cedf9 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -20,8 +20,8 @@
 interface IVibratorService
 {
     boolean hasVibrator();
-    void vibrate(long milliseconds, IBinder token);
-    void vibratePattern(in long[] pattern, int repeat, IBinder token);
+    void vibrate(String packageName, long milliseconds, IBinder token);
+    void vibratePattern(String packageName, in long[] pattern, int repeat, IBinder token);
     void cancelVibrate(IBinder token);
 }
 
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 7c5a47e..54ea385 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.content.Context;
 import android.util.Log;
 
 /**
@@ -26,10 +27,18 @@
 public class SystemVibrator extends Vibrator {
     private static final String TAG = "Vibrator";
 
+    private final String mPackageName;
     private final IVibratorService mService;
     private final Binder mToken = new Binder();
 
     public SystemVibrator() {
+        mPackageName = null;
+        mService = IVibratorService.Stub.asInterface(
+                ServiceManager.getService("vibrator"));
+    }
+
+    public SystemVibrator(Context context) {
+        mPackageName = context.getPackageName();
         mService = IVibratorService.Stub.asInterface(
                 ServiceManager.getService("vibrator"));
     }
@@ -54,7 +63,7 @@
             return;
         }
         try {
-            mService.vibrate(milliseconds, mToken);
+            mService.vibrate(mPackageName, milliseconds, mToken);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to vibrate.", e);
         }
@@ -71,7 +80,7 @@
         // anyway
         if (repeat < pattern.length) {
             try {
-                mService.vibratePattern(pattern, repeat, mToken);
+                mService.vibratePattern(mPackageName, pattern, repeat, mToken);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to vibrate.", e);
             }
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index cc96152..d205253 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import java.io.PrintWriter;
+
 /**
  * Representation of a user on the device.
  */
@@ -152,6 +154,50 @@
     }
 
     /**
+     * Generate a text representation of the uid, breaking out its individual
+     * components -- user, app, isolated, etc.
+     * @hide
+     */
+    public static void formatUid(StringBuilder sb, int uid) {
+        if (uid < Process.FIRST_APPLICATION_UID) {
+            sb.append(uid);
+        } else {
+            sb.append('u');
+            sb.append(getUserId(uid));
+            final int appId = getAppId(uid);
+            if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
+                sb.append('i');
+                sb.append(appId - Process.FIRST_ISOLATED_UID);
+            } else {
+                sb.append('a');
+                sb.append(appId);
+            }
+        }
+    }
+
+    /**
+     * Generate a text representation of the uid, breaking out its individual
+     * components -- user, app, isolated, etc.
+     * @hide
+     */
+    public static void formatUid(PrintWriter pw, int uid) {
+        if (uid < Process.FIRST_APPLICATION_UID) {
+            pw.print(uid);
+        } else {
+            pw.print('u');
+            pw.print(getUserId(uid));
+            final int appId = getAppId(uid);
+            if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
+                pw.print('i');
+                pw.print(appId - Process.FIRST_ISOLATED_UID);
+            } else {
+                pw.print('a');
+                pw.print(appId);
+            }
+        }
+    }
+
+    /**
      * Returns the user id of the current process
      * @return user id of the current process
      * @hide
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index b40260c..8c710ce 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -567,7 +567,10 @@
         applied to the drawable, be sure to call setImageMatrix().
     */
     public Matrix getImageMatrix() {
-        return mMatrix;
+        if (mDrawMatrix == null) {
+            return Matrix.IDENTITY_MATRIX;
+        }
+        return mDrawMatrix;
     }
 
     public void setImageMatrix(Matrix matrix) {
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
new file mode 100644
index 0000000..c934587
--- /dev/null
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+interface IAppOpsService {
+    int noteOperation(int code, int uid, String packageName);
+    int startOperation(int code, int uid, String packageName);
+    void finishOperation(int code, int uid, String packageName);
+    int noteTimedOperation(int code, int uid, String packageName, int duration);
+    void earlyFinishOperation(int code, int uid, String packageName);
+}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 1a76461..823e19f 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -37,6 +37,8 @@
     void noteStartWakelockFromSource(in WorkSource ws, int pid, String name, int type);
     void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, int type);
 
+    void noteVibratorOn(int uid, long durationMillis);
+    void noteVibratorOff(int uid);
     void noteStartGps(int uid);
     void noteStopGps(int uid);
     void noteScreenOn();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 94e7a06..4d35a6b 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,14 +16,11 @@
 
 package com.android.internal.os;
 
-import static android.net.NetworkStats.IFACE_ALL;
-import static android.net.NetworkStats.UID_ALL;
 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
 import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
 
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
-import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.net.NetworkStats;
 import android.os.BatteryManager;
@@ -49,7 +46,6 @@
 import android.util.SparseArray;
 import android.util.TimeUtils;
 
-import com.android.internal.R;
 import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.util.JournaledFile;
 import com.google.android.collect.Sets;
@@ -87,7 +83,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 62 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 64 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -356,8 +352,8 @@
     }
 
     public static interface Unpluggable {
-        void unplug(long batteryUptime, long batteryRealtime);
-        void plug(long batteryUptime, long batteryRealtime);
+        void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime);
+        void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime);
     }
 
     /**
@@ -392,12 +388,12 @@
             out.writeInt(mUnpluggedCount);
         }
 
-        public void unplug(long batteryUptime, long batteryRealtime) {
+        public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
             mUnpluggedCount = mPluggedCount;
             mCount.set(mPluggedCount);
         }
 
-        public void plug(long batteryUptime, long batteryRealtime) {
+        public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
             mPluggedCount = mCount.get();
         }
 
@@ -587,7 +583,7 @@
             out.writeLong(mUnpluggedTime);
         }
 
-        public void unplug(long batteryUptime, long batteryRealtime) {
+        public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
             if (DEBUG && mType < 0) {
                 Log.v(TAG, "unplug #" + mType + ": realtime=" + batteryRealtime
                         + " old mUnpluggedTime=" + mUnpluggedTime
@@ -602,7 +598,7 @@
             }
         }
 
-        public void plug(long batteryUptime, long batteryRealtime) {
+        public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
             if (DEBUG && mType < 0) {
                 Log.v(TAG, "plug #" + mType + ": realtime=" + batteryRealtime
                         + " old mTotalTime=" + mTotalTime);
@@ -731,7 +727,7 @@
         boolean mTrackingReportedValues;
 
         /*
-         * A sequnce counter, incremented once for each update of the stats.
+         * A sequence counter, incremented once for each update of the stats.
          */
         int mUpdateVersion;
 
@@ -786,8 +782,8 @@
             mCurrentReportedTotalTime = totalTime;
         }
 
-        public void unplug(long batteryUptime, long batteryRealtime) {
-            super.unplug(batteryUptime, batteryRealtime);
+        public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+            super.unplug(elapsedRealtime, batteryUptime, batteryRealtime);
             if (mTrackingReportedValues) {
                 mUnpluggedReportedTotalTime = mCurrentReportedTotalTime;
                 mUnpluggedReportedCount = mCurrentReportedCount;
@@ -795,8 +791,8 @@
             mInDischarge = true;
         }
 
-        public void plug(long batteryUptime, long batteryRealtime) {
-            super.plug(batteryUptime, batteryRealtime);
+        public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+            super.plug(elapsedRealtime, batteryUptime, batteryRealtime);
             mInDischarge = false;
         }
 
@@ -849,6 +845,141 @@
     }
 
     /**
+     * A timer that increments in batches.  It does not run for durations, but just jumps
+     * for a pre-determined amount.
+     */
+    public static final class BatchTimer extends Timer {
+        final Uid mUid;
+
+        /**
+         * The last time at which we updated the timer.  This is in elapsed realtime microseconds.
+         */
+        long mLastAddedTime;
+
+        /**
+         * The last duration that we added to the timer.  This is in microseconds.
+         */
+        long mLastAddedDuration;
+
+        /**
+         * Whether we are currently in a discharge cycle.
+         */
+        boolean mInDischarge;
+
+        BatchTimer(Uid uid, int type, ArrayList<Unpluggable> unpluggables,
+                boolean inDischarge, Parcel in) {
+            super(type, unpluggables, in);
+            mUid = uid;
+            mLastAddedTime = in.readLong();
+            mLastAddedDuration = in.readLong();
+            mInDischarge = inDischarge;
+        }
+
+        BatchTimer(Uid uid, int type, ArrayList<Unpluggable> unpluggables,
+                boolean inDischarge) {
+            super(type, unpluggables);
+            mUid = uid;
+            mInDischarge = inDischarge;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, long batteryRealtime) {
+            super.writeToParcel(out, batteryRealtime);
+            out.writeLong(mLastAddedTime);
+            out.writeLong(mLastAddedDuration);
+        }
+
+        @Override
+        public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+            recomputeLastDuration(SystemClock.elapsedRealtime() * 1000, false);
+            mInDischarge = false;
+            super.plug(elapsedRealtime, batteryUptime, batteryRealtime);
+        }
+
+        @Override
+        public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
+            recomputeLastDuration(elapsedRealtime, false);
+            mInDischarge = true;
+            // If we are still within the last added duration, then re-added whatever remains.
+            if (mLastAddedTime == elapsedRealtime) {
+                mTotalTime += mLastAddedDuration;
+            }
+            super.unplug(elapsedRealtime, batteryUptime, batteryRealtime);
+        }
+
+        @Override
+        public void logState(Printer pw, String prefix) {
+            super.logState(pw, prefix);
+            pw.println(prefix + "mLastAddedTime=" + mLastAddedTime
+                    + " mLastAddedDuration=" + mLastAddedDuration);
+        }
+
+        private long computeOverage(long curTime) {
+            if (mLastAddedTime > 0) {
+                return mLastTime + mLastAddedDuration - curTime;
+            }
+            return 0;
+        }
+
+        private void recomputeLastDuration(long curTime, boolean abort) {
+            final long overage = computeOverage(curTime);
+            if (overage > 0) {
+                // Aborting before the duration ran out -- roll back the remaining
+                // duration.  Only do this if currently discharging; otherwise we didn't
+                // actually add the time.
+                if (mInDischarge) {
+                    mTotalTime -= overage;
+                }
+                if (abort) {
+                    mLastAddedTime = 0;
+                } else {
+                    mLastAddedTime = curTime;
+                    mLastAddedDuration -= overage;
+                }
+            }
+        }
+
+        public void addDuration(BatteryStatsImpl stats, long durationMillis) {
+            final long now = SystemClock.elapsedRealtime() * 1000;
+            recomputeLastDuration(now, true);
+            mLastAddedTime = now;
+            mLastAddedDuration = durationMillis * 1000;
+            if (mInDischarge) {
+                mTotalTime += mLastAddedDuration;
+                mCount++;
+            }
+        }
+
+        public void abortLastDuration(BatteryStatsImpl stats) {
+            final long now = SystemClock.elapsedRealtime() * 1000;
+            recomputeLastDuration(now, true);
+        }
+
+        @Override
+        protected int computeCurrentCountLocked() {
+            return mCount;
+        }
+
+        @Override
+        protected long computeRunTimeLocked(long curBatteryRealtime) {
+            final long overage = computeOverage(SystemClock.elapsedRealtime() * 1000);
+            if (overage > 0) {
+                return mTotalTime = overage;
+            }
+            return mTotalTime;
+        }
+
+        @Override
+        boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
+            final long now = SystemClock.elapsedRealtime() * 1000;
+            recomputeLastDuration(now, true);
+            boolean stillActive = mLastAddedTime == now;
+            super.reset(stats, !stillActive && detachIfReset);
+            return !stillActive;
+        }
+    }
+
+    /**
      * State for keeping track of timing information.
      */
     public static final class StopwatchTimer extends Timer {
@@ -902,12 +1033,12 @@
             out.writeLong(mUpdateTime);
         }
 
-        public void plug(long batteryUptime, long batteryRealtime) {
+        public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
             if (mNesting > 0) {
                 if (DEBUG && mType < 0) {
                     Log.v(TAG, "old mUpdateTime=" + mUpdateTime);
                 }
-                super.plug(batteryUptime, batteryRealtime);
+                super.plug(elapsedRealtime, batteryUptime, batteryRealtime);
                 mUpdateTime = batteryRealtime;
                 if (DEBUG && mType < 0) {
                     Log.v(TAG, "new mUpdateTime=" + mUpdateTime);
@@ -1443,7 +1574,7 @@
         mHistoryOverflow = false;
     }
 
-    public void doUnplugLocked(long batteryUptime, long batteryRealtime) {
+    public void doUnplugLocked(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
         NetworkStats.Entry entry = null;
 
         // Track UID data usage
@@ -1462,7 +1593,7 @@
         }
 
         for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
-            mUnpluggables.get(i).unplug(batteryUptime, batteryRealtime);
+            mUnpluggables.get(i).unplug(elapsedRealtime, batteryUptime, batteryRealtime);
         }
 
         // Track both mobile and total overall data
@@ -1483,7 +1614,7 @@
         mBluetoothPingCount = 0;
     }
 
-    public void doPlugLocked(long batteryUptime, long batteryRealtime) {
+    public void doPlugLocked(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
         NetworkStats.Entry entry = null;
 
         for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
@@ -1498,7 +1629,7 @@
             }
         }
         for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
-            mUnpluggables.get(i).plug(batteryUptime, batteryRealtime);
+            mUnpluggables.get(i).plug(elapsedRealtime, batteryUptime, batteryRealtime);
         }
 
         // Track both mobile and total overall data
@@ -2109,6 +2240,14 @@
         getUidStatsLocked(uid).noteVideoTurnedOffLocked();
     }
 
+    public void noteVibratorOnLocked(int uid, long durationMillis) {
+        getUidStatsLocked(uid).noteVibratorOnLocked(durationMillis);
+    }
+
+    public void noteVibratorOffLocked(int uid) {
+        getUidStatsLocked(uid).noteVibratorOffLocked();
+    }
+
     public void noteWifiRunningLocked(WorkSource ws) {
         if (!mGlobalWifiRunning) {
             mHistoryCur.states |= HistoryItem.STATE_WIFI_RUNNING_FLAG;
@@ -2402,6 +2541,8 @@
         boolean mVideoTurnedOn;
         StopwatchTimer mVideoTurnedOnTimer;
 
+        BatchTimer mVibratorOnTimer;
+
         Counter[] mUserActivityCounters;
 
         /**
@@ -2439,10 +2580,6 @@
                     mWifiScanTimers, mUnpluggables);
             mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
                     mWifiMulticastTimers, mUnpluggables);
-            mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
-                    null, mUnpluggables);
-            mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
-                    null, mUnpluggables);
         }
 
         @Override
@@ -2587,15 +2724,19 @@
             }
         }
 
+        public StopwatchTimer createAudioTurnedOnTimerLocked() {
+            if (mAudioTurnedOnTimer == null) {
+                mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
+                        null, mUnpluggables);
+            }
+            return mAudioTurnedOnTimer;
+        }
+
         @Override
         public void noteAudioTurnedOnLocked() {
             if (!mAudioTurnedOn) {
                 mAudioTurnedOn = true;
-                if (mAudioTurnedOnTimer == null) {
-                    mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
-                            null, mUnpluggables);
-                }
-                mAudioTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
+                createAudioTurnedOnTimerLocked().startRunningLocked(BatteryStatsImpl.this);
             }
         }
 
@@ -2603,19 +2744,25 @@
         public void noteAudioTurnedOffLocked() {
             if (mAudioTurnedOn) {
                 mAudioTurnedOn = false;
-                mAudioTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
+                if (mAudioTurnedOnTimer != null) {
+                    mAudioTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
+                }
             }
         }
 
+        public StopwatchTimer createVideoTurnedOnTimerLocked() {
+            if (mVideoTurnedOnTimer == null) {
+                mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
+                        null, mUnpluggables);
+            }
+            return mVideoTurnedOnTimer;
+        }
+
         @Override
         public void noteVideoTurnedOnLocked() {
             if (!mVideoTurnedOn) {
                 mVideoTurnedOn = true;
-                if (mVideoTurnedOnTimer == null) {
-                    mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
-                            null, mUnpluggables);
-                }
-                mVideoTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
+                createVideoTurnedOnTimerLocked().startRunningLocked(BatteryStatsImpl.this);
             }
         }
 
@@ -2623,7 +2770,27 @@
         public void noteVideoTurnedOffLocked() {
             if (mVideoTurnedOn) {
                 mVideoTurnedOn = false;
-                mVideoTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
+                if (mVideoTurnedOnTimer != null) {
+                    mVideoTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
+                }
+            }
+        }
+
+        public BatchTimer createVibratorOnTimerLocked() {
+            if (mVibratorOnTimer == null) {
+                mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON,
+                        mUnpluggables, BatteryStatsImpl.this.mOnBatteryInternal);
+            }
+            return mVibratorOnTimer;
+        }
+
+        public void noteVibratorOnLocked(long durationMillis) {
+            createVibratorOnTimerLocked().addDuration(BatteryStatsImpl.this, durationMillis);
+        }
+
+        public void noteVibratorOffLocked() {
+            if (mVibratorOnTimer != null) {
+                mVibratorOnTimer.abortLastDuration(BatteryStatsImpl.this);
             }
         }
 
@@ -2677,6 +2844,11 @@
         }
 
         @Override
+        public Timer getVibratorOnTimer() {
+            return mVibratorOnTimer;
+        }
+
+        @Override
         public void noteUserActivityLocked(int type) {
             if (mUserActivityCounters == null) {
                 initUserActivityLocked();
@@ -2747,6 +2919,14 @@
                 active |= !mVideoTurnedOnTimer.reset(BatteryStatsImpl.this, false);
                 active |= mVideoTurnedOn;
             }
+            if (mVibratorOnTimer != null) {
+                if (mVibratorOnTimer.reset(BatteryStatsImpl.this, false)) {
+                    mVibratorOnTimer.detach();
+                    mVibratorOnTimer = null;
+                } else {
+                    active = true;
+                }
+            }
 
             mLoadedTcpBytesReceived = mLoadedTcpBytesSent = 0;
             mCurrentTcpBytesReceived = mCurrentTcpBytesSent = 0;
@@ -2832,9 +3012,11 @@
                 }
                 if (mAudioTurnedOnTimer != null) {
                     mAudioTurnedOnTimer.detach();
+                    mAudioTurnedOnTimer = null;
                 }
                 if (mVideoTurnedOnTimer != null) {
                     mVideoTurnedOnTimer.detach();
+                    mVideoTurnedOnTimer = null;
                 }
                 if (mUserActivityCounters != null) {
                     for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
@@ -2917,6 +3099,12 @@
             } else {
                 out.writeInt(0);
             }
+            if (mVibratorOnTimer != null) {
+                out.writeInt(1);
+                mVibratorOnTimer.writeToParcel(out, batteryRealtime);
+            } else {
+                out.writeInt(0);
+            }
             if (mUserActivityCounters != null) {
                 out.writeInt(1);
                 for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
@@ -3016,6 +3204,12 @@
                 mVideoTurnedOnTimer = null;
             }
             if (in.readInt() != 0) {
+                mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON,
+                        mUnpluggables, BatteryStatsImpl.this.mOnBatteryInternal, in);
+            } else {
+                mVibratorOnTimer = null;
+            }
+            if (in.readInt() != 0) {
                 mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES];
                 for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
                     mUserActivityCounters[i] = new Counter(mUnpluggables, in);
@@ -3256,14 +3450,14 @@
                 mSpeedBins = new SamplingCounter[getCpuSpeedSteps()];
             }
 
-            public void unplug(long batteryUptime, long batteryRealtime) {
+            public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
                 mUnpluggedUserTime = mUserTime;
                 mUnpluggedSystemTime = mSystemTime;
                 mUnpluggedStarts = mStarts;
                 mUnpluggedForegroundTime = mForegroundTime;
             }
 
-            public void plug(long batteryUptime, long batteryRealtime) {
+            public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
             }
 
             void detach() {
@@ -3550,11 +3744,11 @@
                 mUnpluggables.add(this);
             }
 
-            public void unplug(long batteryUptime, long batteryRealtime) {
+            public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
                 mUnpluggedWakeups = mWakeups;
             }
 
-            public void plug(long batteryUptime, long batteryRealtime) {
+            public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
             }
 
             void detach() {
@@ -3712,13 +3906,13 @@
                     mUnpluggables.add(this);
                 }
 
-                public void unplug(long batteryUptime, long batteryRealtime) {
+                public void unplug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
                     mUnpluggedStartTime = getStartTimeToNowLocked(batteryUptime);
                     mUnpluggedStarts = mStarts;
                     mUnpluggedLaunches = mLaunches;
                 }
 
-                public void plug(long batteryUptime, long batteryRealtime) {
+                public void plug(long elapsedRealtime, long batteryUptime, long batteryRealtime) {
                 }
 
                 void detach() {
@@ -4367,7 +4561,7 @@
             }
             mDischargeAmountScreenOn = 0;
             mDischargeAmountScreenOff = 0;
-            doUnplugLocked(mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime);
+            doUnplugLocked(realtime, mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime);
         } else {
             updateKernelWakelocksLocked();
             mHistoryCur.batteryLevel = (byte)level;
@@ -4383,7 +4577,7 @@
                 mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;
             }
             updateDischargeScreenLevelsLocked(mScreenOn, mScreenOn);
-            doPlugLocked(getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime));
+            doPlugLocked(realtime, getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime));
         }
         if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
             if (mFile != null) {
@@ -5161,11 +5355,14 @@
             }
             u.mAudioTurnedOn = false;
             if (in.readInt() != 0) {
-                u.mAudioTurnedOnTimer.readSummaryFromParcelLocked(in);
+                u.createAudioTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
             }
             u.mVideoTurnedOn = false;
             if (in.readInt() != 0) {
-                u.mVideoTurnedOnTimer.readSummaryFromParcelLocked(in);
+                u.createVideoTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
+            }
+            if (in.readInt() != 0) {
+                u.createVibratorOnTimerLocked().readSummaryFromParcelLocked(in);
             }
 
             if (in.readInt() != 0) {
@@ -5367,6 +5564,12 @@
             } else {
                 out.writeInt(0);
             }
+            if (u.mVibratorOnTimer != null) {
+                out.writeInt(1);
+                u.mVibratorOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            } else {
+                out.writeInt(0);
+            }
 
             if (u.mUserActivityCounters == null) {
                 out.writeInt(0);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9822e63..e357255 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1327,21 +1327,6 @@
         android:label="@string/permlab_writeGservices"
         android:description="@string/permdesc_writeGservices" />
 
-    <!-- @hide Change the screen compatibility mode of applications -->
-    <permission android:name="android.permission.SET_SCREEN_COMPATIBILITY"
-        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="signature"
-        android:label="@string/permlab_setScreenCompatibility"
-        android:description="@string/permdesc_setScreenCompatibility" />
-
-    <!-- Allows an application to modify the current configuration, such
-         as locale. -->
-    <permission android:name="android.permission.CHANGE_CONFIGURATION"
-        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="system|signature"
-        android:label="@string/permlab_changeConfiguration"
-        android:description="@string/permdesc_changeConfiguration" />
-
     <!-- Allows an application to call
         {@link android.app.ActivityManager#forceStopPackage}.
         @hide -->
@@ -1621,6 +1606,13 @@
         android:description="@string/permdesc_updateBatteryStats"
         android:protectionLevel="signature|system" />
 
+    <!-- Allows an application to update application operation statistics. Not for
+         use by third party apps. @hide -->
+    <permission android:name="android.permission.UPDATE_APP_OPS_STATS"
+        android:label="@string/permlab_updateAppOpsStats"
+        android:description="@string/permdesc_updateAppOpsStats"
+        android:protectionLevel="signature|system" />
+
     <!-- Allows an application to open windows that are for use by parts
          of the system user interface.  Not for use by third party apps. -->
     <permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 21c4a64..a574468 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -321,7 +321,7 @@
     <string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"允許應用程式啟動完整備份確認使用者介面 (不建議任何應用程式使用)。"</string>
     <string name="permlab_internalSystemWindow" msgid="2148563628140193231">"顯示未授權視窗"</string>
     <string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"允許應用程式為內部系統使用者介面建立視窗 (不建議一般應用程式使用)。"</string>
-    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"描繪其他應用程式"</string>
+    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"在其他應用程式之上顯示内容"</string>
     <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"允許應用程式在其他應用程式頂層或使用者介面的特定部分繪圖。這可能會干擾您在所有應用程式中的介面使用行為,或是使您在其他應用程式中預期看到的內容發生變化。"</string>
     <string name="permlab_setAnimationScale" msgid="2805103241153907174">"編輯全域動畫速度"</string>
     <string name="permdesc_setAnimationScale" msgid="7690063428924343571">"允許應用程式隨時變更全域的動畫速度 (更快或更慢)。"</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cb8d0e5..6a93860 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -842,6 +842,12 @@
     <string name="permdesc_updateBatteryStats">Allows the app to modify
         collected battery statistics. Not for use by normal apps.</string>
 
+    <!-- [CHAR LIMIT=NONE] Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_updateAppOpsStats">modify app ops statistics</string>
+    <!-- [CHAR LIMIT=NONE] Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_updateAppOpsStats">Allows the app to modify
+        collected application operation statistics. Not for use by normal apps.</string>
+    
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_backup">control system backup and restore</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 900720a..fa115ff 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -240,10 +240,10 @@
         rsnElementGetSubElements(mContext, id, IDs, names, arraySizes);
     }
 
-    native int rsnTypeCreate(int con, int eid, int x, int y, int z, boolean mips, boolean faces);
-    synchronized int nTypeCreate(int eid, int x, int y, int z, boolean mips, boolean faces) {
+    native int rsnTypeCreate(int con, int eid, int x, int y, int z, boolean mips, boolean faces, int yuv);
+    synchronized int nTypeCreate(int eid, int x, int y, int z, boolean mips, boolean faces, int yuv) {
         validate();
-        return rsnTypeCreate(mContext, eid, x, y, z, mips, faces);
+        return rsnTypeCreate(mContext, eid, x, y, z, mips, faces, yuv);
     }
     native void rsnTypeGetNativeData(int con, int id, int[] typeData);
     synchronized void nTypeGetNativeData(int id, int[] typeData) {
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index 93d8b4b..cb12594 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -18,6 +18,8 @@
 
 
 import java.lang.reflect.Field;
+
+import android.graphics.ImageFormat;
 import android.util.Log;
 
 /**
@@ -208,6 +210,7 @@
         int mDimZ;
         boolean mDimMipmaps;
         boolean mDimFaces;
+        int mYuv;
 
         Element mElement;
 
@@ -263,6 +266,25 @@
             return this;
         }
 
+        /**
+         * @hide
+         *
+         * only NV21, YV12.  Enums from ImageFormat
+         */
+        public Builder setYuvFormat(int yuvFormat) {
+            switch (yuvFormat) {
+            case android.graphics.ImageFormat.NV21:
+            case android.graphics.ImageFormat.YV12:
+                break;
+
+            default:
+                throw new RSIllegalArgumentException("Only NV21 and YV12 are supported..");
+            }
+
+            mYuv = yuvFormat;
+            return this;
+        }
+
 
         /**
          * Validate structure and create a new type.
@@ -289,8 +311,14 @@
                 }
             }
 
+            if (mYuv != 0) {
+                if ((mDimZ != 0) || mDimFaces || mDimMipmaps) {
+                    throw new RSInvalidStateException("YUV only supports basic 2D.");
+                }
+            }
+
             int id = mRS.nTypeCreate(mElement.getID(mRS),
-                                     mDimX, mDimY, mDimZ, mDimMipmaps, mDimFaces);
+                                     mDimX, mDimY, mDimZ, mDimMipmaps, mDimFaces, mYuv);
             Type t = new Type(id, mRS);
             t.mElement = mElement;
             t.mDimX = mDimX;
@@ -298,6 +326,7 @@
             t.mDimZ = mDimZ;
             t.mDimMipmaps = mDimMipmaps;
             t.mDimFaces = mDimFaces;
+            t.mDimYuv = mYuv;
 
             t.calcElementCount();
             return t;
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 75c7903..54413b4 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -427,12 +427,12 @@
 
 static int
 nTypeCreate(JNIEnv *_env, jobject _this, RsContext con, RsElement eid,
-            jint dimx, jint dimy, jint dimz, jboolean mips, jboolean faces)
+            jint dimx, jint dimy, jint dimz, jboolean mips, jboolean faces, jint yuv)
 {
-    LOG_API("nTypeCreate, con(%p) eid(%p), x(%i), y(%i), z(%i), mips(%i), faces(%i)",
-            con, eid, dimx, dimy, dimz, mips, faces);
+    LOG_API("nTypeCreate, con(%p) eid(%p), x(%i), y(%i), z(%i), mips(%i), faces(%i), yuv(%i)",
+            con, eid, dimx, dimy, dimz, mips, faces, yuv);
 
-    jint id = (jint)rsTypeCreate(con, (RsElement)eid, dimx, dimy, dimz, mips, faces);
+    jint id = (jint)rsTypeCreate(con, (RsElement)eid, dimx, dimy, dimz, mips, faces, yuv);
     return (jint)id;
 }
 
@@ -1454,7 +1454,7 @@
 {"rsnElementGetNativeData",          "(II[I)V",                               (void*)nElementGetNativeData },
 {"rsnElementGetSubElements",         "(II[I[Ljava/lang/String;[I)V",          (void*)nElementGetSubElements },
 
-{"rsnTypeCreate",                    "(IIIIIZZ)I",                            (void*)nTypeCreate },
+{"rsnTypeCreate",                    "(IIIIIZZI)I",                           (void*)nTypeCreate },
 {"rsnTypeGetNativeData",             "(II[I)V",                               (void*)nTypeGetNativeData },
 
 {"rsnAllocationCreateTyped",         "(IIIII)I",                               (void*)nAllocationCreateTyped },
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 902c82f..e490151 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -37,7 +37,7 @@
     // 2 triangles per patch, 3 vertices per triangle
     uint32_t maxVertices = ((xCount + 1) * (yCount + 1) - emptyQuads) * 2 * 3;
     mVertices = new TextureVertex[maxVertices];
-    mUploaded = false;
+    mAllocatedVerticesCount = 0;
 
     verticesCount = 0;
     hasEmptyQuads = emptyQuads > 0;
@@ -68,38 +68,37 @@
     memcpy(mYDivs, yDivs, mYCount * sizeof(int32_t));
 }
 
-void Patch::copy(const int32_t* yDivs) {
-    memcpy(mYDivs, yDivs, mYCount * sizeof(int32_t));
-}
-
 void Patch::updateColorKey(const uint32_t colorKey) {
     mColorKey = colorKey;
 }
 
-bool Patch::matches(const int32_t* xDivs, const int32_t* yDivs, const uint32_t colorKey) {
+bool Patch::matches(const int32_t* xDivs, const int32_t* yDivs,
+        const uint32_t colorKey, const int8_t emptyQuads) {
+
+    bool matches = true;
+
+    if (mEmptyQuads != emptyQuads) {
+        mEmptyQuads = emptyQuads;
+        hasEmptyQuads = emptyQuads > 0;
+        matches = false;
+    }
+
     if (mColorKey != colorKey) {
         updateColorKey(colorKey);
-        copy(xDivs, yDivs);
-        return false;
+        matches = false;
     }
 
-    for (uint32_t i = 0; i < mXCount; i++) {
-        if (mXDivs[i] != xDivs[i]) {
-            // The Y divs may or may not match, copy everything
-            copy(xDivs, yDivs);
-            return false;
-        }
+    if (memcmp(mXDivs, xDivs, mXCount * sizeof(int32_t))) {
+        memcpy(mXDivs, xDivs, mXCount * sizeof(int32_t));
+        matches = false;
     }
 
-    for (uint32_t i = 0; i < mYCount; i++) {
-        if (mYDivs[i] != yDivs[i]) {
-            // We know all the X divs match, copy only Y divs
-            copy(yDivs);
-            return false;
-        }
+    if (memcmp(mYDivs, yDivs, mYCount * sizeof(int32_t))) {
+        memcpy(mYDivs, yDivs, mYCount * sizeof(int32_t));
+        matches = false;
     }
 
-    return true;
+    return matches;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -203,10 +202,10 @@
     if (verticesCount > 0) {
         Caches& caches = Caches::getInstance();
         caches.bindMeshBuffer(meshBuffer);
-        if (!mUploaded) {
+        if (mAllocatedVerticesCount < verticesCount) {
             glBufferData(GL_ARRAY_BUFFER, sizeof(TextureVertex) * verticesCount,
                     mVertices, GL_DYNAMIC_DRAW);
-            mUploaded = true;
+            mAllocatedVerticesCount = verticesCount;
         } else {
             glBufferSubData(GL_ARRAY_BUFFER, 0,
                     sizeof(TextureVertex) * verticesCount, mVertices);
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index 0518d91..cab0e54 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -45,7 +45,7 @@
  * indices to render the vertices.
  */
 struct Patch {
-    Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads = 0);
+    Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads);
     ~Patch();
 
     void updateVertices(const float bitmapWidth, const float bitmapHeight,
@@ -53,7 +53,8 @@
 
     void updateColorKey(const uint32_t colorKey);
     void copy(const int32_t* xDivs, const int32_t* yDivs);
-    bool matches(const int32_t* xDivs, const int32_t* yDivs, const uint32_t colorKey);
+    bool matches(const int32_t* xDivs, const int32_t* yDivs,
+            const uint32_t colorKey, const int8_t emptyQuads);
 
     GLuint meshBuffer;
     uint32_t verticesCount;
@@ -62,7 +63,7 @@
 
 private:
     TextureVertex* mVertices;
-    bool mUploaded;
+    uint32_t mAllocatedVerticesCount;
 
     int32_t* mXDivs;
     int32_t* mYDivs;
@@ -72,8 +73,6 @@
     uint32_t mYCount;
     int8_t mEmptyQuads;
 
-    void copy(const int32_t* yDivs);
-
     void generateRow(TextureVertex*& vertex, float y1, float y2,
             float v1, float v2, float stretchX, float rescaleX,
             float width, float bitmapWidth, uint32_t& quadCount);
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index 9702c3d..8ee8f5c 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -97,7 +97,7 @@
         }
 
         mCache.add(description, mesh);
-    } else if (!mesh->matches(xDivs, yDivs, colorKey)) {
+    } else if (!mesh->matches(xDivs, yDivs, colorKey, transparentQuads)) {
         PATCH_LOGD("Patch mesh does not match, refreshing vertices");
         mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f, pixelWidth, pixelHeight);
     }
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index f663e0a..c353ec6 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -47,7 +47,7 @@
 
     Location getLastLocation(in LocationRequest request, String packageName);
 
-    boolean addGpsStatusListener(IGpsStatusListener listener);
+    boolean addGpsStatusListener(IGpsStatusListener listener, String packageName);
     void removeGpsStatusListener(IGpsStatusListener listener);
 
     boolean geocoderIsPresent();
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 5a2f71b..0b9286e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1461,7 +1461,7 @@
         }
         try {
             GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
-            result = mService.addGpsStatusListener(transport);
+            result = mService.addGpsStatusListener(transport, mContext.getPackageName());
             if (result) {
                 mGpsStatusListeners.put(listener, transport);
             }
@@ -1507,7 +1507,7 @@
         }
         try {
             GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
-            result = mService.addGpsStatusListener(transport);
+            result = mService.addGpsStatusListener(transport, mContext.getPackageName());
             if (result) {
                 mNmeaListeners.put(listener, transport);
             }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
index 95e7b5e..5c74552 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
@@ -33,18 +33,16 @@
     // the test must be supported by the corresponding camera.
     public static int mCameraId = 0;
     public static int mProfileQuality = CamcorderProfile.QUALITY_HIGH;
-    public static CamcorderProfile profile =
-                        CamcorderProfile.get(mCameraId, mProfileQuality);
-
-    public static int mIterations = 100;
+    public static CamcorderProfile profile = CamcorderProfile.get(mCameraId, mProfileQuality);
+    public static int mIterations = 15;
     public static int mVideoEncoder = profile.videoCodec;
-    public static int mAudioEncdoer = profile.audioCodec;
+    public static int mAudioEncoder = profile.audioCodec;
     public static int mFrameRate = profile.videoFrameRate;
     public static int mVideoWidth = profile.videoFrameWidth;
     public static int mVideoHeight = profile.videoFrameHeight;
     public static int mBitRate = profile.videoBitRate;
     public static boolean mRemoveVideo = true;
-    public static int mDuration = 10 * 1000; // 10 seconds
+    public static int mDuration = 60 * 1000; // 60 seconds
     public static int mTimeLapseDuration = 180 * 1000; // 3 minutes
     public static double mCaptureRate = 0.5; // 2 sec timelapse interval
 
@@ -64,41 +62,41 @@
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
         String iterations = (String) icicle.get("iterations");
-        String video_encoder = (String) icicle.get("video_encoder");
-        String audio_encoder = (String) icicle.get("audio_encoder");
-        String frame_rate = (String) icicle.get("frame_rate");
-        String video_width = (String) icicle.get("video_width");
-        String video_height = (String) icicle.get("video_height");
-        String bit_rate = (String) icicle.get("bit_rate");
-        String record_duration = (String) icicle.get("record_duration");
-        String remove_videos = (String) icicle.get("remove_videos");
+        String videoEncoder = (String) icicle.get("video_encoder");
+        String audioEncoder = (String) icicle.get("audio_encoder");
+        String frameRate = (String) icicle.get("frame_rate");
+        String videoWidth = (String) icicle.get("video_width");
+        String videoHeight = (String) icicle.get("video_height");
+        String bitRate = (String) icicle.get("bit_rate");
+        String recordDuration = (String) icicle.get("record_duration");
+        String removeVideos = (String) icicle.get("remove_videos");
 
         if (iterations != null ) {
             mIterations = Integer.parseInt(iterations);
         }
-        if ( video_encoder != null) {
-            mVideoEncoder = Integer.parseInt(video_encoder);
+        if (videoEncoder != null) {
+            mVideoEncoder = Integer.parseInt(videoEncoder);
         }
-        if ( audio_encoder != null) {
-            mAudioEncdoer = Integer.parseInt(audio_encoder);
+        if (audioEncoder != null) {
+            mAudioEncoder = Integer.parseInt(audioEncoder);
         }
-        if (frame_rate != null) {
-            mFrameRate = Integer.parseInt(frame_rate);
+        if (frameRate != null) {
+            mFrameRate = Integer.parseInt(frameRate);
         }
-        if (video_width != null) {
-            mVideoWidth = Integer.parseInt(video_width);
+        if (videoWidth != null) {
+            mVideoWidth = Integer.parseInt(videoWidth);
         }
-        if (video_height != null) {
-            mVideoHeight = Integer.parseInt(video_height);
+        if (videoHeight != null) {
+            mVideoHeight = Integer.parseInt(videoHeight);
         }
-        if (bit_rate != null) {
-            mBitRate = Integer.parseInt(bit_rate);
+        if (bitRate != null) {
+            mBitRate = Integer.parseInt(bitRate);
         }
-        if (record_duration != null) {
-            mDuration = Integer.parseInt(record_duration);
+        if (recordDuration != null) {
+            mDuration = Integer.parseInt(recordDuration);
         }
-        if (remove_videos != null) {
-            if (remove_videos.compareTo("true") == 0) {
+        if (removeVideos != null) {
+            if (removeVideos.compareTo("true") == 0) {
                 mRemoveVideo = true;
             } else {
                 mRemoveVideo = false;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
index ab9e36c3..ed1d8fc 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
@@ -28,6 +28,7 @@
 import java.io.Writer;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
+import java.util.List;
 
 import android.hardware.Camera;
 import android.hardware.Camera.PictureCallback;
@@ -44,7 +45,7 @@
 import junit.framework.Assert;
 
 /**
- * Junit / Instrumentation test case for the camera zoom api
+ * Junit / Instrumentation test case for the camera zoom and scene mode APIs
  *
  * adb shell am instrument
  *  -e class com.android.mediaframeworktest.stress.CameraStressTest
@@ -54,18 +55,22 @@
     private String TAG = "CameraStressTest";
     private Camera mCamera;
 
+    private static final int CAMERA_ID = 0;
     private static final int NUMBER_OF_ZOOM_LOOPS = 100;
+    private static final int NUMBER_OF_SCENE_MODE_LOOPS = 10;
     private static final long WAIT_GENERIC = 3 * 1000; // 3 seconds
     private static final long WAIT_TIMEOUT = 10 * 1000; // 10 seconds
     private static final long WAIT_ZOOM_ANIMATION = 5 * 1000; // 5 seconds
-    private static final String CAMERA_STRESS_OUTPUT =
-            "/sdcard/cameraStressOutput.txt";
-    private static final int CAMERA_ID = 0;
+    private static final String CAMERA_STRESS_IMAGES_DIRECTORY = "cameraStressImages";
+    private static final String CAMERA_STRESS_IMAGES_PREFIX = "camera-stress-test";
+    private static final String CAMERA_STRESS_OUTPUT = "cameraStressOutput.txt";
     private final CameraErrorCallback mCameraErrorCallback = new CameraErrorCallback();
 
     private Thread mLooperThread;
     private Handler mHandler;
 
+    private Writer mOutput;
+
     public CameraStressTest() {
         super("com.android.mediaframeworktest", MediaFrameworkTest.class);
     }
@@ -89,6 +94,20 @@
         }
         getActivity();
         super.setUp();
+
+        File sdcard = Environment.getExternalStorageDirectory();
+
+        // Create the test images directory if it doesn't exist
+        File stressImagesDirectory = new File(String.format("%s/%s", sdcard,
+                CAMERA_STRESS_IMAGES_DIRECTORY));
+        if (!stressImagesDirectory.exists()) {
+            stressImagesDirectory.mkdir();
+        }
+
+        // Start writing output file
+        File stressOutFile = new File(String.format("%s/%s",sdcard, CAMERA_STRESS_OUTPUT));
+        mOutput = new BufferedWriter(new FileWriter(stressOutFile, true));
+        mOutput.write(this.getName() + ":\n");
     }
 
     @Override
@@ -105,6 +124,9 @@
             mLooperThread = null;
         }
 
+        mOutput.write("\n\n");
+        mOutput.close();
+
         super.tearDown();
     }
 
@@ -127,9 +149,7 @@
 
     private final class CameraErrorCallback implements android.hardware.Camera.ErrorCallback {
         public void onError(int error, android.hardware.Camera camera) {
-            if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
-                assertTrue("Camera test mediaserver died", false);
-            }
+            fail(String.format("Camera error, code: %d", error));
         }
     }
 
@@ -154,49 +174,76 @@
 
             try {
                 Log.v(TAG, "JPEG picture taken");
-                fos = new FileOutputStream(String.format("%s/zoom-test-%d.jpg",
-                        Environment.getExternalStorageDirectory(), System.currentTimeMillis()));
+                fos = new FileOutputStream(String.format("%s/%s/%s-%d.jpg",
+                        Environment.getExternalStorageDirectory(), CAMERA_STRESS_IMAGES_DIRECTORY,
+                        CAMERA_STRESS_IMAGES_PREFIX, System.currentTimeMillis()));
                 fos.write(data);
-            }
-            catch (FileNotFoundException e) {
-                Log.v(TAG, "File not found: " + e.toString());
-            }
-            catch (IOException e) {
-                Log.v(TAG, "Error accessing file: " + e.toString());
-            }
-            finally {
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, "File not found: " + e.toString());
+            } catch (IOException e) {
+                Log.e(TAG, "Error accessing file: " + e.toString());
+            } finally {
                 try {
                     if (fos != null) {
                         fos.close();
                     }
-                }
-                catch (IOException e) {
-                    Log.v(TAG, "Error closing file: " + e.toString());
+                } catch (IOException e) {
+                    Log.e(TAG, "Error closing file: " + e.toString());
                 }
             }
         }
     };
 
     // Helper method for cleaning up pics taken during testStressCameraZoom
-    private void cleanupZoomImages() {
+    private void cleanupStressTestImages() {
         try {
-            File sdcard = Environment.getExternalStorageDirectory();
+            File stressImagesDirectory = new File(String.format("%s/%s",
+                    Environment.getExternalStorageDirectory(), CAMERA_STRESS_IMAGES_DIRECTORY));
             File[] zoomImages = null;
 
             FilenameFilter filter = new FilenameFilter() {
                 public boolean accept(File dir, String name) {
-                    return name.startsWith("zoom-test-");
+                    return name.startsWith(CAMERA_STRESS_IMAGES_PREFIX);
                 }
             };
 
-            zoomImages = sdcard.listFiles(filter);
+            zoomImages = stressImagesDirectory.listFiles(filter);
 
             for (File f : zoomImages) {
                 f.delete();
             }
+        } catch (SecurityException e) {
+            Log.e(TAG, "Security manager access violation: " + e.toString());
         }
-        catch (SecurityException e) {
-            Log.v(TAG, "Security manager access violation: " + e.toString());
+    }
+
+    // Helper method for starting up the camera preview
+    private void startCameraPreview(SurfaceHolder surfaceHolder) {
+        try {
+            mCamera.setErrorCallback(mCameraErrorCallback);
+            mCamera.setPreviewDisplay(surfaceHolder);
+            mCamera.startPreview();
+            Thread.sleep(WAIT_GENERIC);
+        } catch (IOException e) {
+            Log.e(TAG, "Error setting preview display: " + e.toString());
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Error waiting for preview to come up: " + e.toString());
+        } catch (Exception e) {
+            Log.e(TAG, "Error starting up camera preview: " + e.toString());
+        }
+    }
+
+    // Helper method for taking a photo
+    private void capturePhoto() {
+        try {
+            mCamera.takePicture(shutterCallback, rawCallback, jpegCallback);
+            Thread.sleep(WAIT_GENERIC);
+            mCamera.stopPreview();
+            mCamera.release();
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Error waiting for photo to be taken: " + e.toString());
+        } catch (Exception e) {
+            Log.e(TAG, "Error capturing photo: " + e.toString());
         }
     }
 
@@ -205,14 +252,11 @@
     public void testStressCameraZoom() throws Exception {
         SurfaceHolder mSurfaceHolder;
         mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
-        File stressOutFile = new File(CAMERA_STRESS_OUTPUT);
-        Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
-        output.write("Camera zoom stress:\n");
-        output.write("Total number of loops: " +  NUMBER_OF_ZOOM_LOOPS + "\n");
+        mOutput.write("Total number of loops: " + NUMBER_OF_ZOOM_LOOPS + "\n");
 
         try {
             Log.v(TAG, "Start preview");
-            output.write("No of loop: ");
+            mOutput.write("No of loop: ");
 
             mCamera = Camera.open(CAMERA_ID);
             Camera.Parameters params = mCamera.getParameters();
@@ -220,9 +264,8 @@
 
             if (!params.isSmoothZoomSupported() && !params.isZoomSupported()) {
                 Log.v(TAG, "Device camera does not support zoom");
-                assertTrue("Camera zoom stress test", false);
-            }
-            else {
+                fail("Camera zoom stress test failed");
+            } else {
                 Log.v(TAG, "Device camera does support zoom");
 
                 int nextZoomLevel = 0;
@@ -235,11 +278,7 @@
                         }
                     });
 
-                    mCamera.setErrorCallback(mCameraErrorCallback);
-                    mCamera.setPreviewDisplay(mSurfaceHolder);
-                    mCamera.startPreview();
-                    Thread.sleep(WAIT_GENERIC);
-
+                    startCameraPreview(mSurfaceHolder);
                     params = mCamera.getParameters();
                     int currentZoomLevel = params.getZoom();
 
@@ -250,8 +289,7 @@
 
                     if (params.isSmoothZoomSupported()) {
                         mCamera.startSmoothZoom(nextZoomLevel);
-                    }
-                    else {
+                    } else {
                         params.setZoom(nextZoomLevel);
                         mCamera.setParameters(params);
                     }
@@ -259,23 +297,66 @@
 
                     // sleep allows for zoom animation to finish
                     Thread.sleep(WAIT_ZOOM_ANIMATION);
+                    capturePhoto();
 
-                    // take picture
-                    mCamera.takePicture(shutterCallback, rawCallback, jpegCallback);
-                    Thread.sleep(WAIT_GENERIC);
-                    mCamera.stopPreview();
-                    mCamera.release();
-                    output.write(" ," + i);
+                    if (i == 0) {
+                        mOutput.write(Integer.toString(i));
+                    } else {
+                        mOutput.write(", " + i);
+                    }
                 }
             }
+            cleanupStressTestImages();
+        } catch (Exception e) {
+            Log.e(TAG, e.toString());
+            fail("Camera zoom stress test Exception");
+        }
+    }
 
-            cleanupZoomImages();
+    // Test case for stressing the camera scene mode feature
+    @LargeTest
+    public void testStressCameraSceneModes() throws Exception {
+        SurfaceHolder mSurfaceHolder;
+        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
+
+        try {
+            mCamera = Camera.open(CAMERA_ID);
+            Camera.Parameters params = mCamera.getParameters();
+            mCamera.release();
+            List<String> supportedSceneModes = params.getSupportedSceneModes();
+            assertNotNull("No scene modes supported", supportedSceneModes);
+
+            mOutput.write("Total number of loops: " +
+                    (NUMBER_OF_SCENE_MODE_LOOPS * supportedSceneModes.size()) + "\n");
+            Log.v(TAG, "Start preview");
+            mOutput.write("No of loop: ");
+
+            for (int i = 0; i < supportedSceneModes.size(); i++) {
+                for (int j = 0; j < NUMBER_OF_SCENE_MODE_LOOPS; j++) {
+                    runOnLooper(new Runnable() {
+                        @Override
+                        public void run() {
+                            mCamera = Camera.open(CAMERA_ID);
+                        }
+                    });
+
+                    startCameraPreview(mSurfaceHolder);
+                    Log.v(TAG, "Setting mode to " + supportedSceneModes.get(i));
+                    params.setSceneMode(supportedSceneModes.get(i));
+                    mCamera.setParameters(params);
+                    capturePhoto();
+
+                    if ((i == 0) && (j == 0)) {
+                        mOutput.write(Integer.toString(j + i * NUMBER_OF_SCENE_MODE_LOOPS));
+                    } else {
+                        mOutput.write(", " + (j + i * NUMBER_OF_SCENE_MODE_LOOPS));
+                    }
+                }
+            }
+            cleanupStressTestImages();
+        } catch (Exception e) {
+            Log.e(TAG, e.toString());
+            fail("Camera scene mode test Exception");
         }
-        catch (Exception e) {
-            assertTrue("Camera zoom stress test Exception", false);
-            Log.v(TAG, e.toString());
-        }
-        output.write("\n\n");
-        output.close();
     }
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
index 6995c60..6eb9891 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
@@ -31,6 +31,7 @@
 import android.media.CamcorderProfile;
 import android.media.MediaPlayer;
 import android.media.MediaRecorder;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
 import android.test.ActivityInstrumentationTestCase2;
@@ -48,26 +49,26 @@
     private MediaRecorder mRecorder;
     private Camera mCamera;
 
+    private static final int CAMERA_ID = 0;
     private static final int NUMBER_OF_CAMERA_STRESS_LOOPS = 100;
     private static final int NUMBER_OF_RECORDER_STRESS_LOOPS = 100;
     private static final int NUMBER_OF_RECORDERANDPLAY_STRESS_LOOPS = 50;
     private static final int NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER = 200;
     private static final int NUMBER_OF_TIME_LAPSE_LOOPS = 25;
     private static final int TIME_LAPSE_PLAYBACK_WAIT_TIME = 5* 1000; // 5 seconds
+    private static final int USE_TEST_RUNNER_PROFILE = -1;
+    private static final long WAIT_TIMEOUT = 10 * 1000; // 10 seconds
     private static final long WAIT_TIME_CAMERA_TEST = 3 * 1000; // 3 seconds
     private static final long WAIT_TIME_RECORDER_TEST = 6 * 1000; // 6 seconds
-    private static final String OUTPUT_FILE = "/sdcard/temp";
     private static final String OUTPUT_FILE_EXT = ".3gp";
-    private static final String MEDIA_STRESS_OUTPUT =
-        "/sdcard/mediaStressOutput.txt";
-    private static final int CAMERA_ID = 0;
+    private static final String MEDIA_STRESS_OUTPUT = "mediaStressOutput.txt";
 
     private final CameraErrorCallback mCameraErrorCallback = new CameraErrorCallback();
     private final RecorderErrorCallback mRecorderErrorCallback = new RecorderErrorCallback();
 
-    private final static int WAIT_TIMEOUT = 10 * 1000; // 10 seconds
-    private Thread mLooperThread;
     private Handler mHandler;
+    private Thread mLooperThread;
+    private Writer mOutput;
 
     public MediaRecorderStressTest() {
         super("com.android.mediaframeworktest", MediaFrameworkTest.class);
@@ -95,6 +96,11 @@
         Thread.sleep(2000);
         getActivity();
         super.setUp();
+
+        File stressOutFile = new File(String.format("%s/%s",
+                Environment.getExternalStorageDirectory(), MEDIA_STRESS_OUTPUT));
+        mOutput = new BufferedWriter(new FileWriter(stressOutFile, true));
+        mOutput.write(this.getName() + "\n");
     }
 
     @Override
@@ -110,7 +116,8 @@
             }
             mLooperThread = null;
         }
-
+        mOutput.write("\n\n");
+        mOutput.close();
         super.tearDown();
     }
 
@@ -133,16 +140,13 @@
 
     private final class CameraErrorCallback implements android.hardware.Camera.ErrorCallback {
         public void onError(int error, android.hardware.Camera camera) {
-            if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
-                assertTrue("Camera test mediaserver died", false);
-            }
+            fail(String.format("Camera error, code: %d", error));
         }
     }
 
     private final class RecorderErrorCallback implements MediaRecorder.OnErrorListener {
         public void onError(MediaRecorder mr, int what, int extra) {
-            // fail the test case no matter what error come up
-            assertTrue("mediaRecorder error", false);
+            fail(String.format("Media recorder error, code: %d\textra: %d", what, extra));
         }
     }
 
@@ -151,14 +155,11 @@
     public void testStressCamera() throws Exception {
         SurfaceHolder mSurfaceHolder;
         mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
-        File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
-        Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
-        output.write("Camera start preview stress:\n");
-        output.write("Total number of loops:" +
-                NUMBER_OF_CAMERA_STRESS_LOOPS + "\n");
+        Log.v(TAG, "Camera start preview stress test");
+        mOutput.write("Total number of loops:" + NUMBER_OF_CAMERA_STRESS_LOOPS + "\n");
         try {
             Log.v(TAG, "Start preview");
-            output.write("No of loop: ");
+            mOutput.write("No of loop: ");
 
             for (int i = 0; i< NUMBER_OF_CAMERA_STRESS_LOOPS; i++) {
                 runOnLooper(new Runnable() {
@@ -173,29 +174,27 @@
                 Thread.sleep(WAIT_TIME_CAMERA_TEST);
                 mCamera.stopPreview();
                 mCamera.release();
-                output.write(" ," + i);
+                if (i == 0) {
+                    mOutput.write(i + 1);
+                } else {
+                    mOutput.write(String.format(", %d", (i + 1)));
+                }
             }
         } catch (Exception e) {
-            assertTrue("CameraStressTest", false);
-            Log.v(TAG, e.toString());
+            Log.e(TAG, e.toString());
+            fail("Camera startup preview stress test");
         }
-        output.write("\n\n");
-        output.close();
     }
 
     //Test case for stressing the camera preview.
     @LargeTest
     public void testStressRecorder() throws Exception {
-        String filename;
         SurfaceHolder mSurfaceHolder;
         mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
-        File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
-        Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
-        output.write("H263 video record- reset after prepare Stress test\n");
-        output.write("Total number of loops:" +
-                NUMBER_OF_RECORDER_STRESS_LOOPS + "\n");
+        Log.v(TAG, "H263 video record: reset after prepare Stress test");
+        mOutput.write("Total number of loops:" + NUMBER_OF_RECORDER_STRESS_LOOPS + "\n");
         try {
-            output.write("No of loop: ");
+            mOutput.write("No of loop: ");
             Log.v(TAG, "Start preview");
             for (int i = 0; i < NUMBER_OF_RECORDER_STRESS_LOOPS; i++) {
                 runOnLooper(new Runnable() {
@@ -205,12 +204,15 @@
                     }
                 });
                 Log.v(TAG, "counter = " + i);
-                filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT;
-                Log.v(TAG, filename);
+                String fileName = String.format("%s/temp%d%s",
+                        Environment.getExternalStorageDirectory(),
+                        i, OUTPUT_FILE_EXT);
+
+                Log.v(TAG, fileName);
                 mRecorder.setOnErrorListener(mRecorderErrorCallback);
                 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
                 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
-                mRecorder.setOutputFile(filename);
+                mRecorder.setOutputFile(fileName);
                 mRecorder.setVideoFrameRate(MediaRecorderStressTestRunner.mFrameRate);
                 mRecorder.setVideoSize(176,144);
                 Log.v(TAG, "setEncoder");
@@ -224,30 +226,29 @@
                 Thread.sleep(WAIT_TIME_RECORDER_TEST);
                 mRecorder.reset();
                 mRecorder.release();
-                output.write(", " + i);
+                if (i == 0) {
+                    mOutput.write(i + 1);
+                } else {
+                    mOutput.write(String.format(", %d", (i + 1)));
+                }
             }
         } catch (Exception e) {
-            assertTrue("Recorder Stress test", false);
-            Log.v(TAG, e.toString());
+            Log.e(TAG, e.toString());
+            fail("H263 video recording stress test");
         }
-        output.write("\n\n");
-        output.close();
     }
 
     //Stress test case for switching camera and video recorder preview.
     @LargeTest
     public void testStressCameraSwitchRecorder() throws Exception {
-        String filename;
         SurfaceHolder mSurfaceHolder;
         mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
-        File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
-        Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
-        output.write("Camera and video recorder preview switching\n");
-        output.write("Total number of loops:"
-                + NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER + "\n");
+        Log.v(TAG, "Camera and video recorder preview switching");
+        mOutput.write("Total number of loops: " +
+                NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER + "\n");
         try {
             Log.v(TAG, "Start preview");
-            output.write("No of loop: ");
+            mOutput.write("No of loop: ");
             for (int i = 0; i < NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER; i++) {
                 runOnLooper(new Runnable() {
                     @Override
@@ -263,8 +264,10 @@
                 mCamera.release();
                 mCamera = null;
                 Log.v(TAG, "release camera");
-                filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT;
-                Log.v(TAG, filename);
+                String fileName = String.format("%s/temp%d%s",
+                        Environment.getExternalStorageDirectory(),
+                        i, OUTPUT_FILE_EXT);
+                Log.v(TAG, fileName);
                 runOnLooper(new Runnable() {
                     @Override
                     public void run() {
@@ -274,7 +277,7 @@
                 mRecorder.setOnErrorListener(mRecorderErrorCallback);
                 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
                 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
-                mRecorder.setOutputFile(filename);
+                mRecorder.setOutputFile(fileName);
                 mRecorder.setVideoFrameRate(MediaRecorderStressTestRunner.mFrameRate);
                 mRecorder.setVideoSize(176,144);
                 Log.v(TAG, "Media recorder setEncoder");
@@ -287,117 +290,167 @@
                 Thread.sleep(WAIT_TIME_CAMERA_TEST);
                 mRecorder.release();
                 Log.v(TAG, "release video recorder");
-                output.write(", " + i);
+                if (i == 0) {
+                    mOutput.write(i + 1);
+                } else {
+                    mOutput.write(String.format(", %d", (i + 1)));
+                }
             }
         } catch (Exception e) {
-            assertTrue("Camer and recorder switch mode", false);
-                Log.v(TAG, e.toString());
+            Log.e(TAG, e.toString());
+            fail("Camera and recorder switch mode");
         }
-        output.write("\n\n");
-        output.close();
     }
 
-    public void validateRecordedVideo(String recorded_file) {
+    public void validateRecordedVideo(String recordedFile) {
         try {
             MediaPlayer mp = new MediaPlayer();
-            mp.setDataSource(recorded_file);
+            mp.setDataSource(recordedFile);
             mp.prepare();
             int duration = mp.getDuration();
             if (duration <= 0){
-                assertTrue("stressRecordAndPlayback", false);
+                fail("stressRecordAndPlayback");
             }
             mp.release();
         } catch (Exception e) {
-            assertTrue("stressRecordAndPlayback", false);
+            fail("stressRecordAndPlayback");
         }
     }
 
-    public void removeRecordedVideo(String filename){
-        File video = new File(filename);
-        Log.v(TAG, "remove recorded video " + filename);
+    public void removeRecordedVideo(String fileName){
+        File video = new File(fileName);
+        Log.v(TAG, "remove recorded video " + fileName);
         video.delete();
     }
 
-    //Stress test case for record a video and play right away.
-    @LargeTest
-    public void testStressRecordVideoAndPlayback() throws Exception {
-        int iterations = MediaRecorderStressTestRunner.mIterations;
-        int video_encoder = MediaRecorderStressTestRunner.mVideoEncoder;
-        int audio_encoder = MediaRecorderStressTestRunner.mAudioEncdoer;
-        int frame_rate = MediaRecorderStressTestRunner.mFrameRate;
-        int video_width = MediaRecorderStressTestRunner.mVideoWidth;
-        int video_height = MediaRecorderStressTestRunner.mVideoHeight;
-        int bit_rate = MediaRecorderStressTestRunner.mBitRate;
-        boolean remove_video = MediaRecorderStressTestRunner.mRemoveVideo;
-        int record_duration = MediaRecorderStressTestRunner.mDuration;
+    // Helper method for record & playback testing with different camcorder profiles
+    private void recordVideoAndPlayback(int profile) throws Exception {
+        int iterations;
+        int recordDuration;
+        boolean removeVideo;
 
-        String filename;
-        SurfaceHolder mSurfaceHolder;
-        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
-        File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
-        Writer output = new BufferedWriter(
-                new FileWriter(stressOutFile, true));
-        output.write("Video record and play back stress test:\n");
-        output.write("Total number of loops:"
-                + NUMBER_OF_RECORDERANDPLAY_STRESS_LOOPS + "\n");
+        int videoEncoder;
+        int audioEncoder;
+        int frameRate;
+        int videoWidth;
+        int videoHeight;
+        int bitRate;
+
+        if (profile != USE_TEST_RUNNER_PROFILE) {
+            assertTrue(String.format("Camera doesn't support profile %d", profile),
+                    CamcorderProfile.hasProfile(CAMERA_ID, profile));
+            CamcorderProfile camcorderProfile = CamcorderProfile.get(CAMERA_ID, profile);
+            videoEncoder = camcorderProfile.videoCodec;
+            audioEncoder = camcorderProfile.audioCodec;
+            frameRate = camcorderProfile.videoFrameRate;
+            videoWidth = camcorderProfile.videoFrameWidth;
+            videoHeight = camcorderProfile.videoFrameHeight;
+            bitRate = camcorderProfile.videoBitRate;
+        } else {
+            videoEncoder = MediaRecorderStressTestRunner.mVideoEncoder;
+            audioEncoder = MediaRecorderStressTestRunner.mAudioEncoder;
+            frameRate = MediaRecorderStressTestRunner.mFrameRate;
+            videoWidth = MediaRecorderStressTestRunner.mVideoWidth;
+            videoHeight = MediaRecorderStressTestRunner.mVideoHeight;
+            bitRate = MediaRecorderStressTestRunner.mBitRate;
+        }
+        iterations = MediaRecorderStressTestRunner.mIterations;
+        recordDuration = MediaRecorderStressTestRunner.mDuration;
+        removeVideo = MediaRecorderStressTestRunner.mRemoveVideo;
+
+        SurfaceHolder surfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
+        mOutput.write("Total number of loops: " + iterations + "\n");
+
         try {
-            output.write("No of loop: ");
-            for (int i = 0; i < iterations; i++){
-                filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT;
-                Log.v(TAG, filename);
+            mOutput.write("No of loop: ");
+            for (int i = 0; i < iterations; i++) {
+                String fileName = String.format("%s/temp%d%s",
+                        Environment.getExternalStorageDirectory(), i, OUTPUT_FILE_EXT);
+                Log.v(TAG, fileName);
+
                 runOnLooper(new Runnable() {
                     @Override
                     public void run() {
                         mRecorder = new MediaRecorder();
                     }
                 });
+
                 Log.v(TAG, "iterations : " + iterations);
-                Log.v(TAG, "video_encoder : " + video_encoder);
-                Log.v(TAG, "audio_encoder : " + audio_encoder);
-                Log.v(TAG, "frame_rate : " + frame_rate);
-                Log.v(TAG, "video_width : " + video_width);
-                Log.v(TAG, "video_height : " + video_height);
-                Log.v(TAG, "bit rate : " + bit_rate);
-                Log.v(TAG, "record_duration : " + record_duration);
+                Log.v(TAG, "video encoder : " + videoEncoder);
+                Log.v(TAG, "audio encoder : " + audioEncoder);
+                Log.v(TAG, "frame rate : " + frameRate);
+                Log.v(TAG, "video width : " + videoWidth);
+                Log.v(TAG, "video height : " + videoHeight);
+                Log.v(TAG, "bit rate : " + bitRate);
+                Log.v(TAG, "record duration : " + recordDuration);
 
                 mRecorder.setOnErrorListener(mRecorderErrorCallback);
                 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
                 mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
                 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
-                mRecorder.setOutputFile(filename);
-                mRecorder.setVideoFrameRate(frame_rate);
-                mRecorder.setVideoSize(video_width, video_height);
-                mRecorder.setVideoEncoder(video_encoder);
-                mRecorder.setAudioEncoder(audio_encoder);
-                mRecorder.setVideoEncodingBitRate(bit_rate);
+                mRecorder.setOutputFile(fileName);
+                mRecorder.setVideoFrameRate(frameRate);
+                mRecorder.setVideoSize(videoWidth, videoHeight);
+                mRecorder.setVideoEncoder(videoEncoder);
+                mRecorder.setAudioEncoder(audioEncoder);
+                mRecorder.setVideoEncodingBitRate(bitRate);
+
                 Log.v(TAG, "mediaRecorder setPreview");
-                mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
+                mRecorder.setPreviewDisplay(surfaceHolder.getSurface());
                 mRecorder.prepare();
                 mRecorder.start();
-                Thread.sleep(record_duration);
+                Thread.sleep(recordDuration);
                 Log.v(TAG, "Before stop");
                 mRecorder.stop();
                 mRecorder.release();
+
                 //start the playback
                 MediaPlayer mp = new MediaPlayer();
-                mp.setDataSource(filename);
+                mp.setDataSource(fileName);
                 mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
                 mp.prepare();
                 mp.start();
-                Thread.sleep(record_duration);
+                Thread.sleep(recordDuration);
                 mp.release();
-                validateRecordedVideo(filename);
-                if (remove_video) {
-                    removeRecordedVideo(filename);
+                validateRecordedVideo(fileName);
+                if (removeVideo) {
+                    removeRecordedVideo(fileName);
                 }
-                output.write(", " + i);
+                if (i == 0) {
+                    mOutput.write(i + 1);
+                } else {
+                    mOutput.write(String.format(", %d", (i + 1)));
+                }
             }
         } catch (Exception e) {
-            Log.v(TAG, e.toString());
-            assertTrue("record and playback", false);
+            Log.e(TAG, e.toString());
+            fail("Record and playback");
         }
-        output.write("\n\n");
-        output.close();
+    }
+
+    // Record and playback stress test @ 1080P quality
+    @LargeTest
+    public void testStressRecordVideoAndPlayback1080P() throws Exception {
+        recordVideoAndPlayback(CamcorderProfile.QUALITY_1080P);
+    }
+
+    // Record and playback stress test @ 720P quality
+    @LargeTest
+    public void testStressRecordVideoAndPlayback720P() throws Exception {
+        recordVideoAndPlayback(CamcorderProfile.QUALITY_720P);
+    }
+
+    // Record and playback stress test @ 480P quality
+    @LargeTest
+    public void testStressRecordVideoAndPlayback480P() throws Exception {
+        recordVideoAndPlayback(CamcorderProfile.QUALITY_480P);
+    }
+
+    // This test method uses the codec info from the test runner. Use this
+    // for more granular control of video encoding.
+    @LargeTest
+    public void defaultStressRecordVideoAndPlayback() throws Exception {
+        recordVideoAndPlayback(USE_TEST_RUNNER_PROFILE);
     }
 
     // Test case for stressing time lapse
@@ -405,21 +458,19 @@
     public void testStressTimeLapse() throws Exception {
         SurfaceHolder mSurfaceHolder;
         mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
-        int record_duration = MediaRecorderStressTestRunner.mTimeLapseDuration;
-        boolean remove_video = MediaRecorderStressTestRunner.mRemoveVideo;
+        int recordDuration = MediaRecorderStressTestRunner.mTimeLapseDuration;
+        boolean removeVideo = MediaRecorderStressTestRunner.mRemoveVideo;
         double captureRate = MediaRecorderStressTestRunner.mCaptureRate;
-        String filename;
-        File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
-        Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
-        output.write("Start camera time lapse stress:\n");
-        output.write("Total number of loops: " + NUMBER_OF_TIME_LAPSE_LOOPS + "\n");
+        Log.v(TAG, "Start camera time lapse stress:");
+        mOutput.write("Total number of loops: " + NUMBER_OF_TIME_LAPSE_LOOPS + "\n");
 
         try {
-            for (int j = 0, n = Camera.getNumberOfCameras(); j < n; j++) {
-                output.write("No of loop: camera " + j);
-                for (int i = 0; i < NUMBER_OF_TIME_LAPSE_LOOPS; i++) {
-                    filename = OUTPUT_FILE + j + "_" + i + OUTPUT_FILE_EXT;
-                    Log.v(TAG, filename);
+            for (int i = 0, n = Camera.getNumberOfCameras(); i < n; i++) {
+                mOutput.write("No of loop: camera " + i);
+                for (int j = 0; j < NUMBER_OF_TIME_LAPSE_LOOPS; j++) {
+                    String fileName = String.format("%s/temp%d_%d%s",
+                            Environment.getExternalStorageDirectory(), i, j, OUTPUT_FILE_EXT);
+                    Log.v(TAG, fileName);
                     runOnLooper(new Runnable() {
                         @Override
                         public void run() {
@@ -438,12 +489,12 @@
                         CamcorderProfile.get(j, CamcorderProfile.QUALITY_TIME_LAPSE_HIGH);
                     mRecorder.setProfile(profile);
 
-                    // Set the timelapse setting; 0.1 = 10 sec timelapse, 0.5 = 2 sec timelapse, etc.
+                    // Set the timelapse setting; 0.1 = 10 sec timelapse, 0.5 = 2 sec timelapse, etc
                     // http://developer.android.com/guide/topics/media/camera.html#time-lapse-video
                     mRecorder.setCaptureRate(captureRate);
 
                     // Set output file
-                    mRecorder.setOutputFile(filename);
+                    mRecorder.setOutputFile(fileName);
 
                     // Set the preview display
                     Log.v(TAG, "mediaRecorder setPreviewDisplay");
@@ -451,40 +502,40 @@
 
                     mRecorder.prepare();
                     mRecorder.start();
-                    Thread.sleep(record_duration);
+                    Thread.sleep(recordDuration);
                     Log.v(TAG, "Before stop");
                     mRecorder.stop();
                     mRecorder.release();
 
                     // Start the playback
                     MediaPlayer mp = new MediaPlayer();
-                    mp.setDataSource(filename);
+                    mp.setDataSource(fileName);
                     mp.setDisplay(mSurfaceHolder);
                     mp.prepare();
                     mp.start();
                     Thread.sleep(TIME_LAPSE_PLAYBACK_WAIT_TIME);
                     mp.release();
-                    validateRecordedVideo(filename);
-                    if (remove_video) {
-                        removeRecordedVideo(filename);
+                    validateRecordedVideo(fileName);
+                    if (removeVideo) {
+                        removeRecordedVideo(fileName);
                     }
-                    output.write(", " + i);
+
+                    if (j == 0) {
+                        mOutput.write(j + 1);
+                    } else {
+                        mOutput.write(String.format(", %d", (j + 1)));
+                    }
                 }
             }
+        } catch (IllegalStateException e) {
+            Log.e(TAG, e.toString());
+            fail("Camera time lapse stress test IllegalStateException");
+        } catch (IOException e) {
+            Log.e(TAG, e.toString());
+            fail("Camera time lapse stress test IOException");
+        } catch (Exception e) {
+            Log.e(TAG, e.toString());
+            fail("Camera time lapse stress test Exception");
         }
-        catch (IllegalStateException e) {
-            assertTrue("Camera time lapse stress test IllegalStateException", false);
-            Log.v(TAG, e.toString());
-        }
-        catch (IOException e) {
-            assertTrue("Camera time lapse stress test IOException", false);
-            Log.v(TAG, e.toString());
-        }
-        catch (Exception e) {
-            assertTrue("Camera time lapse stress test Exception", false);
-            Log.v(TAG, e.toString());
-        }
-        output.write("\n\n");
-        output.close();
     }
 }
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
new file mode 100644
index 0000000..5ad4be8
--- /dev/null
+++ b/services/java/com/android/server/AppOpsService.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import com.android.internal.app.IAppOpsService;
+
+public class AppOpsService extends IAppOpsService.Stub {
+    static final String TAG = "AppOps";
+
+    Context mContext;
+    final AtomicFile mFile;
+
+    final SparseArray<HashMap<String, Ops>> mUidOps
+            = new SparseArray<HashMap<String, Ops>>();
+
+    final static class Ops extends SparseArray<Op> {
+        public final String packageName;
+
+        public Ops(String _packageName) {
+            packageName = _packageName;
+        }
+    }
+
+    final static class Op {
+        public final int op;
+        public int duration;
+        public long time;
+
+        public Op(int _op) {
+            op = _op;
+        }
+    }
+
+    public AppOpsService() {
+        mFile = new AtomicFile(new File(Environment.getSecureDataDirectory(), "appops.xml"));
+    }
+    
+    public void publish(Context context) {
+        mContext = context;
+        ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
+    }
+
+    public void shutdown() {
+        Slog.w(TAG, "Writing app ops before shutdown...");
+    }
+
+    @Override
+    public int noteOperation(int code, int uid, String packageName) {
+        uid = handleIncomingUid(uid);
+        synchronized (this) {
+            Op op = getOpLocked(code, uid, packageName);
+            if (op == null) {
+                return AppOpsManager.MODE_IGNORED;
+            }
+            if (op.duration == -1) {
+                Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
+                        + " code " + code + " time=" + op.time + " duration=" + op.duration);
+            }
+            op.time = System.currentTimeMillis();
+            op.duration = 0;
+        }
+        return AppOpsManager.MODE_ALLOWED;
+    }
+
+    @Override
+    public int startOperation(int code, int uid, String packageName) {
+        uid = handleIncomingUid(uid);
+        synchronized (this) {
+            Op op = getOpLocked(code, uid, packageName);
+            if (op == null) {
+                return AppOpsManager.MODE_IGNORED;
+            }
+            if (op.duration == -1) {
+                Slog.w(TAG, "Starting op not finished: uid " + uid + " pkg " + packageName
+                        + " code " + code + " time=" + op.time + " duration=" + op.duration);
+            }
+            op.time = System.currentTimeMillis();
+            op.duration = -1;
+        }
+        return AppOpsManager.MODE_ALLOWED;
+    }
+
+    @Override
+    public void finishOperation(int code, int uid, String packageName) {
+        uid = handleIncomingUid(uid);
+        synchronized (this) {
+            Op op = getOpLocked(code, uid, packageName);
+            if (op == null) {
+                return;
+            }
+            if (op.duration != -1) {
+                Slog.w(TAG, "Ignoring finishing op not started: uid " + uid + " pkg " + packageName
+                        + " code " + code + " time=" + op.time + " duration=" + op.duration);
+                return;
+            }
+            op.duration = (int)(System.currentTimeMillis() - op.time);
+        }
+    }
+
+    @Override
+    public int noteTimedOperation(int code, int uid, String packageName, int duration) {
+        uid = handleIncomingUid(uid);
+        synchronized (this) {
+            Op op = getOpLocked(code, uid, packageName);
+            if (op == null) {
+                return AppOpsManager.MODE_IGNORED;
+            }
+            if (op.duration == -1) {
+                Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
+                        + " code " + code + " time=" + op.time + " duration=" + op.duration);
+            }
+            op.time = System.currentTimeMillis();
+            op.duration = duration;
+        }
+        return AppOpsManager.MODE_ALLOWED;
+    }
+
+    @Override
+    public void earlyFinishOperation(int code, int uid, String packageName) {
+        uid = handleIncomingUid(uid);
+        synchronized (this) {
+            Op op = getOpLocked(code, uid, packageName);
+            if (op == null) {
+                return;
+            }
+            if (op.duration != -1) {
+                Slog.w(TAG, "Noting timed op not finished: uid " + uid + " pkg " + packageName
+                        + " code " + code + " time=" + op.time + " duration=" + op.duration);
+            }
+            int newDuration = (int)(System.currentTimeMillis() - op.time);
+            if (newDuration < op.duration) {
+                op.duration = newDuration;
+            }
+        }
+    }
+
+    private int handleIncomingUid(int uid) {
+        if (uid == Binder.getCallingUid()) {
+            return uid;
+        }
+        if (Binder.getCallingPid() == Process.myPid()) {
+            return uid;
+        }
+        mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+                Binder.getCallingPid(), Binder.getCallingUid(), null);
+        return uid;
+    }
+
+    private Op getOpLocked(int code, int uid, String packageName) {
+        HashMap<String, Ops> pkgOps = mUidOps.get(uid);
+        if (pkgOps == null) {
+            pkgOps = new HashMap<String, Ops>();
+            mUidOps.put(uid, pkgOps);
+        }
+        Ops ops = pkgOps.get(packageName);
+        if (ops == null) {
+            // This is the first time we have seen this package name under this uid,
+            // so let's make sure it is valid.
+            // XXX for now we always allow null through until we can fix everything
+            // to provide the name.
+            if (packageName != null) {
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    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);
+                }
+            }
+            ops = new Ops(packageName);
+            pkgOps.put(packageName, ops);
+        }
+        Op op = ops.get(code);
+        if (op == null) {
+            op = new Op(code);
+            ops.put(code, op);
+        }
+        return op;
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump ApOps service from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+
+        synchronized (this) {
+            pw.println("Current AppOps Service state:");
+            for (int i=0; i<mUidOps.size(); i++) {
+                pw.print("  Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
+                HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
+                for (Ops ops : pkgOps.values()) {
+                    pw.print("    Package "); pw.print(ops.packageName); pw.println(":");
+                    for (int j=0; j<ops.size(); j++) {
+                        Op op = ops.valueAt(j);
+                        pw.print("      "); pw.print(AppOpsManager.opToString(op.op));
+                        pw.print(": time=");
+                        TimeUtils.formatDuration(System.currentTimeMillis()-op.time, pw);
+                        pw.print(" ago");
+                        if (op.duration == -1) {
+                            pw.println(" (running)");
+                        } else {
+                            pw.print("; duration=");
+                                    TimeUtils.formatDuration(op.duration, pw);
+                                    pw.println();
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 83b94e2..9e40dc5 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -53,6 +54,7 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
@@ -60,6 +62,7 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.app.IAppOpsService;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
@@ -124,6 +127,7 @@
     private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
 
     private final Context mContext;
+    private final AppOpsManager mAppOps;
 
     // used internally for synchronization
     private final Object mLock = new Object();
@@ -187,6 +191,7 @@
     public LocationManagerService(Context context) {
         super();
         mContext = context;
+        mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
 
         if (D) Log.d(TAG, "Constructed");
 
@@ -524,6 +529,10 @@
         }
 
         public boolean callLocationChangedLocked(Location location) {
+            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_LOCATION, mUid, mPackageName)
+                    != AppOpsManager.MODE_ALLOWED) {
+                return true;
+            }
             if (mListener != null) {
                 try {
                     synchronized (this) {
@@ -1162,7 +1171,7 @@
     private Receiver checkListenerOrIntent(ILocationListener listener, PendingIntent intent,
             int pid, int uid, String packageName) {
         if (intent == null && listener == null) {
-            throw new IllegalArgumentException("need eiter listener or intent");
+            throw new IllegalArgumentException("need either listener or intent");
         } else if (intent != null && listener != null) {
             throw new IllegalArgumentException("cannot register both listener and intent");
         } else if (intent != null) {
@@ -1185,11 +1194,14 @@
 
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
-        Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
-
         // providers may use public location API's, need to clear identity
         long identity = Binder.clearCallingIdentity();
         try {
+            // We don't check for MODE_IGNORED here; we will do that when we go to deliver
+            // a location.
+            mAppOps.noteOp(AppOpsManager.OP_LOCATION, uid, packageName);
+            Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
+
             synchronized (mLock) {
                 requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
             }
@@ -1296,8 +1308,14 @@
                 request.getProvider());
         // no need to sanitize this request, as only the provider name is used
 
-        long identity = Binder.clearCallingIdentity();
+        final int uid = Binder.getCallingUid();
+        final long identity = Binder.clearCallingIdentity();
         try {
+            if (mAppOps.noteOp(AppOpsManager.OP_LOCATION, uid, packageName)
+                    != AppOpsManager.MODE_ALLOWED) {
+                return null;
+            }
+            
             if (mBlacklist.isBlacklisted(packageName)) {
                 if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
                         packageName);
@@ -1381,13 +1399,24 @@
 
 
     @Override
-    public boolean addGpsStatusListener(IGpsStatusListener listener) {
+    public boolean addGpsStatusListener(IGpsStatusListener listener, String packageName) {
         if (mGpsStatusProvider == null) {
             return false;
         }
         checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
                 LocationManager.GPS_PROVIDER);
 
+        final int uid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            if (mAppOps.noteOp(AppOpsManager.OP_LOCATION, uid, packageName)
+                    != AppOpsManager.MODE_ALLOWED) {
+                return false;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+
         try {
             mGpsStatusProvider.addGpsStatusListener(listener);
         } catch (RemoteException e) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a7b502a..c33eb2b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -261,7 +261,6 @@
             ServiceManager.addService(Context.USER_SERVICE,
                     UserManagerService.getInstance());
 
-
             mContentResolver = context.getContentResolver();
 
             // The AccountManager must come before the ContentService
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index df91dec..69379f1 100644
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -30,6 +31,7 @@
 import android.os.RemoteException;
 import android.os.IBinder;
 import android.os.Binder;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.Vibrator;
@@ -39,6 +41,9 @@
 import android.util.Slog;
 import android.view.InputDevice;
 
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.IBatteryStats;
+
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.ListIterator;
@@ -54,6 +59,8 @@
 
     private final Context mContext;
     private final PowerManager.WakeLock mWakeLock;
+    private final IAppOpsService mAppOpsService;
+    private final IBatteryStats mBatteryStatsService;
     private InputManager mIm;
 
     volatile VibrateThread mThread;
@@ -64,6 +71,8 @@
     private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
     private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
 
+    private int mCurVibUid = -1;
+
     native static boolean vibratorExists();
     native static void vibratorOn(long milliseconds);
     native static void vibratorOff();
@@ -75,23 +84,25 @@
         private final long[]  mPattern;
         private final int     mRepeat;
         private final int     mUid;
+        private final String  mPackageName;
 
-        Vibration(IBinder token, long millis, int uid) {
-            this(token, millis, null, 0, uid);
+        Vibration(IBinder token, long millis, int uid, String packageName) {
+            this(token, millis, null, 0, uid, packageName);
         }
 
-        Vibration(IBinder token, long[] pattern, int repeat, int uid) {
-            this(token, 0, pattern, repeat, uid);
+        Vibration(IBinder token, long[] pattern, int repeat, int uid, String packageName) {
+            this(token, 0, pattern, repeat, uid, packageName);
         }
 
         private Vibration(IBinder token, long millis, long[] pattern,
-                int repeat, int uid) {
+                int repeat, int uid, String packageName) {
             mToken = token;
             mTimeout = millis;
             mStartTime = SystemClock.uptimeMillis();
             mPattern = pattern;
             mRepeat = repeat;
             mUid = uid;
+            mPackageName = packageName;
         }
 
         public void binderDied() {
@@ -131,6 +142,9 @@
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
         mWakeLock.setReferenceCounted(true);
 
+        mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
+        mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+
         mVibrations = new LinkedList<Vibration>();
 
         IntentFilter filter = new IntentFilter();
@@ -164,7 +178,7 @@
         return doVibratorExists();
     }
 
-    public void vibrate(long milliseconds, IBinder token) {
+    public void vibrate(String packageName, long milliseconds, IBinder token) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires VIBRATE permission");
@@ -180,12 +194,18 @@
             return;
         }
 
-        Vibration vib = new Vibration(token, milliseconds, uid);
-        synchronized (mVibrations) {
-            removeVibrationLocked(token);
-            doCancelVibrateLocked();
-            mCurrentVibration = vib;
-            startVibrationLocked(vib);
+        Vibration vib = new Vibration(token, milliseconds, uid, packageName);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mVibrations) {
+                removeVibrationLocked(token);
+                doCancelVibrateLocked();
+                mCurrentVibration = vib;
+                startVibrationLocked(vib);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -199,7 +219,7 @@
         return true;
     }
 
-    public void vibratePattern(long[] pattern, int repeat, IBinder token) {
+    public void vibratePattern(String packageName, long[] pattern, int repeat, IBinder token) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires VIBRATE permission");
@@ -224,7 +244,7 @@
                 return;
             }
 
-            Vibration vib = new Vibration(token, pattern, repeat, uid);
+            Vibration vib = new Vibration(token, pattern, repeat, uid, packageName);
             try {
                 token.linkToDeath(vib, 0);
             } catch (RemoteException e) {
@@ -291,11 +311,13 @@
         }
         doVibratorOff();
         mH.removeCallbacks(mVibrationRunnable);
+        reportFinishVibrationLocked();
     }
 
     // Lock held on mVibrations
     private void startNextVibrationLocked() {
         if (mVibrations.size() <= 0) {
+            reportFinishVibrationLocked();
             mCurrentVibration = null;
             return;
         }
@@ -305,8 +327,19 @@
 
     // Lock held on mVibrations
     private void startVibrationLocked(final Vibration vib) {
+        try {
+            int mode = mAppOpsService.startOperation(AppOpsManager.OP_VIBRATE, vib.mUid, vib.mPackageName);
+            if (mode != AppOpsManager.MODE_ALLOWED) {
+                if (mode == AppOpsManager.MODE_ERRORED) {
+                    Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
+                }
+                mH.post(mVibrationRunnable);
+                return;
+            }
+        } catch (RemoteException e) {
+        }
         if (vib.mTimeout != 0) {
-            doVibratorOn(vib.mTimeout);
+            doVibratorOn(vib.mTimeout, vib.mUid);
             mH.postDelayed(mVibrationRunnable, vib.mTimeout);
         } else {
             // mThread better be null here. doCancelVibrate should always be
@@ -316,6 +349,17 @@
         }
     }
 
+    private void reportFinishVibrationLocked() {
+        if (mCurrentVibration != null) {
+            try {
+                mAppOpsService.finishOperation(AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
+                        mCurrentVibration.mPackageName);
+            } catch (RemoteException e) {
+            }
+            mCurrentVibration = null;
+        }
+    }
+
     // Lock held on mVibrations
     private Vibration removeVibrationLocked(IBinder token) {
         ListIterator<Vibration> iter = mVibrations.listIterator(0);
@@ -413,8 +457,13 @@
         return vibratorExists();
     }
 
-    private void doVibratorOn(long millis) {
+    private void doVibratorOn(long millis, int uid) {
         synchronized (mInputDeviceVibrators) {
+            try {
+                mBatteryStatsService.noteVibratorOn(uid, millis);
+                mCurVibUid = uid;
+            } catch (RemoteException e) {
+            }
             final int vibratorCount = mInputDeviceVibrators.size();
             if (vibratorCount != 0) {
                 for (int i = 0; i < vibratorCount; i++) {
@@ -428,6 +477,13 @@
 
     private void doVibratorOff() {
         synchronized (mInputDeviceVibrators) {
+            if (mCurVibUid >= 0) {
+                try {
+                    mBatteryStatsService.noteVibratorOff(mCurVibUid);
+                } catch (RemoteException e) {
+                }
+                mCurVibUid = -1;
+            }
             final int vibratorCount = mInputDeviceVibrators.size();
             if (vibratorCount != 0) {
                 for (int i = 0; i < vibratorCount; i++) {
@@ -470,10 +526,11 @@
         public void run() {
             Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
             synchronized (this) {
+                final long[] pattern = mVibration.mPattern;
+                final int len = pattern.length;
+                final int repeat = mVibration.mRepeat;
+                final int uid = mVibration.mUid;
                 int index = 0;
-                long[] pattern = mVibration.mPattern;
-                int len = pattern.length;
-                int repeat = mVibration.mRepeat;
                 long duration = 0;
 
                 while (!mDone) {
@@ -493,7 +550,7 @@
                         // duration is saved for delay() at top of loop
                         duration = pattern[index++];
                         if (duration > 0) {
-                            VibratorService.this.doVibratorOn(duration);
+                            VibratorService.this.doVibratorOn(duration, uid);
                         }
                     } else {
                         if (repeat < 0) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 62af91e..b08fc28 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -21,6 +21,7 @@
 import com.android.internal.R;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.ProcessStats;
+import com.android.server.AppOpsService;
 import com.android.server.AttributeCache;
 import com.android.server.IntentResolver;
 import com.android.server.ProcessMap;
@@ -619,9 +620,14 @@
     final BatteryStatsService mBatteryStatsService;
     
     /**
-     * information about component usage
+     * Information about component usage
      */
     final UsageStatsService mUsageStatsService;
+    
+    /**
+     * Information about and control over application operations
+     */
+    final AppOpsService mAppOpsService;
 
     /**
      * Current configuration information.  HistoryRecord objects are given
@@ -1450,7 +1456,8 @@
         
         m.mBatteryStatsService.publish(context);
         m.mUsageStatsService.publish(context);
-        
+        m.mAppOpsService.publish(context);
+
         synchronized (thr) {
             thr.mReady = true;
             thr.notifyAll();
@@ -1613,9 +1620,10 @@
         mOnBattery = DEBUG_POWER ? true
                 : mBatteryStatsService.getActiveStatistics().getIsOnBattery();
         mBatteryStatsService.getActiveStatistics().setCallback(this);
-        
+
         mUsageStatsService = new UsageStatsService(new File(
                 systemDir, "usagestats").toString());
+        mAppOpsService = new AppOpsService();
         mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
 
         // User 0 is the first and only user that runs at boot.
@@ -7174,7 +7182,8 @@
                 }
             }
         }
-        
+
+        mAppOpsService.shutdown();
         mUsageStatsService.shutdown();
         mBatteryStatsService.shutdown();
         
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index ab20208..d19c7f6 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -144,6 +144,20 @@
         }
     }
     
+    public void noteVibratorOn(int uid, long durationMillis) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteVibratorOnLocked(uid, durationMillis);
+        }
+    }
+
+    public void noteVibratorOff(int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteVibratorOffLocked(uid);
+        }
+    }
+
     public void noteStartGps(int uid) {
         enforceCallingPermission();
         synchronized (mStats) {
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 7f059f5..f1739d5 100644
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -17,6 +17,7 @@
 package com.android.server.location;
 
 import android.app.AlarmManager;
+import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -56,6 +57,8 @@
 import android.telephony.gsm.GsmCellLocation;
 import android.util.Log;
 import android.util.NtpTrustedTime;
+
+import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.location.GpsNetInitiatedHandler;
 import com.android.internal.location.ProviderProperties;
@@ -305,6 +308,7 @@
     private final PendingIntent mWakeupIntent;
     private final PendingIntent mTimeoutIntent;
 
+    private final IAppOpsService mAppOpsService;
     private final IBatteryStats mBatteryStats;
 
     // only modified on handler thread
@@ -434,6 +438,10 @@
 
         mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
 
+        // App ops service to keep track of who is accessing the GPS
+        mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(
+                Context.APP_OPS_SERVICE));
+
         // Battery statistics service to be notified when GPS turns on or off
         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
 
@@ -863,6 +871,7 @@
             }
             if (newUid) {
                 try {
+                    mAppOpsService.startOperation(AppOpsManager.OP_GPS, uid1, null);
                     mBatteryStats.noteStartGps(uid1);
                 } catch (RemoteException e) {
                     Log.w(TAG, "RemoteException", e);
@@ -882,6 +891,7 @@
             if (oldUid) {
                 try {
                     mBatteryStats.noteStopGps(uid1);
+                    mAppOpsService.finishOperation(AppOpsManager.OP_GPS, uid1, null);
                 } catch (RemoteException e) {
                     Log.w(TAG, "RemoteException", e);
                 }
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 2eba4e1..5ee52de 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -81,6 +81,13 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public int getPackageUid(String packageName, int userHandle)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public PermissionInfo getPermissionInfo(String name, int flags)
     throws NameNotFoundException {
diff --git a/tests/HwAccelerationTest/res/drawable/patch.9.png b/tests/HwAccelerationTest/res/drawable/patch.9.png
deleted file mode 100644
index e3b3639..0000000
--- a/tests/HwAccelerationTest/res/drawable/patch.9.png
+++ /dev/null
Binary files differ