• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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 
17 package com.android.server.media;
18 
19 import android.app.Notification;
20 import android.media.MediaController2;
21 import android.media.Session2CommandGroup;
22 import android.media.Session2Token;
23 import android.os.Handler;
24 import android.os.Looper;
25 import android.os.ResultReceiver;
26 import android.os.UserHandle;
27 import android.util.Log;
28 import android.view.KeyEvent;
29 
30 import com.android.internal.annotations.GuardedBy;
31 
32 import java.io.PrintWriter;
33 
34 /**
35  * Keeps the record of {@link Session2Token} to help send command to the corresponding session.
36  */
37 // TODO(jaewan): Do not call service method directly -- introduce listener instead.
38 public class MediaSession2Record extends MediaSessionRecordImpl {
39     private static final String TAG = "MediaSession2Record";
40     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
41     private final Object mLock = new Object();
42 
43     @GuardedBy("mLock")
44     private final Session2Token mSessionToken;
45     @GuardedBy("mLock")
46     private final HandlerExecutor mHandlerExecutor;
47     @GuardedBy("mLock")
48     private final MediaController2 mController;
49     @GuardedBy("mLock")
50     private final MediaSessionService mService;
51     @GuardedBy("mLock")
52     private boolean mIsConnected;
53     @GuardedBy("mLock")
54     private int mPolicies;
55     @GuardedBy("mLock")
56     private boolean mIsClosed;
57 
58     private final int mPid;
59 
MediaSession2Record( Session2Token sessionToken, MediaSessionService service, Looper handlerLooper, int pid, int policies)60     public MediaSession2Record(
61             Session2Token sessionToken,
62             MediaSessionService service,
63             Looper handlerLooper,
64             int pid,
65             int policies) {
66         // The lock is required to prevent `Controller2Callback` from using partially initialized
67         // `MediaSession2Record.this`.
68         synchronized (mLock) {
69             mSessionToken = sessionToken;
70             mService = service;
71             mHandlerExecutor = new HandlerExecutor(new Handler(handlerLooper));
72             mController = new MediaController2.Builder(service.getContext(), sessionToken)
73                     .setControllerCallback(mHandlerExecutor, new Controller2Callback())
74                     .build();
75             mPid = pid;
76             mPolicies = policies;
77         }
78     }
79 
80     @Override
getPackageName()81     public String getPackageName() {
82         return mSessionToken.getPackageName();
83     }
84 
getSession2Token()85     public Session2Token getSession2Token() {
86         return mSessionToken;
87     }
88 
89     @Override
getUid()90     public int getUid() {
91         return mSessionToken.getUid();
92     }
93 
94     @Override
getUserId()95     public int getUserId() {
96         return UserHandle.getUserHandleForUid(mSessionToken.getUid()).getIdentifier();
97     }
98 
99     @Override
hasLinkedNotificationSupport()100     public boolean hasLinkedNotificationSupport() {
101         return false;
102     }
103 
104     @Override
isSystemPriority()105     public boolean isSystemPriority() {
106         // System priority session is currently only allowed for telephony, so it's OK to stick to
107         // the media1 API at this moment.
108         return false;
109     }
110 
111     @Override
adjustVolume(String packageName, String opPackageName, int pid, int uid, boolean asSystemService, int direction, int flags, boolean useSuggested)112     public void adjustVolume(String packageName, String opPackageName, int pid, int uid,
113             boolean asSystemService, int direction, int flags, boolean useSuggested) {
114         // TODO(jaewan): Add API to adjust volume.
115     }
116 
117     @Override
isActive()118     public boolean isActive() {
119         synchronized (mLock) {
120             return mIsConnected;
121         }
122     }
123 
124     @Override
checkPlaybackActiveState(boolean expected)125     public boolean checkPlaybackActiveState(boolean expected) {
126         synchronized (mLock) {
127             return (mIsConnected && mController.isPlaybackActive()) == expected;
128         }
129     }
130 
131     @Override
isPlaybackTypeLocal()132     public boolean isPlaybackTypeLocal() {
133         // TODO(jaewan): Implement -- need API to know whether the playback is remote or local.
134         return true;
135     }
136 
137     @Override
close()138     public void close() {
139         synchronized (mLock) {
140             mIsClosed = true;
141             // Call close regardless of the mIsConnected. This may be called when it's not yet
142             // connected.
143             mController.close();
144         }
145     }
146 
147     @Override
isClosed()148     public boolean isClosed() {
149         synchronized (mLock) {
150             return mIsClosed;
151         }
152     }
153 
154     @Override
expireTempEngaged()155     public void expireTempEngaged() {
156         // NA as MediaSession2 doesn't support UserEngagementStates for FGS.
157     }
158 
159     @Override
onGlobalPrioritySessionActiveChanged(boolean isGlobalPrioritySessionActive)160     public void onGlobalPrioritySessionActiveChanged(boolean isGlobalPrioritySessionActive) {
161         // NA as MediaSession2 doesn't support UserEngagementStates for FGS.
162     }
163 
164     @Override
sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, KeyEvent ke, int sequenceId, ResultReceiver cb)165     public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
166             KeyEvent ke, int sequenceId, ResultReceiver cb) {
167         // TODO(jaewan): Implement.
168         return false;
169     }
170 
171     @Override
canHandleVolumeKey()172     public boolean canHandleVolumeKey() {
173         // TODO: Implement when MediaSession2 starts to get key events.
174         return false;
175     }
176 
177     @Override
isLinkedToNotification(Notification notification)178     boolean isLinkedToNotification(Notification notification) {
179         // Currently it's not possible to link MediaSession2 with a Notification
180         return false;
181     }
182 
183     @Override
getSessionPolicies()184     public int getSessionPolicies() {
185         synchronized (mLock) {
186             return mPolicies;
187         }
188     }
189 
190     @Override
setSessionPolicies(int policies)191     public void setSessionPolicies(int policies) {
192         synchronized (mLock) {
193             mPolicies = policies;
194         }
195     }
196 
197     @Override
dump(PrintWriter pw, String prefix)198     public void dump(PrintWriter pw, String prefix) {
199         pw.println(prefix + "uniqueId=" + getUniqueId());
200         pw.println(prefix + "token=" + mSessionToken);
201         pw.println(prefix + "controller=" + mController);
202 
203         final String indent = prefix + "  ";
204         pw.println(indent + "playbackActive=" + mController.isPlaybackActive());
205     }
206 
207     @Override
toString()208     public String toString() {
209         return getPackageName() + "/" + getUniqueId() + " (userId=" + getUserId() + ")";
210     }
211 
212     private class Controller2Callback extends MediaController2.ControllerCallback {
213         @Override
onConnected(MediaController2 controller, Session2CommandGroup allowedCommands)214         public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) {
215             if (DEBUG) {
216                 Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands);
217             }
218             MediaSessionService service;
219             synchronized (mLock) {
220                 mIsConnected = true;
221                 service = mService;
222             }
223             service.onSessionActiveStateChanged(MediaSession2Record.this,
224                     /* playbackState= */ null);
225         }
226 
227         @Override
onDisconnected(MediaController2 controller)228         public void onDisconnected(MediaController2 controller) {
229             if (DEBUG) {
230                 Log.d(TAG, "disconnected from " + mSessionToken);
231             }
232             MediaSessionService service;
233             synchronized (mLock) {
234                 mIsConnected = false;
235                 service = mService;
236             }
237             service.onSessionDied(MediaSession2Record.this);
238         }
239 
240         @Override
onPlaybackActiveChanged(MediaController2 controller, boolean playbackActive)241         public void onPlaybackActiveChanged(MediaController2 controller, boolean playbackActive) {
242             if (DEBUG) {
243                 Log.d(TAG, "playback active changed, " + mSessionToken + ", active="
244                         + playbackActive);
245             }
246             MediaSessionService service;
247             synchronized (mLock) {
248                 service = mService;
249             }
250             service.onSessionPlaybackStateChanged(
251                     MediaSession2Record.this, playbackActive, /* playbackState= */ null);
252         }
253     }
254 }
255