diff options
Diffstat (limited to 'core/java')
39 files changed, 835 insertions, 160 deletions
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java index f383af9..7f0ea99 100644 --- a/core/java/android/animation/LayoutTransition.java +++ b/core/java/android/animation/LayoutTransition.java @@ -657,6 +657,15 @@ public class LayoutTransition { */ private void setupChangeAnimation(final ViewGroup parent, final int changeReason, Animator baseAnimator, final long duration, final View child) { + + // If we already have a listener for this child, then we've already set up the + // changing animation we need. Multiple calls for a child may occur when several + // add/remove operations are run at once on a container; each one will trigger + // changes for the existing children in the container. + if (layoutChangeListenerMap.get(child) != null) { + return; + } + // Make a copy of the appropriate animation final Animator anim = baseAnimator.clone(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index a4714ca..f9896f7 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -45,6 +45,7 @@ import android.graphics.Canvas; import android.net.IConnectivityManager; import android.net.Proxy; import android.net.ProxyProperties; +import android.opengl.GLUtils; import android.os.AsyncTask; import android.os.Bundle; import android.os.Debug; @@ -3714,6 +3715,24 @@ public final class ActivityThread { } } + private void setupGraphicsSupport(LoadedApk info) { + try { + int uid = Process.myUid(); + String[] packages = getPackageManager().getPackagesForUid(uid); + + // If there are several packages in this application we won't + // initialize the graphics disk caches + if (packages.length == 1) { + ContextImpl appContext = new ContextImpl(); + appContext.init(info, null, this); + + HardwareRenderer.setupDiskCache(appContext.getCacheDir()); + } + } catch (RemoteException e) { + // Ignore + } + } + private void handleBindApplication(AppBindData data) { mBoundApplication = data; mConfiguration = new Configuration(data.config); @@ -3737,7 +3756,7 @@ public final class ActivityThread { HardwareRenderer.disable(false); } } - + if (mProfiler.profileFd != null) { mProfiler.startProfiling(); } @@ -3773,6 +3792,8 @@ public final class ActivityThread { data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo); + setupGraphicsSupport(data.info); + /** * For system applications on userdebug/eng builds, log stack * traces of disk and network access to dropbox for analysis. diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 522f477..0c6baeb 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -901,6 +901,7 @@ public final class LoadedApk { private RuntimeException mUnbindLocation; private boolean mDied; + private boolean mForgotten; private static class ConnectionInfo { IBinder binder; @@ -959,6 +960,7 @@ public final class LoadedApk { ci.binder.unlinkToDeath(ci.deathMonitor, 0); } mActiveConnections.clear(); + mForgotten = true; } } @@ -1020,6 +1022,11 @@ public final class LoadedApk { ServiceDispatcher.ConnectionInfo info; synchronized (this) { + if (mForgotten) { + // We unbound before receiving the connection; ignore + // any connection received. + return; + } old = mActiveConnections.get(name); if (old != null && old.binder == service) { // Huh, already have this one. Oh well! diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index f81ea81..b1c1f30 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -212,7 +212,11 @@ public class WallpaperManager { */ mHandler.sendEmptyMessage(MSG_CLEAR_WALLPAPER); } - + + public Handler getHandler() { + return mHandler; + } + public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) { synchronized (this) { if (mWallpaper != null) { @@ -604,7 +608,7 @@ public class WallpaperManager { // Ignore } } - + /** * Set the position of the current wallpaper within any larger space, when * that wallpaper is visible behind the given window. The X and Y offsets @@ -619,16 +623,26 @@ public class WallpaperManager { * @param yOffset The offset along the Y dimension, from 0 to 1. */ public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) { - try { - //Log.v(TAG, "Sending new wallpaper offsets from app..."); - ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition( - windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep); - //Log.v(TAG, "...app returning after sending offsets!"); - } catch (RemoteException e) { - // Ignore. - } + final IBinder fWindowToken = windowToken; + final float fXOffset = xOffset; + final float fYOffset = yOffset; + sGlobals.getHandler().post(new Runnable() { + public void run() { + try { + //Log.v(TAG, "Sending new wallpaper offsets from app..."); + ViewRootImpl.getWindowSession(mContext.getMainLooper()).setWallpaperPosition( + fWindowToken, fXOffset, fYOffset, mWallpaperXStep, mWallpaperYStep); + //Log.v(TAG, "...app returning after sending offsets!"); + } catch (RemoteException e) { + // Ignore. + } catch (IllegalArgumentException e) { + // Since this is being posted, it's possible that this windowToken is no longer + // valid, for example, if setWallpaperOffsets is called just before rotation. + } + } + }); } - + /** * For applications that use multiple virtual screens showing a wallpaper, * specify the step size between virtual screens. For example, if the diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java index 7addd4a..b1d0070 100644 --- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -86,7 +86,7 @@ public final class BluetoothDeviceProfileState extends StateMachine { private static final int CONNECTION_ACCESS_REQUEST_REPLY = 104; private static final int CONNECTION_ACCESS_REQUEST_EXPIRY = 105; - private static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs + public static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs private static final int CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT = 7000; // 7 secs private static final int CONNECTION_ACCESS_UNDEFINED = -1; private static final long INIT_INCOMING_REJECT_TIMER = 1000; // 1 sec diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index fefeb93..deea2b8 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -90,7 +90,7 @@ interface IBluetooth boolean connectHeadset(String address); boolean disconnectHeadset(String address); - boolean notifyIncomingConnection(String address); + boolean notifyIncomingConnection(String address, boolean rejected); // HID profile APIs boolean connectInputDevice(in BluetoothDevice device); diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index ad1bfb2..bea6529 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -484,7 +484,7 @@ final class XmlBlock { private final AssetManager mAssets; private final int mNative; - private final StringBlock mStrings; + /*package*/ final StringBlock mStrings; private boolean mOpen = true; private int mOpenCount = 1; @@ -494,9 +494,9 @@ final class XmlBlock { private static final native int nativeGetStringBlock(int obj); private static final native int nativeCreateParseState(int obj); - private static final native int nativeNext(int state); + /*package*/ static final native int nativeNext(int state); private static final native int nativeGetNamespace(int state); - private static final native int nativeGetName(int state); + /*package*/ static final native int nativeGetName(int state); private static final native int nativeGetText(int state); private static final native int nativeGetLineNumber(int state); private static final native int nativeGetAttributeCount(int state); diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 48adfad..3becec0 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -22,9 +22,12 @@ import android.graphics.ImageFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.media.AudioManager; +import android.media.MediaPlayer; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.SystemProperties; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; @@ -154,6 +157,7 @@ public class Camera { private boolean mOneShot; private boolean mWithBuffer; private boolean mFaceDetectionRunning = false; + private boolean mReleased = false; /** * Broadcast Action: A new picture is taken by the camera, and the entry of @@ -303,7 +307,7 @@ public class Camera { } protected void finalize() { - native_release(); + release(); } private native final void native_setup(Object camera_this, int cameraId); @@ -318,6 +322,15 @@ public class Camera { public final void release() { native_release(); mFaceDetectionRunning = false; + if (mCameraSoundPlayers != null) { + for (CameraSoundPlayer csp: mCameraSoundPlayers) { + if (csp != null) { + csp.release(); + } + } + mCameraSoundPlayers = null; + } + mReleased = true; } /** @@ -2354,7 +2367,7 @@ public class Camera { * * <p>The reference code is as follows. * - * <pre> + * <pre> * public void onOrientationChanged(int orientation) { * if (orientation == ORIENTATION_UNKNOWN) return; * android.hardware.Camera.CameraInfo info = @@ -2369,7 +2382,7 @@ public class Camera { * } * mParameters.setRotation(rotation); * } - * </pre> + * </pre> * * @param rotation The rotation angle in degrees relative to the * orientation of the camera. Rotation can only be 0, @@ -3452,4 +3465,194 @@ public class Camera { return result; } }; + + /** + * <p>The set of default system sounds for camera actions. Use this with + * {@link #playSound} to play an appropriate sound when implementing a + * custom still or video recording mechanism through the preview + * callbacks.</p> + * + * <p>There is no need to play sounds when using {@link #takePicture} or + * {@link android.media.MediaRecorder} for still images or video, + * respectively, as these play their own sounds when needed.</p> + * + * @see #playSound + * @hide + */ + public static class Sound { + /** + * The sound used by {@link android.hardware.Camera#takePicture} to + * indicate still image capture. + */ + public static final int SHUTTER_CLICK = 0; + + /** + * A sound to indicate that focusing has completed. Because deciding + * when this occurs is application-dependent, this sound is not used by + * any methods in the Camera class. + */ + public static final int FOCUS_COMPLETE = 1; + + /** + * The sound used by {@link android.media.MediaRecorder#start} to + * indicate the start of video recording. + */ + public static final int START_VIDEO_RECORDING = 2; + + /** + * The sound used by {@link android.media.MediaRecorder#stop} to + * indicate the end of video recording. + */ + public static final int STOP_VIDEO_RECORDING = 3; + + private static final int NUM_SOUNDS = 4; + }; + + /** + * <p>Play one of the predefined platform sounds for camera actions.</p> + * + * <p>Use this method to play a platform-specific sound for various camera + * actions. The sound playing is done asynchronously, with the same behavior + * and content as the sounds played by {@link #takePicture takePicture}, + * {@link android.media.MediaRecorder#start MediaRecorder.start}, and + * {@link android.media.MediaRecorder#stop MediaRecorder.stop}.</p> + * + * <p>Using this method makes it easy to match the default device sounds + * when recording or capturing data through the preview callbacks + * ({@link #setPreviewCallback setPreviewCallback}, + * {@link #setPreviewTexture setPreviewTexture}).</p> + * + * @param soundId The type of sound to play, selected from the options in + * {@link android.hardware.Camera.Sound} + * @see android.hardware.Camera.Sound + * @see #takePicture + * @see android.media.MediaRecorder + * @hide + */ + public void playSound(int soundId) { + if (mReleased) return; + if (mCameraSoundPlayers == null) { + mCameraSoundPlayers = new CameraSoundPlayer[Sound.NUM_SOUNDS]; + } + if (mCameraSoundPlayers[soundId] == null) { + mCameraSoundPlayers[soundId] = new CameraSoundPlayer(soundId); + } + mCameraSoundPlayers[soundId].play(); + } + + private CameraSoundPlayer[] mCameraSoundPlayers; + + private static class CameraSoundPlayer implements Runnable { + private int mSoundId; + private int mAudioStreamType; + private MediaPlayer mPlayer; + private Thread mThread; + private boolean mExit; + private int mPlayCount; + + private static final String mShutterSound = + "/system/media/audio/ui/camera_click.ogg"; + private static final String mFocusSound = + "/system/media/audio/ui/camera_focus.ogg"; + private static final String mVideoStartSound = + "/system/media/audio/ui/VideoRecord.ogg"; + private static final String mVideoStopSound = + "/system/media/audio/ui/VideoRecord.ogg"; + + @Override + public void run() { + String soundFilePath; + switch (mSoundId) { + case Sound.SHUTTER_CLICK: + soundFilePath = mShutterSound; + break; + case Sound.FOCUS_COMPLETE: + soundFilePath = mFocusSound; + break; + case Sound.START_VIDEO_RECORDING: + soundFilePath = mVideoStartSound; + break; + case Sound.STOP_VIDEO_RECORDING: + soundFilePath = mVideoStopSound; + break; + default: + Log.e(TAG, "Unknown sound " + mSoundId + " requested."); + return; + } + mPlayer = new MediaPlayer(); + try { + mPlayer.setAudioStreamType(mAudioStreamType); + mPlayer.setDataSource(soundFilePath); + mPlayer.setLooping(false); + mPlayer.prepare(); + } catch(IOException e) { + Log.e(TAG, "Error setting up sound " + mSoundId, e); + return; + } + + while(true) { + try { + synchronized (this) { + while(true) { + if (mExit) { + return; + } else if (mPlayCount <= 0) { + wait(); + } else { + mPlayCount--; + break; + } + } + } + mPlayer.start(); + } catch (Exception e) { + Log.e(TAG, "Error playing sound " + mSoundId, e); + } + } + } + + public CameraSoundPlayer(int soundId) { + mSoundId = soundId; + if (SystemProperties.get("ro.camera.sound.forced", "0").equals("0")) { + mAudioStreamType = AudioManager.STREAM_MUSIC; + } else { + mAudioStreamType = AudioManager.STREAM_SYSTEM_ENFORCED; + } + } + + public void play() { + if (mThread == null) { + mThread = new Thread(this); + mThread.start(); + } + synchronized (this) { + mPlayCount++; + notifyAll(); + } + } + + public void release() { + if (mThread != null) { + synchronized (this) { + mExit = true; + notifyAll(); + } + try { + mThread.join(); + } catch (InterruptedException e) { + } + mThread = null; + } + if (mPlayer != null) { + mPlayer.release(); + mPlayer = null; + } + } + + @Override + protected void finalize() { + release(); + } + } + } diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 3605652..f6e627c 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -464,6 +464,19 @@ public class NetworkStats implements Parcelable { * time, and that none of them have disappeared. */ public NetworkStats subtract(NetworkStats value) throws NonMonotonicException { + return subtract(value, false); + } + + /** + * Subtract the given {@link NetworkStats}, effectively leaving the delta + * between two snapshots in time. Assumes that statistics rows collect over + * time, and that none of them have disappeared. + * + * @param clampNonMonotonic When non-monotonic stats are found, just clamp + * to 0 instead of throwing {@link NonMonotonicException}. + */ + public NetworkStats subtract(NetworkStats value, boolean clampNonMonotonic) + throws NonMonotonicException { final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime; if (deltaRealtime < 0) { throw new NonMonotonicException(this, value); @@ -497,7 +510,15 @@ public class NetworkStats implements Parcelable { if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0 || entry.txPackets < 0 || entry.operations < 0) { - throw new NonMonotonicException(this, i, value, j); + if (clampNonMonotonic) { + entry.rxBytes = Math.max(entry.rxBytes, 0); + entry.rxPackets = Math.max(entry.rxPackets, 0); + entry.txBytes = Math.max(entry.txBytes, 0); + entry.txPackets = Math.max(entry.txPackets, 0); + entry.operations = Math.max(entry.operations, 0); + } else { + throw new NonMonotonicException(this, i, value, j); + } } } diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index 016af58..0b93ad0 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -32,7 +32,7 @@ import android.nfc.INfcTag; interface INfcAdapter { INfcTag getNfcTagInterface(); - INfcAdapterExtras getNfcAdapterExtrasInterface(); + INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg); int getState(); boolean disable(); diff --git a/core/java/android/nfc/INfcAdapterExtras.aidl b/core/java/android/nfc/INfcAdapterExtras.aidl index 0c2a2fd..2b9d4f0 100644 --- a/core/java/android/nfc/INfcAdapterExtras.aidl +++ b/core/java/android/nfc/INfcAdapterExtras.aidl @@ -23,10 +23,10 @@ import android.os.Bundle; * {@hide} */ interface INfcAdapterExtras { - Bundle open(IBinder b); - Bundle close(); - Bundle transceive(in byte[] data_in); - int getCardEmulationRoute(); - void setCardEmulationRoute(int route); - void authenticate(in byte[] token); + Bundle open(in String pkg, IBinder b); + Bundle close(in String pkg, IBinder b); + Bundle transceive(in String pkg, in byte[] data_in); + int getCardEmulationRoute(in String pkg); + void setCardEmulationRoute(in String pkg, int route); + void authenticate(in String pkg, in byte[] token); } diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 33310df..a9f1685 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -16,6 +16,8 @@ package android.nfc; +import java.util.HashMap; + import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.Activity; @@ -197,15 +199,21 @@ public final class NfcAdapter { static INfcTag sTagService; /** - * NfcAdapter is currently a singleton, and does not require a context. - * However all the public API's are future-proofed to require a context. - * If we start using that then we'll need to keep a HashMap of - * Context.getApplicationContext() -> NfcAdapter, such that NfcAdapter - * is a singleton within each application context. + * The NfcAdapter object for each application context. + * There is a 1-1 relationship between application context and + * NfcAdapter object. + */ + static HashMap<Context, NfcAdapter> sNfcAdapters = new HashMap(); //guard by NfcAdapter.class + + /** + * NfcAdapter used with a null context. This ctor was deprecated but we have + * to support it for backwards compatibility. New methods that require context + * might throw when called on the null-context NfcAdapter. */ - static NfcAdapter sSingleton; // protected by NfcAdapter.class + static NfcAdapter sNullContextNfcAdapter; // protected by NfcAdapter.class final NfcActivityManager mNfcActivityManager; + final Context mContext; /** * A callback to be invoked when the system successfully delivers your {@link NdefMessage} @@ -280,12 +288,12 @@ public final class NfcAdapter { } /** - * Returns the singleton, or throws if NFC is not available. + * Returns the NfcAdapter for application context, + * or throws if NFC is not available. + * @hide */ - static synchronized NfcAdapter getSingleton() { + public static synchronized NfcAdapter getNfcAdapter(Context context) { if (!sIsInitialized) { - sIsInitialized = true; - /* is this device meant to have NFC */ if (!hasNfcFeature()) { Log.v(TAG, "this device does not have NFC support"); @@ -303,12 +311,21 @@ public final class NfcAdapter { Log.e(TAG, "could not retrieve NFC Tag service"); throw new UnsupportedOperationException(); } - sSingleton = new NfcAdapter(); + + sIsInitialized = true; + } + if (context == null) { + if (sNullContextNfcAdapter == null) { + sNullContextNfcAdapter = new NfcAdapter(null); + } + return sNullContextNfcAdapter; } - if (sSingleton == null) { - throw new UnsupportedOperationException(); + NfcAdapter adapter = sNfcAdapters.get(context); + if (adapter == null) { + adapter = new NfcAdapter(context); + sNfcAdapters.put(context, adapter); } - return sSingleton; + return adapter; } /** get handle to NFC service interface */ @@ -336,6 +353,10 @@ public final class NfcAdapter { * @return the default NFC adapter, or null if no NFC adapter exists */ public static NfcAdapter getDefaultAdapter(Context context) { + if (context == null) { + throw new IllegalArgumentException("context cannot be null"); + } + context = context.getApplicationContext(); /* use getSystemService() instead of just instantiating to take * advantage of the context's cached NfcManager & NfcAdapter */ NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE); @@ -343,25 +364,30 @@ public final class NfcAdapter { } /** - * Get a handle to the default NFC Adapter on this Android device. - * <p> - * Most Android devices will only have one NFC Adapter (NFC Controller). - * - * @return the default NFC adapter, or null if no NFC adapter exists + * Legacy NfcAdapter getter, always use {@link #getDefaultAdapter(Context)} instead.<p> + * This method was deprecated at API level 10 (Gingerbread MR1) because a context is required + * for many NFC API methods. Those methods will fail when called on an NfcAdapter + * object created from this method.<p> * @deprecated use {@link #getDefaultAdapter(Context)} */ @Deprecated public static NfcAdapter getDefaultAdapter() { Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " + "NfcAdapter.getDefaultAdapter(Context) instead", new Exception()); - return getSingleton(); + + return NfcAdapter.getNfcAdapter(null); + } + + NfcAdapter(Context context) { + mContext = context; + mNfcActivityManager = new NfcActivityManager(this); } /** - * Does not currently need a context. + * @hide */ - NfcAdapter() { - mNfcActivityManager = new NfcActivityManager(this); + public Context getContext() { + return mContext; } /** @@ -768,6 +794,61 @@ public final class NfcAdapter { } /** + * TODO: Remove this once pre-built apk's (Maps, Youtube etc) are updated + * @deprecated use {@link CreateNdefMessageCallback} or {@link OnNdefPushCompleteCallback} + * @hide + */ + @Deprecated + public interface NdefPushCallback { + /** + * @deprecated use {@link CreateNdefMessageCallback} instead + */ + @Deprecated + NdefMessage createMessage(); + /** + * @deprecated use{@link OnNdefPushCompleteCallback} instead + */ + @Deprecated + void onMessagePushed(); + } + + /** + * TODO: Remove this + * Converts new callbacks to old callbacks. + */ + static final class LegacyCallbackWrapper implements CreateNdefMessageCallback, + OnNdefPushCompleteCallback { + final NdefPushCallback mLegacyCallback; + LegacyCallbackWrapper(NdefPushCallback legacyCallback) { + mLegacyCallback = legacyCallback; + } + @Override + public void onNdefPushComplete(NfcEvent event) { + mLegacyCallback.onMessagePushed(); + } + @Override + public NdefMessage createNdefMessage(NfcEvent event) { + return mLegacyCallback.createMessage(); + } + } + + /** + * TODO: Remove this once pre-built apk's (Maps, Youtube etc) are updated + * @deprecated use {@link #setNdefPushMessageCallback} instead + * @hide + */ + @Deprecated + public void enableForegroundNdefPush(Activity activity, final NdefPushCallback callback) { + if (activity == null || callback == null) { + throw new NullPointerException(); + } + enforceResumed(activity); + LegacyCallbackWrapper callbackWrapper = new LegacyCallbackWrapper(callback); + mNfcActivityManager.setNdefPushMessageCallback(activity, callbackWrapper); + mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callbackWrapper); + } + + /** * Enable NDEF Push feature. * <p>This API is for the Settings application. * @hide @@ -820,8 +901,12 @@ public final class NfcAdapter { * @hide */ public INfcAdapterExtras getNfcAdapterExtrasInterface() { + if (mContext == null) { + throw new UnsupportedOperationException("You need a context on NfcAdapter to use the " + + " NFC extras APIs"); + } try { - return sService.getNfcAdapterExtrasInterface(); + return sService.getNfcAdapterExtrasInterface(mContext.getPackageName()); } catch (RemoteException e) { attemptDeadServiceRecovery(e); return null; diff --git a/core/java/android/nfc/NfcManager.java b/core/java/android/nfc/NfcManager.java index 300ab45..6ec2e21 100644 --- a/core/java/android/nfc/NfcManager.java +++ b/core/java/android/nfc/NfcManager.java @@ -39,8 +39,9 @@ public final class NfcManager { */ public NfcManager(Context context) { NfcAdapter adapter; + context = context.getApplicationContext(); try { - adapter = NfcAdapter.getSingleton(); + adapter = NfcAdapter.getNfcAdapter(context); } catch (UnsupportedOperationException e) { adapter = null; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 769776e..b032169 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4077,13 +4077,6 @@ public final class Settings { "contacts_preauth_uri_expiration"; /** - * Whether the Messaging app posts notifications. - * 0=disabled. 1=enabled. - */ - public static final String MESSAGING_APP_NOTIFICATIONS = "messaging_app_notifications"; - - - /** * This are the settings to be backed up. * * NOTE: Settings are backed up and restored in the order they appear @@ -4120,8 +4113,7 @@ public final class Settings { MOUNT_UMS_NOTIFY_ENABLED, UI_NIGHT_MODE, LOCK_SCREEN_OWNER_INFO, - LOCK_SCREEN_OWNER_INFO_ENABLED, - MESSAGING_APP_NOTIFICATIONS + LOCK_SCREEN_OWNER_INFO_ENABLED }; /** diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 8eb9da1..0e6d07d 100755 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -1838,15 +1838,5 @@ public final class Telephony { public static final String EXTRA_PLMN = "plmn"; public static final String EXTRA_SHOW_SPN = "showSpn"; public static final String EXTRA_SPN = "spn"; - - /** - * Activity Action: Shows a dialog to turn off Messaging app notification. - * <p>Input: Nothing. - * <p>Output: Nothing. - */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_MESSAGING_APP_NOTIFICATIONS = - "android.provider.Telephony.MESSAGING_APP_NOTIFICATIONS"; - } } diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 1b473ec..aa62cd7 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -784,11 +784,12 @@ class BluetoothEventLoop { // machine. We don't handle AVCTP signals currently. We only send // intents for AVDTP state changes. We need to handle both of them in // some cases. For now, just don't move to incoming state in this case. - mBluetoothService.notifyIncomingA2dpConnection(address); + mBluetoothService.notifyIncomingA2dpConnection(address, true); } else { Log.i(TAG, "" + authorized + "Incoming A2DP / AVRCP connection from " + address); mA2dp.allowIncomingConnect(device, authorized); + mBluetoothService.notifyIncomingA2dpConnection(address, false); } } else if (BluetoothUuid.isInputDevice(uuid)) { // We can have more than 1 input device connected. diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 9ca5847..d604a01 100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -89,7 +89,7 @@ public class BluetoothService extends IBluetooth.Stub { private int mNativeData; private BluetoothEventLoop mEventLoop; - private BluetoothHeadset mBluetoothHeadset; + private BluetoothHeadset mHeadsetProxy; private BluetoothInputDevice mInputDevice; private BluetoothPan mPan; private boolean mIsAirplaneSensitive; @@ -605,6 +605,7 @@ public class BluetoothService extends IBluetooth.Stub { } mBondState.initBondState(); initProfileState(); + getProfileProxy(); } /** @@ -1766,8 +1767,8 @@ public class BluetoothService extends IBluetooth.Stub { private void dumpHeadsetService(PrintWriter pw) { pw.println("\n--Headset Service--"); - if (mBluetoothHeadset != null) { - List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices(); + if (mHeadsetProxy != null) { + List<BluetoothDevice> deviceList = mHeadsetProxy.getConnectedDevices(); if (deviceList.size() == 0) { pw.println("No headsets connected"); } else { @@ -1775,21 +1776,20 @@ public class BluetoothService extends IBluetooth.Stub { pw.println("\ngetConnectedDevices[0] = " + device); dumpHeadsetConnectionState(pw, device); pw.println("getBatteryUsageHint() = " + - mBluetoothHeadset.getBatteryUsageHint(device)); + mHeadsetProxy.getBatteryUsageHint(device)); } deviceList.clear(); - deviceList = mBluetoothHeadset.getDevicesMatchingConnectionStates(new int[] { + deviceList = mHeadsetProxy.getDevicesMatchingConnectionStates(new int[] { BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED}); pw.println("--Connected and Disconnected Headsets"); for (BluetoothDevice device: deviceList) { pw.println(device); - if (mBluetoothHeadset.isAudioConnected(device)) { + if (mHeadsetProxy.isAudioConnected(device)) { pw.println("SCO audio connected to device:" + device); } } } - mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset); } private void dumpInputDeviceProfile(PrintWriter pw) { @@ -1824,7 +1824,6 @@ public class BluetoothService extends IBluetooth.Stub { pw.println(device); } } - mAdapter.closeProfileProxy(BluetoothProfile.INPUT_DEVICE, mBluetoothHeadset); } private void dumpPanProfile(PrintWriter pw) { @@ -1862,7 +1861,7 @@ public class BluetoothService extends IBluetooth.Stub { private void dumpHeadsetConnectionState(PrintWriter pw, BluetoothDevice device) { - switch (mBluetoothHeadset.getConnectionState(device)) { + switch (mHeadsetProxy.getConnectionState(device)) { case BluetoothHeadset.STATE_CONNECTING: pw.println("getConnectionState() = STATE_CONNECTING"); break; @@ -1884,7 +1883,6 @@ public class BluetoothService extends IBluetooth.Stub { Integer pid = mServiceRecordToPid.get(handle).first; pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle)); } - mAdapter.closeProfileProxy(BluetoothProfile.PAN, mBluetoothHeadset); } private void dumpAclConnectedDevices(PrintWriter pw) { @@ -1927,11 +1925,16 @@ public class BluetoothService extends IBluetooth.Stub { } } + private void getProfileProxy() { + mAdapter.getProfileProxy(mContext, + mBluetoothProfileServiceListener, BluetoothProfile.HEADSET); + } + private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = new BluetoothProfile.ServiceListener() { public void onServiceConnected(int profile, BluetoothProfile proxy) { if (profile == BluetoothProfile.HEADSET) { - mBluetoothHeadset = (BluetoothHeadset) proxy; + mHeadsetProxy = (BluetoothHeadset) proxy; } else if (profile == BluetoothProfile.INPUT_DEVICE) { mInputDevice = (BluetoothInputDevice) proxy; } else if (profile == BluetoothProfile.PAN) { @@ -1940,7 +1943,7 @@ public class BluetoothService extends IBluetooth.Stub { } public void onServiceDisconnected(int profile) { if (profile == BluetoothProfile.HEADSET) { - mBluetoothHeadset = null; + mHeadsetProxy = null; } else if (profile == BluetoothProfile.INPUT_DEVICE) { mInputDevice = null; } else if (profile == BluetoothProfile.PAN) { @@ -2424,25 +2427,43 @@ public class BluetoothService extends IBluetooth.Stub { } } - public boolean notifyIncomingConnection(String address) { - BluetoothDeviceProfileState state = - mDeviceProfileState.get(address); + public boolean notifyIncomingConnection(String address, boolean rejected) { + BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state != null) { Message msg = new Message(); - msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING; - state.sendMessage(msg); + if (rejected) { + if (mA2dpService.getPriority(getRemoteDevice(address)) >= + BluetoothProfile.PRIORITY_ON) { + msg.what = BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES; + msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING; + state.sendMessageDelayed(msg, + BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES_DELAY); + } + } else { + msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING; + state.sendMessage(msg); + } return true; } return false; } - /*package*/ boolean notifyIncomingA2dpConnection(String address) { - BluetoothDeviceProfileState state = - mDeviceProfileState.get(address); + /*package*/ boolean notifyIncomingA2dpConnection(String address, boolean rejected) { + BluetoothDeviceProfileState state = mDeviceProfileState.get(address); if (state != null) { Message msg = new Message(); - msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING; - state.sendMessage(msg); + if (rejected) { + if (mHeadsetProxy.getPriority(getRemoteDevice(address)) >= + BluetoothProfile.PRIORITY_ON) { + msg.what = BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES; + msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING; + state.sendMessageDelayed(msg, + BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES_DELAY); + } + } else { + msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING; + state.sendMessage(msg); + } return true; } return false; diff --git a/core/java/android/speech/tts/AudioMessageParams.java b/core/java/android/speech/tts/AudioMessageParams.java index 68d8738..29b4367 100644 --- a/core/java/android/speech/tts/AudioMessageParams.java +++ b/core/java/android/speech/tts/AudioMessageParams.java @@ -15,12 +15,12 @@ */ package android.speech.tts; -import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher; +import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher; class AudioMessageParams extends MessageParams { private final BlockingMediaPlayer mPlayer; - AudioMessageParams(UtteranceCompletedDispatcher dispatcher, + AudioMessageParams(UtteranceProgressDispatcher dispatcher, String callingApp, BlockingMediaPlayer player) { super(dispatcher, callingApp); mPlayer = player; diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java index d970ae6..0194240 100644 --- a/core/java/android/speech/tts/AudioPlaybackHandler.java +++ b/core/java/android/speech/tts/AudioPlaybackHandler.java @@ -312,10 +312,11 @@ class AudioPlaybackHandler { private void handleSilence(MessageParams msg) { if (DBG) Log.d(TAG, "handleSilence()"); SilenceMessageParams params = (SilenceMessageParams) msg; + params.getDispatcher().dispatchOnStart(); if (params.getSilenceDurationMs() > 0) { params.getConditionVariable().block(params.getSilenceDurationMs()); } - params.getDispatcher().dispatchUtteranceCompleted(); + params.getDispatcher().dispatchOnDone(); if (DBG) Log.d(TAG, "handleSilence() done."); } @@ -323,11 +324,12 @@ class AudioPlaybackHandler { private void handleAudio(MessageParams msg) { if (DBG) Log.d(TAG, "handleAudio()"); AudioMessageParams params = (AudioMessageParams) msg; + params.getDispatcher().dispatchOnStart(); // Note that the BlockingMediaPlayer spawns a separate thread. // // TODO: This can be avoided. params.getPlayer().startAndWait(); - params.getDispatcher().dispatchUtteranceCompleted(); + params.getDispatcher().dispatchOnDone(); if (DBG) Log.d(TAG, "handleAudio() done."); } @@ -361,6 +363,7 @@ class AudioPlaybackHandler { if (DBG) Log.d(TAG, "Created audio track [" + audioTrack.hashCode() + "]"); param.setAudioTrack(audioTrack); + msg.getDispatcher().dispatchOnStart(); } // More data available to be flushed to the audio track. @@ -411,6 +414,7 @@ class AudioPlaybackHandler { final AudioTrack audioTrack = params.getAudioTrack(); if (audioTrack == null) { + params.getDispatcher().dispatchOnError(); return; } @@ -439,7 +443,7 @@ class AudioPlaybackHandler { audioTrack.release(); params.setAudioTrack(null); } - params.getDispatcher().dispatchUtteranceCompleted(); + params.getDispatcher().dispatchOnDone(); mLastSynthesisRequest = null; params.mLogger.onWriteData(); } diff --git a/core/java/android/speech/tts/ITextToSpeechCallback.aidl b/core/java/android/speech/tts/ITextToSpeechCallback.aidl index 40902ae..f0287d4 100755 --- a/core/java/android/speech/tts/ITextToSpeechCallback.aidl +++ b/core/java/android/speech/tts/ITextToSpeechCallback.aidl @@ -21,5 +21,7 @@ package android.speech.tts; * {@hide} */ oneway interface ITextToSpeechCallback { - void utteranceCompleted(String utteranceId); + void onStart(String utteranceId); + void onDone(String utteranceId); + void onError(String utteranceId); } diff --git a/core/java/android/speech/tts/MessageParams.java b/core/java/android/speech/tts/MessageParams.java index e7d6da3..de9cc07 100644 --- a/core/java/android/speech/tts/MessageParams.java +++ b/core/java/android/speech/tts/MessageParams.java @@ -15,22 +15,22 @@ */ package android.speech.tts; -import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher; +import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher; abstract class MessageParams { static final int TYPE_SYNTHESIS = 1; static final int TYPE_AUDIO = 2; static final int TYPE_SILENCE = 3; - private final UtteranceCompletedDispatcher mDispatcher; + private final UtteranceProgressDispatcher mDispatcher; private final String mCallingApp; - MessageParams(UtteranceCompletedDispatcher dispatcher, String callingApp) { + MessageParams(UtteranceProgressDispatcher dispatcher, String callingApp) { mDispatcher = dispatcher; mCallingApp = callingApp; } - UtteranceCompletedDispatcher getDispatcher() { + UtteranceProgressDispatcher getDispatcher() { return mDispatcher; } diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java index 0cca06a..ce3522b 100644 --- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java +++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java @@ -15,7 +15,7 @@ */ package android.speech.tts; -import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher; +import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher; import android.util.Log; /** @@ -62,12 +62,12 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { private volatile boolean mDone = false; - private final UtteranceCompletedDispatcher mDispatcher; + private final UtteranceProgressDispatcher mDispatcher; private final String mCallingApp; private final EventLogger mLogger; PlaybackSynthesisCallback(int streamType, float volume, float pan, - AudioPlaybackHandler audioTrackHandler, UtteranceCompletedDispatcher dispatcher, + AudioPlaybackHandler audioTrackHandler, UtteranceProgressDispatcher dispatcher, String callingApp, EventLogger logger) { mStreamType = streamType; mVolume = volume; diff --git a/core/java/android/speech/tts/SilenceMessageParams.java b/core/java/android/speech/tts/SilenceMessageParams.java index 7a4ff1c..9909126 100644 --- a/core/java/android/speech/tts/SilenceMessageParams.java +++ b/core/java/android/speech/tts/SilenceMessageParams.java @@ -16,13 +16,13 @@ package android.speech.tts; import android.os.ConditionVariable; -import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher; +import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher; class SilenceMessageParams extends MessageParams { private final ConditionVariable mCondVar = new ConditionVariable(); private final long mSilenceDurationMs; - SilenceMessageParams(UtteranceCompletedDispatcher dispatcher, + SilenceMessageParams(UtteranceProgressDispatcher dispatcher, String callingApp, long silenceDurationMs) { super(dispatcher, callingApp); mSilenceDurationMs = silenceDurationMs; diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java index 779721e..0c0f033 100644 --- a/core/java/android/speech/tts/SynthesisMessageParams.java +++ b/core/java/android/speech/tts/SynthesisMessageParams.java @@ -17,7 +17,7 @@ package android.speech.tts; import android.media.AudioFormat; import android.media.AudioTrack; -import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher; +import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher; import java.util.LinkedList; @@ -56,7 +56,7 @@ final class SynthesisMessageParams extends MessageParams { SynthesisMessageParams(int streamType, int sampleRate, int audioFormat, int channelCount, - float volume, float pan, UtteranceCompletedDispatcher dispatcher, + float volume, float pan, UtteranceProgressDispatcher dispatcher, String callingApp, EventLogger logger) { super(dispatcher, callingApp); diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index fdc2570..38699ea 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -482,7 +482,7 @@ public class TextToSpeech { private OnInitListener mInitListener; // Written from an unspecified application thread, read from // a binder thread. - private volatile OnUtteranceCompletedListener mUtteranceCompletedListener; + private volatile UtteranceProgressListener mUtteranceProgressListener; private final Object mStartLock = new Object(); private String mRequestedEngine; @@ -1146,9 +1146,28 @@ public class TextToSpeech { * @param listener The listener to use. * * @return {@link #ERROR} or {@link #SUCCESS}. + * + * @deprecated Use {@link #setOnUtteranceProgressListener(UtteranceProgressListener)} + * instead. */ + @Deprecated public int setOnUtteranceCompletedListener(final OnUtteranceCompletedListener listener) { - mUtteranceCompletedListener = listener; + mUtteranceProgressListener = UtteranceProgressListener.from(listener); + return TextToSpeech.SUCCESS; + } + + /** + * Sets the listener that will be notified of various events related to the + * synthesis of a given utterance. + * + * See {@link UtteranceProgressListener} and + * {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}. + * + * @param listener the listener to use. + * @return {@link #ERROR} or {@link #SUCCESS} + */ + public int setOnUtteranceProgressListener(UtteranceProgressListener listener) { + mUtteranceProgressListener = listener; return TextToSpeech.SUCCESS; } @@ -1204,10 +1223,26 @@ public class TextToSpeech { private ITextToSpeechService mService; private final ITextToSpeechCallback.Stub mCallback = new ITextToSpeechCallback.Stub() { @Override - public void utteranceCompleted(String utteranceId) { - OnUtteranceCompletedListener listener = mUtteranceCompletedListener; + public void onDone(String utteranceId) { + UtteranceProgressListener listener = mUtteranceProgressListener; + if (listener != null) { + listener.onDone(utteranceId); + } + } + + @Override + public void onError(String utteranceId) { + UtteranceProgressListener listener = mUtteranceProgressListener; + if (listener != null) { + listener.onError(utteranceId); + } + } + + @Override + public void onStart(String utteranceId) { + UtteranceProgressListener listener = mUtteranceProgressListener; if (listener != null) { - listener.onUtteranceCompleted(utteranceId); + listener.onStart(utteranceId); } } }; diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 83b6d4c..39922da 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -306,6 +306,7 @@ public abstract class TextToSpeechService extends Service { */ public int enqueueSpeechItem(int queueMode, final SpeechItem speechItem) { if (!speechItem.isValid()) { + speechItem.dispatchOnError(); return TextToSpeech.ERROR; } @@ -332,6 +333,7 @@ public abstract class TextToSpeechService extends Service { return TextToSpeech.SUCCESS; } else { Log.w(TAG, "SynthThread has quit"); + speechItem.dispatchOnError(); return TextToSpeech.ERROR; } } @@ -381,14 +383,16 @@ public abstract class TextToSpeechService extends Service { } } - interface UtteranceCompletedDispatcher { - public void dispatchUtteranceCompleted(); + interface UtteranceProgressDispatcher { + public void dispatchOnDone(); + public void dispatchOnStart(); + public void dispatchOnError(); } /** * An item in the synth thread queue. */ - private abstract class SpeechItem implements UtteranceCompletedDispatcher { + private abstract class SpeechItem implements UtteranceProgressDispatcher { private final String mCallingApp; protected final Bundle mParams; private boolean mStarted = false; @@ -443,10 +447,27 @@ public abstract class TextToSpeechService extends Service { stopImpl(); } - public void dispatchUtteranceCompleted() { + @Override + public void dispatchOnDone() { + final String utteranceId = getUtteranceId(); + if (!TextUtils.isEmpty(utteranceId)) { + mCallbacks.dispatchOnDone(getCallingApp(), utteranceId); + } + } + + @Override + public void dispatchOnStart() { + final String utteranceId = getUtteranceId(); + if (!TextUtils.isEmpty(utteranceId)) { + mCallbacks.dispatchOnStart(getCallingApp(), utteranceId); + } + } + + @Override + public void dispatchOnError() { final String utteranceId = getUtteranceId(); if (!TextUtils.isEmpty(utteranceId)) { - mCallbacks.dispatchUtteranceCompleted(getCallingApp(), utteranceId); + mCallbacks.dispatchOnError(getCallingApp(), utteranceId); } } @@ -617,9 +638,12 @@ public abstract class TextToSpeechService extends Service { @Override protected int playImpl() { + dispatchOnStart(); int status = super.playImpl(); if (status == TextToSpeech.SUCCESS) { - dispatchUtteranceCompleted(); + dispatchOnDone(); + } else { + dispatchOnError(); } return status; } @@ -856,16 +880,34 @@ public abstract class TextToSpeechService extends Service { } } - public void dispatchUtteranceCompleted(String packageName, String utteranceId) { - ITextToSpeechCallback cb; - synchronized (mAppToCallback) { - cb = mAppToCallback.get(packageName); + public void dispatchOnDone(String packageName, String utteranceId) { + ITextToSpeechCallback cb = getCallbackFor(packageName); + if (cb == null) return; + try { + cb.onDone(utteranceId); + } catch (RemoteException e) { + Log.e(TAG, "Callback onDone failed: " + e); } + } + + public void dispatchOnStart(String packageName, String utteranceId) { + ITextToSpeechCallback cb = getCallbackFor(packageName); + if (cb == null) return; + try { + cb.onStart(utteranceId); + } catch (RemoteException e) { + Log.e(TAG, "Callback onStart failed: " + e); + } + + } + + public void dispatchOnError(String packageName, String utteranceId) { + ITextToSpeechCallback cb = getCallbackFor(packageName); if (cb == null) return; try { - cb.utteranceCompleted(utteranceId); + cb.onError(utteranceId); } catch (RemoteException e) { - Log.e(TAG, "Callback failed: " + e); + Log.e(TAG, "Callback onError failed: " + e); } } @@ -886,6 +928,15 @@ public abstract class TextToSpeechService extends Service { } } + private ITextToSpeechCallback getCallbackFor(String packageName) { + ITextToSpeechCallback cb; + synchronized (mAppToCallback) { + cb = mAppToCallback.get(packageName); + } + + return cb; + } + } } diff --git a/core/java/android/speech/tts/UtteranceProgressListener.java b/core/java/android/speech/tts/UtteranceProgressListener.java new file mode 100644 index 0000000..a04458a --- /dev/null +++ b/core/java/android/speech/tts/UtteranceProgressListener.java @@ -0,0 +1,68 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +package android.speech.tts; + +/** + * Listener for events relating to the progress of an utterance through + * the synthesis queue. Each utterance is associated with a call to + * {@link TextToSpeech#speak} or {@link TextToSpeech#synthesizeToFile} with an + * associated utterance identifier, as per {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}. + * + * The callbacks specified in this method can be called from multiple threads. + */ +public abstract class UtteranceProgressListener { + /** + * Called when an utterance "starts" as perceived by the caller. This will + * be soon before audio is played back in the case of a {@link TextToSpeech#speak} + * or before the first bytes of a file are written to storage in the case + * of {@link TextToSpeech#synthesizeToFile}. + * + * @param utteranceId the utterance ID of the utterance. + */ + public abstract void onStart(String utteranceId); + + /** + * Called when an utterance has successfully completed processing. + * All audio will have been played back by this point for audible output, and all + * output will have been written to disk for file synthesis requests. + * + * This request is guaranteed to be called after {@link #onStart(String)}. + * + * @param utteranceId the utterance ID of the utterance. + */ + public abstract void onDone(String utteranceId); + + /** + * Called when an error has occurred during processing. This can be called + * at any point in the synthesis process. Note that there might be calls + * to {@link #onStart(String)} for specified utteranceId but there will never + * be a call to both {@link #onDone(String)} and {@link #onError(String)} for + * the same utterance. + * + * @param utteranceId the utterance ID of the utterance. + */ + public abstract void onError(String utteranceId); + + /** + * Wraps an old deprecated OnUtteranceCompletedListener with a shiny new + * progress listener. + * + * @hide + */ + static UtteranceProgressListener from( + final TextToSpeech.OnUtteranceCompletedListener listener) { + return new UtteranceProgressListener() { + @Override + public synchronized void onDone(String utteranceId) { + listener.onUtteranceCompleted(utteranceId); + } + + // The following methods are left unimplemented. + @Override + public void onStart(String utteranceId) { } + + @Override + public void onError(String utteranceId) { } + }; + } +} diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index c934c7b..d948ec2 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -315,6 +315,27 @@ class GLES20Canvas extends HardwareCanvas { private static native void nFlushCaches(int level); + /** + * Release all resources associated with the underlying caches. This should + * only be called after a full flushCaches(). + * + * @hide + */ + public static void terminateCaches() { + nTerminateCaches(); + } + + private static native void nTerminateCaches(); + + /** + * @hide + */ + public static void initCaches() { + nInitCaches(); + } + + private static native void nInitCaches(); + /////////////////////////////////////////////////////////////////////////// // Display list /////////////////////////////////////////////////////////////////////////// diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index b86d21d..e0167d8 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -25,6 +25,7 @@ import android.opengl.GLUtils; import android.os.SystemClock; import android.os.SystemProperties; import android.util.Log; +import com.google.android.gles_jni.EGLImpl; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGL11; @@ -34,6 +35,8 @@ import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; import javax.microedition.khronos.opengles.GL; +import java.io.File; + import static javax.microedition.khronos.egl.EGL10.*; /** @@ -45,6 +48,11 @@ public abstract class HardwareRenderer { static final String LOG_TAG = "HardwareRenderer"; /** + * Name of the file that holds the shaders cache. + */ + private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache"; + + /** * Turn on to only refresh the parts of the screen that need updating. * When turned on the property defined by {@link #RENDER_DIRTY_REGIONS_PROPERTY} * must also have the value "true". @@ -200,6 +208,18 @@ public abstract class HardwareRenderer { abstract int getHeight(); /** + * Sets the directory to use as a persistent storage for hardware rendering + * resources. + * + * @param cacheDir A directory the current process can write to + */ + public static void setupDiskCache(File cacheDir) { + nSetupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath()); + } + + private static native void nSetupShadersDiskCache(String cacheFile); + + /** * Interface used to receive callbacks whenever a view is drawn by * a hardware renderer instance. */ @@ -325,6 +345,15 @@ public abstract class HardwareRenderer { } /** + * Invoke this method when the system needs to clean up all resources + * associated with hardware rendering. + */ + static void terminate() { + Log.d(LOG_TAG, "Terminating hardware rendering"); + Gl20Renderer.terminate(); + } + + /** * Indicates whether hardware acceleration is currently enabled. * * @return True if hardware acceleration is in use, false otherwise. @@ -632,6 +661,8 @@ public abstract class HardwareRenderer { throw new Surface.OutOfResourcesException("eglMakeCurrent failed " + GLUtils.getEGLErrorString(sEgl.eglGetError())); } + + initCaches(); // If mDirtyRegions is set, this means we have an EGL configuration // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set @@ -652,6 +683,8 @@ public abstract class HardwareRenderer { return mEglContext.getGL(); } + abstract void initCaches(); + EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { int[] attribs = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE }; @@ -895,6 +928,11 @@ public abstract class HardwareRenderer { EGL_NONE }; } + + @Override + void initCaches() { + GLES20Canvas.initCaches(); + } @Override boolean canDraw() { @@ -987,16 +1025,7 @@ public abstract class HardwareRenderer { if (eglContext == null) { return; } else { - synchronized (sPbufferLock) { - // Create a temporary 1x1 pbuffer so we have a context - // to clear our OpenGL objects - if (sPbuffer == null) { - sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] { - EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE - }); - } - } - sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext); + usePbufferSurface(eglContext); } switch (level) { @@ -1010,5 +1039,46 @@ public abstract class HardwareRenderer { break; } } + + private static void usePbufferSurface(EGLContext eglContext) { + synchronized (sPbufferLock) { + // Create a temporary 1x1 pbuffer so we have a context + // to clear our OpenGL objects + if (sPbuffer == null) { + sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] { + EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE + }); + } + } + sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext); + } + + static void terminate() { + synchronized (sEglLock) { + if (sEgl == null) return; + + if (EGLImpl.getInitCount(sEglDisplay) == 1) { + EGLContext eglContext = sEglContextStorage.get(); + if (eglContext == null) return; + + usePbufferSurface(eglContext); + GLES20Canvas.terminateCaches(); + + sEgl.eglDestroyContext(sEglDisplay, eglContext); + sEglContextStorage.remove(); + + sEgl.eglDestroySurface(sEglDisplay, sPbuffer); + sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + sEgl.eglReleaseThread(); + sEgl.eglTerminate(sEglDisplay); + + sEgl = null; + sEglDisplay = null; + sEglConfig = null; + sPbuffer = null; + } + } + } } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 62e6ebd..dc46d42 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -11402,8 +11402,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link SystemClock#uptimeMillis} timebase. */ public void scheduleDrawable(Drawable who, Runnable what, long when) { - if (verifyDrawable(who) && what != null && mAttachInfo != null) { - mAttachInfo.mHandler.postAtTime(what, who, when); + if (verifyDrawable(who) && what != null) { + if (mAttachInfo != null) { + mAttachInfo.mHandler.postAtTime(what, who, when); + } else { + ViewRootImpl.getRunQueue().postDelayed(what, when - SystemClock.uptimeMillis()); + } } } @@ -11414,8 +11418,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @param what the action to cancel */ public void unscheduleDrawable(Drawable who, Runnable what) { - if (verifyDrawable(who) && what != null && mAttachInfo != null) { - mAttachInfo.mHandler.removeCallbacks(what, who); + if (verifyDrawable(who) && what != null) { + if (mAttachInfo != null) { + mAttachInfo.mHandler.removeCallbacks(what, who); + } else { + ViewRootImpl.getRunQueue().removeCallbacks(what); + } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f7078ec..b15b155 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -567,7 +567,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, } } - private void destroyHardwareResources() { + void destroyHardwareResources() { if (mAttachInfo.mHardwareRenderer != null) { if (mAttachInfo.mHardwareRenderer.isEnabled()) { mAttachInfo.mHardwareRenderer.destroyLayers(mView); @@ -880,12 +880,10 @@ public final class ViewRootImpl extends Handler implements ViewParent, || mNewSurfaceNeeded; WindowManager.LayoutParams params = null; - int windowAttributesChanges = 0; if (mWindowAttributesChanged) { mWindowAttributesChanged = false; surfaceChanged = true; params = lp; - windowAttributesChanges = mWindowAttributesChangesFlag; } CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get(); if (compatibilityInfo.supportsScreen() == mLastInCompatMode) { diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 5ef4f3e..d89bc36 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -16,6 +16,8 @@ package android.view; +import android.app.ActivityManager; +import android.content.ComponentCallbacks2; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.PixelFormat; @@ -409,7 +411,30 @@ public class WindowManagerImpl implements WindowManager { */ public void trimMemory(int level) { if (HardwareRenderer.isAvailable()) { - HardwareRenderer.trimMemory(level); + switch (level) { + case ComponentCallbacks2.TRIM_MEMORY_COMPLETE: + case ComponentCallbacks2.TRIM_MEMORY_MODERATE: + // On low and medium end gfx devices + if (!ActivityManager.isHighEndGfx(getDefaultDisplay())) { + // Force a full memory flush + HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE); + // Destroy all hardware surfaces and resources associated to + // known windows + synchronized (this) { + if (mViews == null) return; + int count = mViews.length; + for (int i = 0; i < count; i++) { + mRoots[i].destroyHardwareResources(); + } + } + // Terminate the hardware renderer to free all resources + HardwareRenderer.terminate(); + break; + } + // high end gfx devices fall through to next case + default: + HardwareRenderer.trimMemory(level); + } } } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 03d6511..7249497 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -2861,8 +2861,8 @@ public class WebView extends AbsoluteLayout } // Used to avoid sending many visible rect messages. - private Rect mLastVisibleRectSent; - private Rect mLastGlobalRect; + private Rect mLastVisibleRectSent = new Rect(); + private Rect mLastGlobalRect = new Rect(); private Rect mVisibleRect = new Rect(); private Rect mGlobalVisibleRect = new Rect(); private Point mScrollOffset = new Point(); @@ -2878,7 +2878,7 @@ public class WebView extends AbsoluteLayout mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET, nativeMoveGeneration(), mSendScrollEvent ? 1 : 0, mScrollOffset); } - mLastVisibleRectSent = mVisibleRect; + mLastVisibleRectSent.set(mVisibleRect); mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); } if (getGlobalVisibleRect(mGlobalVisibleRect) @@ -2894,7 +2894,7 @@ public class WebView extends AbsoluteLayout if (!mBlockWebkitViewMessages) { mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, mGlobalVisibleRect); } - mLastGlobalRect = mGlobalVisibleRect; + mLastGlobalRect.set(mGlobalVisibleRect); } return mVisibleRect; } diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index fd2abc2..326587e 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -129,7 +129,7 @@ public class EdgeEffect { mEdge = res.getDrawable(R.drawable.overscroll_edge); mGlow = res.getDrawable(R.drawable.overscroll_glow); - mMinWidth = (int) (context.getResources().getDisplayMetrics().density * MIN_WIDTH + 0.5f); + mMinWidth = (int) (res.getDisplayMetrics().density * MIN_WIDTH + 0.5f); mInterpolator = new DecelerateInterpolator(); } diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index b24dd69..73e1273 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -1035,4 +1035,29 @@ public class ImageView extends View { mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8); } } + + @RemotableViewMethod + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + if (mDrawable != null) { + mDrawable.setVisible(visibility == VISIBLE, false); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (mDrawable != null) { + mDrawable.setVisible(getVisibility() == VISIBLE, false); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mDrawable != null) { + mDrawable.setVisible(false, false); + } + } } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 8ba7bee..5fa4ad0 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1248,6 +1248,8 @@ public class PopupWindow { */ public void dismiss() { if (isShowing() && mPopupView != null) { + mIsShowing = false; + unregisterForScrollChanged(); try { @@ -1257,7 +1259,6 @@ public class PopupWindow { ((ViewGroup) mPopupView).removeView(mContentView); } mPopupView = null; - mIsShowing = false; if (mOnDismissListener != null) { mOnDismissListener.onDismiss(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index bc8721a..b106cc5 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.ColorStateList; +import android.content.res.CompatibilityInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; @@ -449,18 +450,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener super(context, attrs, defStyle); mText = ""; + final Resources res = getResources(); + final CompatibilityInfo compat = res.getCompatibilityInfo(); + mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - mTextPaint.density = getResources().getDisplayMetrics().density; - mTextPaint.setCompatibilityScaling( - getResources().getCompatibilityInfo().applicationScale); + mTextPaint.density = res.getDisplayMetrics().density; + mTextPaint.setCompatibilityScaling(compat.applicationScale); // If we get the paint from the skin, we should set it to left, since // the layout always wants it to be left. // mTextPaint.setTextAlign(Paint.Align.LEFT); mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mHighlightPaint.setCompatibilityScaling( - getResources().getCompatibilityInfo().applicationScale); + mHighlightPaint.setCompatibilityScaling(compat.applicationScale); mMovement = getDefaultMovementMethod(); mTransformation = null; diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 4714be8..b689f53 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -537,7 +537,7 @@ public class ActionBarView extends AbsActionBarView { if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) { final boolean showHome = (options & ActionBar.DISPLAY_SHOW_HOME) != 0; - final int vis = showHome ? VISIBLE : GONE; + final int vis = showHome && mExpandedActionView == null ? VISIBLE : GONE; mHomeLayout.setVisibility(vis); if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) { diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index a2fc6e2..0d9cf9a 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -463,7 +463,7 @@ public class LockPatternView extends View { result = desired; break; case MeasureSpec.AT_MOST: - result = Math.min(specSize, desired); + result = Math.max(specSize, desired); break; case MeasureSpec.EXACTLY: default: |
