blob: fe876d7a4d30d40b95c0595c7044e668de364408 [file] [log] [blame]
Jeff Sharkey098d5802012-04-26 17:30:34 -07001/*
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
17package com.android.systemui.media;
18
19import android.content.Context;
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -070020import android.content.pm.PackageManager.NameNotFoundException;
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -070021import android.media.AudioAttributes;
Jeff Sharkey098d5802012-04-26 17:30:34 -070022import android.media.IAudioService;
23import android.media.IRingtonePlayer;
24import android.media.Ringtone;
25import android.net.Uri;
26import android.os.Binder;
27import android.os.IBinder;
28import android.os.Process;
29import android.os.RemoteException;
30import android.os.ServiceManager;
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -070031import android.os.UserHandle;
John Spurlockcd686b52013-06-05 10:13:46 -040032import android.util.Log;
Jeff Sharkey098d5802012-04-26 17:30:34 -070033
34import com.android.systemui.SystemUI;
Jeff Sharkey098d5802012-04-26 17:30:34 -070035
36import java.io.FileDescriptor;
37import java.io.PrintWriter;
38import java.util.HashMap;
39
40/**
41 * Service that offers to play ringtones by {@link Uri}, since our process has
42 * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
43 */
44public class RingtonePlayer extends SystemUI {
45 private static final String TAG = "RingtonePlayer";
Jeff Sharkeyb6e404a2012-05-15 11:36:11 -070046 private static final boolean LOGD = false;
Jeff Sharkey098d5802012-04-26 17:30:34 -070047
48 // TODO: support Uri switching under same IBinder
49
50 private IAudioService mAudioService;
51
52 private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG);
John Spurlockb8baccc2013-06-05 12:59:01 -040053 private final HashMap<IBinder, Client> mClients = new HashMap<IBinder, Client>();
Jeff Sharkey098d5802012-04-26 17:30:34 -070054
55 @Override
56 public void start() {
57 mAsyncPlayer.setUsesWakeLock(mContext);
58
59 mAudioService = IAudioService.Stub.asInterface(
60 ServiceManager.getService(Context.AUDIO_SERVICE));
61 try {
62 mAudioService.setRingtonePlayer(mCallback);
63 } catch (RemoteException e) {
John Spurlockcd686b52013-06-05 10:13:46 -040064 Log.e(TAG, "Problem registering RingtonePlayer: " + e);
Jeff Sharkey098d5802012-04-26 17:30:34 -070065 }
66 }
67
68 /**
69 * Represents an active remote {@link Ringtone} client.
70 */
71 private class Client implements IBinder.DeathRecipient {
72 private final IBinder mToken;
73 private final Ringtone mRingtone;
74
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -070075 public Client(IBinder token, Uri uri, UserHandle user, AudioAttributes aa) {
Jeff Sharkey098d5802012-04-26 17:30:34 -070076 mToken = token;
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -070077
78 mRingtone = new Ringtone(getContextForUser(user), false);
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -070079 mRingtone.setAudioAttributes(aa);
Jeff Sharkey098d5802012-04-26 17:30:34 -070080 mRingtone.setUri(uri);
81 }
82
83 @Override
84 public void binderDied() {
John Spurlockcd686b52013-06-05 10:13:46 -040085 if (LOGD) Log.d(TAG, "binderDied() token=" + mToken);
Jeff Sharkey098d5802012-04-26 17:30:34 -070086 synchronized (mClients) {
87 mClients.remove(mToken);
88 }
89 mRingtone.stop();
90 }
91 }
92
93 private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() {
94 @Override
Jean-Michel Trivi462045e2015-06-30 12:34:48 -070095 public void play(IBinder token, Uri uri, AudioAttributes aa, float volume, boolean looping)
96 throws RemoteException {
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -070097 if (LOGD) {
John Spurlockcd686b52013-06-05 10:13:46 -040098 Log.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid="
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -070099 + Binder.getCallingUid() + ")");
100 }
Jeff Sharkey098d5802012-04-26 17:30:34 -0700101 Client client;
102 synchronized (mClients) {
103 client = mClients.get(token);
104 if (client == null) {
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -0700105 final UserHandle user = Binder.getCallingUserHandle();
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700106 client = new Client(token, uri, user, aa);
Jeff Sharkey098d5802012-04-26 17:30:34 -0700107 token.linkToDeath(client, 0);
108 mClients.put(token, client);
109 }
110 }
Jean-Michel Trivi462045e2015-06-30 12:34:48 -0700111 client.mRingtone.setLooping(looping);
112 client.mRingtone.setVolume(volume);
Jeff Sharkey098d5802012-04-26 17:30:34 -0700113 client.mRingtone.play();
114 }
115
116 @Override
117 public void stop(IBinder token) {
John Spurlockcd686b52013-06-05 10:13:46 -0400118 if (LOGD) Log.d(TAG, "stop(token=" + token + ")");
Jeff Sharkey098d5802012-04-26 17:30:34 -0700119 Client client;
120 synchronized (mClients) {
121 client = mClients.remove(token);
122 }
123 if (client != null) {
124 client.mToken.unlinkToDeath(client, 0);
125 client.mRingtone.stop();
126 }
127 }
128
129 @Override
130 public boolean isPlaying(IBinder token) {
John Spurlockcd686b52013-06-05 10:13:46 -0400131 if (LOGD) Log.d(TAG, "isPlaying(token=" + token + ")");
Jeff Sharkey098d5802012-04-26 17:30:34 -0700132 Client client;
133 synchronized (mClients) {
134 client = mClients.get(token);
135 }
136 if (client != null) {
137 return client.mRingtone.isPlaying();
138 } else {
139 return false;
140 }
141 }
142
143 @Override
Jean-Michel Trivi462045e2015-06-30 12:34:48 -0700144 public void setPlaybackProperties(IBinder token, float volume, boolean looping) {
145 Client client;
146 synchronized (mClients) {
147 client = mClients.get(token);
148 }
149 if (client != null) {
150 client.mRingtone.setVolume(volume);
151 client.mRingtone.setLooping(looping);
152 }
153 // else no client for token when setting playback properties but will be set at play()
154 }
155
156 @Override
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700157 public void playAsync(Uri uri, UserHandle user, boolean looping, AudioAttributes aa) {
John Spurlockcd686b52013-06-05 10:13:46 -0400158 if (LOGD) Log.d(TAG, "playAsync(uri=" + uri + ", user=" + user + ")");
Jeff Sharkey098d5802012-04-26 17:30:34 -0700159 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
160 throw new SecurityException("Async playback only available from system UID.");
161 }
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -0700162
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700163 mAsyncPlayer.play(getContextForUser(user), uri, looping, aa);
Jeff Sharkey098d5802012-04-26 17:30:34 -0700164 }
165
166 @Override
167 public void stopAsync() {
John Spurlockcd686b52013-06-05 10:13:46 -0400168 if (LOGD) Log.d(TAG, "stopAsync()");
Jeff Sharkey098d5802012-04-26 17:30:34 -0700169 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
170 throw new SecurityException("Async playback only available from system UID.");
171 }
172 mAsyncPlayer.stop();
173 }
Todd Kennedy68de1572015-07-20 13:32:40 -0700174
175 @Override
176 public String getTitle(Uri uri) {
177 final UserHandle user = Binder.getCallingUserHandle();
178 return Ringtone.getTitle(getContextForUser(user), uri,
179 false /*followSettingsUri*/, false /*allowRemote*/);
180 }
Jeff Sharkey098d5802012-04-26 17:30:34 -0700181 };
182
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -0700183 private Context getContextForUser(UserHandle user) {
184 try {
185 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
186 } catch (NameNotFoundException e) {
187 throw new RuntimeException(e);
188 }
189 }
190
Jeff Sharkey098d5802012-04-26 17:30:34 -0700191 @Override
192 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
193 pw.println("Clients:");
194 synchronized (mClients) {
195 for (Client client : mClients.values()) {
196 pw.print(" mToken=");
197 pw.print(client.mToken);
198 pw.print(" mUri=");
199 pw.println(client.mRingtone.getUri());
200 }
201 }
202 }
203}