blob: 3502b62b9df8bf736012695c499eadea15d8be64 [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;
20import android.media.IAudioService;
21import android.media.IRingtonePlayer;
22import android.media.Ringtone;
23import android.net.Uri;
24import android.os.Binder;
25import android.os.IBinder;
26import android.os.Process;
27import android.os.RemoteException;
28import android.os.ServiceManager;
29import android.util.Slog;
30
31import com.android.systemui.SystemUI;
32import com.google.android.collect.Maps;
33
34import java.io.FileDescriptor;
35import java.io.PrintWriter;
36import java.util.HashMap;
37
38/**
39 * Service that offers to play ringtones by {@link Uri}, since our process has
40 * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
41 */
42public class RingtonePlayer extends SystemUI {
43 private static final String TAG = "RingtonePlayer";
Jeff Sharkeyb6e404a2012-05-15 11:36:11 -070044 private static final boolean LOGD = false;
Jeff Sharkey098d5802012-04-26 17:30:34 -070045
46 // TODO: support Uri switching under same IBinder
47
48 private IAudioService mAudioService;
49
50 private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG);
51 private final HashMap<IBinder, Client> mClients = Maps.newHashMap();
52
53 @Override
54 public void start() {
55 mAsyncPlayer.setUsesWakeLock(mContext);
56
57 mAudioService = IAudioService.Stub.asInterface(
58 ServiceManager.getService(Context.AUDIO_SERVICE));
59 try {
60 mAudioService.setRingtonePlayer(mCallback);
61 } catch (RemoteException e) {
62 Slog.e(TAG, "Problem registering RingtonePlayer: " + e);
63 }
64 }
65
66 /**
67 * Represents an active remote {@link Ringtone} client.
68 */
69 private class Client implements IBinder.DeathRecipient {
70 private final IBinder mToken;
71 private final Ringtone mRingtone;
72
73 public Client(IBinder token, Uri uri, int streamType) {
74 mToken = token;
75 mRingtone = new Ringtone(mContext, false);
76 mRingtone.setStreamType(streamType);
77 mRingtone.setUri(uri);
78 }
79
80 @Override
81 public void binderDied() {
82 if (LOGD) Slog.d(TAG, "binderDied() token=" + mToken);
83 synchronized (mClients) {
84 mClients.remove(mToken);
85 }
86 mRingtone.stop();
87 }
88 }
89
90 private IRingtonePlayer mCallback = new IRingtonePlayer.Stub() {
91 @Override
92 public void play(IBinder token, Uri uri, int streamType) throws RemoteException {
93 if (LOGD) Slog.d(TAG, "play(token=" + token + ", uri=" + uri + ")");
94 Client client;
95 synchronized (mClients) {
96 client = mClients.get(token);
97 if (client == null) {
98 client = new Client(token, uri, streamType);
99 token.linkToDeath(client, 0);
100 mClients.put(token, client);
101 }
102 }
103 client.mRingtone.play();
104 }
105
106 @Override
107 public void stop(IBinder token) {
108 if (LOGD) Slog.d(TAG, "stop(token=" + token + ")");
109 Client client;
110 synchronized (mClients) {
111 client = mClients.remove(token);
112 }
113 if (client != null) {
114 client.mToken.unlinkToDeath(client, 0);
115 client.mRingtone.stop();
116 }
117 }
118
119 @Override
120 public boolean isPlaying(IBinder token) {
121 if (LOGD) Slog.d(TAG, "isPlaying(token=" + token + ")");
122 Client client;
123 synchronized (mClients) {
124 client = mClients.get(token);
125 }
126 if (client != null) {
127 return client.mRingtone.isPlaying();
128 } else {
129 return false;
130 }
131 }
132
133 @Override
134 public void playAsync(Uri uri, boolean looping, int streamType) {
135 if (LOGD) Slog.d(TAG, "playAsync(uri=" + uri + ")");
136 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
137 throw new SecurityException("Async playback only available from system UID.");
138 }
139 mAsyncPlayer.play(mContext, uri, looping, streamType);
140 }
141
142 @Override
143 public void stopAsync() {
144 if (LOGD) Slog.d(TAG, "stopAsync()");
145 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
146 throw new SecurityException("Async playback only available from system UID.");
147 }
148 mAsyncPlayer.stop();
149 }
150 };
151
152 @Override
153 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
154 pw.println("Clients:");
155 synchronized (mClients) {
156 for (Client client : mClients.values()) {
157 pw.print(" mToken=");
158 pw.print(client.mToken);
159 pw.print(" mUri=");
160 pw.println(client.mRingtone.getUri());
161 }
162 }
163 }
164}