1/* 2** 3** Copyright 2013, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18package com.android.commands.media; 19 20import android.app.ActivityManager; 21import android.content.Context; 22import android.content.pm.ParceledListSlice; 23import android.media.MediaMetadata; 24import android.media.session.ISessionController; 25import android.media.session.ISessionControllerCallback; 26import android.media.session.ISessionManager; 27import android.media.session.MediaController; 28import android.media.session.ParcelableVolumeInfo; 29import android.media.session.PlaybackState; 30import android.os.Bundle; 31import android.os.HandlerThread; 32import android.os.IBinder; 33import android.os.RemoteException; 34import android.os.ServiceManager; 35import android.os.SystemClock; 36import android.util.AndroidException; 37import android.view.InputDevice; 38import android.view.KeyCharacterMap; 39import android.view.KeyEvent; 40 41import com.android.internal.os.BaseCommand; 42 43import java.io.BufferedReader; 44import java.io.IOException; 45import java.io.InputStreamReader; 46import java.io.PrintStream; 47import java.util.List; 48 49public class Media extends BaseCommand { 50 private ISessionManager mSessionService; 51 52 /** 53 * Command-line entry point. 54 * 55 * @param args The command-line arguments 56 */ 57 public static void main(String[] args) { 58 (new Media()).run(args); 59 } 60 61 public void onShowUsage(PrintStream out) { 62 out.println( 63 "usage: media [subcommand] [options]\n" + 64 " media dispatch KEY\n" + 65 " media list-sessions\n" + 66 " media monitor <tag>\n" + 67 "\n" + 68 "media dispatch: dispatch a media key to the system.\n" + 69 " KEY may be: play, pause, play-pause, mute, headsethook,\n" + 70 " stop, next, previous, rewind, record, fast-forword.\n" + 71 "media list-sessions: print a list of the current sessions.\n" + 72 "media monitor: monitor updates to the specified session.\n" + 73 " Use the tag from list-sessions.\n" 74 ); 75 } 76 77 public void onRun() throws Exception { 78 mSessionService = ISessionManager.Stub.asInterface(ServiceManager.checkService( 79 Context.MEDIA_SESSION_SERVICE)); 80 if (mSessionService == null) { 81 System.err.println(NO_SYSTEM_ERROR_CODE); 82 throw new AndroidException( 83 "Can't connect to media session service; is the system running?"); 84 } 85 86 String op = nextArgRequired(); 87 88 if (op.equals("dispatch")) { 89 runDispatch(); 90 } else if (op.equals("list-sessions")) { 91 runListSessions(); 92 } else if (op.equals("monitor")) { 93 runMonitor(); 94 } else { 95 showError("Error: unknown command '" + op + "'"); 96 return; 97 } 98 } 99 100 private void sendMediaKey(KeyEvent event) { 101 try { 102 mSessionService.dispatchMediaKeyEvent(event, false); 103 } catch (RemoteException e) { 104 } 105 } 106 107 private void runMonitor() throws Exception { 108 String id = nextArgRequired(); 109 if (id == null) { 110 showError("Error: must include a session id"); 111 return; 112 } 113 boolean success = false; 114 try { 115 List<IBinder> sessions = mSessionService 116 .getSessions(null, ActivityManager.getCurrentUser()); 117 for (IBinder session : sessions) { 118 ISessionController controller = ISessionController.Stub.asInterface(session); 119 try { 120 if (controller != null && id.equals(controller.getTag())) { 121 ControllerMonitor monitor = new ControllerMonitor(controller); 122 monitor.run(); 123 success = true; 124 break; 125 } 126 } catch (RemoteException e) { 127 // ignore 128 } 129 } 130 } catch (Exception e) { 131 System.out.println("***Error monitoring session*** " + e.getMessage()); 132 } 133 if (!success) { 134 System.out.println("No session found with id " + id); 135 } 136 } 137 138 private void runDispatch() throws Exception { 139 String cmd = nextArgRequired(); 140 int keycode; 141 if ("play".equals(cmd)) { 142 keycode = KeyEvent.KEYCODE_MEDIA_PLAY; 143 } else if ("pause".equals(cmd)) { 144 keycode = KeyEvent.KEYCODE_MEDIA_PAUSE; 145 } else if ("play-pause".equals(cmd)) { 146 keycode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE; 147 } else if ("mute".equals(cmd)) { 148 keycode = KeyEvent.KEYCODE_MUTE; 149 } else if ("headsethook".equals(cmd)) { 150 keycode = KeyEvent.KEYCODE_HEADSETHOOK; 151 } else if ("stop".equals(cmd)) { 152 keycode = KeyEvent.KEYCODE_MEDIA_STOP; 153 } else if ("next".equals(cmd)) { 154 keycode = KeyEvent.KEYCODE_MEDIA_NEXT; 155 } else if ("previous".equals(cmd)) { 156 keycode = KeyEvent.KEYCODE_MEDIA_PREVIOUS; 157 } else if ("rewind".equals(cmd)) { 158 keycode = KeyEvent.KEYCODE_MEDIA_REWIND; 159 } else if ("record".equals(cmd)) { 160 keycode = KeyEvent.KEYCODE_MEDIA_RECORD; 161 } else if ("fast-forward".equals(cmd)) { 162 keycode = KeyEvent.KEYCODE_MEDIA_FAST_FORWARD; 163 } else { 164 showError("Error: unknown dispatch code '" + cmd + "'"); 165 return; 166 } 167 168 final long now = SystemClock.uptimeMillis(); 169 sendMediaKey(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keycode, 0, 0, 170 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD)); 171 sendMediaKey(new KeyEvent(now, now, KeyEvent.ACTION_UP, keycode, 0, 0, 172 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD)); 173 } 174 175 class ControllerMonitor extends ISessionControllerCallback.Stub { 176 private final ISessionController mController; 177 178 public ControllerMonitor(ISessionController controller) { 179 mController = controller; 180 } 181 182 @Override 183 public void onSessionDestroyed() { 184 System.out.println("onSessionDestroyed. Enter q to quit."); 185 186 } 187 188 @Override 189 public void onEvent(String event, Bundle extras) { 190 System.out.println("onSessionEvent event=" + event + ", extras=" + extras); 191 } 192 193 @Override 194 public void onPlaybackStateChanged(PlaybackState state) { 195 System.out.println("onPlaybackStateChanged " + state); 196 } 197 198 @Override 199 public void onMetadataChanged(MediaMetadata metadata) { 200 String mmString = metadata == null ? null : "title=" + metadata 201 .getDescription(); 202 System.out.println("onMetadataChanged " + mmString); 203 } 204 205 @Override 206 public void onQueueChanged(ParceledListSlice queue) throws RemoteException { 207 System.out.println("onQueueChanged, " 208 + (queue == null ? "null queue" : " size=" + queue.getList().size())); 209 } 210 211 @Override 212 public void onQueueTitleChanged(CharSequence title) throws RemoteException { 213 System.out.println("onQueueTitleChange " + title); 214 } 215 216 @Override 217 public void onExtrasChanged(Bundle extras) throws RemoteException { 218 System.out.println("onExtrasChanged " + extras); 219 } 220 221 @Override 222 public void onVolumeInfoChanged(ParcelableVolumeInfo info) throws RemoteException { 223 System.out.println("onVolumeInfoChanged " + info); 224 } 225 226 void printUsageMessage() { 227 try { 228 System.out.println("V2Monitoring session " + mController.getTag() 229 + "... available commands: play, pause, next, previous"); 230 } catch (RemoteException e) { 231 System.out.println("Error trying to monitor session!"); 232 } 233 System.out.println("(q)uit: finish monitoring"); 234 } 235 236 void run() throws RemoteException { 237 printUsageMessage(); 238 HandlerThread cbThread = new HandlerThread("MediaCb") { 239 @Override 240 protected void onLooperPrepared() { 241 try { 242 mController.registerCallbackListener(ControllerMonitor.this); 243 } catch (RemoteException e) { 244 System.out.println("Error registering monitor callback"); 245 } 246 } 247 }; 248 cbThread.start(); 249 250 try { 251 InputStreamReader converter = new InputStreamReader(System.in); 252 BufferedReader in = new BufferedReader(converter); 253 String line; 254 255 while ((line = in.readLine()) != null) { 256 boolean addNewline = true; 257 if (line.length() <= 0) { 258 addNewline = false; 259 } else if ("q".equals(line) || "quit".equals(line)) { 260 break; 261 } else if ("play".equals(line)) { 262 mController.play(); 263 } else if ("pause".equals(line)) { 264 mController.pause(); 265 } else if ("next".equals(line)) { 266 mController.next(); 267 } else if ("previous".equals(line)) { 268 mController.previous(); 269 } else { 270 System.out.println("Invalid command: " + line); 271 } 272 273 synchronized (this) { 274 if (addNewline) { 275 System.out.println(""); 276 } 277 printUsageMessage(); 278 } 279 } 280 } catch (IOException e) { 281 e.printStackTrace(); 282 } finally { 283 cbThread.getLooper().quit(); 284 try { 285 mController.unregisterCallbackListener(this); 286 } catch (Exception e) { 287 // ignoring 288 } 289 } 290 } 291 } 292 293 private void runListSessions() { 294 System.out.println("Sessions:"); 295 try { 296 List<IBinder> sessions = mSessionService 297 .getSessions(null, ActivityManager.getCurrentUser()); 298 for (IBinder session : sessions) { 299 300 ISessionController controller = ISessionController.Stub.asInterface(session); 301 if (controller != null) { 302 try { 303 System.out.println(" tag=" + controller.getTag() 304 + ", package=" + controller.getPackageName()); 305 } catch (RemoteException e) { 306 // ignore 307 } 308 } 309 } 310 } catch (Exception e) { 311 System.out.println("***Error listing sessions***"); 312 } 313 } 314} 315