diff options
| -rw-r--r-- | api/current.txt | 31 | ||||
| -rw-r--r-- | cmds/media/src/com/android/commands/media/Media.java | 5 | ||||
| -rw-r--r-- | media/java/android/media/AudioManager.java | 16 | ||||
| -rw-r--r-- | media/java/android/media/AudioService.java | 18 | ||||
| -rw-r--r-- | media/java/android/media/IAudioService.aidl | 12 | ||||
| -rw-r--r-- | media/java/android/media/IRemoteControlClient.aidl | 1 | ||||
| -rw-r--r-- | media/java/android/media/IRemoteControlDisplay.aidl | 6 | ||||
| -rw-r--r-- | media/java/android/media/MediaFocusControl.java | 222 | ||||
| -rw-r--r-- | media/java/android/media/RemoteControlClient.java | 102 | ||||
| -rw-r--r-- | media/java/android/media/RemoteController.java | 203 | ||||
| -rw-r--r-- | packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java | 3 | ||||
| -rw-r--r-- | packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java | 4 | 
12 files changed, 450 insertions, 173 deletions
| diff --git a/api/current.txt b/api/current.txt index ed0d15496c0b..981eb1f2cbfb 100644 --- a/api/current.txt +++ b/api/current.txt @@ -13352,34 +13352,29 @@ package android.media {    }    public final class RemoteController { -    ctor public RemoteController(android.content.Context) throws java.lang.IllegalArgumentException; -    ctor public RemoteController(android.content.Context, android.os.Looper) throws java.lang.IllegalArgumentException; -    method public int clearArtworkConfiguration(); +    ctor public RemoteController(android.content.Context, android.media.RemoteController.OnClientUpdateListener) throws java.lang.IllegalArgumentException; +    ctor public RemoteController(android.content.Context, android.media.RemoteController.OnClientUpdateListener, android.os.Looper) throws java.lang.IllegalArgumentException; +    method public boolean clearArtworkConfiguration();      method public android.media.RemoteController.MetadataEditor editMetadata();      method public long getEstimatedMediaPosition(); -    method public int seekTo(long); -    method public int sendMediaKeyEvent(android.view.KeyEvent); -    method public int setArtworkConfiguration(int, int); -    method public void setOnClientUpdateListener(android.media.RemoteController.OnClientUpdateListener); -    method public int setSynchronizationMode(int); -    field public static final int ERROR = -1; // 0xffffffff -    field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe +    method public boolean seekTo(long) throws java.lang.IllegalArgumentException; +    method public boolean sendMediaKeyEvent(android.view.KeyEvent) throws java.lang.IllegalArgumentException; +    method public boolean setArtworkConfiguration(int, int) throws java.lang.IllegalArgumentException; +    method public boolean setSynchronizationMode(int) throws java.lang.IllegalArgumentException;      field public static final int POSITION_SYNCHRONIZATION_CHECK = 1; // 0x1      field public static final int POSITION_SYNCHRONIZATION_NONE = 0; // 0x0 -    field public static final int SUCCESS = 0; // 0x0    }    public class RemoteController.MetadataEditor extends android.media.MediaMetadataEditor {      method public synchronized void apply();    } -  public static abstract class RemoteController.OnClientUpdateListener { -    ctor public RemoteController.OnClientUpdateListener(); -    method public void onClientChange(boolean); -    method public void onClientMetadataUpdate(android.media.RemoteController.MetadataEditor); -    method public void onClientPlaybackStateUpdate(int); -    method public void onClientPlaybackStateUpdate(int, long, long, float); -    method public void onClientTransportControlUpdate(int); +  public static abstract interface RemoteController.OnClientUpdateListener { +    method public abstract void onClientChange(boolean); +    method public abstract void onClientMetadataUpdate(android.media.RemoteController.MetadataEditor); +    method public abstract void onClientPlaybackStateUpdate(int); +    method public abstract void onClientPlaybackStateUpdate(int, long, long, float); +    method public abstract void onClientTransportControlUpdate(int);    }    public final class ResourceBusyException extends android.media.MediaDrmException { diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java index 56af7d65eb35..92c6a51e8849 100644 --- a/cmds/media/src/com/android/commands/media/Media.java +++ b/cmds/media/src/com/android/commands/media/Media.java @@ -140,6 +140,11 @@ public class Media extends BaseCommand {          }          @Override +        public void setEnabled(boolean enabled) { +            System.out.println("New enable state= " + (enabled ? "enabled" : "disabled")); +        } + +        @Override          public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,                  long currentPosMs, float speed) {              System.out.println("New state: id=" + generationId + " state=" + state diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index c8ee5ad3f968..680e73a10a1b 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -24,6 +24,7 @@ import android.bluetooth.BluetoothDevice;  import android.content.ComponentName;  import android.content.Context;  import android.content.Intent; +import android.media.RemoteController.OnClientUpdateListener;  import android.os.Binder;  import android.os.Build;  import android.os.Handler; @@ -2270,7 +2271,9 @@ public class AudioManager {       * Registers a {@link RemoteController} instance for it to receive media metadata updates       * and playback state information from applications using {@link RemoteControlClient}, and       * control their playback. -     * <p>Registration requires the {@link Manifest.permission#MEDIA_CONTENT_CONTROL} permission. +     * <p>Registration requires the {@link OnClientUpdateListener} listener to be one of the +     * enabled notification listeners (see +     * {@link android.service.notification.NotificationListenerService}).       * @param rctlr the object to register.       * @return true if the {@link RemoteController} was successfully registered, false if an       *     error occurred, due to an internal system error, or insufficient permissions. @@ -2280,14 +2283,17 @@ public class AudioManager {              return false;          }          IAudioService service = getService(); +        final RemoteController.OnClientUpdateListener l = rctlr.getUpdateListener(); +        final ComponentName listenerComponent = new ComponentName(mContext, l.getClass());          try {              int[] artworkDimensions = rctlr.getArtworkSize(); -            boolean reg = service.registerRemoteControlDisplay(rctlr.getRcDisplay(), -                    artworkDimensions[0]/*w*/, artworkDimensions[1]/*h*/); +            boolean reg = service.registerRemoteController(rctlr.getRcDisplay(), +                    artworkDimensions[0]/*w*/, artworkDimensions[1]/*h*/, +                    listenerComponent);              rctlr.setIsRegistered(reg);              return reg;          } catch (RemoteException e) { -            Log.e(TAG, "Dead object in registerRemoteControlDisplay " + e); +            Log.e(TAG, "Dead object in registerRemoteController " + e);              return false;          }      } @@ -2318,6 +2324,7 @@ public class AudioManager {       * artwork size directly, or       * {@link #remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay, int, int)} later if artwork       * is not yet needed. +     * <p>Registration requires the {@link Manifest.permission#MEDIA_CONTENT_CONTROL} permission.       * @param rcd the IRemoteControlDisplay       */      public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) { @@ -2328,6 +2335,7 @@ public class AudioManager {      /**       * @hide       * Registers a remote control display that will be sent information by remote control clients. +     * <p>Registration requires the {@link Manifest.permission#MEDIA_CONTENT_CONTROL} permission.       * @param rcd       * @param w the maximum width of the expected bitmap. Negative values indicate it is       *   useless to send artwork. diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 84ea4c90d62b..e9d644b92f18 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -48,6 +48,7 @@ import android.content.res.XmlResourceParser;  import android.database.ContentObserver;  import android.media.MediaPlayer.OnCompletionListener;  import android.media.MediaPlayer.OnErrorListener; +import android.net.Uri;  import android.os.Binder;  import android.os.Build;  import android.os.Bundle; @@ -87,6 +88,7 @@ import java.lang.reflect.Field;  import java.util.ArrayList;  import java.util.concurrent.ConcurrentHashMap;  import java.util.HashMap; +import java.util.HashSet;  import java.util.Iterator;  import java.util.List;  import java.util.Map; @@ -4147,17 +4149,13 @@ public class AudioService extends IAudioService.Stub {      //==========================================================================================      // RemoteControlDisplay / RemoteControlClient / Remote info      //========================================================================================== +    public boolean registerRemoteController(IRemoteControlDisplay rcd, int w, int h, +            ComponentName listenerComp) { +        return mMediaFocusControl.registerRemoteController(rcd, w, h, listenerComp); +    } +      public boolean registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) { -        if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( -                android.Manifest.permission.MEDIA_CONTENT_CONTROL)) { -            mMediaFocusControl.registerRemoteControlDisplay(rcd, w, h); -            return true; -        } else { -            Log.w(TAG, "Access denied to process: " + Binder.getCallingPid() + -                    ", must have permission " + android.Manifest.permission.MEDIA_CONTENT_CONTROL + -                    " to register IRemoteControlDisplay"); -            return false; -        } +        return mMediaFocusControl.registerRemoteControlDisplay(rcd, w, h);      }      public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) { diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index e3b87dd87b60..8c05089571e1 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -133,6 +133,8 @@ interface IAudioService {      /**       * Register an IRemoteControlDisplay. +     * Success of registration is subject to a check on +     *   the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission.       * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient       * at the top of the stack to update the new display with its information.       * @param rcd the IRemoteControlDisplay to register. No effect if null. @@ -142,6 +144,16 @@ interface IAudioService {       *   display doesn't need to receive artwork.       */      boolean registerRemoteControlDisplay(in IRemoteControlDisplay rcd, int w, int h); + +    /** +     * Like registerRemoteControlDisplay, but with success being subject to a check on +     *   the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission, and if it fails, +     *   success is subject to listenerComp being one of the ENABLED_NOTIFICATION_LISTENERS +     *   components. +     */ +    boolean registerRemoteController(in IRemoteControlDisplay rcd, int w, int h, +            in ComponentName listenerComp); +      /**       * Unregister an IRemoteControlDisplay.       * No effect if the IRemoteControlDisplay hasn't been successfully registered. diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClient.aidl index 999b8bad7b6b..aa142d6e7da8 100644 --- a/media/java/android/media/IRemoteControlClient.aidl +++ b/media/java/android/media/IRemoteControlClient.aidl @@ -56,6 +56,7 @@ oneway interface IRemoteControlClient      void unplugRemoteControlDisplay(IRemoteControlDisplay rcd);      void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h);      void setWantsSyncForDisplay(IRemoteControlDisplay rcd, boolean wantsSync); +    void enableRemoteControlDisplay(IRemoteControlDisplay rcd, boolean enabled);      void seekTo(int clientGeneration, long timeMs);      void updateMetadata(int clientGeneration, int key, in Rating value);  }
\ No newline at end of file diff --git a/media/java/android/media/IRemoteControlDisplay.aidl b/media/java/android/media/IRemoteControlDisplay.aidl index 583f436690c6..160903048478 100644 --- a/media/java/android/media/IRemoteControlDisplay.aidl +++ b/media/java/android/media/IRemoteControlDisplay.aidl @@ -41,6 +41,12 @@ oneway interface IRemoteControlDisplay              boolean clearing);      /** +     * Sets whether the controls of this display are enabled +     * @param if false, the display shouldn't any commands +     */ +    void setEnabled(boolean enabled); + +    /**       * Sets the playback information (state, position and speed) of a client.       * @param generationId the current generation ID as known by this client       * @param state the current playback state, one of the following values: diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java index 2a7a731cb10b..d18532117acf 100644 --- a/media/java/android/media/MediaFocusControl.java +++ b/media/java/android/media/MediaFocusControl.java @@ -17,6 +17,7 @@  package android.media;  import android.app.Activity; +import android.app.ActivityManager;  import android.app.AppOpsManager;  import android.app.KeyguardManager;  import android.app.PendingIntent; @@ -30,6 +31,8 @@ import android.content.Context;  import android.content.Intent;  import android.content.IntentFilter;  import android.content.pm.PackageManager; +import android.database.ContentObserver; +import android.net.Uri;  import android.os.Binder;  import android.os.Bundle;  import android.os.Handler; @@ -45,6 +48,7 @@ import android.speech.RecognizerIntent;  import android.telephony.PhoneStateListener;  import android.telephony.TelephonyManager;  import android.util.Log; +import android.util.Slog;  import android.view.KeyEvent;  import java.io.FileDescriptor; @@ -78,6 +82,7 @@ public class MediaFocusControl implements OnFinished {      private final AppOpsManager mAppOps;      private final KeyguardManager mKeyguardManager;      private final AudioService mAudioService; +    private final NotificationListenerObserver mNotifListenerObserver;      protected MediaFocusControl(Looper looper, Context cntxt,              VolumeController volumeCtrl, AudioService as) { @@ -111,6 +116,7 @@ public class MediaFocusControl implements OnFinished {          mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);          mKeyguardManager =                  (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); +        mNotifListenerObserver = new NotificationListenerObserver();          mHasRemotePlayback = false;          mMainRemoteIsActive = false; @@ -125,6 +131,182 @@ public class MediaFocusControl implements OnFinished {      }      //========================================================================================== +    // Management of RemoteControlDisplay registration permissions +    //========================================================================================== +    private final static Uri ENABLED_NOTIFICATION_LISTENERS_URI = +            Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); + +    private class NotificationListenerObserver extends ContentObserver { + +        NotificationListenerObserver() { +            super(mEventHandler); +            mContentResolver.registerContentObserver(Settings.Secure.getUriFor( +                    Settings.Secure.ENABLED_NOTIFICATION_LISTENERS), false, this); +        } + +        @Override +        public void onChange(boolean selfChange, Uri uri) { +            if (!ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri) || selfChange) { +                return; +            } +            if (DEBUG_RC) { Log.d(TAG, "NotificationListenerObserver.onChange()"); } +            postReevaluateRemoteControlDisplays(); +        } +    } + +    private final static int RCD_REG_FAILURE = 0; +    private final static int RCD_REG_SUCCESS_PERMISSION = 1; +    private final static int RCD_REG_SUCCESS_ENABLED_NOTIF = 2; + +    /** +     * Checks a caller's authorization to register an IRemoteControlDisplay. +     * Authorization is granted if one of the following is true: +     * <ul> +     * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL permission</li> +     * <li>the caller's listener is one of the enabled notification listeners</li> +     * </ul> +     * @return RCD_REG_FAILURE if it's not safe to proceed with the IRemoteControlDisplay +     *     registration. +     */ +    private int checkRcdRegistrationAuthorization(ComponentName listenerComp) { +        // MEDIA_CONTENT_CONTROL permission check +        if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( +                android.Manifest.permission.MEDIA_CONTENT_CONTROL)) { +            if (DEBUG_RC) { Log.d(TAG, "ok to register Rcd: has MEDIA_CONTENT_CONTROL permission");} +            return RCD_REG_SUCCESS_PERMISSION; +        } + +        // ENABLED_NOTIFICATION_LISTENERS settings check +        if (listenerComp != null) { +            // this call is coming from an app, can't use its identity to read secure settings +            final long ident = Binder.clearCallingIdentity(); +            try { +                final int currentUser = ActivityManager.getCurrentUser(); +                final String enabledNotifListeners = Settings.Secure.getStringForUser( +                        mContext.getContentResolver(), +                        Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, +                        currentUser); +                if (enabledNotifListeners != null) { +                    final String[] components = enabledNotifListeners.split(":"); +                    for (int i=0; i<components.length; i++) { +                        final ComponentName component = +                                ComponentName.unflattenFromString(components[i]); +                        if (component != null) { +                            if (listenerComp.equals(component)) { +                                if (DEBUG_RC) { Log.d(TAG, "ok to register RCC: " + component + +                                        " is authorized notification listener"); } +                                return RCD_REG_SUCCESS_ENABLED_NOTIF; +                            } +                        } +                    } +                } +                if (DEBUG_RC) { Log.d(TAG, "not ok to register RCD, " + listenerComp + +                        " is not in list of ENABLED_NOTIFICATION_LISTENERS"); } +            } finally { +                Binder.restoreCallingIdentity(ident); +            } +        } + +        return RCD_REG_FAILURE; +    } + +    protected boolean registerRemoteController(IRemoteControlDisplay rcd, int w, int h, +            ComponentName listenerComp) { +        int reg = checkRcdRegistrationAuthorization(listenerComp); +        if (reg != RCD_REG_FAILURE) { +            registerRemoteControlDisplay_int(rcd, w, h, listenerComp); +            return true; +        } else { +            Slog.w(TAG, "Access denied to process: " + Binder.getCallingPid() + +                    ", must have permission " + android.Manifest.permission.MEDIA_CONTENT_CONTROL + +                    " or be an enabled NotificationListenerService for registerRemoteController"); +            return false; +        } +    } + +    protected boolean registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) { +        int reg = checkRcdRegistrationAuthorization(null); +        if (reg != RCD_REG_FAILURE) { +            registerRemoteControlDisplay_int(rcd, w, h, null); +            return true; +        } else { +            Slog.w(TAG, "Access denied to process: " + Binder.getCallingPid() + +                    ", must have permission " + android.Manifest.permission.MEDIA_CONTENT_CONTROL + +                    " to register IRemoteControlDisplay"); +            return false; +        } +    } + +    private void postReevaluateRemoteControlDisplays() { +        sendMsg(mEventHandler, MSG_REEVALUATE_RCD, SENDMSG_QUEUE, 0, 0, null, 0); +    } + +    private void onReevaluateRemoteControlDisplays() { +        if (DEBUG_RC) { Log.d(TAG, "onReevaluateRemoteControlDisplays()"); } +        // read which components are enabled notification listeners +        final int currentUser = ActivityManager.getCurrentUser(); +        final String enabledNotifListeners = Settings.Secure.getStringForUser( +                mContext.getContentResolver(), +                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, +                currentUser); +        if (DEBUG_RC) { Log.d(TAG, " > enabled list: " + enabledNotifListeners); } +        synchronized(mAudioFocusLock) { +            synchronized(mRCStack) { +                // check whether the "enable" status of each RCD with a notification listener +                // has changed +                final String[] enabledComponents; +                if (enabledNotifListeners == null) { +                    enabledComponents = null; +                } else { +                    enabledComponents = enabledNotifListeners.split(":"); +                } +                final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); +                while (displayIterator.hasNext()) { +                    final DisplayInfoForServer di = +                            (DisplayInfoForServer) displayIterator.next(); +                    if (di.mClientNotifListComp != null) { +                        boolean wasEnabled = di.mEnabled; +                        di.mEnabled = isComponentInStringArray(di.mClientNotifListComp, +                                enabledComponents); +                        if (wasEnabled != di.mEnabled){ +                            try { +                                // tell the RCD whether it's enabled +                                di.mRcDisplay.setEnabled(di.mEnabled); +                                // tell the RCCs about the change for this RCD +                                enableRemoteControlDisplayForClient_syncRcStack( +                                        di.mRcDisplay, di.mEnabled); +                            } catch (RemoteException e) { +                                Log.e(TAG, "Error en/disabling RCD: ", e); +                            } +                        } +                    } +                } +            } +        } +    } + +    /** +     * @param comp a non-null ComponentName +     * @param enabledArray may be null +     * @return +     */ +    private boolean isComponentInStringArray(ComponentName comp, String[] enabledArray) { +        if (enabledArray == null || enabledArray.length == 0) { +            if (DEBUG_RC) { Log.d(TAG, " > " + comp + " is NOT enabled"); } +            return false; +        } +        final String compString = comp.flattenToString(); +        for (int i=0; i<enabledArray.length; i++) { +            if (compString.equals(enabledArray[i])) { +                if (DEBUG_RC) { Log.d(TAG, " > " + compString + " is enabled"); } +                return true; +            } +        } +        if (DEBUG_RC) { Log.d(TAG, " > " + compString + " is NOT enabled"); } +        return false; +    } + +    //==========================================================================================      // Internal event handling      //========================================================================================== @@ -140,6 +322,7 @@ public class MediaFocusControl implements OnFinished {      private static final int MSG_RCC_SEEK_REQUEST = 8;      private static final int MSG_RCC_UPDATE_METADATA = 9;      private static final int MSG_RCDISPLAY_INIT_INFO = 10; +    private static final int MSG_REEVALUATE_RCD = 11;      // sendMsg() flags      /** If the msg is already queued, replace it with this one. */ @@ -221,6 +404,10 @@ public class MediaFocusControl implements OnFinished {                      onRcDisplayInitInfo((IRemoteControlDisplay)msg.obj /*newRcd*/,                              msg.arg1/*w*/, msg.arg2/*h*/);                      break; + +                case MSG_REEVALUATE_RCD: +                    onReevaluateRemoteControlDisplays(); +                    break;              }          }      } @@ -1193,8 +1380,9 @@ public class MediaFocusControl implements OnFinished {                  final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();                  pw.println("  IRCD: " + di.mRcDisplay +                          "  -- w:" + di.mArtworkExpectedWidth + -                        "  -- h:" + di.mArtworkExpectedHeight+ -                        "  -- wantsPosSync:" + di.mWantsPositionSync); +                        "  -- h:" + di.mArtworkExpectedHeight + +                        "  -- wantsPosSync:" + di.mWantsPositionSync + +                        "  -- " + (di.mEnabled ? "enabled" : "disabled"));              }          }      } @@ -1834,11 +2022,13 @@ public class MediaFocusControl implements OnFinished {       */      private class DisplayInfoForServer implements IBinder.DeathRecipient {          /** may never be null */ -        private IRemoteControlDisplay mRcDisplay; -        private IBinder mRcDisplayBinder; +        private final IRemoteControlDisplay mRcDisplay; +        private final IBinder mRcDisplayBinder;          private int mArtworkExpectedWidth = -1;          private int mArtworkExpectedHeight = -1;          private boolean mWantsPositionSync = false; +        private ComponentName mClientNotifListComp; +        private boolean mEnabled = true;          public DisplayInfoForServer(IRemoteControlDisplay rcd, int w, int h) {              if (DEBUG_RC) Log.i(TAG, "new DisplayInfoForServer for " + rcd + " w=" + w + " h=" + h); @@ -1911,6 +2101,23 @@ public class MediaFocusControl implements OnFinished {          }      } +    private void enableRemoteControlDisplayForClient_syncRcStack(IRemoteControlDisplay rcd, +            boolean enabled) { +        // let all the remote control clients know whether the given display is enabled +        //   (so the remote control stack traversal order doesn't matter). +        final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); +        while(stackIterator.hasNext()) { +            RemoteControlStackEntry rcse = stackIterator.next(); +            if(rcse.mRcClient != null) { +                try { +                    rcse.mRcClient.enableRemoteControlDisplay(rcd, enabled); +                } catch (RemoteException e) { +                    Log.e(TAG, "Error connecting RCD to client: ", e); +                } +            } +        } +    } +      /**       * Is the remote control display interface already registered       * @param rcd @@ -1937,8 +2144,11 @@ public class MediaFocusControl implements OnFinished {       *   display doesn't need to receive artwork.       * @param h the maximum height of the expected bitmap. Negative or zero values indicate this       *   display doesn't need to receive artwork. +     * @param listenerComp the component for the listener interface, may be null if it's not needed +     *   to verify it belongs to one of the enabled notification listeners       */ -    protected void registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) { +    private void registerRemoteControlDisplay_int(IRemoteControlDisplay rcd, int w, int h, +            ComponentName listenerComp) {          if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");          synchronized(mAudioFocusLock) {              synchronized(mRCStack) { @@ -1946,6 +2156,8 @@ public class MediaFocusControl implements OnFinished {                      return;                  }                  DisplayInfoForServer di = new DisplayInfoForServer(rcd, w, h); +                di.mEnabled = true; +                di.mClientNotifListComp = listenerComp;                  if (!di.init()) {                      if (DEBUG_RC) Log.e(TAG, " error registering RCD");                      return; diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java index 497b8b3695b6..0c00abaa93ff 100644 --- a/media/java/android/media/RemoteControlClient.java +++ b/media/java/android/media/RemoteControlClient.java @@ -1049,6 +1049,7 @@ public class RemoteControlClient          private int mArtworkExpectedWidth;          private int mArtworkExpectedHeight;          private boolean mWantsPositionSync = false; +        private boolean mEnabled = true;          DisplayInfoForClient(IRemoteControlDisplay rcd, int w, int h) {              mRcDisplay = rcd; @@ -1165,6 +1166,14 @@ public class RemoteControlClient              }          } +        public void enableRemoteControlDisplay(IRemoteControlDisplay rcd, boolean enabled) { +            // only post messages, we can't block here +            if ((mEventHandler != null) && (rcd != null)) { +                mEventHandler.sendMessage(mEventHandler.obtainMessage( +                        MSG_DISPLAY_ENABLE, enabled ? 1 : 0, 0/*arg2 ignored*/, rcd)); +            } +        } +          public void seekTo(int generationId, long timeMs) {              // only post messages, we can't block here              if (mEventHandler != null) { @@ -1227,6 +1236,7 @@ public class RemoteControlClient      private final static int MSG_DISPLAY_WANTS_POS_SYNC = 12;      private final static int MSG_UPDATE_METADATA = 13;      private final static int MSG_REQUEST_METADATA_ARTWORK = 14; +    private final static int MSG_DISPLAY_ENABLE = 15;      private class EventHandler extends Handler {          public EventHandler(RemoteControlClient rcc, Looper looper) { @@ -1290,6 +1300,9 @@ public class RemoteControlClient                  case MSG_UPDATE_METADATA:                      onUpdateMetadata(msg.arg1, msg.arg2, msg.obj);                      break; +                case MSG_DISPLAY_ENABLE: +                    onDisplayEnable((IRemoteControlDisplay)msg.obj, msg.arg1 == 1); +                    break;                  default:                      Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");              } @@ -1315,13 +1328,15 @@ public class RemoteControlClient              final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();              while (displayIterator.hasNext()) {                  final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next(); -                try { -                    di.mRcDisplay.setPlaybackState(mInternalClientGenId, -                            mPlaybackState, mPlaybackStateChangeTimeMs, mPlaybackPositionMs, -                            mPlaybackSpeed); -                } catch (RemoteException e) { -                    Log.e(TAG, "Error in setPlaybackState(), dead display " + di.mRcDisplay, e); -                    displayIterator.remove(); +                if (di.mEnabled) { +                    try { +                        di.mRcDisplay.setPlaybackState(mInternalClientGenId, +                                mPlaybackState, mPlaybackStateChangeTimeMs, mPlaybackPositionMs, +                                mPlaybackSpeed); +                    } catch (RemoteException e) { +                        Log.e(TAG, "Error in setPlaybackState(), dead display " + di.mRcDisplay, e); +                        displayIterator.remove(); +                    }                  }              }          } @@ -1341,11 +1356,13 @@ public class RemoteControlClient              final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();              while (displayIterator.hasNext()) {                  final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next(); -                try { -                    di.mRcDisplay.setMetadata(mInternalClientGenId, mMetadata); -                } catch (RemoteException e) { -                    Log.e(TAG, "Error in setMetadata(), dead display " + di.mRcDisplay, e); -                    displayIterator.remove(); +                if (di.mEnabled) { +                    try { +                        di.mRcDisplay.setMetadata(mInternalClientGenId, mMetadata); +                    } catch (RemoteException e) { +                        Log.e(TAG, "Error in setMetadata(), dead display " + di.mRcDisplay, e); +                        displayIterator.remove(); +                    }                  }              }          } @@ -1367,13 +1384,15 @@ public class RemoteControlClient              final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();              while (displayIterator.hasNext()) {                  final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next(); -                try { -                    di.mRcDisplay.setTransportControlInfo(mInternalClientGenId, -                            mTransportControlFlags, mPlaybackPositionCapabilities); -                } catch (RemoteException e) { -                    Log.e(TAG, "Error in setTransportControlFlags(), dead display " + di.mRcDisplay, -                            e); -                    displayIterator.remove(); +                if (di.mEnabled) { +                    try { +                        di.mRcDisplay.setTransportControlInfo(mInternalClientGenId, +                                mTransportControlFlags, mPlaybackPositionCapabilities); +                    } catch (RemoteException e) { +                        Log.e(TAG, "Error in setTransportControlFlags(), dead display " + di.mRcDisplay, +                                e); +                        displayIterator.remove(); +                    }                  }              }          } @@ -1438,12 +1457,14 @@ public class RemoteControlClient              while (displayIterator.hasNext()) {                  final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();                  try { -                    if ((di.mArtworkExpectedWidth > 0) && (di.mArtworkExpectedHeight > 0)) { -                        Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork, -                                di.mArtworkExpectedWidth, di.mArtworkExpectedHeight); -                        di.mRcDisplay.setAllMetadata(mInternalClientGenId, mMetadata, artwork); -                    } else { -                        di.mRcDisplay.setMetadata(mInternalClientGenId, mMetadata); +                    if (di.mEnabled) { +                        if ((di.mArtworkExpectedWidth > 0) && (di.mArtworkExpectedHeight > 0)) { +                            Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork, +                                    di.mArtworkExpectedWidth, di.mArtworkExpectedHeight); +                            di.mRcDisplay.setAllMetadata(mInternalClientGenId, mMetadata, artwork); +                        } else { +                            di.mRcDisplay.setMetadata(mInternalClientGenId, mMetadata); +                        }                      }                  } catch (RemoteException e) {                      Log.e(TAG, "Error when setting metadata, dead display " + di.mRcDisplay, e); @@ -1578,8 +1599,10 @@ public class RemoteControlClient                          ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h))) {                      di.mArtworkExpectedWidth = w;                      di.mArtworkExpectedHeight = h; -                    if (!sendArtworkToDisplay(di)) { -                        displayIterator.remove(); +                    if (di.mEnabled) { +                        if (!sendArtworkToDisplay(di)) { +                            displayIterator.remove(); +                        }                      }                      break;                  } @@ -1597,11 +1620,13 @@ public class RemoteControlClient              //  that gets upated, and whether the list has one entry that wants position sync              while (displayIterator.hasNext()) {                  final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next(); -                if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) { -                    di.mWantsPositionSync = wantsSync; -                } -                if (di.mWantsPositionSync) { -                    newNeedsPositionSync = true; +                if (di.mEnabled) { +                    if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) { +                        di.mWantsPositionSync = wantsSync; +                    } +                    if (di.mWantsPositionSync) { +                        newNeedsPositionSync = true; +                    }                  }              }              mNeedsPositionSync = newNeedsPositionSync; @@ -1612,6 +1637,19 @@ public class RemoteControlClient          }      } +    /** pre-condition rcd != null */ +    private void onDisplayEnable(IRemoteControlDisplay rcd, boolean enable) { +        synchronized(mCacheLock) { +            final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); +            while (displayIterator.hasNext()) { +                final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next(); +                if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) { +                    di.mEnabled = enable; +                } +            } +        } +    } +      private void onSeekTo(int generationId, long timeMs) {          synchronized (mCacheLock) {              if ((mCurrentClientGenId == generationId) && (mPositionUpdateListener != null)) { diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java index 22f63435376b..c6d629615f89 100644 --- a/media/java/android/media/RemoteController.java +++ b/media/java/android/media/RemoteController.java @@ -42,14 +42,14 @@ import android.view.KeyEvent;   * <p>   * A RemoteController shall be registered through   * {@link AudioManager#registerRemoteController(RemoteController)} in order for the system to send - * media event updates to the listener set in - * {@link #setOnClientUpdateListener(OnClientUpdateListener)}. This listener is a subclass of - * the {@link OnClientUpdateListener} abstract class. Override its methods to receive the - * information published by the active {@link RemoteControlClient} instances. - * By default an {@link OnClientUpdateListener} implementation will not receive bitmaps for album - * art. Use {@link #setArtworkConfiguration(int, int)} to receive images as well. + * media event updates to the {@link OnClientUpdateListener} listener set in the class constructor. + * Implement the methods of the interface to receive the information published by the active + * {@link RemoteControlClient} instances. + * <br>By default an {@link OnClientUpdateListener} implementation will not receive bitmaps for + * album art. Use {@link #setArtworkConfiguration(int, int)} to receive images as well.   * <p> - * Registration requires the {@link Manifest.permission#MEDIA_CONTENT_CONTROL} permission. + * Registration requires the {@link OnClientUpdateListener} listener to be one of the enabled + * notification listeners (see {@link android.service.notification.NotificationListenerService}).   */  public final class RemoteController  { @@ -77,30 +77,39 @@ public final class RemoteController      private PendingIntent mClientPendingIntentCurrent;      private OnClientUpdateListener mOnClientUpdateListener;      private PlaybackInfo mLastPlaybackInfo; -    private int mLastTransportControlFlags = TRANSPORT_UNKNOWN;      private int mArtworkWidth = -1;      private int mArtworkHeight = -1; +    private boolean mEnabled = true;      /**       * Class constructor. -     * @param context non-null the {@link Context}, must be non-null -     * @throws java.lang.IllegalArgumentException +     * @param context the {@link Context}, must be non-null. +     * @param updateListener the listener to be called whenever new client information is available, +     *     must be non-null. +     * @throws IllegalArgumentException       */ -    public RemoteController(Context context) throws IllegalArgumentException { -        this(context, null); +    public RemoteController(Context context, OnClientUpdateListener updateListener) +            throws IllegalArgumentException { +        this(context, updateListener, null);      }      /**       * Class constructor. +     * @param context the {@link Context}, must be non-null. +     * @param updateListener the listener to be called whenever new client information is available, +     *     must be non-null.       * @param looper the {@link Looper} on which to run the event loop,       *     or null to use the current thread's looper. -     * @param context the {@link Context}, must be non-null       * @throws java.lang.IllegalArgumentException       */ -    public RemoteController(Context context, Looper looper) throws IllegalArgumentException { +    public RemoteController(Context context, OnClientUpdateListener updateListener, Looper looper) +            throws IllegalArgumentException {          if (context == null) {              throw new IllegalArgumentException("Invalid null Context");          } +        if (updateListener == null) { +            throw new IllegalArgumentException("Invalid null OnClientUpdateListener"); +        }          if (looper != null) {              mEventHandler = new EventHandler(this, looper);          } else { @@ -111,6 +120,7 @@ public final class RemoteController                  throw new IllegalArgumentException("Calling thread not associated with a looper");              }          } +        mOnClientUpdateListener = updateListener;          mContext = context;          mRcd = new RcDisplay();          mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); @@ -125,31 +135,31 @@ public final class RemoteController      /** -     * An abstract class definition for the callbacks to be invoked whenever media events, metadata +     * Interface definition for the callbacks to be invoked whenever media events, metadata       * and playback status are available.       */ -    public static abstract class OnClientUpdateListener { +    public interface OnClientUpdateListener {          /** -         * The method called whenever all information previously received through the other +         * Called whenever all information, previously received through the other           * methods of the listener, is no longer valid and is about to be refreshed.           * This is typically called whenever a new {@link RemoteControlClient} has been selected           * by the system to have its media information published.           * @param clearing true if there is no selected RemoteControlClient and no information           *     is available.           */ -        public void onClientChange(boolean clearing) { } +        public void onClientChange(boolean clearing);          /** -         * The method called whenever the playback state has changed. +         * Called whenever the playback state has changed.           * It is called when no information is known about the playback progress in the media and           * the playback speed.           * @param state one of the playback states authorized           *     in {@link RemoteControlClient#setPlaybackState(int)}.           */ -        public void onClientPlaybackStateUpdate(int state) { } +        public void onClientPlaybackStateUpdate(int state);          /** -         * The method called whenever the playback state has changed, and playback position and -         * speed are known. +         * Called whenever the playback state has changed, and playback position +         * and speed are known.           * @param state one of the playback states authorized           *     in {@link RemoteControlClient#setPlaybackState(int)}.           * @param stateChangeTimeMs the system time at which the state change was reported, @@ -161,15 +171,15 @@ public final class RemoteController           *    playing (e.g. when state is {@link RemoteControlClient#PLAYSTATE_ERROR}).           */          public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs, -                long currentPosMs, float speed) { } +                long currentPosMs, float speed);          /** -         * The method called whenever the transport control flags have changed. +         * Called whenever the transport control flags have changed.           * @param transportControlFlags one of the flags authorized           *     in {@link RemoteControlClient#setTransportControlFlags(int)}.           */ -        public void onClientTransportControlUpdate(int transportControlFlags) { } +        public void onClientTransportControlUpdate(int transportControlFlags);          /** -         * The method called whenever new metadata is available. +         * Called whenever new metadata is available.           * See the {@link MediaMetadataEditor#putLong(int, long)},           *  {@link MediaMetadataEditor#putString(int, String)},           *  {@link MediaMetadataEditor#putBitmap(int, Bitmap)}, and @@ -177,39 +187,9 @@ public final class RemoteController           *  can be queried.           * @param metadataEditor the container of the new metadata.           */ -        public void onClientMetadataUpdate(MetadataEditor metadataEditor) { } +        public void onClientMetadataUpdate(MetadataEditor metadataEditor);      }; -    /** -     * Sets the listener to be called whenever new client information is available. -     * This method can only be called on a registered RemoteController. -     * @param l the update listener to be called. -     */ -    public void setOnClientUpdateListener(OnClientUpdateListener l) { -        synchronized(mInfoLock) { -            mOnClientUpdateListener = l; -            if (!mIsRegistered) { -                // since the object is not registered, it hasn't received any information from -                // RemoteControlClients yet, so we can exit here. -                return; -            } -            if (mLastPlaybackInfo != null) { -                sendMsg(mEventHandler, MSG_NEW_PLAYBACK_INFO, SENDMSG_REPLACE, -                        mClientGenerationIdCurrent /*arg1*/, 0, -                        mLastPlaybackInfo /*obj*/, 0 /*delay*/); -            } -            if (mLastTransportControlFlags != TRANSPORT_UNKNOWN) { -                sendMsg(mEventHandler, MSG_NEW_TRANSPORT_INFO, SENDMSG_REPLACE, -                        mClientGenerationIdCurrent /*arg1*/, mLastTransportControlFlags /*arg2*/, -                        null /*obj*/, 0 /*delay*/); -            } -            if (mMetadataEditor != null) { -                sendMsg(mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE, -                        mClientGenerationIdCurrent /*arg1*/, 0 /*arg2*/, -                        mMetadataEditor /*obj*/, 0 /*delay*/); -            } -        } -    }      /**       * @hide @@ -256,6 +236,7 @@ public final class RemoteController          return -1;      } +      /**       * Send a simulated key event for a media button to be received by the current client.       * To simulate a key press, you must first send a KeyEvent built with @@ -280,17 +261,22 @@ public final class RemoteController       *     {@link KeyEvent#KEYCODE_MEDIA_CLOSE},       *     {@link KeyEvent#KEYCODE_MEDIA_EJECT},       *     or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}. +     * @return true if the event was successfully sent, false otherwise. +     * @throws IllegalArgumentException       */ -    public int sendMediaKeyEvent(KeyEvent keyEvent) { +    public boolean sendMediaKeyEvent(KeyEvent keyEvent) throws IllegalArgumentException {          if (!MediaFocusControl.isMediaKeyCode(keyEvent.getKeyCode())) { -            Log.e(TAG, "Cannot use sendMediaKeyEvent() for a non-media key event"); -            return ERROR_BAD_VALUE; +            throw new IllegalArgumentException("not a media key event");          }          final PendingIntent pi;          synchronized(mInfoLock) {              if (!mIsRegistered) {                  Log.e(TAG, "Cannot use sendMediaKeyEvent() from an unregistered RemoteController"); -                return ERROR; +                return false; +            } +            if (!mEnabled) { +                Log.e(TAG, "Cannot use sendMediaKeyEvent() from a disabled RemoteController"); +                return false;              }              pi = mClientPendingIntentCurrent;          } @@ -301,47 +287,37 @@ public final class RemoteController                  pi.send(mContext, 0, intent);              } catch (CanceledException e) {                  Log.e(TAG, "Error sending intent for media button down: ", e); -                return ERROR; +                return false;              }          } else {              Log.i(TAG, "No-op when sending key click, no receiver right now"); -            return ERROR; +            return false;          } -        return SUCCESS; +        return true;      } -    // Error codes -    /** -     * Successful operation. -     */ -    public  static final int SUCCESS            = 0; -    /** -     * Unspecified error. -     */ -    public  static final int ERROR              = -1; -    /** -     * Operation failed due to bad parameter value. -     */ -    public  static final int ERROR_BAD_VALUE    = -2; - -      /**       * Sets the new playback position.       * This method can only be called on a registered RemoteController.       * @param timeMs a 0 or positive value for the new playback position, expressed in ms. -     * @return {@link #SUCCESS}, {@link #ERROR} or {@link #ERROR_BAD_VALUE} +     * @return true if the command to set the playback position was successfully sent. +     * @throws IllegalArgumentException       */ -    public int seekTo(long timeMs) { +    public boolean seekTo(long timeMs) throws IllegalArgumentException { +        if (!mEnabled) { +            Log.e(TAG, "Cannot use seekTo() from a disabled RemoteController"); +            return false; +        }          if (timeMs < 0) { -            return ERROR_BAD_VALUE; +            throw new IllegalArgumentException("illegal negative time value");          }          final int genId;          synchronized (mGenLock) {              genId = mClientGenerationIdCurrent;          }          mAudioManager.setRemoteControlClientPlaybackPosition(genId, timeMs); -        return SUCCESS; +        return true;      } @@ -350,9 +326,11 @@ public final class RemoteController       * @param wantBitmap       * @param width       * @param height -     * @return {@link #SUCCESS}, {@link #ERROR} or {@link #ERROR_BAD_VALUE} +     * @return true if successful +     * @throws IllegalArgumentException       */ -    public int setArtworkConfiguration(boolean wantBitmap, int width, int height) { +    public boolean setArtworkConfiguration(boolean wantBitmap, int width, int height) +            throws IllegalArgumentException {          synchronized (mInfoLock) {              if (wantBitmap) {                  if ((width > 0) && (height > 0)) { @@ -361,8 +339,7 @@ public final class RemoteController                      mArtworkWidth = width;                      mArtworkHeight = height;                  } else { -                    Log.e(TAG, "Invalid dimensions"); -                    return ERROR_BAD_VALUE; +                    throw new IllegalArgumentException("Invalid dimensions");                  }              } else {                  mArtworkWidth = -1; @@ -375,7 +352,7 @@ public final class RemoteController                //    RemoteController.getArtworkSize() when AudioManager.registerRemoteController()                //    is called.          } -        return SUCCESS; +        return true;      }      /** @@ -383,17 +360,18 @@ public final class RemoteController       * No bitmaps will be received unless this has been specified.       * @param width the maximum width in pixels       * @param height  the maximum height in pixels -     * @return {@link #SUCCESS}, {@link #ERROR} or {@link #ERROR_BAD_VALUE} +     * @return true if the artwork dimension was successfully set. +     * @throws IllegalArgumentException       */ -    public int setArtworkConfiguration(int width, int height) { +    public boolean setArtworkConfiguration(int width, int height) throws IllegalArgumentException {          return setArtworkConfiguration(true, width, height);      }      /**       * Prevents this RemoteController from receiving artwork images. -     * @return {@link #SUCCESS}, {@link #ERROR} +     * @return true if receiving artwork images was successfully disabled.       */ -    public int clearArtworkConfiguration() { +    public boolean clearArtworkConfiguration() {          return setArtworkConfiguration(false, -1, -1);      } @@ -420,20 +398,20 @@ public final class RemoteController       * Set the playback position synchronization mode.       * Must be called on a registered RemoteController.       * @param sync {@link #POSITION_SYNCHRONIZATION_NONE} or {@link #POSITION_SYNCHRONIZATION_CHECK} -     * @return {@link #SUCCESS}, {@link #ERROR} or {@link #ERROR_BAD_VALUE} +     * @return true if the synchronization mode was successfully set. +     * @throws IllegalArgumentException       */ -    public int setSynchronizationMode(int sync) { +    public boolean setSynchronizationMode(int sync) throws IllegalArgumentException {          if ((sync != POSITION_SYNCHRONIZATION_NONE) || (sync != POSITION_SYNCHRONIZATION_CHECK)) { -            Log.e(TAG, "Unknown synchronization mode"); -            return ERROR_BAD_VALUE; +            throw new IllegalArgumentException("Unknown synchronization mode " + sync);          }          if (!mIsRegistered) {              Log.e(TAG, "Cannot set synchronization mode on an unregistered RemoteController"); -            return ERROR; +            return false;          }          mAudioManager.remoteControlDisplayWantsPlaybackPositionSync(mRcd,                  POSITION_SYNCHRONIZATION_CHECK == sync); -        return SUCCESS; +        return true;      } @@ -548,6 +526,11 @@ public final class RemoteController              }          } +        public void setEnabled(boolean enabled) { +            sendMsg(mEventHandler, MSG_DISPLAY_ENABLE, SENDMSG_REPLACE, +                    enabled ? 1 : 0 /*arg1*/, 0, null /*obj*/, 0 /*delay*/); +        } +          public void setPlaybackState(int genId, int state,                  long stateChangeTimeMs, long currentPosMs, float speed) {              if (DEBUG) { @@ -642,6 +625,7 @@ public final class RemoteController      private final static int MSG_NEW_TRANSPORT_INFO = 2;      private final static int MSG_NEW_METADATA       = 3; // msg always has non-null obj parameter      private final static int MSG_CLIENT_CHANGE      = 4; +    private final static int MSG_DISPLAY_ENABLE     = 5;      private class EventHandler extends Handler { @@ -667,6 +651,9 @@ public final class RemoteController                  case MSG_CLIENT_CHANGE:                      onClientChange(msg.arg1, msg.arg2 == 1);                      break; +                case MSG_DISPLAY_ENABLE: +                    onDisplayEnable(msg.arg1 == 1); +                    break;                  default:                      Log.e(TAG, "unknown event " + msg.what);              } @@ -735,7 +722,6 @@ public final class RemoteController          final OnClientUpdateListener l;          synchronized(mInfoLock) {              l = mOnClientUpdateListener; -            mLastTransportControlFlags = transportControlFlags;          }          if (l != null) {              l.onClientTransportControlUpdate(transportControlFlags); @@ -797,6 +783,11 @@ public final class RemoteController          }      } +    private void onDisplayEnable(boolean enabled) { +        synchronized(mInfoLock) { +            mEnabled = enabled; +        } +    }      //==================================================      private static class PlaybackInfo { @@ -818,7 +809,7 @@ public final class RemoteController       * Used by AudioManager to mark this instance as registered.       * @param registered       */ -    protected void setIsRegistered(boolean registered) { +    void setIsRegistered(boolean registered) {          synchronized (mInfoLock) {              mIsRegistered = registered;          } @@ -829,7 +820,7 @@ public final class RemoteController       * Used by AudioManager to access binder to be registered/unregistered inside MediaFocusControl       * @return       */ -    protected RcDisplay getRcDisplay() { +    RcDisplay getRcDisplay() {          return mRcd;      } @@ -838,11 +829,19 @@ public final class RemoteController       * Used by AudioManager to read the current artwork dimension       * @return array containing width (index 0) and height (index 1) of currently set artwork size       */ -    protected int[] getArtworkSize() { +    int[] getArtworkSize() {          synchronized (mInfoLock) {              int[] size = { mArtworkWidth, mArtworkHeight };              return size;          }      } +    /** +     * @hide +     * Used by AudioManager to access user listener receiving the client update notifications +     * @return +     */ +    OnClientUpdateListener getUpdateListener() { +        return mOnClientUpdateListener; +    }  } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java index 83d8ab127435..e8c4c237013b 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java @@ -203,8 +203,7 @@ public class KeyguardTransportControlView extends FrameLayout {          if (DEBUG) Log.v(TAG, "Create TCV " + this);          mAudioManager = new AudioManager(mContext);          mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback -        mRemoteController = new RemoteController(context); -        mRemoteController.setOnClientUpdateListener(mRCClientUpdateListener); +        mRemoteController = new RemoteController(context, mRCClientUpdateListener);          final DisplayMetrics dm = context.getResources().getDisplayMetrics();          final int dim = Math.max(dm.widthPixels, dm.heightPixels); diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index 734f51714be6..76deb77656c8 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -232,6 +232,10 @@ public class KeyguardUpdateMonitor {          } +        public void setEnabled(boolean enabled) { +            // no-op: this RemoteControlDisplay is not subject to being disabled. +        } +          public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,                  boolean clearing) throws RemoteException {              Message msg = mHandler.obtainMessage(MSG_SET_CURRENT_CLIENT_ID, |