blob: 8fe6055d10ff01e437114fcc26c1b729bed32296 [file] [log] [blame]
RoboErik01fe6612014-02-13 14:19:04 -08001/*
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;
RoboErik2f5b0572014-02-21 10:30:38 -080020import android.media.session.IMediaSession;
21import android.media.session.IMediaSessionCallback;
22import android.media.session.IMediaSessionManager;
RoboErik01fe6612014-02-13 14:19:04 -080023import android.os.Binder;
RoboErik8ae0f342014-02-24 18:02:08 -080024import android.os.Handler;
RoboErik01fe6612014-02-13 14:19:04 -080025import 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();
RoboErik8ae0f342014-02-24 18:02:08 -080045 // TODO do we want a separate thread for handling mediasession messages?
46 private final Handler mHandler = new Handler();
RoboErik01fe6612014-02-13 14:19:04 -080047
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) {
RoboErik8ae0f342014-02-24 18:02:08 -080097 final MediaSessionRecord session = new MediaSessionRecord(pid, packageName, cb, tag, this,
98 mHandler);
RoboErik01fe6612014-02-13 14:19:04 -080099 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}