diff --git a/api/current.xml b/api/current.xml index e764fa5aa6076..df69ede521ed0 100644 --- a/api/current.xml +++ b/api/current.xml @@ -25618,7 +25618,7 @@ visibility="public" > - - + + + @@ -27787,6 +27789,28 @@ + + + + #include "private/android_filesystem_config.h" +#define LOG_NDEBUG 0 +#define LOG_TAG "dumpstate" +#include + #include "dumpstate.h" static char* const gzip_args[] = { "gzip", "-6", 0 }; @@ -182,6 +186,8 @@ int main(int argc, char *argv[]) { pid_t pid; gid_t groups[] = { AID_LOG, AID_SDCARD_RW }; + LOGI("begin\n"); + /* set as high priority, and protect from OOM killer */ setpriority(PRIO_PROCESS, 0, -20); protect_from_oom_killer(); @@ -332,6 +338,8 @@ int main(int argc, char *argv[]) { /* so gzip will terminate */ close(STDOUT_FILENO); + LOGI("done\n"); + return 0; } diff --git a/core/java/android/accounts/AccountAuthenticatorResponse.java b/core/java/android/accounts/AccountAuthenticatorResponse.java index 3488c5e1dfba3..7c09fbffb86ad 100644 --- a/core/java/android/accounts/AccountAuthenticatorResponse.java +++ b/core/java/android/accounts/AccountAuthenticatorResponse.java @@ -22,8 +22,7 @@ import android.os.Parcel; import android.os.RemoteException; /** - * Object that wraps calls to an {@link IAccountAuthenticatorResponse} object. - * TODO: this interface is still in flux + * Object used to communicate responses back to the AccountManager */ public class AccountAuthenticatorResponse implements Parcelable { private IAccountAuthenticatorResponse mAccountAuthenticatorResponse; diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index ae6d9140c2921..d03244977de2f 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -44,8 +44,7 @@ import com.google.android.collect.Maps; /** * A class that helps with interactions with the AccountManagerService. It provides * methods to allow for account, password, and authtoken management for all accounts on the - * device. Some of these calls are implemented with the help of the corresponding - * {@link IAccountAuthenticator} services. One accesses the {@link AccountManager} by calling: + * device. One accesses the {@link AccountManager} by calling: * AccountManager accountManager = AccountManager.get(context); * *

diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b4ac159ea4ceb..2cd223fc0409d 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -653,7 +653,7 @@ public final class ActivityThread { mStrongRef = strong ? rd : null; } public void performReceive(Intent intent, int resultCode, - String data, Bundle extras, boolean ordered) { + String data, Bundle extras, boolean ordered, boolean sticky) { ReceiverDispatcher rd = mDispatcher.get(); if (DEBUG_BROADCAST) { int seq = intent.getIntExtra("seq", -1); @@ -661,7 +661,8 @@ public final class ActivityThread { + " to " + rd); } if (rd != null) { - rd.performReceive(intent, resultCode, data, extras, ordered); + rd.performReceive(intent, resultCode, data, extras, + ordered, sticky); } } } @@ -681,6 +682,7 @@ public final class ActivityThread { private String mCurData; private Bundle mCurMap; private boolean mCurOrdered; + private boolean mCurSticky; public void run() { BroadcastReceiver receiver = mReceiver; @@ -706,6 +708,7 @@ public final class ActivityThread { receiver.setResult(mCurCode, mCurData, mCurMap); receiver.clearAbortBroadcast(); receiver.setOrderedHint(mCurOrdered); + receiver.setInitialStickyHint(mCurSticky); receiver.onReceive(mContext, intent); } catch (Exception e) { if (mRegistered && mCurOrdered) { @@ -788,7 +791,7 @@ public final class ActivityThread { } public void performReceive(Intent intent, int resultCode, - String data, Bundle extras, boolean ordered) { + String data, Bundle extras, boolean ordered, boolean sticky) { if (DEBUG_BROADCAST) { int seq = intent.getIntExtra("seq", -1); Log.i(TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq @@ -800,6 +803,7 @@ public final class ActivityThread { args.mCurData = data; args.mCurMap = extras; args.mCurOrdered = ordered; + args.mCurSticky = sticky; if (!mActivityThread.post(args)) { if (mRegistered) { IActivityManager mgr = ActivityManagerNative.getDefault(); @@ -1515,9 +1519,9 @@ public final class ActivityThread { // correctly ordered, since these are one-way calls and the binder driver // applies transaction ordering per object for such calls. public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, - int resultCode, String dataStr, Bundle extras, boolean ordered) - throws RemoteException { - receiver.performReceive(intent, resultCode, dataStr, extras, ordered); + int resultCode, String dataStr, Bundle extras, boolean ordered, + boolean sticky) throws RemoteException { + receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky); } public void scheduleLowMemory() { diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 928981d0c2d6c..a772a8f78ddf7 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -317,8 +317,9 @@ public abstract class ApplicationThreadNative extends Binder String dataStr = data.readString(); Bundle extras = data.readBundle(); boolean ordered = data.readInt() != 0; + boolean sticky = data.readInt() != 0; scheduleRegisteredReceiver(receiver, intent, - resultCode, dataStr, extras, ordered); + resultCode, dataStr, extras, ordered, sticky); return true; } @@ -716,7 +717,7 @@ class ApplicationThreadProxy implements IApplicationThread { } public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, - int resultCode, String dataStr, Bundle extras, boolean ordered) + int resultCode, String dataStr, Bundle extras, boolean ordered, boolean sticky) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); @@ -726,6 +727,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeString(dataStr); data.writeBundle(extras); data.writeInt(ordered ? 1 : 0); + data.writeInt(sticky ? 1 : 0); mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index 8dda8985d6be7..89a52fd70d50e 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -91,7 +91,7 @@ public interface IApplicationThread extends IInterface { void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args) throws RemoteException; void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, - int resultCode, String data, Bundle extras, boolean ordered) + int resultCode, String data, Bundle extras, boolean ordered, boolean sticky) throws RemoteException; void scheduleLowMemory() throws RemoteException; void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException; diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 18d9b92fbc5cd..be1dc4acb150d 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -147,7 +147,7 @@ public final class PendingIntent implements Parcelable { mHandler = handler; } public void performReceive(Intent intent, int resultCode, - String data, Bundle extras, boolean serialized) { + String data, Bundle extras, boolean serialized, boolean sticky) { mIntent = intent; mResultCode = resultCode; mResultData = data; diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index e3ec2cc4cb24d..c6a0619cf8ec8 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -18,14 +18,19 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.os.Binder; +import android.os.Handler; +import android.os.Message; import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; import java.io.IOException; import java.util.Collections; -import java.util.Set; import java.util.HashSet; +import java.util.LinkedList; +import java.util.Random; +import java.util.Set; /** * Represents the local Bluetooth adapter. @@ -40,6 +45,7 @@ import java.util.HashSet; */ public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; + private static final boolean DBG = true; //STOPSHIP: Remove excess logging /** * Sentinel error value for this class. Guaranteed to not equal any other @@ -557,33 +563,142 @@ public final class BluetoothAdapter { return null; } + /** + * Randomly picks RFCOMM channels until none are left. + * Avoids reserved channels. + */ + private static class RfcommChannelPicker { + private static final int[] RESERVED_RFCOMM_CHANNELS = new int[] { + 10, // HFAG + 11, // HSAG + 12, // OPUSH + 19, // PBAP + }; + private static LinkedList sChannels; // master list of non-reserved channels + private static Random sRandom; + + private final LinkedList mChannels; // local list of channels left to try + + public RfcommChannelPicker() { + synchronized (RfcommChannelPicker.class) { + if (sChannels == null) { + // lazy initialization of non-reserved rfcomm channels + sChannels = new LinkedList(); + for (int i = 1; i <= BluetoothSocket.MAX_RFCOMM_CHANNEL; i++) { + sChannels.addLast(new Integer(i)); + } + for (int reserved : RESERVED_RFCOMM_CHANNELS) { + sChannels.remove(new Integer(reserved)); + } + sRandom = new Random(); + } + mChannels = (LinkedList)sChannels.clone(); + } + } + /* Returns next random channel, or -1 if we're out */ + public int nextChannel() { + if (mChannels.size() == 0) { + return -1; + } + return mChannels.remove(sRandom.nextInt(mChannels.size())); + } + } + /** * Create a listening, secure RFCOMM Bluetooth socket. *

A remote device connecting to this socket will be authenticated and * communication on this socket will be encrypted. *

Use {@link BluetoothServerSocket#accept} to retrieve incoming - * connections to listening {@link BluetoothServerSocket}. + * connections from a listening {@link BluetoothServerSocket}. *

Valid RFCOMM channels are in range 1 to 30. - *

Requires {@link android.Manifest.permission#BLUETOOTH} + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * @param channel RFCOMM channel to listen on * @return a listening RFCOMM BluetoothServerSocket * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions, or channel in use. + * @hide */ public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_RFCOMM, true, true, channel); - try { - socket.mSocket.bindListen(); - } catch (IOException e) { + int errno = socket.mSocket.bindListen(); + if (errno != 0) { try { socket.close(); - } catch (IOException e2) { } - throw e; + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); } return socket; } + /** + * Create a listening, secure RFCOMM Bluetooth socket with Service Record. + *

A remote device connecting to this socket will be authenticated and + * communication on this socket will be encrypted. + *

Use {@link BluetoothServerSocket#accept} to retrieve incoming + * connections from a listening {@link BluetoothServerSocket}. + *

The system will assign an unused RFCOMM channel to listen on. + *

The system will also register a Service Discovery + * Protocol (SDP) record with the local SDP server containing the specified + * UUID, service name, and auto-assigned channel. Remote Bluetooth devices + * can use the same UUID to query our SDP server and discover which channel + * to connect to. This SDP record will be removed when this socket is + * closed, or if this application closes unexpectedly. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * @param name service name for SDP record + * @param uuid uuid for SDP record + * @return a listening RFCOMM BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions, or channel in use. + */ + public BluetoothServerSocket listenUsingRfcomm(String name, ParcelUuid uuid) + throws IOException { + RfcommChannelPicker picker = new RfcommChannelPicker(); + + BluetoothServerSocket socket; + int channel; + int errno; + while (true) { + channel = picker.nextChannel(); + + if (channel == -1) { + throw new IOException("No available channels"); + } + + socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_RFCOMM, true, true, channel); + errno = socket.mSocket.bindListen(); + if (errno == 0) { + if (DBG) Log.d(TAG, "listening on RFCOMM channel " + channel); + break; // success + } else if (errno == BluetoothSocket.EADDRINUSE) { + if (DBG) Log.d(TAG, "RFCOMM channel " + channel + " in use"); + try { + socket.close(); + } catch (IOException e) {} + continue; // try another channel + } else { + try { + socket.close(); + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); // Exception as a result of bindListen() + } + } + + int handle = -1; + try { + handle = mService.addRfcommServiceRecord(name, uuid, channel, new Binder()); + } catch (RemoteException e) {Log.e(TAG, "", e);} + if (handle == -1) { + try { + socket.close(); + } catch (IOException e) {} + throw new IOException("Not able to register SDP record for " + name); + } + socket.setCloseHandler(mHandler, handle); + return socket; + } + /** * Construct an unencrypted, unauthenticated, RFCOMM server socket. * Call #accept to retrieve connections to this socket. @@ -595,13 +710,12 @@ public final class BluetoothAdapter { public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_RFCOMM, false, false, port); - try { - socket.mSocket.bindListen(); - } catch (IOException e) { + int errno = socket.mSocket.bindListen(); + if (errno != 0) { try { socket.close(); - } catch (IOException e2) { } - throw e; + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); } return socket; } @@ -617,13 +731,12 @@ public final class BluetoothAdapter { public static BluetoothServerSocket listenUsingScoOn() throws IOException { BluetoothServerSocket socket = new BluetoothServerSocket( BluetoothSocket.TYPE_SCO, false, false, -1); - try { - socket.mSocket.bindListen(); - } catch (IOException e) { + int errno = socket.mSocket.bindListen(); + if (errno != 0) { try { socket.close(); - } catch (IOException e2) { } - throw e; + } catch (IOException e) {} + socket.mSocket.throwErrnoNative(errno); } return socket; } @@ -636,6 +749,17 @@ public final class BluetoothAdapter { return Collections.unmodifiableSet(devices); } + private Handler mHandler = new Handler() { + public void handleMessage(Message msg) { + /* handle socket closing */ + int handle = msg.what; + try { + if (DBG) Log.d(TAG, "Removing service record " + Integer.toHexString(handle)); + mService.removeServiceRecord(handle); + } catch (RemoteException e) {Log.e(TAG, "", e);} + } + }; + /** * Validate a Bluetooth address, such as "00:43:A8:23:10:F0" *

Alphabetic characters must be uppercase to be valid. diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java index 45dc432d332fe..d126ea48dddd5 100644 --- a/core/java/android/bluetooth/BluetoothServerSocket.java +++ b/core/java/android/bluetooth/BluetoothServerSocket.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.os.Handler; + import java.io.Closeable; import java.io.IOException; @@ -38,7 +40,7 @@ import java.io.IOException; * BluetoothSocket} ready for an outgoing connection to a remote * {@link BluetoothDevice}. * - *

Use {@link BluetoothAdapter#listenUsingRfcommOn} to create a listening + *

Use {@link BluetoothAdapter#listenUsingRfcomm} to create a listening * {@link BluetoothServerSocket} ready for incoming connections to the local * {@link BluetoothAdapter}. * @@ -52,6 +54,8 @@ import java.io.IOException; public final class BluetoothServerSocket implements Closeable { /*package*/ final BluetoothSocket mSocket; + private Handler mHandler; + private int mMessage; /** * Construct a socket for incoming connections. @@ -101,6 +105,16 @@ public final class BluetoothServerSocket implements Closeable { * throw an IOException. */ public void close() throws IOException { + synchronized (this) { + if (mHandler != null) { + mHandler.obtainMessage(mMessage).sendToTarget(); + } + } mSocket.close(); } + + /*package*/ synchronized void setCloseHandler(Handler handler, int message) { + mHandler = handler; + mMessage = message; + } } diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index e462ea6383147..b9e33f3bda057 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -42,7 +42,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * BluetoothSocket} ready for an outgoing connection to a remote * {@link BluetoothDevice}. * - *

Use {@link BluetoothAdapter#listenUsingRfcommOn} to create a listening + *

Use {@link BluetoothAdapter#listenUsingRfcomm} to create a listening * {@link BluetoothServerSocket} ready for incoming connections to the local * {@link BluetoothAdapter}. * @@ -54,11 +54,17 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * {@link android.Manifest.permission#BLUETOOTH} */ public final class BluetoothSocket implements Closeable { + /** @hide */ + public static final int MAX_RFCOMM_CHANNEL = 30; + /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */ /*package*/ static final int TYPE_RFCOMM = 1; /*package*/ static final int TYPE_SCO = 2; /*package*/ static final int TYPE_L2CAP = 3; + /*package*/ static final int EBADFD = 77; + /*package*/ static final int EADDRINUSE = 98; + private final int mType; /* one of TYPE_RFCOMM etc */ private final int mPort; /* RFCOMM channel or L2CAP psm */ private final BluetoothDevice mDevice; /* remote device */ @@ -90,6 +96,11 @@ public final class BluetoothSocket implements Closeable { */ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, BluetoothDevice device, int port) throws IOException { + if (type == BluetoothSocket.TYPE_RFCOMM) { + if (port < 1 || port > MAX_RFCOMM_CHANNEL) { + throw new IOException("Invalid RFCOMM channel: " + port); + } + } mType = type; mAuth = auth; mEncrypt = encrypt; @@ -211,11 +222,15 @@ public final class BluetoothSocket implements Closeable { return mOutputStream; } - /*package*/ void bindListen() throws IOException { + /** + * Currently returns unix errno instead of throwing IOException, + * so that BluetoothAdapter can check the error code for EADDRINUSE + */ + /*package*/ int bindListen() { mLock.readLock().lock(); try { - if (mClosed) throw new IOException("socket closed"); - bindListenNative(); + if (mClosed) return EBADFD; + return bindListenNative(); } finally { mLock.readLock().unlock(); } @@ -264,11 +279,16 @@ public final class BluetoothSocket implements Closeable { private native void initSocketNative() throws IOException; private native void initSocketFromFdNative(int fd) throws IOException; private native void connectNative() throws IOException; - private native void bindListenNative() throws IOException; + private native int bindListenNative(); private native BluetoothSocket acceptNative(int timeout) throws IOException; private native int availableNative() throws IOException; private native int readNative(byte[] b, int offset, int length) throws IOException; private native int writeNative(byte[] b, int offset, int length) throws IOException; private native void abortNative() throws IOException; private native void destroyNative() throws IOException; + /** + * Throws an IOException for given posix errno. Done natively so we can + * use strerr to convert to string error. + */ + /*package*/ native void throwErrnoNative(int errno) throws IOException; } diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index da0564a2a057a..4164a3d6e6658 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -50,6 +50,10 @@ public final class BluetoothUuid { public static final ParcelUuid ObexObjectPush = ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); + public static final ParcelUuid[] RESERVED_UUIDS = { + AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, + ObexObjectPush}; + public static boolean isAudioSource(ParcelUuid uuid) { return uuid.equals(AudioSource); } diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 2f77ba48da162..e54abec2de28d 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -63,4 +63,7 @@ interface IBluetooth boolean setTrust(in String address, in boolean value); boolean getTrustState(in String address); + + int addRfcommServiceRecord(in String serviceName, in ParcelUuid uuid, int channel, IBinder b); + void removeServiceRecord(int handle); } diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index b391c57df5ee6..b63d026c2b331 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -383,6 +383,24 @@ public abstract class BroadcastReceiver { mAbortBroadcast = false; } + /** + * Returns true if the receiver is currently processing an ordered + * broadcast. + */ + public final boolean isOrderedBroadcast() { + return mOrderedHint; + } + + /** + * Returns true if the receiver is currently processing the initial + * value of a sticky broadcast -- that is, the value that was last + * broadcast and is currently held in the sticky cache, so this is + * not directly the result of a broadcast right now. + */ + public final boolean isInitialStickyBroadcast() { + return mInitialStickyHint; + } + /** * For internal use, sets the hint about whether this BroadcastReceiver is * running in ordered mode. @@ -391,6 +409,14 @@ public abstract class BroadcastReceiver { mOrderedHint = isOrdered; } + /** + * For internal use, sets the hint about whether this BroadcastReceiver is + * receiving the initial sticky broadcast value. @hide + */ + public final void setInitialStickyHint(boolean isInitialSticky) { + mInitialStickyHint = isInitialSticky; + } + /** * Control inclusion of debugging help for mismatched * calls to {@ Context#registerReceiver(BroadcastReceiver, IntentFilter) @@ -414,7 +440,10 @@ public abstract class BroadcastReceiver { } void checkSynchronousHint() { - if (mOrderedHint) { + // Note that we don't assert when receiving the initial sticky value, + // since that may have come from an ordered broadcast. We'll catch + // them later when the real broadcast happens again. + if (mOrderedHint || mInitialStickyHint) { return; } RuntimeException e = new RuntimeException( @@ -429,5 +458,6 @@ public abstract class BroadcastReceiver { private boolean mAbortBroadcast; private boolean mDebugUnregister; private boolean mOrderedHint; + private boolean mInitialStickyHint; } diff --git a/core/java/android/content/IIntentReceiver.aidl b/core/java/android/content/IIntentReceiver.aidl index 443db2d06d0f6..6f2f7c4053b48 100755 --- a/core/java/android/content/IIntentReceiver.aidl +++ b/core/java/android/content/IIntentReceiver.aidl @@ -28,6 +28,6 @@ import android.os.Bundle; */ oneway interface IIntentReceiver { void performReceive(in Intent intent, int resultCode, - String data, in Bundle extras, boolean ordered); + String data, in Bundle extras, boolean ordered, boolean sticky); } diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java index c8f7aa952d95e..e182021f8ef1b 100644 --- a/core/java/android/content/IntentSender.java +++ b/core/java/android/content/IntentSender.java @@ -113,7 +113,7 @@ public class IntentSender implements Parcelable { mHandler = handler; } public void performReceive(Intent intent, int resultCode, - String data, Bundle extras, boolean serialized) { + String data, Bundle extras, boolean serialized, boolean sticky) { mIntent = intent; mResultCode = resultCode; mResultData = data; diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index abfb27412de0c..7d2c698895817 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -413,6 +413,7 @@ public abstract class AsyncTask { } private void finish(Result result) { + if (isCancelled()) result = null; onPostExecute(result); mStatus = Status.FINISHED; } diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index edf6d10b5dd20..93133d70255c8 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -28,6 +28,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetooth; import android.os.ParcelUuid; @@ -37,6 +38,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Binder; +import android.os.IBinder; import android.os.Handler; import android.os.Message; import android.os.RemoteException; @@ -90,6 +92,8 @@ public class BluetoothService extends IBluetooth.Stub { private final HashMap > mDeviceServiceChannelCache; private final ArrayList mUuidIntentTracker; + private final HashMap mServiceRecordToPid; + static { classInitNative(); } @@ -117,6 +121,7 @@ public class BluetoothService extends IBluetooth.Stub { mDeviceServiceChannelCache = new HashMap>(); mUuidIntentTracker = new ArrayList(); + mServiceRecordToPid = new HashMap(); registerForAirplaneMode(); } @@ -206,6 +211,7 @@ public class BluetoothService extends IBluetooth.Stub { mIsDiscovering = false; mAdapterProperties.clear(); + mServiceRecordToPid.clear(); if (saveSetting) { persistBluetoothOnSetting(false); @@ -1211,6 +1217,71 @@ public class BluetoothService extends IBluetooth.Stub { mDeviceServiceChannelCache.put(address, value); } + /** + * b is a handle to a Binder instance, so that this service can be notified + * for Applications that terminate unexpectedly, to clean there service + * records + */ + public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid, + int channel, IBinder b) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, + "Need BLUETOOTH permission"); + if (serviceName == null || uuid == null || channel < 1 || + channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) { + return -1; + } + if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) { + Log.w(TAG, "Attempted to register a reserved UUID: " + uuid); + return -1; + } + int handle = addRfcommServiceRecordNative(serviceName, + uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(), + (short)channel); + if (DBG) log("new handle " + Integer.toHexString(handle)); + if (handle == -1) { + return -1; + } + + int pid = Binder.getCallingPid(); + mServiceRecordToPid.put(new Integer(handle), new Integer(pid)); + try { + b.linkToDeath(new Reaper(handle, pid), 0); + } catch (RemoteException e) {} + return handle; + } + + public void removeServiceRecord(int handle) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, + "Need BLUETOOTH permission"); + checkAndRemoveRecord(handle, Binder.getCallingPid()); + } + + private synchronized void checkAndRemoveRecord(int handle, int pid) { + Integer handleInt = new Integer(handle); + Integer owner = mServiceRecordToPid.get(handleInt); + if (owner != null && pid == owner.intValue()) { + if (DBG) log("Removing service record " + Integer.toHexString(handle) + " for pid " + + pid); + mServiceRecordToPid.remove(handleInt); + removeServiceRecordNative(handle); + } + } + + private class Reaper implements IBinder.DeathRecipient { + int pid; + int handle; + Reaper(int handle, int pid) { + this.pid = pid; + this.handle = handle; + } + public void binderDied() { + synchronized (BluetoothService.this) { + if (DBG) log("Tracked app " + pid + " died"); + checkAndRemoveRecord(handle, pid); + } + } + } + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -1263,25 +1334,25 @@ public class BluetoothService extends IBluetooth.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n"); - switch(mBluetoothState) { case BluetoothAdapter.STATE_OFF: - pw.println("\nBluetooth OFF\n"); + pw.println("Bluetooth OFF\n"); return; case BluetoothAdapter.STATE_TURNING_ON: - pw.println("\nBluetooth TURNING ON\n"); + pw.println("Bluetooth TURNING ON\n"); return; case BluetoothAdapter.STATE_TURNING_OFF: - pw.println("\nBluetooth TURNING OFF\n"); + pw.println("Bluetooth TURNING OFF\n"); return; case BluetoothAdapter.STATE_ON: - pw.println("\nBluetooth ON\n"); + pw.println("Bluetooth ON\n"); } - pw.println("\nLocal address = " + getAddress()); - pw.println("\nLocal name = " + getName()); - pw.println("\nisDiscovering() = " + isDiscovering()); + pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive); + + pw.println("Local address = " + getAddress()); + pw.println("Local name = " + getName()); + pw.println("isDiscovering() = " + isDiscovering()); BluetoothHeadset headset = new BluetoothHeadset(mContext, null); @@ -1292,13 +1363,17 @@ public class BluetoothService extends IBluetooth.Stub { toBondStateString(bondState), mBondState.getAttempt(address), getRemoteName(address)); - if (bondState == BluetoothDevice.BOND_BONDED) { - ParcelUuid[] uuids = getRemoteUuids(address); - if (uuids == null) { - pw.printf("\tuuids = null\n"); - } else { - for (ParcelUuid uuid : uuids) { - pw.printf("\t" + uuid + "\n"); + + Map uuidChannels = mDeviceServiceChannelCache.get(address); + if (uuidChannels == null) { + pw.println("\tuuids = null"); + } else { + for (ParcelUuid uuid : uuidChannels.keySet()) { + Integer channel = uuidChannels.get(uuid); + if (channel == null) { + pw.println("\t" + uuid); + } else { + pw.println("\t" + uuid + " RFCOMM channel = " + channel); } } } @@ -1310,8 +1385,10 @@ public class BluetoothService extends IBluetooth.Stub { devicesObjectPath = value.split(","); } pw.println("\n--ACL connected devices--"); - for (String device : devicesObjectPath) { - pw.println(getAddressFromObjectPath(device)); + if (devicesObjectPath != null) { + for (String device : devicesObjectPath) { + pw.println(getAddressFromObjectPath(device)); + } } // Rather not do this from here, but no-where else and I need this @@ -1331,10 +1408,15 @@ public class BluetoothService extends IBluetooth.Stub { pw.println("getState() = STATE_ERROR"); break; } - pw.println("getCurrentHeadset() = " + headset.getCurrentHeadset()); - pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint()); + pw.println("\ngetCurrentHeadset() = " + headset.getCurrentHeadset()); + pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint()); headset.close(); + pw.println("\n--Application Service Records--"); + for (Integer handle : mServiceRecordToPid.keySet()) { + Integer pid = mServiceRecordToPid.get(handle); + pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle)); + } } /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) { @@ -1423,8 +1505,12 @@ 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 setDevicePropertyBooleanNative(String objectPath, String key, int value); + private native boolean setDevicePropertyBooleanNative(String objectPath, String key, + int value); private native boolean createDeviceNative(String address); private native boolean discoverServicesNative(String objectPath, String pattern); + private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb, + short channel); + private native boolean removeServiceRecordNative(int handle); } diff --git a/core/jni/android_bluetooth_BluetoothSocket.cpp b/core/jni/android_bluetooth_BluetoothSocket.cpp index 2532effe98bdc..31ebf8ca58cce 100644 --- a/core/jni/android_bluetooth_BluetoothSocket.cpp +++ b/core/jni/android_bluetooth_BluetoothSocket.cpp @@ -237,7 +237,8 @@ static void connectNative(JNIEnv *env, jobject obj) { jniThrowIOException(env, ENOSYS); } -static void bindListenNative(JNIEnv *env, jobject obj) { +/* Returns errno instead of throwing, so java can check errno */ +static int bindListenNative(JNIEnv *env, jobject obj) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); @@ -248,7 +249,7 @@ static void bindListenNative(JNIEnv *env, jobject obj) { struct asocket *s = get_socketData(env, obj); if (!s) - return; + return EINVAL; type = env->GetIntField(obj, field_mType); @@ -283,28 +284,25 @@ static void bindListenNative(JNIEnv *env, jobject obj) { memcpy(&addr_l2.l2_bdaddr, &bdaddr, sizeof(bdaddr_t)); break; default: - jniThrowIOException(env, ENOSYS); - return; + return ENOSYS; } if (bind(s->fd, addr, addr_sz)) { LOGV("...bind(%d) gave errno %d", s->fd, errno); - jniThrowIOException(env, errno); - return; + return errno; } if (listen(s->fd, 1)) { LOGV("...listen(%d) gave errno %d", s->fd, errno); - jniThrowIOException(env, errno); - return; + return errno; } LOGV("...bindListenNative(%d) success", s->fd); - return; + return 0; #endif - jniThrowIOException(env, ENOSYS); + return ENOSYS; } static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) { @@ -521,17 +519,22 @@ static void destroyNative(JNIEnv *env, jobject obj) { jniThrowIOException(env, ENOSYS); } +static void throwErrnoNative(JNIEnv *env, jobject obj, jint err) { + jniThrowIOException(env, err); +} + static JNINativeMethod sMethods[] = { {"initSocketNative", "()V", (void*) initSocketNative}, {"initSocketFromFdNative", "(I)V", (void*) initSocketFromFdNative}, {"connectNative", "()V", (void *) connectNative}, - {"bindListenNative", "()V", (void *) bindListenNative}, + {"bindListenNative", "()I", (void *) bindListenNative}, {"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative}, {"availableNative", "()I", (void *) availableNative}, {"readNative", "([BII)I", (void *) readNative}, {"writeNative", "([BII)I", (void *) writeNative}, {"abortNative", "()V", (void *) abortNative}, {"destroyNative", "()V", (void *) destroyNative}, + {"throwErrnoNative", "(I)V", (void *) throwErrnoNative}, }; int register_android_bluetooth_BluetoothSocket(JNIEnv *env) { diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index c2f93eea868d3..ea64305d145f8 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -818,6 +818,48 @@ static jboolean discoverServicesNative(JNIEnv *env, jobject object, return JNI_FALSE; } +static jint addRfcommServiceRecordNative(JNIEnv *env, jobject object, + jstring name, jlong uuidMsb, jlong uuidLsb, jshort channel) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_name = env->GetStringUTFChars(name, NULL); + LOGV("... name = %s", c_name); + LOGV("... uuid1 = %llX", uuidMsb); + LOGV("... uuid2 = %llX", uuidLsb); + LOGV("... channel = %d", channel); + DBusMessage *reply = dbus_func_args(env, nat->conn, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "AddRfcommServiceRecord", + DBUS_TYPE_STRING, &c_name, + DBUS_TYPE_UINT64, &uuidMsb, + DBUS_TYPE_UINT64, &uuidLsb, + DBUS_TYPE_UINT16, &channel, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(name, c_name); + return reply ? dbus_returns_uint32(env, reply) : -1; + } +#endif + return -1; +} + +static jboolean removeServiceRecordNative(JNIEnv *env, jobject object, jint handle) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + LOGV("... handle = %X", handle); + DBusMessage *reply = dbus_func_args(env, nat->conn, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "RemoveServiceRecord", + DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_INVALID); + return reply ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ @@ -861,6 +903,8 @@ static JNINativeMethod sMethods[] = { (void *)setDevicePropertyBooleanNative}, {"createDeviceNative", "(Ljava/lang/String;)Z", (void *)createDeviceNative}, {"discoverServicesNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)discoverServicesNative}, + {"addRfcommServiceRecordNative", "(Ljava/lang/String;JJS)I", (void *)addRfcommServiceRecordNative}, + {"removeServiceRecordNative", "(I)Z", (void *)removeServiceRecordNative}, }; int register_android_server_BluetoothService(JNIEnv *env) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index cd39d0d8af246..34302b19b9e27 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -8389,7 +8389,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (i == 0) { finisher = new IIntentReceiver.Stub() { public void performReceive(Intent intent, int resultCode, - String data, Bundle extras, boolean ordered) + String data, Bundle extras, boolean ordered, + boolean sticky) throws RemoteException { synchronized (ActivityManagerService.this) { mDidUpdate = true; @@ -11571,7 +11572,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Intent intent = (Intent)allSticky.get(i); BroadcastRecord r = new BroadcastRecord(intent, null, null, -1, -1, null, receivers, null, 0, null, null, - false); + false, true); if (mParallelBroadcasts.size() == 0) { scheduleBroadcastsLocked(); } @@ -11796,7 +11797,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen BroadcastRecord r = new BroadcastRecord(intent, callerApp, callerPackage, callingPid, callingUid, requiredPermission, registeredReceivers, resultTo, resultCode, resultData, map, - ordered); + ordered, false); if (DEBUG_BROADCAST) Log.v( TAG, "Enqueueing parallel broadcast " + r + ": prev had " + mParallelBroadcasts.size()); @@ -11875,7 +11876,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen || resultTo != null) { BroadcastRecord r = new BroadcastRecord(intent, callerApp, callerPackage, callingPid, callingUid, requiredPermission, - receivers, resultTo, resultCode, resultData, map, ordered); + receivers, resultTo, resultCode, resultData, map, ordered, false); if (DEBUG_BROADCAST) Log.v( TAG, "Enqueueing ordered broadcast " + r + ": prev had " + mOrderedBroadcasts.size()); @@ -12179,15 +12180,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } static void performReceive(ProcessRecord app, IIntentReceiver receiver, - Intent intent, int resultCode, String data, - Bundle extras, boolean ordered) throws RemoteException { + Intent intent, int resultCode, String data, Bundle extras, + boolean ordered, boolean sticky) throws RemoteException { if (app != null && app.thread != null) { // If we have an app thread, do the call through that so it is // correctly ordered with other one-way calls. app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode, - data, extras, ordered); + data, extras, ordered, sticky); } else { - receiver.performReceive(intent, resultCode, data, extras, ordered); + receiver.performReceive(intent, resultCode, data, extras, ordered, sticky); } } @@ -12251,7 +12252,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } performReceive(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, - r.resultData, r.resultExtras, r.ordered); + r.resultData, r.resultExtras, r.ordered, r.sticky); if (ordered) { r.state = BroadcastRecord.CALL_DONE_RECEIVE; } @@ -12384,7 +12385,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } performReceive(r.callerApp, r.resultTo, new Intent(r.intent), r.resultCode, - r.resultData, r.resultExtras, false); + r.resultData, r.resultExtras, false, false); } catch (RemoteException e) { Log.w(TAG, "Failure sending broadcast result of " + r.intent, e); } diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java index da55049de7ada..db0a6cb3070b7 100644 --- a/services/java/com/android/server/am/BroadcastRecord.java +++ b/services/java/com/android/server/am/BroadcastRecord.java @@ -39,7 +39,9 @@ class BroadcastRecord extends Binder { final String callerPackage; // who sent this final int callingPid; // the pid of who sent this final int callingUid; // the uid of who sent this - String requiredPermission; // a permission the caller has required + final boolean ordered; // serialize the send to receivers? + final boolean sticky; // originated from existing sticky data? + final String requiredPermission; // a permission the caller has required final List receivers; // contains BroadcastFilter and ResolveInfo final IIntentReceiver resultTo; // who receives final result if non-null long dispatchTime; // when dispatch started on this set of receivers @@ -48,7 +50,6 @@ class BroadcastRecord extends Binder { String resultData; // current result data value. Bundle resultExtras; // current result extra data values. boolean resultAbort; // current result abortBroadcast value. - boolean ordered; // serialize the send to receivers? int nextReceiver; // next receiver to be executed. IBinder receiver; // who is currently running, null if none. int state; @@ -86,7 +87,7 @@ class BroadcastRecord extends Binder { + " resultCode=" + resultCode + " resultData=" + resultData); pw.println(prefix + "resultExtras=" + resultExtras); pw.println(prefix + "resultAbort=" + resultAbort - + " ordered=" + ordered); + + " ordered=" + ordered + " sticky=" + sticky); pw.println(prefix + "nextReceiver=" + nextReceiver + " receiver=" + receiver); pw.println(prefix + "curFilter=" + curFilter); @@ -122,7 +123,8 @@ class BroadcastRecord extends Binder { BroadcastRecord(Intent _intent, ProcessRecord _callerApp, String _callerPackage, int _callingPid, int _callingUid, String _requiredPermission, List _receivers, IIntentReceiver _resultTo, int _resultCode, - String _resultData, Bundle _resultExtras, boolean _serialized) { + String _resultData, Bundle _resultExtras, boolean _serialized, + boolean _sticky) { intent = _intent; callerApp = _callerApp; callerPackage = _callerPackage; @@ -135,6 +137,7 @@ class BroadcastRecord extends Binder { resultData = _resultData; resultExtras = _resultExtras; ordered = _serialized; + sticky = _sticky; nextReceiver = 0; state = IDLE; } diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index a753d059177dc..b3086d51fe360 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -248,7 +248,7 @@ class PendingIntentRecord extends IIntentSender.Stub { if (sendFinish) { try { finishedReceiver.performReceive(new Intent(finalIntent), 0, - null, null, false); + null, null, false, false); } catch (RemoteException e) { } } diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java index d9cc2c62d1505..f49b5029189bb 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java @@ -135,7 +135,7 @@ public class CdmaSmsAddress extends SmsAddress { }; private static final char[] numericCharsSugar = { - '(', ')', ' ', '-', '+' + '(', ')', ' ', '-', '+', '.' }; private static final SparseBooleanArray numericCharDialableMap = new SparseBooleanArray ( diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java index 4a77e1968ca08..85840a89358d8 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java @@ -61,6 +61,16 @@ public class CdmaSmsTest extends AndroidTestCase { for (int i = 0; i < data2.length; i++) { assertEquals(addr.origBytes[i], data2[i]); } + addr = CdmaSmsAddress.parse("650.253.1000"); + assertEquals(addr.ton, CdmaSmsAddress.TON_UNKNOWN); + assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF); + assertEquals(addr.numberMode, CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK); + assertEquals(addr.numberOfDigits, 10); + assertEquals(addr.origBytes.length, 10); + byte[] data5 = {6, 5, 10, 2, 5, 3, 1, 10, 10, 10}; + for (int i = 0; i < data2.length; i++) { + assertEquals(addr.origBytes[i], data5[i]); + } addr = CdmaSmsAddress.parse("(+886) 917 222 555"); assertEquals(addr.ton, CdmaSmsAddress.TON_INTERNATIONAL_OR_IP); assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF); diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java index e424f1d801851..c66ae37e70bc2 100644 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeTest.java @@ -73,6 +73,48 @@ public class BridgeTest extends TestCase { } } + /** + * Mock implementation of {@link IStyleResourceValue}. + */ + private static class StyleResourceValueMock extends ResourceValue + implements IStyleResourceValue { + + private String mParentStyle = null; + private HashMap mItems = new HashMap(); + + StyleResourceValueMock(String name) { + super(name); + } + + StyleResourceValueMock(String name, String parentStyle) { + super(name); + mParentStyle = parentStyle; + } + + public String getParentStyle() { + return mParentStyle; + } + + public IResourceValue findItem(String name) { + return mItems.get(name); + } + + public void addItem(IResourceValue value) { + mItems.put(value.getName(), value); + } + + @Override + public void replaceWith(ResourceValue value) { + super.replaceWith(value); + + if (value instanceof StyleResourceValueMock) { + mItems.clear(); + mItems.putAll(((StyleResourceValueMock)value).mItems); + } + } + } + + public void testComputeLayout() throws Exception { TestParser parser = new TestParser(); @@ -88,8 +130,10 @@ public class BridgeTest extends TestCase { // FIXME need a dummy font for the tests! ILayoutResult result = mBridge.computeLayout(parser, new Integer(1) /* projectKey */, - screenWidth, screenHeight, - "Theme", projectResources, frameworkResources, null, null); + screenWidth, screenHeight, false /* full render */, + 160, 160f, 160f, + "Theme", false /* is project theme */, + projectResources, frameworkResources, null, null); display(result.getRootView(), ""); } @@ -191,7 +235,7 @@ public class BridgeTest extends TestCase { * a style item value. If the number of string in the array is not even, an exception is thrown. */ private IStyleResourceValue createStyle(String styleName, String... items) { - StyleResourceValue value = new StyleResourceValue(styleName); + StyleResourceValueMock value = new StyleResourceValueMock(styleName); if (items.length % 3 == 0) { for (int i = 0 ; i < items.length;) { @@ -220,8 +264,10 @@ public class BridgeTest extends TestCase { // FIXME need a dummy font for the tests! ILayoutResult result = mBridge.computeLayout(parser, new Integer(1) /* projectKey */, - screenWidth, screenHeight, - "Theme", projectResources, frameworkResources, null, null); + screenWidth, screenHeight, false /* full render */, + 160, 160f, 160f, + "Theme", false /* is project theme */, + projectResources, frameworkResources, null, null); display(result.getRootView(), ""); } diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java index cac1f95df2c2b..ef7442c4a5ca6 100644 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java @@ -41,7 +41,7 @@ public class BridgeXmlBlockParserTest extends TestCase { @Override protected void setUp() throws Exception { super.setUp(); - URL url = this.getClass().getClassLoader().getResource("data/layout1.xml"); + URL url = this.getClass().getClassLoader().getResource("layout1.xml"); mXmlPath = url.getFile(); mDoc = getXmlDocument(mXmlPath); } diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java index 67ec5e16241f2..e667472c75db8 100644 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/NinePatchTest.java @@ -12,7 +12,7 @@ public class NinePatchTest extends TestCase { @Override protected void setUp() throws Exception { - URL url = this.getClass().getClassLoader().getResource("data/button.9.png"); + URL url = this.getClass().getClassLoader().getResource("button.9.png"); mPatch = NinePatch.load(url, false /* convert */); } diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java deleted file mode 100644 index 84bdc2f03c92d..0000000000000 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/StyleResourceValue.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.layoutlib.bridge; - -import com.android.layoutlib.api.IResourceValue; -import com.android.layoutlib.api.IStyleResourceValue; - -import java.util.HashMap; - -class StyleResourceValue extends ResourceValue implements IStyleResourceValue { - - private String mParentStyle = null; - private HashMap mItems = new HashMap(); - - StyleResourceValue(String name) { - super(name); - } - - StyleResourceValue(String name, String parentStyle) { - super(name); - mParentStyle = parentStyle; - } - - public String getParentStyle() { - return mParentStyle; - } - - public IResourceValue findItem(String name) { - return mItems.get(name); - } - - public void addItem(IResourceValue value) { - mItems.put(value.getName(), value); - } - - @Override - public void replaceWith(ResourceValue value) { - super.replaceWith(value); - - if (value instanceof StyleResourceValue) { - mItems.clear(); - mItems.putAll(((StyleResourceValue)value).mItems); - } - } - -} diff --git a/tools/layoutlib/bridge/tests/data/button.9.png b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/button.9.png similarity index 100% rename from tools/layoutlib/bridge/tests/data/button.9.png rename to tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/button.9.png diff --git a/tools/layoutlib/bridge/tests/data/layout1.xml b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/layout1.xml similarity index 100% rename from tools/layoutlib/bridge/tests/data/layout1.xml rename to tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/layout1.xml diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java index 1adcc17270218..7b55ed3e8aff3 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java @@ -68,6 +68,7 @@ public class AsmGenerator { * * @param log Output logger. * @param osDestJar The path of the destination JAR to create. + * @param injectClasses The list of class from layoutlib_create to inject in layoutlib. * @param stubMethods The list of methods to stub out. Each entry must be in the form * "package.package.OuterClass$InnerClass#MethodName". * @param renameClasses The list of classes to rename, must be an even list: the binary FQCN diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java new file mode 100644 index 0000000000000..5a13b0be2bb3a --- /dev/null +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tools.layoutlib.create; + +public class CreateInfo { + /** + * The list of class from layoutlib_create to inject in layoutlib. + */ + public final static Class[] INJECTED_CLASSES = new Class[] { + OverrideMethod.class, + MethodListener.class, + MethodAdapter.class, + CreateInfo.class + }; + + /** + * The list of methods to stub out. Each entry must be in the form + * "package.package.OuterClass$InnerClass#MethodName". + */ + public final static String[] OVERRIDDEN_METHODS = new String[] { + "android.view.View#isInEditMode", + "android.content.res.Resources$Theme#obtainStyledAttributes", + }; + + /** + * The list of classes to rename, must be an even list: the binary FQCN + * of class to replace followed by the new FQCN. + */ + public final static String[] RENAMED_CLASSES = + new String[] { + "android.graphics.Bitmap", "android.graphics._Original_Bitmap", + "android.graphics.BitmapShader", "android.graphics._Original_BitmapShader", + "android.graphics.Canvas", "android.graphics._Original_Canvas", + "android.graphics.ComposeShader", "android.graphics._Original_ComposeShader", + "android.graphics.LinearGradient", "android.graphics._Original_LinearGradient", + "android.graphics.Matrix", "android.graphics._Original_Matrix", + "android.graphics.Paint", "android.graphics._Original_Paint", + "android.graphics.Path", "android.graphics._Original_Path", + "android.graphics.PorterDuffXfermode", "android.graphics._Original_PorterDuffXfermode", + "android.graphics.RadialGradient", "android.graphics._Original_RadialGradient", + "android.graphics.Shader", "android.graphics._Original_Shader", + "android.graphics.SweepGradient", "android.graphics._Original_SweepGradient", + "android.graphics.Typeface", "android.graphics._Original_Typeface", + "android.os.ServiceManager", "android.os._Original_ServiceManager", + "android.util.FloatMath", "android.util._Original_FloatMath", + "android.view.SurfaceView", "android.view._Original_SurfaceView", + "android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager", + }; + + /** + * List of classes for which the methods returning them should be deleted. + * The array contains a list of null terminated section starting with the name of the class + * to rename in which the methods are deleted, followed by a list of return types identifying + * the methods to delete. + */ + public final static String[] REMOVED_METHODS = + new String[] { + "android.graphics.Paint", // class to delete methods from + "android.graphics.Paint$Align", // list of type identifying methods to delete + "android.graphics.Paint$Style", + "android.graphics.Paint$Join", + "android.graphics.Paint$Cap", + "android.graphics.Paint$FontMetrics", + "android.graphics.Paint$FontMetricsInt", + null }; // separator, for next class/methods list. +} + diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java index 47184f10efdc6..303f0974343c2 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java @@ -43,44 +43,10 @@ public class Main { try { AsmGenerator agen = new AsmGenerator(log, osDestJar[0], - new Class[] { // classes to inject in the final JAR - OverrideMethod.class, - MethodListener.class, - MethodAdapter.class - }, - new String[] { // methods to force override - "android.view.View#isInEditMode", - "android.content.res.Resources$Theme#obtainStyledAttributes", - }, - new String[] { // classes to rename (so that we can replace them in layoutlib) - // original-platform-class-name ======> renamed-class-name - "android.graphics.Bitmap", "android.graphics._Original_Bitmap", - "android.graphics.BitmapShader", "android.graphics._Original_BitmapShader", - "android.graphics.Canvas", "android.graphics._Original_Canvas", - "android.graphics.ComposeShader", "android.graphics._Original_ComposeShader", - "android.graphics.LinearGradient", "android.graphics._Original_LinearGradient", - "android.graphics.Matrix", "android.graphics._Original_Matrix", - "android.graphics.Paint", "android.graphics._Original_Paint", - "android.graphics.Path", "android.graphics._Original_Path", - "android.graphics.PorterDuffXfermode", "android.graphics._Original_PorterDuffXfermode", - "android.graphics.RadialGradient", "android.graphics._Original_RadialGradient", - "android.graphics.Shader", "android.graphics._Original_Shader", - "android.graphics.SweepGradient", "android.graphics._Original_SweepGradient", - "android.graphics.Typeface", "android.graphics._Original_Typeface", - "android.os.ServiceManager", "android.os._Original_ServiceManager", - "android.util.FloatMath", "android.util._Original_FloatMath", - "android.view.SurfaceView", "android.view._Original_SurfaceView", - "android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager", - }, - new String[] { // methods deleted from their return type. - "android.graphics.Paint", // class to delete method from - "android.graphics.Paint$Align", // list of type identifying methods to delete - "android.graphics.Paint$Style", - "android.graphics.Paint$Join", - "android.graphics.Paint$Cap", - "android.graphics.Paint$FontMetrics", - "android.graphics.Paint$FontMetricsInt", - null } + CreateInfo.INJECTED_CLASSES, + CreateInfo.OVERRIDDEN_METHODS, + CreateInfo.RENAMED_CLASSES, + CreateInfo.REMOVED_METHODS ); AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen,