1098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey/* 2098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * Copyright (C) 2012 The Android Open Source Project 3098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * 4098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 5098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * you may not use this file except in compliance with the License. 6098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * You may obtain a copy of the License at 7098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * 8098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 9098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * 10098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 11098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 12098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * See the License for the specific language governing permissions and 14098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * limitations under the License. 15098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey */ 16098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 17098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeypackage com.android.systemui.media; 18098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 19098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.content.Context; 2065c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkeyimport android.content.pm.PackageManager.NameNotFoundException; 2181f871e2b96125d57b76c07169e868e516443794Jean-Michel Triviimport android.media.AudioAttributes; 22098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.media.IAudioService; 23098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.media.IRingtonePlayer; 24098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.media.Ringtone; 25098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.net.Uri; 26098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.os.Binder; 27098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.os.IBinder; 28098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.os.Process; 29098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.os.RemoteException; 30098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.os.ServiceManager; 3165c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkeyimport android.os.UserHandle; 32cd686b5b6d4166b510df8e32138479a9559bc117John Spurlockimport android.util.Log; 33098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 34098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport com.android.systemui.SystemUI; 35098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 36098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport java.io.FileDescriptor; 37098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport java.io.PrintWriter; 38098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport java.util.HashMap; 39098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 40098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey/** 41098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * Service that offers to play ringtones by {@link Uri}, since our process has 42098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}. 43098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey */ 44098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeypublic class RingtonePlayer extends SystemUI { 45098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey private static final String TAG = "RingtonePlayer"; 46b6e404a95f31f4681265eb53d2b7969f900151ccJeff Sharkey private static final boolean LOGD = false; 47098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 48098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey // TODO: support Uri switching under same IBinder 49098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 50098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey private IAudioService mAudioService; 51098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 52098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG); 53b8bacccfc159fe204e5b09b52de55f8ba853f713John Spurlock private final HashMap<IBinder, Client> mClients = new HashMap<IBinder, Client>(); 54098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 55098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 56098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey public void start() { 57098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mAsyncPlayer.setUsesWakeLock(mContext); 58098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 59098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mAudioService = IAudioService.Stub.asInterface( 60098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey ServiceManager.getService(Context.AUDIO_SERVICE)); 61098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey try { 62098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mAudioService.setRingtonePlayer(mCallback); 63098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } catch (RemoteException e) { 64cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock Log.e(TAG, "Problem registering RingtonePlayer: " + e); 65098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 66098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 67098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 68098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey /** 69098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * Represents an active remote {@link Ringtone} client. 70098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey */ 71098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey private class Client implements IBinder.DeathRecipient { 72098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey private final IBinder mToken; 73098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey private final Ringtone mRingtone; 74098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 7581f871e2b96125d57b76c07169e868e516443794Jean-Michel Trivi public Client(IBinder token, Uri uri, UserHandle user, AudioAttributes aa) { 76098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mToken = token; 7765c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey 7865c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey mRingtone = new Ringtone(getContextForUser(user), false); 7981f871e2b96125d57b76c07169e868e516443794Jean-Michel Trivi mRingtone.setAudioAttributes(aa); 80098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mRingtone.setUri(uri); 81098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 82098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 83098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 84098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey public void binderDied() { 85cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (LOGD) Log.d(TAG, "binderDied() token=" + mToken); 86098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey synchronized (mClients) { 87098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mClients.remove(mToken); 88098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 89098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mRingtone.stop(); 90098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 91098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 92098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 93098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() { 94098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 9581f871e2b96125d57b76c07169e868e516443794Jean-Michel Trivi public void play(IBinder token, Uri uri, AudioAttributes aa) throws RemoteException { 9665c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey if (LOGD) { 97cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock Log.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid=" 9865c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey + Binder.getCallingUid() + ")"); 9965c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey } 100098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey Client client; 101098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey synchronized (mClients) { 102098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey client = mClients.get(token); 103098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey if (client == null) { 10465c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey final UserHandle user = Binder.getCallingUserHandle(); 10581f871e2b96125d57b76c07169e868e516443794Jean-Michel Trivi client = new Client(token, uri, user, aa); 106098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey token.linkToDeath(client, 0); 107098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mClients.put(token, client); 108098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 109098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 110098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey client.mRingtone.play(); 111098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 112098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 113098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 114098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey public void stop(IBinder token) { 115cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (LOGD) Log.d(TAG, "stop(token=" + token + ")"); 116098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey Client client; 117098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey synchronized (mClients) { 118098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey client = mClients.remove(token); 119098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 120098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey if (client != null) { 121098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey client.mToken.unlinkToDeath(client, 0); 122098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey client.mRingtone.stop(); 123098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 124098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 125098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 126098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 127098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey public boolean isPlaying(IBinder token) { 128cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (LOGD) Log.d(TAG, "isPlaying(token=" + token + ")"); 129098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey Client client; 130098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey synchronized (mClients) { 131098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey client = mClients.get(token); 132098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 133098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey if (client != null) { 134098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey return client.mRingtone.isPlaying(); 135098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } else { 136098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey return false; 137098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 138098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 139098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 140098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 14181f871e2b96125d57b76c07169e868e516443794Jean-Michel Trivi public void playAsync(Uri uri, UserHandle user, boolean looping, AudioAttributes aa) { 142cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (LOGD) Log.d(TAG, "playAsync(uri=" + uri + ", user=" + user + ")"); 143098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey if (Binder.getCallingUid() != Process.SYSTEM_UID) { 144098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey throw new SecurityException("Async playback only available from system UID."); 145098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 14665c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey 14781f871e2b96125d57b76c07169e868e516443794Jean-Michel Trivi mAsyncPlayer.play(getContextForUser(user), uri, looping, aa); 148098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 149098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 150098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 151098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey public void stopAsync() { 152cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (LOGD) Log.d(TAG, "stopAsync()"); 153098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey if (Binder.getCallingUid() != Process.SYSTEM_UID) { 154098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey throw new SecurityException("Async playback only available from system UID."); 155098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 156098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mAsyncPlayer.stop(); 157098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 158098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey }; 159098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 16065c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey private Context getContextForUser(UserHandle user) { 16165c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey try { 16265c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 16365c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey } catch (NameNotFoundException e) { 16465c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey throw new RuntimeException(e); 16565c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey } 16665c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey } 16765c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey 168098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 169098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 170098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey pw.println("Clients:"); 171098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey synchronized (mClients) { 172098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey for (Client client : mClients.values()) { 173098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey pw.print(" mToken="); 174098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey pw.print(client.mToken); 175098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey pw.print(" mUri="); 176098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey pw.println(client.mRingtone.getUri()); 177098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 178098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 179098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 180098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey} 181