Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43ba05c0 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add API getCurrentChannelInfos"

parents b9e67709 47a260f8
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.media.tv.ITvInputClient;
import android.media.tv.ITvInputHardware;
import android.media.tv.ITvInputHardwareCallback;
import android.media.tv.ITvInputManagerCallback;
import android.media.tv.TvChannelInfo;
import android.media.tv.TvContentRatingSystemInfo;
import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvInputInfo;
@@ -88,6 +89,8 @@ interface ITvInputManager {
    void timeShiftSetPlaybackParams(in IBinder sessionToken, in PlaybackParams params, int userId);
    void timeShiftEnablePositionTracking(in IBinder sessionToken, boolean enable, int userId);

    List<TvChannelInfo> getTvCurrentChannelInfos(int userId);

    // For the recording session
    void startRecording(in IBinder sessionToken, in Uri programUri, in Bundle params, int userId);
    void stopRecording(in IBinder sessionToken, int userId);
+19 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020 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.tv;

parcelable TvChannelInfo;
+148 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020 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.tv;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * @hide
 */
public final class TvChannelInfo implements Parcelable {
    static final String TAG = "TvChannelInfo";
    public static final int APP_TAG_SELF = 0;
    public static final int APP_TYPE_SELF = 1;
    public static final int APP_TYPE_SYSTEM = 2;
    public static final int APP_TYPE_NON_SYSTEM = 3;

    /** @hide */
    @IntDef(prefix = "APP_TYPE_", value = {APP_TYPE_SELF, APP_TYPE_SYSTEM, APP_TYPE_NON_SYSTEM})
    @Retention(RetentionPolicy.SOURCE)
    public @interface AppType {}

    public static final @NonNull Parcelable.Creator<TvChannelInfo> CREATOR =
            new Parcelable.Creator<TvChannelInfo>() {
                @Override
                public TvChannelInfo createFromParcel(Parcel source) {
                    try {
                        return new TvChannelInfo(source);
                    } catch (Exception e) {
                        Log.e(TAG, "Exception creating TvChannelInfo from parcel", e);
                        return null;
                    }
                }

                @Override
                public TvChannelInfo[] newArray(int size) {
                    return new TvChannelInfo[size];
                }
            };


    private final String mInputId;
    @Nullable private final Uri mChannelUri;
    private final boolean mIsRecordingSession;
    private final boolean mIsForeground;
    @AppType private final int mAppType;
    private final int mAppTag;

    public TvChannelInfo(
            String inputId, @Nullable Uri channelUri, boolean isRecordingSession,
            boolean isForeground, @AppType int appType, int appTag) {
        mInputId = inputId;
        mChannelUri = channelUri;
        mIsRecordingSession = isRecordingSession;
        mIsForeground = isForeground;
        mAppType = appType;
        mAppTag = appTag;
    }


    private TvChannelInfo(Parcel source) {
        mInputId = source.readString();
        String uriString = source.readString();
        mChannelUri = uriString == null ? null : Uri.parse(uriString);
        mIsRecordingSession = (source.readInt() == 1);
        mIsForeground = (source.readInt() == 1);
        mAppType = source.readInt();
        mAppTag = source.readInt();
    }

    public String getInputId() {
        return mInputId;
    }

    public Uri getChannelUri() {
        return mChannelUri;
    }

    public boolean isRecordingSession() {
        return mIsRecordingSession;
    }

    public boolean isForeground() {
        return mIsForeground;
    }

    /**
     * Gets app tag.
     * <p>App tag is used to differentiate one app from another.
     * {@link #APP_TAG_SELF} is for current app.
     */
    public int getAppTag() {
        return mAppTag;
    }

    @AppType
    public int getAppType() {
        return mAppType;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mInputId);
        String uriString = mChannelUri == null ? null : mChannelUri.toString();
        dest.writeString(uriString);
        dest.writeInt(mIsRecordingSession ? 1 : 0);
        dest.writeInt(mIsForeground ? 1 : 0);
        dest.writeInt(mAppType);
        dest.writeInt(mAppTag);
    }

    @Override
    public String toString() {
        return "inputID=" + mInputId
                + ";channelUri=" + mChannelUri
                + ";isRecording=" + mIsRecordingSession
                + ";isForeground=" + mIsForeground
                + ";appType=" + mAppType
                + ";appTag=" + mAppTag;
    }
}
+13 −0
Original line number Diff line number Diff line
@@ -1952,6 +1952,19 @@ public final class TvInputManager {
        }
    }

    /**
     * @hide
     */
    public List<TvChannelInfo> getTvCurrentChannelInfos() {
        // TODO: handle retuned() cases and add a method to TvInputCallback
        // TODO: unhide
        try {
            return mService.getTvCurrentChannelInfos(mUserId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * The Session provides the per-session functionality of TV inputs.
     * @hide
+103 −2
Original line number Diff line number Diff line
@@ -22,7 +22,9 @@ import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -33,6 +35,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
@@ -51,6 +54,7 @@ import android.media.tv.ITvInputService;
import android.media.tv.ITvInputServiceCallback;
import android.media.tv.ITvInputSession;
import android.media.tv.ITvInputSessionCallback;
import android.media.tv.TvChannelInfo;
import android.media.tv.TvContentRating;
import android.media.tv.TvContentRatingSystemInfo;
import android.media.tv.TvContract;
@@ -78,6 +82,7 @@ import android.util.SparseArray;
import android.view.InputChannel;
import android.view.Surface;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
@@ -108,6 +113,9 @@ public final class TvInputManagerService extends SystemService {
    private static final boolean DEBUG = false;
    private static final String TAG = "TvInputManagerService";
    private static final String DVB_DIRECTORY = "/dev/dvb";
    private static final int APP_TAG_SELF = TvChannelInfo.APP_TAG_SELF;
    private static final String PERMISSION_ACCESS_WATCHED_PROGRAMS =
            "com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS";

    // There are two different formats of DVB frontend devices. One is /dev/dvb%d.frontend%d,
    // another one is /dev/dvb/adapter%d/frontend%d. Followings are the patterns for selecting the
@@ -136,6 +144,8 @@ public final class TvInputManagerService extends SystemService {

    private final WatchLogHandler mWatchLogHandler;

    private final ActivityManager mActivityManager;

    public TvInputManagerService(Context context) {
        super(context);

@@ -144,6 +154,9 @@ public final class TvInputManagerService extends SystemService {
                IoThread.get().getLooper());
        mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());

        mActivityManager =
                (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);

        synchronized (mLock) {
            getOrCreateUserStateLocked(mCurrentUserId);
        }
@@ -694,6 +707,8 @@ public final class TvInputManagerService extends SystemService {
                sessionState.session.asBinder().unlinkToDeath(sessionState, 0);
                sessionState.session.release();
            }
            sessionState.isCurrent = false;
            sessionState.currentChannel = null;
        } catch (RemoteException | SessionNotFoundException e) {
            Slog.e(TAG, "error in releaseSession", e);
        } finally {
@@ -1394,13 +1409,17 @@ public final class TvInputManagerService extends SystemService {
                    try {
                        getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(
                                channelUri, params);
                        UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
                        if (sessionState != null) {
                            sessionState.isCurrent = true;
                            sessionState.currentChannel = channelUri;
                        }
                        if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
                            // Do not log the watch history for passthrough inputs.
                            return;
                        }

                        UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
                        if (sessionState.isRecordingSession) {
                            return;
                        }
@@ -2049,6 +2068,80 @@ public final class TvInputManagerService extends SystemService {
            return clientPid;
        }

        @Override
        public List<TvChannelInfo> getTvCurrentChannelInfos(@UserIdInt int userId) {
            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
                    Binder.getCallingUid(), userId, "getTvCurrentChannelInfos");
            final long identity = Binder.clearCallingIdentity();
            try {
                synchronized (mLock) {
                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                    List<TvChannelInfo> channelInfos = new ArrayList<>();
                    boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission();
                    for (SessionState state : userState.sessionStateMap.values()) {
                        if (state.isCurrent) {
                            Integer appTag;
                            int appType;
                            if (state.callingUid == Binder.getCallingUid()) {
                                appTag = APP_TAG_SELF;
                                appType = TvChannelInfo.APP_TYPE_SELF;
                            } else {
                                appTag = userState.mAppTagMap.get(state.callingUid);
                                if (appTag == null) {
                                    appTag = userState.mNextAppTag++;
                                    userState.mAppTagMap.put(state.callingUid, appTag);
                                }
                                appType = isSystemApp(state.componentName.getPackageName())
                                        ? TvChannelInfo.APP_TYPE_SYSTEM
                                        : TvChannelInfo.APP_TYPE_NON_SYSTEM;
                            }
                            channelInfos.add(new TvChannelInfo(
                                    state.inputId,
                                    watchedProgramsAccess ? state.currentChannel : null,
                                    state.isRecordingSession,
                                    isForeground(state.callingPid),
                                    appType,
                                    appTag));
                        }
                    }
                    return channelInfos;
                }
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
        }

        protected boolean isForeground(int pid) {
            if (mActivityManager == null) {
                return false;
            }
            List<RunningAppProcessInfo> appProcesses = mActivityManager.getRunningAppProcesses();
            if (appProcesses == null) {
                return false;
            }
            for (RunningAppProcessInfo appProcess : appProcesses) {
                if (appProcess.pid == pid
                        && appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                    return true;
                }
            }
            return false;
        }

        private boolean hasAccessWatchedProgramsPermission() {
            return mContext.checkCallingPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS)
                    == PackageManager.PERMISSION_GRANTED;
        }

        private boolean isSystemApp(String pkg) {
            try {
                return (mContext.getPackageManager().getApplicationInfo(pkg, 0).flags
                        & ApplicationInfo.FLAG_SYSTEM) != 0;
            } catch (NameNotFoundException e) {
                return false;
            }
        }

        /**
         * Add a hardware device in the TvInputHardwareManager for CTS testing
         * purpose.
@@ -2250,6 +2343,11 @@ public final class TvInputManagerService extends SystemService {
        // service.
        private final PersistentDataStore persistentDataStore;

        @GuardedBy("mLock")
        private final Map<Integer, Integer> mAppTagMap = new HashMap<>();
        @GuardedBy("mLock")
        private int mNextAppTag = 1;

        private UserState(Context context, int userId) {
            persistentDataStore = new PersistentDataStore(context, userId);
        }
@@ -2336,6 +2434,9 @@ public final class TvInputManagerService extends SystemService {
        // Not null if this session represents an external device connected to a hardware TV input.
        private IBinder hardwareSessionToken;

        private boolean isCurrent = false;
        private Uri currentChannel = null;

        private SessionState(IBinder sessionToken, String inputId, ComponentName componentName,
                boolean isRecordingSession, ITvInputClient client, int seq, int callingUid,
                int callingPid, int userId, String sessionId) {