MediaSessionService.java revision 2f5b057da7d05d5d699a272aa24fd7c97cdda820
1/*
2 * Copyright (C) 2014 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.server.media;
18
19import android.content.Context;
20import android.media.session.IMediaSession;
21import android.media.session.IMediaSessionCallback;
22import android.media.session.IMediaSessionManager;
23import android.os.Binder;
24import android.os.RemoteException;
25import android.text.TextUtils;
26import android.util.Log;
27
28import com.android.server.SystemService;
29
30import java.util.ArrayList;
31
32/**
33 * System implementation of MediaSessionManager
34 */
35public class MediaSessionService extends SystemService {
36    private static final String TAG = "MediaSessionService";
37    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
38
39    private final SessionManagerImpl mSessionManagerImpl;
40
41    private final ArrayList<MediaSessionRecord> mSessions
42            = new ArrayList<MediaSessionRecord>();
43    private final Object mLock = new Object();
44
45    public MediaSessionService(Context context) {
46        super(context);
47        mSessionManagerImpl = new SessionManagerImpl();
48    }
49
50    @Override
51    public void onStart() {
52        publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
53    }
54
55    void sessionDied(MediaSessionRecord session) {
56        synchronized (mSessions) {
57            destroySessionLocked(session);
58        }
59    }
60
61    void destroySession(MediaSessionRecord session) {
62        synchronized (mSessions) {
63            destroySessionLocked(session);
64        }
65    }
66
67    private void destroySessionLocked(MediaSessionRecord session) {
68        mSessions.remove(session);
69    }
70
71    private void enforcePackageName(String packageName, int uid) {
72        if (TextUtils.isEmpty(packageName)) {
73            throw new IllegalArgumentException("packageName may not be empty");
74        }
75        String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
76        final int packageCount = packages.length;
77        for (int i = 0; i < packageCount; i++) {
78            if (packageName.equals(packages[i])) {
79                return;
80            }
81        }
82        throw new IllegalArgumentException("packageName is not owned by the calling process");
83    }
84
85    private MediaSessionRecord createSessionInternal(int pid, String packageName,
86            IMediaSessionCallback cb, String tag) {
87        synchronized (mLock) {
88            return createSessionLocked(pid, packageName, cb, tag);
89        }
90    }
91
92    private MediaSessionRecord createSessionLocked(int pid, String packageName,
93            IMediaSessionCallback cb, String tag) {
94        final MediaSessionRecord session = new MediaSessionRecord(pid, packageName, cb, tag, this);
95        try {
96            cb.asBinder().linkToDeath(session, 0);
97        } catch (RemoteException e) {
98            throw new RuntimeException("Media Session owner died prematurely.", e);
99        }
100        synchronized (mSessions) {
101            mSessions.add(session);
102        }
103        if (DEBUG) {
104            Log.d(TAG, "Created session for package " + packageName + " with tag " + tag);
105        }
106        return session;
107    }
108
109    class SessionManagerImpl extends IMediaSessionManager.Stub {
110        @Override
111        public IMediaSession createSession(String packageName, IMediaSessionCallback cb, String tag)
112                throws RemoteException {
113            final int pid = Binder.getCallingPid();
114            final int uid = Binder.getCallingUid();
115            final long token = Binder.clearCallingIdentity();
116            try {
117                enforcePackageName(packageName, uid);
118                if (cb == null) {
119                    throw new IllegalArgumentException("Controller callback cannot be null");
120                }
121                return createSessionInternal(pid, packageName, cb, tag).getSessionBinder();
122            } finally {
123                Binder.restoreCallingIdentity(token);
124            }
125        }
126    }
127
128}
129