Remove some more old code and fix Media command

Removes some more hidden apis from AudioService/Manager. This also
fixes up Media.java to support commands for the new service to help
with debugging. Also fixes a couple bugs that were found while fixing
up Media.

Change-Id: I68e4aa80a4de430b98236aafc883664b9432c62b
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index 92c6a51..4e91361 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -17,12 +17,19 @@
 
 package com.android.commands.media;
 
-import android.app.PendingIntent;
+import android.app.ActivityManager;
 import android.content.Context;
-import android.graphics.Bitmap;
-import android.media.IAudioService;
-import android.media.IRemoteControlDisplay;
+import android.media.MediaMetadata;
+import android.media.session.ISessionController;
+import android.media.session.ISessionManager;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionInfo;
+import android.media.session.PlaybackState;
+import android.media.session.RouteInfo;
 import android.os.Bundle;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -30,16 +37,17 @@
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
+
 import com.android.internal.os.BaseCommand;
 
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintStream;
+import java.util.List;
 
 public class Media extends BaseCommand {
-
-    private IAudioService mAudioService;
+    private ISessionManager mSessionService;
 
     /**
      * Command-line entry point.
@@ -54,29 +62,35 @@
         out.println(
                 "usage: media [subcommand] [options]\n" +
                 "       media dispatch KEY\n" +
-                "       media remote-display\n" +
+                "       media list-sessions\n" +
+                "       media monitor <sessionId>\n" +
                 "\n" +
-                "media dispatch: dispatch a media key to the current media client.\n" +
+                "media dispatch: dispatch a media key to the system.\n" +
                 "                KEY may be: play, pause, play-pause, mute, headsethook,\n" +
-                "                stop, next, previous, rewind, recordm fast-forword.\n" +
-                "media remote-display: monitor remote display updates.\n"
+                "                stop, next, previous, rewind, record, fast-forword.\n" +
+                "media list-sessions: print a list of the current sessions.\n" +
+                        "media monitor: monitor updates to the specified session.\n" +
+                "                       Use the sessionId from list-sessions.\n"
         );
     }
 
     public void onRun() throws Exception {
-        mAudioService = IAudioService.Stub.asInterface(ServiceManager.checkService(
-                Context.AUDIO_SERVICE));
-        if (mAudioService == null) {
+        mSessionService = ISessionManager.Stub.asInterface(ServiceManager.checkService(
+                Context.MEDIA_SESSION_SERVICE));
+        if (mSessionService == null) {
             System.err.println(NO_SYSTEM_ERROR_CODE);
-            throw new AndroidException("Can't connect to audio service; is the system running?");
+            throw new AndroidException(
+                    "Can't connect to media session service; is the system running?");
         }
 
         String op = nextArgRequired();
 
         if (op.equals("dispatch")) {
             runDispatch();
-        } else if (op.equals("remote-display")) {
-            runRemoteDisplay();
+        } else if (op.equals("list-sessions")) {
+            runListSessions();
+        } else if (op.equals("monitor")) {
+            runMonitor();
         } else {
             showError("Error: unknown command '" + op + "'");
             return;
@@ -85,11 +99,39 @@
 
     private void sendMediaKey(KeyEvent event) {
         try {
-            mAudioService.dispatchMediaKeyEvent(event);
+            mSessionService.dispatchMediaKeyEvent(event, false);
         } catch (RemoteException e) {
         }
     }
 
+    private void runMonitor() throws Exception {
+        String id = nextArgRequired();
+        if (id == null) {
+            showError("Error: must include a session id");
+            return;
+        }
+        boolean success = false;
+        try {
+            List<IBinder> sessions = mSessionService
+                    .getSessions(null, ActivityManager.getCurrentUser());
+            for (IBinder session : sessions) {
+                MediaController controller = MediaController.fromBinder(ISessionController.Stub
+                        .asInterface(session));
+                if (controller != null && controller.getSessionInfo().getId().equals(id)) {
+                    ControllerMonitor monitor = new ControllerMonitor(controller);
+                    monitor.run();
+                    success = true;
+                    break;
+                }
+            }
+        } catch (Exception e) {
+            System.out.println("***Error monitoring session*** " + e.getMessage());
+        }
+        if (!success) {
+            System.out.println("No session found with id " + id);
+        }
+    }
+
     private void runDispatch() throws Exception {
         String cmd = nextArgRequired();
         int keycode;
@@ -127,65 +169,49 @@
                 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
     }
 
-    class RemoteDisplayMonitor extends IRemoteControlDisplay.Stub {
-        RemoteDisplayMonitor() {
+    class ControllerMonitor extends MediaController.Callback {
+        private final MediaController mController;
+
+        public ControllerMonitor(MediaController controller) {
+            mController = controller;
         }
-
-
         @Override
-        public void setCurrentClientId(int clientGeneration, PendingIntent clientMediaIntent,
-                boolean clearing) {
-            System.out.println("New client: id=" + clientGeneration
-                    + " intent=" + clientMediaIntent + " clearing=" + clearing);
+        public void onSessionEvent(String event, Bundle extras) {
+            System.out.println("onSessionEvent event=" + event + ", extras=" + extras);
         }
 
         @Override
-        public void setEnabled(boolean enabled) {
-            System.out.println("New enable state= " + (enabled ? "enabled" : "disabled"));
+        public void onRouteChanged(RouteInfo route) {
+            System.out.println("onRouteChanged " + route);
         }
 
         @Override
-        public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
-                long currentPosMs, float speed) {
-            System.out.println("New state: id=" + generationId + " state=" + state
-                    + " time=" + stateChangeTimeMs + " pos=" + currentPosMs + " speed=" + speed);
+        public void onPlaybackStateChanged(PlaybackState state) {
+            System.out.println("onPlaybackStateChanged " + state);
         }
 
         @Override
-        public void setTransportControlInfo(int generationId, int transportControlFlags,
-                int posCapabilities) {
-            System.out.println("New control info: id=" + generationId
-                    + " flags=0x" + Integer.toHexString(transportControlFlags)
-                    + " cap=0x" + Integer.toHexString(posCapabilities));
-        }
-
-        @Override
-        public void setMetadata(int generationId, Bundle metadata) {
-            System.out.println("New metadata: id=" + generationId
-                    + " data=" + metadata);
-        }
-
-        @Override
-        public void setArtwork(int generationId, Bitmap artwork) {
-            System.out.println("New artwork: id=" + generationId
-                    + " art=" + artwork);
-        }
-
-        @Override
-        public void setAllMetadata(int generationId, Bundle metadata, Bitmap artwork) {
-            System.out.println("New metadata+artwork: id=" + generationId
-                    + " data=" + metadata + " art=" + artwork);
+        public void onMetadataChanged(MediaMetadata metadata) {
+            String mmString = metadata == null ? null : "title=" + metadata
+                    .getString(MediaMetadata.METADATA_KEY_TITLE);
+            System.out.println("onMetadataChanged " + mmString);
         }
 
         void printUsageMessage() {
-            System.out.println("Monitoring remote control displays...  available commands:");
+            System.out.println("V2Monitoring session " + mController.getSessionInfo().getId()
+                    + "...  available commands:");
             System.out.println("(q)uit: finish monitoring");
         }
 
         void run() throws RemoteException {
             printUsageMessage();
-
-            mAudioService.registerRemoteControlDisplay(this, 0, 0);
+            HandlerThread cbThread = new HandlerThread("MediaCb") {
+                @Override
+                protected void onLooperPrepared() {
+                    mController.addCallback(ControllerMonitor.this);
+                }
+            };
+            cbThread.start();
 
             try {
                 InputStreamReader converter = new InputStreamReader(System.in);
@@ -209,17 +235,35 @@
                         printUsageMessage();
                     }
                 }
-
             } catch (IOException e) {
                 e.printStackTrace();
             } finally {
-                mAudioService.unregisterRemoteControlDisplay(this);
+                cbThread.getLooper().quit();
+                try {
+                    mController.removeCallback(this);
+                } catch (Exception e) {
+                    // ignoring
+                }
             }
         }
     }
 
-    private void runRemoteDisplay() throws Exception {
-        RemoteDisplayMonitor monitor = new RemoteDisplayMonitor();
-        monitor.run();
+    private void runListSessions() {
+        System.out.println("Sessions:");
+        try {
+            List<IBinder> sessions = mSessionService
+                    .getSessions(null, ActivityManager.getCurrentUser());
+            for (IBinder session : sessions) {
+                MediaController controller = MediaController.fromBinder(ISessionController.Stub
+                        .asInterface(session));
+                if (controller != null) {
+                    MediaSessionInfo info = controller.getSessionInfo();
+                    System.out.println("  id=" + info.getId() + ", package="
+                            + info.getPackageName());
+                }
+            }
+        } catch (Exception e) {
+            System.out.println("***Error listing sessions***");
+        }
     }
 }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index cf6d35ee8..d35225a 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1616,26 +1616,6 @@
     }
 
     /**
-     * @hide
-     * If the stream is active locally or remotely, adjust its volume according to the enforced
-     * priority rules.
-     * Note: only AudioManager.STREAM_MUSIC is supported at the moment
-     */
-    public void adjustLocalOrRemoteStreamVolume(int streamType, int direction) {
-        if (streamType != STREAM_MUSIC) {
-            Log.w(TAG, "adjustLocalOrRemoteStreamVolume() doesn't support stream " + streamType);
-        }
-        IAudioService service = getService();
-        try {
-            service.adjustLocalOrRemoteStreamVolume(streamType, direction,
-                    mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in adjustLocalOrRemoteStreamVolume", e);
-        }
-    }
-
-
-    /**
      * Return a new audio session identifier not associated with any player or effect.
      * It can for instance be used to create one of the {@link android.media.audiofx.AudioEffect}
      * objects.
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 72f4a58..48479a4 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -830,24 +830,6 @@
     }
 
     /** @see AudioManager#adjustVolume(int, int) */
-    public void adjustVolume(int direction, int flags, String callingPackage) {
-        adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags,
-                callingPackage);
-    }
-
-    /** @see AudioManager#adjustLocalOrRemoteStreamVolume(int, int) with current assumption
-     *  on streamType: fixed to STREAM_MUSIC */
-    public void adjustLocalOrRemoteStreamVolume(int streamType, int direction,
-            String callingPackage) {
-        if (DEBUG_VOL) Log.d(TAG, "adjustLocalOrRemoteStreamVolume(dir="+direction+")");
-        if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
-            adjustStreamVolume(AudioSystem.STREAM_MUSIC, direction, 0, callingPackage);
-        } else if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
-            mMediaFocusControl.adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, 0);
-        }
-    }
-
-    /** @see AudioManager#adjustVolume(int, int) */
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
             String callingPackage) {
         if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
@@ -4482,22 +4464,6 @@
         mMediaFocusControl.setPlaybackInfoForRcc(rccId, what, value);
     }
 
