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