blob: 7eed7f25715c4e50165fce924cd0d4aacd835e29 [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 Trivi81f871e2014-08-06 16:32:38 -070095 public void play(IBinder token, Uri uri, AudioAttributes aa) throws RemoteException {
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -070096 if (LOGD) {
John Spurlockcd686b52013-06-05 10:13:46 -040097 Log.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid="
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -070098 + Binder.getCallingUid() + ")");
99 }
Jeff Sharkey098d5802012-04-26 17:30:34 -0700100 Client client;
101 synchronized (mClients) {
102 client = mClients.get(token);
103 if (client == null) {
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -0700104 final UserHandle user = Binder.getCallingUserHandle();
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700105 client = new Client(token, uri, user, aa);
Jeff Sharkey098d5802012-04-26 17:30:34 -0700106 token.linkToDeath(client, 0);
107 mClients.put(token, client);
108 }
109 }
110 client.mRingtone.play();
111 }
112
113 @Override
114 public void stop(IBinder token) {
John Spurlockcd686b52013-06-05 10:13:46 -0400115 if (LOGD) Log.d(TAG, "stop(token=" + token + ")");
Jeff Sharkey098d5802012-04-26 17:30:34 -0700116 Client client;
117 synchronized (mClients) {
118 client = mClients.remove(token);
119 }
120 if (client != null) {
121 client.mToken.unlinkToDeath(client, 0);
122 client.mRingtone.stop();
123 }
124 }
125
126 @Override
127 public boolean isPlaying(IBinder token) {
John Spurlockcd686b52013-06-05 10:13:46 -0400128 if (LOGD) Log.d(TAG, "isPlaying(token=" + token + ")");
Jeff Sharkey098d5802012-04-26 17:30:34 -0700129 Client client;
130 synchronized (mClients) {
131 client = mClients.get(token);
132 }
133 if (client != null) {
134 return client.mRingtone.isPlaying();
135 } else {
136 return false;
137 }
138 }
139
140 @Override
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700141 public void playAsync(Uri uri, UserHandle user, boolean looping, AudioAttributes aa) {
John Spurlockcd686b52013-06-05 10:13:46 -0400142 if (LOGD) Log.d(TAG, "playAsync(uri=" + uri + ", user=" + user + ")");
Jeff Sharkey098d5802012-04-26 17:30:34 -0700143 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
144 throw new SecurityException("Async playback only available from system UID.");
145 }
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -0700146
Jean-Michel Trivi81f871e2014-08-06 16:32:38 -0700147 mAsyncPlayer.play(getContextForUser(user), uri, looping, aa);
Jeff Sharkey098d5802012-04-26 17:30:34 -0700148 }
149
150 @Override
151 public void stopAsync() {
John Spurlockcd686b52013-06-05 10:13:46 -0400152 if (LOGD) Log.d(TAG, "stopAsync()");
Jeff Sharkey098d5802012-04-26 17:30:34 -0700153 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
154 throw new SecurityException("Async playback only available from system UID.");
155 }
156 mAsyncPlayer.stop();
157 }
158 };
159
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -0700160 private Context getContextForUser(UserHandle user) {
161 try {
162 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
163 } catch (NameNotFoundException e) {
164 throw new RuntimeException(e);
165 }
166 }
167
Jeff Sharkey098d5802012-04-26 17:30:34 -0700168 @Override
169 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
170 pw.println("Clients:");
171 synchronized (mClients) {
172 for (Client client : mClients.values()) {
173 pw.print(" mToken=");
174 pw.print(client.mToken);
175 pw.print(" mUri=");
176 pw.println(client.mRingtone.getUri());
177 }
178 }
179 }
180}