Merge "sql statement with syntax errors can leave database lock in bad state"
diff --git a/api/current.xml b/api/current.xml
index ed51bbf..a9abb141 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -93977,6 +93977,8 @@
>
<parameter name="uid" type="int">
</parameter>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
</method>
<method name="onDisable"
return="void"
@@ -94106,6 +94108,8 @@
>
<parameter name="uid" type="int">
</parameter>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
</method>
<method name="onRequiresCell"
return="boolean"
@@ -94167,6 +94171,8 @@
>
<parameter name="minTime" type="long">
</parameter>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
</method>
<method name="onSupportsAltitude"
return="boolean"
@@ -111503,6 +111509,19 @@
<parameter name="refCounted" type="boolean">
</parameter>
</method>
+<method name="setWorkSource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
+</method>
</class>
</package>
<package name="android.opengl"
@@ -138355,6 +138374,19 @@
<parameter name="value" type="boolean">
</parameter>
</method>
+<method name="setWorkSource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
+</method>
</class>
<class name="Process"
extends="java.lang.Object"
@@ -139503,6 +139535,134 @@
</parameter>
</method>
</class>
+<class name="WorkSource"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="WorkSource"
+ type="android.os.WorkSource"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="WorkSource"
+ type="android.os.WorkSource"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="orig" type="android.os.WorkSource">
+</parameter>
+</constructor>
+<method name="add"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="other" type="android.os.WorkSource">
+</parameter>
+</method>
+<method name="clear"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="diff"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="other" type="android.os.WorkSource">
+</parameter>
+</method>
+<method name="remove"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="other" type="android.os.WorkSource">
+</parameter>
+</method>
+<method name="set"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="other" type="android.os.WorkSource">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
</package>
<package name="android.os.storage"
>
@@ -142069,6 +142229,19 @@
<parameter name="args" type="android.os.Bundle">
</parameter>
</method>
+<method name="switchToHeader"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="header" type="android.preference.PreferenceActivity.Header">
+</parameter>
+</method>
<field name="EXTRA_NO_HEADERS"
type="java.lang.String"
transient="false"
@@ -142102,15 +142275,28 @@
visibility="public"
>
</field>
+<field name="HEADER_ID_UNDEFINED"
+ type="long"
+ transient="false"
+ volatile="false"
+ value="-1L"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="PreferenceActivity.Header"
extends="java.lang.Object"
abstract="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
+<implements name="android.os.Parcelable">
+</implements>
<constructor name="PreferenceActivity.Header"
type="android.preference.PreferenceActivity.Header"
static="false"
@@ -142119,6 +142305,65 @@
visibility="public"
>
</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="readFromParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="in" type="android.os.Parcel">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="extras"
+ type="android.os.Bundle"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="fragment"
type="java.lang.String"
transient="false"
@@ -142139,8 +142384,8 @@
visibility="public"
>
</field>
-<field name="icon"
- type="android.graphics.drawable.Drawable"
+<field name="iconRes"
+ type="int"
transient="false"
volatile="false"
static="false"
@@ -142149,8 +142394,8 @@
visibility="public"
>
</field>
-<field name="iconRes"
- type="int"
+<field name="id"
+ type="long"
transient="false"
volatile="false"
static="false"
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index d3ec3d9..e1d6619 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -64,7 +64,7 @@
= IPowerManager.Stub.asInterface(ServiceManager.getService(Context.POWER_SERVICE));
try {
IBinder lock = new Binder();
- pm.acquireWakeLock(PowerManager.FULL_WAKE_LOCK, lock, "svc power");
+ pm.acquireWakeLock(PowerManager.FULL_WAKE_LOCK, lock, "svc power", null);
pm.setStayOnSetting(val);
pm.releaseWakeLock(lock, 0);
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 97aa23e..2e8d682 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4204,6 +4204,7 @@
}
final void performStart() {
+ mFragments.mStateSaved = false;
mCalled = false;
mFragments.execPendingActions();
mInstrumentation.callActivityOnStart(this);
@@ -4221,6 +4222,8 @@
}
final void performRestart() {
+ mFragments.mStateSaved = false;
+
synchronized (mManagedCursors) {
final int N = mManagedCursors.size();
for (int i=0; i<N; i++) {
@@ -4359,6 +4362,7 @@
if (Config.LOGV) Log.v(
TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode
+ ", resCode=" + resultCode + ", data=" + data);
+ mFragments.mStateSaved = false;
if (who == null) {
onActivityResult(requestCode, resultCode, data);
} else {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 03bcadc..16a8c57 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -26,8 +26,10 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
+import android.util.Pair;
import java.io.IOException;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
@@ -863,6 +865,37 @@
return socket;
}
+ /**
+ * Read the local Out of Band Pairing Data
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @return Pair<byte[], byte[]> of Hash and Randomizer
+ *
+ * @hide
+ */
+ public Pair<byte[], byte[]> readOutOfBandData() {
+ if (getState() != STATE_ON) return null;
+ try {
+ byte[] hash = new byte[16];
+ byte[] randomizer = new byte[16];
+
+ byte[] ret = mService.readOutOfBandData();
+
+ if (ret == null || ret.length != 32) return null;
+
+ hash = Arrays.copyOfRange(ret, 0, 16);
+ randomizer = Arrays.copyOfRange(ret, 16, 32);
+
+ if (DBG) {
+ Log.d(TAG, "readOutOfBandData:" + Arrays.toString(hash) +
+ ":" + Arrays.toString(randomizer));
+ }
+ return new Pair<byte[], byte[]>(hash, randomizer);
+
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return null;
+ }
+
private Set<BluetoothDevice> toDeviceSet(String[] addresses) {
Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length);
for (int i = 0; i < addresses.length; i++) {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index e77e76f..e577ec4 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -325,7 +325,9 @@
/** The user will be prompted to enter the passkey displayed on remote device
* @hide */
public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4;
-
+ /** The user will be prompted to accept or deny the OOB pairing request
+ * @hide */
+ public static final int PAIRING_VARIANT_OOB_CONSENT = 5;
/**
* Used as an extra field in {@link #ACTION_UUID} intents,
* Contains the {@link android.os.ParcelUuid}s of the remote device which
@@ -464,6 +466,52 @@
}
/**
+ * Start the bonding (pairing) process with the remote device using the
+ * Out Of Band mechanism.
+ *
+ * <p>This is an asynchronous call, it will return immediately. Register
+ * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
+ * the bonding process completes, and its result.
+ *
+ * <p>Android system services will handle the necessary user interactions
+ * to confirm and complete the bonding process.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
+ *
+ * @param hash - Simple Secure pairing hash
+ * @param randomizer - The random key obtained using OOB
+ * @return false on immediate error, true if bonding will begin
+ *
+ * @hide
+ */
+ public boolean createBondOutOfBand(byte[] hash, byte[] randomizer) {
+ try {
+ return sService.createBondOutOfBand(mAddress, hash, randomizer);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
+ * Set the Out Of Band data for a remote device to be used later
+ * in the pairing mechanism. Users can obtain this data through other
+ * trusted channels
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
+ *
+ * @param hash Simple Secure pairing hash
+ * @param randomizer The random key obtained using OOB
+ * @return false on error; true otherwise
+ *
+ * @hide
+ */
+ public boolean setDeviceOutOfBandData(byte[] hash, byte[] randomizer) {
+ try {
+ return sService.setDeviceOutOfBandData(mAddress, hash, randomizer);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
* Cancel an in-progress bonding request started with {@link #createBond}.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
*
@@ -617,6 +665,14 @@
}
/** @hide */
+ public boolean setRemoteOutOfBandData() {
+ try {
+ return sService.setRemoteOutOfBandData(mAddress);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /** @hide */
public boolean cancelPairingUserInput() {
try {
return sService.cancelPairingUserInput(mAddress);
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index c4a40cd..cc23146 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -45,12 +45,15 @@
boolean startDiscovery();
boolean cancelDiscovery();
boolean isDiscovering();
+ byte[] readOutOfBandData();
boolean createBond(in String address);
+ boolean createBondOutOfBand(in String address, in byte[] hash, in byte[] randomizer);
boolean cancelBondProcess(in String address);
boolean removeBond(in String address);
String[] listBonds();
int getBondState(in String address);
+ boolean setDeviceOutOfBandData(in String address, in byte[] hash, in byte[] randomizer);
String getRemoteName(in String address);
int getRemoteClass(in String address);
@@ -61,6 +64,7 @@
boolean setPin(in String address, in byte[] pin);
boolean setPasskey(in String address, int passkey);
boolean setPairingConfirmation(in String address, boolean confirm);
+ boolean setRemoteOutOfBandData(in String addres);
boolean cancelPairingUserInput(in String address);
boolean setTrust(in String address, in boolean value);
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 7f749bb..950d339 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -47,6 +47,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.WorkSource;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.text.format.Time;
@@ -129,8 +130,8 @@
private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000;
- private static final String SYNC_WAKE_LOCK_PREFIX = "SyncWakeLock";
- private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock";
+ private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*";
+ private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
private Context mContext;
@@ -1735,6 +1736,7 @@
PowerManager.WakeLock oldWakeLock = mSyncWakeLock;
try {
mSyncWakeLock = getSyncWakeLock(op.account.type, op.authority);
+ mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterInfo.uid));
mSyncWakeLock.acquire();
} finally {
if (oldWakeLock != null && oldWakeLock != mSyncWakeLock) {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index f5b1e57..f182a7a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -404,6 +404,7 @@
public static final byte CMD_UPDATE = 0;
public static final byte CMD_START = 1;
+ public static final byte CMD_OVERFLOW = 2;
public byte cmd;
@@ -1703,6 +1704,8 @@
pw.print(" ");
if (rec.cmd == HistoryItem.CMD_START) {
pw.println(" START");
+ } else if (rec.cmd == HistoryItem.CMD_OVERFLOW) {
+ pw.println(" *OVERFLOW*");
} else {
if (rec.batteryLevel < 10) pw.print("00");
else if (rec.batteryLevel < 100) pw.print("0");
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 01cc408..0067e940 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -17,10 +17,13 @@
package android.os;
+import android.os.WorkSource;
+
/** @hide */
interface IPowerManager
{
- void acquireWakeLock(int flags, IBinder lock, String tag);
+ void acquireWakeLock(int flags, IBinder lock, String tag, in WorkSource ws);
+ void updateWakeLockWorkSource(IBinder lock, in WorkSource ws);
void goToSleep(long time);
void goToSleepWithReason(long time, int reason);
void releaseWakeLock(IBinder lock, int flags);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index f4ca8bc..3876a3e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -209,6 +209,7 @@
int mCount = 0;
boolean mRefCounted = true;
boolean mHeld = false;
+ WorkSource mWorkSource;
WakeLock(int flags, String tag)
{
@@ -247,7 +248,7 @@
synchronized (mToken) {
if (!mRefCounted || mCount++ == 0) {
try {
- mService.acquireWakeLock(mFlags, mToken, mTag);
+ mService.acquireWakeLock(mFlags, mToken, mTag, mWorkSource);
} catch (RemoteException e) {
}
mHeld = true;
@@ -313,6 +314,32 @@
}
}
+ public void setWorkSource(WorkSource ws) {
+ synchronized (mToken) {
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+ boolean changed = true;
+ if (ws == null) {
+ mWorkSource = null;
+ } else if (mWorkSource == null) {
+ changed = mWorkSource != null;
+ mWorkSource = new WorkSource(ws);
+ } else {
+ changed = mWorkSource.diff(ws);
+ if (changed) {
+ mWorkSource.set(ws);
+ }
+ }
+ if (changed && mHeld) {
+ try {
+ mService.updateWakeLockWorkSource(mToken, mWorkSource);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
public String toString() {
synchronized (mToken) {
return "WakeLock{"
diff --git a/core/java/android/os/WorkSource.aidl b/core/java/android/os/WorkSource.aidl
new file mode 100644
index 0000000..1e7fabc
--- /dev/null
+++ b/core/java/android/os/WorkSource.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2010, 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.os;
+
+parcelable WorkSource;
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
new file mode 100644
index 0000000..bba1984
--- /dev/null
+++ b/core/java/android/os/WorkSource.java
@@ -0,0 +1,311 @@
+package android.os;
+
+/**
+ * Describes the source of some work that may be done by someone else.
+ * Currently the public representation of what a work source is is not
+ * defined; this is an opaque container.
+ */
+public class WorkSource implements Parcelable {
+ int mNum;
+ int[] mUids;
+
+ /**
+ * Internal statics to avoid object allocations in some operations.
+ * The WorkSource object itself is not thread safe, but we need to
+ * hold sTmpWorkSource lock while working with these statics.
+ */
+ static final WorkSource sTmpWorkSource = new WorkSource(0);
+ /**
+ * For returning newbie work from a modification operation.
+ */
+ static WorkSource sNewbWork;
+ /**
+ * For returning gone work form a modification operation.
+ */
+ static WorkSource sGoneWork;
+
+ /**
+ * Create an empty work source.
+ */
+ public WorkSource() {
+ mNum = 0;
+ }
+
+ /**
+ * Create a new WorkSource that is a copy of an existing one.
+ * If <var>orig</var> is null, an empty WorkSource is created.
+ */
+ public WorkSource(WorkSource orig) {
+ if (orig == null) {
+ mNum = 0;
+ return;
+ }
+ mNum = orig.mNum;
+ if (orig.mUids != null) {
+ mUids = orig.mUids.clone();
+ } else {
+ mUids = null;
+ }
+ }
+
+ /** @hide */
+ public WorkSource(int uid) {
+ mNum = 1;
+ mUids = new int[] { uid, 0 };
+ }
+
+ WorkSource(Parcel in) {
+ mNum = in.readInt();
+ mUids = in.createIntArray();
+ }
+
+ /** @hide */
+ public int size() {
+ return mNum;
+ }
+
+ /** @hide */
+ public int get(int index) {
+ return mUids[index];
+ }
+
+ /**
+ * Clear this WorkSource to be empty.
+ */
+ public void clear() {
+ mNum = 0;
+ }
+
+ /**
+ * Compare this WorkSource with another.
+ * @param other The WorkSource to compare against.
+ * @return If there is a difference, true is returned.
+ */
+ public boolean diff(WorkSource other) {
+ int N = mNum;
+ if (N != other.mNum) {
+ return true;
+ }
+ final int[] uids1 = mUids;
+ final int[] uids2 = other.mUids;
+ for (int i=0; i<N; i++) {
+ if (uids1[i] != uids2[i]) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Replace the current contents of this work source with the given
+ * work source. If <var>other</var> is null, the current work source
+ * will be made empty.
+ */
+ public void set(WorkSource other) {
+ if (other == null) {
+ mNum = 0;
+ return;
+ }
+ mNum = other.mNum;
+ if (other.mUids != null) {
+ if (mUids != null && mUids.length >= mNum) {
+ System.arraycopy(other.mUids, 0, mUids, 0, mNum);
+ } else {
+ mUids = other.mUids.clone();
+ }
+ } else {
+ mUids = null;
+ }
+ }
+
+ /** @hide */
+ public void set(int uid) {
+ mNum = 1;
+ if (mUids == null) mUids = new int[2];
+ mUids[0] = uid;
+ }
+
+ /** @hide */
+ public WorkSource[] setReturningDiffs(WorkSource other) {
+ synchronized (sTmpWorkSource) {
+ sNewbWork = null;
+ sGoneWork = null;
+ updateLocked(other, true, true);
+ if (sNewbWork != null || sGoneWork != null) {
+ WorkSource[] diffs = new WorkSource[2];
+ diffs[0] = sNewbWork;
+ diffs[1] = sGoneWork;
+ return diffs;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Merge the contents of <var>other</var> WorkSource in to this one.
+ *
+ * @param other The other WorkSource whose contents are to be merged.
+ * @return Returns true if any new sources were added.
+ */
+ public boolean add(WorkSource other) {
+ synchronized (sTmpWorkSource) {
+ return updateLocked(other, false, false);
+ }
+ }
+
+ /** @hide */
+ public WorkSource addReturningNewbs(WorkSource other) {
+ synchronized (sTmpWorkSource) {
+ sNewbWork = null;
+ updateLocked(other, false, true);
+ return sNewbWork;
+ }
+ }
+
+ /** @hide */
+ public boolean add(int uid) {
+ synchronized (sTmpWorkSource) {
+ sTmpWorkSource.mUids[0] = uid;
+ return updateLocked(sTmpWorkSource, false, false);
+ }
+ }
+
+ /** @hide */
+ public WorkSource addReturningNewbs(int uid) {
+ synchronized (sTmpWorkSource) {
+ sNewbWork = null;
+ sTmpWorkSource.mUids[0] = uid;
+ updateLocked(sTmpWorkSource, false, true);
+ return sNewbWork;
+ }
+ }
+
+ public boolean remove(WorkSource other) {
+ int N1 = mNum;
+ final int[] uids1 = mUids;
+ final int N2 = other.mNum;
+ final int[] uids2 = other.mUids;
+ boolean changed = false;
+ int i1 = 0;
+ for (int i2=0; i2<N2 && i1<N1; i2++) {
+ if (uids2[i2] == uids1[i1]) {
+ N1--;
+ if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1-1, N1-i1);
+ }
+ while (i1 < N1 && uids2[i2] > uids1[i1]) {
+ i1++;
+ }
+ }
+
+ mNum = N1;
+
+ return changed;
+ }
+
+ private boolean updateLocked(WorkSource other, boolean set, boolean returnNewbs) {
+ int N1 = mNum;
+ int[] uids1 = mUids;
+ final int N2 = other.mNum;
+ final int[] uids2 = other.mUids;
+ boolean changed = false;
+ int i1 = 0;
+ for (int i2=0; i2<N2; i2++) {
+ if (i1 >= N1 || uids2[i2] < uids1[i1]) {
+ // Need to insert a new uid.
+ changed = true;
+ if (uids1 == null) {
+ uids1 = new int[4];
+ uids1[0] = uids2[i2];
+ } else if (i1 >= uids1.length) {
+ int[] newuids = new int[(uids1.length*3)/2];
+ if (i1 > 0) System.arraycopy(uids1, 0, newuids, 0, i1);
+ if (i1 < N1) System.arraycopy(uids1, i1, newuids, i1+1, N1-i1);
+ uids1 = newuids;
+ uids1[i1] = uids2[i2];
+ } else {
+ if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1+1, N1-i1);
+ uids1[i1] = uids2[i2];
+ }
+ if (returnNewbs) {
+ if (sNewbWork == null) {
+ sNewbWork = new WorkSource(uids2[i2]);
+ } else {
+ sNewbWork.addLocked(uids2[i2]);
+ }
+ }
+ N1++;
+ i1++;
+ } else {
+ if (!set) {
+ // Skip uids that already exist or are not in 'other'.
+ do {
+ i1++;
+ } while (i1 < N1 && uids2[i2] >= uids1[i1]);
+ } else {
+ // Remove any uids that don't exist in 'other'.
+ int start = i1;
+ while (i1 < N1 && uids2[i2] > uids1[i1]) {
+ if (sGoneWork == null) {
+ sGoneWork = new WorkSource(uids1[i1]);
+ } else {
+ sGoneWork.addLocked(uids1[i1]);
+ }
+ i1++;
+ }
+ if (start < i1) {
+ System.arraycopy(uids1, i1, uids1, start, i1-start);
+ N1 -= i1-start;
+ i1 = start;
+ }
+ // If there is a matching uid, skip it.
+ if (i1 < N1 && uids2[i1] == uids1[i1]) {
+ i1++;
+ }
+ }
+ }
+ }
+
+ mNum = N1;
+ mUids = uids1;
+
+ return changed;
+ }
+
+ private void addLocked(int uid) {
+ if (mUids == null) {
+ mUids = new int[4];
+ mUids[0] = uid;
+ mNum = 1;
+ return;
+ }
+ if (mNum >= mUids.length) {
+ int[] newuids = new int[(mNum*3)/2];
+ System.arraycopy(mUids, 0, newuids, 0, mNum);
+ mUids = newuids;
+ }
+
+ mUids[mNum] = uid;
+ mNum++;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mNum);
+ dest.writeIntArray(mUids);
+ }
+
+ public static final Parcelable.Creator<WorkSource> CREATOR
+ = new Parcelable.Creator<WorkSource>() {
+ public WorkSource createFromParcel(Parcel in) {
+ return new WorkSource(in);
+ }
+ public WorkSource[] newArray(int size) {
+ return new WorkSource[size];
+ }
+ };
+}
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 2efb09d..60d810e 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -29,10 +29,11 @@
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Xml;
@@ -40,6 +41,7 @@
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
+import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.FrameLayout;
@@ -112,7 +114,11 @@
PreferenceFragment.OnPreferenceStartFragmentCallback {
private static final String TAG = "PreferenceActivity";
- private static final String PREFERENCES_TAG = "android:preferences";
+ // Constants for state save/restore
+ private static final String HEADERS_TAG = ":android:headers";
+ private static final String CUR_HEADER_TAG = ":android:cur_header";
+ private static final String SINGLE_PANE_TAG = ":android:single_pane";
+ private static final String PREFERENCES_TAG = ":android:preferences";
/**
* When starting this activity, the invoking Intent can contain this extra
@@ -165,6 +171,8 @@
private boolean mSinglePane;
+ private Header mCurHeader;
+
// --- State for old mode when showing a single preference list
private PreferenceManager mPreferenceManager;
@@ -186,17 +194,29 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_BIND_PREFERENCES:
+ case MSG_BIND_PREFERENCES: {
bindPreferences();
- break;
- case MSG_BUILD_HEADERS:
+ } break;
+ case MSG_BUILD_HEADERS: {
+ ArrayList<Header> oldHeaders = new ArrayList<Header>(mHeaders);
+ mHeaders.clear();
onBuildHeaders(mHeaders);
mAdapter.notifyDataSetChanged();
Header header = onGetNewHeader();
if (header != null && header.fragment != null) {
- switchToHeader(header.fragment, header.fragmentArguments);
+ Header mappedHeader = findBestMatchingHeader(header, oldHeaders);
+ if (mappedHeader == null || mCurHeader != mappedHeader) {
+ switchToHeader(header);
+ }
+ } else if (mCurHeader != null) {
+ Header mappedHeader = findBestMatchingHeader(mCurHeader, mHeaders);
+ if (mappedHeader != null) {
+ setSelectedHeader(mappedHeader);
+ } else {
+ switchToHeader(null);
+ }
}
- break;
+ } break;
}
}
};
@@ -235,13 +255,7 @@
// All view fields must be updated every time, because the view may be recycled
Header header = getItem(position);
- if (header.icon == null) {
- holder.icon.setImageDrawable(null);
- holder.icon.setImageResource(header.iconRes);
- } else {
- holder.icon.setImageResource(0);
- holder.icon.setImageDrawable(header.icon);
- }
+ holder.icon.setImageResource(header.iconRes);
holder.title.setText(header.title);
if (TextUtils.isEmpty(header.summary)) {
holder.summary.setVisibility(View.GONE);
@@ -255,9 +269,24 @@
}
/**
+ * Default value for {@link Header#id Header.id} indicating that no
+ * identifier value is set. All other values (including those below -1)
+ * are valid.
+ */
+ public static final long HEADER_ID_UNDEFINED = -1;
+
+ /**
* Description of a single Header item that the user can select.
*/
- public static class Header {
+ public static final class Header implements Parcelable {
+ /**
+ * Identifier for this header, to correlate with a new list when
+ * it is updated. The default value is
+ * {@link PreferenceActivity#HEADER_ID_UNDEFINED}, meaning no id.
+ * @attr ref android.R.styleable#PreferenceHeader_id
+ */
+ public long id = HEADER_ID_UNDEFINED;
+
/**
* Title of the header that is shown to the user.
* @attr ref android.R.styleable#PreferenceHeader_title
@@ -277,12 +306,6 @@
public int iconRes;
/**
- * Optional icon drawable to show for this header. (If this is non-null,
- * the iconRes will be ignored.)
- */
- public Drawable icon;
-
- /**
* Full class name of the fragment to display when this header is
* selected.
* @attr ref android.R.styleable#PreferenceHeader_fragment
@@ -299,6 +322,62 @@
* Intent to launch when the preference is selected.
*/
public Intent intent;
+
+ /**
+ * Optional additional data for use by subclasses of PreferenceActivity.
+ */
+ public Bundle extras;
+
+ public Header() {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(id);
+ TextUtils.writeToParcel(title, dest, flags);
+ TextUtils.writeToParcel(summary, dest, flags);
+ dest.writeInt(iconRes);
+ dest.writeString(fragment);
+ dest.writeBundle(fragmentArguments);
+ if (intent != null) {
+ dest.writeInt(1);
+ intent.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeBundle(extras);
+ }
+
+ public void readFromParcel(Parcel in) {
+ id = in.readLong();
+ title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ iconRes = in.readInt();
+ fragment = in.readString();
+ fragmentArguments = in.readBundle();
+ if (in.readInt() != 0) {
+ intent = Intent.CREATOR.createFromParcel(in);
+ }
+ extras = in.readBundle();
+ }
+
+ Header(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public static final Creator<Header> CREATOR = new Creator<Header>() {
+ public Header createFromParcel(Parcel source) {
+ return new Header(source);
+ }
+ public Header[] newArray(int size) {
+ return new Header[size];
+ }
+ };
}
@Override
@@ -314,40 +393,66 @@
String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
- if (initialFragment != null && mSinglePane) {
- // If we are just showing a fragment, we want to run in
- // new fragment mode, but don't need to compute and show
- // the headers.
- getListView().setVisibility(View.GONE);
- mPrefsContainer.setVisibility(View.VISIBLE);
- switchToHeader(initialFragment, initialArguments);
+ if (savedInstanceState != null) {
+ // We are restarting from a previous saved state; used that to
+ // initialize, instead of starting fresh.
+ ArrayList<Header> headers = savedInstanceState.getParcelableArrayList(HEADERS_TAG);
+ if (headers != null) {
+ mHeaders.addAll(headers);
+ int curHeader = savedInstanceState.getInt(CUR_HEADER_TAG,
+ (int)HEADER_ID_UNDEFINED);
+ if (curHeader >= 0 && curHeader < mHeaders.size()) {
+ setSelectedHeader(mHeaders.get(curHeader));
+ }
+ }
+ mSinglePane = savedInstanceState.getBoolean(SINGLE_PANE_TAG);
} else {
- // We need to try to build the headers.
- onBuildHeaders(mHeaders);
+ if (initialFragment != null && mSinglePane) {
+ // If we are just showing a fragment, we want to run in
+ // new fragment mode, but don't need to compute and show
+ // the headers.
+ switchToHeader(initialFragment, initialArguments);
- // If there are headers, then at this point we need to show
- // them and, depending on the screen, we may also show in-line
- // the currently selected preference fragment.
- if (mHeaders.size() > 0) {
- mAdapter = new HeaderAdapter(this, mHeaders);
- setListAdapter(mAdapter);
- if (!mSinglePane) {
- mPrefsContainer.setVisibility(View.VISIBLE);
- if (initialFragment == null) {
- Header h = onGetInitialHeader();
- initialFragment = h.fragment;
- initialArguments = h.fragmentArguments;
+ } else {
+ // We need to try to build the headers.
+ onBuildHeaders(mHeaders);
+
+ // If there are headers, then at this point we need to show
+ // them and, depending on the screen, we may also show in-line
+ // the currently selected preference fragment.
+ if (mHeaders.size() > 0) {
+ if (!mSinglePane) {
+ if (initialFragment == null) {
+ Header h = onGetInitialHeader();
+ switchToHeader(h);
+ } else {
+ switchToHeader(initialFragment, initialArguments);
+ }
}
- switchToHeader(initialFragment, initialArguments);
}
+ }
+ }
+ // The default configuration is to only show the list view. Adjust
+ // visibility for other configurations.
+ if (initialFragment != null && mSinglePane) {
+ // Single pane, showing just a prefs fragment.
+ getListView().setVisibility(View.GONE);
+ mPrefsContainer.setVisibility(View.VISIBLE);
+ } else if (mHeaders.size() > 0) {
+ mAdapter = new HeaderAdapter(this, mHeaders);
+ setListAdapter(mAdapter);
+ if (!mSinglePane) {
+ // Multi-pane.
+ getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
+ mPrefsContainer.setVisibility(View.VISIBLE);
+ }
+ } else {
// If there are no headers, we are in the old "just show a screen
// of preferences" mode.
- } else {
- mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
- mPreferenceManager.setOnPreferenceTreeClickListener(this);
- }
+ mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
+ mPreferenceManager.setOnPreferenceTreeClickListener(this);
}
getListView().setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
@@ -534,6 +639,9 @@
TypedArray sa = getResources().obtainAttributes(attrs,
com.android.internal.R.styleable.PreferenceHeader);
+ header.id = sa.getInt(
+ com.android.internal.R.styleable.PreferenceHeader_id,
+ (int)HEADER_ID_UNDEFINED);
header.title = sa.getText(
com.android.internal.R.styleable.PreferenceHeader_title);
header.summary = sa.getText(
@@ -621,6 +729,17 @@
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
+ if (mHeaders.size() > 0) {
+ outState.putParcelableArrayList(HEADERS_TAG, mHeaders);
+ if (mCurHeader != null) {
+ int index = mHeaders.indexOf(mCurHeader);
+ if (index >= 0) {
+ outState.putInt(CUR_HEADER_TAG, index);
+ }
+ }
+ }
+ outState.putBoolean(SINGLE_PANE_TAG, mSinglePane);
+
if (mPreferenceManager != null) {
final PreferenceScreen preferenceScreen = getPreferenceScreen();
if (preferenceScreen != null) {
@@ -680,7 +799,7 @@
/**
* Called when the user selects an item in the header list. The default
* implementation will call either {@link #startWithFragment(String, Bundle)}
- * or {@link #switchToHeader(String, Bundle)} as appropriate.
+ * or {@link #switchToHeader(Header)} as appropriate.
*
* @param header The header that was selected.
* @param position The header's position in the list.
@@ -690,7 +809,7 @@
if (mSinglePane) {
startWithFragment(header.fragment, header.fragmentArguments);
} else {
- switchToHeader(header.fragment, header.fragmentArguments);
+ switchToHeader(header);
}
} else if (header.intent != null) {
startActivity(header.intent);
@@ -715,6 +834,16 @@
startActivity(intent);
}
+ void setSelectedHeader(Header header) {
+ mCurHeader = header;
+ int index = mHeaders.indexOf(header);
+ if (index >= 0) {
+ getListView().setItemChecked(index, true);
+ } else {
+ getListView().clearChoices();
+ }
+ }
+
/**
* When in two-pane mode, switch the fragment pane to show the given
* preference fragment.
@@ -723,6 +852,8 @@
* @param args Optional arguments to supply to the fragment.
*/
public void switchToHeader(String fragmentName, Bundle args) {
+ setSelectedHeader(null);
+
getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
Fragment f = Fragment.instantiate(this, fragmentName, args);
@@ -731,6 +862,63 @@
}
/**
+ * When in two-pane mode, switch to the fragment pane to show the given
+ * preference fragment.
+ *
+ * @param header The new header to display.
+ */
+ public void switchToHeader(Header header) {
+ switchToHeader(header.fragment, header.fragmentArguments);
+ mCurHeader = header;
+ setSelectedHeader(header);
+ }
+
+ Header findBestMatchingHeader(Header cur, ArrayList<Header> from) {
+ ArrayList<Header> matches = new ArrayList<Header>();
+ for (int j=0; j<from.size(); j++) {
+ Header oh = from.get(j);
+ if (cur == oh || (cur.id != HEADER_ID_UNDEFINED && cur.id == oh.id)) {
+ // Must be this one.
+ matches.clear();
+ matches.add(oh);
+ break;
+ }
+ if (cur.fragment != null) {
+ if (cur.fragment.equals(oh.fragment)) {
+ matches.add(oh);
+ }
+ } else if (cur.intent != null) {
+ if (cur.intent.equals(oh.intent)) {
+ matches.add(oh);
+ }
+ } else if (cur.title != null) {
+ if (cur.title.equals(oh.title)) {
+ matches.add(oh);
+ }
+ }
+ }
+ final int NM = matches.size();
+ if (NM == 1) {
+ return matches.get(0);
+ } else if (NM > 1) {
+ for (int j=0; j<NM; j++) {
+ Header oh = matches.get(j);
+ if (cur.fragmentArguments != null &&
+ cur.fragmentArguments.equals(oh.fragmentArguments)) {
+ return oh;
+ }
+ if (cur.extras != null && cur.extras.equals(oh.extras)) {
+ return oh;
+ }
+ if (cur.title != null && cur.title.equals(oh.title)) {
+ return oh;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
* Start a new fragment.
*
* @param fragment The fragment to start
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index fde3769..7ba11bb 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -614,6 +614,17 @@
mWakeLock.release();
}
+ private void onRequestOobData(String objectPath , int nativeData) {
+ String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
+ if (address == null) return;
+
+ Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
+ intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
+ BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT);
+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ }
+
private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
if (!mBluetoothService.isEnabled()) return false;
@@ -672,6 +683,19 @@
return false;
}
+ private boolean onAgentOutOfBandDataAvailable(String objectPath) {
+ if (!mBluetoothService.isEnabled()) return false;
+
+ String address = mBluetoothService.getAddressFromObjectPath(objectPath);
+ if (address == null) return false;
+
+ if (mBluetoothService.getDeviceOutOfBandData(
+ mAdapter.getRemoteDevice(address)) != null) {
+ return true;
+ }
+ return false;
+ }
+
private boolean isOtherSinkInNonDisconnectingState(String address) {
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
Set<BluetoothDevice> devices = a2dp.getNonDisconnectedSinks();
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 7252736..71f2477 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -55,6 +55,7 @@
import android.os.SystemService;
import android.provider.Settings;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.app.IBatteryStats;
@@ -144,6 +145,7 @@
private BluetoothA2dpService mA2dpService;
private final HashMap<BluetoothDevice, Integer> mInputDevices;
private final HashMap<BluetoothDevice, Integer> mPanDevices;
+ private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
private static String mDockAddress;
private String mDockPin;
@@ -200,6 +202,7 @@
mDeviceProperties = new HashMap<String, Map<String,String>>();
mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
+ mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>();
mUuidIntentTracker = new ArrayList<String>();
mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
mServiceRecordToPid = new HashMap<Integer, Integer>();
@@ -1155,7 +1158,7 @@
mIsDiscovering = isDiscovering;
}
- public synchronized boolean createBond(String address) {
+ private boolean isBondingFeasible(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
if (!isEnabledInternal()) return false;
@@ -1185,8 +1188,13 @@
return false;
}
}
+ return true;
+ }
- if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) {
+ public synchronized boolean createBond(String address) {
+ if (!isBondingFeasible(address)) return false;
+
+ if (!createPairedDeviceNative(address, 60000 /*1 minute*/ )) {
return false;
}
@@ -1196,6 +1204,51 @@
return true;
}
+ public synchronized boolean createBondOutOfBand(String address, byte[] hash,
+ byte[] randomizer) {
+ if (!isBondingFeasible(address)) return false;
+
+ if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) {
+ return false;
+ }
+
+ setDeviceOutOfBandData(address, hash, randomizer);
+ mBondState.setPendingOutgoingBonding(address);
+ mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
+
+ return true;
+ }
+
+ public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash,
+ byte[] randomizer) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ if (!isEnabledInternal()) return false;
+
+ Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer);
+
+ if (DBG) {
+ log("Setting out of band data for:" + address + ":" +
+ Arrays.toString(hash) + ":" + Arrays.toString(randomizer));
+ }
+
+ mDeviceOobData.put(address, value);
+ return true;
+ }
+
+ Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) {
+ return mDeviceOobData.get(device.getAddress());
+ }
+
+
+ public synchronized byte[] readOutOfBandData() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
+ "Need BLUETOOTH permission");
+ if (!isEnabledInternal()) return null;
+
+ return readAdapterOutOfBandDataNative();
+ }
+
public synchronized boolean cancelBondProcess(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
@@ -1954,6 +2007,32 @@
return setPairingConfirmationNative(address, confirm, data.intValue());
}
+ public synchronized boolean setRemoteOutOfBandData(String address) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ if (!isEnabledInternal()) return false;
+ address = address.toUpperCase();
+ Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
+ if (data == null) {
+ Log.w(TAG, "setRemoteOobData(" + address + ") called but no native data available, " +
+ "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
+ " or by bluez.\n");
+ return false;
+ }
+
+ Pair<byte[], byte[]> val = mDeviceOobData.get(address);
+ byte[] hash, randomizer;
+ if (val == null) {
+ // TODO: check what should be passed in this case.
+ hash = new byte[16];
+ randomizer = new byte[16];
+ } else {
+ hash = val.first;
+ randomizer = val.second;
+ }
+ return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue());
+ }
+
public synchronized boolean cancelPairingUserInput(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
@@ -2487,6 +2566,9 @@
private native boolean stopDiscoveryNative();
private native boolean createPairedDeviceNative(String address, int timeout_ms);
+ private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
+ private native byte[] readAdapterOutOfBandDataNative();
+
private native boolean cancelDeviceCreationNative(String address);
private native boolean removeDeviceNative(String objectPath);
private native int getDeviceServiceChannelNative(String objectPath, String uuid,
@@ -2497,6 +2579,9 @@
private native boolean setPasskeyNative(String address, int passkey, int nativeData);
private native boolean setPairingConfirmationNative(String address, boolean confirm,
int nativeData);
+ private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
+ byte[] randomizer, int nativeData);
+
private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
int value);
private native boolean createDeviceNative(String address);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 3db05b6..012f3cc 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -877,7 +877,7 @@
mSplitMotionTargets = new SplitMotionTargets();
}
return dispatchSplitTouchEvent(ev);
- }
+ }
final int action = ev.getAction();
final float xf = ev.getX();
@@ -1222,13 +1222,7 @@
uniqueTargetCount--;
}
- final boolean childHandled = target.dispatchTouchEvent(targetEvent);
- handled |= childHandled;
- if (!childHandled) {
- // Child doesn't want these events anymore, but we're still dispatching
- // other split events to children.
- targets.removeView(target);
- }
+ handled |= target.dispatchTouchEvent(targetEvent);
} finally {
targetEvent.recycle();
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 1620778..bd87a0d 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -18,6 +18,7 @@
import com.android.internal.os.BatteryStatsImpl;
+import android.os.WorkSource;
import android.telephony.SignalStrength;
interface IBatteryStats {
@@ -33,6 +34,9 @@
SensorService.cpp */
void noteStopSensor(int uid, int sensor);
+ void noteStartWakelockFromSource(in WorkSource ws, int pid, String name, int type);
+ void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, int type);
+
void noteStartGps(int uid);
void noteStopGps(int uid);
void noteScreenOn();
@@ -57,6 +61,12 @@
void noteScanWifiLockReleased(int uid);
void noteWifiMulticastEnabled(int uid);
void noteWifiMulticastDisabled(int uid);
+ void noteFullWifiLockAcquiredFromSource(in WorkSource ws);
+ void noteFullWifiLockReleasedFromSource(in WorkSource ws);
+ void noteScanWifiLockAcquiredFromSource(in WorkSource ws);
+ void noteScanWifiLockReleasedFromSource(in WorkSource ws);
+ void noteWifiMulticastEnabledFromSource(in WorkSource ws);
+ void noteWifiMulticastDisabledFromSource(in WorkSource ws);
void setBatteryState(int status, int health, int plugType, int level, int temp, int volt);
long getAwakeTimeBattery();
long getAwakeTimePlugged();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b9f0c61..4943531 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -29,6 +29,7 @@
import android.os.Parcelable;
import android.os.Process;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
@@ -68,12 +69,12 @@
private static final int VERSION = 50;
// Maximum number of items we will record in the history.
- private static final int MAX_HISTORY_ITEMS = 1000;
+ private static final int MAX_HISTORY_ITEMS = 2000;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
// in to one common name.
- private static final int MAX_WAKELOCKS_PER_UID = 20;
+ private static final int MAX_WAKELOCKS_PER_UID = 30;
private static final String BATCHED_WAKELOCK_NAME = "*overflow*";
@@ -1173,7 +1174,7 @@
// If the current time is basically the same as the last time,
// just collapse into one record.
if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
- && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+100)) {
+ && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+500)) {
// If the current is the same as the one before, then we no
// longer need the entry.
if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
@@ -1189,6 +1190,10 @@
return;
}
+ if (mNumHistoryItems == MAX_HISTORY_ITEMS) {
+ addHistoryRecordLocked(curTime, HistoryItem.CMD_OVERFLOW);
+ }
+
if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
// Once we've reached the maximum number of items, we only
// record changes to the battery level.
@@ -1329,6 +1334,20 @@
}
}
+ public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name, int type) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteStartWakeLocked(ws.get(i), pid, name, type);
+ }
+ }
+
+ public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name, int type) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteStopWakeLocked(ws.get(i), pid, name, type);
+ }
+ }
+
public int startAddingCpuLocked() {
mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
@@ -1949,6 +1968,48 @@
getUidStatsLocked(uid).noteWifiMulticastDisabledLocked();
}
+ public void noteFullWifiLockAcquiredFromSourceLocked(WorkSource ws) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteFullWifiLockAcquiredLocked(ws.get(i));
+ }
+ }
+
+ public void noteFullWifiLockReleasedFromSourceLocked(WorkSource ws) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteFullWifiLockReleasedLocked(ws.get(i));
+ }
+ }
+
+ public void noteScanWifiLockAcquiredFromSourceLocked(WorkSource ws) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteScanWifiLockAcquiredLocked(ws.get(i));
+ }
+ }
+
+ public void noteScanWifiLockReleasedFromSourceLocked(WorkSource ws) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteScanWifiLockReleasedLocked(ws.get(i));
+ }
+ }
+
+ public void noteWifiMulticastEnabledFromSourceLocked(WorkSource ws) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteWifiMulticastEnabledLocked(ws.get(i));
+ }
+ }
+
+ public void noteWifiMulticastDisabledFromSourceLocked(WorkSource ws) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteWifiMulticastDisabledLocked(ws.get(i));
+ }
+ }
+
@Override public long getScreenOnTime(long batteryRealtime, int which) {
return mScreenOnTimer.getTotalTimeLocked(batteryRealtime, which);
}
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index 1307ec3..459458b 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -63,6 +63,8 @@
static jmethodID method_onRequestPasskeyConfirmation;
static jmethodID method_onRequestPairingConsent;
static jmethodID method_onDisplayPasskey;
+static jmethodID method_onRequestOobData;
+static jmethodID method_onAgentOutOfBandDataAvailable;
static jmethodID method_onAgentAuthorize;
static jmethodID method_onAgentCancel;
@@ -116,6 +118,8 @@
method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
"(Ljava/lang/String;Ljava/lang/String;)Z");
+ method_onAgentOutOfBandDataAvailable = env->GetMethodID(clazz, "onAgentOutOfBandDataAvailable",
+ "(Ljava/lang/String;)Z");
method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode",
"(Ljava/lang/String;I)V");
@@ -135,6 +139,8 @@
"(Ljava/lang/String;[Ljava/lang/String;)V");
method_onPanDeviceConnectionResult = env->GetMethodID(clazz, "onPanDeviceConnectionResult",
"(Ljava/lang/String;Z)V");
+ method_onRequestOobData = env->GetMethodID(clazz, "onRequestOobData",
+ "(Ljava/lang/String;I)V");
field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
#endif
@@ -345,6 +351,7 @@
{
DBusMessage *msg, *reply;
DBusError err;
+ bool oob = TRUE;
if (!dbus_connection_register_object_path(nat->conn, agent_path,
&agent_vtable, nat)) {
@@ -366,6 +373,7 @@
}
dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
DBUS_TYPE_STRING, &capabilities,
+ DBUS_TYPE_BOOLEAN, &oob,
DBUS_TYPE_INVALID);
dbus_error_init(&err);
@@ -1056,6 +1064,43 @@
}
goto success;
} else if (dbus_message_is_method_call(msg,
+ "org.bluez.Agent", "OutOfBandAvailable")) {
+ char *object_path;
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &object_path,
+ DBUS_TYPE_INVALID)) {
+ LOGE("%s: Invalid arguments for OutOfBandData available() method", __FUNCTION__);
+ goto failure;
+ }
+
+ LOGV("... object_path = %s", object_path);
+
+ bool available =
+ env->CallBooleanMethod(nat->me, method_onAgentOutOfBandDataAvailable,
+ env->NewStringUTF(object_path));
+
+
+ // reply
+ if (available) {
+ DBusMessage *reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ LOGE("%s: Cannot create message reply\n", __FUNCTION__);
+ goto failure;
+ }
+ dbus_connection_send(nat->conn, reply, NULL);
+ dbus_message_unref(reply);
+ } else {
+ DBusMessage *reply = dbus_message_new_error(msg,
+ "org.bluez.Error.DoesNotExist", "OutofBand data not available");
+ if (!reply) {
+ LOGE("%s: Cannot create message reply\n", __FUNCTION__);
+ goto failure;
+ }
+ dbus_connection_send(nat->conn, reply, NULL);
+ dbus_message_unref(reply);
+ }
+ goto success;
+ } else if (dbus_message_is_method_call(msg,
"org.bluez.Agent", "RequestPinCode")) {
char *object_path;
if (!dbus_message_get_args(msg, NULL,
@@ -1086,6 +1131,21 @@
int(msg));
goto success;
} else if (dbus_message_is_method_call(msg,
+ "org.bluez.Agent", "RequestOobData")) {
+ char *object_path;
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &object_path,
+ DBUS_TYPE_INVALID)) {
+ LOGE("%s: Invalid arguments for RequestOobData() method", __FUNCTION__);
+ goto failure;
+ }
+
+ dbus_message_ref(msg); // increment refcount because we pass to java
+ env->CallVoidMethod(nat->me, method_onRequestOobData,
+ env->NewStringUTF(object_path),
+ int(msg));
+ goto success;
+ } else if (dbus_message_is_method_call(msg,
"org.bluez.Agent", "DisplayPasskey")) {
char *object_path;
uint32_t passkey;
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index 74127cf..337dccc 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -296,6 +296,46 @@
#endif
}
+static jbyteArray readAdapterOutOfBandDataNative(JNIEnv *env, jobject object) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ native_data_t *nat = get_native_data(env, object);
+ DBusError err;
+ jbyte *hash, *randomizer;
+ jbyteArray byteArray = NULL;
+ int hash_len, r_len;
+ if (nat) {
+ DBusMessage *reply = dbus_func_args(env, nat->conn,
+ get_adapter_path(env, object),
+ DBUS_ADAPTER_IFACE, "ReadLocalOutOfBandData",
+ DBUS_TYPE_INVALID);
+ if (!reply) return NULL;
+
+ dbus_error_init(&err);
+ if (dbus_message_get_args(reply, &err,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash, &hash_len,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer, &r_len,
+ DBUS_TYPE_INVALID)) {
+ if (hash_len == 16 && r_len == 16) {
+ byteArray = env->NewByteArray(32);
+ if (byteArray) {
+ env->SetByteArrayRegion(byteArray, 0, 16, hash);
+ env->SetByteArrayRegion(byteArray, 16, 16, randomizer);
+ }
+ } else {
+ LOGE("readAdapterOutOfBandDataNative: Hash len = %d, R len = %d",
+ hash_len, r_len);
+ }
+ } else {
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ }
+ dbus_message_unref(reply);
+ return byteArray;
+ }
+#endif
+ return NULL;
+}
+
static jboolean createPairedDeviceNative(JNIEnv *env, jobject object,
jstring address, jint timeout_ms) {
LOGV(__FUNCTION__);
@@ -332,6 +372,41 @@
return JNI_FALSE;
}
+static jboolean createPairedDeviceOutOfBandNative(JNIEnv *env, jobject object,
+ jstring address, jint timeout_ms) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ native_data_t *nat = get_native_data(env, object);
+ jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+ struct event_loop_native_data_t *eventLoopNat =
+ get_EventLoop_native_data(env, eventLoop);
+
+ if (nat && eventLoopNat) {
+ const char *c_address = env->GetStringUTFChars(address, NULL);
+ LOGV("... address = %s", c_address);
+ char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
+ const char *capabilities = "DisplayYesNo";
+ const char *agent_path = "/android/bluetooth/remote_device_agent";
+
+ strlcpy(context_address, c_address, BTADDR_SIZE); // for callback
+ bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms,
+ onCreatePairedDeviceResult, // callback
+ context_address,
+ eventLoopNat,
+ get_adapter_path(env, object),
+ DBUS_ADAPTER_IFACE,
+ "CreatePairedDeviceOutOfBand",
+ DBUS_TYPE_STRING, &c_address,
+ DBUS_TYPE_OBJECT_PATH, &agent_path,
+ DBUS_TYPE_STRING, &capabilities,
+ DBUS_TYPE_INVALID);
+ env->ReleaseStringUTFChars(address, c_address);
+ return ret ? JNI_TRUE : JNI_FALSE;
+ }
+#endif
+ return JNI_FALSE;
+}
+
static jint getDeviceServiceChannelNative(JNIEnv *env, jobject object,
jstring path,
jstring pattern, jint attr_id) {
@@ -498,6 +573,40 @@
return JNI_FALSE;
}
+static jboolean setRemoteOutOfBandDataNative(JNIEnv *env, jobject object, jstring address,
+ jbyteArray hash, jbyteArray randomizer, int nativeData) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ DBusMessage *msg = (DBusMessage *)nativeData;
+ DBusMessage *reply = dbus_message_new_method_return(msg);
+ jbyte *h_ptr = env->GetByteArrayElements(hash, NULL);
+ jbyte *r_ptr = env->GetByteArrayElements(randomizer, NULL);
+ if (!reply) {
+ LOGE("%s: Cannot create message reply to return remote OOB data to "
+ "D-Bus\n", __FUNCTION__);
+ dbus_message_unref(msg);
+ return JNI_FALSE;
+ }
+
+ dbus_message_append_args(reply,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &h_ptr, 16,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &r_ptr, 16,
+ DBUS_TYPE_INVALID);
+
+ env->ReleaseByteArrayElements(hash, h_ptr, 0);
+ env->ReleaseByteArrayElements(randomizer, r_ptr, 0);
+
+ dbus_connection_send(nat->conn, reply, NULL);
+ dbus_message_unref(msg);
+ dbus_message_unref(reply);
+ return JNI_TRUE;
+ }
+#endif
+ return JNI_FALSE;
+}
+
static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
jstring pin, int nativeData) {
#ifdef HAVE_BLUETOOTH
@@ -1069,7 +1178,10 @@
{"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
{"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative},
+ {"readAdapterOutOfBandDataNative", "()[B", (void *)readAdapterOutOfBandDataNative},
{"createPairedDeviceNative", "(Ljava/lang/String;I)Z", (void *)createPairedDeviceNative},
+ {"createPairedDeviceOutOfBandNative", "(Ljava/lang/String;I)Z",
+ (void *)createPairedDeviceOutOfBandNative},
{"cancelDeviceCreationNative", "(Ljava/lang/String;)Z", (void *)cancelDeviceCreationNative},
{"removeDeviceNative", "(Ljava/lang/String;)Z", (void *)removeDeviceNative},
{"getDeviceServiceChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)I",
@@ -1078,6 +1190,7 @@
{"setPairingConfirmationNative", "(Ljava/lang/String;ZI)Z",
(void *)setPairingConfirmationNative},
{"setPasskeyNative", "(Ljava/lang/String;II)Z", (void *)setPasskeyNative},
+ {"setRemoteOutOfBandDataNative", "(Ljava/lang/String;[B[BI)Z", (void *)setRemoteOutOfBandDataNative},
{"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
{"cancelPairingUserInputNative", "(Ljava/lang/String;I)Z",
(void *)cancelPairingUserInputNative},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 89298ea..4d02b60 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -939,7 +939,7 @@
<permission android:name="android.permission.UPDATE_DEVICE_STATS"
android:label="@string/permlab_batteryStats"
android:description="@string/permdesc_batteryStats"
- android:protectionLevel="signature" />
+ android:protectionLevel="signatureOrSystem" />
<!-- Allows an application to open windows that are for use by parts
of the system user interface. Not for use by third party apps. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9363db5..cda997a 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3530,6 +3530,8 @@
<!-- Attribute for a header describing the item shown in the top-level list
from which the selects the set of preference to dig in to. -->
<declare-styleable name="PreferenceHeader">
+ <!-- Identifier value for the header. -->
+ <attr name="id" />
<!-- The title of the item that is shown to the user. -->
<attr name="title" />
<!-- The summary for the item. -->
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7cc4880..b1f5f6b 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -80,6 +80,9 @@
{ SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }
};
+// This array contains the swapped version of each SkXfermode. For instance
+// this array's SrcOver blending mode is actually DstOver. You can refer to
+// createLayer() for more information on the purpose of this array.
static const Blender gBlendsSwap[] = {
{ SkXfermode::kClear_Mode, GL_ZERO, GL_ZERO },
{ SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE },
@@ -280,6 +283,54 @@
}
}
+/**
+ * Layers are viewed by Skia are slightly different than layers in image editing
+ * programs (for instance.) When a layer is created, previously created layers
+ * and the frame buffer still receive every drawing command. For instance, if a
+ * layer is created and a shape intersecting the bounds of the layers and the
+ * framebuffer is draw, the shape will be drawn on both (unless the layer was
+ * created with the SkCanvas::kClipToLayer_SaveFlag flag.)
+ *
+ * A way to implement layers is to create an FBO for each layer, backed by an RGBA
+ * texture. Unfortunately, this is inefficient as it requires every primitive to
+ * be drawn n + 1 times, where n is the number of active layers. In practice this
+ * means, for every primitive:
+ * - Switch active frame buffer
+ * - Change viewport, clip and projection matrix
+ * - Issue the drawing
+ *
+ * Switching rendering target n + 1 times per drawn primitive is extremely costly.
+ * To avoid this, layers are implemented in a different way here.
+ *
+ * This implementation relies on the frame buffer being at least RGBA 8888. When
+ * a layer is created, only a texture is created, not an FBO. The content of the
+ * frame buffer contained within the layer's bounds is copied into this texture
+ * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame
+ * buffer and drawing continues as normal. This technique therefore treats the
+ * frame buffer as a scratch buffer for the layers.
+ *
+ * To compose the layers back onto the frame buffer, each layer texture
+ * (containing the original frame buffer data) is drawn as a simple quad over
+ * the frame buffer. The trick is that the quad is set as the composition
+ * destination in the blending equation, and the frame buffer becomes the source
+ * of the composition.
+ *
+ * Drawing layers with an alpha value requires an extra step before composition.
+ * An empty quad is drawn over the layer's region in the frame buffer. This quad
+ * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the
+ * quad is used to multiply the colors in the frame buffer. This is achieved by
+ * changing the GL blend functions for the GL_FUNC_ADD blend equation to
+ * GL_ZERO, GL_SRC_ALPHA.
+ *
+ * Because glCopyTexImage2D() can be slow, an alternative implementation might
+ * be use to draw a single clipped layer. The implementation described above
+ * is correct in every case.
+ *
+ * (1) The frame buffer is actually not cleared right away. To allow the GPU
+ * to potentially optimize series of calls to glCopyTexImage2D, the frame
+ * buffer is left untouched until the first drawing operation. Only when
+ * something actually gets drawn are the layers regions cleared.
+ */
bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) {
LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top);
@@ -289,8 +340,11 @@
Rect bounds(left, top, right, bottom);
mSnapshot->transform->mapRect(bounds);
- LayerSize size(bounds.getWidth(), bounds.getHeight());
+ // Layers only make sense if they are in the framebuffer's bounds
+ bounds.intersect(*mSnapshot->clipRect);
+ if (bounds.isEmpty()) return false;
+ LayerSize size(bounds.getWidth(), bounds.getHeight());
Layer* layer = mCaches.layerCache.get(size);
if (!layer) {
return false;
@@ -319,6 +373,9 @@
return true;
}
+/**
+ * Read the documentation of createLayer() before doing anything in this method.
+ */
void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
if (!current->layer) {
LOGE("Attempting to compose a layer that does not exist");
@@ -333,16 +390,8 @@
const Rect& rect = layer->layer;
if (layer->alpha < 255) {
- glEnable(GL_BLEND);
- glBlendFuncSeparate(GL_ZERO, GL_SRC_ALPHA, GL_DST_ALPHA, GL_ZERO);
-
drawColorRect(rect.left, rect.top, rect.right, rect.bottom,
- layer->alpha << 24, SkXfermode::kSrcOver_Mode, true, true);
-
- glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode);
- if (!mCaches.blend) {
- glDisable(GL_BLEND);
- }
+ layer->alpha << 24, SkXfermode::kDstIn_Mode, true);
}
// Layers are already drawn with a top-left origin, don't flip the texture
@@ -525,6 +574,7 @@
Patch* mesh = mCaches.patchCache.get(patch);
mesh->updateVertices(bitmap, left, top, right, bottom,
&patch->xDivs[0], &patch->yDivs[0], patch->numXDivs, patch->numYDivs);
+ mesh->dump();
// Specify right and bottom as +1.0f from left/top to prevent scaling since the
// patch mesh already defines the final size
@@ -841,7 +891,7 @@
}
void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
- int color, SkXfermode::Mode mode, bool ignoreTransform, bool ignoreBlending) {
+ int color, SkXfermode::Mode mode, bool ignoreTransform) {
clearLayerRegions();
// If a shader is set, preserve only the alpha
@@ -867,10 +917,8 @@
mColorFilter->describe(description, mExtensions);
}
- if (!ignoreBlending) {
- // Setup the blending mode
- chooseBlending(alpha < 255 || (mShader && mShader->blend()), mode, description);
- }
+ // Setup the blending mode
+ chooseBlending(alpha < 255 || (mShader && mShader->blend()), mode, description);
// Build and use the appropriate shader
useProgram(mCaches.programCache.get(description));
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 12ec276..3126754 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -176,8 +176,7 @@
* @paran ignoreBlending True if the blending is set by the caller
*/
void drawColorRect(float left, float top, float right, float bottom,
- int color, SkXfermode::Mode mode, bool ignoreTransform = false,
- bool ignoreBlending = false);
+ int color, SkXfermode::Mode mode, bool ignoreTransform = false);
/**
* Draws a textured rectangle with the specified texture. The specified coordinates
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index ebedf53..062c986 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -116,6 +116,11 @@
bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op) {
bool clipped = false;
+ // NOTE: The unimplemented operations require support for regions
+ // Supporting regions would require using a stencil buffer instead
+ // of the scissor. The stencil buffer itself is not too expensive
+ // (memory cost excluded) but on fillrate limited devices, managing
+ // the stencil might have a negative impact on the framerate.
switch (op) {
case SkRegion::kDifference_Op:
break;
diff --git a/location/java/android/location/ILocationProvider.aidl b/location/java/android/location/ILocationProvider.aidl
index 2b9782a..ecf6789 100644
--- a/location/java/android/location/ILocationProvider.aidl
+++ b/location/java/android/location/ILocationProvider.aidl
@@ -20,6 +20,7 @@
import android.location.Location;
import android.net.NetworkInfo;
import android.os.Bundle;
+import android.os.WorkSource;
/**
* Binder interface for services that implement location providers.
@@ -43,7 +44,7 @@
long getStatusUpdateTime();
String getInternalState();
void enableLocationTracking(boolean enable);
- void setMinTime(long minTime);
+ void setMinTime(long minTime, in WorkSource ws);
void updateNetworkState(int state, in NetworkInfo info);
void updateLocation(in Location location);
boolean sendExtraCommand(String command, inout Bundle extras);
diff --git a/location/java/android/location/provider/LocationProvider.java b/location/java/android/location/provider/LocationProvider.java
index cf939de..95b4425 100644
--- a/location/java/android/location/provider/LocationProvider.java
+++ b/location/java/android/location/provider/LocationProvider.java
@@ -26,6 +26,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.WorkSource;
import android.util.Log;
/**
@@ -106,8 +107,8 @@
LocationProvider.this.onEnableLocationTracking(enable);
}
- public void setMinTime(long minTime) {
- LocationProvider.this.onSetMinTime(minTime);
+ public void setMinTime(long minTime, WorkSource ws) {
+ LocationProvider.this.onSetMinTime(minTime, ws);
}
public void updateNetworkState(int state, NetworkInfo info) {
@@ -123,11 +124,11 @@
}
public void addListener(int uid) {
- LocationProvider.this.onAddListener(uid);
+ LocationProvider.this.onAddListener(uid, new WorkSource(uid));
}
public void removeListener(int uid) {
- LocationProvider.this.onRemoveListener(uid);
+ LocationProvider.this.onRemoveListener(uid, new WorkSource(uid));
}
};
@@ -303,8 +304,9 @@
* the frequency of updates to match the requested frequency.
*
* @param minTime the smallest minTime value over all listeners for this provider.
+ * @param ws the source this work is coming from.
*/
- public abstract void onSetMinTime(long minTime);
+ public abstract void onSetMinTime(long minTime, WorkSource ws);
/**
* Updates the network state for the given provider. This function must
@@ -340,14 +342,16 @@
* Notifies the location provider when a new client is listening for locations.
*
* @param uid user ID of the new client.
+ * @param ws a WorkSource representation of the client.
*/
- public abstract void onAddListener(int uid);
+ public abstract void onAddListener(int uid, WorkSource ws);
/**
* Notifies the location provider when a client is no longer listening for locations.
*
* @param uid user ID of the client no longer listening.
+ * @param ws a WorkSource representation of the client.
*/
- public abstract void onRemoveListener(int uid);
+ public abstract void onRemoveListener(int uid, WorkSource ws);
}
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 9f2a49c..b2a0a46 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -110,15 +110,28 @@
private final static String TAG = "MediaScanner";
- private static final String[] PRESCAN_PROJECTION = new String[] {
+ private static final String[] FILES_PRESCAN_PROJECTION = new String[] {
Files.FileColumns._ID, // 0
Files.FileColumns.DATA, // 1
- Files.FileColumns.DATE_MODIFIED, // 2
+ Files.FileColumns.FORMAT, // 2
+ Files.FileColumns.DATE_MODIFIED, // 3
};
- private static final int PRESCAN_ID_COLUMN_INDEX = 0;
- private static final int PRESCAN_PATH_COLUMN_INDEX = 1;
- private static final int PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 2;
+ private static final int FILES_PRESCAN_ID_COLUMN_INDEX = 0;
+ private static final int FILES_PRESCAN_PATH_COLUMN_INDEX = 1;
+ private static final int FILES_PRESCAN_FORMAT_COLUMN_INDEX = 2;
+ private static final int FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 3;
+
+ private static final String[] MEDIA_PRESCAN_PROJECTION = new String[] {
+ MediaStore.MediaColumns._ID, // 0
+ MediaStore.MediaColumns.DATA, // 1
+ MediaStore.MediaColumns.DATE_MODIFIED, // 2
+ };
+
+ private static final int MEDIA_PRESCAN_ID_COLUMN_INDEX = 0;
+ private static final int MEDIA_PRESCAN_PATH_COLUMN_INDEX = 1;
+ private static final int MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 2;
+
private static final String[] PLAYLIST_MEMBERS_PROJECTION = new String[] {
Audio.Playlists.Members.PLAYLIST_ID, // 0
@@ -316,14 +329,16 @@
long mRowId;
String mPath;
long mLastModified;
+ int mFormat;
boolean mSeenInFileSystem;
boolean mLastModifiedChanged;
- FileCacheEntry(Uri tableUri, long rowId, String path, long lastModified) {
+ FileCacheEntry(Uri tableUri, long rowId, String path, long lastModified, int format) {
mTableUri = tableUri;
mRowId = rowId;
mPath = path;
mLastModified = lastModified;
+ mFormat = format;
mSeenInFileSystem = false;
mLastModifiedChanged = false;
}
@@ -444,7 +459,7 @@
} else {
tableUri = mFilesUri;
}
- entry = new FileCacheEntry(tableUri, 0, path, 0);
+ entry = new FileCacheEntry(tableUri, 0, path, 0, 0);
mFileCache.put(key, entry);
}
entry.mSeenInFileSystem = true;
@@ -884,13 +899,15 @@
if (prescanFiles) {
// First read existing files from the files table
- c = mMediaProvider.query(mFilesUri, PRESCAN_PROJECTION, where, selectionArgs, null);
+ c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
+ where, selectionArgs, null);
if (c != null) {
while (c.moveToNext()) {
- long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
- String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
- long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+ long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
+ String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
+ int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
+ long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
// Only consider entries with absolute path names.
// This allows storing URIs in the database without the
@@ -902,7 +919,7 @@
}
FileCacheEntry entry = new FileCacheEntry(mFilesUri, rowId, path,
- lastModified);
+ lastModified, format);
mFileCache.put(key, entry);
}
}
@@ -912,12 +929,13 @@
}
// Read existing files from the audio table and update FileCacheEntry
- c = mMediaProvider.query(mAudioUri, PRESCAN_PROJECTION, where, selectionArgs, null);
+ c = mMediaProvider.query(mAudioUri, MEDIA_PRESCAN_PROJECTION,
+ where, selectionArgs, null);
if (c != null) {
while (c.moveToNext()) {
- long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
- String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
- long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+ long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
+ String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
+ long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
// Only consider entries with absolute path names.
// This allows storing URIs in the database without the
@@ -930,7 +948,7 @@
FileCacheEntry entry = mFileCache.get(path);
if (entry == null) {
mFileCache.put(key, new FileCacheEntry(mAudioUri, rowId, path,
- lastModified));
+ lastModified, 0));
} else {
// update the entry
entry.mTableUri = mAudioUri;
@@ -943,12 +961,13 @@
}
// Read existing files from the video table and update FileCacheEntry
- c = mMediaProvider.query(mVideoUri, PRESCAN_PROJECTION, where, selectionArgs, null);
+ c = mMediaProvider.query(mVideoUri, MEDIA_PRESCAN_PROJECTION,
+ where, selectionArgs, null);
if (c != null) {
while (c.moveToNext()) {
- long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
- String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
- long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+ long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
+ String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
+ long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
// Only consider entries with absolute path names.
// This allows storing URIs in the database without the
@@ -961,7 +980,7 @@
FileCacheEntry entry = mFileCache.get(path);
if (entry == null) {
mFileCache.put(key, new FileCacheEntry(mVideoUri, rowId, path,
- lastModified));
+ lastModified, 0));
} else {
// update the entry
entry.mTableUri = mVideoUri;
@@ -974,12 +993,13 @@
}
// Read existing files from the video table and update FileCacheEntry
- c = mMediaProvider.query(mImagesUri, PRESCAN_PROJECTION, where, selectionArgs, null);
+ c = mMediaProvider.query(mImagesUri, MEDIA_PRESCAN_PROJECTION,
+ where, selectionArgs, null);
if (c != null) {
while (c.moveToNext()) {
- long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
- String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
- long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+ long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
+ String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
+ long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
// Only consider entries with absolute path names.
// This allows storing URIs in the database without the
@@ -992,7 +1012,7 @@
FileCacheEntry entry = mFileCache.get(path);
if (entry == null) {
mFileCache.put(key, new FileCacheEntry(mImagesUri, rowId, path,
- lastModified));
+ lastModified, 0));
} else {
// update the entry
entry.mTableUri = mImagesUri;
@@ -1006,13 +1026,13 @@
if (mProcessPlaylists) {
// Read existing files from the playlists table and update FileCacheEntry
- c = mMediaProvider.query(mPlaylistsUri, PRESCAN_PROJECTION, where,
- selectionArgs, null);
+ c = mMediaProvider.query(mPlaylistsUri, MEDIA_PRESCAN_PROJECTION,
+ where, selectionArgs, null);
if (c != null) {
while (c.moveToNext()) {
- long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
- String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
- long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+ long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
+ String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
+ long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
// Only consider entries with absolute path names.
// This allows storing URIs in the database without the
@@ -1025,7 +1045,7 @@
FileCacheEntry entry = mFileCache.get(path);
if (entry == null) {
mFileCache.put(key, new FileCacheEntry(mPlaylistsUri, rowId, path,
- lastModified));
+ lastModified, 0));
} else {
// update the entry
entry.mTableUri = mPlaylistsUri;
@@ -1109,12 +1129,14 @@
// remove database entries for files that no longer exist.
boolean fileMissing = false;
- if (!entry.mSeenInFileSystem) {
- if (inScanDirectory(path, directories)) {
+ if (!entry.mSeenInFileSystem && !MtpConstants.isAbstractObject(entry.mFormat)) {
+ if (entry.mFormat != MtpConstants.FORMAT_ASSOCIATION &&
+ inScanDirectory(path, directories)) {
// we didn't see this file in the scan directory.
fileMissing = true;
} else {
- // the file is outside of our scan directory,
+ // the file actually a directory or other abstract object
+ // or is outside of our scan directory,
// so we need to check for file existence here.
File testFile = new File(path);
if (!testFile.exists()) {
@@ -1134,9 +1156,11 @@
ContentValues values = new ContentValues();
values.put(MediaStore.Audio.Playlists.DATA, "");
values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, 0);
- mMediaProvider.update(ContentUris.withAppendedId(mPlaylistsUri, entry.mRowId), values, null, null);
+ mMediaProvider.update(ContentUris.withAppendedId(mPlaylistsUri, entry.mRowId),
+ values, null, null);
} else {
- mMediaProvider.delete(ContentUris.withAppendedId(mFilesUri, entry.mRowId), null, null);
+ mMediaProvider.delete(ContentUris.withAppendedId(mFilesUri, entry.mRowId),
+ null, null);
iterator.remove();
}
}
diff --git a/media/java/android/media/MtpConstants.java b/media/java/android/media/MtpConstants.java
index 153f64f..a7d33ce 100644
--- a/media/java/android/media/MtpConstants.java
+++ b/media/java/android/media/MtpConstants.java
@@ -138,6 +138,28 @@
public static final int FORMAT_ABSTRACT_CONTACT = 0xBB81;
public static final int FORMAT_VCARD_2 = 0xBB82;
+ public static boolean isAbstractObject(int format) {
+ switch (format) {
+ case FORMAT_ABSTRACT_MULTIMEDIA_ALBUM:
+ case FORMAT_ABSTRACT_IMAGE_ALBUM:
+ case FORMAT_ABSTRACT_AUDIO_ALBUM:
+ case FORMAT_ABSTRACT_VIDEO_ALBUM:
+ case FORMAT_ABSTRACT_AV_PLAYLIST:
+ case FORMAT_ABSTRACT_CONTACT_GROUP:
+ case FORMAT_ABSTRACT_MESSAGE_FOLDER:
+ case FORMAT_ABSTRACT_CHAPTERED_PRODUCTION:
+ case FORMAT_ABSTRACT_AUDIO_PLAYLIST:
+ case FORMAT_ABSTRACT_VIDEO_PLAYLIST:
+ case FORMAT_ABSTRACT_MEDIACAST:
+ case FORMAT_ABSTRACT_DOCUMENT:
+ case FORMAT_ABSTRACT_MESSSAGE:
+ case FORMAT_ABSTRACT_CONTACT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
// MTP object properties
public static final int PROPERTY_STORAGE_ID = 0xDC01;
public static final int PROPERTY_OBJECT_FORMAT = 0xDC02;
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index 6b0b761..b64299a 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -478,12 +478,26 @@
}
}
+ private int deleteRecursive(int handle) throws RemoteException {
+ int[] children = getObjectList(0 /* storageID */, 0 /* format */, handle);
+ Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
+ // delete parent first, to avoid potential infinite recursion
+ int count = mMediaProvider.delete(uri, null, null);
+ if (count == 1) {
+ if (children != null) {
+ for (int i = 0; i < children.length; i++) {
+ count += deleteRecursive(children[i]);
+ }
+ }
+ }
+ return count;
+ }
+
private int deleteFile(int handle) {
Log.d(TAG, "deleteFile: " + handle);
mDatabaseModified = true;
- Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
try {
- if (mMediaProvider.delete(uri, null, null) == 1) {
+ if (deleteRecursive(handle) > 0) {
return MtpConstants.RESPONSE_OK;
} else {
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index e86ed99..90756d0 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -65,6 +65,7 @@
int LvmInitFlag = LVM_FALSE;
int LvmSessionsActive = 0;
SessionContext GlobalSessionMemory[LVM_MAX_SESSIONS];
+
int SessionIndex[LVM_MAX_SESSIONS];
// NXP SW BassBoost UUID
@@ -199,11 +200,6 @@
return -EINVAL;
}
- if(sessionId < 0){
- LOGV("\tLVM_ERROR : EffectCreate sessionId is less than 0");
- return -EINVAL;
- }
-
if(LvmInitFlag == LVM_FALSE){
LvmInitFlag = LVM_TRUE;
LOGV("\tEffectCreate - Initializing all global memory");
@@ -214,7 +210,7 @@
// Find next available sessionNo
for(i=0; i<LVM_MAX_SESSIONS; i++){
- if((SessionIndex[i] == -1)||(SessionIndex[i] == sessionId)){
+ if((SessionIndex[i] == LVM_UNUSED_SESSION)||(SessionIndex[i] == sessionId)){
sessionNo = i;
SessionIndex[i] = sessionId;
LOGV("\tEffectCreate: Allocating SessionNo %d for SessionId %d\n", sessionNo,sessionId);
@@ -398,7 +394,7 @@
// Clear the SessionIndex
for(int i=0; i<LVM_MAX_SESSIONS; i++){
if(SessionIndex[i] == pContext->pBundledContext->SessionId){
- SessionIndex[i] = -1;
+ SessionIndex[i] = LVM_UNUSED_SESSION;
LOGV("\tEffectRelease: Clearing SessionIndex SessionNo %d for SessionId %d\n",
i, pContext->pBundledContext->SessionId);
break;
@@ -432,7 +428,7 @@
GlobalSessionMemory[i].bVirtualizerInstantiated = LVM_FALSE;
GlobalSessionMemory[i].pBundledContext = LVM_NULL;
- SessionIndex[i] = -1;
+ SessionIndex[i] = LVM_UNUSED_SESSION;
}
return;
}
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
index 35e1114..91963af 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
@@ -21,6 +21,7 @@
#include <media/EffectBassBoostApi.h>
#include <media/EffectVirtualizerApi.h>
#include <LVM.h>
+#include <limits.h>
#if __cplusplus
extern "C" {
@@ -30,6 +31,7 @@
#define MAX_NUM_BANDS 5
#define MAX_CALL_SIZE 256
#define LVM_MAX_SESSIONS 32
+#define LVM_UNUSED_SESSION INT_MAX
#define BASS_BOOST_CUP_LOAD_ARM9E 150 // Expressed in 0.1 MIPS
#define VIRTUALIZER_CUP_LOAD_ARM9E 120 // Expressed in 0.1 MIPS
#define EQUALIZER_CUP_LOAD_ARM9E 220 // Expressed in 0.1 MIPS
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 60f4288..26c5aca 100755
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -59,17 +59,17 @@
// REVERB_PRESET_NONE: values are unused
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
// REVERB_PRESET_SMALLROOM
- {-1000, -600, 1100, 830, -400, 5, 500, 10, 1000, 1000},
+ {-400, -600, 1100, 830, -400, 5, 500, 10, 1000, 1000},
// REVERB_PRESET_MEDIUMROOM
- {-1000, -600, 1300, 830, -1000, 20, -200, 20, 1000, 1000},
+ {-400, -600, 1300, 830, -1000, 20, -200, 20, 1000, 1000},
// REVERB_PRESET_LARGEROOM
- {-1000, -600, 1500, 830, -1600, 5, -1000, 40, 1000, 1000},
+ {-400, -600, 1500, 830, -1600, 5, -1000, 40, 1000, 1000},
// REVERB_PRESET_MEDIUMHALL
- {-1000, -600, 1800, 700, -1300, 15, -800, 30, 1000, 1000},
+ {-400, -600, 1800, 700, -1300, 15, -800, 30, 1000, 1000},
// REVERB_PRESET_LARGEHALL
- {-1000, -600, 1800, 700, -2000, 30, -1400, 60, 1000, 1000},
+ {-400, -600, 1800, 700, -2000, 30, -1400, 60, 1000, 1000},
// REVERB_PRESET_PLATE
- {-1000, -200, 1300, 900, 0, 2, 0, 10, 1000, 750},
+ {-400, -200, 1300, 900, 0, 2, 0, 10, 1000, 750},
};
@@ -90,7 +90,7 @@
{0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, {0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e}},
{0xc7a511a0, 0xa3bb, 0x11df, 0x860e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
EFFECT_API_VERSION,
- EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+ EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST | EFFECT_FLAG_VOLUME_CTRL,
LVREV_CUP_LOAD_ARM9E,
LVREV_MEM_USAGE,
"Insert Environmental Reverb",
@@ -114,7 +114,7 @@
{0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
{0x172cdf00, 0xa3bc, 0x11df, 0xa72f, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
EFFECT_API_VERSION,
- EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+ EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST | EFFECT_FLAG_VOLUME_CTRL,
LVREV_CUP_LOAD_ARM9E,
LVREV_MEM_USAGE,
"Insert Preset Reverb",
@@ -153,10 +153,25 @@
uint16_t curPreset;
uint16_t nextPreset;
int SamplesToExitCount;
+ LVM_INT16 leftVolume;
+ LVM_INT16 rightVolume;
+ LVM_INT16 prevLeftVolume;
+ LVM_INT16 prevRightVolume;
+ int volumeMode;
+};
+
+enum {
+ REVERB_VOLUME_OFF,
+ REVERB_VOLUME_FLAT,
+ REVERB_VOLUME_RAMP,
};
#define REVERB_DEFAULT_PRESET REVERB_PRESET_MEDIUMROOM
+
+#define REVERB_SEND_LEVEL (0x0C00) // 0.75 in 4.12 format
+#define REVERB_UNIT_VOLUME (0x1000) // 1.0 in 4.12 format
+
//--- local function prototypes
int Reverb_init (ReverbContext *pContext);
void Reverb_free (ReverbContext *pContext);
@@ -426,9 +441,20 @@
if (pContext->preset && pContext->nextPreset != pContext->curPreset) {
Reverb_LoadPreset(pContext);
}
+
+
+
// Convert to Input 32 bits
- for(int i=0; i<frameCount*samplesPerFrame; i++){
- pContext->InFrames32[i] = (LVM_INT32)pIn[i]<<8;
+ if (pContext->auxiliary) {
+ for(int i=0; i<frameCount*samplesPerFrame; i++){
+ pContext->InFrames32[i] = (LVM_INT32)pIn[i]<<8;
+ }
+ } else {
+ // insert reverb input is always stereo
+ for (int i = 0; i < frameCount; i++) {
+ pContext->InFrames32[2*i] = (pIn[2*i] * REVERB_SEND_LEVEL) >> 4; // <<8 + >>12
+ pContext->InFrames32[2*i+1] = (pIn[2*i+1] * REVERB_SEND_LEVEL) >> 4; // <<8 + >>12
+ }
}
if (pContext->preset && pContext->curPreset == REVERB_PRESET_NONE) {
@@ -458,6 +484,42 @@
for (int i=0; i < frameCount*2; i++) { //always stereo here
OutFrames16[i] = clamp16((pContext->OutFrames32[i]>>8) + (LVM_INT32)pIn[i]);
}
+
+ // apply volume with ramp if needed
+ if ((pContext->leftVolume != pContext->prevLeftVolume ||
+ pContext->rightVolume != pContext->prevRightVolume) &&
+ pContext->volumeMode == REVERB_VOLUME_RAMP) {
+ LVM_INT32 vl = (LVM_INT32)pContext->prevLeftVolume << 16;
+ LVM_INT32 incl = (((LVM_INT32)pContext->leftVolume << 16) - vl) / frameCount;
+ LVM_INT32 vr = (LVM_INT32)pContext->prevRightVolume << 16;
+ LVM_INT32 incr = (((LVM_INT32)pContext->rightVolume << 16) - vr) / frameCount;
+
+ for (int i = 0; i < frameCount; i++) {
+ OutFrames16[2*i] =
+ clamp16((LVM_INT32)((vl >> 16) * OutFrames16[2*i]) >> 12);
+ OutFrames16[2*i+1] =
+ clamp16((LVM_INT32)((vr >> 16) * OutFrames16[2*i+1]) >> 12);
+
+ vl += incl;
+ vr += incr;
+ }
+
+ pContext->prevLeftVolume = pContext->leftVolume;
+ pContext->prevRightVolume = pContext->rightVolume;
+ } else if (pContext->volumeMode != REVERB_VOLUME_OFF) {
+ if (pContext->leftVolume != REVERB_UNIT_VOLUME ||
+ pContext->rightVolume != REVERB_UNIT_VOLUME) {
+ for (int i = 0; i < frameCount; i++) {
+ OutFrames16[2*i] =
+ clamp16((LVM_INT32)(pContext->leftVolume * OutFrames16[2*i]) >> 12);
+ OutFrames16[2*i+1] =
+ clamp16((LVM_INT32)(pContext->rightVolume * OutFrames16[2*i+1]) >> 12);
+ }
+ }
+ pContext->prevLeftVolume = pContext->leftVolume;
+ pContext->prevRightVolume = pContext->rightVolume;
+ pContext->volumeMode = REVERB_VOLUME_RAMP;
+ }
}
#ifdef LVM_PCM
@@ -658,6 +720,12 @@
pContext->config.outputCfg.bufferProvider.cookie = NULL;
pContext->config.outputCfg.mask = EFFECT_CONFIG_ALL;
+ pContext->leftVolume = REVERB_UNIT_VOLUME;
+ pContext->rightVolume = REVERB_UNIT_VOLUME;
+ pContext->prevLeftVolume = REVERB_UNIT_VOLUME;
+ pContext->prevRightVolume = REVERB_UNIT_VOLUME;
+ pContext->volumeMode = REVERB_VOLUME_FLAT;
+
LVREV_ReturnStatus_en LvmStatus=LVREV_SUCCESS; /* Function call status */
LVREV_ControlParams_st params; /* Control Parameters */
LVREV_InstanceParams_st InstParams; /* Instance parameters */
@@ -1781,15 +1849,6 @@
LOGV("\tLVM_ERROR : Reverb_process() ERROR NULL INPUT POINTER OR FRAME COUNT IS WRONG");
return -EINVAL;
}
- if (pContext->bEnabled == LVM_FALSE){
- if( pContext->SamplesToExitCount > 0){
- pContext->SamplesToExitCount -= outBuffer->frameCount;
- LOGV("\tReverb_process() Effect is being stopped %d", pContext->SamplesToExitCount);
- }else{
- LOGV("\tReverb_process() Effect is being stopped");
- return -ENODATA;
- }
- }
//LOGV("\tReverb_process() Calling process with %d frames", outBuffer->frameCount);
/* Process all the available frames, block processing is handled internalLY by the LVM bundle */
status = process( (LVM_INT16 *)inBuffer->raw,
@@ -1797,6 +1856,14 @@
outBuffer->frameCount,
pContext);
+ if (pContext->bEnabled == LVM_FALSE) {
+ if (pContext->SamplesToExitCount > 0) {
+ pContext->SamplesToExitCount -= outBuffer->frameCount;
+ } else {
+ status = -ENODATA;
+ }
+ }
+
return status;
} /* end Reverb_process */
@@ -1943,6 +2010,8 @@
LVM_ERROR_CHECK(LvmStatus, "LVREV_GetControlParameters", "EFFECT_CMD_ENABLE")
pContext->SamplesToExitCount =
(ActiveParams.T60 * pContext->config.inputCfg.samplingRate)/1000;
+ // force no volume ramp for first buffer processed after enabling the effect
+ pContext->volumeMode = android::REVERB_VOLUME_FLAT;
//LOGV("\tEFFECT_CMD_ENABLE SamplesToExitCount = %d", pContext->SamplesToExitCount);
break;
case EFFECT_CMD_DISABLE:
@@ -1963,8 +2032,34 @@
pContext->bEnabled = LVM_FALSE;
break;
- case EFFECT_CMD_SET_DEVICE:
case EFFECT_CMD_SET_VOLUME:
+ if (pCmdData == NULL ||
+ cmdSize != 2 * sizeof(uint32_t)) {
+ LOGV("\tLVM_ERROR : Reverb_command cmdCode Case: "
+ "EFFECT_CMD_SET_VOLUME: ERROR");
+ return -EINVAL;
+ }
+
+
+ if (pReplyData != NULL) { // we have volume control
+ pContext->leftVolume = (LVM_INT16)((*(uint32_t *)pCmdData + (1 << 11)) >> 12);
+ pContext->rightVolume = (LVM_INT16)((*((uint32_t *)pCmdData + 1) + (1 << 11)) >> 12);
+ *(uint32_t *)pReplyData = (1 << 24);
+ *((uint32_t *)pReplyData + 1) = (1 << 24);
+ if (pContext->volumeMode == android::REVERB_VOLUME_OFF) {
+ // force no volume ramp for first buffer processed after getting volume control
+ pContext->volumeMode = android::REVERB_VOLUME_FLAT;
+ }
+ } else { // we don't have volume control
+ pContext->leftVolume = REVERB_UNIT_VOLUME;
+ pContext->rightVolume = REVERB_UNIT_VOLUME;
+ pContext->volumeMode = android::REVERB_VOLUME_OFF;
+ }
+ LOGV("EFFECT_CMD_SET_VOLUME left %d, right %d mode %d",
+ pContext->leftVolume, pContext->rightVolume, pContext->volumeMode);
+ break;
+
+ case EFFECT_CMD_SET_DEVICE:
case EFFECT_CMD_SET_AUDIO_MODE:
//LOGV("\tReverb_command cmdCode Case: "
// "EFFECT_CMD_SET_DEVICE/EFFECT_CMD_SET_VOLUME/EFFECT_CMD_SET_AUDIO_MODE start");
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 3d3bd62..6332b4e 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -21,6 +21,8 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
+#include <sys/stat.h>
+#include <dirent.h>
#include <cutils/properties.h>
@@ -686,21 +688,77 @@
return result;
}
+static void deleteRecursive(const char* path) {
+ char pathbuf[PATH_MAX];
+ int pathLength = strlen(path);
+ if (pathLength >= sizeof(pathbuf) - 1) {
+ LOGE("path too long: %s\n", path);
+ }
+ strcpy(pathbuf, path);
+ if (pathbuf[pathLength - 1] != '/') {
+ pathbuf[pathLength++] = '/';
+ }
+ char* fileSpot = pathbuf + pathLength;
+ int pathRemaining = sizeof(pathbuf) - pathLength - 1;
+
+ DIR* dir = opendir(path);
+ if (!dir) {
+ LOGE("opendir %s failed: %s", path, strerror(errno));
+ return;
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dir))) {
+ const char* name = entry->d_name;
+
+ // ignore "." and ".."
+ if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
+ continue;
+ }
+
+ int nameLength = strlen(name);
+ if (nameLength > pathRemaining) {
+ LOGE("path %s/%s too long\n", path, name);
+ continue;
+ }
+ strcpy(fileSpot, name);
+
+ int type = entry->d_type;
+ if (entry->d_type == DT_DIR) {
+ deleteRecursive(pathbuf);
+ rmdir(pathbuf);
+ } else {
+ unlink(pathbuf);
+ }
+ }
+}
+
+static void deletePath(const char* path) {
+ struct stat statbuf;
+ if (stat(path, &statbuf) == 0) {
+ if (S_ISDIR(statbuf.st_mode)) {
+ deleteRecursive(path);
+ rmdir(path);
+ } else {
+ unlink(path);
+ }
+ } else {
+ LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
+ }
+}
+
MtpResponseCode MtpServer::doDeleteObject() {
MtpObjectHandle handle = mRequest.getParameter(1);
- MtpObjectFormat format = mRequest.getParameter(1);
+ MtpObjectFormat format = mRequest.getParameter(2);
// FIXME - support deleting all objects if handle is 0xFFFFFFFF
// FIXME - implement deleting objects by format
- // FIXME - handle non-empty directories
MtpString filePath;
int64_t fileLength;
int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
if (result == MTP_RESPONSE_OK) {
LOGV("deleting %s", (const char *)filePath);
- // one of these should work
- rmdir((const char *)filePath);
- unlink((const char *)filePath);
+ deletePath((const char *)filePath);
return mDatabase->deleteFile(handle);
} else {
return result;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index b4e0d3a..5dedcc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -718,7 +718,6 @@
iconId = sWifiSignalImages[mLastWifiInetConnectivityState]
[mLastWifiSignalLevel];
}
-
mService.setIcon("wifi", iconId, 0);
// Show the icon since wi-fi is connected
mService.setIconVisibility("wifi", true);
@@ -1101,7 +1100,7 @@
int iconId;
final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
int newSignalLevel = WifiManager.calculateSignalLevel(newRssi,
- sWifiSignalImages.length);
+ sWifiSignalImages[0].length);
if (newSignalLevel != mLastWifiSignalLevel) {
mLastWifiSignalLevel = newSignalLevel;
if (mIsWifiConnected) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 3770b55..886c25b 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1752,14 +1752,15 @@
}
// compute volume for this track
- int16_t left, right, aux;
+ uint32_t vl, vr, va;
if (track->isMuted() || track->isPausing() ||
mStreamTypes[track->type()].mute) {
- left = right = aux = 0;
+ vl = vr = va = 0;
if (track->isPausing()) {
track->setPaused();
}
} else {
+
// read original volumes with volume control
float typeVolume = mStreamTypes[track->type()].volume;
#ifdef LVMX
@@ -1774,35 +1775,36 @@
}
#endif
float v = masterVolume * typeVolume;
- uint32_t vl = (uint32_t)(v * cblk->volume[0]) << 12;
- uint32_t vr = (uint32_t)(v * cblk->volume[1]) << 12;
+ vl = (uint32_t)(v * cblk->volume[0]) << 12;
+ vr = (uint32_t)(v * cblk->volume[1]) << 12;
- // Delegate volume control to effect in track effect chain if needed
- if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
- // Do not ramp volume is volume is controlled by effect
- param = AudioMixer::VOLUME;
- track->mHasVolumeController = true;
- } else {
- // force no volume ramp when volume controller was just disabled or removed
- // from effect chain to avoid volume spike
- if (track->mHasVolumeController) {
- param = AudioMixer::VOLUME;
- }
- track->mHasVolumeController = false;
- }
-
- // Convert volumes from 8.24 to 4.12 format
- uint32_t v_clamped = (vl + (1 << 11)) >> 12;
- if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
- left = int16_t(v_clamped);
- v_clamped = (vr + (1 << 11)) >> 12;
- if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
- right = int16_t(v_clamped);
-
- v_clamped = (uint32_t)(v * cblk->sendLevel);
- if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
- aux = int16_t(v_clamped);
+ va = (uint32_t)(v * cblk->sendLevel);
}
+ // Delegate volume control to effect in track effect chain if needed
+ if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
+ // Do not ramp volume if volume is controlled by effect
+ param = AudioMixer::VOLUME;
+ track->mHasVolumeController = true;
+ } else {
+ // force no volume ramp when volume controller was just disabled or removed
+ // from effect chain to avoid volume spike
+ if (track->mHasVolumeController) {
+ param = AudioMixer::VOLUME;
+ }
+ track->mHasVolumeController = false;
+ }
+
+ // Convert volumes from 8.24 to 4.12 format
+ int16_t left, right, aux;
+ uint32_t v_clamped = (vl + (1 << 11)) >> 12;
+ if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+ left = int16_t(v_clamped);
+ v_clamped = (vr + (1 << 11)) >> 12;
+ if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+ right = int16_t(v_clamped);
+
+ if (va > MAX_GAIN_INT) va = MAX_GAIN_INT;
+ aux = int16_t(va);
#ifdef LVMX
if ( tracksConnectedChanged || stateChanged )
@@ -2283,7 +2285,7 @@
// only one effect chain can be present on DirectOutputThread, so if
// there is one, the track is connected to it
if (!effectChains.isEmpty()) {
- // Do not ramp volume is volume is controlled by effect
+ // Do not ramp volume if volume is controlled by effect
if(effectChains[0]->setVolume_l(&vl, &vr)) {
rampVolume = false;
}
@@ -4728,7 +4730,7 @@
// Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects
// that can only be created by audio policy manager (running in same process)
if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE &&
- getpid() != IPCThreadState::self()->getCallingPid()) {
+ getpid() != pid) {
lStatus = INVALID_OPERATION;
goto Exit;
}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index e454c08..aa87f29 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -54,6 +54,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;
@@ -504,7 +505,7 @@
parseLeftoverJournals();
// Power management
- mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "backup");
+ mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
// Start the backup passes going
setBackupEnabled(areEnabled);
@@ -1363,6 +1364,7 @@
? IApplicationThread.BACKUP_MODE_FULL
: IApplicationThread.BACKUP_MODE_INCREMENTAL;
try {
+ mWakelock.setWorkSource(new WorkSource(request.appInfo.uid));
agent = bindToAgentSynchronous(request.appInfo, mode);
if (agent != null) {
int result = processOneBackup(request, agent, transport);
@@ -1378,6 +1380,8 @@
}
}
+ mWakelock.setWorkSource(null);
+
return BackupConstants.TRANSPORT_OK;
}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index a343c59..8452a9f 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -52,6 +52,7 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.WorkSource;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
@@ -157,6 +158,12 @@
private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
new HashMap<String,ArrayList<UpdateRecord>>();
+ /**
+ * Temporary filled in when computing min time for a provider. Access is
+ * protected by global lock mLock.
+ */
+ private final WorkSource mTmpWorkSource = new WorkSource();
+
// Proximity listeners
private Receiver mProximityReceiver = null;
private ILocationListener mProximityListener = null;
@@ -913,7 +920,7 @@
if (enabled) {
p.enable();
if (listeners > 0) {
- p.setMinTime(getMinTimeLocked(provider));
+ p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource);
p.enableLocationTracking(true);
}
} else {
@@ -925,9 +932,21 @@
private long getMinTimeLocked(String provider) {
long minTime = Long.MAX_VALUE;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
+ mTmpWorkSource.clear();
if (records != null) {
for (int i=records.size()-1; i>=0; i--) {
- minTime = Math.min(minTime, records.get(i).mMinTime);
+ UpdateRecord ur = records.get(i);
+ long curTime = ur.mMinTime;
+ if (curTime < minTime) {
+ minTime = curTime;
+ }
+ }
+ long inclTime = (minTime*3)/2;
+ for (int i=records.size()-1; i>=0; i--) {
+ UpdateRecord ur = records.get(i);
+ if (ur.mMinTime <= inclTime) {
+ mTmpWorkSource.add(ur.mUid);
+ }
}
}
return minTime;
@@ -1125,7 +1144,7 @@
boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
if (isProviderEnabled) {
long minTimeForProvider = getMinTimeLocked(provider);
- p.setMinTime(minTimeForProvider);
+ p.setMinTime(minTimeForProvider, mTmpWorkSource);
// try requesting single shot if singleShot is true, and fall back to
// regular location tracking if requestSingleShotFix() is not supported
if (!singleShot || !p.requestSingleShotFix()) {
@@ -1223,7 +1242,7 @@
LocationProviderInterface p = mProvidersByName.get(provider);
if (p != null) {
if (hasOtherListener) {
- p.setMinTime(getMinTimeLocked(provider));
+ p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource);
} else {
p.enableLocationTracking(false);
}
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 2e32e60..4d68b52 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -50,6 +50,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.provider.Settings.SettingNotFoundException;
import android.provider.Settings;
import android.util.EventLog;
@@ -310,7 +311,7 @@
long ident = Binder.clearCallingIdentity();
try {
PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken,
- MY_UID, MY_PID, mTag);
+ MY_UID, MY_PID, mTag, null);
mHeld = true;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -607,6 +608,7 @@
final int uid;
final int pid;
final int monitorType;
+ WorkSource ws;
boolean activated = true;
int minState;
}
@@ -630,35 +632,74 @@
|| n == PowerManager.SCREEN_DIM_WAKE_LOCK;
}
- public void acquireWakeLock(int flags, IBinder lock, String tag) {
+ void enforceWakeSourcePermission(int uid, int pid) {
+ if (uid == Process.myUid()) {
+ return;
+ }
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
+ pid, uid, null);
+ }
+
+ public void acquireWakeLock(int flags, IBinder lock, String tag, WorkSource ws) {
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
if (uid != Process.myUid()) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
}
+ if (ws != null) {
+ enforceWakeSourcePermission(uid, pid);
+ }
long ident = Binder.clearCallingIdentity();
try {
synchronized (mLocks) {
- acquireWakeLockLocked(flags, lock, uid, pid, tag);
+ acquireWakeLockLocked(flags, lock, uid, pid, tag, ws);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag) {
- int acquireUid = -1;
- int acquirePid = -1;
- String acquireName = null;
- int acquireType = -1;
+ void noteStartWakeLocked(WakeLock wl, WorkSource ws) {
+ try {
+ if (ws != null) {
+ mBatteryStats.noteStartWakelockFromSource(ws, wl.pid, wl.tag,
+ wl.monitorType);
+ } else {
+ mBatteryStats.noteStartWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType);
+ }
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }
+ void noteStopWakeLocked(WakeLock wl, WorkSource ws) {
+ try {
+ if (ws != null) {
+ mBatteryStats.noteStopWakelockFromSource(ws, wl.pid, wl.tag,
+ wl.monitorType);
+ } else {
+ mBatteryStats.noteStopWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType);
+ }
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }
+
+ public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag,
+ WorkSource ws) {
if (mSpew) {
Slog.d(TAG, "acquireWakeLock flags=0x" + Integer.toHexString(flags) + " tag=" + tag);
}
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+
int index = mLocks.getIndex(lock);
WakeLock wl;
boolean newlock;
+ boolean diffsource;
+ WorkSource oldsource;
if (index < 0) {
wl = new WakeLock(flags, lock, tag, uid, pid);
switch (wl.flags & LOCK_MASK)
@@ -687,10 +728,31 @@
return;
}
mLocks.addLock(wl);
+ if (ws != null) {
+ wl.ws = new WorkSource(ws);
+ }
newlock = true;
+ diffsource = false;
+ oldsource = null;
} else {
wl = mLocks.get(index);
newlock = false;
+ oldsource = wl.ws;
+ if (oldsource != null) {
+ if (ws == null) {
+ wl.ws = null;
+ diffsource = true;
+ } else {
+ diffsource = oldsource.diff(ws);
+ }
+ } else if (ws != null) {
+ diffsource = true;
+ } else {
+ diffsource = false;
+ }
+ if (diffsource) {
+ wl.ws = new WorkSource(ws);
+ }
}
if (isScreenLock(flags)) {
// if this causes a wakeup, we reactivate all of the locks and
@@ -731,19 +793,41 @@
enableProximityLockLocked();
}
}
- if (newlock) {
- acquireUid = wl.uid;
- acquirePid = wl.pid;
- acquireName = wl.tag;
- acquireType = wl.monitorType;
- }
- if (acquireType >= 0) {
- try {
- mBatteryStats.noteStartWakelock(acquireUid, acquirePid, acquireName, acquireType);
- } catch (RemoteException e) {
- // Ignore
+ if (diffsource) {
+ // If the lock sources have changed, need to first release the
+ // old ones.
+ noteStopWakeLocked(wl, oldsource);
+ }
+ if (newlock || diffsource) {
+ noteStartWakeLocked(wl, ws);
+ }
+ }
+
+ public void updateWakeLockWorkSource(IBinder lock, WorkSource ws) {
+ int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+ if (ws != null) {
+ enforceWakeSourcePermission(uid, pid);
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLocks) {
+ int index = mLocks.getIndex(lock);
+ if (index < 0) {
+ throw new IllegalArgumentException("Wake lock not active");
+ }
+ WakeLock wl = mLocks.get(index);
+ WorkSource oldsource = wl.ws;
+ wl.ws = ws != null ? new WorkSource(ws) : null;
+ noteStopWakeLocked(wl, oldsource);
+ noteStartWakeLocked(wl, ws);
}
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -759,11 +843,6 @@
}
private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) {
- int releaseUid;
- int releasePid;
- String releaseName;
- int releaseType;
-
WakeLock wl = mLocks.removeLock(lock);
if (wl == null) {
return;
@@ -804,17 +883,11 @@
}
// Unlink the lock from the binder.
wl.binder.unlinkToDeath(wl, 0);
- releaseUid = wl.uid;
- releasePid = wl.pid;
- releaseName = wl.tag;
- releaseType = wl.monitorType;
- if (releaseType >= 0) {
+ if (wl.monitorType >= 0) {
long origId = Binder.clearCallingIdentity();
try {
- mBatteryStats.noteStopWakelock(releaseUid, releasePid, releaseName, releaseType);
- } catch (RemoteException e) {
- // Ignore
+ noteStopWakeLocked(wl, wl.ws);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index 2e7e3e1..f0b5955 100755
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -29,6 +29,7 @@
import android.os.IBinder;
import android.os.Binder;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.util.Slog;
import java.util.LinkedList;
@@ -39,6 +40,7 @@
private final LinkedList<Vibration> mVibrations;
private Vibration mCurrentVibration;
+ private final WorkSource mTmpWorkSource = new WorkSource();
private class Vibration implements IBinder.DeathRecipient {
private final IBinder mToken;
@@ -46,22 +48,24 @@
private final long mStartTime;
private final long[] mPattern;
private final int mRepeat;
+ private final int mUid;
- Vibration(IBinder token, long millis) {
- this(token, millis, null, 0);
+ Vibration(IBinder token, long millis, int uid) {
+ this(token, millis, null, 0, uid);
}
- Vibration(IBinder token, long[] pattern, int repeat) {
- this(token, 0, pattern, repeat);
+ Vibration(IBinder token, long[] pattern, int repeat, int uid) {
+ this(token, 0, pattern, repeat, uid);
}
private Vibration(IBinder token, long millis, long[] pattern,
- int repeat) {
+ int repeat, int uid) {
mToken = token;
mTimeout = millis;
mStartTime = SystemClock.uptimeMillis();
mPattern = pattern;
mRepeat = repeat;
+ mUid = uid;
}
public void binderDied() {
@@ -98,7 +102,7 @@
mContext = context;
PowerManager pm = (PowerManager)context.getSystemService(
Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
mWakeLock.setReferenceCounted(true);
mVibrations = new LinkedList<Vibration>();
@@ -113,6 +117,7 @@
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
}
+ int uid = Binder.getCallingUid();
// We're running in the system server so we cannot crash. Check for a
// timeout of 0 or negative. This will ensure that a vibration has
// either a timeout of > 0 or a non-null pattern.
@@ -122,7 +127,7 @@
// longer than milliseconds.
return;
}
- Vibration vib = new Vibration(token, milliseconds);
+ Vibration vib = new Vibration(token, milliseconds, uid);
synchronized (mVibrations) {
removeVibrationLocked(token);
doCancelVibrateLocked();
@@ -146,6 +151,7 @@
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
}
+ int uid = Binder.getCallingUid();
// so wakelock calls will succeed
long identity = Binder.clearCallingIdentity();
try {
@@ -165,7 +171,7 @@
return;
}
- Vibration vib = new Vibration(token, pattern, repeat);
+ Vibration vib = new Vibration(token, pattern, repeat, uid);
try {
token.linkToDeath(vib, 0);
} catch (RemoteException e) {
@@ -280,6 +286,8 @@
VibrateThread(Vibration vib) {
mVibration = vib;
+ mTmpWorkSource.set(vib.mUid);
+ mWakeLock.setWorkSource(mTmpWorkSource);
mWakeLock.acquire();
}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 6ecc511..e8502fa 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -50,6 +50,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.WorkSource;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
@@ -1035,8 +1036,8 @@
}
private class WifiLock extends DeathRecipient {
- WifiLock(int lockMode, String tag, IBinder binder) {
- super(lockMode, tag, binder);
+ WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
+ super(lockMode, tag, binder, ws);
}
public void binderDied() {
@@ -1106,33 +1107,70 @@
}
}
- public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
+ void enforceWakeSourcePermission(int uid, int pid) {
+ if (uid == android.os.Process.myUid()) {
+ return;
+ }
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
+ pid, uid, null);
+ }
+
+ public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
return false;
}
- WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
+ if (ws != null) {
+ enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid());
+ }
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+ if (ws == null) {
+ ws = new WorkSource(Binder.getCallingUid());
+ }
+ WifiLock wifiLock = new WifiLock(lockMode, tag, binder, ws);
synchronized (mLocks) {
return acquireWifiLockLocked(wifiLock);
}
}
+ private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException {
+ switch(wifiLock.mMode) {
+ case WifiManager.WIFI_MODE_FULL:
+ mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
+ break;
+ case WifiManager.WIFI_MODE_SCAN_ONLY:
+ mBatteryStats.noteScanWifiLockAcquiredFromSource(wifiLock.mWorkSource);
+ break;
+ }
+ }
+
+ private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException {
+ switch(wifiLock.mMode) {
+ case WifiManager.WIFI_MODE_FULL:
+ mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
+ break;
+ case WifiManager.WIFI_MODE_SCAN_ONLY:
+ mBatteryStats.noteScanWifiLockReleasedFromSource(wifiLock.mWorkSource);
+ break;
+ }
+ }
+
private boolean acquireWifiLockLocked(WifiLock wifiLock) {
Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
mLocks.addLock(wifiLock);
- int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
+ noteAcquireWifiLock(wifiLock);
switch(wifiLock.mMode) {
case WifiManager.WIFI_MODE_FULL:
++mFullLocksAcquired;
- mBatteryStats.noteFullWifiLockAcquired(uid);
break;
case WifiManager.WIFI_MODE_SCAN_ONLY:
++mScanLocksAcquired;
- mBatteryStats.noteScanWifiLockAcquired(uid);
break;
}
} catch (RemoteException e) {
@@ -1144,6 +1182,33 @@
return true;
}
+ public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
+ int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+ if (ws != null) {
+ enforceWakeSourcePermission(uid, pid);
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLocks) {
+ int index = mLocks.findLockByBinder(lock);
+ if (index < 0) {
+ throw new IllegalArgumentException("Wifi lock not active");
+ }
+ WifiLock wl = mLocks.mList.get(index);
+ noteReleaseWifiLock(wl);
+ wl.mWorkSource = ws != null ? new WorkSource(ws) : new WorkSource(uid);
+ noteAcquireWifiLock(wl);
+ }
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
public boolean releaseWifiLock(IBinder lock) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
synchronized (mLocks) {
@@ -1161,17 +1226,15 @@
hadLock = (wifiLock != null);
if (hadLock) {
- int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
+ noteAcquireWifiLock(wifiLock);
switch(wifiLock.mMode) {
case WifiManager.WIFI_MODE_FULL:
++mFullLocksReleased;
- mBatteryStats.noteFullWifiLockReleased(uid);
break;
case WifiManager.WIFI_MODE_SCAN_ONLY:
++mScanLocksReleased;
- mBatteryStats.noteScanWifiLockReleased(uid);
break;
}
} catch (RemoteException e) {
@@ -1189,12 +1252,14 @@
String mTag;
int mMode;
IBinder mBinder;
+ WorkSource mWorkSource;
- DeathRecipient(int mode, String tag, IBinder binder) {
+ DeathRecipient(int mode, String tag, IBinder binder, WorkSource ws) {
super();
mTag = tag;
mMode = mode;
mBinder = binder;
+ mWorkSource = ws;
try {
mBinder.linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -1209,7 +1274,7 @@
private class Multicaster extends DeathRecipient {
Multicaster(String tag, IBinder binder) {
- super(Binder.getCallingUid(), tag, binder);
+ super(Binder.getCallingUid(), tag, binder, null);
}
public void binderDied() {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 73c1790..5432890 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -6021,12 +6021,18 @@
finisher = new IIntentReceiver.Stub() {
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
- boolean sticky)
- throws RemoteException {
- synchronized (ActivityManagerService.this) {
- mDidUpdate = true;
- }
- systemReady(goingCallback);
+ boolean sticky) {
+ // The raw IIntentReceiver interface is called
+ // with the AM lock held, so redispatch to
+ // execute our code without the lock.
+ mHandler.post(new Runnable() {
+ public void run() {
+ synchronized (ActivityManagerService.this) {
+ mDidUpdate = true;
+ }
+ systemReady(goingCallback);
+ }
+ });
}
};
}
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index a0c21dd..4fc8020 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1104,7 +1104,7 @@
// Okay we are now going to start a switch, to 'next'. We may first
// have to pause the current activity, but this is an important point
// where we have decided to go to 'next' so keep track of that.
- if (mLastStartedActivity != null) {
+ if (mLastStartedActivity != null && !mLastStartedActivity.finishing) {
long now = SystemClock.uptimeMillis();
final boolean inTime = mLastStartedActivity.startTime != 0
&& (mLastStartedActivity.startTime + START_WARN_TIME) >= now;
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 7314e04..bb40967 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -23,6 +23,7 @@
import android.os.Parcel;
import android.os.Process;
import android.os.ServiceManager;
+import android.os.WorkSource;
import android.telephony.SignalStrength;
import android.util.Slog;
@@ -107,6 +108,20 @@
}
}
+ public void noteStartWakelockFromSource(WorkSource ws, int pid, String name, int type) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteStartWakeFromSourceLocked(ws, pid, name, type);
+ }
+ }
+
+ public void noteStopWakelockFromSource(WorkSource ws, int pid, String name, int type) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteStopWakeFromSourceLocked(ws, pid, name, type);
+ }
+ }
+
public void noteStartSensor(int uid, int sensor) {
enforceCallingPermission();
synchronized (mStats) {
@@ -317,6 +332,48 @@
}
}
+ public void noteFullWifiLockAcquiredFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteFullWifiLockAcquiredFromSourceLocked(ws);
+ }
+ }
+
+ public void noteFullWifiLockReleasedFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteFullWifiLockReleasedFromSourceLocked(ws);
+ }
+ }
+
+ public void noteScanWifiLockAcquiredFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteScanWifiLockAcquiredFromSourceLocked(ws);
+ }
+ }
+
+ public void noteScanWifiLockReleasedFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteScanWifiLockReleasedFromSourceLocked(ws);
+ }
+ }
+
+ public void noteWifiMulticastEnabledFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteWifiMulticastEnabledFromSourceLocked(ws);
+ }
+ }
+
+ public void noteWifiMulticastDisabledFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteWifiMulticastDisabledFromSourceLocked(ws);
+ }
+ }
+
public boolean isOnBattery() {
return mStats.isOnBattery();
}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index c1165c7..3bf6ee4 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -44,6 +44,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.provider.Settings;
import android.util.Log;
import android.util.SparseIntArray;
@@ -736,7 +737,7 @@
startNavigating(true);
}
- public void setMinTime(long minTime) {
+ public void setMinTime(long minTime, WorkSource ws) {
if (DEBUG) Log.d(TAG, "setMinTime " + minTime);
if (minTime >= 0) {
@@ -779,7 +780,7 @@
public void addListener(int uid) {
synchronized (mWakeLock) {
mPendingListenerMessages++;
- mWakeLock.acquire();
+ mWakeLock.acquire();
Message m = Message.obtain(mHandler, ADD_LISTENER);
m.arg1 = uid;
mHandler.sendMessage(m);
diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
index 084ab81..858a582 100644
--- a/services/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -20,6 +20,7 @@
import android.location.Location;
import android.net.NetworkInfo;
import android.os.Bundle;
+import android.os.WorkSource;
/**
* Location Manager's interface for location providers.
@@ -47,7 +48,7 @@
/* returns false if single shot is not supported */
boolean requestSingleShotFix();
String getInternalState();
- void setMinTime(long minTime);
+ void setMinTime(long minTime, WorkSource ws);
void updateNetworkState(int state, NetworkInfo info);
void updateLocation(Location location);
boolean sendExtraCommand(String command, Bundle extras);
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index 24d7737..7dc9920 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -29,6 +29,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.util.Log;
import com.android.internal.location.DummyLocationProvider;
@@ -52,6 +53,7 @@
private boolean mLocationTracking = false;
private boolean mEnabled = false;
private long mMinTime = -1;
+ private WorkSource mMinTimeSource = new WorkSource();
private int mNetworkState;
private NetworkInfo mNetworkInfo;
@@ -122,7 +124,7 @@
provider.enableLocationTracking(true);
}
if (mMinTime >= 0) {
- provider.setMinTime(mMinTime);
+ provider.setMinTime(mMinTime, mMinTimeSource);
}
if (mNetworkInfo != null) {
provider.updateNetworkState(mNetworkState, mNetworkInfo);
@@ -318,6 +320,7 @@
mLocationTracking = enable;
if (!enable) {
mMinTime = -1;
+ mMinTimeSource.clear();
}
ILocationProvider provider;
synchronized (mServiceConnection) {
@@ -339,15 +342,16 @@
return mMinTime;
}
- public void setMinTime(long minTime) {
- mMinTime = minTime;
+ public void setMinTime(long minTime, WorkSource ws) {
+ mMinTime = minTime;
+ mMinTimeSource.set(ws);
ILocationProvider provider;
synchronized (mServiceConnection) {
provider = mProvider;
}
if (provider != null) {
try {
- provider.setMinTime(minTime);
+ provider.setMinTime(minTime, ws);
} catch (RemoteException e) {
}
}
diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
index 01b34b7..09d799f 100644
--- a/services/java/com/android/server/location/MockProvider.java
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -23,6 +23,7 @@
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.WorkSource;
import android.util.Log;
import android.util.PrintWriterPrinter;
@@ -201,7 +202,7 @@
return false;
}
- public void setMinTime(long minTime) {
+ public void setMinTime(long minTime, WorkSource ws) {
}
public void updateNetworkState(int state, NetworkInfo info) {
diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
index 7fc93f8..ea0d1b0 100644
--- a/services/java/com/android/server/location/PassiveProvider.java
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -24,6 +24,7 @@
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.WorkSource;
import android.util.Log;
/**
@@ -123,7 +124,7 @@
return false;
}
- public void setMinTime(long minTime) {
+ public void setMinTime(long minTime, WorkSource ws) {
}
public void updateNetworkState(int state, NetworkInfo info) {
diff --git a/tests/DumpRenderTree2/AndroidManifest.xml b/tests/DumpRenderTree2/AndroidManifest.xml
index b4dc190..5233677 100644
--- a/tests/DumpRenderTree2/AndroidManifest.xml
+++ b/tests/DumpRenderTree2/AndroidManifest.xml
@@ -40,6 +40,7 @@
</activity>
<activity android:name=".LayoutTestsExecutor"
+ android:theme="@style/WhiteBackground"
android:label="Layout tests' executor"
android:process=":executor">
</activity>
diff --git a/tests/DumpRenderTree2/assets/run_layout_tests.py b/tests/DumpRenderTree2/assets/run_layout_tests.py
index bd6e457..c584d596 100755
--- a/tests/DumpRenderTree2/assets/run_layout_tests.py
+++ b/tests/DumpRenderTree2/assets/run_layout_tests.py
@@ -3,29 +3,30 @@
"""Run layout tests on the device.
It runs the specified tests on the device, downloads the summaries to the temporary directory
- and opens html details in the default browser.
+ and optionally shows the detailed results the host's default browser.
Usage:
- run_layout_tests.py PATH
+ run_layout_tests.py --show-results-in-browser test-relative-path
"""
-import sys
-import os
-import subprocess
import logging
-import webbrowser
+import optparse
+import os
+import sys
+import subprocess
import tempfile
+import webbrowser
#TODO: These should not be hardcoded
RESULTS_ABSOLUTE_PATH = "/sdcard/layout-test-results/"
DETAILS_HTML = "details.html"
SUMMARY_TXT = "summary.txt"
-def main():
- if len(sys.argv) > 1:
- path = sys.argv[1]
+def main(options, args):
+ if args:
+ path = " ".join(args);
else:
- path = ""
+ path = "";
logging.basicConfig(level=logging.INFO, format='%(message)s')
@@ -61,7 +62,12 @@
logging.info("")
# Open the browser with summary
- webbrowser.open(details_html_tmp_path)
+ if options.show_results_in_browser != "false":
+ webbrowser.open(details_html_tmp_path)
if __name__ == "__main__":
- main();
+ option_parser = optparse.OptionParser(usage="Usage: %prog [options] test-relative-path")
+ option_parser.add_option("", "--show-results-in-browser", default="true",
+ help="Show the results the host's default web browser, default=true")
+ options, args = option_parser.parse_args();
+ main(options, args);
diff --git a/tests/DumpRenderTree2/res/values/style.xml b/tests/DumpRenderTree2/res/values/style.xml
new file mode 100644
index 0000000..35f3419
--- /dev/null
+++ b/tests/DumpRenderTree2/res/values/style.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2010 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<resources>
+ <style name="WhiteBackground">
+ <item name="android:background">@android:color/white</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
index a82037e..c9e194f 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
@@ -35,12 +35,9 @@
mLayoutTestsExecutor = layoutTestsExecutor;
}
- public void waitUntilDone() {
- mLayoutTestsExecutor.waitUntilDone();
- }
-
- public void notifyDone() {
- mLayoutTestsExecutor.notifyDone();
+ public void clearAllDatabases() {
+ Log.i(LOG_TAG, "clearAllDatabases() called");
+ WebStorage.getInstance().deleteAllData();
}
public void dumpAsText() {
@@ -55,19 +52,22 @@
mLayoutTestsExecutor.dumpChildFramesAsText();
}
- public void clearAllDatabases() {
- Log.i(LOG_TAG, "clearAllDatabases() called");
- WebStorage.getInstance().deleteAllData();
+ public void dumpDatabaseCallbacks() {
+ mLayoutTestsExecutor.dumpDatabaseCallbacks();
+ }
+
+ public void notifyDone() {
+ mLayoutTestsExecutor.notifyDone();
+ }
+
+ public void overridePreference(String key, boolean value) {
+ mLayoutTestsExecutor.overridePreference(key, value);
}
public void setCanOpenWindows() {
mLayoutTestsExecutor.setCanOpenWindows();
}
- public void dumpDatabaseCallbacks() {
- mLayoutTestsExecutor.dumpDatabaseCallbacks();
- }
-
public void setDatabaseQuota(long quota) {
/** TODO: Reset this before every test! */
Log.i(LOG_TAG, "setDatabaseQuota() called with: " + quota);
@@ -79,21 +79,6 @@
mLayoutTestsExecutor.setGeolocationPermission(allow);
}
- public void overridePreference(String key, boolean value) {
- mLayoutTestsExecutor.overridePreference(key, value);
- }
-
- public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
- Log.i(LOG_TAG, "setMockGeolocationPosition(): " + "latitude=" + latitude +
- " longitude=" + longitude + " accuracy=" + accuracy);
- MockGeolocation.getInstance().setPosition(latitude, longitude, accuracy);
- }
-
- public void setMockGeolocationError(int code, String message) {
- Log.i(LOG_TAG, "setMockGeolocationError(): " + "code=" + code + " message=" + message);
- MockGeolocation.getInstance().setError(code, message);
- }
-
public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
// Configuration is in WebKit, so stay on WebCore thread, but go via LayoutTestsExecutor
@@ -104,4 +89,19 @@
mLayoutTestsExecutor.setMockDeviceOrientation(
canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
}
-}
\ No newline at end of file
+
+ public void setMockGeolocationError(int code, String message) {
+ Log.i(LOG_TAG, "setMockGeolocationError(): " + "code=" + code + " message=" + message);
+ MockGeolocation.getInstance().setError(code, message);
+ }
+
+ public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
+ Log.i(LOG_TAG, "setMockGeolocationPosition(): " + "latitude=" + latitude +
+ " longitude=" + longitude + " accuracy=" + accuracy);
+ MockGeolocation.getInstance().setPosition(latitude, longitude, accuracy);
+ }
+
+ public void waitUntilDone() {
+ mLayoutTestsExecutor.waitUntilDone();
+ }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
index c997911..4c7124b 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -519,24 +519,12 @@
assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
switch (msg.what) {
- case MSG_WAIT_UNTIL_DONE:
- mCurrentState = CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST;
- break;
-
- case MSG_NOTIFY_DONE:
- if (mCurrentState == CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST) {
- onTestFinished();
- }
- break;
-
case MSG_DUMP_AS_TEXT:
if (mCurrentResult == null) {
mCurrentResult = new TextResult(mCurrentTestRelativePath);
}
-
assert mCurrentResult instanceof TextResult
: "mCurrentResult instanceof" + mCurrentResult.getClass().getName();
-
break;
case MSG_DUMP_CHILD_FRAMES_AS_TEXT:
@@ -551,27 +539,13 @@
((TextResult)mCurrentResult).setDumpChildFramesAsText(true);
break;
- case MSG_SET_CAN_OPEN_WINDOWS:
- mCanOpenWindows = true;
- break;
-
case MSG_DUMP_DATABASE_CALLBACKS:
mDumpDatabaseCallbacks = true;
break;
- case MSG_SET_GEOLOCATION_PERMISSION:
- mIsGeolocationPermissionSet = true;
- mGeolocationPermission = msg.arg1 == 1;
-
- if (mPendingGeolocationPermissionCallbacks != null) {
- Iterator<GeolocationPermissions.Callback> iter =
- mPendingGeolocationPermissionCallbacks.keySet().iterator();
- while (iter.hasNext()) {
- GeolocationPermissions.Callback callback = iter.next();
- String origin = mPendingGeolocationPermissionCallbacks.get(callback);
- callback.invoke(origin, mGeolocationPermission, false);
- }
- mPendingGeolocationPermissionCallbacks = null;
+ case MSG_NOTIFY_DONE:
+ if (mCurrentState == CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST) {
+ onTestFinished();
}
break;
@@ -591,6 +565,30 @@
}
break;
+ case MSG_SET_CAN_OPEN_WINDOWS:
+ mCanOpenWindows = true;
+ break;
+
+ case MSG_SET_GEOLOCATION_PERMISSION:
+ mIsGeolocationPermissionSet = true;
+ mGeolocationPermission = msg.arg1 == 1;
+
+ if (mPendingGeolocationPermissionCallbacks != null) {
+ Iterator<GeolocationPermissions.Callback> iter =
+ mPendingGeolocationPermissionCallbacks.keySet().iterator();
+ while (iter.hasNext()) {
+ GeolocationPermissions.Callback callback = iter.next();
+ String origin = mPendingGeolocationPermissionCallbacks.get(callback);
+ callback.invoke(origin, mGeolocationPermission, false);
+ }
+ mPendingGeolocationPermissionCallbacks = null;
+ }
+ break;
+
+ case MSG_WAIT_UNTIL_DONE:
+ mCurrentState = CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST;
+ break;
+
default:
assert false : "msg.what=" + msg.what;
break;
@@ -605,16 +603,6 @@
mPendingGeolocationPermissionCallbacks = null;
}
- public void waitUntilDone() {
- Log.i(LOG_TAG, mCurrentTestRelativePath + ": waitUntilDone() called");
- mLayoutTestControllerHandler.sendEmptyMessage(MSG_WAIT_UNTIL_DONE);
- }
-
- public void notifyDone() {
- Log.i(LOG_TAG, mCurrentTestRelativePath + ": notifyDone() called");
- mLayoutTestControllerHandler.sendEmptyMessage(MSG_NOTIFY_DONE);
- }
-
public void dumpAsText(boolean enablePixelTest) {
Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpAsText(" + enablePixelTest + ") called");
/** TODO: Implement */
@@ -629,22 +617,14 @@
mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_CHILD_FRAMES_AS_TEXT);
}
- public void setCanOpenWindows() {
- Log.i(LOG_TAG, mCurrentTestRelativePath + ": setCanOpenWindows() called");
- mLayoutTestControllerHandler.sendEmptyMessage(MSG_SET_CAN_OPEN_WINDOWS);
- }
-
public void dumpDatabaseCallbacks() {
Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpDatabaseCallbacks() called");
mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_DATABASE_CALLBACKS);
}
- public void setGeolocationPermission(boolean allow) {
- Log.i(LOG_TAG, mCurrentTestRelativePath + ": setGeolocationPermission(" + allow +
- ") called");
- Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_GEOLOCATION_PERMISSION);
- msg.arg1 = allow ? 1 : 0;
- msg.sendToTarget();
+ public void notifyDone() {
+ Log.i(LOG_TAG, mCurrentTestRelativePath + ": notifyDone() called");
+ mLayoutTestControllerHandler.sendEmptyMessage(MSG_NOTIFY_DONE);
}
public void overridePreference(String key, boolean value) {
@@ -656,6 +636,19 @@
msg.sendToTarget();
}
+ public void setCanOpenWindows() {
+ Log.i(LOG_TAG, mCurrentTestRelativePath + ": setCanOpenWindows() called");
+ mLayoutTestControllerHandler.sendEmptyMessage(MSG_SET_CAN_OPEN_WINDOWS);
+ }
+
+ public void setGeolocationPermission(boolean allow) {
+ Log.i(LOG_TAG, mCurrentTestRelativePath + ": setGeolocationPermission(" + allow +
+ ") called");
+ Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_GEOLOCATION_PERMISSION);
+ msg.arg1 = allow ? 1 : 0;
+ msg.sendToTarget();
+ }
+
public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
Log.i(LOG_TAG, mCurrentTestRelativePath + ": setMockDeviceOrientation(" + canProvideAlpha +
@@ -664,4 +657,9 @@
mCurrentWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
canProvideGamma, gamma);
}
+
+ public void waitUntilDone() {
+ Log.i(LOG_TAG, mCurrentTestRelativePath + ": waitUntilDone() called");
+ mLayoutTestControllerHandler.sendEmptyMessage(MSG_WAIT_UNTIL_DONE);
+ }
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index d30a723..5597415 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -118,6 +118,16 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <activity
+ android:name="ThinPatchesActivity"
+ android:label="_9patchThin"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
<activity
android:name="NinePatchesActivity"
diff --git a/tests/HwAccelerationTest/res/drawable/btn_toggle_off.9.png b/tests/HwAccelerationTest/res/drawable/btn_toggle_off.9.png
new file mode 100644
index 0000000..26ee1c2
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/btn_toggle_off.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/btn_toggle_on.9.png b/tests/HwAccelerationTest/res/drawable/btn_toggle_on.9.png
new file mode 100644
index 0000000..53e95af
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/btn_toggle_on.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
index 5b7753e..d9a2893 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
@@ -39,6 +39,7 @@
super(c);
mLayerPaint = new Paint();
+ mLayerPaint.setAlpha(127);
mRectPaint = new Paint();
mRectPaint.setAntiAlias(true);
mRectPaint.setTextSize(24.0f);
@@ -47,28 +48,21 @@
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
-
canvas.drawRGB(128, 255, 128);
+
+ canvas.save();
+
canvas.translate(140.0f, 100.0f);
-
- mLayerPaint.setAlpha(127);
- int count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint,
- Canvas.ALL_SAVE_FLAG);
-
- mRectPaint.setColor(0x7fff0000);
- canvas.drawRect(-20.0f, -20.0f, 220.0f, 120.0f, mRectPaint);
-
- mRectPaint.setColor(0xff000000);
- canvas.drawText("This is a very long string to overlap between layers and framebuffer",
- -100.0f, 50.0f, mRectPaint);
-
- canvas.restoreToCount(count);
+ drawStuff(canvas, Canvas.ALL_SAVE_FLAG);
canvas.translate(0.0f, 200.0f);
+ drawStuff(canvas, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
- mLayerPaint.setAlpha(127);
- count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint,
- Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
+ canvas.restore();
+ }
+
+ private void drawStuff(Canvas canvas, int saveFlags) {
+ int count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint, saveFlags);
mRectPaint.setColor(0x7fff0000);
canvas.drawRect(-20.0f, -20.0f, 220.0f, 120.0f, mRectPaint);
@@ -76,7 +70,7 @@
mRectPaint.setColor(0xff000000);
canvas.drawText("This is a very long string to overlap between layers and framebuffer",
-100.0f, 50.0f, mRectPaint);
-
+
canvas.restoreToCount(count);
}
}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java
new file mode 100644
index 0000000..d374c32
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 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.test.hwui;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ThinPatchesActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FrameLayout layout = new FrameLayout(this);
+ PatchView b = new PatchView(this);
+ b.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT));
+ layout.addView(b);
+ layout.setBackgroundColor(0xffffffff);
+
+ setContentView(layout);
+ }
+
+ private class PatchView extends View {
+ private Drawable mPatch;
+
+ public PatchView(Activity activity) {
+ super(activity);
+
+ final Resources resources = activity.getResources();
+ mPatch = resources.getDrawable(R.drawable.btn_toggle_on);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ final int width = 100;
+ final int height = 60;
+
+ final int left = (getWidth() - width) / 2;
+ final int top = (getHeight() - height) / 2;
+
+ mPatch.setBounds(left, top, left + width, top + height);
+ mPatch.draw(canvas);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1913fa0..e73bca0 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -21,6 +21,8 @@
import android.net.wifi.ScanResult;
import android.net.DhcpInfo;
+import android.os.WorkSource;
+
/**
* Interface that allows controlling and querying Wi-Fi connectivity.
*
@@ -66,7 +68,9 @@
DhcpInfo getDhcpInfo();
- boolean acquireWifiLock(IBinder lock, int lockType, String tag);
+ boolean acquireWifiLock(IBinder lock, int lockType, String tag, in WorkSource ws);
+
+ void updateWifiLockWorkSource(IBinder lock, in WorkSource ws);
boolean releaseWifiLock(IBinder lock);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 8c3ec5f..26ed878 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -23,6 +23,7 @@
import android.os.IBinder;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.WorkSource;
import java.util.List;
@@ -1050,6 +1051,7 @@
int mLockType;
private boolean mRefCounted;
private boolean mHeld;
+ private WorkSource mWorkSource;
private WifiLock(int lockType, String tag) {
mTag = tag;
@@ -1075,7 +1077,7 @@
synchronized (mBinder) {
if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
try {
- mService.acquireWifiLock(mBinder, mLockType, mTag);
+ mService.acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
synchronized (WifiManager.this) {
if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
mService.releaseWifiLock(mBinder);
@@ -1147,6 +1149,32 @@
}
}
+ public void setWorkSource(WorkSource ws) {
+ synchronized (mBinder) {
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+ boolean changed = true;
+ if (ws == null) {
+ mWorkSource = null;
+ } else if (mWorkSource == null) {
+ changed = mWorkSource != null;
+ mWorkSource = new WorkSource(ws);
+ } else {
+ changed = mWorkSource.diff(ws);
+ if (changed) {
+ mWorkSource.set(ws);
+ }
+ }
+ if (changed && mHeld) {
+ try {
+ mService.updateWifiLockWorkSource(mBinder, mWorkSource);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
public String toString() {
String s1, s2, s3;
synchronized (mBinder) {