Merge "Cleanup 9patch mesh matching code Bug #7970966"
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 e4d5fd1..72e7601 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 257f84e..c777250 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2289,6 +2289,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/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index d909362..122f8a1 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -503,8 +503,15 @@
mNumberOfBlocks = newNumberOfBlocks;
final int deltaLines = newLineCount - (endLine - startLine + 1);
- for (int i = firstBlock + numAddedBlocks; i < mNumberOfBlocks; i++) {
- mBlockEndLines[i] += deltaLines;
+ if (deltaLines != 0) {
+ // Display list whose index is >= mIndexFirstChangedBlock is valid
+ // but it needs to update its drawing location.
+ mIndexFirstChangedBlock = firstBlock + numAddedBlocks;
+ for (int i = mIndexFirstChangedBlock; i < mNumberOfBlocks; i++) {
+ mBlockEndLines[i] += deltaLines;
+ }
+ } else {
+ mIndexFirstChangedBlock = mNumberOfBlocks;
}
int blockIndex = firstBlock;
@@ -559,6 +566,20 @@
return mNumberOfBlocks;
}
+ /**
+ * @hide
+ */
+ public int getIndexFirstChangedBlock() {
+ return mIndexFirstChangedBlock;
+ }
+
+ /**
+ * @hide
+ */
+ public void setIndexFirstChangedBlock(int i) {
+ mIndexFirstChangedBlock = i;
+ }
+
@Override
public int getLineCount() {
return mInts.size() - 1;
@@ -697,6 +718,8 @@
private int[] mBlockIndices;
// Number of items actually currently being used in the above 2 arrays
private int mNumberOfBlocks;
+ // The first index of the blocks whose locations are changed
+ private int mIndexFirstChangedBlock;
private int mTopPadding, mBottomPadding;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 41d6033..4eaa78a 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -124,7 +124,6 @@
InputMethodState mInputMethodState;
DisplayList[] mTextDisplayLists;
- int mLastLayoutHeight;
boolean mFrozenWithFocus;
boolean mSelectionMoved;
@@ -1289,20 +1288,11 @@
mTextDisplayLists = new DisplayList[ArrayUtils.idealObjectArraySize(0)];
}
- // If the height of the layout changes (usually when inserting or deleting a line,
- // but could be changes within a span), invalidate everything. We could optimize
- // more aggressively (for example, adding offsets to blocks) but it would be more
- // complex and we would only get the benefit in some cases.
- int layoutHeight = layout.getHeight();
- if (mLastLayoutHeight != layoutHeight) {
- invalidateTextDisplayList();
- mLastLayoutHeight = layoutHeight;
- }
-
DynamicLayout dynamicLayout = (DynamicLayout) layout;
int[] blockEndLines = dynamicLayout.getBlockEndLines();
int[] blockIndices = dynamicLayout.getBlockIndices();
final int numberOfBlocks = dynamicLayout.getNumberOfBlocks();
+ final int indexFirstChangedBlock = dynamicLayout.getIndexFirstChangedBlock();
int endOfPreviousBlock = -1;
int searchStartIndex = 0;
@@ -1327,7 +1317,8 @@
if (blockIsInvalid) blockDisplayList.invalidate();
}
- if (!blockDisplayList.isValid()) {
+ final boolean blockDisplayListIsInvalid = !blockDisplayList.isValid();
+ if (i >= indexFirstChangedBlock || blockDisplayListIsInvalid) {
final int blockBeginLine = endOfPreviousBlock + 1;
final int top = layout.getLineTop(blockBeginLine);
final int bottom = layout.getLineBottom(blockEndLine);
@@ -1344,24 +1335,30 @@
right = (int) (max + 0.5f);
}
- final HardwareCanvas hardwareCanvas = blockDisplayList.start();
- try {
- // Tighten the bounds of the viewport to the actual text size
- hardwareCanvas.setViewport(right - left, bottom - top);
- // The dirty rect should always be null for a display list
- hardwareCanvas.onPreDraw(null);
- // drawText is always relative to TextView's origin, this translation brings
- // this range of text back to the top left corner of the viewport
- hardwareCanvas.translate(-left, -top);
- layout.drawText(hardwareCanvas, blockBeginLine, blockEndLine);
- // No need to untranslate, previous context is popped after drawDisplayList
- } finally {
- hardwareCanvas.onPostDraw();
- blockDisplayList.end();
- blockDisplayList.setLeftTopRightBottom(left, top, right, bottom);
- // Same as drawDisplayList below, handled by our TextView's parent
- blockDisplayList.setClipChildren(false);
+ // Rebuild display list if it is invalid
+ if (blockDisplayListIsInvalid) {
+ final HardwareCanvas hardwareCanvas = blockDisplayList.start();
+ try {
+ // Tighten the bounds of the viewport to the actual text size
+ hardwareCanvas.setViewport(right - left, bottom - top);
+ // The dirty rect should always be null for a display list
+ hardwareCanvas.onPreDraw(null);
+ // drawText is always relative to TextView's origin, this translation brings
+ // this range of text back to the top left corner of the viewport
+ hardwareCanvas.translate(-left, -top);
+ layout.drawText(hardwareCanvas, blockBeginLine, blockEndLine);
+ // No need to untranslate, previous context is popped after drawDisplayList
+ } finally {
+ hardwareCanvas.onPostDraw();
+ blockDisplayList.end();
+ // Same as drawDisplayList below, handled by our TextView's parent
+ blockDisplayList.setClipChildren(false);
+ }
}
+
+ // Valid disply list whose index is >= indexFirstChangedBlock
+ // only needs to update its drawing location.
+ blockDisplayList.setLeftTopRightBottom(left, top, right, bottom);
}
((HardwareCanvas) canvas).drawDisplayList(blockDisplayList, null,
@@ -1369,6 +1366,8 @@
endOfPreviousBlock = blockEndLine;
}
+
+ dynamicLayout.setIndexFirstChangedBlock(numberOfBlocks);
} else {
// Boring layout is used for empty and hint text
layout.drawText(canvas, firstLine, lastLine);
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/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d635de6..f8db622 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6459,7 +6459,6 @@
mDeferScroll = -1;
bringPointIntoView(Math.min(curs, mText.length()));
}
- if (changed && mEditor != null) mEditor.invalidateTextDisplayList();
}
private boolean isShowingHint() {
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/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/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index fbbcb0a..7768a61 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -958,7 +958,9 @@
// If the rowId of the inserted file is needed, it gets inserted immediately,
// bypassing the bulk inserter.
if (inserter == null || needToSetSettings) {
- inserter.flushAll();
+ if (inserter != null) {
+ inserter.flushAll();
+ }
result = mMediaProvider.insert(tableUri, values);
} else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
inserter.insertwithPriority(tableUri, values);
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 {