-    public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
-        if (DEBUG_SESSIONS) {
-            int pid = getCallingPid();
-            Log.w(TAG, "Call to dispatchMediaKeyEvent from " + pid);
-        }
-        MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
-    }
-
-    public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
-        if (DEBUG_SESSIONS) {
-            int pid = getCallingPid();
-            Log.w(TAG, "Call to dispatchMediaKeyEventUnderWakelock from " + pid);
-        }
-        MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, true);
-    }
-
     //==========================================================================================
     // Audio Focus
     //==========================================================================================
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index c29e967..169631e 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -36,13 +36,8 @@
  */
 interface IAudioService {
 
-    void adjustVolume(int direction, int flags, String callingPackage);
-
     boolean isLocalOrRemoteMusicActive();
 
-    oneway void adjustLocalOrRemoteStreamVolume(int streamType, int direction,
-            String callingPackage);
-
     void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
             String callingPackage);
 
@@ -127,9 +122,6 @@
 
     int getCurrentAudioFocus();
 
-    oneway void dispatchMediaKeyEvent(in KeyEvent keyEvent);
-    void dispatchMediaKeyEventUnderWakelock(in KeyEvent keyEvent);
-
            void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c, IBinder token);
     oneway void unregisterMediaButtonIntent(in PendingIntent pi);
 
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 5ca7daa..87a43e4 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -70,14 +70,7 @@
      * @hide
      */
     public static MediaController fromBinder(ISessionController sessionBinder) {
-        MediaController controller = new MediaController(sessionBinder);
-        try {
-            controller.mSessionBinder.registerCallbackListener(controller.mCbStub);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "MediaController created with expired token", e);
-            controller = null;
-        }
-        return controller;
+        return new MediaController(sessionBinder);
     }
 
     /**
@@ -305,7 +298,7 @@
                 mSessionBinder.registerCallbackListener(mCbStub);
                 mCbRegistered = true;
             } catch (RemoteException e) {
-                Log.d(TAG, "Dead object in registerCallback", e);
+                Log.e(TAG, "Dead object in registerCallback", e);
             }
         }
     }
@@ -314,14 +307,23 @@
         if (cb == null) {
             throw new IllegalArgumentException("Callback cannot be null");
         }
+        boolean success = false;
         for (int i = mCallbacks.size() - 1; i >= 0; i--) {
             MessageHandler handler = mCallbacks.get(i);
             if (cb == handler.mCallback) {
                 mCallbacks.remove(i);
-                return true;
+                success = true;
             }
         }
-        return false;
+        if (mCbRegistered && mCallbacks.size() == 0) {
+            try {
+                mSessionBinder.unregisterCallbackListener(mCbStub);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in removeCallbackLocked");
+            }
+            mCbRegistered = false;
+        }
+        return success;
     }
 
     private MessageHandler getHandlerForCallbackLocked(Callback cb) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
index 3e444fa..8945b15 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
@@ -341,11 +341,11 @@
                         }
                         // Volume buttons should only function for music (local or remote).
                         // TODO: Actually handle MUTE.
-                        mAudioManager.adjustLocalOrRemoteStreamVolume(
-                                AudioManager.STREAM_MUSIC,
+                        mAudioManager.adjustSuggestedStreamVolume(
                                 keyCode == KeyEvent.KEYCODE_VOLUME_UP
                                         ? AudioManager.ADJUST_RAISE
-                                        : AudioManager.ADJUST_LOWER);
+                                        : AudioManager.ADJUST_LOWER /* direction */,
+                                AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */);
                         // Don't execute default volume behavior
                         return true;
                     } else {
@@ -376,17 +376,13 @@
     }
 
     private void handleMediaKeyEvent(KeyEvent keyEvent) {
-        IAudioService audioService = IAudioService.Stub.asInterface(
-                ServiceManager.checkService(Context.AUDIO_SERVICE));
-        if (audioService != null) {
-            try {
-                audioService.dispatchMediaKeyEvent(keyEvent);
-            } catch (RemoteException e) {
-                Log.e("KeyguardViewBase", "dispatchMediaKeyEvent threw exception " + e);
+        synchronized (this) {
+            if (mAudioManager == null) {
+                mAudioManager = (AudioManager) getContext().getSystemService(
+                        Context.AUDIO_SERVICE);
             }
-        } else {
-            Slog.w("KeyguardViewBase", "Unable to find IAudioService for media key event");
         }
+        mAudioManager.dispatchMediaKeyEvent(keyEvent);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 9ae8aed..9d61493 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -38,6 +38,7 @@
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.os.Bundle;
+import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -63,6 +64,7 @@
  */
 public class MediaSessionRecord implements IBinder.DeathRecipient {
     private static final String TAG = "MediaSessionRecord";
+    private static final boolean DEBUG = false;
 
     /**
      * These are the playback states that count as currently active.
@@ -506,9 +508,12 @@
                 ISessionControllerCallback cb = mControllerCallbacks.get(i);
                 try {
                     cb.onPlaybackStateChanged(mPlaybackState);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Removing dead callback in pushPlaybackStateUpdate.", e);
+                } catch (DeadObjectException e) {
                     mControllerCallbacks.remove(i);
+                    Log.w(TAG, "Removed dead callback in pushPlaybackStateUpdate. size="
+                            + mControllerCallbacks.size() + " cb=" + cb, e);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "unexpected exception in pushPlaybackStateUpdate.", e);
                 }
             }
         }
@@ -523,9 +528,11 @@
                 ISessionControllerCallback cb = mControllerCallbacks.get(i);
                 try {
                     cb.onMetadataChanged(mMetadata);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Removing dead callback in pushMetadataUpdate.", e);
+                } catch (DeadObjectException e) {
+                    Log.w(TAG, "Removing dead callback in pushMetadataUpdate. " + cb, e);
                     mControllerCallbacks.remove(i);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "unexpected exception in pushMetadataUpdate. " + cb, e);
                 }
             }
         }
@@ -540,9 +547,11 @@
                 ISessionControllerCallback cb = mControllerCallbacks.get(i);
                 try {
                     cb.onRouteChanged(mRoute);
-                } catch (RemoteException e) {
+                } catch (DeadObjectException e) {
                     Log.w(TAG, "Removing dead callback in pushRouteUpdate.", e);
                     mControllerCallbacks.remove(i);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "unexpected exception in pushRouteUpdate.", e);
                 }
             }
         }
@@ -557,8 +566,11 @@
                 ISessionControllerCallback cb = mControllerCallbacks.get(i);
                 try {
                     cb.onEvent(event, data);
+                } catch (DeadObjectException e) {
+                    Log.w(TAG, "Removing dead callback in pushEvent.", e);
+                    mControllerCallbacks.remove(i);
                 } catch (RemoteException e) {
-                    Log.w(TAG, "Error with callback in pushEvent.", e);
+                    Log.w(TAG, "unexpected exception in pushEvent.", e);
                 }
             }
         }
@@ -611,6 +623,16 @@
         return result == null ? state : result;
     }
 
+    private int getControllerCbIndexForCb(ISessionControllerCallback cb) {
+        IBinder binder = cb.asBinder();
+        for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+            if (binder.equals(mControllerCallbacks.get(i).asBinder())) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
     private final RouteConnectionRecord.Listener mConnectionListener
             = new RouteConnectionRecord.Listener() {
         @Override
@@ -929,8 +951,11 @@
         @Override
         public void registerCallbackListener(ISessionControllerCallback cb) {
             synchronized (mLock) {
-                if (!mControllerCallbacks.contains(cb)) {
+                if (getControllerCbIndexForCb(cb) < 0) {
                     mControllerCallbacks.add(cb);
+                    if (DEBUG) {
+                        Log.d(TAG, "registering controller callback " + cb);
+                    }
                 }
             }
         }
@@ -939,7 +964,13 @@
         public void unregisterCallbackListener(ISessionControllerCallback cb)
                 throws RemoteException {
             synchronized (mLock) {
-                mControllerCallbacks.remove(cb);
+                int index = getControllerCbIndexForCb(cb);
+                if (index != -1) {
+                    mControllerCallbacks.remove(index);
+                }
+                if (DEBUG) {
+                    Log.d(TAG, "unregistering callback " + cb + ". index=" + index);
+                }
             }
         }