RingtonePlayer.java revision cd686b5b6d4166b510df8e32138479a9559bc117
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; 21098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.media.IAudioService; 22098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.media.IRingtonePlayer; 23098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.media.Ringtone; 24098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.net.Uri; 25098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.os.Binder; 26098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.os.IBinder; 27098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.os.Process; 28098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.os.RemoteException; 29098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport android.os.ServiceManager; 3065c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkeyimport android.os.UserHandle; 31cd686b5b6d4166b510df8e32138479a9559bc117John Spurlockimport android.util.Log; 32098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 33098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport com.android.systemui.SystemUI; 34098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 35098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport java.io.FileDescriptor; 36098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport java.io.PrintWriter; 37098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeyimport java.util.HashMap; 38098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 39098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey/** 40098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * Service that offers to play ringtones by {@link Uri}, since our process has 41098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}. 42098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey */ 43098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkeypublic class RingtonePlayer extends SystemUI { 44098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey private static final String TAG = "RingtonePlayer"; 45b6e404a95f31f4681265eb53d2b7969f900151ccJeff Sharkey private static final boolean LOGD = false; 46098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 47098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey // TODO: support Uri switching under same IBinder 48098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 49098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey private IAudioService mAudioService; 50098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 51098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG); 52b8bacccfc159fe204e5b09b52de55f8ba853f713John Spurlock private final HashMap<IBinder, Client> mClients = new HashMap<IBinder, Client>(); 53098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 54098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 55098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey public void start() { 56098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mAsyncPlayer.setUsesWakeLock(mContext); 57098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 58098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mAudioService = IAudioService.Stub.asInterface( 59098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey ServiceManager.getService(Context.AUDIO_SERVICE)); 60098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey try { 61098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mAudioService.setRingtonePlayer(mCallback); 62098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } catch (RemoteException e) { 63cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock Log.e(TAG, "Problem registering RingtonePlayer: " + e); 64098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 65098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 66098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 67098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey /** 68098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey * Represents an active remote {@link Ringtone} client. 69098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey */ 70098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey private class Client implements IBinder.DeathRecipient { 71098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey private final IBinder mToken; 72098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey private final Ringtone mRingtone; 73098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 7465c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey public Client(IBinder token, Uri uri, UserHandle user, int streamType) { 75098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mToken = token; 7665c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey 7765c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey mRingtone = new Ringtone(getContextForUser(user), false); 78098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mRingtone.setStreamType(streamType); 79098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mRingtone.setUri(uri); 80098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 81098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 82098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 83098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey public void binderDied() { 84cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (LOGD) Log.d(TAG, "binderDied() token=" + mToken); 85098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey synchronized (mClients) { 86098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mClients.remove(mToken); 87098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 88098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mRingtone.stop(); 89098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 90098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 91098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 92098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() { 93098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 94098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey public void play(IBinder token, Uri uri, int streamType) throws RemoteException { 9565c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey if (LOGD) { 96cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock Log.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid=" 9765c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey + Binder.getCallingUid() + ")"); 9865c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey } 99098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey Client client; 100098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey synchronized (mClients) { 101098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey client = mClients.get(token); 102098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey if (client == null) { 10365c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey final UserHandle user = Binder.getCallingUserHandle(); 10465c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey client = new Client(token, uri, user, streamType); 105098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey token.linkToDeath(client, 0); 106098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mClients.put(token, client); 107098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 108098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 109098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey client.mRingtone.play(); 110098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 111098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 112098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 113098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey public void stop(IBinder token) { 114cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (LOGD) Log.d(TAG, "stop(token=" + token + ")"); 115098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey Client client; 116098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey synchronized (mClients) { 117098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey client = mClients.remove(token); 118098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 119098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey if (client != null) { 120098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey client.mToken.unlinkToDeath(client, 0); 121098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey client.mRingtone.stop(); 122098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 123098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 124098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 125098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 126098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey public boolean isPlaying(IBinder token) { 127cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (LOGD) Log.d(TAG, "isPlaying(token=" + token + ")"); 128098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey Client client; 129098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey synchronized (mClients) { 130098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey client = mClients.get(token); 131098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 132098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey if (client != null) { 133098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey return client.mRingtone.isPlaying(); 134098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } else { 135098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey return false; 136098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 137098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 138098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 139098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 14065c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey public void playAsync(Uri uri, UserHandle user, boolean looping, int streamType) { 141cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (LOGD) Log.d(TAG, "playAsync(uri=" + uri + ", user=" + user + ")"); 142098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey if (Binder.getCallingUid() != Process.SYSTEM_UID) { 143098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey throw new SecurityException("Async playback only available from system UID."); 144098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 14565c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey 14665c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey mAsyncPlayer.play(getContextForUser(user), uri, looping, streamType); 147098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 148098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 149098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 150098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey public void stopAsync() { 151cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (LOGD) Log.d(TAG, "stopAsync()"); 152098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey if (Binder.getCallingUid() != Process.SYSTEM_UID) { 153098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey throw new SecurityException("Async playback only available from system UID."); 154098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 155098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey mAsyncPlayer.stop(); 156098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 157098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey }; 158098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey 15965c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey private Context getContextForUser(UserHandle user) { 16065c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey try { 16165c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 16265c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey } catch (NameNotFoundException e) { 16365c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey throw new RuntimeException(e); 16465c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey } 16565c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey } 16665c4a2b26cd8776b0927e9b0e07ecf53bd31b627Jeff Sharkey 167098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey @Override 168098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 169098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey pw.println("Clients:"); 170098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey synchronized (mClients) { 171098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey for (Client client : mClients.values()) { 172098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey pw.print(" mToken="); 173098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey pw.print(client.mToken); 174098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey pw.print(" mUri="); 175098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey pw.println(client.mRingtone.getUri()); 176098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 177098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 178098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey } 179098d580cc2bb6c0891c756a4e5230c6c6b0d2376Jeff Sharkey} 180