blob: 9c96c35f006f9be0700b313a51e7fb37f13c7447 [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;
20import android.media.IMediaSession;
21import android.media.IMediaSessionCallback;
22import android.media.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}