• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 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.media;
18 
19 import static android.media.SessionToken2.TYPE_SESSION;
20 import static android.media.SessionToken2.TYPE_SESSION_SERVICE;
21 import static android.media.SessionToken2.TYPE_LIBRARY_SERVICE;
22 
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.PackageManager;
26 import android.content.pm.PackageManager.NameNotFoundException;
27 import android.content.pm.ResolveInfo;
28 import android.media.MediaLibraryService2;
29 import android.media.MediaSessionService2;
30 import android.media.SessionToken2;
31 import android.media.SessionToken2.TokenType;
32 import android.media.update.SessionToken2Provider;
33 import android.os.Bundle;
34 import android.os.IBinder;
35 import android.text.TextUtils;
36 
37 import java.util.List;
38 
39 public class SessionToken2Impl implements SessionToken2Provider {
40     private static final String KEY_UID = "android.media.token.uid";
41     private static final String KEY_TYPE = "android.media.token.type";
42     private static final String KEY_PACKAGE_NAME = "android.media.token.package_name";
43     private static final String KEY_SERVICE_NAME = "android.media.token.service_name";
44     private static final String KEY_ID = "android.media.token.id";
45     private static final String KEY_SESSION_BINDER = "android.media.token.session_binder";
46 
47     private final SessionToken2 mInstance;
48     private final int mUid;
49     private final @TokenType int mType;
50     private final String mPackageName;
51     private final String mServiceName;
52     private final String mId;
53     private final IMediaSession2 mSessionBinder;
54 
55     /**
56      * Public constructor for the legacy support (i.e. browser can try connecting to any browser
57      * service if it knows the service name)
58      */
SessionToken2Impl(Context context, SessionToken2 instance, String packageName, String serviceName, int uid)59     public SessionToken2Impl(Context context, SessionToken2 instance,
60             String packageName, String serviceName, int uid) {
61         if (TextUtils.isEmpty(packageName)) {
62             throw new IllegalArgumentException("packageName shouldn't be empty");
63         }
64         if (TextUtils.isEmpty(serviceName)) {
65             throw new IllegalArgumentException("serviceName shouldn't be empty");
66         }
67         mInstance = instance;
68         // Calculate uid if it's not specified.
69         final PackageManager manager = context.getPackageManager();
70         if (uid < 0) {
71             try {
72                 uid = manager.getPackageUid(packageName, 0);
73             } catch (NameNotFoundException e) {
74                 throw new IllegalArgumentException("Cannot find package " + packageName);
75             }
76         }
77         mUid = uid;
78 
79         // Infer id and type from package name and service name
80         // TODO(jaewan): Handle multi-user.
81         String id = getSessionIdFromService(manager, MediaLibraryService2.SERVICE_INTERFACE,
82                 packageName, serviceName);
83         if (id != null) {
84             mId = id;
85             mType = TYPE_LIBRARY_SERVICE;
86         } else {
87             // retry with session service
88             mId = getSessionIdFromService(manager, MediaSessionService2.SERVICE_INTERFACE,
89                     packageName, serviceName);
90             mType = TYPE_SESSION_SERVICE;
91         }
92         if (mId == null) {
93             throw new IllegalArgumentException("service " + serviceName + " doesn't implement"
94                     + " session service nor library service. Use service's full name.");
95         }
96         mPackageName = packageName;
97         mServiceName = serviceName;
98         mSessionBinder = null;
99     }
100 
SessionToken2Impl(int uid, int type, String packageName, String serviceName, String id, IMediaSession2 sessionBinder)101     SessionToken2Impl(int uid, int type, String packageName, String serviceName, String id,
102             IMediaSession2 sessionBinder) {
103         // TODO(jaewan): Add sanity check (b/73863865)
104         mUid = uid;
105         mType = type;
106         mPackageName = packageName;
107         mServiceName = serviceName;
108         mId = id;
109         mSessionBinder = sessionBinder;
110         mInstance = new SessionToken2(this);
111     }
112 
getSessionIdFromService(PackageManager manager, String serviceInterface, String packageName, String serviceName)113     private static String getSessionIdFromService(PackageManager manager, String serviceInterface,
114             String packageName, String serviceName) {
115         Intent serviceIntent = new Intent(serviceInterface);
116         serviceIntent.setPackage(packageName);
117         // Use queryIntentServices to find services with MediaLibraryService2.SERVICE_INTERFACE.
118         // We cannot use resolveService with intent specified class name, because resolveService
119         // ignores actions if Intent.setClassName() is specified.
120         List<ResolveInfo> list = manager.queryIntentServices(
121                 serviceIntent, PackageManager.GET_META_DATA);
122         if (list != null) {
123             for (int i = 0; i < list.size(); i++) {
124                 ResolveInfo resolveInfo = list.get(i);
125                 if (resolveInfo == null || resolveInfo.serviceInfo == null) {
126                     continue;
127                 }
128                 if (TextUtils.equals(resolveInfo.serviceInfo.name, serviceName)) {
129                     return getSessionId(resolveInfo);
130                 }
131             }
132         }
133         return null;
134     }
135 
getSessionId(ResolveInfo resolveInfo)136     public static String getSessionId(ResolveInfo resolveInfo) {
137         if (resolveInfo == null || resolveInfo.serviceInfo == null) {
138             return null;
139         } else if (resolveInfo.serviceInfo.metaData == null) {
140             return "";
141         } else {
142             return resolveInfo.serviceInfo.metaData.getString(
143                     MediaSessionService2.SERVICE_META_DATA, "");
144         }
145     }
146 
getInstance()147     public SessionToken2 getInstance() {
148         return mInstance;
149     }
150 
151     @Override
getPackageName_impl()152     public String getPackageName_impl() {
153         return mPackageName;
154     }
155 
156     @Override
getUid_impl()157     public int getUid_impl() {
158         return mUid;
159     }
160 
161     @Override
getId_imp()162     public String getId_imp() {
163         return mId;
164     }
165 
166     @Override
getType_impl()167     public int getType_impl() {
168         return mType;
169     }
170 
getServiceName()171     String getServiceName() {
172         return mServiceName;
173     }
174 
getSessionBinder()175     IMediaSession2 getSessionBinder() {
176         return mSessionBinder;
177     }
178 
fromBundle_impl(Bundle bundle)179     public static SessionToken2 fromBundle_impl(Bundle bundle) {
180         if (bundle == null) {
181             return null;
182         }
183         final int uid = bundle.getInt(KEY_UID);
184         final @TokenType int type = bundle.getInt(KEY_TYPE, -1);
185         final String packageName = bundle.getString(KEY_PACKAGE_NAME);
186         final String serviceName = bundle.getString(KEY_SERVICE_NAME);
187         final String id = bundle.getString(KEY_ID);
188         final IBinder sessionBinder = bundle.getBinder(KEY_SESSION_BINDER);
189 
190         // Sanity check.
191         switch (type) {
192             case TYPE_SESSION:
193                 if (sessionBinder == null) {
194                     throw new IllegalArgumentException("Unexpected sessionBinder for session,"
195                             + " binder=" + sessionBinder);
196                 }
197                 break;
198             case TYPE_SESSION_SERVICE:
199             case TYPE_LIBRARY_SERVICE:
200                 if (TextUtils.isEmpty(serviceName)) {
201                     throw new IllegalArgumentException("Session service needs service name");
202                 }
203                 break;
204             default:
205                 throw new IllegalArgumentException("Invalid type");
206         }
207         if (TextUtils.isEmpty(packageName) || id == null) {
208             throw new IllegalArgumentException("Package name nor ID cannot be null.");
209         }
210         return new SessionToken2Impl(uid, type, packageName, serviceName, id,
211                 sessionBinder != null ? IMediaSession2.Stub.asInterface(sessionBinder) : null)
212                 .getInstance();
213     }
214 
215     @Override
toBundle_impl()216     public Bundle toBundle_impl() {
217         Bundle bundle = new Bundle();
218         bundle.putInt(KEY_UID, mUid);
219         bundle.putString(KEY_PACKAGE_NAME, mPackageName);
220         bundle.putString(KEY_SERVICE_NAME, mServiceName);
221         bundle.putString(KEY_ID, mId);
222         bundle.putInt(KEY_TYPE, mType);
223         bundle.putBinder(KEY_SESSION_BINDER,
224                 mSessionBinder != null ? mSessionBinder.asBinder() : null);
225         return bundle;
226     }
227 
228     @Override
hashCode_impl()229     public int hashCode_impl() {
230         final int prime = 31;
231         return mType
232                 + prime * (mUid
233                 + prime * (mPackageName.hashCode()
234                 + prime * (mId.hashCode()
235                 + prime * (mServiceName != null ? mServiceName.hashCode() : 0))));
236     }
237 
238     @Override
equals_impl(Object obj)239     public boolean equals_impl(Object obj) {
240         if (!(obj instanceof SessionToken2)) {
241             return false;
242         }
243         SessionToken2Impl other = from((SessionToken2) obj);
244         return mUid == other.mUid
245                 && TextUtils.equals(mPackageName, other.mPackageName)
246                 && TextUtils.equals(mServiceName, other.mServiceName)
247                 && TextUtils.equals(mId, other.mId)
248                 && mType == other.mType;
249     }
250 
251     @Override
toString_impl()252     public String toString_impl() {
253         return "SessionToken {pkg=" + mPackageName + " id=" + mId + " type=" + mType
254                 + " service=" + mServiceName + " binder=" + mSessionBinder + "}";
255     }
256 
from(SessionToken2 token)257     static SessionToken2Impl from(SessionToken2 token) {
258         return ((SessionToken2Impl) token.getProvider());
259     }
260 }
261