diff options
37 files changed, 1205 insertions, 426 deletions
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp index e54852446692..df59dcff0b29 100644 --- a/camera/libcameraservice/CameraService.cpp +++ b/camera/libcameraservice/CameraService.cpp @@ -683,22 +683,30 @@ void CameraService::Client::stopPreview()  {      LOGD("stopPreview (pid %d)", getCallingPid()); -    Mutex::Autolock lock(mLock); -    if (checkPid() != NO_ERROR) return; +    // hold main lock during state transition +    { +        Mutex::Autolock lock(mLock); +        if (checkPid() != NO_ERROR) return; -    if (mHardware == 0) { -        LOGE("mHardware is NULL, returning."); -        return; -    } +        if (mHardware == 0) { +            LOGE("mHardware is NULL, returning."); +            return; +        } -    mHardware->stopPreview(); -    mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME); -    LOGD("stopPreview(), hardware stopped OK"); +        mHardware->stopPreview(); +        mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME); +        LOGD("stopPreview(), hardware stopped OK"); -    if (mSurface != 0 && !mUseOverlay) { -        mSurface->unregisterBuffers(); +        if (mSurface != 0 && !mUseOverlay) { +            mSurface->unregisterBuffers(); +        } +    } + +    // hold preview buffer lock +    { +        Mutex::Autolock lock(mPreviewLock); +        mPreviewBuffer.clear();      } -    mPreviewBuffer.clear();  }  // stop recording mode @@ -706,24 +714,31 @@ void CameraService::Client::stopRecording()  {      LOGD("stopRecording (pid %d)", getCallingPid()); -    Mutex::Autolock lock(mLock); -    if (checkPid() != NO_ERROR) return; +    // hold main lock during state transition +    { +        Mutex::Autolock lock(mLock); +        if (checkPid() != NO_ERROR) return; -    if (mHardware == 0) { -        LOGE("mHardware is NULL, returning."); -        return; -    } +        if (mHardware == 0) { +            LOGE("mHardware is NULL, returning."); +            return; +        } -    if (mMediaPlayerBeep.get() != NULL) { -        mMediaPlayerBeep->seekTo(0); -        mMediaPlayerBeep->start(); -    } +        if (mMediaPlayerBeep.get() != NULL) { +            mMediaPlayerBeep->seekTo(0); +            mMediaPlayerBeep->start(); +        } -    mHardware->stopRecording(); -    mHardware->disableMsgType(CAMERA_MSG_VIDEO_FRAME); -    LOGD("stopRecording(), hardware stopped OK"); +        mHardware->stopRecording(); +        mHardware->disableMsgType(CAMERA_MSG_VIDEO_FRAME); +        LOGD("stopRecording(), hardware stopped OK"); +    } -    mPreviewBuffer.clear(); +    // hold preview buffer lock +    { +        Mutex::Autolock lock(mPreviewLock); +        mPreviewBuffer.clear(); +    }  }  // release a recording frame @@ -1216,10 +1231,10 @@ void CameraService::Client::copyFrameAndPostCopiedFrame(const sp<ICameraClient>&      // provided it's big enough. Don't allocate the memory or      // perform the copy if there's no callback. -    // hold the lock while we grab a reference to the preview buffer +    // hold the preview lock while we grab a reference to the preview buffer      sp<MemoryHeapBase> previewBuffer;      { -        Mutex::Autolock lock(mLock); +        Mutex::Autolock lock(mPreviewLock);          if (mPreviewBuffer == 0) {              mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);          } else if (size > mPreviewBuffer->virtualSize()) { diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h index 41c5d99faaaa..3e3e54f0a950 100644 --- a/camera/libcameraservice/CameraService.h +++ b/camera/libcameraservice/CameraService.h @@ -181,7 +181,6 @@ private:          mutable     Condition                   mReady;                      sp<CameraService>           mCameraService;                      sp<ISurface>                mSurface; -                    sp<MemoryHeapBase>          mPreviewBuffer;                      int                         mPreviewCallbackFlag;                      sp<MediaPlayer>             mMediaPlayerClick; @@ -197,6 +196,9 @@ private:                      sp<OverlayRef>              mOverlayRef;                      int                         mOverlayW;                      int                         mOverlayH; + +        mutable     Mutex                       mPreviewLock; +                    sp<MemoryHeapBase>          mPreviewBuffer;      };  // ---------------------------------------------------------------------------- diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java index ce063a773a67..d6c76a2a5d75 100644 --- a/core/java/android/accounts/AccountAuthenticatorCache.java +++ b/core/java/android/accounts/AccountAuthenticatorCache.java @@ -18,10 +18,16 @@ package android.accounts;  import android.content.pm.PackageManager;  import android.content.pm.RegisteredServicesCache; +import android.content.pm.XmlSerializerAndParser;  import android.content.res.TypedArray;  import android.content.Context;  import android.util.AttributeSet;  import android.text.TextUtils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException;  /**   * A cache of services that export the {@link IAccountAuthenticator} interface. This cache @@ -33,10 +39,12 @@ import android.text.TextUtils;  /* package private */ class AccountAuthenticatorCache          extends RegisteredServicesCache<AuthenticatorDescription> {      private static final String TAG = "Account"; +    private static final MySerializer sSerializer = new MySerializer();      public AccountAuthenticatorCache(Context context) {          super(context, AccountManager.ACTION_AUTHENTICATOR_INTENT, -                AccountManager.AUTHENTICATOR_META_DATA_NAME, AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME); +                AccountManager.AUTHENTICATOR_META_DATA_NAME, +                AccountManager.AUTHENTICATOR_ATTRIBUTES_NAME, sSerializer);      }      public AuthenticatorDescription parseServiceAttributes(String packageName, AttributeSet attrs) { @@ -62,4 +70,16 @@ import android.text.TextUtils;              sa.recycle();          }      } + +    private static class MySerializer implements XmlSerializerAndParser<AuthenticatorDescription> { +        public void writeAsXml(AuthenticatorDescription item, XmlSerializer out) +                throws IOException { +            out.attribute(null, "type", item.type); +        } + +        public AuthenticatorDescription createFromXml(XmlPullParser parser) +                throws IOException, XmlPullParserException { +            return AuthenticatorDescription.newKey(parser.getAttributeValue(null, "type")); +        } +    }  } diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index 4f59c4e67edd..800ad749335a 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -72,7 +72,7 @@ import com.android.internal.R;   */  public class AccountManagerService          extends IAccountManager.Stub -        implements RegisteredServicesCacheListener { +        implements RegisteredServicesCacheListener<AuthenticatorDescription> {      private static final String GOOGLE_ACCOUNT_TYPE = "com.google";      private static final String NO_BROADCAST_FLAG = "nobroadcast"; @@ -220,34 +220,29 @@ public class AccountManagerService          mMessageHandler = new MessageHandler(mMessageThread.getLooper());          mAuthenticatorCache = new AccountAuthenticatorCache(mContext); -        mAuthenticatorCache.setListener(this); +        mAuthenticatorCache.setListener(this, null /* Handler */);          mBindHelper = new AuthenticatorBindHelper(mContext, mAuthenticatorCache, mMessageHandler,                  MESSAGE_CONNECTED, MESSAGE_DISCONNECTED);          mSimWatcher = new SimWatcher(mContext);          sThis.set(this); - -        onRegisteredServicesCacheChanged();      } -    public void onRegisteredServicesCacheChanged() { +    public void onServiceChanged(AuthenticatorDescription desc, boolean removed) {          boolean accountDeleted = false;          SQLiteDatabase db = mOpenHelper.getWritableDatabase();          Cursor cursor = db.query(TABLE_ACCOUNTS,                  new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME}, -                null, null, null, null, null); +                ACCOUNTS_TYPE + "=?", new String[]{desc.type}, null, null, null);          try {              while (cursor.moveToNext()) {                  final long accountId = cursor.getLong(0);                  final String accountType = cursor.getString(1);                  final String accountName = cursor.getString(2); -                if (mAuthenticatorCache.getServiceInfo(AuthenticatorDescription.newKey(accountType)) -                        == null) { -                    Log.d(TAG, "deleting account " + accountName + " because type " -                            + accountType + " no longer has a registered authenticator"); -                    db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null); -                    accountDeleted= true; -                } +                Log.d(TAG, "deleting account " + accountName + " because type " +                        + accountType + " no longer has a registered authenticator"); +                db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null); +                accountDeleted = true;              }          } finally {              cursor.close(); diff --git a/core/java/android/accounts/AuthenticatorDescription.java b/core/java/android/accounts/AuthenticatorDescription.java index e6427004993d..91c94e6de62e 100644 --- a/core/java/android/accounts/AuthenticatorDescription.java +++ b/core/java/android/accounts/AuthenticatorDescription.java @@ -87,6 +87,10 @@ public class AuthenticatorDescription implements Parcelable {          return type.equals(other.type);      } +    public String toString() { +        return "AuthenticatorDescription {type=" + type + "}"; +    } +      /** @inhericDoc */      public void writeToParcel(Parcel dest, int flags) {          dest.writeString(type); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index daf4090369e2..b116bf8fd306 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -485,7 +485,8 @@ public final class ActivityThread {              return mResources;          } -        public Application makeApplication(boolean forceDefaultAppClass) { +        public Application makeApplication(boolean forceDefaultAppClass, +                Instrumentation instrumentation) {              if (mApplication != null) {                  return mApplication;              } @@ -512,7 +513,21 @@ public final class ActivityThread {                  }              }              mActivityThread.mAllApplications.add(app); -            return mApplication = app; +            mApplication = app; +             +            if (instrumentation != null) { +                try { +                    instrumentation.callApplicationOnCreate(app); +                } catch (Exception e) { +                    if (!instrumentation.onException(app, e)) { +                        throw new RuntimeException( +                            "Unable to create application " + app.getClass().getName() +                            + ": " + e.toString(), e); +                    } +                } +            } +             +            return app;          }          public void removeContextRegistrations(Context context, @@ -2378,7 +2393,7 @@ public final class ActivityThread {          }          try { -            Application app = r.packageInfo.makeApplication(false); +            Application app = r.packageInfo.makeApplication(false, mInstrumentation);              if (localLOGV) Log.v(TAG, "Performing launch of " + r);              if (localLOGV) Log.v( @@ -2576,7 +2591,7 @@ public final class ActivityThread {          }          try { -            Application app = packageInfo.makeApplication(false); +            Application app = packageInfo.makeApplication(false, mInstrumentation);              if (localLOGV) Log.v(                  TAG, "Performing receive of " + data.intent @@ -2730,7 +2745,7 @@ public final class ActivityThread {              ApplicationContext context = new ApplicationContext();              context.init(packageInfo, null, this); -            Application app = packageInfo.makeApplication(false); +            Application app = packageInfo.makeApplication(false, mInstrumentation);              context.setOuterContext(service);              service.attach(context, this, data.info.name, data.token, app,                      ActivityManagerNative.getDefault()); @@ -3940,7 +3955,7 @@ public final class ActivityThread {          // If the app is being launched for full backup or restore, bring it up in          // a restricted environment with the base application class. -        Application app = data.info.makeApplication(data.restrictedBackupMode); +        Application app = data.info.makeApplication(data.restrictedBackupMode, null);          mInitialApplication = app;          List<ProviderInfo> providers = data.providers; diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 4684f455e95a..bd5b07cff640 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -36,13 +36,30 @@ import java.util.Set;  import java.util.UUID;  /** - * Represents the local Bluetooth adapter. + * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter} + * lets you perform fundamental Bluetooth tasks, such as initiate + * device discovery, query a list of bonded (paired) devices, + * instantiate a {@link BluetoothDevice} using a known MAC address, and create + * a {@link BluetoothServerSocket} to listen for connection requests from other + * devices.   * - * <p>Use {@link #getDefaultAdapter} to get the default local Bluetooth - * adapter. + * <p>To get a {@link BluetoothAdapter} representing the local Bluetooth + * adapter, call the static {@link #getDefaultAdapter} method. + * Fundamentally, this is your starting point for all + * Bluetooth actions. Once you have the local adapter, you can get a set of + * {@link BluetoothDevice} objects representing all paired devices with + * {@link #getBondedDevices()}; start device discovery with + * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to + * listen for incoming connection requests with + * {@link #listenUsingRfcommWithServiceRecord(String,UUID)}.   * - * <p>Use the {@link BluetoothDevice} class for operations on remote Bluetooth - * devices. + * <p class="note"><strong>Note:</strong> + * Most methods require the {@link android.Manifest.permission#BLUETOOTH} + * permission and some also require the + * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * {@see BluetoothDevice} + * {@see BluetoothServerSocket}   */  public final class BluetoothAdapter {      private static final String TAG = "BluetoothAdapter"; diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java index 6210380595af..bc067130584e 100644 --- a/core/java/android/bluetooth/BluetoothClass.java +++ b/core/java/android/bluetooth/BluetoothClass.java @@ -20,25 +20,37 @@ import android.os.Parcel;  import android.os.Parcelable;  /** - * Represents a Bluetooth class. + * Represents a Bluetooth class, which describes general characteristics + * and capabilities of a device. For example, a Bluetooth class will + * specify the general device type such as a phone, a computer, or + * headset, and whether it's capable of services such as audio or telephony.   * - * <p>Bluetooth Class is a 32 bit field. The format of these bits is defined at - *   http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm - * (login required). This class contains that 32 bit field, and provides - * constants and methods to determine which Service Class(es) and Device Class - * are encoded in that field. + * <p>The Bluetooth class is useful as a hint to roughly describe a device (for example to + * show an icon in the UI), but does not reliably describe which Bluetooth + * profiles or services are actually supported by a device.   * - * <p>Every Bluetooth Class is composed of zero or more service classes, and + * <p>Every Bluetooth class is composed of zero or more service classes, and   * exactly one device class. The device class is further broken down into major   * and minor device class components.   * - * <p>Class is useful as a hint to roughly describe a device (for example to - * show an icon in the UI), but does not reliably describe which Bluetooth - * profiles or services are actually supported by a device. Accurate service - * discovery is done through SDP requests. + * <p>{@link BluetoothClass} is useful as a hint to roughly describe a device + * (for example to show an icon in the UI), but does not reliably describe which + * Bluetooth profiles or services are actually supported by a device. Accurate + * service discovery is done through SDP requests, which are automatically + * performed when creating an RFCOMM socket with {@link + * BluetoothDevice#createRfcommSocketToServiceRecord(UUID)} and {@link + * BluetoothAdapter#listenUsingRfcommWithServiceRecord(String,UUID)}</p>   *   * <p>Use {@link BluetoothDevice#getBluetoothClass} to retrieve the class for   * a remote device. + * + * <!-- + * The Bluetooth class is a 32 bit field. The format of these bits is defined at + * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm + * (login required). This class contains that 32 bit field, and provides + * constants and methods to determine which Service Class(es) and Device Class + * are encoded in that field. + * -->   */  public final class BluetoothClass implements Parcelable {      /** @@ -91,7 +103,7 @@ public final class BluetoothClass implements Parcelable {      }      /** -     * Bluetooth service classes. +     * Defines all service class constants.       * <p>Each {@link BluetoothClass} encodes zero or more service classes.       */      public static final class Service { @@ -109,7 +121,8 @@ public final class BluetoothClass implements Parcelable {      }      /** -     * Return true if the specified service class is supported by this class. +     * Return true if the specified service class is supported by this +     * {@link BluetoothClass}.       * <p>Valid service classes are the public constants in       * {@link BluetoothClass.Service}. For example, {@link       * BluetoothClass.Service#AUDIO}. @@ -122,17 +135,22 @@ public final class BluetoothClass implements Parcelable {      }      /** -     * Bluetooth device classes. +     * Defines all device class constants.       * <p>Each {@link BluetoothClass} encodes exactly one device class, with       * major and minor components.       * <p>The constants in {@link       * BluetoothClass.Device} represent a combination of major and minor -     * components (the complete device class). The constants in {@link -     * BluetoothClass.Device.Major} represent just the major device classes. +     * device components (the complete device class). The constants in {@link +     * BluetoothClass.Device.Major} represent only major device classes. +     * <p>See {@link BluetoothClass.Service} for service class constants.       */      public static class Device {          private static final int BITMASK               = 0x1FFC; +        /** +         * Defines all major device class constants. +         * <p>See {@link BluetoothClass.Device} for minor classes. +         */          public static class Major {              private static final int BITMASK           = 0x1F00; @@ -215,7 +233,7 @@ public final class BluetoothClass implements Parcelable {      }      /** -     * Return the major device class component of this Bluetooth class. +     * Return the major device class component of this {@link BluetoothClass}.       * <p>Values returned from this function can be compared with the       * public constants in {@link BluetoothClass.Device.Major} to determine       * which major class is encoded in this Bluetooth class. diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 849e6c7b2464..6cb9770a6100 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -31,16 +31,31 @@ import java.io.UnsupportedEncodingException;  import java.util.UUID;  /** - * Represents a remote Bluetooth device. - * - * <p>Use {@link BluetoothAdapter#getRemoteDevice} to create a {@link - * BluetoothDevice}. + * Represents a remote Bluetooth device. A {@link BluetoothDevice} lets you + * create a connection with the repective device or query information about + * it, such as the name, address, class, and bonding state.   *   * <p>This class is really just a thin wrapper for a Bluetooth hardware   * address. Objects of this class are immutable. Operations on this class   * are performed on the remote Bluetooth hardware address, using the   * {@link BluetoothAdapter} that was used to create this {@link   * BluetoothDevice}. + * + * <p>To get a {@link BluetoothDevice}, use + * {@link BluetoothAdapter#getRemoteDevice(String) + * BluetoothAdapter.getRemoteDevice(String)} to create one representing a device + * of a known MAC address (which you can get through device discovery with + * {@link BluetoothAdapter}) or get one from the set of bonded devices + * returned by {@link BluetoothAdapter#getBondedDevices() + * BluetoothAdapter.getBondedDevices()}. You can then open a + * {@link BluetoothSocket} for communciation with the remote device, using + * {@link #createRfcommSocketToServiceRecord(UUID)}. + * + * <p class="note"><strong>Note:</strong> + * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. + * + * {@see BluetoothAdapter} + * {@see BluetoothSocket}   */  public final class BluetoothDevice implements Parcelable {      private static final String TAG = "BluetoothDevice"; diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java index 605bdc11e193..1b23f6c048d2 100644 --- a/core/java/android/bluetooth/BluetoothServerSocket.java +++ b/core/java/android/bluetooth/BluetoothServerSocket.java @@ -27,29 +27,31 @@ import java.io.IOException;   * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:   * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server   * side, use a {@link BluetoothServerSocket} to create a listening server - * socket. It will return a new, connected {@link BluetoothSocket} on an - * accepted connection. On the client side, use the same - * {@link BluetoothSocket} object to both intiate the outgoing connection, - * and to manage the connected socket. + * socket. When a connection is accepted by the {@link BluetoothServerSocket}, + * it will return a new {@link BluetoothSocket} to manage the connection. + * On the client side, use a single {@link BluetoothSocket} to both intiate + * an outgoing connection and to manage the connection.   * - * <p>The most common type of Bluetooth Socket is RFCOMM. RFCOMM is a - * connection orientated, streaming transport over Bluetooth. It is also known - * as the Serial Port Profile (SPP). + * <p>The most common type of Bluetooth socket is RFCOMM, which is the type + * supported by the Android APIs. RFCOMM is a connection-oriented, streaming + * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).   * - * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to create - * a new {@link BluetoothSocket} ready for an outgoing connection to a remote - * {@link BluetoothDevice}. + * <p>To create a listenting {@link BluetoothServerSocket} that's ready for + * incoming connections, use + * {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord + * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. Then call + * {@link #accept()} to listen for incoming connection requests. This call + * will block until a connection is established, at which point, it will return + * a {@link BluetoothSocket} to manage the connection.   * - * <p>Use {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord} to - * create a listening {@link BluetoothServerSocket} ready for incoming - * connections to the local {@link BluetoothAdapter}. - * - * <p>{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread + * <p>{@link BluetoothServerSocket} is thread   * safe. In particular, {@link #close} will always immediately abort ongoing - * operations and close the socket. + * operations and close the server socket. + * + * <p class="note"><strong>Note:</strong> + * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.   * - * <p>All methods on a {@link BluetoothServerSocket} require - * {@link android.Manifest.permission#BLUETOOTH} + * {@see BluetoothSocket}   */  public final class BluetoothServerSocket implements Closeable { diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index 7e725903b3e0..dbcc758574b6 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -33,29 +33,41 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;   * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:   * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server   * side, use a {@link BluetoothServerSocket} to create a listening server - * socket. It will return a new, connected {@link BluetoothSocket} on an - * accepted connection. On the client side, use the same - * {@link BluetoothSocket} object to both intiate the outgoing connection, - * and to manage the connected socket. + * socket. When a connection is accepted by the {@link BluetoothServerSocket}, + * it will return a new {@link BluetoothSocket} to manage the connection. + * On the client side, use a single {@link BluetoothSocket} to both intiate + * an outgoing connection and to manage the connection.   * - * <p>The most common type of Bluetooth Socket is RFCOMM. RFCOMM is a - * connection orientated, streaming transport over Bluetooth. It is also known - * as the Serial Port Profile (SPP). + * <p>The most common type of Bluetooth socket is RFCOMM, which is the type + * supported by the Android APIs. RFCOMM is a connection-oriented, streaming + * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).   * - * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to create - * a new {@link BluetoothSocket} ready for an outgoing connection to a remote - * {@link BluetoothDevice}. + * <p>To create a {@link BluetoothSocket} for connecting to a known device, use + * {@link BluetoothDevice#createRfcommSocketToServiceRecord + * BluetoothDevice.createRfcommSocketToServiceRecord()}. + * Then call {@link #connect()} to attempt a connection to the remote device. + * This call will block until a connection is established or the connection + * fails.   * - * <p>Use {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord} to - * create a listening {@link BluetoothServerSocket} ready for incoming - * connections to the local {@link BluetoothAdapter}. + * <p>To create a {@link BluetoothSocket} as a server (or "host"), see the + * {@link BluetoothServerSocket} documentation.   * - * <p>{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread + * <p>Once the socket is connected, whether initiated as a client or accepted + * as a server, open the IO streams by calling {@link #getInputStream} and + * {@link #getOutputStream} in order to retrieve {@link java.io.InputStream} + * and {@link java.io.OutputStream} objects, respectively, which are + * automatically connected to the socket. + * + * <p>{@link BluetoothSocket} is thread   * safe. In particular, {@link #close} will always immediately abort ongoing   * operations and close the socket.   * - * <p>All methods on a {@link BluetoothSocket} require - * {@link android.Manifest.permission#BLUETOOTH} + * <p class="note"><strong>Note:</strong> + * Requires the {@link android.Manifest.permission#BLUETOOTH} permission. + * + * {@see BluetoothServerSocket} + * {@see java.io.InputStream} + * {@see java.io.OutputStream}   */  public final class BluetoothSocket implements Closeable {      private static final String TAG = "BluetoothSocket"; diff --git a/core/java/android/bluetooth/package.html b/core/java/android/bluetooth/package.html index 79abf0cb4a04..4f0755e71508 100644 --- a/core/java/android/bluetooth/package.html +++ b/core/java/android/bluetooth/package.html @@ -1,13 +1,109 @@  <HTML>  <BODY> -Provides classes that manage Bluetooth functionality on the device. -<p> -The Bluetooth APIs allow applications can connect and disconnect headsets, or scan  -for other kinds of Bluetooth devices and pair them. Further control includes the  -ability to write and modify the local Service Discovery Protocol (SDP) database,  -query the SDP database of other Bluetooth devices, establish RFCOMM  -channels/sockets on Android, and connect to specified sockets on other devices. +Provides classes that manage Bluetooth functionality, such as scanning for +devices, connecting with devices, and managing data transfer between devices. + +<p>The Bluetooth APIs let applications:</p> +<ul> +  <li>Scan for other Bluetooth devices</li> +  <li>Query the local Bluetooth adapter for paired Bluetooth devices</li> +  <li>Establish RFCOMM channels/sockets</li> +  <li>Connect to specified sockets on other devices</li> +  <li>Transfer data to and from other devices</li> +</ul> + +<p class="note"><strong>Note:</strong> +To perform Bluetooth communication using these APIs, an application must +declare the {@link android.Manifest.permission#BLUETOOTH} permission. Some +additional functionality, such as requesting device discovery and +pairing also requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} +permission.  </p> -<p>Remember, not all Android devices are guaranteed to have Bluetooth functionality.</p> + +<h3>Overview</h3> + +<p>Here's a basic introduction to the Bluetooth classes:</p> +<dl> +  <dt>{@link android.bluetooth.BluetoothAdapter}</dt> +  <dd>This represents the local Bluetooth adapter, which is essentially the +  entry-point to performing any interaction with Bluetooth. With it, you can +  discover other Bluetooth devices, query a list of bonded (paired) devices, +  initialize a {@link android.bluetooth.BluetoothDevice} using a known MAC +  address, and create a {@link android.bluetooth.BluetoothServerSocket} to +  listen for communications from other devices.</dd> + +  <dt>{@link android.bluetooth.BluetoothDevice}</dt> +  <dd>This represents a remote Bluetooth device. Use this to request a +  connection with a remote device through a +  {@link android.bluetooth.BluetoothSocket} +  or query information about the device such as its name, address, class, and +  bonding state.</dd> + +  <dt>{@link android.bluetooth.BluetoothSocket}</dt> +  <dd>This represents the interface for a Bluetooth socket +  (similar to a TCP client-side {@link java.net.Socket}). This is the +  connection point that allows an app to transfer data with another Bluetooth +  device via {@link java.io.InputStream} and {@link java.io.OutputStream}.</dd> +  <dt>{@link android.bluetooth.BluetoothServerSocket}</dt> + +  <dd>This represents an open server socket that listens for incoming requests +  (similar to a TCP server-side {@link java.net.ServerSocket}). +  When attempting to connect two Android devices, one device will need to open +  a server socket with this class. When a connection is accepted, a new +  {@link android.bluetooth.BluetoothSocket} will be returned, +  which can be used to manage the connection and transfer data.</dd> + +  <dt>{@link android.bluetooth.BluetoothClass}</dt> +  <dd>This represents the Bluetooth class for a device which describes general +  characteristics and capabilities of a device. This class and its subclasses +  don't provide any actual functionality. The sub-classes are entirely composed +  of constants for the device and service class definitions.</dd> +</dl> + + +<h3>Example Procedure</h3> + +<p>For example, here's an pseudo-code procedure for discovering and +connecting a remote device, and transfering data:</p> + +<ol> +  <li>Register a {@link android.content.BroadcastReceiver} that accepts the +  {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent.</li> +  <li>Call {@link android.bluetooth.BluetoothAdapter#getDefaultAdapter} to +  retrieve the Android system's local +  {@link android.bluetooth.BluetoothAdapter}.</li> +  <li>Call {@link android.bluetooth.BluetoothAdapter#startDiscovery() +  BluetoothAdapter.startDiscovery()} to scan for local devices. This is where +  the BroadcastReceiver comes in; Android now scans for devices and will +  broadcast the {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent +  for each remote device discovered. The +  {@link android.content.BroadcastReceiver} +  you created will receive each Intent.</li> +  <li>The {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent +  includes the {@link android.bluetooth.BluetoothDevice#EXTRA_DEVICE} +  Parcelable extra, which is a {@link android.bluetooth.BluetoothDevice} +  object. Extract this from the Intent and call +  {@link android.bluetooth.BluetoothDevice#createRfcommSocketToServiceRecord(java.util.UUID) +  BluetoothDevice.createRfcommSocketToServiceRecord()} +  to open a {@link android.bluetooth.BluetoothSocket} with a chosen +  remote device.</li> +  <li>Call {@link android.bluetooth.BluetoothSocket#connect() +  BluetoothSocket.connect()} to connect with the remote device.</li> +  <li>When successfully connected, call +  {@link android.bluetooth.BluetoothSocket#getInputStream() +  BluetoothSocket.getInputStream()} and/or +  {@link android.bluetooth.BluetoothSocket#getOutputStream() +  BluetoothSocket.getOutputStream()} to retreive an +  {@link java.io.InputStream} and {@link java.io.OutputStream}, respectively, +  which are hooked into the socket.</li> +  <li>Use {@link java.io.InputStream#read(byte[]) InputStream.read()} and +  {@link java.io.OutputStream#write(byte[]) OutputStream.write()} to transfer +  data.</li> +</ol> + + + +<p class="note"><strong>Note:</strong> +Not all Android devices are guaranteed to have Bluetooth functionality.</p>  </BODY>  </HTML> diff --git a/core/java/android/content/AbstractSyncableContentProvider.java b/core/java/android/content/AbstractSyncableContentProvider.java index eba87150e4e7..fbe3548e8a02 100644 --- a/core/java/android/content/AbstractSyncableContentProvider.java +++ b/core/java/android/content/AbstractSyncableContentProvider.java @@ -135,6 +135,8 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro          public void onCreate(SQLiteDatabase db) {              bootstrapDatabase(db);              mSyncState.createDatabase(db); +            ContentResolver.requestSync(null /* all accounts */, +                    mContentUri.getAuthority(), new Bundle());          }          @Override diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java index 7d9f1de9da92..6ade83781470 100644 --- a/core/java/android/content/SyncAdaptersCache.java +++ b/core/java/android/content/SyncAdaptersCache.java @@ -17,9 +17,14 @@  package android.content;  import android.content.pm.RegisteredServicesCache; +import android.content.pm.XmlSerializerAndParser;  import android.content.res.TypedArray; -import android.content.Context;  import android.util.AttributeSet; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException;  /**   * A cache of services that export the {@link android.content.ISyncAdapter} interface. @@ -31,9 +36,10 @@ import android.util.AttributeSet;      private static final String SERVICE_INTERFACE = "android.content.SyncAdapter";      private static final String SERVICE_META_DATA = "android.content.SyncAdapter";      private static final String ATTRIBUTES_NAME = "sync-adapter"; +    private static final MySerializer sSerializer = new MySerializer();      SyncAdaptersCache(Context context) { -        super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME); +        super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, sSerializer);      }      public SyncAdapterType parseServiceAttributes(String packageName, AttributeSet attrs) { @@ -57,4 +63,18 @@ import android.util.AttributeSet;              sa.recycle();          }      } + +    static class MySerializer implements XmlSerializerAndParser<SyncAdapterType> { +        public void writeAsXml(SyncAdapterType item, XmlSerializer out) throws IOException { +            out.attribute(null, "authority", item.authority); +            out.attribute(null, "accountType", item.accountType); +        } +     +        public SyncAdapterType createFromXml(XmlPullParser parser) +                throws IOException, XmlPullParserException { +            final String authority = parser.getAttributeValue(null, "authority"); +            final String accountType = parser.getAttributeValue(null, "accountType"); +            return SyncAdapterType.newKey(authority, accountType); +        } +    }  }
\ No newline at end of file diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 538373f32278..b39a67de5f8b 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -22,6 +22,8 @@ import android.content.Intent;  import android.content.IntentFilter;  import android.content.ComponentName;  import android.content.res.XmlResourceParser; +import android.os.Environment; +import android.os.Handler;  import android.util.Log;  import android.util.AttributeSet;  import android.util.Xml; @@ -29,14 +31,26 @@ import android.util.Xml;  import java.util.Map;  import java.util.Collection;  import java.util.Collections; +import java.util.HashMap;  import java.util.List; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicReference; +import java.io.File; +import java.io.FileOutputStream;  import java.io.FileDescriptor;  import java.io.PrintWriter;  import java.io.IOException; +import java.io.FileInputStream; + +import com.android.internal.os.AtomicFile; +import com.android.internal.util.FastXmlSerializer;  import com.google.android.collect.Maps; +import com.google.android.collect.Lists; +  import org.xmlpull.v1.XmlPullParserException;  import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer;  /**   * A cache of registered services. This cache @@ -52,75 +66,104 @@ public abstract class RegisteredServicesCache<V> {      private final String mInterfaceName;      private final String mMetaDataName;      private final String mAttributesName; +    private final XmlSerializerAndParser<V> mSerializerAndParser; +    private final AtomicReference<BroadcastReceiver> mReceiver; -    public RegisteredServicesCacheListener getListener() { -        return mListener; -    } - -    public void setListener(RegisteredServicesCacheListener listener) { -        mListener = listener; -    } - -    private volatile RegisteredServicesCacheListener mListener; +    private final Object mServicesLock = new Object(); +    // synchronized on mServicesLock +    private HashMap<V, Integer> mPersistentServices; +    // synchronized on mServicesLock +    private Map<V, ServiceInfo<V>> mServices; +    // synchronized on mServicesLock +    private boolean mPersistentServicesFileDidNotExist; -    // no need to be synchronized since the map is never changed once mService is written -    volatile Map<V, ServiceInfo<V>> mServices; +    /** +     * This file contains the list of known services. We would like to maintain this forever +     * so we store it as an XML file. +     */ +    private final AtomicFile mPersistentServicesFile; -    // synchronized on "this" -    private BroadcastReceiver mReceiver = null; +    // the listener and handler are synchronized on "this" and must be updated together +    private RegisteredServicesCacheListener<V> mListener; +    private Handler mHandler;      public RegisteredServicesCache(Context context, String interfaceName, String metaDataName, -            String attributeName) { +            String attributeName, XmlSerializerAndParser<V> serializerAndParser) {          mContext = context;          mInterfaceName = interfaceName;          mMetaDataName = metaDataName;          mAttributesName = attributeName; +        mSerializerAndParser = serializerAndParser; + +        File dataDir = Environment.getDataDirectory(); +        File systemDir = new File(dataDir, "system"); +        File syncDir = new File(systemDir, "registered_services"); +        mPersistentServicesFile = new AtomicFile(new File(syncDir, interfaceName + ".xml")); + +        generateServicesMap(); + +        final BroadcastReceiver receiver = new BroadcastReceiver() { +            @Override +            public void onReceive(Context context1, Intent intent) { +                generateServicesMap(); +            } +        }; +        mReceiver = new AtomicReference<BroadcastReceiver>(receiver); +        IntentFilter intentFilter = new IntentFilter(); +        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); +        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); +        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); +        intentFilter.addDataScheme("package"); +        mContext.registerReceiver(receiver, intentFilter);      }      public void dump(FileDescriptor fd, PrintWriter fout, String[] args) { -        getAllServices(); -        Map<V, ServiceInfo<V>> services = mServices; +        Map<V, ServiceInfo<V>> services; +        synchronized (mServicesLock) { +            services = mServices; +        }          fout.println("RegisteredServicesCache: " + services.size() + " services");          for (ServiceInfo info : services.values()) {              fout.println("  " + info);          }      } -    private boolean maybeRegisterForPackageChanges() { +    public RegisteredServicesCacheListener<V> getListener() {          synchronized (this) { -            if (mReceiver == null) { -                synchronized (this) { -                    mReceiver = new BroadcastReceiver() { -                        @Override -                        public void onReceive(Context context, Intent intent) { -                            mServices = generateServicesMap(); -                            RegisteredServicesCacheListener listener = mListener; -                            if (listener != null) { -                                listener.onRegisteredServicesCacheChanged(); -                            } -                        } -                    }; -                } +            return mListener; +        } +    } -                IntentFilter intentFilter = new IntentFilter(); -                intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); -                intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); -                intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); -                intentFilter.addDataScheme("package"); -                mContext.registerReceiver(mReceiver, intentFilter); -                return true; -            } -            return false; +    public void setListener(RegisteredServicesCacheListener<V> listener, Handler handler) { +        if (handler == null) { +            handler = new Handler(mContext.getMainLooper()); +        } +        synchronized (this) { +            mHandler = handler; +            mListener = listener;          }      } -    private void maybeUnregisterForPackageChanges() { +    private void notifyListener(final V type, final boolean removed) { +        if (Log.isLoggable(TAG, Log.VERBOSE)) { +            Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added")); +        } +        RegisteredServicesCacheListener<V> listener; +        Handler handler;           synchronized (this) { -            if (mReceiver != null) { -                mContext.unregisterReceiver(mReceiver); -                mReceiver = null; -            } +            listener = mListener; +            handler = mHandler; +        } +        if (listener == null) { +            return;          } +         +        final RegisteredServicesCacheListener<V> listener2 = listener; +        handler.post(new Runnable() { +            public void run() { +                listener2.onServiceChanged(type, removed); +            } +        });      }      /** @@ -140,7 +183,7 @@ public abstract class RegisteredServicesCache<V> {          @Override          public String toString() { -            return "ServiceInfo: " + type + ", " + componentName; +            return "ServiceInfo: " + type + ", " + componentName + ", uid " + uid;          }      } @@ -150,11 +193,9 @@ public abstract class RegisteredServicesCache<V> {       * @return the AuthenticatorInfo that matches the account type or null if none is present       */      public ServiceInfo<V> getServiceInfo(V type) { -        if (mServices == null) { -            maybeRegisterForPackageChanges(); -            mServices = generateServicesMap(); +        synchronized (mServicesLock) { +            return mServices.get(type);          } -        return mServices.get(type);      }      /** @@ -162,54 +203,171 @@ public abstract class RegisteredServicesCache<V> {       * registered authenticators.       */      public Collection<ServiceInfo<V>> getAllServices() { -        if (mServices == null) { -            maybeRegisterForPackageChanges(); -            mServices = generateServicesMap(); +        synchronized (mServicesLock) { +            return Collections.unmodifiableCollection(mServices.values());          } -        return Collections.unmodifiableCollection(mServices.values());      }      /**       * Stops the monitoring of package additions, removals and changes.       */      public void close() { -        maybeUnregisterForPackageChanges(); +        final BroadcastReceiver receiver = mReceiver.getAndSet(null); +        if (receiver != null) { +            mContext.unregisterReceiver(receiver); +        }      }      @Override      protected void finalize() throws Throwable { -        synchronized (this) { -            if (mReceiver != null) { -                Log.e(TAG, "RegisteredServicesCache finalized without being closed"); -            } +        if (mReceiver.get() != null) { +            Log.e(TAG, "RegisteredServicesCache finalized without being closed");          }          close();          super.finalize();      } -    Map<V, ServiceInfo<V>> generateServicesMap() { -        Map<V, ServiceInfo<V>> services = Maps.newHashMap(); -        PackageManager pm = mContext.getPackageManager(); - -        List<ResolveInfo> resolveInfos = -                pm.queryIntentServices(new Intent(mInterfaceName), PackageManager.GET_META_DATA); +    private boolean inSystemImage(int callerUid) { +        String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid); +        for (String name : packages) { +            try { +                PackageInfo packageInfo = +                        mContext.getPackageManager().getPackageInfo(name, 0 /* flags */); +                if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { +                    return true; +                } +            } catch (PackageManager.NameNotFoundException e) { +                return false; +            } +        } +        return false; +    } +    void generateServicesMap() { +        PackageManager pm = mContext.getPackageManager(); +        ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>(); +        List<ResolveInfo> resolveInfos = pm.queryIntentServices(new Intent(mInterfaceName), +                PackageManager.GET_META_DATA);          for (ResolveInfo resolveInfo : resolveInfos) {              try {                  ServiceInfo<V> info = parseServiceInfo(resolveInfo); -                if (info != null) { -                    services.put(info.type, info); -                } else { -                    Log.w(TAG, "Unable to load input method " + resolveInfo.toString()); +                if (info == null) { +                    Log.w(TAG, "Unable to load service info " + resolveInfo.toString()); +                    continue;                  } +                serviceInfos.add(info);              } catch (XmlPullParserException e) { -                Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e); +                Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);              } catch (IOException e) { -                Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e); +                Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);              }          } -        return services; +        synchronized (mServicesLock) { +            if (Log.isLoggable(TAG, Log.VERBOSE)) { +                Log.d(TAG, "generateServicesMap: " + mInterfaceName); +            } +            if (mPersistentServices == null) { +                readPersistentServicesLocked(); +            } +            mServices = Maps.newHashMap(); +            boolean changed = false; +            if (Log.isLoggable(TAG, Log.VERBOSE)) { +                Log.d(TAG, "found " + serviceInfos.size() + " services"); +            } +            for (ServiceInfo<V> info : serviceInfos) { +                // four cases: +                // - doesn't exist yet +                //   - add, notify user that it was added +                // - exists and the UID is the same +                //   - replace, don't notify user +                // - exists, the UID is different, and the new one is not a system package +                //   - ignore +                // - exists, the UID is different, and the new one is a system package +                //   - add, notify user that it was added +                Integer previousUid = mPersistentServices.get(info.type); +                if (previousUid == null) { +                    if (Log.isLoggable(TAG, Log.VERBOSE)) { +                        Log.d(TAG, "encountered new type: " + info); +                    } +                    changed = true; +                    mServices.put(info.type, info); +                    mPersistentServices.put(info.type, info.uid); +                    if (!mPersistentServicesFileDidNotExist) { +                        notifyListener(info.type, false /* removed */); +                    } +                } else if (previousUid == info.uid) { +                    if (Log.isLoggable(TAG, Log.VERBOSE)) { +                        Log.d(TAG, "encountered existing type with the same uid: " + info); +                    } +                    mServices.put(info.type, info); +                } else if (inSystemImage(info.uid) +                        || !containsTypeAndUid(serviceInfos, info.type, previousUid)) { +                    if (Log.isLoggable(TAG, Log.VERBOSE)) { +                        if (inSystemImage(info.uid)) { +                            Log.d(TAG, "encountered existing type with a new uid but from" +                                    + " the system: " + info); +                        } else { +                            Log.d(TAG, "encountered existing type with a new uid but existing was" +                                    + " removed: " + info); +                        } +                    } +                    changed = true; +                    mServices.put(info.type, info); +                    mPersistentServices.put(info.type, info.uid); +                    notifyListener(info.type, false /* removed */); +                } else { +                    // ignore +                    if (Log.isLoggable(TAG, Log.VERBOSE)) { +                        Log.d(TAG, "encountered existing type with a new uid, ignoring: " + info); +                    } +                } +            } + +            ArrayList<V> toBeRemoved = Lists.newArrayList(); +            for (V v1 : mPersistentServices.keySet()) { +                if (!containsType(serviceInfos, v1)) { +                    toBeRemoved.add(v1); +                } +            } +            for (V v1 : toBeRemoved) { +                mPersistentServices.remove(v1); +                changed = true; +                notifyListener(v1, true /* removed */); +            } +            if (changed) { +                if (Log.isLoggable(TAG, Log.VERBOSE)) { +                    Log.d(TAG, "writing updated list of persistent services"); +                } +                writePersistentServicesLocked(); +            } else { +                if (Log.isLoggable(TAG, Log.VERBOSE)) { +                    Log.d(TAG, "persistent services did not change, so not writing anything"); +                } +            } +            mPersistentServicesFileDidNotExist = false; +        } +    } + +    private boolean containsType(ArrayList<ServiceInfo<V>> serviceInfos, V type) { +        for (int i = 0, N = serviceInfos.size(); i < N; i++) { +            if (serviceInfos.get(i).type.equals(type)) { +                return true; +            } +        } + +        return false; +    } + +    private boolean containsTypeAndUid(ArrayList<ServiceInfo<V>> serviceInfos, V type, int uid) { +        for (int i = 0, N = serviceInfos.size(); i < N; i++) { +            final ServiceInfo<V> serviceInfo = serviceInfos.get(i); +            if (serviceInfo.type.equals(type) && serviceInfo.uid == uid) { +                return true; +            } +        } + +        return false;      }      private ServiceInfo<V> parseServiceInfo(ResolveInfo service) @@ -252,5 +410,89 @@ public abstract class RegisteredServicesCache<V> {          }      } +    /** +     * Read all sync status back in to the initial engine state. +     */ +    private void readPersistentServicesLocked() { +        mPersistentServices = Maps.newHashMap(); +        if (mSerializerAndParser == null) { +            return; +        } +        FileInputStream fis = null; +        try { +            mPersistentServicesFileDidNotExist = !mPersistentServicesFile.getBaseFile().exists(); +            if (mPersistentServicesFileDidNotExist) { +                return; +            } +            fis = mPersistentServicesFile.openRead(); +            XmlPullParser parser = Xml.newPullParser(); +            parser.setInput(fis, null); +            int eventType = parser.getEventType(); +            while (eventType != XmlPullParser.START_TAG) { +                eventType = parser.next(); +            } +            String tagName = parser.getName(); +            if ("services".equals(tagName)) { +                eventType = parser.next(); +                do { +                    if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) { +                        tagName = parser.getName(); +                        if ("service".equals(tagName)) { +                            V service = mSerializerAndParser.createFromXml(parser); +                            if (service == null) { +                                break; +                            } +                            String uidString = parser.getAttributeValue(null, "uid"); +                            int uid = Integer.parseInt(uidString); +                            mPersistentServices.put(service, uid); +                        } +                    } +                    eventType = parser.next(); +                } while (eventType != XmlPullParser.END_DOCUMENT); +            } +        } catch (Exception e) { +            Log.w(TAG, "Error reading persistent services, starting from scratch", e); +        } finally { +            if (fis != null) { +                try { +                    fis.close(); +                } catch (java.io.IOException e1) { +                } +            } +        } +    } + +    /** +     * Write all sync status to the sync status file. +     */ +    private void writePersistentServicesLocked() { +        if (mSerializerAndParser == null) { +            return; +        } +        FileOutputStream fos = null; +        try { +            fos = mPersistentServicesFile.startWrite(); +            XmlSerializer out = new FastXmlSerializer(); +            out.setOutput(fos, "utf-8"); +            out.startDocument(null, true); +            out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); +            out.startTag(null, "services"); +            for (Map.Entry<V, Integer> service : mPersistentServices.entrySet()) { +                out.startTag(null, "service"); +                out.attribute(null, "uid", Integer.toString(service.getValue())); +                mSerializerAndParser.writeAsXml(service.getKey(), out); +                out.endTag(null, "service"); +            } +            out.endTag(null, "services"); +            out.endDocument(); +            mPersistentServicesFile.finishWrite(fos); +        } catch (java.io.IOException e1) { +            Log.w(TAG, "Error writing accounts", e1); +            if (fos != null) { +                mPersistentServicesFile.failWrite(fos); +            } +        } +    } +      public abstract V parseServiceAttributes(String packageName, AttributeSet attrs);  } diff --git a/core/java/android/content/pm/RegisteredServicesCacheListener.java b/core/java/android/content/pm/RegisteredServicesCacheListener.java index c92c86e8da0a..2bc094276dce 100644 --- a/core/java/android/content/pm/RegisteredServicesCacheListener.java +++ b/core/java/android/content/pm/RegisteredServicesCacheListener.java @@ -1,12 +1,16 @@  package android.content.pm; +import android.os.Parcelable; +  /**   * Listener for changes to the set of registered services managed by a RegisteredServicesCache.   * @hide   */ -public interface RegisteredServicesCacheListener { +public interface RegisteredServicesCacheListener<V> {      /** -     * Invoked when the registered services cache changes. +     * Invoked when a service is registered or changed. +     * @param type the type of registered service +     * @param removed true if the service was removed       */ -    void onRegisteredServicesCacheChanged(); +    void onServiceChanged(V type, boolean removed);  } diff --git a/core/java/android/content/pm/XmlSerializerAndParser.java b/core/java/android/content/pm/XmlSerializerAndParser.java new file mode 100644 index 000000000000..33598f0b3890 --- /dev/null +++ b/core/java/android/content/pm/XmlSerializerAndParser.java @@ -0,0 +1,14 @@ +package android.content.pm; + +import org.xmlpull.v1.XmlSerializer; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import android.os.Parcel; + +import java.io.IOException; + +/** @hide */ +public interface XmlSerializerAndParser<T> { +    void writeAsXml(T item, XmlSerializer out) throws IOException; +    T createFromXml(XmlPullParser parser) throws IOException, XmlPullParserException; +} diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java index 9638262faee0..f9dce255056e 100644 --- a/core/java/android/pim/vcard/VCardComposer.java +++ b/core/java/android/pim/vcard/VCardComposer.java @@ -41,6 +41,8 @@ import android.provider.ContactsContract.CommonDataKinds.Photo;  import android.provider.ContactsContract.CommonDataKinds.StructuredName;  import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;  import android.provider.ContactsContract.CommonDataKinds.Website; +import android.telephony.PhoneNumberUtils; +import android.text.SpannableStringBuilder;  import android.text.TextUtils;  import android.text.format.Time;  import android.util.CharsetUtils; @@ -761,27 +763,58 @@ public class VCardComposer {          }      } +    private boolean containsNonEmptyName(ContentValues contentValues) { +        final String familyName = contentValues.getAsString(StructuredName.FAMILY_NAME); +        final String middleName = contentValues.getAsString(StructuredName.MIDDLE_NAME); +        final String givenName = contentValues.getAsString(StructuredName.GIVEN_NAME); +        final String prefix = contentValues.getAsString(StructuredName.PREFIX); +        final String suffix = contentValues.getAsString(StructuredName.SUFFIX); +        final String displayName = contentValues.getAsString(StructuredName.DISPLAY_NAME); +        return !(TextUtils.isEmpty(familyName) && TextUtils.isEmpty(middleName) && +                TextUtils.isEmpty(givenName) && TextUtils.isEmpty(prefix) && +                TextUtils.isEmpty(suffix) && TextUtils.isEmpty(displayName)); +    } +      private void appendStructuredNamesInternal(final StringBuilder builder,              final List<ContentValues> contentValuesList) {          // For safety, we'll emit just one value around StructuredName, as external importers          // may get confused with multiple "N", "FN", etc. properties, though it is valid in          // vCard spec.          ContentValues primaryContentValues = null; +        ContentValues subprimaryContentValues = null;          for (ContentValues contentValues : contentValuesList) { +            if (contentValues == null){ +                continue; +            }              Integer isSuperPrimary = contentValues.getAsInteger(StructuredName.IS_SUPER_PRIMARY); -            if (isSuperPrimary != null && isSuperPrimary != 0) { +            if (isSuperPrimary != null && isSuperPrimary > 0) {                  // We choose "super primary" ContentValues.                  primaryContentValues = contentValues;                  break; -            } else if (primaryContentValues == null && contentValues != null) { -                // We choose the first ContentValues if "super primary" ContentValues does not exist. -                primaryContentValues = contentValues; +            } else if (primaryContentValues == null) { +                // We choose the first "primary" ContentValues +                // if "super primary" ContentValues does not exist. +                Integer isPrimary = contentValues.getAsInteger(StructuredName.IS_PRIMARY); +                if (isPrimary != null && isPrimary > 0 && +                        containsNonEmptyName(contentValues)) { +                    primaryContentValues = contentValues; +                    // Do not break, since there may be ContentValues with "super primary" +                    // afterword. +                } else if (subprimaryContentValues == null && +                        containsNonEmptyName(contentValues)) { +                    subprimaryContentValues = contentValues; +                }              }          }          if (primaryContentValues == null) { -            Log.e(LOG_TAG, "All ContentValues given from database is empty."); -            primaryContentValues = new ContentValues(); +            if (subprimaryContentValues != null) { +                // We choose the first ContentValues if any "primary" ContentValues does not exist. +                primaryContentValues = subprimaryContentValues; +            } else { +                Log.e(LOG_TAG, "All ContentValues given from database is empty."); +                primaryContentValues = new ContentValues(); +            }          }          final String familyName = primaryContentValues @@ -1140,13 +1173,39 @@ public class VCardComposer {                  if (TextUtils.isEmpty(phoneNumber)) {                      continue;                  } -                phoneLineExists = true;                  int type = (typeAsObject != null ? typeAsObject : Phone.TYPE_HOME); -                // TODO: Premature, since this allows two phone numbers which are -                //        same from the view of phone number format (e.g. "100" v.s. "1-0-0") -                if (!phoneSet.contains(phoneNumber)) { -                    phoneSet.add(phoneNumber); -                    appendVCardTelephoneLine(builder, type, label, phoneNumber); + +                phoneLineExists = true; +                if (type == Phone.TYPE_PAGER) { +                    phoneLineExists = true; +                    if (!phoneSet.contains(phoneNumber)) { +                        phoneSet.add(phoneNumber); +                        appendVCardTelephoneLine(builder, type, label, phoneNumber); +                    } +                } else { +                    // The entry "may" have several phone numbers when the contact entry is +                    // corrupted because of its original source. +                    // +                    // e.g. I encountered the entry like the following. +                    // "111-222-3333 (Miami)\n444-555-6666 (Broward; 305-653-6796 (Miami); ..." +                    // This kind of entry is not able to be inserted via Android devices, but +                    // possible if the source of the data is already corrupted. +                    List<String> phoneNumberList = splitIfSeveralPhoneNumbersExist(phoneNumber); +                    if (phoneNumberList.isEmpty()) { +                        continue; +                    } +                    phoneLineExists = true; +                    for (String actualPhoneNumber : phoneNumberList) { +                        if (!phoneSet.contains(actualPhoneNumber)) { +                            final int format = VCardUtils.getPhoneNumberFormat(mVCardType); +                            SpannableStringBuilder tmpBuilder = +                                new SpannableStringBuilder(actualPhoneNumber); +                            PhoneNumberUtils.formatNumber(tmpBuilder, format); +                            final String formattedPhoneNumber = tmpBuilder.toString(); +                            phoneSet.add(actualPhoneNumber); +                            appendVCardTelephoneLine(builder, type, label, formattedPhoneNumber); +                        } +                    }                  }              }          } @@ -1156,6 +1215,27 @@ public class VCardComposer {          }      } +    private List<String> splitIfSeveralPhoneNumbersExist(final String phoneNumber) { +        List<String> phoneList = new ArrayList<String>(); + +        StringBuilder builder = new StringBuilder(); +        final int length = phoneNumber.length(); +        for (int i = 0; i < length; i++) { +            final char ch = phoneNumber.charAt(i); +            if (Character.isDigit(ch)) { +                builder.append(ch); +            } else if ((ch == ';' || ch == '\n') && builder.length() > 0) { +                phoneList.add(builder.toString()); +                builder = new StringBuilder(); +            } +        } +        if (builder.length() > 0) { +            phoneList.add(builder.toString()); +        } + +        return phoneList; +    } +      private void appendEmails(final StringBuilder builder,              final Map<String, List<ContentValues>> contentValuesListMap) {          final List<ContentValues> contentValuesList = contentValuesListMap diff --git a/core/java/android/pim/vcard/VCardUtils.java b/core/java/android/pim/vcard/VCardUtils.java index 4f50103a84d7..dd44288a881a 100644 --- a/core/java/android/pim/vcard/VCardUtils.java +++ b/core/java/android/pim/vcard/VCardUtils.java @@ -20,6 +20,7 @@ import android.content.ContentValues;  import android.provider.ContactsContract.Data;  import android.provider.ContactsContract.CommonDataKinds.Phone;  import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; +import android.telephony.PhoneNumberUtils;  import android.text.TextUtils;  import java.util.Collection; @@ -168,6 +169,14 @@ public class VCardUtils {          return list;      } +    public static int getPhoneNumberFormat(final int vcardType) { +        if (VCardConfig.isJapaneseDevice(vcardType)) { +            return PhoneNumberUtils.FORMAT_JAPAN; +        } else { +            return PhoneNumberUtils.FORMAT_NANP; +        } +    } +      /**       * Inserts postal data into the builder object.       *  diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl index bbd9ddeada9b..37e6133bd134 100644 --- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl +++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl @@ -16,11 +16,14 @@  package android.service.wallpaper; +import android.view.MotionEvent; +  /**   * @hide   */  oneway interface IWallpaperEngine {      void setDesiredSize(int width, int height);      void setVisibility(boolean visible); +    void dispatchPointer(in MotionEvent event);  	void destroy();  } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index e79832b20053..b29d8374d7a5 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -746,6 +746,12 @@ public abstract class WallpaperService extends Service {              mCaller.sendMessage(msg);          } +        public void dispatchPointer(MotionEvent event) { +            if (mEngine != null) { +                mEngine.mWindow.onDispatchPointer(event, event.getEventTime(), false); +            } +        } +                  public void destroy() {              Message msg = mCaller.obtainMessage(DO_DETACH);              mCaller.sendMessage(msg); @@ -805,6 +811,7 @@ public abstract class WallpaperService extends Service {                              mEngine.mPendingMove = null;                          }                      } +                    if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev);                      mEngine.onTouchEvent(ev);                      ev.recycle();                  } break; diff --git a/docs/html/sdk/adding-components.jd b/docs/html/sdk/adding-components.jd index 967c3521b6b6..bc8217091eef 100644 --- a/docs/html/sdk/adding-components.jd +++ b/docs/html/sdk/adding-components.jd @@ -54,8 +54,13 @@ compatibility.</p>  <h2 id="InstallingComponents">Installing SDK Components</h2> -<p>Use the Android SDK and AVD Manager to install new SDK components.  -You can launch the SDK and AVD Manager in one of these ways:</p> +<p>Use the Android SDK and AVD Manager to install new SDK components. </p> + +<p class="caution"><strong>Important:</strong> Before you install SDK components,  +we recommend that you disable any antivirus programs that may be running on +your computer.</p> + +<p>You can launch the SDK and AVD Manager in one of these ways:</p>  <ul>    <li>Execute the {@code android}</a> tool command with no options. If you  haven't used the tool before, change to the <code><sdk>/tools</code> diff --git a/docs/html/sdk/installing.jd b/docs/html/sdk/installing.jd index edc77e83855f..261b49f97fa9 100644 --- a/docs/html/sdk/installing.jd +++ b/docs/html/sdk/installing.jd @@ -26,8 +26,8 @@ sdk.preview=0  </div>  </div> -<p>This page describes how to install the Android SDK and set up your -development environment for the first time.</p> +<p>This page describes how to install the latest version of the Android SDK  +and set up your development environment for the first time.</p>  <p>If you encounter any problems during installation, see the   <a href="#troubleshooting">Troubleshooting</a> section at the bottom of @@ -36,14 +36,14 @@ this page.</p>  <h4>Updating?</h4>  <p>If you are currently using the Android 1.6 SDK, you do not necessarily need -to install the new SDK, since your existing SDK already includes the Android SDK -and AVD Manager tool. To develop against the new Android 2.0 platform, for -example, you could just download the updated SDK Tools (Revision 3) and the -Android 2.0 platform into your existing SDK.</p> +to install a newer SDK, since you can already update the platforms, tools, and +other components using the Android SDK and AVD Manager tool. To develop against +the latest Android platform, for example, you could just download the latest SDK +Tools and then add the new Android platform into your existing SDK.</p>  <p>If you are using Android 1.5 SDK or older, you should install the new SDK as  described in this document and move your application projects to the new -environment. </p> +SDK environment. </p>  <h2 id="Preparing">Prepare for Installation</h2> @@ -65,13 +65,15 @@ to install Eclipse, you can download it from this location: </p>  <h2 id="Installing">Download and Install the SDK</h2>  <p>Download the SDK package that is appropriate for your development computer. -Unpack the Android SDK archive to a suitable location on your machine. By -default, the SDK files are unpacked into a directory named -<code>android-sdk-<machine-platform></code>. </p> +You can get the latest version from the <a href="{@docRoot}sdk/index.html">SDK +download page</a>.</p> -<p>Make a note of the name and location of the unpacked SDK directory on your -system — you will need to refer to the SDK directory later, when setting -up the ADT plugin or when using the SDK tools.</p> +<p>After downloading, unpack the Android SDK archive to a suitable location on your +machine. By default, the SDK files are unpacked into a directory named +<code>android-sdk-<machine-platform></code>. Make a note of the name and +location of the unpacked SDK directory on your system — you will need to +refer to the SDK directory later, when setting up the ADT plugin or when using +the SDK tools.</p>  <p>Optionally, you may want to add the location of the SDK's primary  <code>tools</code> directory to your system <code>PATH</code>. The primary @@ -128,7 +130,7 @@ debug your application.</p>  <h2 id="components">Add Android Platforms and Other Components</h2> -<div class="sidebox-wrapper"> +<div class="sidebox-wrapper" style="margin-right:2.5em;">  <div class="sidebox">  <p>The <strong>Android SDK and AVD Manager</strong> tool is pre-installed in  your SDK. Using the tool is a key part of performing the initial setup of your diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp index 9a06d13be747..dbee451b01e1 100644 --- a/media/libmediaplayerservice/StagefrightPlayer.cpp +++ b/media/libmediaplayerservice/StagefrightPlayer.cpp @@ -40,11 +40,13 @@ status_t StagefrightPlayer::setDataSource(const char *url) {      return err;  } +// Warning: The filedescriptor passed into this method will only be valid until +// the method returns, if you want to keep it, dup it!  status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {      LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);      reset(); -    mPlayer = new MediaPlayerImpl(fd, offset, length); +    mPlayer = new MediaPlayerImpl(dup(fd), offset, length);      status_t err = mPlayer->initCheck();      if (err != OK) { diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml index 690822013a68..246f9fc528ab 100644 --- a/media/tests/MediaFrameworkTest/AndroidManifest.xml +++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml @@ -19,6 +19,7 @@      <uses-permission android:name="android.permission.RECORD_AUDIO" />      <uses-permission android:name="android.permission.CAMERA" /> +    <uses-permission android:name="android.permission.INTERNET" />      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />      <application>              <uses-library android:name="android.test.runner" /> diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java index 5e830a818932..9fb49b12a5bb 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java @@ -42,6 +42,7 @@ import com.android.mediaframeworktest.MediaNames;  import java.io.File;  import java.io.FileDescriptor; +import java.net.InetAddress;  public class MediaFrameworkTest extends Activity { @@ -142,4 +143,9 @@ public class MediaFrameworkTest extends Activity {        return super.onKeyDown(keyCode, event);    }   + +  public static boolean checkStreamingServer() throws Exception { +      InetAddress address = InetAddress.getByAddress(MediaNames.STREAM_SERVER); +      return address.isReachable(10000); +  }  } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java index 3b69df8983ca..5127255d8779 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java @@ -489,6 +489,7 @@ public class MediaNames {    };    //Streaming test files +  public static final byte [] STREAM_SERVER = new byte[] {(byte)75,(byte)17,(byte)48,(byte)204};    public static final String STREAM_H264_480_360_1411k =         "http://75.17.48.204:10088/yslau/stress_media/h264_regular.mp4";    public static final String STREAM_WMV =  diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java index 5725c447f197..4e30f913c935 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java @@ -439,6 +439,7 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra      @MediumTest      public void testPrepareAsyncReset() throws Exception { +      assertTrue(MediaFrameworkTest.checkStreamingServer());        boolean isReset = CodecTest.prepareAsyncReset(MediaNames.STREAM_MP3);        assertTrue("PrepareAsync Reset", isReset);               } @@ -471,6 +472,7 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra      @LargeTest      public void testStreamPrepareAsyncCallback() throws Exception { +        assertTrue(MediaFrameworkTest.checkStreamingServer());          boolean onPrepareSuccess =               CodecTest.prepareAsyncCallback(MediaNames.STREAM_H264_480_360_1411k, false);          assertTrue("StreamH264PrepareAsyncCallback", onPrepareSuccess); @@ -478,6 +480,7 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra      @LargeTest      public void testStreamPrepareAsyncCallbackReset() throws Exception { +        assertTrue(MediaFrameworkTest.checkStreamingServer());          boolean onPrepareSuccess =               CodecTest.prepareAsyncCallback(MediaNames.STREAM_H264_480_360_1411k, true);          assertTrue("StreamH264PrepareAsyncCallback", onPrepareSuccess); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java index fdb43da675f7..b476e01a5408 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java @@ -64,6 +64,7 @@ public class MediaPlayerStressTest extends ActivityInstrumentationTestCase2<Medi          mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();          try { +            assertTrue(MediaFrameworkTest.checkStreamingServer());              for (int i = 0; i < NUMBER_OF_STRESS_LOOPS; i++) {                  MediaPlayer mp = new MediaPlayer();                  mp.setDataSource(MediaNames.STREAM_H264_480_360_1411k); diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java index 65663b1a5bb9..05b498c1357f 100644 --- a/obex/javax/obex/ClientOperation.java +++ b/obex/javax/obex/ClientOperation.java @@ -269,7 +269,7 @@ public final class ClientOperation implements Operation, BaseStream {          if (mPrivateOutput == null) {              // there are 3 bytes operation headers and 3 bytes body headers // -            mPrivateOutput = new PrivateOutputStream(this, mMaxPacketSize - 6); +            mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());          }          mPrivateOutputOpen = true; @@ -278,7 +278,13 @@ public final class ClientOperation implements Operation, BaseStream {      }      public int getMaxPacketSize() { -        return mMaxPacketSize - 6; +        return mMaxPacketSize - 6 - getHeaderLength(); +    } + +    public int getHeaderLength() { +        // OPP may need it +        byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false); +        return headerArray.length;      }      /** diff --git a/obex/javax/obex/Operation.java b/obex/javax/obex/Operation.java index 20653f26df2d..25656ed5d6ab 100644 --- a/obex/javax/obex/Operation.java +++ b/obex/javax/obex/Operation.java @@ -163,6 +163,8 @@ public interface Operation {      long getLength(); +    int getHeaderLength(); +      String getType();      InputStream openInputStream() throws IOException; diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java index 504fe350327d..07a3a53482ea 100644 --- a/obex/javax/obex/ServerOperation.java +++ b/obex/javax/obex/ServerOperation.java @@ -124,21 +124,30 @@ public final class ServerOperation implements Operation, BaseStream {               * It is a PUT request.               */              mGetOperation = false; -        } else { + +            /* +             * Determine if the final bit is set +             */ +            if ((request & 0x80) == 0) { +                finalBitSet = false; +            } else { +                finalBitSet = true; +                mRequestFinished = true; +            } +        } else if ((request == 0x03) || (request == 0x83)) {              /*               * It is a GET request.               */              mGetOperation = true; -        } -        /* -         * Determine if the final bit is set -         */ -        if ((request & 0x80) == 0) { +            // For Get request, final bit set is decided by server side logic              finalBitSet = false; + +            if (request == 0x83) { +                mRequestFinished = true; +            }          } else { -            finalBitSet = true; -            mRequestFinished = true; +            throw new IOException("ServerOperation can not handle such request");          }          int length = in.read(); @@ -216,12 +225,9 @@ public final class ServerOperation implements Operation, BaseStream {          }          // wait for get request finished !!!! -        while (mGetOperation && !finalBitSet) { +        while (mGetOperation && !mRequestFinished) {              sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);          } -        if (finalBitSet && mGetOperation) { -            mRequestFinished = true; -        }      }      public boolean isValidBody() { @@ -333,6 +339,12 @@ public final class ServerOperation implements Operation, BaseStream {              out.write(headerArray);          } +        // For Get operation: if response code is OBEX_HTTP_OK, then this is the +        // last packet; so set finalBitSet to true. +        if (mGetOperation && type == ResponseCodes.OBEX_HTTP_OK) { +            finalBitSet = true; +        } +          if ((finalBitSet) || (headerArray.length < (mMaxPacketLength - 20))) {              if (bodyLength > 0) {                  /* @@ -410,9 +422,10 @@ public final class ServerOperation implements Operation, BaseStream {                  }              } else { -                if ((headerID == ObexHelper.OBEX_OPCODE_PUT_FINAL) -                        || (headerID == ObexHelper.OBEX_OPCODE_GET_FINAL)) { +                if ((headerID == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {                      finalBitSet = true; +                } else if (headerID == ObexHelper.OBEX_OPCODE_GET_FINAL) { +                    mRequestFinished = true;                  }                  /* @@ -584,7 +597,20 @@ public final class ServerOperation implements Operation, BaseStream {      }      public int getMaxPacketSize() { -        return mMaxPacketLength - 6; +        return mMaxPacketLength - 6 - getHeaderLength(); +    } + +    public int getHeaderLength() { +        long id = mListener.getConnectionId(); +        if (id == -1) { +            replyHeader.mConnectionID = null; +        } else { +            replyHeader.mConnectionID = ObexHelper.convertToByteArray(id); +        } + +        byte[] headerArray = ObexHelper.createHeader(replyHeader, false); + +        return headerArray.length;      }      /** @@ -623,7 +649,7 @@ public final class ServerOperation implements Operation, BaseStream {          }          if (mPrivateOutput == null) { -            mPrivateOutput = new PrivateOutputStream(this, mMaxPacketLength - 6); +            mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());          }          mPrivateOutputOpen = true;          return mPrivateOutput; diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java index 03301639cd29..9ca57bae7a1f 100644 --- a/opengl/java/android/opengl/GLSurfaceView.java +++ b/opengl/java/android/opengl/GLSurfaceView.java @@ -18,7 +18,6 @@ package android.opengl;  import java.io.Writer;  import java.util.ArrayList; -import java.util.concurrent.Semaphore;  import javax.microedition.khronos.egl.EGL10;  import javax.microedition.khronos.egl.EGL11; @@ -943,6 +942,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback       * to a Renderer instance to do the actual drawing. Can be configured to       * render continuously or on request.       * +     * All potentially blocking synchronization is done through the +     * sGLThreadManager object. This avoids multiple-lock ordering issues. +     *       */      class GLThread extends Thread {          GLThread(Renderer renderer) { @@ -962,51 +964,31 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback                  Log.i("GLThread", "starting tid=" + getId());              } -            /* -             * When the android framework launches a second instance of -             * an activity, the new instance's onCreate() method may be -             * called before the first instance returns from onDestroy(). -             * -             * This semaphore ensures that only one instance at a time -             * accesses EGL. -             */              try {                  guardedRun();              } catch (InterruptedException e) {                  // fall thru and exit normally              } finally { -                synchronized(this) { -                    if (LOG_THREADS) { -                        Log.i("GLThread", "exiting tid=" +  getId()); -                    } -                    mDone = true; -                    notifyAll(); -                } +                sGLThreadManager.threadExiting(this);              }          } -        private void startEgl() throws InterruptedException { -            if (! mHaveEgl) { -                mHaveEgl = true; -                sGLThreadManager.start(this); -                mEglHelper.start(); -            } -        } - -        private void stopEgl() { +        /* +         * This private method should only be called inside a +         * synchronized(sGLThreadManager) block. +         */ +        private void stopEglLocked() {              if (mHaveEgl) {                  mHaveEgl = false;                  mEglHelper.destroySurface();                  mEglHelper.finish(); -                sGLThreadManager.end(this); +                sGLThreadManager.releaseEglSurface(this);              }          }          private void guardedRun() throws InterruptedException {              mEglHelper = new EglHelper();              try { -                startEgl(); -                  GL10 gl = null;                  boolean tellRendererSurfaceCreated = true;                  boolean tellRendererSurfaceChanged = true; @@ -1015,63 +997,97 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback                   * This is our main activity thread's loop, we go until                   * asked to quit.                   */ -                while (!mDone) { - +                while (!isDone()) {                      /*                       *  Update the asynchronous state (window size)                       */ -                    int w, h; -                    boolean changed; +                    int w = 0; +                    int h = 0; +                    boolean changed = false;                      boolean needStart = false; -                    synchronized (this) { -                        Runnable r; -                        while ((r = getEvent()) != null) { -                            r.run(); -                        } -                        if (mPaused) { -                            stopEgl(); -                            needStart = true; -                        } -                        while(true) { +                    boolean eventsWaiting = false; + +                    synchronized (sGLThreadManager) { +                        while (true) { +                            // Manage acquiring and releasing the SurfaceView +                            // surface and the EGL surface. +                            if (mPaused) { +                                stopEglLocked(); +                            }                              if (!mHasSurface) {                                  if (!mWaitingForSurface) { -                                    stopEgl(); +                                    stopEglLocked();                                      mWaitingForSurface = true; -                                    notifyAll(); +                                    sGLThreadManager.notifyAll();                                  }                              } else { -                                boolean shouldHaveEgl = sGLThreadManager.shouldHaveEgl(this); -                                if (mHaveEgl && (!shouldHaveEgl)) { -                                    stopEgl(); -                                } else if ((!mHaveEgl) && shouldHaveEgl) { -                                    startEgl(); -                                    needStart = true; +                                if (!mHaveEgl) { +                                    if (sGLThreadManager.tryAcquireEglSurface(this)) { +                                        mHaveEgl = true; +                                        mEglHelper.start(); +                                        mRequestRender = true; +                                        needStart = true; +                                    }                                  }                              } -                            if (!needToWait()) { + +                            // Check if we need to wait. If not, update any state +                            // that needs to be updated, copy any state that +                            // needs to be copied, and use "break" to exit the +                            // wait loop. + +                            if (mDone) { +                                return; +                            } + +                            if (mEventsWaiting) { +                                eventsWaiting = true; +                                mEventsWaiting = false; +                                break; +                            } + +                            if ( (! mPaused) && mHasSurface && mHaveEgl +                                    && (mWidth > 0) && (mHeight > 0) +                                    && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY)) +                                    ) { +                                changed = mSizeChanged; +                                w = mWidth; +                                h = mHeight; +                                mSizeChanged = false; +                                mRequestRender = false; +                                if (mHasSurface && mWaitingForSurface) { +                                    changed = true; +                                    mWaitingForSurface = false; +                                    sGLThreadManager.notifyAll(); +                                }                                  break;                              } + +                            // By design, this is the only place where we wait(). +                              if (LOG_THREADS) { -                                Log.i("GLThread", "needToWait tid=" + getId()); +                                Log.i("GLThread", "waiting tid=" + getId());                              } -                            wait(); -                        } -                        if (mDone) { -                            break; +                            sGLThreadManager.wait();                          } -                        changed = mSizeChanged; -                        w = mWidth; -                        h = mHeight; -                        mSizeChanged = false; -                        mRequestRender = false; -                        if (mHasSurface && mWaitingForSurface) { -                            changed = true; -                            mWaitingForSurface = false; -                            notifyAll(); +                    } // end of synchronized(sGLThreadManager) + +                    /* +                     * Handle queued events +                     */ +                    if (eventsWaiting) { +                        Runnable r; +                        while ((r = getEvent()) != null) { +                            r.run(); +                            if (isDone()) { +                                return; +                            }                          } +                        // Go back and see if we need to wait to render. +                        continue;                      } +                      if (needStart) { -                        startEgl();                          tellRendererSurfaceCreated = true;                          changed = true;                      } @@ -1102,71 +1118,63 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback                  /*                   * clean-up everything...                   */ -                stopEgl(); +                synchronized (sGLThreadManager) { +                    stopEglLocked(); +                }              }          } -        private boolean needToWait() { -            if (mDone) { -                return false; +        private boolean isDone() { +            synchronized (sGLThreadManager) { +                return mDone;              } - -            if (mPaused || (! mHasSurface) || (! mHaveEgl)) { -                return true; -            } - -            if ((mWidth > 0) && (mHeight > 0) && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) { -                return false; -            } - -            return true;          }          public void setRenderMode(int renderMode) {              if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {                  throw new IllegalArgumentException("renderMode");              } -            synchronized(this) { +            synchronized(sGLThreadManager) {                  mRenderMode = renderMode;                  if (renderMode == RENDERMODE_CONTINUOUSLY) { -                    notifyAll(); +                    sGLThreadManager.notifyAll();                  }              }          }          public int getRenderMode() { -            synchronized(this) { +            synchronized(sGLThreadManager) {                  return mRenderMode;              }          }          public void requestRender() { -            synchronized(this) { +            synchronized(sGLThreadManager) {                  mRequestRender = true; -                notifyAll(); +                sGLThreadManager.notifyAll();              }          }          public void surfaceCreated() { -            synchronized(this) { +            synchronized(sGLThreadManager) {                  if (LOG_THREADS) {                      Log.i("GLThread", "surfaceCreated tid=" + getId());                  }                  mHasSurface = true; -                notifyAll(); +                sGLThreadManager.notifyAll();              }          }          public void surfaceDestroyed() { -            synchronized(this) { +            synchronized(sGLThreadManager) {                  if (LOG_THREADS) {                      Log.i("GLThread", "surfaceDestroyed tid=" + getId());                  }                  mHasSurface = false; -                notifyAll(); +                sGLThreadManager.notifyAll();                  while(!mWaitingForSurface && isAlive() && ! mDone) {                      try { -                        wait(); +                        sGLThreadManager.wait();                      } catch (InterruptedException e) {                          Thread.currentThread().interrupt();                      } @@ -1175,35 +1183,35 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback          }          public void onPause() { -            synchronized (this) { +            synchronized (sGLThreadManager) {                  mPaused = true; -                notifyAll(); +                sGLThreadManager.notifyAll();              }          }          public void onResume() { -            synchronized (this) { +            synchronized (sGLThreadManager) {                  mPaused = false;                  mRequestRender = true; -                notifyAll(); +                sGLThreadManager.notifyAll();              }          }          public void onWindowResize(int w, int h) { -            synchronized (this) { +            synchronized (sGLThreadManager) {                  mWidth = w;                  mHeight = h;                  mSizeChanged = true; -                notifyAll(); +                sGLThreadManager.notifyAll();              }          }          public void requestExitAndWait() {              // don't call this from GLThread thread or it is a guaranteed              // deadlock! -            synchronized(this) { +            synchronized(sGLThreadManager) {                  mDone = true; -                notifyAll(); +                sGLThreadManager.notifyAll();              }              try {                  join(); @@ -1219,6 +1227,10 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback          public void queueEvent(Runnable r) {              synchronized(this) {                  mEventQueue.add(r); +                synchronized(sGLThreadManager) { +                    mEventsWaiting = true; +                    sGLThreadManager.notifyAll(); +                }              }          } @@ -1232,6 +1244,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback              return null;          } +        // Once the thread is started, all accesses to the following member +        // variables are protected by the sGLThreadManager monitor          private boolean mDone;          private boolean mPaused;          private boolean mHasSurface; @@ -1241,6 +1255,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback          private int mHeight;          private int mRenderMode;          private boolean mRequestRender; +        private boolean mEventsWaiting; +        // End of member variables protected by the sGLThreadManager monitor. +          private Renderer mRenderer;          private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();          private EglHelper mEglHelper; @@ -1286,37 +1303,43 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback          }      } -    static class GLThreadManager { -        public boolean shouldHaveEgl(GLThread thread) { -            synchronized(this) { -                return thread == mMostRecentGLThread || mMostRecentGLThread == null; +    private static class GLThreadManager { + +        public synchronized void threadExiting(GLThread thread) { +            if (LOG_THREADS) { +                Log.i("GLThread", "exiting tid=" +  thread.getId());              } -        } -        public void start(GLThread thread) throws InterruptedException { -            GLThread oldThread = null; -            synchronized(this) { -                oldThread = mMostRecentGLThread; -                mMostRecentGLThread = thread; +            thread.mDone = true; +            if (mEglOwner == thread) { +                mEglOwner = null;              } -            if (oldThread != null) { -                synchronized(oldThread) { -                    oldThread.notifyAll(); -                } +            notifyAll(); +        } + +        /* +         * Tries once to acquire the right to use an EGL +         * surface. Does not block. +         * @return true if the right to use an EGL surface was acquired. +         */ +        public synchronized boolean tryAcquireEglSurface(GLThread thread) { +            if (mEglOwner == thread || mEglOwner == null) { +                mEglOwner = thread; +                notifyAll(); +                return true;              } -            sEglSemaphore.acquire(); +            return false;          } -        public void end(GLThread thread) { -            sEglSemaphore.release(); -            synchronized(this) { -                if (mMostRecentGLThread == thread) { -                    mMostRecentGLThread = null; -                } + +        public synchronized void releaseEglSurface(GLThread thread) { +            if (mEglOwner == thread) { +                mEglOwner = null;              } +            notifyAll();          } -        private GLThread mMostRecentGLThread; + +        private GLThread mEglOwner;      } -    private static final Semaphore sEglSemaphore = new Semaphore(1);      private static final GLThreadManager sGLThreadManager = new GLThreadManager();      private boolean mSizeChanged = true; diff --git a/services/java/com/android/server/HardwareService.java b/services/java/com/android/server/HardwareService.java index 3e3cf065bedb..88074c2c59ea 100755 --- a/services/java/com/android/server/HardwareService.java +++ b/services/java/com/android/server/HardwareService.java @@ -54,6 +54,16 @@ public class HardwareService extends IHardwareService.Stub {      static final int LIGHT_FLASH_TIMED = 1;      static final int LIGHT_FLASH_HARDWARE = 2; +    /** +     * Light brightness is managed by a user setting. +     */ +    static final int BRIGHTNESS_MODE_USER = 0; + +    /** +     * Light brightness is managed by a light sensor. +     */ +    static final int BRIGHTNESS_MODE_SENSOR = 1; +      private final LinkedList<Vibration> mVibrations;      private Vibration mCurrentVibration; @@ -266,21 +276,21 @@ public class HardwareService extends IHardwareService.Stub {      }      void setLightOff_UNCHECKED(int light) { -        setLight_native(mNativePointer, light, 0, LIGHT_FLASH_NONE, 0, 0); +        setLight_native(mNativePointer, light, 0, LIGHT_FLASH_NONE, 0, 0, 0);      } -    void setLightBrightness_UNCHECKED(int light, int brightness) { +    void setLightBrightness_UNCHECKED(int light, int brightness, int brightnessMode) {          int b = brightness & 0x000000ff;          b = 0xff000000 | (b << 16) | (b << 8) | b; -        setLight_native(mNativePointer, light, b, LIGHT_FLASH_NONE, 0, 0); +        setLight_native(mNativePointer, light, b, LIGHT_FLASH_NONE, 0, 0, brightnessMode);      }      void setLightColor_UNCHECKED(int light, int color) { -        setLight_native(mNativePointer, light, color, LIGHT_FLASH_NONE, 0, 0); +        setLight_native(mNativePointer, light, color, LIGHT_FLASH_NONE, 0, 0, 0);      }      void setLightFlashing_UNCHECKED(int light, int color, int mode, int onMS, int offMS) { -        setLight_native(mNativePointer, light, color, mode, onMS, offMS); +        setLight_native(mNativePointer, light, color, mode, onMS, offMS, 0);      }      public void setAttentionLight(boolean on, int color) { @@ -289,7 +299,7 @@ public class HardwareService extends IHardwareService.Stub {              mAttentionLightOn = on;              mPulsing = false;              setLight_native(mNativePointer, LIGHT_ID_ATTENTION, color, -                    LIGHT_FLASH_HARDWARE, on ? 3 : 0, 0); +                    LIGHT_FLASH_HARDWARE, on ? 3 : 0, 0, 0);          }      } @@ -304,7 +314,7 @@ public class HardwareService extends IHardwareService.Stub {              if (!mAttentionLightOn && !mPulsing) {                  mPulsing = true;                  setLight_native(mNativePointer, LIGHT_ID_ATTENTION, 0x00ffffff, -                        LIGHT_FLASH_HARDWARE, 7, 0); +                        LIGHT_FLASH_HARDWARE, 7, 0, 0);                  mH.sendMessageDelayed(Message.obtain(mH, 1), 3000);              }          } @@ -321,7 +331,7 @@ public class HardwareService extends IHardwareService.Stub {                      mPulsing = false;                      setLight_native(mNativePointer, LIGHT_ID_ATTENTION,                              mAttentionLightOn ? 0xffffffff : 0, -                            LIGHT_FLASH_NONE, 0, 0); +                            LIGHT_FLASH_NONE, 0, 0, 0);                  }              }          } @@ -484,7 +494,7 @@ public class HardwareService extends IHardwareService.Stub {      private static native void finalize_native(int ptr);      private static native void setLight_native(int ptr, int light, int color, int mode, -            int onMS, int offMS); +            int onMS, int offMS, int brightnessMode);      private final Context mContext;      private final PowerManager.WakeLock mWakeLock; diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index fd518c3c87f6..e1425d444c34 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -158,12 +158,13 @@ class PowerManagerService extends IPowerManager.Stub      private int[] mBroadcastQueue = new int[] { -1, -1, -1 };      private int[] mBroadcastWhy = new int[3];      private int mPartialCount = 0; -    private int mProximityCount = 0;      private int mPowerState;      private boolean mOffBecauseOfUser;      private int mUserState;      private boolean mKeyboardVisible = false;      private boolean mUserActivityAllowed = true; +    private int mProximityWakeLockCount = 0; +    private boolean mProximitySensorEnabled = false;      private boolean mProximitySensorActive = false;      private int mProximityPendingValue = -1; // -1 == nothing, 0 == inactive, 1 == active      private long mLastProximityEventTime; @@ -235,6 +236,7 @@ class PowerManagerService extends IPowerManager.Stub      // could be either static or controllable at runtime      private static final boolean mSpew = false; +    private static final boolean mDebugProximitySensor = (true || mSpew);      private static final boolean mDebugLightSensor = (false || mSpew);      /* @@ -657,8 +659,8 @@ class PowerManagerService extends IPowerManager.Stub              }              Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME);          } else if ((flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) { -            mProximityCount++; -            if (mProximityCount == 1) { +            mProximityWakeLockCount++; +            if (mProximityWakeLockCount == 1) {                  enableProximityLockLocked();              }          } @@ -718,9 +720,16 @@ class PowerManagerService extends IPowerManager.Stub                  Power.releaseWakeLock(PARTIAL_NAME);              }          } else if ((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) { -            mProximityCount--; -            if (mProximityCount == 0) { -                disableProximityLockLocked(); +            mProximityWakeLockCount--; +            if (mProximityWakeLockCount == 0) { +                if (mProximitySensorActive) { +                    // wait for proximity sensor to go negative before disabling sensor +                    if (mDebugProximitySensor) { +                        Log.d(TAG, "waiting for proximity sensor to go negative"); +                    } +                } else { +                    disableProximityLockLocked(); +                }              }          }          // Unlink the lock from the binder. @@ -898,6 +907,8 @@ class PowerManagerService extends IPowerManager.Stub          pw.println("  mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock);          pw.println("  mStayOnWhilePluggedInPartialLock=" + mStayOnWhilePluggedInPartialLock);          pw.println("  mPreventScreenOnPartialLock=" + mPreventScreenOnPartialLock); +        pw.println("  mProximityWakeLockCount=" + mProximityWakeLockCount); +        pw.println("  mProximitySensorEnabled=" + mProximitySensorEnabled);          pw.println("  mProximitySensorActive=" + mProximitySensorActive);          pw.println("  mProximityPendingValue=" + mProximityPendingValue);          pw.println("  mLastProximityEventTime=" + mLastProximityEventTime); @@ -1313,8 +1324,13 @@ class PowerManagerService extends IPowerManager.Stub                  enableLightSensor(on);                  if (!on) {                      // make sure button and key backlights are off too -                    mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, 0); -                    mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, 0); +                    int brightnessMode = (mUseSoftwareAutoBrightness +                            ? HardwareService.BRIGHTNESS_MODE_SENSOR +                            : HardwareService.BRIGHTNESS_MODE_USER); +                    mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, 0, +                        brightnessMode); +                    mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, 0, +                        brightnessMode);                      // clear current value so we will update based on the new conditions                      // when the sensor is reenabled.                      mLightSensorValue = -1; @@ -1364,7 +1380,7 @@ class PowerManagerService extends IPowerManager.Stub              boolean oldScreenOn = (mPowerState & SCREEN_ON_BIT) != 0;              boolean newScreenOn = (newState & SCREEN_ON_BIT) != 0; -            if (mSpew) { +            if (mPowerState != newState) {                  Log.d(TAG, "setPowerState: mPowerState=" + mPowerState                          + " newState=" + newState + " noChangeLights=" + noChangeLights);                  Log.d(TAG, "  oldKeyboardBright=" + ((mPowerState & KEYBOARD_BRIGHT_BIT) != 0) @@ -1669,14 +1685,23 @@ class PowerManagerService extends IPowerManager.Stub      }      private void setLightBrightness(int mask, int value) { +        int brightnessMode = (mAutoBrightessEnabled +                            ? HardwareService.BRIGHTNESS_MODE_SENSOR +                            : HardwareService.BRIGHTNESS_MODE_USER);          if ((mask & SCREEN_BRIGHT_BIT) != 0) { -            mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, value); +            mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, value, +                brightnessMode);          } +        brightnessMode = (mUseSoftwareAutoBrightness +                            ? HardwareService.BRIGHTNESS_MODE_SENSOR +                            : HardwareService.BRIGHTNESS_MODE_USER);          if ((mask & BUTTON_BRIGHT_BIT) != 0) { -            mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, value); +            mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, value, +                brightnessMode);          }          if ((mask & KEYBOARD_BRIGHT_BIT) != 0) { -            mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, value); +            mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, value, +                brightnessMode);          }      } @@ -1975,8 +2000,11 @@ class PowerManagerService extends IPowerManager.Stub                              startAnimation = true;                          }                      } else { +                        int brightnessMode = (mAutoBrightessEnabled +                                            ? HardwareService.BRIGHTNESS_MODE_SENSOR +                                            : HardwareService.BRIGHTNESS_MODE_USER);                          mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, -                                lcdValue); +                                lcdValue, brightnessMode);                      }                  }                  if (ANIMATE_BUTTON_LIGHTS) { @@ -1986,8 +2014,11 @@ class PowerManagerService extends IPowerManager.Stub                          startAnimation = true;                      }                  } else { +                    int brightnessMode = (mUseSoftwareAutoBrightness +                                        ? HardwareService.BRIGHTNESS_MODE_SENSOR +                                        : HardwareService.BRIGHTNESS_MODE_USER);                      mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, -                            buttonValue); +                            buttonValue, brightnessMode);                  }                  if (ANIMATE_KEYBOARD_LIGHTS) {                      if (mKeyboardBrightness.setTargetLocked(keyboardValue, @@ -1996,8 +2027,11 @@ class PowerManagerService extends IPowerManager.Stub                          startAnimation = true;                      }                  } else { +                    int brightnessMode = (mUseSoftwareAutoBrightness +                                        ? HardwareService.BRIGHTNESS_MODE_SENSOR +                                        : HardwareService.BRIGHTNESS_MODE_USER);                      mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, -                            keyboardValue); +                            keyboardValue, brightnessMode);                  }                  if (startAnimation) {                      if (mDebugLightSensor) { @@ -2095,10 +2129,19 @@ class PowerManagerService extends IPowerManager.Stub      /**       * When the keyguard is up, it manages the power state, and userActivity doesn't do anything. +     * When disabling user activity we also reset user power state so the keyguard can reset its +     * short screen timeout when keyguard is unhidden.       */      public void enableUserActivity(boolean enabled) { +        if (mSpew) { +            Log.d(TAG, "enableUserActivity " + enabled); +        }          synchronized (mLocks) {              mUserActivityAllowed = enabled; +            if (!enabled) { +                // cancel timeout and clear mUserState so the keyguard can set a short timeout +                setTimeoutLocked(SystemClock.uptimeMillis(), 0); +            }          }      } @@ -2309,10 +2352,12 @@ class PowerManagerService extends IPowerManager.Stub          mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);          // Don't let applications turn the screen all the way off          brightness = Math.max(brightness, Power.BRIGHTNESS_DIM); -        mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, brightness); +        mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BACKLIGHT, brightness, +                HardwareService.BRIGHTNESS_MODE_USER);          mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_KEYBOARD, -            (mKeyboardVisible ? brightness : 0)); -        mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, brightness); +            (mKeyboardVisible ? brightness : 0), HardwareService.BRIGHTNESS_MODE_USER); +        mHardware.setLightBrightness_UNCHECKED(HardwareService.LIGHT_ID_BUTTONS, brightness, +            HardwareService.BRIGHTNESS_MODE_USER);          long identity = Binder.clearCallingIdentity();          try {              mBatteryStats.noteScreenBrightness(brightness); @@ -2341,43 +2386,49 @@ class PowerManagerService extends IPowerManager.Stub      }      private void enableProximityLockLocked() { -        if (mSpew) { +        if (mDebugProximitySensor) {              Log.d(TAG, "enableProximityLockLocked");          } -        // clear calling identity so sensor manager battery stats are accurate -        long identity = Binder.clearCallingIdentity(); -        try { -            mSensorManager.registerListener(mProximityListener, mProximitySensor, -                    SensorManager.SENSOR_DELAY_NORMAL); -        } finally { -            Binder.restoreCallingIdentity(identity); +        if (!mProximitySensorEnabled) { +            // clear calling identity so sensor manager battery stats are accurate +            long identity = Binder.clearCallingIdentity(); +            try { +                mSensorManager.registerListener(mProximityListener, mProximitySensor, +                        SensorManager.SENSOR_DELAY_NORMAL); +                mProximitySensorEnabled = true; +            } finally { +                Binder.restoreCallingIdentity(identity); +            }          }      }      private void disableProximityLockLocked() { -        if (mSpew) { +        if (mDebugProximitySensor) {              Log.d(TAG, "disableProximityLockLocked");          } -        // clear calling identity so sensor manager battery stats are accurate -        long identity = Binder.clearCallingIdentity(); -        try { -            mSensorManager.unregisterListener(mProximityListener); -            mHandler.removeCallbacks(mProximityTask); -        } finally { -            Binder.restoreCallingIdentity(identity); -        } -        if (mProximitySensorActive) { -            mProximitySensorActive = false; -            forceUserActivityLocked(); +        if (mProximitySensorEnabled) { +            // clear calling identity so sensor manager battery stats are accurate +            long identity = Binder.clearCallingIdentity(); +            try { +                mSensorManager.unregisterListener(mProximityListener); +                mHandler.removeCallbacks(mProximityTask); +                mProximitySensorEnabled = false; +            } finally { +                Binder.restoreCallingIdentity(identity); +            } +            if (mProximitySensorActive) { +                mProximitySensorActive = false; +                forceUserActivityLocked(); +            }          }      }      private void proximityChangedLocked(boolean active) { -        if (mSpew) { +        if (mDebugProximitySensor) {              Log.d(TAG, "proximityChangedLocked, active: " + active);          } -        if (mProximityCount <= 0) { -            Log.d(TAG, "Ignoring proximity change after last proximity lock is released"); +        if (!mProximitySensorEnabled) { +            Log.d(TAG, "Ignoring proximity change after sensor is disabled");              return;          }          if (active) { @@ -2389,6 +2440,11 @@ class PowerManagerService extends IPowerManager.Stub              // even when the keyguard is on.              mProximitySensorActive = false;              forceUserActivityLocked(); + +            if (mProximityWakeLockCount == 0) { +                // disable sensor if we have no listeners left after proximity negative +                disableProximityLockLocked(); +            }          }      } @@ -2427,6 +2483,9 @@ class PowerManagerService extends IPowerManager.Stub                  boolean active = (distance >= 0.0 && distance < PROXIMITY_THRESHOLD &&                          distance < mProximitySensor.getMaximumRange()); +                if (mDebugProximitySensor) { +                    Log.d(TAG, "mProximityListener.onSensorChanged active: " + active); +                }                  if (timeSinceLastEvent < PROXIMITY_SENSOR_DELAY) {                      // enforce delaying atleast PROXIMITY_SENSOR_DELAY before processing                      mProximityPendingValue = (active ? 1 : 0); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 69f4b89eb6b1..327cd72c8195 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -435,6 +435,7 @@ public class WindowManagerService extends IWindowManager.Stub      float mLastWallpaperY = -1;      float mLastWallpaperXStep = -1;      float mLastWallpaperYStep = -1; +    boolean mSendingPointersToWallpaper = false;      // This is set when we are waiting for a wallpaper to tell us it is done      // changing its scroll position.      WindowState mWaitingOnWallpaper; @@ -1749,8 +1750,20 @@ public class WindowManagerService extends IWindowManager.Stub                  }                  try {                      MotionEvent ev = MotionEvent.obtainNoHistory(pointer); -                    ev.offsetLocation(srcWin.mFrame.left-wallpaper.mFrame.left, -                            srcWin.mFrame.top-wallpaper.mFrame.top); +                    if (srcWin != null) { +                        ev.offsetLocation(srcWin.mFrame.left-wallpaper.mFrame.left, +                                srcWin.mFrame.top-wallpaper.mFrame.top); +                    } else { +                        ev.offsetLocation(-wallpaper.mFrame.left, -wallpaper.mFrame.top); +                    } +                    switch (pointer.getAction()) { +                        case MotionEvent.ACTION_DOWN: +                            mSendingPointersToWallpaper = true; +                            break; +                        case MotionEvent.ACTION_UP: +                            mSendingPointersToWallpaper = false; +                            break; +                    }                      wallpaper.mClient.dispatchPointer(ev, eventTime, false);                  } catch (RemoteException e) {                      Log.w(TAG, "Failure sending pointer to wallpaper", e); @@ -4836,6 +4849,12 @@ public class WindowManagerService extends IWindowManager.Stub              if (action != MotionEvent.ACTION_MOVE) {                  Log.w(TAG, "No window to dispatch pointer action " + ev.getAction());              } +            synchronized (mWindowMap) { +                if (mSendingPointersToWallpaper) { +                    Log.i(TAG, "Sending skipped pointer to wallpaper!"); +                    sendPointerToWallpaperLocked(null, ev, ev.getEventTime()); +                } +            }              if (qev != null) {                  mQueue.recycleEvent(qev);              } @@ -4843,6 +4862,12 @@ public class WindowManagerService extends IWindowManager.Stub              return INJECT_FAILED;          }          if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) { +            synchronized (mWindowMap) { +                if (mSendingPointersToWallpaper) { +                    Log.i(TAG, "Sending skipped pointer to wallpaper!"); +                    sendPointerToWallpaperLocked(null, ev, ev.getEventTime()); +                } +            }              if (qev != null) {                  mQueue.recycleEvent(qev);              } @@ -4963,6 +4988,19 @@ public class WindowManagerService extends IWindowManager.Stub          }          synchronized(mWindowMap) { +            if (!target.isVisibleLw()) { +                // During this motion dispatch, the target window has become +                // invisible. +                if (mSendingPointersToWallpaper) { +                    sendPointerToWallpaperLocked(null, ev, eventTime); +                } +                if (qev != null) { +                    mQueue.recycleEvent(qev); +                } +                ev.recycle(); +                return INJECT_SUCCEEDED; +            } +                          if (qev != null && action == MotionEvent.ACTION_MOVE) {                  mKeyWaiter.bindTargetWindowLocked(target,                          KeyWaiter.RETURN_PENDING_POINTER, qev); @@ -4987,15 +5025,16 @@ public class WindowManagerService extends IWindowManager.Stub                          mKeyWaiter.mOutsideTouchTargets = null;                      }                  } -                final Rect frame = target.mFrame; -                ev.offsetLocation(-(float)frame.left, -(float)frame.top); -                mKeyWaiter.bindTargetWindowLocked(target);                  // If we are on top of the wallpaper, then the wallpaper also                  // gets to see this movement. -                if (mWallpaperTarget == target) { -                    sendPointerToWallpaperLocked(target, ev, eventTime); +                if (mWallpaperTarget == target || mSendingPointersToWallpaper) { +                    sendPointerToWallpaperLocked(null, ev, eventTime);                  } +                 +                final Rect frame = target.mFrame; +                ev.offsetLocation(-(float)frame.left, -(float)frame.top); +                mKeyWaiter.bindTargetWindowLocked(target);              }          } @@ -5918,12 +5957,12 @@ public class WindowManagerService extends IWindowManager.Stub                          res.offsetLocation(-win.mFrame.left, -win.mFrame.top);                      }                  } -            } -             -            if (res != null && returnWhat == RETURN_PENDING_POINTER) { -                synchronized (mWindowMap) { -                    if (mWallpaperTarget == win) { -                        sendPointerToWallpaperLocked(win, res, res.getEventTime()); +                 +                if (res != null && returnWhat == RETURN_PENDING_POINTER) { +                    synchronized (mWindowMap) { +                        if (mWallpaperTarget == win || mSendingPointersToWallpaper) { +                            sendPointerToWallpaperLocked(win, res, res.getEventTime()); +                        }                      }                  }              } diff --git a/services/jni/com_android_server_HardwareService.cpp b/services/jni/com_android_server_HardwareService.cpp index 22d4bd8766e8..253e6558de36 100644 --- a/services/jni/com_android_server_HardwareService.cpp +++ b/services/jni/com_android_server_HardwareService.cpp @@ -101,7 +101,7 @@ static void finalize_native(JNIEnv *env, jobject clazz, int ptr)  }  static void setLight_native(JNIEnv *env, jobject clazz, int ptr, -        int light, int colorARGB, int flashMode, int onMS, int offMS) +        int light, int colorARGB, int flashMode, int onMS, int offMS, int brightnessMode)  {      Devices* devices = (Devices*)ptr;      light_state_t state; @@ -115,6 +115,7 @@ static void setLight_native(JNIEnv *env, jobject clazz, int ptr,      state.flashMode = flashMode;      state.flashOnMS = onMS;      state.flashOffMS = offMS; +    state.brightnessMode = brightnessMode;      devices->lights[light]->set_light(devices->lights[light], &state);  } @@ -134,7 +135,7 @@ static void vibratorOff(JNIEnv *env, jobject clazz)  static JNINativeMethod method_table[] = {      { "init_native", "()I", (void*)init_native },      { "finalize_native", "(I)V", (void*)finalize_native }, -    { "setLight_native", "(IIIIII)V", (void*)setLight_native }, +    { "setLight_native", "(IIIIIII)V", (void*)setLight_native },      { "vibratorOn", "(J)V", (void*)vibratorOn },      { "vibratorOff", "()V", (void*)vibratorOff }  };  |