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