1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.media; 18 19 import android.content.Context; 20 import android.media.IAudioService; 21 import android.media.IRingtonePlayer; 22 import android.media.Ringtone; 23 import android.net.Uri; 24 import android.os.Binder; 25 import android.os.IBinder; 26 import android.os.Process; 27 import android.os.RemoteException; 28 import android.os.ServiceManager; 29 import android.util.Slog; 30 31 import com.android.systemui.SystemUI; 32 import com.google.android.collect.Maps; 33 34 import java.io.FileDescriptor; 35 import java.io.PrintWriter; 36 import java.util.HashMap; 37 38 /** 39 * Service that offers to play ringtones by {@link Uri}, since our process has 40 * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}. 41 */ 42 public class RingtonePlayer extends SystemUI { 43 private static final String TAG = "RingtonePlayer"; 44 private static final boolean LOGD = false; 45 46 // TODO: support Uri switching under same IBinder 47 48 private IAudioService mAudioService; 49 50 private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG); 51 private final HashMap<IBinder, Client> mClients = Maps.newHashMap(); 52 53 @Override start()54 public void start() { 55 mAsyncPlayer.setUsesWakeLock(mContext); 56 57 mAudioService = IAudioService.Stub.asInterface( 58 ServiceManager.getService(Context.AUDIO_SERVICE)); 59 try { 60 mAudioService.setRingtonePlayer(mCallback); 61 } catch (RemoteException e) { 62 Slog.e(TAG, "Problem registering RingtonePlayer: " + e); 63 } 64 } 65 66 /** 67 * Represents an active remote {@link Ringtone} client. 68 */ 69 private class Client implements IBinder.DeathRecipient { 70 private final IBinder mToken; 71 private final Ringtone mRingtone; 72 Client(IBinder token, Uri uri, int streamType)73 public Client(IBinder token, Uri uri, int streamType) { 74 mToken = token; 75 mRingtone = new Ringtone(mContext, false); 76 mRingtone.setStreamType(streamType); 77 mRingtone.setUri(uri); 78 } 79 80 @Override binderDied()81 public void binderDied() { 82 if (LOGD) Slog.d(TAG, "binderDied() token=" + mToken); 83 synchronized (mClients) { 84 mClients.remove(mToken); 85 } 86 mRingtone.stop(); 87 } 88 } 89 90 private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() { 91 @Override 92 public void play(IBinder token, Uri uri, int streamType) throws RemoteException { 93 if (LOGD) Slog.d(TAG, "play(token=" + token + ", uri=" + uri + ")"); 94 Client client; 95 synchronized (mClients) { 96 client = mClients.get(token); 97 if (client == null) { 98 client = new Client(token, uri, streamType); 99 token.linkToDeath(client, 0); 100 mClients.put(token, client); 101 } 102 } 103 client.mRingtone.play(); 104 } 105 106 @Override 107 public void stop(IBinder token) { 108 if (LOGD) Slog.d(TAG, "stop(token=" + token + ")"); 109 Client client; 110 synchronized (mClients) { 111 client = mClients.remove(token); 112 } 113 if (client != null) { 114 client.mToken.unlinkToDeath(client, 0); 115 client.mRingtone.stop(); 116 } 117 } 118 119 @Override 120 public boolean isPlaying(IBinder token) { 121 if (LOGD) Slog.d(TAG, "isPlaying(token=" + token + ")"); 122 Client client; 123 synchronized (mClients) { 124 client = mClients.get(token); 125 } 126 if (client != null) { 127 return client.mRingtone.isPlaying(); 128 } else { 129 return false; 130 } 131 } 132 133 @Override 134 public void playAsync(Uri uri, boolean looping, int streamType) { 135 if (LOGD) Slog.d(TAG, "playAsync(uri=" + uri + ")"); 136 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 137 throw new SecurityException("Async playback only available from system UID."); 138 } 139 mAsyncPlayer.play(mContext, uri, looping, streamType); 140 } 141 142 @Override 143 public void stopAsync() { 144 if (LOGD) Slog.d(TAG, "stopAsync()"); 145 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 146 throw new SecurityException("Async playback only available from system UID."); 147 } 148 mAsyncPlayer.stop(); 149 } 150 }; 151 152 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)153 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 154 pw.println("Clients:"); 155 synchronized (mClients) { 156 for (Client client : mClients.values()) { 157 pw.print(" mToken="); 158 pw.print(client.mToken); 159 pw.print(" mUri="); 160 pw.println(client.mRingtone.getUri()); 161 } 162 } 163 } 164 } 165