blob: 728c913408ad23b6b102d7ba7399524d7181e9fd [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;
Jeff Sharkey098d5802012-04-26 17:30:34 -070021import android.media.IAudioService;
22import android.media.IRingtonePlayer;
23import android.media.Ringtone;
24import android.net.Uri;
25import android.os.Binder;
26import android.os.IBinder;
27import android.os.Process;
28import android.os.RemoteException;
29import android.os.ServiceManager;
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -070030import android.os.UserHandle;
Jeff Sharkey098d5802012-04-26 17:30:34 -070031import android.util.Slog;
32
33import com.android.systemui.SystemUI;
Jeff Sharkey098d5802012-04-26 17:30:34 -070034
35import java.io.FileDescriptor;
36import java.io.PrintWriter;
37import java.util.HashMap;
38
39/**
40 * Service that offers to play ringtones by {@link Uri}, since our process has
41 * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
42 */
43public class RingtonePlayer extends SystemUI {
44 private static final String TAG = "RingtonePlayer";
Jeff Sharkeyb6e404a2012-05-15 11:36:11 -070045 private static final boolean LOGD = false;
Jeff Sharkey098d5802012-04-26 17:30:34 -070046
47 // TODO: support Uri switching under same IBinder
48
49 private IAudioService mAudioService;
50
51 private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG);
John Spurlockb8baccc2013-06-05 12:59:01 -040052 private final HashMap<IBinder, Client> mClients = new HashMap<IBinder, Client>();
Jeff Sharkey098d5802012-04-26 17:30:34 -070053
54 @Override
55 public void start() {
56 mAsyncPlayer.setUsesWakeLock(mContext);
57
58 mAudioService = IAudioService.Stub.asInterface(
59 ServiceManager.getService(Context.AUDIO_SERVICE));
60 try {
61 mAudioService.setRingtonePlayer(mCallback);
62 } catch (RemoteException e) {
63 Slog.e(TAG, "Problem registering RingtonePlayer: " + e);
64 }
65 }
66
67 /**
68 * Represents an active remote {@link Ringtone} client.
69 */
70 private class Client implements IBinder.DeathRecipient {
71 private final IBinder mToken;
72 private final Ringtone mRingtone;
73
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -070074 public Client(IBinder token, Uri uri, UserHandle user, int streamType) {
Jeff Sharkey098d5802012-04-26 17:30:34 -070075 mToken = token;
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -070076
77 mRingtone = new Ringtone(getContextForUser(user), false);
Jeff Sharkey098d5802012-04-26 17:30:34 -070078 mRingtone.setStreamType(streamType);
79 mRingtone.setUri(uri);
80 }
81
82 @Override
83 public void binderDied() {
84 if (LOGD) Slog.d(TAG, "binderDied() token=" + mToken);
85 synchronized (mClients) {
86 mClients.remove(mToken);
87 }
88 mRingtone.stop();
89 }
90 }
91
92 private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() {
93 @Override
94 public void play(IBinder token, Uri uri, int streamType) throws RemoteException {
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -070095 if (LOGD) {
96 Slog.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid="
97 + Binder.getCallingUid() + ")");
98 }
Jeff Sharkey098d5802012-04-26 17:30:34 -070099 Client client;
100 synchronized (mClients) {
101 client = mClients.get(token);
102 if (client == null) {
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -0700103 final UserHandle user = Binder.getCallingUserHandle();
104 client = new Client(token, uri, user, streamType);
Jeff Sharkey098d5802012-04-26 17:30:34 -0700105 token.linkToDeath(client, 0);
106 mClients.put(token, client);
107 }
108 }
109 client.mRingtone.play();
110 }
111
112 @Override
113 public void stop(IBinder token) {
114 if (LOGD) Slog.d(TAG, "stop(token=" + token + ")");
115 Client client;
116 synchronized (mClients) {
117 client = mClients.remove(token);
118 }
119 if (client != null) {
120 client.mToken.unlinkToDeath(client, 0);
121 client.mRingtone.stop();
122 }
123 }
124
125 @Override
126 public boolean isPlaying(IBinder token) {
127 if (LOGD) Slog.d(TAG, "isPlaying(token=" + token + ")");
128 Client client;
129 synchronized (mClients) {
130 client = mClients.get(token);
131 }
132 if (client != null) {
133 return client.mRingtone.isPlaying();
134 } else {
135 return false;
136 }
137 }
138
139 @Override
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -0700140 public void playAsync(Uri uri, UserHandle user, boolean looping, int streamType) {
141 if (LOGD) Slog.d(TAG, "playAsync(uri=" + uri + ", user=" + user + ")");
Jeff Sharkey098d5802012-04-26 17:30:34 -0700142 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
143 throw new SecurityException("Async playback only available from system UID.");
144 }
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -0700145
146 mAsyncPlayer.play(getContextForUser(user), uri, looping, streamType);
Jeff Sharkey098d5802012-04-26 17:30:34 -0700147 }
148
149 @Override
150 public void stopAsync() {
151 if (LOGD) Slog.d(TAG, "stopAsync()");
152 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
153 throw new SecurityException("Async playback only available from system UID.");
154 }
155 mAsyncPlayer.stop();
156 }
157 };
158
Jeff Sharkey65c4a2b2012-09-25 17:22:27 -0700159 private Context getContextForUser(UserHandle user) {
160 try {
161 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
162 } catch (NameNotFoundException e) {
163 throw new RuntimeException(e);
164 }
165 }
166
Jeff Sharkey098d5802012-04-26 17:30:34 -0700167 @Override
168 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
169 pw.println("Clients:");
170 synchronized (mClients) {
171 for (Client client : mClients.values()) {
172 pw.print(" mToken=");
173 pw.print(client.mToken);
174 pw.print(" mUri=");
175 pw.println(client.mRingtone.getUri());
176 }
177 }
178 }
179}