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