diff options
| -rw-r--r-- | media/java/android/media/Session2Token.java | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/media/java/android/media/Session2Token.java b/media/java/android/media/Session2Token.java new file mode 100644 index 000000000000..af140789ea85 --- /dev/null +++ b/media/java/android/media/Session2Token.java @@ -0,0 +1,288 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.Objects; + +/** + * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}. + * If it's representing a session service, it may not be ongoing. + * <p> + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx/">Support Library</a> instead. + * {@link androidx.media2.SessionToken} for consistent behavior across all devices. + * <p> + * This may be passed to apps by the session owner to allow them to create a + * {@link MediaController2} to communicate with the session. + * <p> + * It can be also obtained by {@link MediaSessionManager}. + * + * @hide + */ +// New version of MediaSession2.Token for following reasons +// - Stop implementing Parcelable for updatable support +// - Represent session and library service (formerly browser service) in one class. +// Previously MediaSession2.Token was for session and ComponentName was for service. +// This helps controller apps to keep target of dispatching media key events in uniform way. +// For details about the reason, see following. (Android O+) +// android.media.session.MediaSessionManager.Callback#onAddressedPlayerChanged +public final class Session2Token implements Parcelable { + private static final String TAG = "Session2Token"; + + public static final Creator<Session2Token> CREATOR = new Creator<Session2Token>() { + @Override + public Session2Token createFromParcel(Parcel p) { + return new Session2Token(p); + } + + @Override + public Session2Token[] newArray(int size) { + return new Session2Token[size]; + } + }; + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "TYPE_", value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE}) + public @interface TokenType { + } + + /** + * Type for {@link MediaSession2}. + */ + public static final int TYPE_SESSION = 0; + + /** + * Type for {@link MediaSessionService2}. + */ + public static final int TYPE_SESSION_SERVICE = 1; + + /** + * Type for {@link MediaLibraryService2}. + */ + public static final int TYPE_LIBRARY_SERVICE = 2; + + private final int mUid; + private final @TokenType int mType; + private final String mPackageName; + private final String mServiceName; + private final IBinder mISession; + private final ComponentName mComponentName; + + /** + * Constructor for the token. You can create token of {@link MediaSessionService2}, + * {@link MediaLibraryService2} for {@link MediaController2} or {@link MediaBrowser2}. + * + * @param context The context. + * @param serviceComponent The component name of the service. + */ + public Session2Token(@NonNull Context context, @NonNull ComponentName serviceComponent) { + if (context == null) { + throw new IllegalArgumentException("context shouldn't be null"); + } + if (serviceComponent == null) { + throw new IllegalArgumentException("serviceComponent shouldn't be null"); + } + + final PackageManager manager = context.getPackageManager(); + final int uid = getUid(manager, serviceComponent.getPackageName()); + + // TODO: Uncomment below to stop hardcode type. + final int type = TYPE_SESSION_SERVICE; +// final int type; +// if (isInterfaceDeclared(manager, MediaLibraryService2.SERVICE_INTERFACE, +// serviceComponent)) { +// type = TYPE_LIBRARY_SERVICE; +// } else if (isInterfaceDeclared(manager, MediaSessionService2.SERVICE_INTERFACE, +// serviceComponent)) { +// type = TYPE_SESSION_SERVICE; +// } else if (isInterfaceDeclared(manager, +// MediaBrowserServiceCompat.SERVICE_INTERFACE, serviceComponent)) { +// type = TYPE_BROWSER_SERVICE_LEGACY; +// } else { +// throw new IllegalArgumentException(serviceComponent + " doesn't implement none of" +// + " MediaSessionService2, MediaLibraryService2, MediaBrowserService nor" +// + " MediaBrowserServiceCompat. Use service's full name."); +// } + mComponentName = serviceComponent; + mPackageName = serviceComponent.getPackageName(); + mServiceName = serviceComponent.getClassName(); + mUid = uid; + mType = type; + mISession = null; + } + + // TODO: Uncomment below +// Session2Token(int uid, int type, String packageName, IMediaSession2 iSession) { +// mUid = uid; +// mType = type; +// mPackageName = packageName; +// mServiceName = null; +// mComponentName = null; +// mISession = iSession.asBinder(); +// } + + Session2Token(Parcel in) { + mUid = in.readInt(); + mType = in.readInt(); + mPackageName = in.readString(); + mServiceName = in.readString(); + // TODO: Uncomment below and stop hardcode mISession + mISession = null; + //mISession = ISession.Stub.asInterface(in.readStrongBinder()); + mComponentName = ComponentName.unflattenFromString(in.readString()); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mUid); + dest.writeInt(mType); + dest.writeString(mPackageName); + dest.writeString(mServiceName); + // TODO: Uncomment below + //dest.writeStrongBinder(mISession.asBinder()); + dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(mType, mUid, mPackageName, mServiceName, mISession); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Session2Token)) { + return false; + } + Session2Token other = (Session2Token) obj; + return mUid == other.mUid + && TextUtils.equals(mPackageName, other.mPackageName) + && TextUtils.equals(mServiceName, other.mServiceName) + && mType == other.mType + && Objects.equals(mISession, other.mISession); + } + + @Override + public String toString() { + return "Session2Token {pkg=" + mPackageName + " type=" + mType + + " service=" + mServiceName + " IMediaSession2=" + mISession + "}"; + } + + /** + * @return uid of the session + */ + public int getUid() { + return mUid; + } + + /** + * @return package name of the session + */ + @NonNull + public String getPackageName() { + return mPackageName; + } + + /** + * @return service name of the session. Can be {@code null} for {@link #TYPE_SESSION}. + */ + @Nullable + public String getServiceName() { + return mServiceName; + } + + /** + * @hide + * @return component name of the session. Can be {@code null} for {@link #TYPE_SESSION}. + */ + public ComponentName getComponentName() { + return mComponentName; + } + + /** + * @return type of the token + * @see #TYPE_SESSION + * @see #TYPE_SESSION_SERVICE + * @see #TYPE_LIBRARY_SERVICE + */ + public @TokenType int getType() { + return mType; + } + + /** + * @hide + */ + public Object getBinder() { + return mISession; + } + + private static boolean isInterfaceDeclared(PackageManager manager, String serviceInterface, + ComponentName serviceComponent) { + Intent serviceIntent = new Intent(serviceInterface); + // Use queryIntentServices to find services with MediaLibraryService2.SERVICE_INTERFACE. + // We cannot use resolveService with intent specified class name, because resolveService + // ignores actions if Intent.setClassName() is specified. + serviceIntent.setPackage(serviceComponent.getPackageName()); + + List<ResolveInfo> list = manager.queryIntentServices( + serviceIntent, PackageManager.GET_META_DATA); + if (list != null) { + for (int i = 0; i < list.size(); i++) { + ResolveInfo resolveInfo = list.get(i); + if (resolveInfo == null || resolveInfo.serviceInfo == null) { + continue; + } + if (TextUtils.equals( + resolveInfo.serviceInfo.name, serviceComponent.getClassName())) { + return true; + } + } + } + return false; + } + + private static int getUid(PackageManager manager, String packageName) { + try { + return manager.getApplicationInfo(packageName, 0).uid; + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalArgumentException("Cannot find package " + packageName); + } + } +} |