diff options
107 files changed, 3872 insertions, 1574 deletions
@@ -431,8 +431,8 @@ framework_docs_SDK_PREVIEW:=0 ## Latest ADT version identifiers, for reference from published docs framework_docs_ADT_VERSION:=0.9.8 framework_docs_ADT_DOWNLOAD:=ADT-0.9.8.zip -framework_docs_ADT_BYTES:=8703591 -framework_docs_ADT_CHECKSUM:=22070f8e52924605a3b3abf87c1ba39f +framework_docs_ADT_BYTES:=8301417 +framework_docs_ADT_CHECKSUM:=27e0de800512f13feae46fb554e6ee2f framework_docs_LOCAL_DROIDDOC_OPTIONS += \ -hdf sdk.version $(framework_docs_SDK_VERSION) \ diff --git a/api/current.xml b/api/current.xml index d2e1f05..8e8a671 100644 --- a/api/current.xml +++ b/api/current.xml @@ -84326,6 +84326,8 @@ > <parameter name="uid" type="int"> </parameter> +<parameter name="ws" type="android.os.WorkSource"> +</parameter> </method> <method name="onDisable" return="void" @@ -84455,6 +84457,8 @@ > <parameter name="uid" type="int"> </parameter> +<parameter name="ws" type="android.os.WorkSource"> +</parameter> </method> <method name="onRequiresCell" return="boolean" @@ -84516,6 +84520,8 @@ > <parameter name="minTime" type="long"> </parameter> +<parameter name="ws" type="android.os.WorkSource"> +</parameter> </method> <method name="onSupportsAltitude" return="boolean" @@ -101636,6 +101642,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" @@ -128447,6 +128466,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" @@ -129595,6 +129627,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" > @@ -151863,6 +152023,17 @@ visibility="public" > </method> +<method name="getPsc" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="setLacAndCid" return="void" abstract="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 @@ public class PowerCommand extends Svc.Command { = 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/ActivityThread.java b/core/java/android/app/ActivityThread.java index f8407c2..d8b5253 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3107,18 +3107,11 @@ public final class ActivityThread { /** * For system applications on userdebug/eng builds, log stack * traces of disk and network access to dropbox for analysis. - * - * Similar logic exists in SystemServer.java. */ if ((data.appInfo.flags & (ApplicationInfo.FLAG_SYSTEM | - ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0 && - !"user".equals(Build.TYPE)) { - StrictMode.setThreadPolicy( - StrictMode.DISALLOW_DISK_WRITE | - StrictMode.DISALLOW_DISK_READ | - StrictMode.DISALLOW_NETWORK | - StrictMode.PENALTY_DROPBOX); + ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0) { + StrictMode.conditionallyEnableDebugLogging(); } /** diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 7625c04..cd22fa1 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -588,7 +588,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS */ private void updateVoiceButton(boolean empty) { int visibility = View.GONE; - if (mSearchable.getVoiceSearchEnabled() && empty) { + if ((mAppSearchData == null || !mAppSearchData.getBoolean( + SearchManager.DISABLE_VOICE_SEARCH, false)) + && mSearchable.getVoiceSearchEnabled() && empty) { Intent testIntent = null; if (mSearchable.getVoiceSearchLaunchWebSearch()) { testIntent = mVoiceWebSearchIntent; diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index a1ca707..2e9cd96 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -395,6 +395,14 @@ public class SearchManager public final static String CONTEXT_IS_VOICE = "android.search.CONTEXT_IS_VOICE"; /** + * This means that the voice icon should not be shown at all, because the + * current search engine does not support voice search. + * @hide + */ + public final static String DISABLE_VOICE_SEARCH + = "android.search.DISABLE_VOICE_SEARCH"; + + /** * Reference to the shared system search service. */ private static ISearchManager mService; 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.ParcelUuid; 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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothDevice implements Parcelable { /** 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 @@ public final class BluetoothDevice implements Parcelable { } /** + * 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 @@ public final class BluetoothDevice implements Parcelable { } /** @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 ea71034..6dd8dd6 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -44,12 +44,15 @@ interface IBluetooth 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); @@ -60,6 +63,7 @@ interface IBluetooth 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 d0b67cc..26b6ad7 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -45,6 +45,7 @@ import android.os.Process; 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; @@ -126,8 +127,8 @@ public class SyncManager implements OnAccountsUpdateListener { private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000; - private static final String SYNC_WAKE_LOCK = "SyncManagerSyncWakeLock"; - private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock"; + private static final String SYNC_WAKE_LOCK = "*sync*"; + private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm"; private Context mContext; @@ -1700,10 +1701,12 @@ public class SyncManager implements OnAccountsUpdateListener { mActiveSyncContext.close(); mActiveSyncContext = null; mSyncStorageEngine.setActiveSync(mActiveSyncContext); + mSyncWakeLock.setWorkSource(null); runStateIdle(); return; } + mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterInfo.uid)); mSyncWakeLock.acquire(); // no need to schedule an alarm, as that will be done by our caller. diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index ef72013..4bd9bd9 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -192,7 +192,7 @@ public abstract class PackageManager { /** * Signature check result: this is returned by {@link #checkSignatures} - * if the two packages have a matching signature. + * if all signatures on the two packages match. */ public static final int SIGNATURE_MATCH = 0; @@ -204,25 +204,25 @@ public abstract class PackageManager { /** * Signature check result: this is returned by {@link #checkSignatures} - * if the first package is not signed, but the second is. + * if the first package is not signed but the second is. */ public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; /** * Signature check result: this is returned by {@link #checkSignatures} - * if the second package is not signed, but the first is. + * if the second package is not signed but the first is. */ public static final int SIGNATURE_SECOND_NOT_SIGNED = -2; /** * Signature check result: this is returned by {@link #checkSignatures} - * if both packages are signed but there is no matching signature. + * if not all signatures on both packages match. */ public static final int SIGNATURE_NO_MATCH = -3; /** * Signature check result: this is returned by {@link #checkSignatures} - * if either of the given package names are not valid. + * if either of the packages are not valid. */ public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; @@ -1230,20 +1230,14 @@ public abstract class PackageManager { * * @param pkg1 First package name whose signature will be compared. * @param pkg2 Second package name whose signature will be compared. - * @return Returns an integer indicating whether there is a matching - * signature: the value is >= 0 if there is a match (or neither package - * is signed), or < 0 if there is not a match. The match result can be - * further distinguished with the success (>= 0) constants - * {@link #SIGNATURE_MATCH}, {@link #SIGNATURE_NEITHER_SIGNED}; or - * failure (< 0) constants {@link #SIGNATURE_FIRST_NOT_SIGNED}, - * {@link #SIGNATURE_SECOND_NOT_SIGNED}, {@link #SIGNATURE_NO_MATCH}, - * or {@link #SIGNATURE_UNKNOWN_PACKAGE}. + * + * @return Returns an integer indicating whether all signatures on the + * two packages match. The value is >= 0 ({@link #SIGNATURE_MATCH}) if + * all signatures match or < 0 if there is not a match ({@link + * #SIGNATURE_NO_MATCH} or {@link #SIGNATURE_UNKNOWN_PACKAGE}). * * @see #checkSignatures(int, int) * @see #SIGNATURE_MATCH - * @see #SIGNATURE_NEITHER_SIGNED - * @see #SIGNATURE_FIRST_NOT_SIGNED - * @see #SIGNATURE_SECOND_NOT_SIGNED * @see #SIGNATURE_NO_MATCH * @see #SIGNATURE_UNKNOWN_PACKAGE */ @@ -1258,20 +1252,14 @@ public abstract class PackageManager { * * @param uid1 First UID whose signature will be compared. * @param uid2 Second UID whose signature will be compared. - * @return Returns an integer indicating whether there is a matching - * signature: the value is >= 0 if there is a match (or neither package - * is signed), or < 0 if there is not a match. The match result can be - * further distinguished with the success (>= 0) constants - * {@link #SIGNATURE_MATCH}, {@link #SIGNATURE_NEITHER_SIGNED}; or - * failure (< 0) constants {@link #SIGNATURE_FIRST_NOT_SIGNED}, - * {@link #SIGNATURE_SECOND_NOT_SIGNED}, {@link #SIGNATURE_NO_MATCH}, - * or {@link #SIGNATURE_UNKNOWN_PACKAGE}. * - * @see #checkSignatures(int, int) + * @return Returns an integer indicating whether all signatures on the + * two packages match. The value is >= 0 ({@link #SIGNATURE_MATCH}) if + * all signatures match or < 0 if there is not a match ({@link + * #SIGNATURE_NO_MATCH} or {@link #SIGNATURE_UNKNOWN_PACKAGE}). + * + * @see #checkSignatures(String, String) * @see #SIGNATURE_MATCH - * @see #SIGNATURE_NEITHER_SIGNED - * @see #SIGNATURE_FIRST_NOT_SIGNED - * @see #SIGNATURE_SECOND_NOT_SIGNED * @see #SIGNATURE_NO_MATCH * @see #SIGNATURE_UNKNOWN_PACKAGE */ diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index 9166019..b822b27 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -49,7 +49,6 @@ import javax.net.ssl.X509TrustManager; import org.apache.harmony.xnet.provider.jsse.OpenSSLContextImpl; import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl; import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; -import org.apache.harmony.xnet.provider.jsse.SSLParameters; /** * SSLSocketFactory implementation with several extra features: @@ -211,7 +210,8 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) { try { OpenSSLContextImpl sslContext = new OpenSSLContextImpl(); - sslContext.engineInit(null, trustManagers, null, mSessionCache, null); + sslContext.engineInit(null, trustManagers, null); + sslContext.engineGetClientSessionContext().setPersistentCache(mSessionCache); return sslContext.engineGetSocketFactory(); } catch (KeyManagementException e) { Log.wtf(TAG, e); diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java index c527fe4..4ca5903 100644 --- a/core/java/android/net/http/CertificateChainValidator.java +++ b/core/java/android/net/http/CertificateChainValidator.java @@ -19,7 +19,7 @@ package android.net.http; import com.android.internal.net.DomainNameValidator; -import org.apache.harmony.xnet.provider.jsse.SSLParameters; +import org.apache.harmony.xnet.provider.jsse.SSLParametersImpl; import java.io.IOException; @@ -191,7 +191,7 @@ class CertificateChainValidator { // report back to the user. // try { - SSLParameters.getDefaultTrustManager().checkServerTrusted( + SSLParametersImpl.getDefaultTrustManager().checkServerTrusted( newServerCertificates, "RSA"); // no errors!!! diff --git a/core/java/android/net/http/HttpsConnection.java b/core/java/android/net/http/HttpsConnection.java index 8c9d013f..b361dca 100644 --- a/core/java/android/net/http/HttpsConnection.java +++ b/core/java/android/net/http/HttpsConnection.java @@ -98,7 +98,8 @@ public class HttpsConnection extends Connection { } }; - sslContext.engineInit(null, trustManagers, null, cache, null); + sslContext.engineInit(null, trustManagers, null); + sslContext.engineGetClientSessionContext().setPersistentCache(cache); synchronized (HttpsConnection.class) { mSslSocketFactory = sslContext.engineGetSocketFactory(); diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index d28148c..5fb1d7c 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -129,7 +129,7 @@ public abstract class AsyncTask<Params, Progress, Result> { private static final int CORE_POOL_SIZE = 5; private static final int MAXIMUM_POOL_SIZE = 128; - private static final int KEEP_ALIVE = 10; + private static final int KEEP_ALIVE = 1; private static final BlockingQueue<Runnable> sWorkQueue = new LinkedBlockingQueue<Runnable>(10); 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 abstract class BatteryStats implements Parcelable { 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 @@ public abstract class BatteryStats implements Parcelable { 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..0067e94 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 @@ public class PowerManager int mCount = 0; boolean mRefCounted = true; boolean mHeld = false; + WorkSource mWorkSource; WakeLock(int flags, String tag) { @@ -247,7 +248,7 @@ public class PowerManager 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 class PowerManager } } + 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/StrictMode.java b/core/java/android/os/StrictMode.java index 7f7b02b..f571c42 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -201,6 +201,25 @@ public final class StrictMode { } /** + * Enable DropBox logging for debug phone builds. + * + * @hide + */ + public static boolean conditionallyEnableDebugLogging() { + // For debug builds, log event loop stalls to dropbox for analysis. + // Similar logic also appears in ActivityThread.java for system apps. + if ("user".equals(Build.TYPE)) { + return false; + } + StrictMode.setThreadPolicy( + StrictMode.DISALLOW_DISK_WRITE | + StrictMode.DISALLOW_DISK_READ | + StrictMode.DISALLOW_NETWORK | + StrictMode.PENALTY_DROPBOX); + return true; + } + + /** * Parses the BlockGuard policy mask out from the Exception's * getMessage() String value. Kinda gross, but least * invasive. :/ 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..287c136 --- /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+1, uids1, i1, 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/provider/Calendar.java b/core/java/android/provider/Calendar.java index 9a09805..6355a9c 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/Calendar.java @@ -943,6 +943,80 @@ public final class Calendar { } /** + * CalendarCache stores some settings for calendar including the current + * time zone for the app. These settings are stored using a key/value + * scheme. + */ + public interface CalendarCacheColumns { + /** + * The key for the setting. Keys are defined in CalendarChache in the + * Calendar provider. + * TODO Add keys to this file + */ + public static final String KEY = "key"; + + /** + * The value of the given setting. + */ + public static final String VALUE = "value"; + } + + public static class CalendarCache implements CalendarCacheColumns { + /** + * The URI to use for retrieving the properties from the Calendar db. + */ + public static final Uri URI = + Uri.parse("content://" + AUTHORITY + "/properties"); + public static final String[] POJECTION = { KEY, VALUE }; + + /** + * If updating a property, this must be provided as the selection. All + * other selections will fail. For queries this field can be omitted to + * retrieve all properties or used to query a single property. Valid + * keys include {@link #TIMEZONE_KEY_TYPE}, + * {@link #TIMEZONE_KEY_INSTANCES}, and + * {@link #TIMEZONE_KEY_INSTANCES_PREVIOUS}, though the last one can + * only be read, not written. + */ + public static final String WHERE = "key=?"; + + /** + * They key for updating the use of auto/home time zones in Calendar. + * Valid values are {@link #TIMEZONE_TYPE_AUTO} or + * {@link #TIMEZONE_TYPE_HOME}. + */ + public static final String TIMEZONE_KEY_TYPE = "timezoneType"; + + /** + * The key for updating the time zone used by the provider when it + * generates the instances table. This should only be written if the + * type is set to {@link #TIMEZONE_TYPE_HOME}. A valid time zone id + * should be written to this field. + */ + public static final String TIMEZONE_KEY_INSTANCES = "timezoneInstances"; + + /** + * The key for reading the last time zone set by the user. This should + * only be read by apps and it will be automatically updated whenever + * {@link #TIMEZONE_KEY_INSTANCES} is updated with + * {@link #TIMEZONE_TYPE_HOME} set. + */ + public static final String TIMEZONE_KEY_INSTANCES_PREVIOUS = "timezoneInstancesPrevious"; + + /** + * The value to write to {@link #TIMEZONE_KEY_TYPE} if the provider + * should stay in sync with the device's time zone. + */ + public static final String TIMEZONE_TYPE_AUTO = "auto"; + + /** + * The value to write to {@link #TIMEZONE_KEY_TYPE} if the provider + * should use a fixed time zone set by the user. + */ + public static final String TIMEZONE_TYPE_HOME = "home"; + } + + /** * A few Calendar globals are needed in the CalendarProvider for expanding * the Instances table and these are all stored in the first (and only) * row of the CalendarMetaData table. diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 094258b..e05fe7b 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -52,22 +52,14 @@ class BluetoothEventLoop { private final BluetoothAdapter mAdapter; private final Context mContext; - private static final int EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 1; - private static final int EVENT_RESTART_BLUETOOTH = 2; - private static final int EVENT_PAIRING_CONSENT_DELAYED_ACCEPT = 3; - private static final int EVENT_AGENT_CANCEL = 4; + private static final int EVENT_RESTART_BLUETOOTH = 1; + private static final int EVENT_PAIRING_CONSENT_DELAYED_ACCEPT = 2; + private static final int EVENT_AGENT_CANCEL = 3; private static final int CREATE_DEVICE_ALREADY_EXISTS = 1; private static final int CREATE_DEVICE_SUCCESS = 0; private static final int CREATE_DEVICE_FAILED = -1; - // The time (in millisecs) to delay the pairing attempt after the first - // auto pairing attempt fails. We use an exponential delay with - // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and - // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value. - private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000; - private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000; - private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; @@ -76,13 +68,6 @@ class BluetoothEventLoop { public void handleMessage(Message msg) { String address = null; switch (msg.what) { - case EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY: - address = (String)msg.obj; - if (address != null) { - mBluetoothService.createBond(address); - return; - } - break; case EVENT_RESTART_BLUETOOTH: mBluetoothService.restart(); break; @@ -95,8 +80,7 @@ class BluetoothEventLoop { case EVENT_AGENT_CANCEL: // Set the Bond State to BOND_NONE. // We always have only 1 device in BONDING state. - String[] devices = - mBluetoothService.getBondState().listInState(BluetoothDevice.BOND_BONDING); + String[] devices = mBluetoothService.listInState(BluetoothDevice.BOND_BONDING); if (devices.length == 0) { break; } else if (devices.length > 1) { @@ -104,7 +88,7 @@ class BluetoothEventLoop { break; } address = devices[0]; - mBluetoothService.getBondState().setBondState(address, + mBluetoothService.setBondState(address, BluetoothDevice.BOND_NONE, BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED); break; @@ -115,7 +99,7 @@ class BluetoothEventLoop { static { classInitNative(); } private static native void classInitNative(); - /* pacakge */ BluetoothEventLoop(Context context, BluetoothAdapter adapter, + /* package */ BluetoothEventLoop(Context context, BluetoothAdapter adapter, BluetoothService bluetoothService) { mBluetoothService = bluetoothService; mContext = context; @@ -209,55 +193,7 @@ class BluetoothEventLoop { private void onCreatePairedDeviceResult(String address, int result) { address = address.toUpperCase(); - if (result == BluetoothDevice.BOND_SUCCESS) { - mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED); - if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) { - mBluetoothService.getBondState().clearPinAttempts(address); - } - } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED && - mBluetoothService.getBondState().getAttempt(address) == 1) { - mBluetoothService.getBondState().addAutoPairingFailure(address); - pairingAttempt(address, result); - } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN && - mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) { - pairingAttempt(address, result); - } else { - mBluetoothService.getBondState().setBondState(address, - BluetoothDevice.BOND_NONE, result); - if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) { - mBluetoothService.getBondState().clearPinAttempts(address); - } - } - } - - private void pairingAttempt(String address, int result) { - // This happens when our initial guess of "0000" as the pass key - // fails. Try to create the bond again and display the pin dialog - // to the user. Use back-off while posting the delayed - // message. The initial value is - // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is - // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is - // reached, display an error to the user. - int attempt = mBluetoothService.getBondState().getAttempt(address); - if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY > - MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) { - mBluetoothService.getBondState().clearPinAttempts(address); - mBluetoothService.getBondState().setBondState(address, - BluetoothDevice.BOND_NONE, result); - return; - } - - Message message = mHandler.obtainMessage(EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY); - message.obj = address; - boolean postResult = mHandler.sendMessageDelayed(message, - attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY); - if (!postResult) { - mBluetoothService.getBondState().clearPinAttempts(address); - mBluetoothService.getBondState().setBondState(address, - BluetoothDevice.BOND_NONE, result); - return; - } - mBluetoothService.getBondState().attempt(address); + mBluetoothService.onCreatePairedDeviceResult(address, result); } private void onDeviceCreated(String deviceObjectPath) { @@ -275,8 +211,8 @@ class BluetoothEventLoop { private void onDeviceRemoved(String deviceObjectPath) { String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath); if (address != null) { - mBluetoothService.getBondState().setBondState(address.toUpperCase(), - BluetoothDevice.BOND_NONE, BluetoothDevice.UNBOND_REASON_REMOVED); + mBluetoothService.setBondState(address.toUpperCase(), BluetoothDevice.BOND_NONE, + BluetoothDevice.UNBOND_REASON_REMOVED); mBluetoothService.setRemoteDeviceProperty(address, "UUIDs", null); } } @@ -407,13 +343,11 @@ class BluetoothEventLoop { // If locally initiated pairing, we will // not go to BOND_BONDED state until we have received a // successful return value in onCreatePairedDeviceResult - if (null == mBluetoothService.getBondState().getPendingOutgoingBonding()) { - mBluetoothService.getBondState().setBondState(address, - BluetoothDevice.BOND_BONDED); + if (null == mBluetoothService.getPendingOutgoingBonding()) { + mBluetoothService.setBondState(address, BluetoothDevice.BOND_BONDED); } } else { - mBluetoothService.getBondState().setBondState(address, - BluetoothDevice.BOND_NONE); + mBluetoothService.setBondState(address, BluetoothDevice.BOND_NONE); mBluetoothService.setRemoteDeviceProperty(address, "Trusted", "false"); } } else if (name.equals("Trusted")) { @@ -443,8 +377,8 @@ class BluetoothEventLoop { // Also set it only when the state is not already Bonded, we can sometimes // get an authorization request from the remote end if it doesn't have the link key // while we still have it. - if (mBluetoothService.getBondState().getBondState(address) != BluetoothDevice.BOND_BONDED) - mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDING); + if (mBluetoothService.getBondState(address) != BluetoothDevice.BOND_BONDED) + mBluetoothService.setBondState(address, BluetoothDevice.BOND_BONDING); return address; } @@ -458,7 +392,7 @@ class BluetoothEventLoop { * so we may get this request many times. Also if we respond immediately, * the other end is unable to handle it. Delay sending the message. */ - if (mBluetoothService.getBondState().getBondState(address) == BluetoothDevice.BOND_BONDED) { + if (mBluetoothService.getBondState(address) == BluetoothDevice.BOND_BONDED) { Message message = mHandler.obtainMessage(EVENT_PAIRING_CONSENT_DELAYED_ACCEPT); message.obj = address; mHandler.sendMessageDelayed(message, 1500); @@ -503,7 +437,7 @@ class BluetoothEventLoop { if (address == null) return; String pendingOutgoingAddress = - mBluetoothService.getBondState().getPendingOutgoingBonding(); + mBluetoothService.getPendingOutgoingBonding(); if (address.equals(pendingOutgoingAddress)) { // we initiated the bonding @@ -524,12 +458,7 @@ class BluetoothEventLoop { case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO: case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: - if (!mBluetoothService.getBondState().hasAutoPairingFailed(address) && - !mBluetoothService.getBondState().isAutoPairingBlacklisted(address)) { - mBluetoothService.getBondState().attempt(address); - mBluetoothService.setPin(address, BluetoothDevice.convertPinToBytes("0000")); - return; - } + if (mBluetoothService.attemptAutoPair(address)) return; } } Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST); @@ -551,6 +480,17 @@ class BluetoothEventLoop { mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); } + 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) { String address = mBluetoothService.getAddressFromObjectPath(objectPath); if (address == null) { @@ -583,7 +523,21 @@ class BluetoothEventLoop { return authorized; } - boolean isOtherSinkInNonDisconnectingState(String address) { + 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(); if (devices.size() == 0) return false; diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index b2aa04d..dfe3a25 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -50,6 +50,7 @@ import android.os.ServiceManager; import android.os.SystemService; import android.provider.Settings; import android.util.Log; +import android.util.Pair; import com.android.internal.app.IBatteryStats; @@ -103,6 +104,14 @@ public class BluetoothService extends IBluetooth.Stub { private static final int MESSAGE_FINISH_DISABLE = 2; private static final int MESSAGE_UUID_INTENT = 3; private static final int MESSAGE_DISCOVERABLE_TIMEOUT = 4; + private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 5; + + // The time (in millisecs) to delay the pairing attempt after the first + // auto pairing attempt fails. We use an exponential delay with + // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and + // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value. + private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000; + private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000; // The timeout used to sent the UUIDs Intent // This timeout should be greater than the page timeout @@ -129,6 +138,8 @@ public class BluetoothService extends IBluetooth.Stub { private final BluetoothProfileState mHfpProfileState; private BluetoothA2dpService mA2dpService; + private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData; + private static String mDockAddress; private String mDockPin; @@ -183,6 +194,7 @@ public class BluetoothService extends IBluetooth.Stub { 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>(); @@ -498,6 +510,13 @@ public class BluetoothService extends IBluetooth.Stub { setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, -1); } break; + case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY: + address = (String)msg.obj; + if (address != null) { + createBond(address); + return; + } + break; } } }; @@ -587,8 +606,68 @@ public class BluetoothService extends IBluetooth.Stub { Binder.restoreCallingIdentity(origCallerIdentityToken); } - /* package */ BondState getBondState() { - return mBondState; + /*package*/ synchronized boolean attemptAutoPair(String address) { + if (!mBondState.hasAutoPairingFailed(address) && + !mBondState.isAutoPairingBlacklisted(address)) { + mBondState.attempt(address); + setPin(address, BluetoothDevice.convertPinToBytes("0000")); + return true; + } + return false; + } + + /*package*/ synchronized void onCreatePairedDeviceResult(String address, int result) { + if (result == BluetoothDevice.BOND_SUCCESS) { + setBondState(address, BluetoothDevice.BOND_BONDED); + if (mBondState.isAutoPairingAttemptsInProgress(address)) { + mBondState.clearPinAttempts(address); + } + } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED && + mBondState.getAttempt(address) == 1) { + mBondState.addAutoPairingFailure(address); + pairingAttempt(address, result); + } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN && + mBondState.isAutoPairingAttemptsInProgress(address)) { + pairingAttempt(address, result); + } else { + setBondState(address, BluetoothDevice.BOND_NONE, result); + if (mBondState.isAutoPairingAttemptsInProgress(address)) { + mBondState.clearPinAttempts(address); + } + } + } + + /*package*/ synchronized String getPendingOutgoingBonding() { + return mBondState.getPendingOutgoingBonding(); + } + + private void pairingAttempt(String address, int result) { + // This happens when our initial guess of "0000" as the pass key + // fails. Try to create the bond again and display the pin dialog + // to the user. Use back-off while posting the delayed + // message. The initial value is + // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is + // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is + // reached, display an error to the user. + int attempt = mBondState.getAttempt(address); + if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY > + MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) { + mBondState.clearPinAttempts(address); + setBondState(address, BluetoothDevice.BOND_NONE, result); + return; + } + + Message message = mHandler.obtainMessage(MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY); + message.obj = address; + boolean postResult = mHandler.sendMessageDelayed(message, + attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY); + if (!postResult) { + mBondState.clearPinAttempts(address); + setBondState(address, + BluetoothDevice.BOND_NONE, result); + return; + } + mBondState.attempt(address); } /** local cache of bonding state. @@ -1119,7 +1198,7 @@ public class BluetoothService extends IBluetooth.Stub { 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; @@ -1149,8 +1228,13 @@ public class BluetoothService extends IBluetooth.Stub { return false; } } + return true; + } + + public synchronized boolean createBond(String address) { + if (!isBondingFeasible(address)) return false; - if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) { + if (!createPairedDeviceNative(address, 60000 /*1 minute*/ )) { return false; } @@ -1160,6 +1244,51 @@ public class BluetoothService extends IBluetooth.Stub { 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"); @@ -1205,6 +1334,10 @@ public class BluetoothService extends IBluetooth.Stub { return mBondState.listInState(BluetoothDevice.BOND_BONDED); } + /*package*/ synchronized String[] listInState(int state) { + return mBondState.listInState(state); + } + public synchronized int getBondState(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (!BluetoothAdapter.checkBluetoothAddress(address)) { @@ -1213,6 +1346,15 @@ public class BluetoothService extends IBluetooth.Stub { return mBondState.getBondState(address.toUpperCase()); } + /*package*/ synchronized boolean setBondState(String address, int state) { + return setBondState(address, state, 0); + } + + /*package*/ synchronized boolean setBondState(String address, int state, int reason) { + mBondState.setBondState(address.toUpperCase(), state); + return true; + } + public synchronized boolean isBluetoothDock(String address) { SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, mContext.MODE_PRIVATE); @@ -1551,6 +1693,32 @@ public class BluetoothService extends IBluetooth.Stub { 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"); @@ -2084,6 +2252,9 @@ public class BluetoothService extends IBluetooth.Stub { 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, @@ -2094,6 +2265,9 @@ public class BluetoothService extends IBluetooth.Stub { 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/util/CalendarUtils.java b/core/java/android/util/CalendarUtils.java new file mode 100644 index 0000000..9a4a67d --- /dev/null +++ b/core/java/android/util/CalendarUtils.java @@ -0,0 +1,332 @@ +/* + * 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 android.util; + +import android.content.AsyncQueryHandler; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.provider.Calendar.CalendarCache; +import android.text.TextUtils; +import android.text.format.DateUtils; +import android.text.format.Time; + +import java.util.Formatter; +import java.util.HashSet; +import java.util.Locale; + +/** + * A class containing utility methods related to Calendar apps. + * + * @hide + */ +public class CalendarUtils { + private static final boolean DEBUG = false; + private static final String TAG = "CalendarUtils"; + + /** + * This class contains methods specific to reading and writing time zone + * values. + */ + public static class TimeZoneUtils { + private static final String[] TIMEZONE_TYPE_ARGS = { CalendarCache.TIMEZONE_KEY_TYPE }; + private static final String[] TIMEZONE_INSTANCES_ARGS = + { CalendarCache.TIMEZONE_KEY_INSTANCES }; + + private static StringBuilder mSB = new StringBuilder(50); + private static Formatter mF = new Formatter(mSB, Locale.getDefault()); + private volatile static boolean mFirstTZRequest = true; + private volatile static boolean mTZQueryInProgress = false; + + private volatile static boolean mUseHomeTZ = false; + private volatile static String mHomeTZ = Time.getCurrentTimezone(); + + private static HashSet<Runnable> mTZCallbacks = new HashSet<Runnable>(); + private static int mToken = 1; + private static AsyncTZHandler mHandler; + + // The name of the shared preferences file. This name must be maintained for historical + // reasons, as it's what PreferenceManager assigned the first time the file was created. + private final String mPrefsName; + + /** + * This is the key used for writing whether or not a home time zone should + * be used in the Calendar app to the Calendar Preferences. + */ + public static final String KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled"; + /** + * This is the key used for writing the time zone that should be used if + * home time zones are enabled for the Calendar app. + */ + public static final String KEY_HOME_TZ = "preferences_home_tz"; + + /** + * This is a helper class for handling the async queries and updates for the + * time zone settings in Calendar. + */ + private class AsyncTZHandler extends AsyncQueryHandler { + public AsyncTZHandler(ContentResolver cr) { + super(cr); + } + + @Override + protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + synchronized (mTZCallbacks) { + boolean writePrefs = false; + // Check the values in the db + int keyColumn = cursor.getColumnIndexOrThrow(CalendarCache.KEY); + int valueColumn = cursor.getColumnIndexOrThrow(CalendarCache.VALUE); + while(cursor.moveToNext()) { + String key = cursor.getString(keyColumn); + String value = cursor.getString(valueColumn); + if (TextUtils.equals(key, CalendarCache.TIMEZONE_KEY_TYPE)) { + boolean useHomeTZ = !TextUtils.equals( + value, CalendarCache.TIMEZONE_TYPE_AUTO); + if (useHomeTZ != mUseHomeTZ) { + writePrefs = true; + mUseHomeTZ = useHomeTZ; + } + } else if (TextUtils.equals( + key, CalendarCache.TIMEZONE_KEY_INSTANCES_PREVIOUS)) { + if (!TextUtils.isEmpty(value) && !TextUtils.equals(mHomeTZ, value)) { + writePrefs = true; + mHomeTZ = value; + } + } + } + if (writePrefs) { + SharedPreferences prefs = getSharedPreferences((Context)cookie, mPrefsName); + // Write the prefs + setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ); + setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ); + } + + mTZQueryInProgress = false; + for (Runnable callback : mTZCallbacks) { + if (callback != null) { + callback.run(); + } + } + mTZCallbacks.clear(); + } + } + } + + /** + * The name of the file where the shared prefs for Calendar are stored + * must be provided. All activities within an app should provide the + * same preferences name or behavior may become erratic. + * + * @param prefsName + */ + public TimeZoneUtils(String prefsName) { + mPrefsName = prefsName; + } + + /** + * Formats a date or a time range according to the local conventions. + * + * This formats a date/time range using Calendar's time zone and the + * local conventions for the region of the device. + * + * @param context the context is required only if the time is shown + * @param startMillis the start time in UTC milliseconds + * @param endMillis the end time in UTC milliseconds + * @param flags a bit mask of options See + * {@link DateUtils#formatDateRange(Context, Formatter, long, long, int, String) formatDateRange} + * @return a string containing the formatted date/time range. + */ + public String formatDateRange(Context context, long startMillis, + long endMillis, int flags) { + String date; + synchronized (mSB) { + mSB.setLength(0); + date = DateUtils.formatDateRange(context, mF, startMillis, endMillis, flags, + getTimeZone(context, null)).toString(); + } + return date; + } + + /** + * Writes a new home time zone to the db. + * + * Updates the home time zone in the db asynchronously and updates + * the local cache. Sending a time zone of + * {@link CalendarCache#TIMEZONE_TYPE_AUTO} will cause it to be set + * to the device's time zone. null or empty tz will be ignored. + * + * @param context The calling activity + * @param timeZone The time zone to set Calendar to, or + * {@link CalendarCache#TIMEZONE_TYPE_AUTO} + */ + public void setTimeZone(Context context, String timeZone) { + if (TextUtils.isEmpty(timeZone)) { + if (DEBUG) { + Log.d(TAG, "Empty time zone, nothing to be done."); + } + return; + } + boolean updatePrefs = false; + synchronized (mTZCallbacks) { + if (CalendarCache.TIMEZONE_TYPE_AUTO.equals(timeZone)) { + if (mUseHomeTZ) { + updatePrefs = true; + } + mUseHomeTZ = false; + } else { + if (!mUseHomeTZ || !TextUtils.equals(mHomeTZ, timeZone)) { + updatePrefs = true; + } + mUseHomeTZ = true; + mHomeTZ = timeZone; + } + } + if (updatePrefs) { + // Write the prefs + SharedPreferences prefs = getSharedPreferences(context, mPrefsName); + setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ); + setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ); + + // Update the db + ContentValues values = new ContentValues(); + if (mHandler == null) { + mHandler = new AsyncTZHandler(context.getContentResolver()); + } + + mHandler.cancelOperation(mToken); + + // skip 0 so query can use it + if (++mToken == 0) { + mToken = 1; + } + + // Write the use home tz setting + values.put(CalendarCache.VALUE, mUseHomeTZ ? CalendarCache.TIMEZONE_TYPE_HOME + : CalendarCache.TIMEZONE_TYPE_AUTO); + mHandler.startUpdate(mToken, null, CalendarCache.URI, values, CalendarCache.WHERE, + TIMEZONE_TYPE_ARGS); + + // If using a home tz write it to the db + if (mUseHomeTZ) { + ContentValues values2 = new ContentValues(); + values2.put(CalendarCache.VALUE, mHomeTZ); + mHandler.startUpdate(mToken, null, CalendarCache.URI, values2, + CalendarCache.WHERE, TIMEZONE_INSTANCES_ARGS); + } + } + } + + /** + * Gets the time zone that Calendar should be displayed in + * + * This is a helper method to get the appropriate time zone for Calendar. If this + * is the first time this method has been called it will initiate an asynchronous + * query to verify that the data in preferences is correct. The callback supplied + * will only be called if this query returns a value other than what is stored in + * preferences and should cause the calling activity to refresh anything that + * depends on calling this method. + * + * @param context The calling activity + * @param callback The runnable that should execute if a query returns new values + * @return The string value representing the time zone Calendar should display + */ + public String getTimeZone(Context context, Runnable callback) { + synchronized (mTZCallbacks){ + if (mFirstTZRequest) { + mTZQueryInProgress = true; + mFirstTZRequest = false; + + SharedPreferences prefs = getSharedPreferences(context, mPrefsName); + mUseHomeTZ = prefs.getBoolean(KEY_HOME_TZ_ENABLED, false); + mHomeTZ = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone()); + + // When the async query returns it should synchronize on + // mTZCallbacks, update mUseHomeTZ, mHomeTZ, and the + // preferences, set mTZQueryInProgress to false, and call all + // the runnables in mTZCallbacks. + if (mHandler == null) { + mHandler = new AsyncTZHandler(context.getContentResolver()); + } + mHandler.startQuery(0, context, CalendarCache.URI, CalendarCache.POJECTION, + null, null, null); + } + if (mTZQueryInProgress) { + mTZCallbacks.add(callback); + } + } + return mUseHomeTZ ? mHomeTZ : Time.getCurrentTimezone(); + } + + /** + * Forces a query of the database to check for changes to the time zone. + * This should be called if another app may have modified the db. If a + * query is already in progress the callback will be added to the list + * of callbacks to be called when it returns. + * + * @param context The calling activity + * @param callback The runnable that should execute if a query returns + * new values + */ + public void forceDBRequery(Context context, Runnable callback) { + synchronized (mTZCallbacks){ + if (mTZQueryInProgress) { + mTZCallbacks.add(callback); + return; + } + mFirstTZRequest = true; + getTimeZone(context, callback); + } + } + } + + /** + * A helper method for writing a String value to the preferences + * asynchronously. + * + * @param context A context with access to the correct preferences + * @param key The preference to write to + * @param value The value to write + */ + public static void setSharedPreference(SharedPreferences prefs, String key, String value) { +// SharedPreferences prefs = getSharedPreferences(context); + SharedPreferences.Editor editor = prefs.edit(); + editor.putString(key, value); + editor.apply(); + } + + /** + * A helper method for writing a boolean value to the preferences + * asynchronously. + * + * @param context A context with access to the correct preferences + * @param key The preference to write to + * @param value The value to write + */ + public static void setSharedPreference(SharedPreferences prefs, String key, boolean value) { +// SharedPreferences prefs = getSharedPreferences(context, prefsName); + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean(key, value); + editor.apply(); + } + + /** Return a properly configured SharedPreferences instance */ + public static SharedPreferences getSharedPreferences(Context context, String prefsName) { + return context.getSharedPreferences(prefsName, Context.MODE_PRIVATE); + } +} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index fe003a4..b794a6a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1582,7 +1582,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * {@link #OVERSCROLL_ALWAYS}, {@link #OVERSCROLL_IF_CONTENT_SCROLLS}, * and {@link #OVERSCROLL_NEVER}. */ - private int mOverscrollMode = OVERSCROLL_ALWAYS; + private int mOverscrollMode; /** * The parent this view is attached to. @@ -1876,6 +1876,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility // Used for debug only //++sInstanceCount; mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + setOverscrollMode(OVERSCROLL_ALWAYS); } /** diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java index e766693..1f8d53f 100644 --- a/core/java/android/webkit/JWebCoreJavaBridge.java +++ b/core/java/android/webkit/JWebCoreJavaBridge.java @@ -20,6 +20,8 @@ import android.os.Handler; import android.os.Message; import android.util.Log; +import java.lang.ref.WeakReference; +import java.util.HashMap; import java.util.Set; final class JWebCoreJavaBridge extends Handler { @@ -44,7 +46,8 @@ final class JWebCoreJavaBridge extends Handler { // keep track of the main WebView attached to the current window so that we // can get the proper Context. - private WebView mCurrentMainWebView; + private static WeakReference<WebView> sCurrentMainWebView = + new WeakReference<WebView>(null); /* package */ static final int REFRESH_PLUGINS = 100; @@ -62,20 +65,20 @@ final class JWebCoreJavaBridge extends Handler { nativeFinalize(); } - synchronized void setActiveWebView(WebView webview) { - if (mCurrentMainWebView != null) { + static synchronized void setActiveWebView(WebView webview) { + if (sCurrentMainWebView.get() != null) { // it is possible if there is a sub-WebView. Do nothing. return; } - mCurrentMainWebView = webview; + sCurrentMainWebView = new WeakReference<WebView>(webview); } - synchronized void removeActiveWebView(WebView webview) { - if (mCurrentMainWebView != webview) { + static synchronized void removeActiveWebView(WebView webview) { + if (sCurrentMainWebView.get() != webview) { // it is possible if there is a sub-WebView. Do nothing. return; } - mCurrentMainWebView = null; + sCurrentMainWebView.clear(); } /** @@ -256,11 +259,12 @@ final class JWebCoreJavaBridge extends Handler { synchronized private String getSignedPublicKey(int index, String challenge, String url) { - if (mCurrentMainWebView != null) { + WebView current = sCurrentMainWebView.get(); + if (current != null) { // generateKeyPair expects organizations which we don't have. Ignore // url. return CertTool.getSignedPublicKey( - mCurrentMainWebView.getContext(), index, challenge); + current.getContext(), index, challenge); } else { Log.e(LOGTAG, "There is no active WebView for getSignedPublicKey"); return ""; diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 4bb11bb..456e0d9 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -4502,9 +4502,9 @@ public class WebView extends AbsoluteLayout public void onWindowFocusChanged(boolean hasWindowFocus) { setActive(hasWindowFocus); if (hasWindowFocus) { - BrowserFrame.sJavaBridge.setActiveWebView(this); + JWebCoreJavaBridge.setActiveWebView(this); } else { - BrowserFrame.sJavaBridge.removeActiveWebView(this); + JWebCoreJavaBridge.removeActiveWebView(this); } super.onWindowFocusChanged(hasWindowFocus); } 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 @@ package com.android.internal.app; import com.android.internal.os.BatteryStatsImpl; +import android.os.WorkSource; import android.telephony.SignalStrength; interface IBatteryStats { @@ -33,6 +34,9 @@ interface IBatteryStats { 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 @@ interface IBatteryStats { 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 0ef1ea5..753dbf0 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.ParcelFormatException; 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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*"; @@ -1171,7 +1172,7 @@ public final class BatteryStatsImpl extends BatteryStats { // 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 @@ -1187,6 +1188,10 @@ public final class BatteryStatsImpl extends BatteryStats { 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. @@ -1327,6 +1332,20 @@ public final class BatteryStatsImpl extends BatteryStats { } } + 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); @@ -1947,6 +1966,48 @@ public final class BatteryStatsImpl extends BatteryStats { 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_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 2517a8a..6aa77f6 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -28,7 +28,7 @@ #include <surfaceflinger/Surface.h> #include <ui/egl/android_natives.h> #include <ui/InputTransport.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> #include "JNIHelp.h" #include "android_os_MessageQueue.h" @@ -128,17 +128,17 @@ AInputQueue::~AInputQueue() { } void AInputQueue::attachLooper(ALooper* looper, int ident, - ALooper_callbackFunc* callback, void* data) { - mPollLoop = static_cast<android::PollLoop*>(looper); - mPollLoop->setLooperCallback(mConsumer.getChannel()->getReceivePipeFd(), - ident, POLLIN, callback, data); - mPollLoop->setLooperCallback(mDispatchKeyRead, - ident, POLLIN, callback, data); + ALooper_callbackFunc callback, void* data) { + mLooper = static_cast<android::Looper*>(looper); + mLooper->addFd(mConsumer.getChannel()->getReceivePipeFd(), + ident, ALOOPER_EVENT_INPUT, callback, data); + mLooper->addFd(mDispatchKeyRead, + ident, ALOOPER_EVENT_INPUT, callback, data); } void AInputQueue::detachLooper() { - mPollLoop->removeCallback(mConsumer.getChannel()->getReceivePipeFd()); - mPollLoop->removeCallback(mDispatchKeyRead); + mLooper->removeFd(mConsumer.getChannel()->getReceivePipeFd()); + mLooper->removeFd(mDispatchKeyRead); } int32_t AInputQueue::hasEvents() { @@ -440,8 +440,8 @@ struct NativeCode : public ANativeActivity { if (env != NULL && clazz != NULL) { env->DeleteGlobalRef(clazz); } - if (pollLoop != NULL && mainWorkRead >= 0) { - pollLoop->removeCallback(mainWorkRead); + if (looper != NULL && mainWorkRead >= 0) { + looper->removeFd(mainWorkRead); } if (nativeInputQueue != NULL) { nativeInputQueue->mWorkWrite = -1; @@ -509,7 +509,7 @@ struct NativeCode : public ANativeActivity { // These are used to wake up the main thread to process work. int mainWorkRead; int mainWorkWrite; - sp<PollLoop> pollLoop; + sp<Looper> looper; }; void android_NativeActivity_setWindowFormat( @@ -541,15 +541,15 @@ void android_NativeActivity_hideSoftInput( /* * Callback for handling native events on the application's main thread. */ -static bool mainWorkCallback(int fd, int events, void* data) { +static int mainWorkCallback(int fd, int events, void* data) { NativeCode* code = (NativeCode*)data; if ((events & POLLIN) == 0) { - return true; + return 1; } ActivityWork work; if (!read_work(code->mainWorkRead, &work)) { - return true; + return 1; } LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd); @@ -593,7 +593,7 @@ static bool mainWorkCallback(int fd, int events, void* data) { break; } - return true; + return 1; } // ------------------------------------------------------------------------ @@ -621,9 +621,9 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQ return 0; } - code->pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueue); - if (code->pollLoop == NULL) { - LOGW("Unable to retrieve MessageQueue's PollLoop"); + code->looper = android_os_MessageQueue_getLooper(env, messageQueue); + if (code->looper == NULL) { + LOGW("Unable to retrieve MessageQueue's Looper"); delete code; return 0; } @@ -642,7 +642,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQ result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK); SLOGW_IF(result != 0, "Could not make main work write pipe " "non-blocking: %s", strerror(errno)); - code->pollLoop->setCallback(code->mainWorkRead, POLLIN, mainWorkCallback, code); + code->looper->addFd(code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code); code->ANativeActivity::callbacks = &code->callbacks; if (env->GetJavaVM(&code->vm) < 0) { diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index e29495c..10ceb7b 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -60,7 +60,7 @@ sensors_module_get_next_sensor(JNIEnv *env, jobject clazz, jobject sensor, jint Sensor const* const* sensorList; size_t count = mgr.getSensorList(&sensorList); - if (next >= count) + if (size_t(next) >= count) return -1; Sensor const* const list = sensorList[next]; @@ -78,7 +78,7 @@ sensors_module_get_next_sensor(JNIEnv *env, jobject clazz, jobject sensor, jint env->SetIntField(sensor, sensorOffsets.minDelay, list->getMinDelay()); next++; - return next<count ? next : 0; + return size_t(next) < count ? next : 0; } //---------------------------------------------------------------------------- diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp index 847b5a5..1b203ca 100644 --- a/core/jni/android_os_MessageQueue.cpp +++ b/core/jni/android_os_MessageQueue.cpp @@ -18,7 +18,7 @@ #include "JNIHelp.h" -#include <utils/PollLoop.h> +#include <utils/Looper.h> #include <utils/Log.h> #include "android_os_MessageQueue.h" @@ -39,22 +39,22 @@ public: NativeMessageQueue(); ~NativeMessageQueue(); - inline sp<PollLoop> getPollLoop() { return mPollLoop; } + inline sp<Looper> getLooper() { return mLooper; } bool pollOnce(int timeoutMillis); void wake(); private: - sp<PollLoop> mPollLoop; + sp<Looper> mLooper; }; // ---------------------------------------------------------------------------- NativeMessageQueue::NativeMessageQueue() { - mPollLoop = PollLoop::getForThread(); - if (mPollLoop == NULL) { - mPollLoop = new PollLoop(false); - PollLoop::setForThread(mPollLoop); + mLooper = Looper::getForThread(); + if (mLooper == NULL) { + mLooper = new Looper(false); + Looper::setForThread(mLooper); } } @@ -62,11 +62,11 @@ NativeMessageQueue::~NativeMessageQueue() { } bool NativeMessageQueue::pollOnce(int timeoutMillis) { - return mPollLoop->pollOnce(timeoutMillis) != PollLoop::POLL_TIMEOUT; + return mLooper->pollOnce(timeoutMillis) != ALOOPER_POLL_TIMEOUT; } void NativeMessageQueue::wake() { - mPollLoop->wake(); + mLooper->wake(); } // ---------------------------------------------------------------------------- @@ -83,10 +83,10 @@ static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject m reinterpret_cast<jint>(nativeMessageQueue)); } -sp<PollLoop> android_os_MessageQueue_getPollLoop(JNIEnv* env, jobject messageQueueObj) { +sp<Looper> android_os_MessageQueue_getLooper(JNIEnv* env, jobject messageQueueObj) { NativeMessageQueue* nativeMessageQueue = android_os_MessageQueue_getNativeMessageQueue(env, messageQueueObj); - return nativeMessageQueue != NULL ? nativeMessageQueue->getPollLoop() : NULL; + return nativeMessageQueue != NULL ? nativeMessageQueue->getLooper() : NULL; } static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) { diff --git a/core/jni/android_os_MessageQueue.h b/core/jni/android_os_MessageQueue.h index 5c742e2..f961d8f 100644 --- a/core/jni/android_os_MessageQueue.h +++ b/core/jni/android_os_MessageQueue.h @@ -21,9 +21,9 @@ namespace android { -class PollLoop; +class Looper; -extern sp<PollLoop> android_os_MessageQueue_getPollLoop(JNIEnv* env, jobject messageQueueObj); +extern sp<Looper> android_os_MessageQueue_getLooper(JNIEnv* env, jobject messageQueueObj); } // namespace android diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index db45d6d..d8e049d 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -61,6 +61,8 @@ static jmethodID method_onRequestPasskey; 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; @@ -105,6 +107,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) { 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"); @@ -116,6 +120,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) { "(Ljava/lang/String;I)V"); method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey", "(Ljava/lang/String;II)V"); + method_onRequestOobData = env->GetMethodID(clazz, "onRequestOobData", + "(Ljava/lang/String;I)V"); field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I"); #endif @@ -305,6 +311,7 @@ static int register_agent(native_data_t *nat, { DBusMessage *msg, *reply; DBusError err; + bool oob = TRUE; if (!dbus_connection_register_object_path(nat->conn, agent_path, &agent_vtable, nat)) { @@ -326,6 +333,7 @@ static int register_agent(native_data_t *nat, } 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); @@ -934,6 +942,43 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn, } 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, @@ -964,6 +1009,21 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn, 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 4420aca..daa59a6 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -288,6 +288,46 @@ done: #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__); @@ -324,6 +364,41 @@ static jboolean createPairedDeviceNative(JNIEnv *env, jobject object, 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) { @@ -490,6 +565,40 @@ static jboolean setPasskeyNative(JNIEnv *env, jobject object, jstring address, 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 @@ -907,7 +1016,10 @@ static JNINativeMethod sMethods[] = { {"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", @@ -916,6 +1028,7 @@ static JNINativeMethod sMethods[] = { {"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/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp index 42f35d1..282e9ed 100644 --- a/core/jni/android_view_InputQueue.cpp +++ b/core/jni/android_view_InputQueue.cpp @@ -29,7 +29,7 @@ #include <android_runtime/AndroidRuntime.h> #include <utils/Log.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> #include <utils/KeyedVector.h> #include <utils/threads.h> #include <ui/InputTransport.h> @@ -77,7 +77,7 @@ private: }; Connection(uint16_t id, - const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop); + const sp<InputChannel>& inputChannel, const sp<Looper>& looper); inline const char* getInputChannelName() const { return inputChannel->getName().string(); } @@ -88,7 +88,7 @@ private: sp<InputChannel> inputChannel; InputConsumer inputConsumer; - sp<PollLoop> pollLoop; + sp<Looper> looper; jobject inputHandlerObjGlobal; PreallocatedInputEventFactory inputEventFactory; @@ -110,7 +110,7 @@ private: static void handleInputChannelDisposed(JNIEnv* env, jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data); - static bool handleReceiveCallback(int receiveFd, int events, void* data); + static int handleReceiveCallback(int receiveFd, int events, void* data); static jlong generateFinishedToken(int32_t receiveFd, uint16_t connectionId, uint16_t messageSeqNum); @@ -141,7 +141,7 @@ status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChanne LOGD("channel '%s' - Registered", inputChannel->getName().string()); #endif - sp<PollLoop> pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueueObj); + sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj); { // acquire lock AutoMutex _l(mLock); @@ -153,7 +153,7 @@ status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChanne } uint16_t connectionId = mNextConnectionId++; - sp<Connection> connection = new Connection(connectionId, inputChannel, pollLoop); + sp<Connection> connection = new Connection(connectionId, inputChannel, looper); status_t result = connection->inputConsumer.initialize(); if (result) { LOGW("Failed to initialize input consumer for input channel '%s', status=%d", @@ -166,7 +166,7 @@ status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChanne int32_t receiveFd = inputChannel->getReceivePipeFd(); mConnectionsByReceiveFd.add(receiveFd, connection); - pollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this); + looper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); } // release lock android_view_InputChannel_setDisposeCallback(env, inputChannelObj, @@ -201,7 +201,7 @@ status_t NativeInputQueue::unregisterInputChannel(JNIEnv* env, jobject inputChan connection->status = Connection::STATUS_ZOMBIE; - connection->pollLoop->removeCallback(inputChannel->getReceivePipeFd()); + connection->looper->removeFd(inputChannel->getReceivePipeFd()); env->DeleteGlobalRef(connection->inputHandlerObjGlobal); connection->inputHandlerObjGlobal = NULL; @@ -293,7 +293,7 @@ void NativeInputQueue::handleInputChannelDisposed(JNIEnv* env, q->unregisterInputChannel(env, inputChannelObj); } -bool NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) { +int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) { NativeInputQueue* q = static_cast<NativeInputQueue*>(data); JNIEnv* env = AndroidRuntime::getJNIEnv(); @@ -308,33 +308,33 @@ bool NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* da if (connectionIndex < 0) { LOGE("Received spurious receive callback for unknown input channel. " "fd=%d, events=0x%x", receiveFd, events); - return false; // remove the callback + return 0; // remove the callback } connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex); - if (events & (POLLERR | POLLHUP | POLLNVAL)) { + if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { LOGE("channel '%s' ~ Publisher closed input channel or an error occurred. " "events=0x%x", connection->getInputChannelName(), events); - return false; // remove the callback + return 0; // remove the callback } - if (! (events & POLLIN)) { + if (! (events & ALOOPER_EVENT_INPUT)) { LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " "events=0x%x", connection->getInputChannelName(), events); - return true; + return 1; } status_t status = connection->inputConsumer.receiveDispatchSignal(); if (status) { LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d", connection->getInputChannelName(), status); - return false; // remove the callback + return 0; // remove the callback } if (connection->messageInProgress) { LOGW("channel '%s' ~ Publisher sent spurious dispatch signal.", connection->getInputChannelName()); - return true; + return 1; } status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent); @@ -342,7 +342,7 @@ bool NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* da LOGW("channel '%s' ~ Failed to consume input event. status=%d", connection->getInputChannelName(), status); connection->inputConsumer.sendFinishedSignal(); - return true; + return 1; } connection->messageInProgress = true; @@ -394,7 +394,7 @@ bool NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* da connection->getInputChannelName()); env->DeleteLocalRef(inputHandlerObjLocal); q->finished(env, finishedToken, false); - return true; + return 1; } #if DEBUG_DISPATCH_CYCLE @@ -417,7 +417,7 @@ bool NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* da env->DeleteLocalRef(inputEventObj); env->DeleteLocalRef(inputHandlerObjLocal); - return true; + return 1; } jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, uint16_t connectionId, @@ -435,9 +435,9 @@ void NativeInputQueue::parseFinishedToken(jlong finishedToken, // ---------------------------------------------------------------------------- NativeInputQueue::Connection::Connection(uint16_t id, - const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop) : + const sp<InputChannel>& inputChannel, const sp<Looper>& looper) : id(id), status(STATUS_NORMAL), inputChannel(inputChannel), inputConsumer(inputChannel), - pollLoop(pollLoop), inputHandlerObjGlobal(NULL), + looper(looper), inputHandlerObjGlobal(NULL), messageSeqNum(0), messageInProgress(false) { } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index a9e4971..9b9b4be 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -930,7 +930,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/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd index 4594bb5..3d4b6a1 100644 --- a/docs/html/sdk/eclipse-adt.jd +++ b/docs/html/sdk/eclipse-adt.jd @@ -428,7 +428,7 @@ environment. </p> <h3 id="preparing">Configuring the ADT Plugin</h3> -<p>Once you've successfully downnloaded ADT as described above, the next step +<p>Once you've successfully downloaded ADT as described above, the next step is to modify your ADT preferences in Eclipse to point to the Android SDK directory:</p> <ol> diff --git a/include/android_runtime/android_app_NativeActivity.h b/include/android_runtime/android_app_NativeActivity.h index fdceb84..b49e02a 100644 --- a/include/android_runtime/android_app_NativeActivity.h +++ b/include/android_runtime/android_app_NativeActivity.h @@ -18,6 +18,7 @@ #define _ANDROID_APP_NATIVEACTIVITY_H #include <ui/InputTransport.h> +#include <utils/Looper.h> #include <android/native_activity.h> @@ -69,7 +70,7 @@ public: /* Destroys the consumer and releases its input channel. */ ~AInputQueue(); - void attachLooper(ALooper* looper, int ident, ALooper_callbackFunc* callback, void* data); + void attachLooper(ALooper* looper, int ident, ALooper_callbackFunc callback, void* data); void detachLooper(); @@ -103,7 +104,7 @@ private: void wakeupDispatch(); android::InputConsumer mConsumer; - android::sp<android::PollLoop> mPollLoop; + android::sp<android::Looper> mLooper; int mDispatchKeyRead; int mDispatchKeyWrite; diff --git a/include/gui/SensorEventQueue.h b/include/gui/SensorEventQueue.h index 6581ae3..97dd391 100644 --- a/include/gui/SensorEventQueue.h +++ b/include/gui/SensorEventQueue.h @@ -42,7 +42,7 @@ namespace android { class ISensorEventConnection; class Sensor; -class PollLoop; +class Looper; // ---------------------------------------------------------------------------- @@ -69,11 +69,11 @@ public: status_t disableSensor(int32_t handle) const; private: - sp<PollLoop> getPollLoop() const; + sp<Looper> getLooper() const; sp<ISensorEventConnection> mSensorEventConnection; sp<SensorChannel> mSensorChannel; mutable Mutex mLock; - mutable sp<PollLoop> mPollLoop; + mutable sp<Looper> mLooper; }; // ---------------------------------------------------------------------------- diff --git a/include/ui/FramebufferNativeWindow.h b/include/ui/FramebufferNativeWindow.h index 0f4594f..c913355 100644 --- a/include/ui/FramebufferNativeWindow.h +++ b/include/ui/FramebufferNativeWindow.h @@ -56,6 +56,9 @@ public: status_t setUpdateRectangle(const Rect& updateRect); status_t compositionComplete(); + // for debugging only + int getCurrentBufferIndex() const; + private: friend class LightRefBase<FramebufferNativeWindow>; ~FramebufferNativeWindow(); // this class cannot be overloaded @@ -77,6 +80,7 @@ private: int32_t mNumBuffers; int32_t mNumFreeBuffers; int32_t mBufferHead; + int32_t mCurrentBufferIndex; bool mUpdateOnDemand; }; diff --git a/include/ui/GraphicLog.h b/include/ui/GraphicLog.h new file mode 100644 index 0000000..f929e6a --- /dev/null +++ b/include/ui/GraphicLog.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#ifndef _UI_GRAPHIC_LOG_H +#define _UI_GRAPHIC_LOG_H + +#include <utils/Singleton.h> +#include <cutils/compiler.h> + +namespace android { + +class GraphicLog : public Singleton<GraphicLog> +{ + int32_t mEnabled; + static void logImpl(int32_t tag, int32_t buffer); + static void logImpl(int32_t tag, int32_t identity, int32_t buffer); + +public: + enum { + SF_APP_DEQUEUE_BEFORE = 60000, + SF_APP_DEQUEUE_AFTER = 60001, + SF_APP_LOCK_BEFORE = 60002, + SF_APP_LOCK_AFTER = 60003, + SF_APP_QUEUE = 60004, + + SF_REPAINT = 60005, + SF_COMPOSITION_COMPLETE = 60006, + SF_UNLOCK_CLIENTS = 60007, + SF_SWAP_BUFFERS = 60008, + SF_REPAINT_DONE = 60009, + + SF_FB_POST_BEFORE = 60010, + SF_FB_POST_AFTER = 60011, + SF_FB_DEQUEUE_BEFORE = 60012, + SF_FB_DEQUEUE_AFTER = 60013, + SF_FB_LOCK_BEFORE = 60014, + SF_FB_LOCK_AFTER = 60015, + }; + + inline void log(int32_t tag, int32_t buffer) { + if (CC_UNLIKELY(mEnabled)) + logImpl(tag, buffer); + } + inline void log(int32_t tag, int32_t identity, int32_t buffer) { + if (CC_UNLIKELY(mEnabled)) + logImpl(tag, identity, buffer); + } + + GraphicLog(); + + void setEnabled(bool enable); +}; + +} + +#endif // _UI_GRAPHIC_LOG_H + diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h index a06208a..d7e6254 100644 --- a/include/ui/InputDispatcher.h +++ b/include/ui/InputDispatcher.h @@ -25,7 +25,7 @@ #include <utils/Timers.h> #include <utils/RefBase.h> #include <utils/String8.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> #include <utils/Pool.h> #include <stddef.h> @@ -826,7 +826,7 @@ private: Mutex mLock; Allocator mAllocator; - sp<PollLoop> mPollLoop; + sp<Looper> mLooper; EventEntry* mPendingEvent; Queue<EventEntry> mInboundQueue; @@ -837,7 +837,7 @@ private: void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime); - // Enqueues an inbound event. Returns true if mPollLoop->wake() should be called. + // Enqueues an inbound event. Returns true if mLooper->wake() should be called. bool enqueueInboundEventLocked(EventEntry* entry); // App switch latency optimization. @@ -1010,7 +1010,7 @@ private: void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, bool broken); void drainOutboundQueueLocked(Connection* connection, DispatchEntry* firstDispatchEntryToDrain); - static bool handleReceiveCallback(int receiveFd, int events, void* data); + static int handleReceiveCallback(int receiveFd, int events, void* data); // Preempting input dispatch. bool preemptInputDispatchInnerLocked(); diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h index 82831e2..dc9e27a 100644 --- a/include/ui/InputTransport.h +++ b/include/ui/InputTransport.h @@ -33,7 +33,6 @@ #include <semaphore.h> #include <ui/Input.h> #include <utils/Errors.h> -#include <utils/PollLoop.h> #include <utils/Timers.h> #include <utils/RefBase.h> #include <utils/String8.h> diff --git a/include/utils/Looper.h b/include/utils/Looper.h new file mode 100644 index 0000000..92e4b0a --- /dev/null +++ b/include/utils/Looper.h @@ -0,0 +1,210 @@ +/* + * 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. + */ + +#ifndef UTILS_LOOPER_H +#define UTILS_LOOPER_H + +#include <utils/threads.h> +#include <utils/RefBase.h> +#include <utils/KeyedVector.h> + +#include <android/looper.h> + +/* + * Declare a concrete type for the NDK's looper forward declaration. + */ +struct ALooper { +}; + +namespace android { + +/** + * A polling loop that supports monitoring file descriptor events, optionally + * using callbacks. The implementation uses epoll() internally. + * + * A looper can be associated with a thread although there is no requirement that it must be. + */ +class Looper : public ALooper, public RefBase { +protected: + virtual ~Looper(); + +public: + /** + * Creates a looper. + * + * If allowNonCallbaks is true, the looper will allow file descriptors to be + * registered without associated callbacks. This assumes that the caller of + * pollOnce() is prepared to handle callback-less events itself. + */ + Looper(bool allowNonCallbacks); + + /** + * Returns whether this looper instance allows the registration of file descriptors + * using identifiers instead of callbacks. + */ + bool getAllowNonCallbacks() const; + + /** + * Waits for events to be available, with optional timeout in milliseconds. + * Invokes callbacks for all file descriptors on which an event occurred. + * + * If the timeout is zero, returns immediately without blocking. + * If the timeout is negative, waits indefinitely until an event appears. + * + * Returns ALOOPER_POLL_WAKE if the poll was awoken using wake() before + * the timeout expired and no callbacks were invoked and no other file + * descriptors were ready. + * + * Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked. + * + * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given + * timeout expired. + * + * Returns ALOOPER_POLL_ERROR if an error occurred. + * + * Returns a value >= 0 containing an identifier if its file descriptor has data + * and it has no callback function (requiring the caller here to handle it). + * In this (and only this) case outFd, outEvents and outData will contain the poll + * events and data associated with the fd, otherwise they will be set to NULL. + * + * This method does not return until it has finished invoking the appropriate callbacks + * for all file descriptors that were signalled. + */ + int pollOnce(int timeoutMillis, + int* outFd = NULL, int* outEvents = NULL, void** outData = NULL); + + /** + * Like pollOnce(), but performs all pending callbacks until all + * data has been consumed or a file descriptor is available with no callback. + * This function will never return ALOOPER_POLL_CALLBACK. + */ + int pollAll(int timeoutMillis, + int* outFd = NULL, int* outEvents = NULL, void** outData = NULL); + + /** + * Wakes the poll asynchronously. + * + * This method can be called on any thread. + * This method returns immediately. + */ + void wake(); + + /** + * Adds a new file descriptor to be polled by the looper. + * If the same file descriptor was previously added, it is replaced. + * + * "fd" is the file descriptor to be added. + * "ident" is an identifier for this event, which is returned from ALooper_pollOnce(). + * The identifier must be >= 0, or ALOOPER_POLL_CALLBACK if providing a non-NULL callback. + * "events" are the poll events to wake up on. Typically this is ALOOPER_EVENT_INPUT. + * "callback" is the function to call when there is an event on the file descriptor. + * "data" is a private data pointer to supply to the callback. + * + * There are two main uses of this function: + * + * (1) If "callback" is non-NULL, then this function will be called when there is + * data on the file descriptor. It should execute any events it has pending, + * appropriately reading from the file descriptor. The 'ident' is ignored in this case. + * + * (2) If "callback" is NULL, the 'ident' will be returned by ALooper_pollOnce + * when its file descriptor has data available, requiring the caller to take + * care of processing it. + * + * Returns 1 if the file descriptor was added, 0 if the arguments were invalid. + * + * This method can be called on any thread. + * This method may block briefly if it needs to wake the poll. + */ + int addFd(int fd, int ident, + int events, ALooper_callbackFunc callback, void* data = NULL); + + /** + * Removes a previously added file descriptor from the looper. + * + * When this method returns, it is safe to close the file descriptor since the looper + * will no longer have a reference to it. However, it is possible for the callback to + * already be running or for it to run one last time if the file descriptor was already + * signalled. Calling code is responsible for ensuring that this case is safely handled. + * For example, if the callback takes care of removing itself during its own execution either + * by returning 0 or by calling this method, then it can be guaranteed to not be invoked + * again at any later time unless registered anew. + * + * Returns 1 if the file descriptor was removed, 0 if none was previously registered. + * + * This method can be called on any thread. + * This method may block briefly if it needs to wake the poll. + */ + int removeFd(int fd); + + /** + * Prepares a looper associated with the calling thread, and returns it. + * If the thread already has a looper, it is returned. Otherwise, a new + * one is created, associated with the thread, and returned. + * + * The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0. + */ + static sp<Looper> prepare(int opts); + + /** + * Sets the given looper to be associated with the calling thread. + * If another looper is already associated with the thread, it is replaced. + * + * If "looper" is NULL, removes the currently associated looper. + */ + static void setForThread(const sp<Looper>& looper); + + /** + * Returns the looper associated with the calling thread, or NULL if + * there is not one. + */ + static sp<Looper> getForThread(); + +private: + struct Request { + int fd; + int ident; + ALooper_callbackFunc callback; + void* data; + }; + + struct Response { + int events; + Request request; + }; + + const bool mAllowNonCallbacks; // immutable + + int mEpollFd; // immutable + int mWakeReadPipeFd; // immutable + int mWakeWritePipeFd; // immutable + + // Locked list of file descriptor monitoring requests. + Mutex mLock; + KeyedVector<int, Request> mRequests; + + // This state is only used privately by pollOnce and does not require a lock since + // it runs on a single thread. + Vector<Response> mResponses; + size_t mResponseIndex; + + int pollInner(int timeoutMillis); + + static void threadDestructor(void *st); +}; + +} // namespace android + +#endif // UTILS_LOOPER_H diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h deleted file mode 100644 index c2dfe5d..0000000 --- a/include/utils/PollLoop.h +++ /dev/null @@ -1,219 +0,0 @@ -/* - * 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. - */ - -#ifndef UTILS_POLL_LOOP_H -#define UTILS_POLL_LOOP_H - -#include <utils/Vector.h> -#include <utils/threads.h> - -#include <sys/poll.h> - -#include <android/looper.h> - -struct ALooper : public android::RefBase { -protected: - virtual ~ALooper() { } - -public: - ALooper() { } -}; - -namespace android { - -/** - * A basic file descriptor polling loop based on poll() with callbacks. - */ -class PollLoop : public ALooper { -protected: - virtual ~PollLoop(); - -public: - PollLoop(bool allowNonCallbacks); - - /** - * A callback that it to be invoked when an event occurs on a file descriptor. - * Specifies the events that were triggered and the user data provided when the - * callback was set. - * - * Returns true if the callback should be kept, false if it should be removed automatically - * after the callback returns. - */ - typedef bool (*Callback)(int fd, int events, void* data); - - enum { - POLL_CALLBACK = ALOOPER_POLL_CALLBACK, - POLL_TIMEOUT = ALOOPER_POLL_TIMEOUT, - POLL_ERROR = ALOOPER_POLL_ERROR, - }; - - /** - * Performs a single call to poll() with optional timeout in milliseconds. - * Invokes callbacks for all file descriptors on which an event occurred. - * - * If the timeout is zero, returns immediately without blocking. - * If the timeout is negative, waits indefinitely until awoken. - * - * Returns ALOOPER_POLL_CALLBACK if a callback was invoked. - * - * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given - * timeout expired. - * - * Returns ALOPER_POLL_ERROR if an error occurred. - * - * Returns a value >= 0 containing a file descriptor if it has data - * and it has no callback function (requiring the caller here to handle it). - * In this (and only this) case outEvents and outData will contain the poll - * events and data associated with the fd. - * - * This method must only be called on the thread owning the PollLoop. - * This method blocks until either a file descriptor is signalled, a timeout occurs, - * or wake() is called. - * This method does not return until it has finished invoking the appropriate callbacks - * for all file descriptors that were signalled. - */ - int32_t pollOnce(int timeoutMillis, int* outEvents = NULL, void** outData = NULL); - - /** - * Wakes the loop asynchronously. - * - * This method can be called on any thread. - * This method returns immediately. - */ - void wake(); - - /** - * Control whether this PollLoop instance allows using IDs instead - * of callbacks. - */ - bool getAllowNonCallbacks() const; - - /** - * Sets the callback for a file descriptor, replacing the existing one, if any. - * It is an error to call this method with events == 0 or callback == NULL. - * - * Note that a callback can be invoked with the POLLERR, POLLHUP or POLLNVAL events - * even if it is not explicitly requested when registered. - * - * This method can be called on any thread. - * This method may block briefly if it needs to wake the poll loop. - */ - void setCallback(int fd, int ident, int events, Callback callback, void* data = NULL); - - /** - * Convenience for above setCallback when ident is not used. In this case - * the ident is set to POLL_CALLBACK. - */ - void setCallback(int fd, int events, Callback callback, void* data = NULL); - - /** - * Like setCallback(), but for the NDK callback function. - */ - void setLooperCallback(int fd, int ident, int events, ALooper_callbackFunc* callback, - void* data); - - /** - * Removes the callback for a file descriptor, if one exists. - * - * When this method returns, it is safe to close the file descriptor since the poll loop - * will no longer have a reference to it. However, it is possible for the callback to - * already be running or for it to run one last time if the file descriptor was already - * signalled. Calling code is responsible for ensuring that this case is safely handled. - * For example, if the callback takes care of removing itself during its own execution either - * by returning false or calling this method, then it can be guaranteed to not be invoked - * again at any later time unless registered anew. - * - * This method can be called on any thread. - * This method may block briefly if it needs to wake the poll loop. - * - * Returns true if a callback was actually removed, false if none was registered. - */ - bool removeCallback(int fd); - - /** - * Set the given PollLoop to be associated with the - * calling thread. There must be a 1:1 relationship between - * PollLoop and thread. - */ - static void setForThread(const sp<PollLoop>& pollLoop); - - /** - * Return the PollLoop associated with the calling thread. - */ - static sp<PollLoop> getForThread(); - -private: - struct RequestedCallback { - Callback callback; - ALooper_callbackFunc* looperCallback; - int ident; - void* data; - }; - - struct PendingCallback { - int fd; - int ident; - int events; - Callback callback; - ALooper_callbackFunc* looperCallback; - void* data; - }; - - const bool mAllowNonCallbacks; // immutable - - int mWakeReadPipeFd; // immutable - int mWakeWritePipeFd; // immutable - - // The lock guards state used to track whether there is a poll() in progress and whether - // there are any other threads waiting in wakeAndLock(). The condition variables - // are used to transfer control among these threads such that all waiters are - // serviced before a new poll can begin. - // The wakeAndLock() method increments mWaiters, wakes the poll, blocks on mAwake - // until mPolling becomes false, then decrements mWaiters again. - // The poll() method blocks on mResume until mWaiters becomes 0, then sets - // mPolling to true, blocks until the poll completes, then resets mPolling to false - // and signals mResume if there are waiters. - Mutex mLock; - bool mPolling; // guarded by mLock - uint32_t mWaiters; // guarded by mLock - Condition mAwake; // guarded by mLock - Condition mResume; // guarded by mLock - - // The next two vectors are only mutated when mPolling is false since they must - // not be changed while the poll() system call is in progress. To mutate these - // vectors, the poll() must first be awoken then the lock acquired. - Vector<struct pollfd> mRequestedFds; - Vector<RequestedCallback> mRequestedCallbacks; - - // This state is only used privately by pollOnce and does not require a lock since - // it runs on a single thread. - Vector<PendingCallback> mPendingCallbacks; - Vector<PendingCallback> mPendingFds; - size_t mPendingFdsPos; - - void openWakePipe(); - void closeWakePipe(); - - void setCallbackCommon(int fd, int ident, int events, Callback callback, - ALooper_callbackFunc* looperCallback, void* data); - ssize_t getRequestIndexLocked(int fd); - void wakeAndLock(); - static void threadDestructor(void *st); -}; - -} // namespace android - -#endif // UTILS_POLL_LOOP_H diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp index 7eb6da5..c3a9f22 100644 --- a/libs/gui/SensorEventQueue.cpp +++ b/libs/gui/SensorEventQueue.cpp @@ -21,7 +21,7 @@ #include <utils/Errors.h> #include <utils/RefBase.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> #include <gui/Sensor.h> #include <gui/SensorChannel.h> @@ -81,28 +81,28 @@ ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) return size; } -sp<PollLoop> SensorEventQueue::getPollLoop() const +sp<Looper> SensorEventQueue::getLooper() const { Mutex::Autolock _l(mLock); - if (mPollLoop == 0) { - mPollLoop = new PollLoop(true); - mPollLoop->setCallback(getFd(), getFd(), POLLIN, NULL, NULL); + if (mLooper == 0) { + mLooper = new Looper(true); + mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL); } - return mPollLoop; + return mLooper; } status_t SensorEventQueue::waitForEvent() const { const int fd = getFd(); - sp<PollLoop> pollLoop(getPollLoop()); - int32_t result = pollLoop->pollOnce(-1, NULL, NULL); - return (result == fd) ? NO_ERROR : -1; + sp<Looper> looper(getLooper()); + int32_t result = looper->pollOnce(-1); + return (result == fd) ? status_t(NO_ERROR) : status_t(-1); } status_t SensorEventQueue::wake() const { - sp<PollLoop> pollLoop(getPollLoop()); - pollLoop->wake(); + sp<Looper> looper(getLooper()); + looper->wake(); return NO_ERROR; } diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp index cb76091..560ea67 100644 --- a/libs/surfaceflinger_client/Surface.cpp +++ b/libs/surfaceflinger_client/Surface.cpp @@ -32,6 +32,7 @@ #include <ui/DisplayInfo.h> #include <ui/GraphicBuffer.h> #include <ui/GraphicBufferMapper.h> +#include <ui/GraphicLog.h> #include <ui/Rect.h> #include <surfaceflinger/Surface.h> @@ -568,7 +569,13 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer) if (err != NO_ERROR) return err; + GraphicLog& logger(GraphicLog::getInstance()); + logger.log(GraphicLog::SF_APP_DEQUEUE_BEFORE, mIdentity, -1); + ssize_t bufIdx = mSharedBufferClient->dequeue(); + + logger.log(GraphicLog::SF_APP_DEQUEUE_AFTER, mIdentity, bufIdx); + if (bufIdx < 0) { LOGE("error dequeuing a buffer (%s)", strerror(bufIdx)); return bufIdx; @@ -617,13 +624,20 @@ int Surface::lockBuffer(android_native_buffer_t* buffer) return err; int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); + + GraphicLog& logger(GraphicLog::getInstance()); + logger.log(GraphicLog::SF_APP_LOCK_BEFORE, mIdentity, bufIdx); + err = mSharedBufferClient->lock(bufIdx); + + logger.log(GraphicLog::SF_APP_LOCK_AFTER, mIdentity, bufIdx); + LOGE_IF(err, "error locking buffer %d (%s)", bufIdx, strerror(-err)); return err; } int Surface::queueBuffer(android_native_buffer_t* buffer) -{ +{ status_t err = validate(); if (err != NO_ERROR) return err; @@ -633,6 +647,9 @@ int Surface::queueBuffer(android_native_buffer_t* buffer) } int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); + + GraphicLog::getInstance().log(GraphicLog::SF_APP_QUEUE, mIdentity, bufIdx); + mSharedBufferClient->setTransform(bufIdx, mNextBufferTransform); mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop); mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion); diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index 9f49348..c4a09d6 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -9,6 +9,7 @@ LOCAL_SRC_FILES:= \ GraphicBuffer.cpp \ GraphicBufferAllocator.cpp \ GraphicBufferMapper.cpp \ + GraphicLog.cpp \ KeyLayoutMap.cpp \ KeyCharacterMap.cpp \ Input.cpp \ diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp index 6f8948d..a36d555 100644 --- a/libs/ui/FramebufferNativeWindow.cpp +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -29,6 +29,7 @@ #include <ui/Rect.h> #include <ui/FramebufferNativeWindow.h> +#include <ui/GraphicLog.h> #include <EGL/egl.h> @@ -174,6 +175,14 @@ int FramebufferNativeWindow::setSwapInterval( return fb->setSwapInterval(fb, interval); } +// only for debugging / logging +int FramebufferNativeWindow::getCurrentBufferIndex() const +{ + Mutex::Autolock _l(mutex); + const int index = mCurrentBufferIndex; + return index; +} + int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer) { @@ -181,18 +190,24 @@ int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, Mutex::Autolock _l(self->mutex); framebuffer_device_t* fb = self->fbDev; + int index = self->mBufferHead++; + if (self->mBufferHead >= self->mNumBuffers) + self->mBufferHead = 0; + + GraphicLog& logger(GraphicLog::getInstance()); + logger.log(GraphicLog::SF_FB_DEQUEUE_BEFORE, index); + // wait for a free buffer while (!self->mNumFreeBuffers) { self->mCondition.wait(self->mutex); } // get this buffer self->mNumFreeBuffers--; - int index = self->mBufferHead++; - if (self->mBufferHead >= self->mNumBuffers) - self->mBufferHead = 0; + self->mCurrentBufferIndex = index; *buffer = self->buffers[index].get(); + logger.log(GraphicLog::SF_FB_DEQUEUE_AFTER, index); return 0; } @@ -202,11 +217,17 @@ int FramebufferNativeWindow::lockBuffer(ANativeWindow* window, FramebufferNativeWindow* self = getSelf(window); Mutex::Autolock _l(self->mutex); + const int index = self->mCurrentBufferIndex; + GraphicLog& logger(GraphicLog::getInstance()); + logger.log(GraphicLog::SF_FB_LOCK_BEFORE, index); + // wait that the buffer we're locking is not front anymore while (self->front == buffer) { self->mCondition.wait(self->mutex); } + logger.log(GraphicLog::SF_FB_LOCK_AFTER, index); + return NO_ERROR; } @@ -217,7 +238,15 @@ int FramebufferNativeWindow::queueBuffer(ANativeWindow* window, Mutex::Autolock _l(self->mutex); framebuffer_device_t* fb = self->fbDev; buffer_handle_t handle = static_cast<NativeBuffer*>(buffer)->handle; + + const int index = self->mCurrentBufferIndex; + GraphicLog& logger(GraphicLog::getInstance()); + logger.log(GraphicLog::SF_FB_POST_BEFORE, index); + int res = fb->post(fb, handle); + + logger.log(GraphicLog::SF_FB_POST_AFTER, index); + self->front = static_cast<NativeBuffer*>(buffer); self->mNumFreeBuffers++; self->mCondition.broadcast(); diff --git a/libs/ui/GraphicLog.cpp b/libs/ui/GraphicLog.cpp new file mode 100644 index 0000000..b55ce23 --- /dev/null +++ b/libs/ui/GraphicLog.cpp @@ -0,0 +1,84 @@ +/* + * 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. + */ + + +#include <stdlib.h> +#include <unistd.h> +#include <cutils/log.h> +#include <cutils/properties.h> +#include <utils/Endian.h> +#include <utils/Timers.h> + +#include <ui/GraphicLog.h> + +namespace android { + +ANDROID_SINGLETON_STATIC_INSTANCE(GraphicLog) + +static inline +void writeInt32(uint8_t* base, size_t& pos, int32_t value) { + int32_t v = htole32(value); + base[pos] = EVENT_TYPE_INT; + memcpy(&base[pos+1], &v, sizeof(int32_t)); + pos += 1+sizeof(int32_t); +} + +static inline +void writeInt64(uint8_t* base, size_t& pos, int64_t value) { + int64_t v = htole64(value); + base[pos] = EVENT_TYPE_LONG; + memcpy(&base[pos+1], &v, sizeof(int64_t)); + pos += 1+sizeof(int64_t); +} + +void GraphicLog::logImpl(int32_t tag, int32_t buffer) +{ + uint8_t scratch[2 + 2 + sizeof(int32_t) + sizeof(int64_t)]; + size_t pos = 0; + scratch[pos++] = EVENT_TYPE_LIST; + scratch[pos++] = 2; + writeInt32(scratch, pos, buffer); + writeInt64(scratch, pos, ns2ms( systemTime( SYSTEM_TIME_MONOTONIC ) )); + android_bWriteLog(tag, scratch, sizeof(scratch)); +} + +void GraphicLog::logImpl(int32_t tag, int32_t identity, int32_t buffer) +{ + uint8_t scratch[2 + 3 + sizeof(int32_t) + sizeof(int32_t) + sizeof(int64_t)]; + size_t pos = 0; + scratch[pos++] = EVENT_TYPE_LIST; + scratch[pos++] = 3; + writeInt32(scratch, pos, buffer); + writeInt32(scratch, pos, identity); + writeInt64(scratch, pos, ns2ms( systemTime( SYSTEM_TIME_MONOTONIC ) )); + android_bWriteLog(tag, scratch, sizeof(scratch)); +} + +GraphicLog::GraphicLog() + : mEnabled(0) +{ + char property[PROPERTY_VALUE_MAX]; + if (property_get("debug.graphic_log", property, NULL) > 0) { + mEnabled = atoi(property); + } +} + +void GraphicLog::setEnabled(bool enable) +{ + mEnabled = enable; +} + +} diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index b8a26b0..48dea57 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -95,7 +95,7 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mFocusedApplication(NULL), mCurrentInputTargetsValid(false), mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { - mPollLoop = new PollLoop(false); + mLooper = new Looper(false); mInboundQueue.headSentinel.refCount = -1; mInboundQueue.headSentinel.type = EventEntry::TYPE_SENTINEL; @@ -156,7 +156,7 @@ void InputDispatcher::dispatchOnce() { timeoutMillis = 0; } - mPollLoop->pollOnce(timeoutMillis); + mLooper->pollOnce(timeoutMillis); } void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, @@ -1784,7 +1784,7 @@ void InputDispatcher::drainOutboundQueueLocked(Connection* connection, } } -bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) { +int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) { InputDispatcher* d = static_cast<InputDispatcher*>(data); { // acquire lock @@ -1794,24 +1794,24 @@ bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* dat if (connectionIndex < 0) { LOGE("Received spurious receive callback for unknown input channel. " "fd=%d, events=0x%x", receiveFd, events); - return false; // remove the callback + return 0; // remove the callback } nsecs_t currentTime = now(); sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex); - if (events & (POLLERR | POLLHUP | POLLNVAL)) { + if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { LOGE("channel '%s' ~ Consumer closed input channel or an error occurred. " "events=0x%x", connection->getInputChannelName(), events); d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/); d->runCommandsLockedInterruptible(); - return false; // remove the callback + return 0; // remove the callback } - if (! (events & POLLIN)) { + if (! (events & ALOOPER_EVENT_INPUT)) { LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " "events=0x%x", connection->getInputChannelName(), events); - return true; + return 1; } status_t status = connection->inputPublisher.receiveFinishedSignal(); @@ -1820,12 +1820,12 @@ bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* dat connection->getInputChannelName(), status); d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/); d->runCommandsLockedInterruptible(); - return false; // remove the callback + return 0; // remove the callback } d->finishDispatchCycleLocked(currentTime, connection); d->runCommandsLockedInterruptible(); - return true; + return 1; } // release lock } @@ -1843,7 +1843,7 @@ void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) { } // release lock if (needWake) { - mPollLoop->wake(); + mLooper->wake(); } } @@ -1870,7 +1870,7 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou } // release lock if (needWake) { - mPollLoop->wake(); + mLooper->wake(); } } @@ -2007,7 +2007,7 @@ NoBatchingOrStreaming:; } // release lock if (needWake) { - mPollLoop->wake(); + mLooper->wake(); } } @@ -2043,7 +2043,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, } // release lock if (needWake) { - mPollLoop->wake(); + mLooper->wake(); } int32_t injectionResult; @@ -2294,7 +2294,7 @@ void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) { } // release lock // Wake up poll loop since it may need to make new input dispatching choices. - mPollLoop->wake(); + mLooper->wake(); } void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) { @@ -2317,7 +2317,7 @@ void InputDispatcher::setFocusedApplication(const InputApplication* inputApplica } // release lock // Wake up poll loop since it may need to make new input dispatching choices. - mPollLoop->wake(); + mLooper->wake(); } void InputDispatcher::releaseFocusedApplicationLocked() { @@ -2355,7 +2355,7 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { if (changed) { // Wake up poll loop since it may need to make new input dispatching choices. - mPollLoop->wake(); + mLooper->wake(); } } @@ -2372,7 +2372,7 @@ void InputDispatcher::preemptInputDispatch() { if (preemptedOne) { // Wake up the poll loop so it can get a head start dispatching the next event. - mPollLoop->wake(); + mLooper->wake(); } } @@ -2495,7 +2495,7 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan mMonitoringChannels.push(inputChannel); } - mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this); + mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); runCommandsLockedInterruptible(); } // release lock @@ -2529,7 +2529,7 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh } } - mPollLoop->removeCallback(inputChannel->getReceivePipeFd()); + mLooper->removeFd(inputChannel->getReceivePipeFd()); nsecs_t currentTime = now(); abortDispatchCycleLocked(currentTime, connection, true /*broken*/); @@ -2539,7 +2539,7 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh // Wake the poll loop because removing the connection may have changed the current // synchronization state. - mPollLoop->wake(); + mLooper->wake(); return OK; } diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 2e20268..eb75ed8 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -86,7 +86,7 @@ LOCAL_SRC_FILES:= \ $(commonSources) \ BackupData.cpp \ BackupHelpers.cpp \ - PollLoop.cpp + Looper.cpp ifeq ($(TARGET_OS),linux) LOCAL_LDLIBS += -lrt -ldl diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp new file mode 100644 index 0000000..fd287da --- /dev/null +++ b/libs/utils/Looper.cpp @@ -0,0 +1,368 @@ +// +// Copyright 2010 The Android Open Source Project +// +// A looper implementation based on epoll(). +// +#define LOG_TAG "Looper" + +//#define LOG_NDEBUG 0 + +// Debugs poll and wake interactions. +#define DEBUG_POLL_AND_WAKE 0 + +// Debugs callback registration and invocation. +#define DEBUG_CALLBACKS 0 + +#include <cutils/log.h> +#include <utils/Looper.h> +#include <utils/Timers.h> + +#include <unistd.h> +#include <fcntl.h> +#include <sys/epoll.h> + + +namespace android { + +static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; +static bool gHaveTLS = false; +static pthread_key_t gTLS = 0; + +// Hint for number of file descriptors to be associated with the epoll instance. +static const int EPOLL_SIZE_HINT = 8; + +// Maximum number of file descriptors for which to retrieve poll events each iteration. +static const int EPOLL_MAX_EVENTS = 16; + +Looper::Looper(bool allowNonCallbacks) : + mAllowNonCallbacks(allowNonCallbacks), + mResponseIndex(0) { + mEpollFd = epoll_create(EPOLL_SIZE_HINT); + LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); + + int wakeFds[2]; + int result = pipe(wakeFds); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); + + mWakeReadPipeFd = wakeFds[0]; + mWakeWritePipeFd = wakeFds[1]; + + result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", + errno); + + result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", + errno); + + struct epoll_event eventItem; + eventItem.events = EPOLLIN; + eventItem.data.fd = mWakeReadPipeFd; + result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", + errno); +} + +Looper::~Looper() { + close(mWakeReadPipeFd); + close(mWakeWritePipeFd); + close(mEpollFd); +} + +void Looper::threadDestructor(void *st) { + Looper* const self = static_cast<Looper*>(st); + if (self != NULL) { + self->decStrong((void*)threadDestructor); + } +} + +void Looper::setForThread(const sp<Looper>& looper) { + sp<Looper> old = getForThread(); // also has side-effect of initializing TLS + + if (looper != NULL) { + looper->incStrong((void*)threadDestructor); + } + + pthread_setspecific(gTLS, looper.get()); + + if (old != NULL) { + old->decStrong((void*)threadDestructor); + } +} + +sp<Looper> Looper::getForThread() { + if (!gHaveTLS) { + pthread_mutex_lock(&gTLSMutex); + if (pthread_key_create(&gTLS, threadDestructor) != 0) { + pthread_mutex_unlock(&gTLSMutex); + return NULL; + } + gHaveTLS = true; + pthread_mutex_unlock(&gTLSMutex); + } + + return (Looper*)pthread_getspecific(gTLS); +} + +sp<Looper> Looper::prepare(int opts) { + bool allowNonCallbacks = opts & ALOOPER_PREPARE_ALLOW_NON_CALLBACKS; + sp<Looper> looper = Looper::getForThread(); + if (looper == NULL) { + looper = new Looper(allowNonCallbacks); + Looper::setForThread(looper); + } + if (looper->getAllowNonCallbacks() != allowNonCallbacks) { + LOGW("Looper already prepared for this thread with a different value for the " + "ALOOPER_PREPARE_ALLOW_NON_CALLBACKS option."); + } + return looper; +} + +bool Looper::getAllowNonCallbacks() const { + return mAllowNonCallbacks; +} + +int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { + int result = 0; + for (;;) { + while (mResponseIndex < mResponses.size()) { + const Response& response = mResponses.itemAt(mResponseIndex++); + if (! response.request.callback) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - returning signalled identifier %d: " + "fd=%d, events=0x%x, data=%p", this, + response.request.ident, response.request.fd, + response.events, response.request.data); +#endif + if (outFd != NULL) *outFd = response.request.fd; + if (outEvents != NULL) *outEvents = response.events; + if (outData != NULL) *outData = response.request.data; + return response.request.ident; + } + } + + if (result != 0) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - returning result %d", this, result); +#endif + if (outFd != NULL) *outFd = 0; + if (outEvents != NULL) *outEvents = NULL; + if (outData != NULL) *outData = NULL; + return result; + } + + result = pollInner(timeoutMillis); + } +} + +int Looper::pollInner(int timeoutMillis) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis); +#endif + struct epoll_event eventItems[EPOLL_MAX_EVENTS]; + int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); + if (eventCount < 0) { + if (errno != EINTR) { + LOGW("Poll failed with an unexpected error, errno=%d", errno); + } + return ALOOPER_POLL_ERROR; + } + + if (eventCount == 0) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - timeout", this); +#endif + return ALOOPER_POLL_TIMEOUT; + } + + int result = ALOOPER_POLL_WAKE; + mResponses.clear(); + mResponseIndex = 0; + +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount); +#endif + { // acquire lock + AutoMutex _l(mLock); + for (int i = 0; i < eventCount; i++) { + int fd = eventItems[i].data.fd; + uint32_t epollEvents = eventItems[i].events; + if (fd == mWakeReadPipeFd) { + if (epollEvents & EPOLLIN) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - awoken", this); +#endif + char buffer[16]; + ssize_t nRead; + do { + nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); + } while (nRead == sizeof(buffer)); + } else { + LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); + } + } else { + ssize_t requestIndex = mRequests.indexOfKey(fd); + if (requestIndex >= 0) { + int events = 0; + if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT; + if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT; + if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR; + if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP; + + Response response; + response.events = events; + response.request = mRequests.valueAt(requestIndex); + mResponses.push(response); + } else { + LOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " + "no longer registered.", epollEvents, fd); + } + } + } + } + + for (size_t i = 0; i < mResponses.size(); i++) { + const Response& response = mResponses.itemAt(i); + if (response.request.callback) { +#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS + LOGD("%p ~ pollOnce - invoking callback: fd=%d, events=0x%x, data=%p", this, + response.request.fd, response.events, response.request.data); +#endif + int callbackResult = response.request.callback( + response.request.fd, response.events, response.request.data); + if (callbackResult == 0) { + removeFd(response.request.fd); + } + + result = ALOOPER_POLL_CALLBACK; + } + } + return result; +} + +int Looper::pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) { + if (timeoutMillis <= 0) { + int result; + do { + result = pollOnce(timeoutMillis, outFd, outEvents, outData); + } while (result == ALOOPER_POLL_CALLBACK); + return result; + } else { + nsecs_t endTime = systemTime(SYSTEM_TIME_MONOTONIC) + + milliseconds_to_nanoseconds(timeoutMillis); + + for (;;) { + int result = pollOnce(timeoutMillis, outFd, outEvents, outData); + if (result != ALOOPER_POLL_CALLBACK) { + return result; + } + + nsecs_t timeoutNanos = endTime - systemTime(SYSTEM_TIME_MONOTONIC); + if (timeoutNanos <= 0) { + return ALOOPER_POLL_TIMEOUT; + } + + timeoutMillis = int(nanoseconds_to_milliseconds(timeoutNanos + 999999LL)); + } + } +} + +void Looper::wake() { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ wake", this); +#endif + + ssize_t nWrite = write(mWakeWritePipeFd, "W", 1); + if (nWrite != 1) { + if (errno != EAGAIN) { + LOGW("Could not write wake signal, errno=%d", errno); + } + } +} + +int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) { +#if DEBUG_CALLBACKS + LOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident, + events, callback, data); +#endif + + int epollEvents = 0; + if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN; + if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT; + if (events & ALOOPER_EVENT_ERROR) epollEvents |= EPOLLERR; + if (events & ALOOPER_EVENT_HANGUP) epollEvents |= EPOLLHUP; + + if (epollEvents == 0) { + LOGE("Invalid attempt to set a callback with no selected poll events."); + return -1; + } + + if (! callback) { + if (! mAllowNonCallbacks) { + LOGE("Invalid attempt to set NULL callback but not allowed for this looper."); + return -1; + } + + if (ident < 0) { + LOGE("Invalid attempt to set NULL callback with ident <= 0."); + return -1; + } + } + + { // acquire lock + AutoMutex _l(mLock); + + Request request; + request.fd = fd; + request.ident = ident; + request.callback = callback; + request.data = data; + + struct epoll_event eventItem; + eventItem.events = epollEvents; + eventItem.data.fd = fd; + + ssize_t requestIndex = mRequests.indexOfKey(fd); + if (requestIndex < 0) { + int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); + if (epollResult < 0) { + LOGE("Error adding epoll events for fd %d, errno=%d", fd, errno); + return -1; + } + mRequests.add(fd, request); + } else { + int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem); + if (epollResult < 0) { + LOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno); + return -1; + } + mRequests.replaceValueAt(requestIndex, request); + } + } // release lock + return 1; +} + +int Looper::removeFd(int fd) { +#if DEBUG_CALLBACKS + LOGD("%p ~ removeFd - fd=%d", this, fd); +#endif + + { // acquire lock + AutoMutex _l(mLock); + ssize_t requestIndex = mRequests.indexOfKey(fd); + if (requestIndex < 0) { + return 0; + } + + int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL); + if (epollResult < 0) { + LOGE("Error removing epoll events for fd %d, errno=%d", fd, errno); + return -1; + } + + mRequests.removeItemsAt(requestIndex); + } // request lock + return 1; +} + +} // namespace android diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp deleted file mode 100644 index fe76cd0..0000000 --- a/libs/utils/PollLoop.cpp +++ /dev/null @@ -1,377 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// -// A select loop implementation. -// -#define LOG_TAG "PollLoop" - -//#define LOG_NDEBUG 0 - -// Debugs poll and wake interactions. -#define DEBUG_POLL_AND_WAKE 0 - -// Debugs callback registration and invocation. -#define DEBUG_CALLBACKS 0 - -#include <cutils/log.h> -#include <utils/PollLoop.h> - -#include <unistd.h> -#include <fcntl.h> - -namespace android { - -static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; -static bool gHaveTLS = false; -static pthread_key_t gTLS = 0; - -PollLoop::PollLoop(bool allowNonCallbacks) : - mAllowNonCallbacks(allowNonCallbacks), mPolling(false), - mWaiters(0), mPendingFdsPos(0) { - openWakePipe(); -} - -PollLoop::~PollLoop() { - closeWakePipe(); -} - -void PollLoop::threadDestructor(void *st) { - PollLoop* const self = static_cast<PollLoop*>(st); - if (self != NULL) { - self->decStrong((void*)threadDestructor); - } -} - -void PollLoop::setForThread(const sp<PollLoop>& pollLoop) { - sp<PollLoop> old = getForThread(); - - if (pollLoop != NULL) { - pollLoop->incStrong((void*)threadDestructor); - } - - pthread_setspecific(gTLS, pollLoop.get()); - - if (old != NULL) { - old->decStrong((void*)threadDestructor); - } -} - -sp<PollLoop> PollLoop::getForThread() { - if (!gHaveTLS) { - pthread_mutex_lock(&gTLSMutex); - if (pthread_key_create(&gTLS, threadDestructor) != 0) { - pthread_mutex_unlock(&gTLSMutex); - return NULL; - } - gHaveTLS = true; - pthread_mutex_unlock(&gTLSMutex); - } - - return (PollLoop*)pthread_getspecific(gTLS); -} - -void PollLoop::openWakePipe() { - int wakeFds[2]; - int result = pipe(wakeFds); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); - - mWakeReadPipeFd = wakeFds[0]; - mWakeWritePipeFd = wakeFds[1]; - - result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", - errno); - - result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", - errno); - - // Add the wake pipe to the head of the request list with a null callback. - struct pollfd requestedFd; - requestedFd.fd = mWakeReadPipeFd; - requestedFd.events = POLLIN; - mRequestedFds.insertAt(requestedFd, 0); - - RequestedCallback requestedCallback; - requestedCallback.callback = NULL; - requestedCallback.looperCallback = NULL; - requestedCallback.ident = 0; - requestedCallback.data = NULL; - mRequestedCallbacks.insertAt(requestedCallback, 0); -} - -void PollLoop::closeWakePipe() { - close(mWakeReadPipeFd); - close(mWakeWritePipeFd); - - // Note: We don't need to remove the poll structure or callback entry because this - // method is currently only called by the destructor. -} - -int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { - // If there are still pending fds from the last call, dispatch those - // first, to avoid an earlier fd from starving later ones. - const size_t pendingFdsCount = mPendingFds.size(); - if (mPendingFdsPos < pendingFdsCount) { - const PendingCallback& pending = mPendingFds.itemAt(mPendingFdsPos); - mPendingFdsPos++; - if (outEvents != NULL) *outEvents = pending.events; - if (outData != NULL) *outData = pending.data; - return pending.ident; - } - - // Wait for wakeAndLock() waiters to run then set mPolling to true. - mLock.lock(); - while (mWaiters != 0) { - mResume.wait(mLock); - } - mPolling = true; - mLock.unlock(); - - // Poll. - int32_t result; - size_t requestedCount = mRequestedFds.size(); - -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - waiting on %d fds", this, requestedCount); - for (size_t i = 0; i < requestedCount; i++) { - LOGD(" fd %d - events %d", mRequestedFds[i].fd, mRequestedFds[i].events); - } -#endif - - int respondedCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis); - - if (respondedCount == 0) { - // Timeout -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - timeout", this); -#endif - result = POLL_TIMEOUT; - goto Done; - } - - if (respondedCount < 0) { - // Error -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - error, errno=%d", this, errno); -#endif - if (errno != EINTR) { - LOGW("Poll failed with an unexpected error, errno=%d", errno); - } - result = POLL_ERROR; - goto Done; - } - -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - handling responses from %d fds", this, respondedCount); - for (size_t i = 0; i < requestedCount; i++) { - LOGD(" fd %d - events %d, revents %d", mRequestedFds[i].fd, mRequestedFds[i].events, - mRequestedFds[i].revents); - } -#endif - - // Process the poll results. - mPendingCallbacks.clear(); - mPendingFds.clear(); - mPendingFdsPos = 0; - if (outEvents != NULL) *outEvents = 0; - if (outData != NULL) *outData = NULL; - - result = POLL_CALLBACK; - for (size_t i = 0; i < requestedCount; i++) { - const struct pollfd& requestedFd = mRequestedFds.itemAt(i); - - short revents = requestedFd.revents; - if (revents) { - const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i); - PendingCallback pending; - pending.fd = requestedFd.fd; - pending.ident = requestedCallback.ident; - pending.events = revents; - pending.callback = requestedCallback.callback; - pending.looperCallback = requestedCallback.looperCallback; - pending.data = requestedCallback.data; - - if (pending.callback || pending.looperCallback) { - mPendingCallbacks.push(pending); - } else if (pending.fd != mWakeReadPipeFd) { - if (result == POLL_CALLBACK) { - result = pending.ident; - if (outEvents != NULL) *outEvents = pending.events; - if (outData != NULL) *outData = pending.data; - } else { - mPendingFds.push(pending); - } - } else { -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - awoken", this); -#endif - char buffer[16]; - ssize_t nRead; - do { - nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); - } while (nRead == sizeof(buffer)); - } - - respondedCount -= 1; - if (respondedCount == 0) { - break; - } - } - } - -Done: - // Set mPolling to false and wake up the wakeAndLock() waiters. - mLock.lock(); - mPolling = false; - if (mWaiters != 0) { - mAwake.broadcast(); - } - mLock.unlock(); - - if (result == POLL_CALLBACK || result >= 0) { - size_t pendingCount = mPendingCallbacks.size(); - for (size_t i = 0; i < pendingCount; i++) { - const PendingCallback& pendingCallback = mPendingCallbacks.itemAt(i); -#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS - LOGD("%p ~ pollOnce - invoking callback for fd %d", this, pendingCallback.fd); -#endif - - bool keep = true; - if (pendingCallback.callback != NULL) { - keep = pendingCallback.callback(pendingCallback.fd, pendingCallback.events, - pendingCallback.data); - } else { - keep = pendingCallback.looperCallback(pendingCallback.fd, pendingCallback.events, - pendingCallback.data) != 0; - } - if (! keep) { - removeCallback(pendingCallback.fd); - } - } - } - -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - done", this); -#endif - return result; -} - -void PollLoop::wake() { -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ wake", this); -#endif - - ssize_t nWrite = write(mWakeWritePipeFd, "W", 1); - if (nWrite != 1) { - if (errno != EAGAIN) { - LOGW("Could not write wake signal, errno=%d", errno); - } - } -} - -bool PollLoop::getAllowNonCallbacks() const { - return mAllowNonCallbacks; -} - -void PollLoop::setCallback(int fd, int ident, int events, Callback callback, void* data) { - setCallbackCommon(fd, ident, events, callback, NULL, data); -} - -void PollLoop::setCallback(int fd, int events, Callback callback, void* data) { - setCallbackCommon(fd, POLL_CALLBACK, events, callback, NULL, data); -} - -void PollLoop::setLooperCallback(int fd, int ident, int events, ALooper_callbackFunc* callback, - void* data) { - setCallbackCommon(fd, ident, events, NULL, callback, data); -} - -void PollLoop::setCallbackCommon(int fd, int ident, int events, Callback callback, - ALooper_callbackFunc* looperCallback, void* data) { - -#if DEBUG_CALLBACKS - LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events); -#endif - - if (! events) { - LOGE("Invalid attempt to set a callback with no selected poll events."); - removeCallback(fd); - return; - } - - if (! callback && ! looperCallback && ! mAllowNonCallbacks) { - LOGE("Invalid attempt to set NULL callback but not allowed."); - removeCallback(fd); - return; - } - - wakeAndLock(); - - struct pollfd requestedFd; - requestedFd.fd = fd; - requestedFd.events = events; - - RequestedCallback requestedCallback; - requestedCallback.callback = callback; - requestedCallback.looperCallback = looperCallback; - requestedCallback.ident = ident; - requestedCallback.data = data; - - ssize_t index = getRequestIndexLocked(fd); - if (index < 0) { - mRequestedFds.push(requestedFd); - mRequestedCallbacks.push(requestedCallback); - } else { - mRequestedFds.replaceAt(requestedFd, size_t(index)); - mRequestedCallbacks.replaceAt(requestedCallback, size_t(index)); - } - - mLock.unlock(); -} - -bool PollLoop::removeCallback(int fd) { -#if DEBUG_CALLBACKS - LOGD("%p ~ removeCallback - fd=%d", this, fd); -#endif - - wakeAndLock(); - - ssize_t index = getRequestIndexLocked(fd); - if (index >= 0) { - mRequestedFds.removeAt(size_t(index)); - mRequestedCallbacks.removeAt(size_t(index)); - } - - mLock.unlock(); - return index >= 0; -} - -ssize_t PollLoop::getRequestIndexLocked(int fd) { - size_t requestCount = mRequestedFds.size(); - - for (size_t i = 0; i < requestCount; i++) { - if (mRequestedFds.itemAt(i).fd == fd) { - return i; - } - } - - return -1; -} - -void PollLoop::wakeAndLock() { - mLock.lock(); - - mWaiters += 1; - while (mPolling) { - wake(); - mAwake.wait(mLock); - } - - mWaiters -= 1; - if (mWaiters == 0) { - mResume.signal(); - } -} - -} // namespace android diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 725de9c..00077ee 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -7,7 +7,7 @@ ifneq ($(TARGET_SIMULATOR),true) # Build the unit tests. test_src_files := \ ObbFile_test.cpp \ - PollLoop_test.cpp \ + Looper_test.cpp \ String8_test.cpp shared_libraries := \ diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp new file mode 100644 index 0000000..afc92f8 --- /dev/null +++ b/libs/utils/tests/Looper_test.cpp @@ -0,0 +1,433 @@ +// +// Copyright 2010 The Android Open Source Project +// + +#include <utils/Looper.h> +#include <utils/Timers.h> +#include <utils/StopWatch.h> +#include <gtest/gtest.h> +#include <unistd.h> +#include <time.h> + +#include "TestHelpers.h" + +// # of milliseconds to fudge stopwatch measurements +#define TIMING_TOLERANCE_MS 25 + +namespace android { + +class DelayedWake : public DelayedTask { + sp<Looper> mLooper; + +public: + DelayedWake(int delayMillis, const sp<Looper> looper) : + DelayedTask(delayMillis), mLooper(looper) { + } + +protected: + virtual void doTask() { + mLooper->wake(); + } +}; + +class DelayedWriteSignal : public DelayedTask { + Pipe* mPipe; + +public: + DelayedWriteSignal(int delayMillis, Pipe* pipe) : + DelayedTask(delayMillis), mPipe(pipe) { + } + +protected: + virtual void doTask() { + mPipe->writeSignal(); + } +}; + +class CallbackHandler { +public: + void setCallback(const sp<Looper>& looper, int fd, int events) { + looper->addFd(fd, 0, events, staticHandler, this); + } + +protected: + virtual ~CallbackHandler() { } + + virtual int handler(int fd, int events) = 0; + +private: + static int staticHandler(int fd, int events, void* data) { + return static_cast<CallbackHandler*>(data)->handler(fd, events); + } +}; + +class StubCallbackHandler : public CallbackHandler { +public: + int nextResult; + int callbackCount; + + int fd; + int events; + + StubCallbackHandler(int nextResult) : nextResult(nextResult), + callbackCount(0), fd(-1), events(-1) { + } + +protected: + virtual int handler(int fd, int events) { + callbackCount += 1; + this->fd = fd; + this->events = events; + return nextResult; + } +}; + +class LooperTest : public testing::Test { +protected: + sp<Looper> mLooper; + + virtual void SetUp() { + mLooper = new Looper(true); + } + + virtual void TearDown() { + mLooper.clear(); + } +}; + + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeout) { + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturns) { + mLooper->wake(); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because wake() was called before waiting"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturns) { + sp<DelayedWake> delayedWake = new DelayedWake(100, mLooper); + delayedWake->run(); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal wake delay"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken"; +} + +TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturns) { + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; +} + +TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not have been invoked because FD was not signalled"; +} + +TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + + ASSERT_EQ(OK, pipe.writeSignal()); + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) + << "callback should have received ALOOPER_EVENT_INPUT as events"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not have been invoked because FD was not signalled"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + + pipe.writeSignal(); + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) + << "callback should have received ALOOPER_EVENT_INPUT as events"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + delayedWriteSignal->run(); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal signal delay"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) + << "callback should have received ALOOPER_EVENT_INPUT as events"; +} + +TEST_F(LooperTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + pipe.writeSignal(); // would cause FD to be considered signalled + mLooper->removeFd(pipe.receiveFd); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout because FD was no longer registered"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not be invoked"; +} + +TEST_F(LooperTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) { + Pipe pipe; + StubCallbackHandler handler(false); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + // First loop: Callback is registered and FD is signalled. + pipe.writeSignal(); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal zero because FD was already signalled"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked"; + + // Second loop: Callback is no longer registered and FD is signalled. + pipe.writeSignal(); + + stopWatch.reset(); + result = mLooper->pollOnce(0); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal zero because timeout was zero"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should not be invoked this time"; +} + +TEST_F(LooperTest, PollOnce_WhenNonCallbackFdIsSignalled_ReturnsIdent) { + const int expectedIdent = 5; + void* expectedData = this; + + Pipe pipe; + + pipe.writeSignal(); + mLooper->addFd(pipe.receiveFd, expectedIdent, ALOOPER_EVENT_INPUT, NULL, expectedData); + + StopWatch stopWatch("pollOnce"); + int fd; + int events; + void* data; + int result = mLooper->pollOnce(100, &fd, &events, &data); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(expectedIdent, result) + << "pollOnce result should be the ident of the FD that was signalled"; + EXPECT_EQ(pipe.receiveFd, fd) + << "pollOnce should have returned the received pipe fd"; + EXPECT_EQ(ALOOPER_EVENT_INPUT, events) + << "pollOnce should have returned ALOOPER_EVENT_INPUT as events"; + EXPECT_EQ(expectedData, data) + << "pollOnce should have returned the data"; +} + +TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) { + Pipe pipe; + int result = mLooper->addFd(pipe.receiveFd, 0, ALOOPER_EVENT_INPUT, NULL, NULL); + + EXPECT_EQ(1, result) + << "addFd should return 1 because FD was added"; +} + +TEST_F(LooperTest, AddFd_WhenEventsIsZero_ReturnsError) { + Pipe pipe; + int result = mLooper->addFd(pipe.receiveFd, 0, 0, NULL, NULL); + + EXPECT_EQ(-1, result) + << "addFd should return -1 because arguments were invalid"; +} + +TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) { + Pipe pipe; + int result = mLooper->addFd(pipe.receiveFd, -1, ALOOPER_EVENT_INPUT, NULL, NULL); + + EXPECT_EQ(-1, result) + << "addFd should return -1 because arguments were invalid"; +} + +TEST_F(LooperTest, AddFd_WhenNoCallbackAndAllowNonCallbacksIsFalse_ReturnsError) { + Pipe pipe; + sp<Looper> looper = new Looper(false /*allowNonCallbacks*/); + int result = looper->addFd(pipe.receiveFd, 0, 0, NULL, NULL); + + EXPECT_EQ(-1, result) + << "addFd should return -1 because arguments were invalid"; +} + +TEST_F(LooperTest, RemoveFd_WhenCallbackNotAdded_ReturnsZero) { + int result = mLooper->removeFd(1); + + EXPECT_EQ(0, result) + << "removeFd should return 0 because FD not registered"; +} + +TEST_F(LooperTest, RemoveFd_WhenCallbackAddedThenRemovedTwice_ReturnsOnceFirstTimeAndReturnsZeroSecondTime) { + Pipe pipe; + StubCallbackHandler handler(false); + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + // First time. + int result = mLooper->removeFd(pipe.receiveFd); + + EXPECT_EQ(1, result) + << "removeFd should return 1 first time because FD was registered"; + + // Second time. + result = mLooper->removeFd(pipe.receiveFd); + + EXPECT_EQ(0, result) + << "removeFd should return 0 second time because FD was no longer registered"; +} + +TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) { + Pipe pipe; + StubCallbackHandler handler1(true); + StubCallbackHandler handler2(true); + + handler1.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + handler2.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); // replace it + pipe.writeSignal(); // would cause FD to be considered signalled + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because FD was already signalled"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(0, handler1.callbackCount) + << "original handler callback should not be invoked because it was replaced"; + EXPECT_EQ(1, handler2.callbackCount) + << "replacement handler callback should be invoked"; +} + + +} // namespace android diff --git a/libs/utils/tests/PollLoop_test.cpp b/libs/utils/tests/PollLoop_test.cpp deleted file mode 100644 index 02f1808..0000000 --- a/libs/utils/tests/PollLoop_test.cpp +++ /dev/null @@ -1,370 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// - -#include <utils/PollLoop.h> -#include <utils/Timers.h> -#include <utils/StopWatch.h> -#include <gtest/gtest.h> -#include <unistd.h> -#include <time.h> - -#include "TestHelpers.h" - -// # of milliseconds to fudge stopwatch measurements -#define TIMING_TOLERANCE_MS 25 - -namespace android { - -class DelayedWake : public DelayedTask { - sp<PollLoop> mPollLoop; - -public: - DelayedWake(int delayMillis, const sp<PollLoop> pollLoop) : - DelayedTask(delayMillis), mPollLoop(pollLoop) { - } - -protected: - virtual void doTask() { - mPollLoop->wake(); - } -}; - -class DelayedWriteSignal : public DelayedTask { - Pipe* mPipe; - -public: - DelayedWriteSignal(int delayMillis, Pipe* pipe) : - DelayedTask(delayMillis), mPipe(pipe) { - } - -protected: - virtual void doTask() { - mPipe->writeSignal(); - } -}; - -class CallbackHandler { -public: - void setCallback(const sp<PollLoop>& pollLoop, int fd, int events) { - pollLoop->setCallback(fd, events, staticHandler, this); - } - -protected: - virtual ~CallbackHandler() { } - - virtual bool handler(int fd, int events) = 0; - -private: - static bool staticHandler(int fd, int events, void* data) { - return static_cast<CallbackHandler*>(data)->handler(fd, events); - } -}; - -class StubCallbackHandler : public CallbackHandler { -public: - bool nextResult; - int callbackCount; - - int fd; - int events; - - StubCallbackHandler(bool nextResult) : nextResult(nextResult), - callbackCount(0), fd(-1), events(-1) { - } - -protected: - virtual bool handler(int fd, int events) { - callbackCount += 1; - this->fd = fd; - this->events = events; - return nextResult; - } -}; - -class PollLoopTest : public testing::Test { -protected: - sp<PollLoop> mPollLoop; - - virtual void SetUp() { - mPollLoop = new PollLoop(false); - } - - virtual void TearDown() { - mPollLoop.clear(); - } -}; - - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeoutAndReturnsFalse) { - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal timeout"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturnsTrue) { - mPollLoop->wake(); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because wake() was called before waiting"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because loop was awoken"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturnsTrue) { - sp<DelayedWake> delayedWake = new DelayedWake(100, mPollLoop); - delayedWake->run(); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal wake delay"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because loop was awoken"; -} - -TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturnsFalse) { - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; -} - -TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturnsFalse) { - Pipe pipe; - StubCallbackHandler handler(true); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; - EXPECT_EQ(0, handler.callbackCount) - << "callback should not have been invoked because FD was not signalled"; -} - -TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturnsTrue) { - Pipe pipe; - StubCallbackHandler handler(true); - - ASSERT_EQ(OK, pipe.writeSignal()); - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked exactly once"; - EXPECT_EQ(pipe.receiveFd, handler.fd) - << "callback should have received pipe fd as parameter"; - EXPECT_EQ(POLL_IN, handler.events) - << "callback should have received POLL_IN as events"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturnsFalse) { - Pipe pipe; - StubCallbackHandler handler(true); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal timeout"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; - EXPECT_EQ(0, handler.callbackCount) - << "callback should not have been invoked because FD was not signalled"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturnsTrue) { - Pipe pipe; - StubCallbackHandler handler(true); - - pipe.writeSignal(); - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked exactly once"; - EXPECT_EQ(pipe.receiveFd, handler.fd) - << "callback should have received pipe fd as parameter"; - EXPECT_EQ(POLL_IN, handler.events) - << "callback should have received POLL_IN as events"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturnsTrue) { - Pipe pipe; - StubCallbackHandler handler(true); - sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - delayedWriteSignal->run(); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal signal delay"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked exactly once"; - EXPECT_EQ(pipe.receiveFd, handler.fd) - << "callback should have received pipe fd as parameter"; - EXPECT_EQ(POLL_IN, handler.events) - << "callback should have received POLL_IN as events"; -} - -TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) { - Pipe pipe; - StubCallbackHandler handler(true); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - pipe.writeSignal(); // would cause FD to be considered signalled - mPollLoop->removeCallback(pipe.receiveFd); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal timeout because FD was no longer registered"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; - EXPECT_EQ(0, handler.callbackCount) - << "callback should not be invoked"; -} - -TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) { - Pipe pipe; - StubCallbackHandler handler(false); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - // First loop: Callback is registered and FD is signalled. - pipe.writeSignal(); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal zero because FD was already signalled"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked"; - - // Second loop: Callback is no longer registered and FD is signalled. - pipe.writeSignal(); - - stopWatch.reset(); - result = mPollLoop->pollOnce(0); - elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal zero because timeout was zero"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should not be invoked this time"; -} - -TEST_F(PollLoopTest, RemoveCallback_WhenCallbackNotAdded_ReturnsFalse) { - bool result = mPollLoop->removeCallback(1); - - EXPECT_FALSE(result) - << "removeCallback should return false because FD not registered"; -} - -TEST_F(PollLoopTest, RemoveCallback_WhenCallbackAddedThenRemovedTwice_ReturnsTrueFirstTimeAndReturnsFalseSecondTime) { - Pipe pipe; - StubCallbackHandler handler(false); - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - // First time. - bool result = mPollLoop->removeCallback(pipe.receiveFd); - - EXPECT_TRUE(result) - << "removeCallback should return true first time because FD was registered"; - - // Second time. - result = mPollLoop->removeCallback(pipe.receiveFd); - - EXPECT_FALSE(result) - << "removeCallback should return false second time because FD was no longer registered"; -} - -TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) { - Pipe pipe; - StubCallbackHandler handler1(true); - StubCallbackHandler handler2(true); - - handler1.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - handler2.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); // replace it - pipe.writeSignal(); // would cause FD to be considered signalled - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because FD was already signalled"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(0, handler1.callbackCount) - << "original handler callback should not be invoked because it was replaced"; - EXPECT_EQ(1, handler2.callbackCount) - << "replacement handler callback should be invoked"; -} - - -} // namespace android 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.Criteria; 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 @@ interface ILocationProvider { 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.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.WorkSource; import android.util.Log; /** @@ -106,8 +107,8 @@ public abstract class LocationProvider { 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 abstract class LocationProvider { } 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 @@ public abstract class LocationProvider { * 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 @@ public abstract class LocationProvider { * 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/AudioEffect.java b/media/java/android/media/AudioEffect.java index 35038fa..ae67114 100644 --- a/media/java/android/media/AudioEffect.java +++ b/media/java/android/media/AudioEffect.java @@ -16,12 +16,14 @@ package android.media; -import android.util.Log; -import java.lang.ref.WeakReference; -import java.io.IOException; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.util.Log; +import java.io.IOException; +import java.lang.ref.WeakReference; import java.nio.ByteOrder; import java.nio.ByteBuffer; import java.util.UUID; @@ -839,6 +841,128 @@ public class AudioEffect { byte[] value); } + + // ------------------------------------------------------------------------- + // Audio Effect Control panel intents + // ------------------------------------------------------------------------- + + /** + * This intent launches an audio effect control panel UI. The goal of this intent is to enable + * separate implementations of music/media player applications and audio effect control + * application or services. This will allow platform vendors to offer more advanced control + * options for standard effects or control for platform specific effects. + * <p>The intent carries a number of extras used by the player application to communicate + * necessary pieces of information to the control panel application. + * <p>The calling application must use the + * {@link android.app.Activity#startActivityForResult(Intent, int)} method to launch the + * control panel so that its package name is indicated and used by the control panel + * application to keep track of changes for this particular application. + * <p>The android.media.EXTRA_AUDIO_SESSION extra will indicate an audio session to which the + * audio effects should be applied. If no audio session is specified, either one of the + * follownig will happen: + * - If an audio session was previously opened by the calling application with + * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intent, the effect changes will + * be applied to that session. + * - If no audio session is opened, the changes will be stored in the package specific storage + * area and applied whenever a new audio session is opened by this application. + * <p>The android.media.EXTRA_CONTENT_TYPE extra will help the control panel application + * customize both the UI layout and the default audio effect settings if none are already + * stored for the calling application. + * {@hide} pending API council approval + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL = + "android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL"; + + /** + * This intent indicates to the effect control application or service that a new audio session + * is opened and requires audio effects to be applied. This is different from + * {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} in that no UI should be displayed in + * this case. Music player applications can broadcast this intent before starting playback + * to make sure that any audio effect settings previously selected by the user are applied. + * <p>The effect control application receiving this intent will look for previously stored + * settings for the calling application, create all required audio effects and apply the + * effect settings to the specified audio session. + * <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the + * audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory. + * <p>If no stored settings are found for the calling application, default settings for the + * content type indicated by {@link #EXTRA_CONTENT_TYPE} will be applied. The default settings + * for a given content type are platform specific. + * {@hide} pending API council approval + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION = + "android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION"; + + /** + * This intent indicates to the effect control application or service that an audio session + * is closed and that effects should not be applied anymore. + * <p>The effect control application receiving this intent will delete all effects on this + * session and store current settings in package specific storage. + * <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the + * audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory. + * <p>It is good practice for applications to broadcast this intent when music playback stops + * and/or when exiting to free system resources consumed by audio effect engines. + * {@hide} pending API council approval + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION = + "android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION"; + + /** + * This extra indicates the ID of the audio session the effects should be applied to. + * <p>The extra value is of type int and is the audio session ID. + * @see android.media.MediaPlayer#setAudioSessionId(int) for details on audio sessions. + * {@hide} pending API council approval + */ + public static final String EXTRA_AUDIO_SESSION = "android.media.extra.AUDIO_SESSION"; + + /** + * This extra indicates the package name of the calling application for + * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and + * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents. + * <p>The extra value is a string containing the full package name. + * {@hide} pending API council approval + */ + public static final String EXTRA_PACKAGE_NAME = "android.media.extra.PACKAGE_NAME"; + + /** + * This extra indicates which type of content is played by the application. This information is + * used by the effect control application to customize UI and default effect settings. + * The content type is one of the following: + * <ul> + * <li>{@link #CONTENT_TYPE_MUSIC}</li> + * <li>{@link #CONTENT_TYPE_MOVIE}</li> + * <li>{@link #CONTENT_TYPE_GAME}</li> + * <li>{@link #CONTENT_TYPE_VOICE}</li> + * </ul> + * If omitted, the content type defaults to {@link #CONTENT_TYPE_MUSIC}. + * {@hide} pending API council approval + */ + public static final String EXTRA_CONTENT_TYPE = "android.media.extra.CONTENT_TYPE"; + + /** + * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is music + * {@hide} pending API council approval + */ + public static final int CONTENT_TYPE_MUSIC = 0; + /** + * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is video of movie + * {@hide} pending API council approval + */ + public static final int CONTENT_TYPE_MOVIE = 1; + /** + * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is game audio + * {@hide} pending API council approval + */ + public static final int CONTENT_TYPE_GAME = 2; + /** + * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is voice audio + * {@hide} pending API council approval + */ + public static final int CONTENT_TYPE_VOICE = 3; + + // --------------------------------------------------------- // Inner classes // -------------------- diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index b8403e1..280def9 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1210,7 +1210,6 @@ public class MediaPlayer * effect which can be applied on any sound source that directs a certain amount of its * energy to this effect. This amount is defined by setAuxEffectSendLevel(). * {@see #setAuxEffectSendLevel(float)}. - // TODO when AudioEffect is unhidden * <p>After creating an auxiliary effect (e.g. {@link android.media.EnvironmentalReverb}), * retrieve its ID with {@link android.media.AudioEffect#getId()} and use it when calling * this method to attach the player to the effect. diff --git a/native/android/input.cpp b/native/android/input.cpp index 57f0072..c753aa5 100644 --- a/native/android/input.cpp +++ b/native/android/input.cpp @@ -20,7 +20,7 @@ #include <android/input.h> #include <ui/Input.h> #include <ui/InputTransport.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> #include <utils/RefBase.h> #include <utils/Vector.h> @@ -250,7 +250,7 @@ float AMotionEvent_getHistoricalOrientation(AInputEvent* motion_event, size_t po void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper, - int ident, ALooper_callbackFunc* callback, void* data) { + int ident, ALooper_callbackFunc callback, void* data) { queue->attachLooper(looper, ident, callback, data); } diff --git a/native/android/looper.cpp b/native/android/looper.cpp index 0aeed77..9f5cda9 100644 --- a/native/android/looper.cpp +++ b/native/android/looper.cpp @@ -18,65 +18,56 @@ #include <utils/Log.h> #include <android/looper.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> -using android::PollLoop; +using android::Looper; using android::sp; ALooper* ALooper_forThread() { - return PollLoop::getForThread().get(); + return Looper::getForThread().get(); } -ALooper* ALooper_prepare(int32_t opts) { - bool allowFds = (opts&ALOOPER_PREPARE_ALLOW_NON_CALLBACKS) != 0; - sp<PollLoop> loop = PollLoop::getForThread(); - if (loop == NULL) { - loop = new PollLoop(allowFds); - PollLoop::setForThread(loop); - } - if (loop->getAllowNonCallbacks() != allowFds) { - LOGW("ALooper_prepare again with different ALOOPER_PREPARE_ALLOW_NON_CALLBACKS"); - } - return loop.get(); +ALooper* ALooper_prepare(int opts) { + return Looper::prepare(opts).get(); } -int32_t ALooper_pollOnce(int timeoutMillis, int* outEvents, void** outData) { - sp<PollLoop> loop = PollLoop::getForThread(); - if (loop == NULL) { - LOGW("ALooper_pollOnce: No looper for this thread!"); - return -1; - } - return loop->pollOnce(timeoutMillis, outEvents, outData); +void ALooper_acquire(ALooper* looper) { + static_cast<Looper*>(looper)->incStrong((void*)ALooper_acquire); } -int32_t ALooper_pollAll(int timeoutMillis, int* outEvents, void** outData) { - sp<PollLoop> loop = PollLoop::getForThread(); - if (loop == NULL) { - LOGW("ALooper_pollOnce: No looper for this thread!"); - return -1; - } - - int32_t result; - while ((result = loop->pollOnce(timeoutMillis, outEvents, outData)) == ALOOPER_POLL_CALLBACK) { - ; +void ALooper_release(ALooper* looper) { + static_cast<Looper*>(looper)->decStrong((void*)ALooper_acquire); +} + +int ALooper_pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { + sp<Looper> looper = Looper::getForThread(); + if (looper == NULL) { + LOGE("ALooper_pollOnce: No looper for this thread!"); + return ALOOPER_POLL_ERROR; } - - return result; + + return looper->pollOnce(timeoutMillis, outFd, outEvents, outData); } -void ALooper_acquire(ALooper* looper) { - static_cast<PollLoop*>(looper)->incStrong((void*)ALooper_acquire); +int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) { + sp<Looper> looper = Looper::getForThread(); + if (looper == NULL) { + LOGE("ALooper_pollAll: No looper for this thread!"); + return ALOOPER_POLL_ERROR; + } + + return looper->pollAll(timeoutMillis, outFd, outEvents, outData); } -void ALooper_release(ALooper* looper) { - static_cast<PollLoop*>(looper)->decStrong((void*)ALooper_acquire); +void ALooper_wake(ALooper* looper) { + static_cast<Looper*>(looper)->wake(); } -void ALooper_addFd(ALooper* looper, int fd, int ident, int events, - ALooper_callbackFunc* callback, void* data) { - static_cast<PollLoop*>(looper)->setLooperCallback(fd, ident, events, callback, data); +int ALooper_addFd(ALooper* looper, int fd, int ident, int events, + ALooper_callbackFunc callback, void* data) { + return static_cast<Looper*>(looper)->addFd(fd, ident, events, callback, data); } -int32_t ALooper_removeFd(ALooper* looper, int fd) { - return static_cast<PollLoop*>(looper)->removeCallback(fd) ? 1 : 0; +int ALooper_removeFd(ALooper* looper, int fd) { + return static_cast<Looper*>(looper)->removeFd(fd); } diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp index cf7635d..76c6eda 100644 --- a/native/android/sensor.cpp +++ b/native/android/sensor.cpp @@ -21,7 +21,7 @@ #include <android/sensor.h> #include <utils/RefBase.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> #include <utils/Timers.h> #include <gui/Sensor.h> @@ -60,12 +60,12 @@ ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type } ASensorEventQueue* ASensorManager_createEventQueue(ASensorManager* manager, - ALooper* looper, int ident, ALooper_callbackFunc* callback, void* data) + ALooper* looper, int ident, ALooper_callbackFunc callback, void* data) { sp<SensorEventQueue> queue = static_cast<SensorManager*>(manager)->createEventQueue(); if (queue != 0) { - ALooper_addFd(looper, queue->getFd(), ident, POLLIN, callback, data); + ALooper_addFd(looper, queue->getFd(), ident, ALOOPER_EVENT_INPUT, callback, data); queue->looper = looper; queue->incStrong(manager); } diff --git a/native/include/android/input.h b/native/include/android/input.h index 9da122b..c1134bf 100644 --- a/native/include/android/input.h +++ b/native/include/android/input.h @@ -645,7 +645,7 @@ typedef struct AInputQueue AInputQueue; * ALooper_addFd() for information on the ident, callback, and data params. */ void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper, - int ident, ALooper_callbackFunc* callback, void* data); + int ident, ALooper_callbackFunc callback, void* data); /* * Remove the input queue from the looper it is currently attached to. diff --git a/native/include/android/looper.h b/native/include/android/looper.h index 287bcd5..a63b744 100644 --- a/native/include/android/looper.h +++ b/native/include/android/looper.h @@ -18,8 +18,6 @@ #ifndef ANDROID_LOOPER_H #define ANDROID_LOOPER_H -#include <poll.h> - #ifdef __cplusplus extern "C" { #endif @@ -41,25 +39,14 @@ struct ALooper; typedef struct ALooper ALooper; /** - * For callback-based event loops, this is the prototype of the function - * that is called. It is given the file descriptor it is associated with, - * a bitmask of the poll events that were triggered (typically POLLIN), and - * the data pointer that was originally supplied. - * - * Implementations should return 1 to continue receiving callbacks, or 0 - * to have this file descriptor and callback unregistered from the looper. - */ -typedef int ALooper_callbackFunc(int fd, int events, void* data); - -/** - * Return the ALooper associated with the calling thread, or NULL if + * Returns the looper associated with the calling thread, or NULL if * there is not one. */ ALooper* ALooper_forThread(); enum { /** - * Option for ALooper_prepare: this ALooper will accept calls to + * Option for ALooper_prepare: this looper will accept calls to * ALooper_addFd() that do not have a callback (that is provide NULL * for the callback). In this case the caller of ALooper_pollOnce() * or ALooper_pollAll() MUST check the return from these functions to @@ -69,108 +56,188 @@ enum { }; /** - * Prepare an ALooper associated with the calling thread, and return it. - * If the thread already has an ALooper, it is returned. Otherwise, a new + * Prepares a looper associated with the calling thread, and returns it. + * If the thread already has a looper, it is returned. Otherwise, a new * one is created, associated with the thread, and returned. * * The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0. */ -ALooper* ALooper_prepare(int32_t opts); +ALooper* ALooper_prepare(int opts); enum { /** - * Result from ALooper_pollOnce() and ALooper_pollAll(): one or - * more callbacks were executed. + * Result from ALooper_pollOnce() and ALooper_pollAll(): + * The poll was awoken using wake() before the timeout expired + * and no callbacks were executed and no other file descriptors were ready. */ - ALOOPER_POLL_CALLBACK = -1, - + ALOOPER_POLL_WAKE = -1, + + /** + * Result from ALooper_pollOnce() and ALooper_pollAll(): + * One or more callbacks were executed. + */ + ALOOPER_POLL_CALLBACK = -2, + /** - * Result from ALooper_pollOnce() and ALooper_pollAll(): the - * timeout expired. + * Result from ALooper_pollOnce() and ALooper_pollAll(): + * The timeout expired. */ - ALOOPER_POLL_TIMEOUT = -2, - + ALOOPER_POLL_TIMEOUT = -3, + /** - * Result from ALooper_pollOnce() and ALooper_pollAll(): an error - * occurred. + * Result from ALooper_pollOnce() and ALooper_pollAll(): + * An error occurred. */ - ALOOPER_POLL_ERROR = -3, + ALOOPER_POLL_ERROR = -4, }; /** - * Wait for events to be available, with optional timeout in milliseconds. + * Acquire a reference on the given ALooper object. This prevents the object + * from being deleted until the reference is removed. This is only needed + * to safely hand an ALooper from one thread to another. + */ +void ALooper_acquire(ALooper* looper); + +/** + * Remove a reference that was previously acquired with ALooper_acquire(). + */ +void ALooper_release(ALooper* looper); + +/** + * Flags for file descriptor events that a looper can monitor. + * + * These flag bits can be combined to monitor multiple events at once. + */ +enum { + /** + * The file descriptor is available for read operations. + */ + ALOOPER_EVENT_INPUT = 1 << 0, + + /** + * The file descriptor is available for write operations. + */ + ALOOPER_EVENT_OUTPUT = 1 << 1, + + /** + * The file descriptor has encountered an error condition. + * + * The looper always sends notifications about errors; it is not necessary + * to specify this event flag in the requested event set. + */ + ALOOPER_EVENT_ERROR = 1 << 2, + + /** + * The file descriptor was hung up. + * For example, indicates that the remote end of a pipe or socket was closed. + * + * The looper always sends notifications about hangups; it is not necessary + * to specify this event flag in the requested event set. + */ + ALOOPER_EVENT_HANGUP = 1 << 3, +}; + +/** + * For callback-based event loops, this is the prototype of the function + * that is called. It is given the file descriptor it is associated with, + * a bitmask of the poll events that were triggered (typically ALOOPER_EVENT_INPUT), + * and the data pointer that was originally supplied. + * + * Implementations should return 1 to continue receiving callbacks, or 0 + * to have this file descriptor and callback unregistered from the looper. + */ +typedef int (*ALooper_callbackFunc)(int fd, int events, void* data); + +/** + * Waits for events to be available, with optional timeout in milliseconds. * Invokes callbacks for all file descriptors on which an event occurred. * * If the timeout is zero, returns immediately without blocking. * If the timeout is negative, waits indefinitely until an event appears. * - * Returns ALOOPER_POLL_CALLBACK if a callback was invoked. + * Returns ALOOPER_POLL_WAKE if the poll was awoken using wake() before + * the timeout expired and no callbacks were invoked and no other file + * descriptors were ready. + * + * Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked. * * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given * timeout expired. * - * Returns ALOPER_POLL_ERROR if an error occurred. + * Returns ALOOPER_POLL_ERROR if an error occurred. * * Returns a value >= 0 containing an identifier if its file descriptor has data * and it has no callback function (requiring the caller here to handle it). - * In this (and only this) case outEvents and outData will contain the poll - * events and data associated with the fd. + * In this (and only this) case outFd, outEvents and outData will contain the poll + * events and data associated with the fd, otherwise they will be set to NULL. * * This method does not return until it has finished invoking the appropriate callbacks * for all file descriptors that were signalled. */ -int32_t ALooper_pollOnce(int timeoutMillis, int* outEvents, void** outData); +int ALooper_pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData); /** * Like ALooper_pollOnce(), but performs all pending callbacks until all * data has been consumed or a file descriptor is available with no callback. * This function will never return ALOOPER_POLL_CALLBACK. */ -int32_t ALooper_pollAll(int timeoutMillis, int* outEvents, void** outData); - -/** - * Acquire a reference on the given ALooper object. This prevents the object - * from being deleted until the reference is removed. This is only needed - * to safely hand an ALooper from one thread to another. - */ -void ALooper_acquire(ALooper* looper); +int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData); /** - * Remove a reference that was previously acquired with ALooper_acquire(). + * Wakes the poll asynchronously. + * + * This method can be called on any thread. + * This method returns immediately. */ -void ALooper_release(ALooper* looper); +void ALooper_wake(ALooper* looper); /** - * Add a new file descriptor to be polled by the looper. If the same file - * descriptor was previously added, it is replaced. + * Adds a new file descriptor to be polled by the looper. + * If the same file descriptor was previously added, it is replaced. * * "fd" is the file descriptor to be added. - * "ident" is an identifier for this event, which is returned from - * ALooper_pollOnce(). Must be >= 0, or ALOOPER_POLL_CALLBACK if - * providing a non-NULL callback. - * "events" are the poll events to wake up on. Typically this is POLLIN. - * "callback" is the function to call when there is an event on the file - * descriptor. + * "ident" is an identifier for this event, which is returned from ALooper_pollOnce(). + * The identifier must be >= 0, or ALOOPER_POLL_CALLBACK if providing a non-NULL callback. + * "events" are the poll events to wake up on. Typically this is ALOOPER_EVENT_INPUT. + * "callback" is the function to call when there is an event on the file descriptor. * "data" is a private data pointer to supply to the callback. * * There are two main uses of this function: * - * (1) If "callback" is non-NULL, then - * this function will be called when there is data on the file descriptor. It - * should execute any events it has pending, appropriately reading from the - * file descriptor. The 'ident' is ignored in this case. + * (1) If "callback" is non-NULL, then this function will be called when there is + * data on the file descriptor. It should execute any events it has pending, + * appropriately reading from the file descriptor. The 'ident' is ignored in this case. * * (2) If "callback" is NULL, the 'ident' will be returned by ALooper_pollOnce * when its file descriptor has data available, requiring the caller to take * care of processing it. + * + * Returns 1 if the file descriptor was added or -1 if an error occurred. + * + * This method can be called on any thread. + * This method may block briefly if it needs to wake the poll. */ -void ALooper_addFd(ALooper* looper, int fd, int ident, int events, - ALooper_callbackFunc* callback, void* data); +int ALooper_addFd(ALooper* looper, int fd, int ident, int events, + ALooper_callbackFunc callback, void* data); /** - * Remove a previously added file descriptor from the looper. + * Removes a previously added file descriptor from the looper. + * + * When this method returns, it is safe to close the file descriptor since the looper + * will no longer have a reference to it. However, it is possible for the callback to + * already be running or for it to run one last time if the file descriptor was already + * signalled. Calling code is responsible for ensuring that this case is safely handled. + * For example, if the callback takes care of removing itself during its own execution either + * by returning 0 or by calling this method, then it can be guaranteed to not be invoked + * again at any later time unless registered anew. + * + * Returns 1 if the file descriptor was removed, 0 if none was previously registered + * or -1 if an error occurred. + * + * This method can be called on any thread. + * This method may block briefly if it needs to wake the poll. */ -int32_t ALooper_removeFd(ALooper* looper, int fd); +int ALooper_removeFd(ALooper* looper, int fd); #ifdef __cplusplus }; diff --git a/native/include/android/sensor.h b/native/include/android/sensor.h index a102d43..f163f18 100644 --- a/native/include/android/sensor.h +++ b/native/include/android/sensor.h @@ -166,7 +166,7 @@ ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type * Creates a new sensor event queue and associate it with a looper. */ ASensorEventQueue* ASensorManager_createEventQueue(ASensorManager* manager, - ALooper* looper, int ident, ALooper_callbackFunc* callback, void* data); + ALooper* looper, int ident, ALooper_callbackFunc callback, void* data); /* * Destroys the event queue and free all resources associated to it. 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 @@ public class StatusBarPolicy { 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 @@ public class StatusBarPolicy { 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/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.PowerManager; 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 @@ class BackupManagerService extends IBackupManager.Stub { 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 @@ class BackupManagerService extends IBackupManager.Stub { ? 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 @@ class BackupManagerService extends IBackupManager.Stub { } } + 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 a38970f..f3ce8ba 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -52,6 +52,7 @@ import android.os.Message; 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 @@ public class LocationManagerService extends ILocationManager.Stub implements Run 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; @@ -912,7 +919,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (enabled) { p.enable(); if (listeners > 0) { - p.setMinTime(getMinTimeLocked(provider)); + p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource); p.enableLocationTracking(true); } } else { @@ -924,9 +931,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run 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; @@ -1124,7 +1143,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run 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()) { @@ -1222,7 +1241,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run 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/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 685bee4..72784d5 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -2546,7 +2546,7 @@ class PackageManagerService extends IPackageManager.Stub { if (GET_CERTIFICATES) { if (ps != null && ps.codePath.equals(srcFile) - && ps.getTimeStamp() == srcFile.lastModified()) { + && ps.timeStamp == srcFile.lastModified()) { if (ps.signatures.mSignatures != null && ps.signatures.mSignatures.length != 0) { // Optimization: reuse the existing cached certificates @@ -3139,7 +3139,7 @@ class PackageManagerService extends IPackageManager.Stub { long scanFileTime = scanFile.lastModified(); final boolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0; - final boolean scanFileNewer = forceDex || scanFileTime != pkgSetting.getTimeStamp(); + final boolean scanFileNewer = forceDex || scanFileTime != pkgSetting.timeStamp; pkg.applicationInfo.processName = fixProcessName( pkg.applicationInfo.packageName, pkg.applicationInfo.processName, @@ -7007,7 +7007,7 @@ class PackageManagerService extends IPackageManager.Stub { } } pw.println("]"); - pw.print(" timeStamp="); pw.println(ps.getTimeStampStr()); + pw.print(" timeStamp="); pw.println(String.valueOf(ps.timeStamp)); pw.print(" signatures="); pw.println(ps.signatures); pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed); pw.print(" haveGids="); pw.println(ps.haveGids); @@ -7532,8 +7532,7 @@ class PackageManagerService extends IPackageManager.Stub { String resourcePathString; String nativeLibraryPathString; String obbPathString; - private long timeStamp; - private String timeStampString = "0"; + long timeStamp; int versionCode; boolean uidError; @@ -7590,23 +7589,7 @@ class PackageManagerService extends IPackageManager.Stub { } public void setTimeStamp(long newStamp) { - if (newStamp != timeStamp) { - timeStamp = newStamp; - timeStampString = Long.toString(newStamp); - } - } - - public void setTimeStamp(long newStamp, String newStampStr) { timeStamp = newStamp; - timeStampString = newStampStr; - } - - public long getTimeStamp() { - return timeStamp; - } - - public String getTimeStampStr() { - return timeStampString; } public void copyFrom(PackageSettingBase base) { @@ -7614,7 +7597,6 @@ class PackageManagerService extends IPackageManager.Stub { gids = base.gids; timeStamp = base.timeStamp; - timeStampString = base.timeStampString; signatures = base.signatures; permissionsFixed = base.permissionsFixed; haveGids = base.haveGids; @@ -8476,7 +8458,7 @@ class PackageManagerService extends IPackageManager.Stub { serializer.attribute(null, "realName", pkg.realName); } serializer.attribute(null, "codePath", pkg.codePathString); - serializer.attribute(null, "ts", pkg.getTimeStampStr()); + serializer.attribute(null, "ts", String.valueOf(pkg.timeStamp)); serializer.attribute(null, "version", String.valueOf(pkg.versionCode)); if (!pkg.resourcePathString.equals(pkg.codePathString)) { serializer.attribute(null, "resourcePath", pkg.resourcePathString); @@ -8528,7 +8510,7 @@ class PackageManagerService extends IPackageManager.Stub { } serializer.attribute(null, "flags", Integer.toString(pkg.pkgFlags)); - serializer.attribute(null, "ts", pkg.getTimeStampStr()); + serializer.attribute(null, "ts", String.valueOf(pkg.timeStamp)); serializer.attribute(null, "version", String.valueOf(pkg.versionCode)); if (pkg.sharedUser == null) { serializer.attribute(null, "userId", @@ -8888,7 +8870,7 @@ class PackageManagerService extends IPackageManager.Stub { if (timeStampStr != null) { try { long timeStamp = Long.parseLong(timeStampStr); - ps.setTimeStamp(timeStamp, timeStampStr); + ps.setTimeStamp(timeStamp); } catch (NumberFormatException e) { } } @@ -8936,7 +8918,6 @@ class PackageManagerService extends IPackageManager.Stub { String installerPackageName = null; String uidError = null; int pkgFlags = 0; - String timeStampStr; long timeStamp = 0; PackageSettingBase packageSetting = null; String version = null; @@ -8977,7 +8958,7 @@ class PackageManagerService extends IPackageManager.Stub { pkgFlags |= ApplicationInfo.FLAG_SYSTEM; } } - timeStampStr = parser.getAttributeValue(null, "ts"); + final String timeStampStr = parser.getAttributeValue(null, "ts"); if (timeStampStr != null) { try { timeStamp = Long.parseLong(timeStampStr); @@ -9013,7 +8994,7 @@ class PackageManagerService extends IPackageManager.Stub { + " while parsing settings at " + parser.getPositionDescription()); } else { - packageSetting.setTimeStamp(timeStamp, timeStampStr); + packageSetting.setTimeStamp(timeStamp); } } else if (sharedIdStr != null) { userId = sharedIdStr != null @@ -9022,7 +9003,7 @@ class PackageManagerService extends IPackageManager.Stub { packageSetting = new PendingPackage(name.intern(), realName, new File(codePathStr), new File(resourcePathStr), nativeLibraryPathStr, userId, versionCode, pkgFlags); - packageSetting.setTimeStamp(timeStamp, timeStampStr); + packageSetting.setTimeStamp(timeStamp); mPendingPackages.add((PendingPackage) packageSetting); if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name + ": sharedUserId=" + userId + " pkg=" diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 16f3f10..8ab1bb8 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -50,6 +50,7 @@ import android.os.Process; 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 @@ class PowerManagerService extends IPowerManager.Stub 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 @@ class PowerManagerService extends IPowerManager.Stub final int uid; final int pid; final int monitorType; + WorkSource ws; boolean activated = true; int minState; } @@ -630,35 +632,84 @@ class PowerManagerService extends IPowerManager.Stub || 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) { + if (wl.monitorType >= 0) { + long origId = Binder.clearCallingIdentity(); + 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 + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + + void noteStopWakeLocked(WakeLock wl, WorkSource ws) { + if (wl.monitorType >= 0) { + long origId = Binder.clearCallingIdentity(); + 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 + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + 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 +738,31 @@ class PowerManagerService extends IPowerManager.Stub 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 +803,36 @@ class PowerManagerService extends IPowerManager.Stub enableProximityLockLocked(); } } - if (newlock) { - acquireUid = wl.uid; - acquirePid = wl.pid; - acquireName = wl.tag; - acquireType = wl.monitorType; + + if (diffsource) { + // If the lock sources have changed, need to first release the + // old ones. + noteStopWakeLocked(wl, oldsource); + } + if (newlock || diffsource) { + noteStartWakeLocked(wl, ws); } + } - if (acquireType >= 0) { - try { - mBatteryStats.noteStartWakelock(acquireUid, acquirePid, acquireName, acquireType); - } catch (RemoteException e) { - // Ignore + 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); + } + 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); } } @@ -759,11 +848,6 @@ class PowerManagerService extends IPowerManager.Stub } 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,21 +888,8 @@ class PowerManagerService extends IPowerManager.Stub } // 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) { - long origId = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteStopWakelock(releaseUid, releasePid, releaseName, releaseType); - } catch (RemoteException e) { - // Ignore - } finally { - Binder.restoreCallingIdentity(origId); - } - } + noteStopWakeLocked(wl, wl.ws); } private class PokeLock implements IBinder.DeathRecipient diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 4a9c9e9..859de46 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -518,14 +518,8 @@ class ServerThread extends Thread { }); // For debug builds, log event loop stalls to dropbox for analysis. - // Similar logic also appears in ActivityThread.java for system apps. - if (!"user".equals(Build.TYPE)) { - Slog.i(TAG, "Enabling StrictMode for system server."); - StrictMode.setThreadPolicy( - StrictMode.DISALLOW_DISK_WRITE | - StrictMode.DISALLOW_DISK_READ | - StrictMode.DISALLOW_NETWORK | - StrictMode.PENALTY_DROPBOX); + if (StrictMode.conditionallyEnableDebugLogging()) { + Slog.i(TAG, "Enabled StrictMode for system server main thread."); } Looper.loop(); 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.RemoteException; 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 @@ public class VibratorService extends IVibratorService.Stub { 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 @@ public class VibratorService extends IVibratorService.Stub { 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 @@ public class VibratorService extends IVibratorService.Stub { 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 @@ public class VibratorService extends IVibratorService.Stub { != 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 @@ public class VibratorService extends IVibratorService.Stub { // 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 @@ public class VibratorService extends IVibratorService.Stub { != 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 @@ public class VibratorService extends IVibratorService.Stub { 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 @@ public class VibratorService extends IVibratorService.Stub { VibrateThread(Vibration vib) { mVibration = vib; + mTmpWorkSource.set(vib.mUid); + mWakeLock.setWorkSource(mTmpWorkSource); mWakeLock.acquire(); } diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java index 124da4e..c837a3a 100644 --- a/services/java/com/android/server/WallpaperManagerService.java +++ b/services/java/com/android/server/WallpaperManagerService.java @@ -727,9 +727,10 @@ class WallpaperManagerService extends IWallpaperManager.Stub { } } + // Called by SystemBackupAgent after files are restored to disk. void settingsRestored() { if (DEBUG) Slog.v(TAG, "settingsRestored"); - + boolean success = false; synchronized (mLock) { loadSettingsLocked(); @@ -766,7 +767,10 @@ class WallpaperManagerService extends IWallpaperManager.Stub { mName = ""; WALLPAPER_FILE.delete(); } - saveSettingsLocked(); + + synchronized (mLock) { + saveSettingsLocked(); + } } boolean restoreNamedResourceLocked() { diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 0eca082..f11c0f7 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -63,6 +63,7 @@ import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.WorkSource; import android.provider.Settings; import android.util.Slog; import android.text.TextUtils; @@ -2036,8 +2037,8 @@ public class WifiService extends IWifiManager.Stub { } 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() { @@ -2111,7 +2112,15 @@ public class WifiService extends IWifiManager.Stub { } } - public boolean acquireWifiLock(IBinder binder, int lockMode, 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 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 && @@ -2120,34 +2129,68 @@ public class WifiService extends IWifiManager.Stub { if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode); 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_FULL_HIGH_PERF: + /* Treat high power as a full lock for battery stats */ + 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_FULL_HIGH_PERF: + /* Treat high power as a full lock for battery stats */ + 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_FULL_HIGH_PERF: ++mFullHighPerfLocksAcquired; - /* Treat high power as a full lock for battery stats */ - mBatteryStats.noteFullWifiLockAcquired(uid); break; - case WifiManager.WIFI_MODE_SCAN_ONLY: ++mScanLocksAcquired; - mBatteryStats.noteScanWifiLockAcquired(uid); break; } } catch (RemoteException e) { @@ -2159,6 +2202,33 @@ public class WifiService extends IWifiManager.Stub { 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) { @@ -2176,21 +2246,18 @@ public class WifiService extends IWifiManager.Stub { 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_FULL_HIGH_PERF: ++mFullHighPerfLocksReleased; - mBatteryStats.noteFullWifiLockReleased(uid); break; case WifiManager.WIFI_MODE_SCAN_ONLY: ++mScanLocksReleased; - mBatteryStats.noteScanWifiLockReleased(uid); break; } } catch (RemoteException e) { @@ -2208,12 +2275,14 @@ public class WifiService extends IWifiManager.Stub { 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) { @@ -2228,7 +2297,7 @@ public class WifiService extends IWifiManager.Stub { 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 2cdf31e..cf767ca 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -5985,12 +5985,18 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public class ActivityStack { // 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.IBinder; 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 final class BatteryStatsService extends IBatteryStats.Stub { } } + 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 final class BatteryStatsService extends IBatteryStats.Stub { } } + 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.Process; 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 @@ public class GpsLocationProvider implements LocationProviderInterface { 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 class GpsLocationProvider implements LocationProviderInterface { 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.Criteria; 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 @@ public interface LocationProviderInterface { /* 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.Handler; 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 @@ public class LocationProviderProxy implements LocationProviderInterface { 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 @@ public class LocationProviderProxy implements LocationProviderInterface { provider.enableLocationTracking(true); } if (mMinTime >= 0) { - provider.setMinTime(mMinTime); + provider.setMinTime(mMinTime, mMinTimeSource); } if (mNetworkInfo != null) { provider.updateNetworkState(mNetworkState, mNetworkInfo); @@ -318,6 +320,7 @@ public class LocationProviderProxy implements LocationProviderInterface { mLocationTracking = enable; if (!enable) { mMinTime = -1; + mMinTimeSource.clear(); } ILocationProvider provider; synchronized (mServiceConnection) { @@ -339,15 +342,16 @@ public class LocationProviderProxy implements LocationProviderInterface { 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.location.LocationProvider; 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 @@ public class MockProvider implements LocationProviderInterface { 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.location.LocationProvider; import android.net.NetworkInfo; import android.os.Bundle; import android.os.RemoteException; +import android.os.WorkSource; import android.util.Log; /** @@ -123,7 +124,7 @@ public class PassiveProvider implements LocationProviderInterface { 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/sip/SipService.java b/services/java/com/android/server/sip/SipService.java index c3786f5..c1841f6 100644 --- a/services/java/com/android/server/sip/SipService.java +++ b/services/java/com/android/server/sip/SipService.java @@ -404,6 +404,7 @@ public final class SipService extends ISipService.Stub { public void onConnectivityChanged(boolean connected) throws SipException { + mSipGroup.onConnectivityChanged(); if (connected) { resetGroup(mLocalIp); if (mOpened) openToReceiveCalls(); @@ -770,6 +771,23 @@ public final class SipService extends ISipService.Stub { b.get(ConnectivityManager.EXTRA_NETWORK_INFO); String type = netInfo.getTypeName(); NetworkInfo.State state = netInfo.getState(); + + NetworkInfo activeNetInfo = getActiveNetworkInfo(); + if (activeNetInfo != null) { + Log.v(TAG, "active network: " + activeNetInfo.getTypeName() + + ((activeNetInfo.getState() == NetworkInfo.State.CONNECTED) + ? " CONNECTED" : " DISCONNECTED")); + } else { + Log.v(TAG, "active network: null"); + } + if ((state == NetworkInfo.State.CONNECTED) + && (activeNetInfo != null) + && (activeNetInfo.getType() != netInfo.getType())) { + Log.d(TAG, "ignore connect event: " + type + + ", active: " + activeNetInfo.getTypeName()); + return; + } + if (state == NetworkInfo.State.CONNECTED) { Log.v(TAG, "Connectivity alert: CONNECTED " + type); onChanged(type, true); @@ -784,6 +802,12 @@ public final class SipService extends ISipService.Stub { } } + private NetworkInfo getActiveNetworkInfo() { + ConnectivityManager cm = (ConnectivityManager) + mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + return cm.getActiveNetworkInfo(); + } + private void onChanged(String type, boolean connected) { synchronized (SipService.this) { // When turning on WIFI, it needs some time for network diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/services/java/com/android/server/sip/SipSessionGroup.java index da8e9b8..06b6ec9 100644 --- a/services/java/com/android/server/sip/SipSessionGroup.java +++ b/services/java/com/android/server/sip/SipSessionGroup.java @@ -19,6 +19,7 @@ package com.android.server.sip; import gov.nist.javax.sip.clientauthutils.AccountManager; import gov.nist.javax.sip.clientauthutils.UserCredentials; import gov.nist.javax.sip.header.SIPHeaderNames; +import gov.nist.javax.sip.header.ProxyAuthenticate; import gov.nist.javax.sip.header.WWWAuthenticate; import gov.nist.javax.sip.message.SIPMessage; @@ -153,6 +154,13 @@ class SipSessionGroup implements SipListener { mSessionMap.clear(); } + synchronized void onConnectivityChanged() { + for (SipSessionImpl s : mSessionMap.values()) { + s.onError(SipErrorCode.DATA_CONNECTION_LOST, + "data connection lost"); + } + } + public SipProfile getLocalProfile() { return mLocalProfile; } @@ -210,10 +218,10 @@ class SipSessionGroup implements SipListener { private synchronized SipSessionImpl getSipSession(EventObject event) { String key = SipHelper.getCallId(event); - Log.d(TAG, " sesssion key from event: " + key); - Log.d(TAG, " active sessions:"); + Log.d(TAG, "sesssion key from event: " + key); + Log.d(TAG, "active sessions:"); for (String k : mSessionMap.keySet()) { - Log.d(TAG, " ..... '" + k + "': " + mSessionMap.get(k)); + Log.d(TAG, " ..." + k + ": " + mSessionMap.get(k)); } SipSessionImpl session = mSessionMap.get(key); return ((session != null) ? session : mCallReceiverSession); @@ -222,7 +230,7 @@ class SipSessionGroup implements SipListener { private synchronized void addSipSession(SipSessionImpl newSession) { removeSipSession(newSession); String key = newSession.getCallId(); - Log.d(TAG, " +++++ add a session with key: '" + key + "'"); + Log.d(TAG, "+++ add a session with key: '" + key + "'"); mSessionMap.put(key, newSession); for (String k : mSessionMap.keySet()) { Log.d(TAG, " ..... " + k + ": " + mSessionMap.get(k)); @@ -724,7 +732,8 @@ class SipSessionGroup implements SipListener { Response response = event.getResponse(); String nonce = getNonceFromResponse(response); if (((nonce != null) && nonce.equals(mLastNonce)) || - (nonce == mLastNonce)) { + (nonce == null)) { + mLastNonce = nonce; return false; } else { mClientTransaction = mSipHelper.handleChallenge( @@ -757,9 +766,12 @@ class SipSessionGroup implements SipListener { } private String getNonceFromResponse(Response response) { - WWWAuthenticate authHeader = (WWWAuthenticate)(response.getHeader( - SIPHeaderNames.WWW_AUTHENTICATE)); - return (authHeader == null) ? null : authHeader.getNonce(); + WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader( + SIPHeaderNames.WWW_AUTHENTICATE); + if (wwwAuth != null) return wwwAuth.getNonce(); + ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader( + SIPHeaderNames.PROXY_AUTHENTICATE); + return (proxyAuth == null) ? null : proxyAuth.getNonce(); } private boolean readyForCall(EventObject evt) throws SipException { @@ -998,7 +1010,8 @@ class SipSessionGroup implements SipListener { onRegistrationFailed(errorCode, message); break; default: - if (mInCall) { + if ((errorCode != SipErrorCode.DATA_CONNECTION_LOST) + && mInCall) { fallbackToPreviousInCall(errorCode, message); } else { endCallOnError(errorCode, message); @@ -1148,7 +1161,7 @@ class SipSessionGroup implements SipListener { .setPort(uri.getPort()) .setDisplayName(address.getDisplayName()) .build(); - } catch (InvalidArgumentException e) { + } catch (IllegalArgumentException e) { throw new SipException("createPeerProfile()", e); } catch (ParseException e) { throw new SipException("createPeerProfile()", e); diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 3025f77..e204e04 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -529,7 +529,7 @@ status_t SensorService::SensorEventConnection::sendEvents( LOGE_IF(size<0, "dropping %d events on the floor (%s)", count, strerror(-size)); - return size < 0 ? size : NO_ERROR; + return size < 0 ? status_t(size) : status_t(NO_ERROR); } sp<SensorChannel> SensorService::SensorEventConnection::getSensorChannel() const diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp index e464713..42bf983 100644 --- a/services/sensorservice/tests/sensorservicetest.cpp +++ b/services/sensorservice/tests/sensorservicetest.cpp @@ -18,11 +18,11 @@ #include <gui/Sensor.h> #include <gui/SensorManager.h> #include <gui/SensorEventQueue.h> -#include <utils/PollLoop.h> +#include <utils/Looper.h> using namespace android; -bool receiver(int fd, int events, void* data) +int receiver(int fd, int events, void* data) { sp<SensorEventQueue> q((SensorEventQueue*)data); ssize_t n; @@ -41,7 +41,7 @@ bool receiver(int fd, int events, void* data) if (n<0 && n != -EAGAIN) { printf("error reading events (%s)\n", strerror(-n)); } - return true; + return 1; } @@ -51,7 +51,7 @@ int main(int argc, char** argv) Sensor const* const* list; ssize_t count = mgr.getSensorList(&list); - printf("numSensors=%d\n", count); + printf("numSensors=%d\n", int(count)); sp<SensorEventQueue> q = mgr.createEventQueue(); printf("queue=%p\n", q.get()); @@ -63,13 +63,16 @@ int main(int argc, char** argv) q->setEventRate(accelerometer, ms2ns(10)); - sp<PollLoop> loop = new PollLoop(false); - loop->setCallback(q->getFd(), POLLIN, receiver, q.get()); + sp<Looper> loop = new Looper(false); + loop->addFd(q->getFd(), 0, ALOOPER_EVENT_INPUT, receiver, q.get()); do { //printf("about to poll...\n"); - int32_t ret = loop->pollOnce(-1, 0, 0); + int32_t ret = loop->pollOnce(-1); switch (ret) { + case ALOOPER_POLL_WAKE: + //("ALOOPER_POLL_WAKE\n"); + break; case ALOOPER_POLL_CALLBACK: //("ALOOPER_POLL_CALLBACK\n"); break; diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp index 2eac0a8..0515110 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -296,6 +296,10 @@ status_t DisplayHardware::compositionComplete() const { return mNativeWindow->compositionComplete(); } +int DisplayHardware::getCurrentBufferIndex() const { + return mNativeWindow->getCurrentBufferIndex(); +} + void DisplayHardware::flip(const Region& dirty) const { checkGLErrors(); diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h index 66bf521..2d7900c 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -87,6 +87,9 @@ public: return Rect(mWidth, mHeight); } + // only for debugging + int getCurrentBufferIndex() const; + private: void init(uint32_t displayIndex) __attribute__((noinline)); void fini() __attribute__((noinline)); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 637ae48..f199ca9 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -38,6 +38,7 @@ #include <utils/StopWatch.h> #include <ui/GraphicBufferAllocator.h> +#include <ui/GraphicLog.h> #include <ui/PixelFormat.h> #include <pixelflinger/pixelflinger.h> @@ -371,15 +372,25 @@ bool SurfaceFlinger::threadLoop() const DisplayHardware& hw(graphicPlane(0).displayHardware()); if (LIKELY(hw.canDraw() && !isFrozen())) { // repaint the framebuffer (if needed) + + const int index = hw.getCurrentBufferIndex(); + GraphicLog& logger(GraphicLog::getInstance()); + + logger.log(GraphicLog::SF_REPAINT, index); handleRepaint(); // inform the h/w that we're done compositing + logger.log(GraphicLog::SF_COMPOSITION_COMPLETE, index); hw.compositionComplete(); // release the clients before we flip ('cause flip might block) + logger.log(GraphicLog::SF_UNLOCK_CLIENTS, index); unlockClients(); + logger.log(GraphicLog::SF_SWAP_BUFFERS, index); postFramebuffer(); + + logger.log(GraphicLog::SF_REPAINT_DONE, index); } else { // pretend we did the post unlockClients(); @@ -1470,8 +1481,7 @@ status_t SurfaceFlinger::onTransact( int n; switch (code) { case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE - return NO_ERROR; - case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE + case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE return NO_ERROR; case 1002: // SHOW_UPDATES n = data.readInt32(); @@ -1492,6 +1502,11 @@ status_t SurfaceFlinger::onTransact( setTransactionFlags(eTransactionNeeded|eTraversalNeeded); return NO_ERROR; } + case 1006:{ // enable/disable GraphicLog + int enabled = data.readInt32(); + GraphicLog::getInstance().setEnabled(enabled); + return NO_ERROR; + } case 1007: // set mFreezeCount mFreezeCount = data.readInt32(); mFreezeDisplayTime = 0; diff --git a/telephony/java/android/telephony/gsm/GsmCellLocation.java b/telephony/java/android/telephony/gsm/GsmCellLocation.java index c4204fa..313bc82 100644 --- a/telephony/java/android/telephony/gsm/GsmCellLocation.java +++ b/telephony/java/android/telephony/gsm/GsmCellLocation.java @@ -60,8 +60,10 @@ public class GsmCellLocation extends CellLocation { } /** + * On a UMTS network, returns the primary scrambling code of the serving + * cell. + * * @return primary scrambling code for UMTS, -1 if unknown or GSM - * @hide */ public int getPsc() { return mPsc; diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java index 4bf3282..8bd789c 100644 --- a/telephony/java/com/android/internal/telephony/CallManager.java +++ b/telephony/java/com/android/internal/telephony/CallManager.java @@ -24,6 +24,7 @@ import android.os.AsyncResult; import android.os.Handler; import android.os.Message; import android.os.RegistrantList; +import android.os.Registrant; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.util.Log; @@ -75,6 +76,7 @@ public final class CallManager { private static final int EVENT_SUBSCRIPTION_INFO_READY = 116; private static final int EVENT_SUPP_SERVICE_FAILED = 117; private static final int EVENT_SERVICE_STATE_CHANGED = 118; + private static final int EVENT_POST_DIAL_CHARACTER = 119; // Singleton instance private static final CallManager INSTANCE = new CallManager(); @@ -158,6 +160,9 @@ public final class CallManager { protected final RegistrantList mServiceStateChangedRegistrants = new RegistrantList(); + protected final RegistrantList mPostDialCharacterRegistrants + = new RegistrantList(); + private CallManager() { mPhones = new ArrayList<Phone>(); mRingingCalls = new ArrayList<Call>(); @@ -334,6 +339,7 @@ public final class CallManager { } private void registerForPhoneStates(Phone phone) { + // for common events supported by all phones phone.registerForPreciseCallStateChanged(mHandler, EVENT_PRECISE_CALL_STATE_CHANGED, null); phone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null); phone.registerForNewRingingConnection(mHandler, EVENT_NEW_RINGING_CONNECTION, null); @@ -342,20 +348,31 @@ public final class CallManager { phone.registerForRingbackTone(mHandler, EVENT_RINGBACK_TONE, null); phone.registerForInCallVoicePrivacyOn(mHandler, EVENT_IN_CALL_VOICE_PRIVACY_ON, null); phone.registerForInCallVoicePrivacyOff(mHandler, EVENT_IN_CALL_VOICE_PRIVACY_OFF, null); - phone.registerForCallWaiting(mHandler, EVENT_CALL_WAITING, null); phone.registerForDisplayInfo(mHandler, EVENT_DISPLAY_INFO, null); phone.registerForSignalInfo(mHandler, EVENT_SIGNAL_INFO, null); - phone.registerForCdmaOtaStatusChange(mHandler, EVENT_CDMA_OTA_STATUS_CHANGE, null); phone.registerForResendIncallMute(mHandler, EVENT_RESEND_INCALL_MUTE, null); phone.registerForMmiInitiate(mHandler, EVENT_MMI_INITIATE, null); phone.registerForMmiComplete(mHandler, EVENT_MMI_COMPLETE, null); - phone.registerForEcmTimerReset(mHandler, EVENT_ECM_TIMER_RESET, null); - phone.registerForSubscriptionInfoReady(mHandler, EVENT_SUBSCRIPTION_INFO_READY, null); phone.registerForSuppServiceFailed(mHandler, EVENT_SUPP_SERVICE_FAILED, null); phone.registerForServiceStateChanged(mHandler, EVENT_SERVICE_STATE_CHANGED, null); + + // for events supported only by GSM and CDMA phone + if (phone.getPhoneType() == Phone.PHONE_TYPE_GSM || + phone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { + phone.setOnPostDialCharacter(mHandler, EVENT_POST_DIAL_CHARACTER, null); + } + + // for events supported only by CDMA phone + if (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA ){ + phone.registerForCdmaOtaStatusChange(mHandler, EVENT_CDMA_OTA_STATUS_CHANGE, null); + phone.registerForSubscriptionInfoReady(mHandler, EVENT_SUBSCRIPTION_INFO_READY, null); + phone.registerForCallWaiting(mHandler, EVENT_CALL_WAITING, null); + phone.registerForEcmTimerReset(mHandler, EVENT_ECM_TIMER_RESET, null); + } } private void unregisterForPhoneStates(Phone phone) { + // for common events supported by all phones phone.unregisterForPreciseCallStateChanged(mHandler); phone.unregisterForDisconnect(mHandler); phone.unregisterForNewRingingConnection(mHandler); @@ -364,17 +381,27 @@ public final class CallManager { phone.unregisterForRingbackTone(mHandler); phone.unregisterForInCallVoicePrivacyOn(mHandler); phone.unregisterForInCallVoicePrivacyOff(mHandler); - phone.unregisterForCallWaiting(mHandler); phone.unregisterForDisplayInfo(mHandler); phone.unregisterForSignalInfo(mHandler); - phone.unregisterForCdmaOtaStatusChange(mHandler); phone.unregisterForResendIncallMute(mHandler); phone.unregisterForMmiInitiate(mHandler); phone.unregisterForMmiComplete(mHandler); - phone.unregisterForEcmTimerReset(mHandler); - phone.unregisterForSubscriptionInfoReady(mHandler); phone.unregisterForSuppServiceFailed(mHandler); phone.unregisterForServiceStateChanged(mHandler); + + // for events supported only by GSM and CDMA phone + if (phone.getPhoneType() == Phone.PHONE_TYPE_GSM || + phone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { + phone.setOnPostDialCharacter(null, EVENT_POST_DIAL_CHARACTER, null); + } + + // for events supported only by CDMA phone + if (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA ){ + phone.unregisterForCdmaOtaStatusChange(mHandler); + phone.unregisterForSubscriptionInfoReady(mHandler); + phone.unregisterForCallWaiting(mHandler); + phone.unregisterForEcmTimerReset(mHandler); + } } /** @@ -1132,6 +1159,46 @@ public final class CallManager { mSubscriptionInfoReadyRegistrants.remove(h); } + /** + * Sets an event to be fired when the telephony system processes + * a post-dial character on an outgoing call.<p> + * + * Messages of type <code>what</code> will be sent to <code>h</code>. + * The <code>obj</code> field of these Message's will be instances of + * <code>AsyncResult</code>. <code>Message.obj.result</code> will be + * a Connection object.<p> + * + * Message.arg1 will be the post dial character being processed, + * or 0 ('\0') if end of string.<p> + * + * If Connection.getPostDialState() == WAIT, + * the application must call + * {@link com.android.internal.telephony.Connection#proceedAfterWaitChar() + * Connection.proceedAfterWaitChar()} or + * {@link com.android.internal.telephony.Connection#cancelPostDial() + * Connection.cancelPostDial()} + * for the telephony system to continue playing the post-dial + * DTMF sequence.<p> + * + * If Connection.getPostDialState() == WILD, + * the application must call + * {@link com.android.internal.telephony.Connection#proceedAfterWildChar + * Connection.proceedAfterWildChar()} + * or + * {@link com.android.internal.telephony.Connection#cancelPostDial() + * Connection.cancelPostDial()} + * for the telephony system to continue playing the + * post-dial DTMF sequence.<p> + * + */ + public void registerForPostDialCharacter(Handler h, int what, Object obj){ + mPostDialCharacterRegistrants.addUnique(h, what, obj); + } + + public void unregisterForPostDialCharacter(Handler h){ + mPostDialCharacterRegistrants.remove(h); + } + /* APIs to access foregroudCalls, backgroudCalls, and ringingCalls * 1. APIs to access list of calls * 2. APIs to check if any active call, which has connection other than @@ -1144,22 +1211,22 @@ public final class CallManager { /** * @return list of all ringing calls */ - public ArrayList<Call> getRingingCalls() { - return mRingingCalls; + public List<Call> getRingingCalls() { + return Collections.unmodifiableList(mRingingCalls); } /** * @return list of all foreground calls */ - public ArrayList<Call> getForegroundCalls() { - return mForegroundCalls; + public List<Call> getForegroundCalls() { + return Collections.unmodifiableList(mForegroundCalls); } /** * @return list of all background calls */ - public ArrayList<Call> getBackgroundCalls() { - return mBackgroundCalls; + public List<Call> getBackgroundCalls() { + return Collections.unmodifiableList(mBackgroundCalls); } /** @@ -1269,7 +1336,7 @@ public final class CallManager { /** * @return the connections of active foreground call - * return null if there is no active foreground call + * return empty list if there is no active foreground call */ public List<Connection> getFgCallConnections() { Call fgCall = getActiveFgCall(); @@ -1408,6 +1475,18 @@ public final class CallManager { break; case EVENT_SERVICE_STATE_CHANGED: mServiceStateChangedRegistrants.notifyRegistrants((AsyncResult) msg.obj); + break; + case EVENT_POST_DIAL_CHARACTER: + // we need send the character that is being processed in msg.arg1 + // so can't use notifyRegistrants() + for(int i=0; i < mPostDialCharacterRegistrants.size(); i++) { + Message notifyMsg; + notifyMsg = ((Registrant)mPostDialCharacterRegistrants.get(i)).messageForRegistrant(); + notifyMsg.obj = msg.obj; + notifyMsg.arg1 = msg.arg1; + notifyMsg.sendToTarget(); + } + break; } } }; diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java index 2c99145..943f21c 100755 --- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java +++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java @@ -635,9 +635,9 @@ public class SipPhone extends SipPhoneBase { @Override protected void onError(DisconnectCause cause) { Log.w(LOG_TAG, "SIP error: " + cause); - if (mSipAudioCall.isInCall()) { - // Don't end the call when in call. - // TODO: how to deliver the error to PhoneApp + if (mSipAudioCall.isInCall() + && (cause != DisconnectCause.LOST_SIGNAL)) { + // Don't end the call when in a call. return; } @@ -819,9 +819,9 @@ public class SipPhone extends SipPhoneBase { } @Override - public void onError(SipAudioCall call, String errorCode, + public void onError(SipAudioCall call, SipErrorCode errorCode, String errorMessage) { - switch (Enum.valueOf(SipErrorCode.class, errorCode)) { + switch (errorCode) { case INVALID_REMOTE_URI: onError(Connection.DisconnectCause.INVALID_NUMBER); break; @@ -829,6 +829,9 @@ public class SipPhone extends SipPhoneBase { case TRANSACTION_TERMINTED: onError(Connection.DisconnectCause.TIMED_OUT); break; + case DATA_CONNECTION_LOST: + onError(Connection.DisconnectCause.LOST_SIGNAL); + break; case INVALID_CREDENTIALS: onError(Connection.DisconnectCause.INVALID_CREDENTIALS); break; diff --git a/tests/CoreTests/android/core/SSLPerformanceTest.java b/tests/CoreTests/android/core/SSLPerformanceTest.java index fd87e89..5b5be0a 100644 --- a/tests/CoreTests/android/core/SSLPerformanceTest.java +++ b/tests/CoreTests/android/core/SSLPerformanceTest.java @@ -211,17 +211,17 @@ public class SSLPerformanceTest extends AndroidTestCase { deleteDirectory(); OpenSSLContextImpl sslContext = new OpenSSLContextImpl(); - sslContext.engineInit(null, null, null, - FileClientSessionCache.usingDirectory(getCacheDirectory()), - null); + sslContext.engineInit(null, null, null); + sslContext.engineGetClientSessionContext().setPersistentCache( + FileClientSessionCache.usingDirectory(getCacheDirectory())); // Make sure www.google.com is in the cache. getVerisignDotCom(sslContext); // Re-initialize so we hit the file cache. - sslContext.engineInit(null, null, null, - FileClientSessionCache.usingDirectory(getCacheDirectory()), - null); + sslContext.engineInit(null, null, null); + sslContext.engineGetClientSessionContext().setPersistentCache( + FileClientSessionCache.usingDirectory(getCacheDirectory())); Stopwatch stopwatch = new Stopwatch(); diff --git a/tests/CoreTests/android/core/SSLSocketTest.java b/tests/CoreTests/android/core/SSLSocketTest.java index 021df80..03905e1 100644 --- a/tests/CoreTests/android/core/SSLSocketTest.java +++ b/tests/CoreTests/android/core/SSLSocketTest.java @@ -911,7 +911,8 @@ public class SSLSocketTest extends TestCase { // Cache size = 2. FakeClientSessionCache fakeCache = new FakeClientSessionCache(); - context.engineInit(null, null, null, fakeCache, null); + context.engineInit(null, null, null); + context.engineGetClientSessionContext().setPersistentCache(fakeCache); SSLSocketFactory socketFactory = context.engineGetSocketFactory(); context.engineGetClientSessionContext().setSessionCacheSize(2); makeRequests(socketFactory); @@ -933,7 +934,8 @@ public class SSLSocketTest extends TestCase { // Cache size = 3. fakeCache = new FakeClientSessionCache(); - context.engineInit(null, null, null, fakeCache, null); + context.engineInit(null, null, null); + context.engineGetClientSessionContext().setPersistentCache(fakeCache); socketFactory = context.engineGetSocketFactory(); context.engineGetClientSessionContext().setSessionCacheSize(3); makeRequests(socketFactory); @@ -952,7 +954,8 @@ public class SSLSocketTest extends TestCase { // Cache size = 4. fakeCache = new FakeClientSessionCache(); - context.engineInit(null, null, null, fakeCache, null); + context.engineInit(null, null, null); + context.engineGetClientSessionContext().setPersistentCache(fakeCache); socketFactory = context.engineGetSocketFactory(); context.engineGetClientSessionContext().setSessionCacheSize(4); makeRequests(socketFactory); @@ -1010,7 +1013,8 @@ public class SSLSocketTest extends TestCase { try { ClientSessionCacheProxy cacheProxy = new ClientSessionCacheProxy(fileCache); - context.engineInit(null, null, null, cacheProxy, null); + context.engineInit(null, null, null); + context.engineGetClientSessionContext().setPersistentCache(cacheProxy); SSLSocketFactory socketFactory = context.engineGetSocketFactory(); context.engineGetClientSessionContext().setSessionCacheSize(1); makeRequests(socketFactory); @@ -1033,7 +1037,8 @@ public class SSLSocketTest extends TestCase { // Try again now that file-based cache is populated. fileCache = FileClientSessionCache.usingDirectory(cacheDir); cacheProxy = new ClientSessionCacheProxy(fileCache); - context.engineInit(null, null, null, cacheProxy, null); + context.engineInit(null, null, null); + context.engineGetClientSessionContext().setPersistentCache(cacheProxy); socketFactory = context.engineGetSocketFactory(); context.engineGetClientSessionContext().setSessionCacheSize(1); makeRequests(socketFactory); diff --git a/voip/java/android/net/sip/ISipSession.aidl b/voip/java/android/net/sip/ISipSession.aidl index 1a23527..29fcb32 100644 --- a/voip/java/android/net/sip/ISipSession.aidl +++ b/voip/java/android/net/sip/ISipSession.aidl @@ -17,7 +17,6 @@ package android.net.sip; import android.net.sip.ISipSessionListener; -import android.net.sip.SessionDescription; import android.net.sip.SipProfile; /** diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java index 39083a5..1d1be69 100644 --- a/voip/java/android/net/sip/SipAudioCall.java +++ b/voip/java/android/net/sip/SipAudioCall.java @@ -88,10 +88,11 @@ public interface SipAudioCall { * Called when an error occurs. * * @param call the call object that carries out the audio call - * @param errorCode error code defined in {@link SipErrorCode} + * @param errorCode error code of this error * @param errorMessage error message */ - void onError(SipAudioCall call, String errorCode, String errorMessage); + void onError(SipAudioCall call, SipErrorCode errorCode, + String errorMessage); } /** @@ -125,7 +126,7 @@ public interface SipAudioCall { public void onCallHeld(SipAudioCall call) { onChanged(call); } - public void onError(SipAudioCall call, String errorCode, + public void onError(SipAudioCall call, SipErrorCode errorCode, String errorMessage) { onChanged(call); } diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java index 67ba97f..8bf486a 100644 --- a/voip/java/android/net/sip/SipAudioCallImpl.java +++ b/voip/java/android/net/sip/SipAudioCallImpl.java @@ -79,6 +79,9 @@ public class SipAudioCallImpl extends SipSessionAdapter private WifiManager mWm; private WifiManager.WifiLock mWifiHighPerfLock; + private SipErrorCode mErrorCode; + private String mErrorMessage; + public SipAudioCallImpl(Context context, SipProfile localProfile) { mContext = context; mLocalProfile = localProfile; @@ -92,23 +95,33 @@ public class SipAudioCallImpl extends SipSessionAdapter public void setListener(SipAudioCall.Listener listener, boolean callbackImmediately) { mListener = listener; - if ((listener == null) || !callbackImmediately) return; try { - SipSessionState state = getState(); - switch (state) { - case READY_TO_CALL: - listener.onReadyToCall(this); - break; - case INCOMING_CALL: - listener.onRinging(this, getPeerProfile(mSipSession)); - startRinging(); - break; - case OUTGOING_CALL: - listener.onCalling(this); - break; - default: - listener.onError(this, SipErrorCode.CLIENT_ERROR.toString(), - "wrong state to attach call: " + state); + if ((listener == null) || !callbackImmediately) { + // do nothing + } else if (mErrorCode != null) { + listener.onError(this, mErrorCode, mErrorMessage); + } else if (mInCall) { + if (mHold) { + listener.onCallHeld(this); + } else { + listener.onCallEstablished(this); + } + } else { + SipSessionState state = getState(); + switch (state) { + case READY_TO_CALL: + listener.onReadyToCall(this); + break; + case INCOMING_CALL: + listener.onRinging(this, getPeerProfile(mSipSession)); + break; + case OUTGOING_CALL: + listener.onCalling(this); + break; + case OUTGOING_CALL_RING_BACK: + listener.onRingingBack(this); + break; + } } } catch (Throwable t) { Log.e(TAG, "setListener()", t); @@ -135,6 +148,8 @@ public class SipAudioCallImpl extends SipSessionAdapter mInCall = false; mHold = false; mSessionId = -1L; + mErrorCode = null; + mErrorMessage = null; } public synchronized SipProfile getLocalProfile() { @@ -169,7 +184,7 @@ public class SipAudioCallImpl extends SipSessionAdapter Listener listener = mListener; if (listener != null) { try { - listener.onCalling(SipAudioCallImpl.this); + listener.onCalling(this); } catch (Throwable t) { Log.e(TAG, "onCalling()", t); } @@ -183,7 +198,7 @@ public class SipAudioCallImpl extends SipSessionAdapter Listener listener = mListener; if (listener != null) { try { - listener.onRingingBack(SipAudioCallImpl.this); + listener.onRingingBack(this); } catch (Throwable t) { Log.e(TAG, "onRingingBack()", t); } @@ -236,9 +251,9 @@ public class SipAudioCallImpl extends SipSessionAdapter if (listener != null) { try { if (mHold) { - listener.onCallHeld(SipAudioCallImpl.this); + listener.onCallHeld(this); } else { - listener.onCallEstablished(SipAudioCallImpl.this); + listener.onCallEstablished(this); } } catch (Throwable t) { Log.e(TAG, "onCallEstablished()", t); @@ -253,7 +268,7 @@ public class SipAudioCallImpl extends SipSessionAdapter Listener listener = mListener; if (listener != null) { try { - listener.onCallEnded(SipAudioCallImpl.this); + listener.onCallEnded(this); } catch (Throwable t) { Log.e(TAG, "onCallEnded()", t); } @@ -267,21 +282,27 @@ public class SipAudioCallImpl extends SipSessionAdapter Listener listener = mListener; if (listener != null) { try { - listener.onCallBusy(SipAudioCallImpl.this); + listener.onCallBusy(this); } catch (Throwable t) { Log.e(TAG, "onCallBusy()", t); } } } + private SipErrorCode getErrorCode(String errorCode) { + return Enum.valueOf(SipErrorCode.class, errorCode); + } + @Override public void onCallChangeFailed(ISipSession session, String errorCode, String message) { Log.d(TAG, "sip call change failed: " + message); + mErrorCode = getErrorCode(errorCode); + mErrorMessage = message; Listener listener = mListener; if (listener != null) { try { - listener.onError(SipAudioCallImpl.this, errorCode, message); + listener.onError(this, mErrorCode, message); } catch (Throwable t) { Log.e(TAG, "onCallBusy()", t); } @@ -289,16 +310,21 @@ public class SipAudioCallImpl extends SipSessionAdapter } @Override - public void onError(ISipSession session, String errorCode, + public void onError(ISipSession session, String errorCodeString, String message) { - Log.d(TAG, "sip session error: " + errorCode + ": " + message); + Log.d(TAG, "sip session error: " + errorCodeString + ": " + message); + SipErrorCode errorCode = mErrorCode = getErrorCode(errorCodeString); + mErrorMessage = message; synchronized (this) { - if (!isInCall()) close(true); + if ((mErrorCode == SipErrorCode.DATA_CONNECTION_LOST) + || !isInCall()) { + close(true); + } } Listener listener = mListener; if (listener != null) { try { - listener.onError(SipAudioCallImpl.this, errorCode, message); + listener.onError(this, errorCode, message); } catch (Throwable t) { Log.e(TAG, "onError()", t); } @@ -311,6 +337,8 @@ public class SipAudioCallImpl extends SipSessionAdapter try { mPeerSd = new SdpSessionDescription(sessionDescription); session.setListener(this); + + if (getState() == SipSessionState.INCOMING_CALL) startRinging(); } catch (Throwable e) { Log.e(TAG, "attachCall()", e); throwSipException(e); diff --git a/voip/java/android/net/sip/SipErrorCode.java b/voip/java/android/net/sip/SipErrorCode.java index 8624811..963733e 100644 --- a/voip/java/android/net/sip/SipErrorCode.java +++ b/voip/java/android/net/sip/SipErrorCode.java @@ -47,5 +47,8 @@ public enum SipErrorCode { INVALID_CREDENTIALS, /** The client is in a transaction and cannot initiate a new one. */ - IN_PROGRESS; + IN_PROGRESS, + + /** When data connection is lost. */ + DATA_CONNECTION_LOST; } diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java index ccae7f9..149053c 100644 --- a/voip/java/android/net/sip/SipManager.java +++ b/voip/java/android/net/sip/SipManager.java @@ -502,13 +502,14 @@ public class SipManager { @Override public void onRegistrationFailed(ISipSession session, String errorCode, String message) { - mListener.onRegistrationFailed(getUri(session), errorCode, message); + mListener.onRegistrationFailed(getUri(session), + Enum.valueOf(SipErrorCode.class, errorCode), message); } @Override public void onRegistrationTimeout(ISipSession session) { mListener.onRegistrationFailed(getUri(session), - SipErrorCode.TIME_OUT.toString(), "registration timed out"); + SipErrorCode.TIME_OUT, "registration timed out"); } } } diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java index aa2da75..1a7d8bf 100644 --- a/voip/java/android/net/sip/SipProfile.java +++ b/voip/java/android/net/sip/SipProfile.java @@ -167,11 +167,15 @@ public class SipProfile implements Parcelable, Serializable, Cloneable { * * @param port port number of the server * @return this builder object - * @throws InvalidArgumentException if the port number is out of range + * @throws IllegalArgumentException if the port number is out of range */ - public Builder setPort(int port) throws InvalidArgumentException { - mUri.setPort(port); - return this; + public Builder setPort(int port) throws IllegalArgumentException { + try { + mUri.setPort(port); + return this; + } catch (InvalidArgumentException e) { + throw new IllegalArgumentException(e); + } } /** @@ -180,16 +184,16 @@ public class SipProfile implements Parcelable, Serializable, Cloneable { * * @param protocol the protocol string * @return this builder object - * @throws InvalidArgumentException if the protocol is not recognized + * @throws IllegalArgumentException if the protocol is not recognized */ public Builder setProtocol(String protocol) - throws InvalidArgumentException { + throws IllegalArgumentException { if (protocol == null) { throw new NullPointerException("protocol cannot be null"); } protocol = protocol.toUpperCase(); if (!protocol.equals("UDP") && !protocol.equals("TCP")) { - throw new InvalidArgumentException( + throw new IllegalArgumentException( "unsupported protocol: " + protocol); } mProfile.mProtocol = protocol; diff --git a/voip/java/android/net/sip/SipRegistrationListener.java b/voip/java/android/net/sip/SipRegistrationListener.java index 22488d7..e751e46 100644 --- a/voip/java/android/net/sip/SipRegistrationListener.java +++ b/voip/java/android/net/sip/SipRegistrationListener.java @@ -40,9 +40,9 @@ public interface SipRegistrationListener { * Called when the registration fails. * * @param localProfileUri the URI string of the SIP profile to register with - * @param errorCode error code defined in {@link SipErrorCode} + * @param errorCode error code of this error * @param errorMessage error message */ - void onRegistrationFailed(String localProfileUri, String errorCode, + void onRegistrationFailed(String localProfileUri, SipErrorCode errorCode, String errorMessage); } diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 6e0bc9d..0ee559e 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.WifiConfiguration; 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 @@ interface IWifiManager 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 9d21521..dd162f2 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -23,6 +23,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.Handler; import android.os.RemoteException; +import android.os.WorkSource; import java.util.List; @@ -872,6 +873,7 @@ public class WifiManager { int mLockType; private boolean mRefCounted; private boolean mHeld; + private WorkSource mWorkSource; private WifiLock(int lockType, String tag) { mTag = tag; @@ -897,7 +899,7 @@ public class WifiManager { 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); @@ -969,6 +971,32 @@ public class WifiManager { } } + 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) { |
