diff options
| -rw-r--r-- | core/api/system-current.txt | 8 | ||||
| -rw-r--r-- | core/res/AndroidManifest.xml | 6 | ||||
| -rw-r--r-- | data/etc/privapp-permissions-platform.xml | 1 | ||||
| -rw-r--r-- | media/java/android/media/tv/TunedInfo.java | 44 | ||||
| -rw-r--r-- | media/java/android/media/tv/TvInputManager.java | 3 | ||||
| -rw-r--r-- | packages/Shell/AndroidManifest.xml | 1 | ||||
| -rwxr-xr-x | services/core/java/com/android/server/tv/TvInputManagerService.java | 89 |
7 files changed, 108 insertions, 44 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 4210d1efd6bb..ee31e8592194 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -19,6 +19,7 @@ package android { field public static final String ACCESS_SHARED_LIBRARIES = "android.permission.ACCESS_SHARED_LIBRARIES"; field public static final String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS"; field public static final String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER"; + field public static final String ACCESS_TUNED_INFO = "android.permission.ACCESS_TUNED_INFO"; field public static final String ACCESS_TV_DESCRAMBLER = "android.permission.ACCESS_TV_DESCRAMBLER"; field public static final String ACCESS_TV_TUNER = "android.permission.ACCESS_TV_TUNER"; field public static final String ACCESS_VIBRATOR_STATE = "android.permission.ACCESS_VIBRATOR_STATE"; @@ -5635,8 +5636,9 @@ package android.media.tv { method public int getAppType(); method @Nullable public android.net.Uri getChannelUri(); method @NonNull public String getInputId(); - method public boolean isForeground(); + method public boolean isMainSession(); method public boolean isRecordingSession(); + method public boolean isVisible(); method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int APP_TAG_SELF = 0; // 0x0 field public static final int APP_TYPE_NON_SYSTEM = 3; // 0x3 @@ -5760,7 +5762,7 @@ package android.media.tv { method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating); method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig); method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String); - method @NonNull @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS") public java.util.List<android.media.tv.TunedInfo> getCurrentTunedInfos(); + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO) public java.util.List<android.media.tv.TunedInfo> getCurrentTunedInfos(); method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList(); method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList(); method @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList(); @@ -5787,7 +5789,7 @@ package android.media.tv { } public abstract static class TvInputManager.TvInputCallback { - method public void onCurrentTunedInfosUpdated(@NonNull java.util.List<android.media.tv.TunedInfo>); + method @RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO) public void onCurrentTunedInfosUpdated(@NonNull java.util.List<android.media.tv.TunedInfo>); } public abstract class TvInputService extends android.app.Service { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index f9945c70efd7..0fe7e18c0ac0 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5734,6 +5734,12 @@ <permission android:name="android.permission.SET_CLIP_SOURCE" android:protectionLevel="signature|recents" /> + <!-- @SystemApi Allows an application to access TV tuned info + <p>Not for use by third-party applications. + @hide --> + <permission android:name="android.permission.ACCESS_TUNED_INFO" + android:protectionLevel="signature|privileged|vendorPrivileged" /> + <!-- Allows an application to indicate via {@link android.content.pm.PackageInstaller.SessionParams#setRequireUserAction(boolean)} that user action should not be required for an app update. diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index c5a796fb8ce9..64c914e58411 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -458,6 +458,7 @@ applications that come with the platform <permission name="android.permission.ACCESS_TV_TUNER" /> <permission name="android.permission.TUNER_RESOURCE_ACCESS" /> <!-- Permissions required for CTS test - TVInputManagerTest --> + <permission name="android.permission.ACCESS_TUNED_INFO" /> <permission name="android.permission.TV_INPUT_HARDWARE" /> <!-- Permission required for CTS test - PrivilegedLocationPermissionTest --> <permission name="android.permission.LOCATION_HARDWARE" /> diff --git a/media/java/android/media/tv/TunedInfo.java b/media/java/android/media/tv/TunedInfo.java index 6199c89fe4fa..20acefaea77e 100644 --- a/media/java/android/media/tv/TunedInfo.java +++ b/media/java/android/media/tv/TunedInfo.java @@ -25,6 +25,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; +import android.view.Surface; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -92,18 +93,20 @@ public final class TunedInfo implements Parcelable { private final String mInputId; @Nullable private final Uri mChannelUri; private final boolean mIsRecordingSession; - private final boolean mIsForeground; + private final boolean mIsVisible; + private final boolean mIsMainSession; @AppType private final int mAppType; private final int mAppTag; /** @hide */ public TunedInfo( String inputId, @Nullable Uri channelUri, boolean isRecordingSession, - boolean isForeground, @AppType int appType, int appTag) { + boolean isVisible, boolean isMainSession, @AppType int appType, int appTag) { mInputId = inputId; mChannelUri = channelUri; mIsRecordingSession = isRecordingSession; - mIsForeground = isForeground; + mIsVisible = isVisible; + mIsMainSession = isMainSession; mAppType = appType; mAppTag = appTag; } @@ -114,7 +117,8 @@ public final class TunedInfo implements Parcelable { String uriString = source.readString(); mChannelUri = uriString == null ? null : Uri.parse(uriString); mIsRecordingSession = (source.readInt() == 1); - mIsForeground = (source.readInt() == 1); + mIsVisible = (source.readInt() == 1); + mIsMainSession = (source.readInt() == 1); mAppType = source.readInt(); mAppTag = source.readInt(); } @@ -145,11 +149,23 @@ public final class TunedInfo implements Parcelable { } /** - * Returns {@code true} if the application is a foreground application. - * @see android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND + * Returns {@code true} if the corresponding session is visible. + * <p>The system checks whether the {@link Surface} of the session is {@code null} or not. When + * it becomes invisible, the surface is destroyed and set to null. + * @see TvInputService.Session#onSetSurface(Surface) + * @see android.view.SurfaceView#notifySurfaceDestroyed */ - public boolean isForeground() { - return mIsForeground; + public boolean isVisible() { + return mIsVisible; + } + + /** + * Returns {@code true} if the corresponding session is set as main session. + * @see TvView#setMain + * @see TvInputService.Session#onSetMain + */ + public boolean isMainSession() { + return mIsMainSession; } /** @@ -180,7 +196,8 @@ public final class TunedInfo implements Parcelable { String uriString = mChannelUri == null ? null : mChannelUri.toString(); dest.writeString(uriString); dest.writeInt(mIsRecordingSession ? 1 : 0); - dest.writeInt(mIsForeground ? 1 : 0); + dest.writeInt(mIsVisible ? 1 : 0); + dest.writeInt(mIsMainSession ? 1 : 0); dest.writeInt(mAppType); dest.writeInt(mAppTag); } @@ -190,7 +207,8 @@ public final class TunedInfo implements Parcelable { return "inputID=" + mInputId + ";channelUri=" + mChannelUri + ";isRecording=" + mIsRecordingSession - + ";isForeground=" + mIsForeground + + ";isVisible=" + mIsVisible + + ";isMainSession=" + mIsMainSession + ";appType=" + mAppType + ";appTag=" + mAppTag; } @@ -206,7 +224,8 @@ public final class TunedInfo implements Parcelable { return TextUtils.equals(mInputId, other.getInputId()) && Objects.equals(mChannelUri, other.mChannelUri) && mIsRecordingSession == other.mIsRecordingSession - && mIsForeground == other.mIsForeground + && mIsVisible == other.mIsVisible + && mIsMainSession == other.mIsMainSession && mAppType == other.mAppType && mAppTag == other.mAppTag; } @@ -214,6 +233,7 @@ public final class TunedInfo implements Parcelable { @Override public int hashCode() { return Objects.hash( - mInputId, mChannelUri, mIsRecordingSession, mIsForeground, mAppType, mAppTag); + mInputId, mChannelUri, mIsRecordingSession, mIsVisible, mIsMainSession, mAppType, + mAppTag); } } diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index c0185dcc4539..34e4609a71f7 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -907,6 +907,7 @@ public final class TvInputManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO) public void onCurrentTunedInfosUpdated(@NonNull List<TunedInfo> tunedInfos) { } } @@ -1989,7 +1990,7 @@ public final class TvInputManager { * @hide */ @SystemApi - @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS") + @RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO) @NonNull public List<TunedInfo> getCurrentTunedInfos() { try { diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index b5af9ad21fa2..0958e544a2a9 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -429,6 +429,7 @@ <uses-permission android:name="android.permission.ACCESS_SHARED_LIBRARIES" /> <!-- Permissions required for CTS test - TVInputManagerTest --> + <uses-permission android:name="android.permission.ACCESS_TUNED_INFO" /> <uses-permission android:name="android.permission.TV_INPUT_HARDWARE" /> <!-- Permission needed for CTS test - PrivilegedLocationPermissionTest --> diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index e728ab0944a7..49170f38bd07 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -24,7 +24,6 @@ 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; @@ -109,6 +108,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.regex.Matcher; @@ -511,11 +511,21 @@ public final class TvInputManagerService extends SystemService { sessionStatesToRelease.add(sessionState); } } + boolean notifyInfoUpdated = false; for (SessionState sessionState : sessionStatesToRelease) { try { sessionState.session.release(); + sessionState.currentChannel = null; + if (sessionState.isCurrent) { + sessionState.isCurrent = false; + notifyInfoUpdated = true; + } } catch (RemoteException e) { Slog.e(TAG, "error in release", e); + } finally { + if (notifyInfoUpdated) { + notifyCurrentChannelInfosUpdatedLocked(userState); + } } clearSessionAndNotifyClientLocked(sessionState); } @@ -576,12 +586,22 @@ public final class TvInputManagerService extends SystemService { return; } // Release all created sessions. + boolean notifyInfoUpdated = false; for (SessionState state : userState.sessionStateMap.values()) { if (state.session != null) { try { state.session.release(); + state.currentChannel = null; + if (state.isCurrent) { + state.isCurrent = false; + notifyInfoUpdated = true; + } } catch (RemoteException e) { Slog.e(TAG, "error in release", e); + } finally { + if (notifyInfoUpdated) { + notifyCurrentChannelInfosUpdatedLocked(userState); + } } } } @@ -826,9 +846,11 @@ public final class TvInputManagerService extends SystemService { sessionState.session.asBinder().unlinkToDeath(sessionState, 0); sessionState.session.release(); } - sessionState.isCurrent = false; sessionState.currentChannel = null; - notifyCurrentChannelInfosUpdatedLocked(userState); + if (sessionState.isCurrent) { + sessionState.isCurrent = false; + notifyCurrentChannelInfosUpdatedLocked(userState); + } } catch (RemoteException | SessionNotFoundException e) { Slog.e(TAG, "error in releaseSession", e); } finally { @@ -898,6 +920,11 @@ public final class TvInputManagerService extends SystemService { } ITvInputSession session = getSessionLocked(sessionState); session.setMain(isMain); + if (sessionState.isMainSession != isMain) { + UserState userState = getUserStateLocked(userId); + sessionState.isMainSession = isMain; + notifyCurrentChannelInfosUpdatedLocked(userState); + } } catch (RemoteException | SessionNotFoundException e) { Slog.e(TAG, "error in setMain", e); } @@ -987,6 +1014,10 @@ public final class TvInputManagerService extends SystemService { try { ITvInputManagerCallback callback = userState.mCallbacks.getBroadcastItem(i); Pair<Integer, Integer> pidUid = userState.callbackPidUidMap.get(callback); + if (mContext.checkPermission(android.Manifest.permission.ACCESS_TUNED_INFO, + pidUid.first, pidUid.second) != PackageManager.PERMISSION_GRANTED) { + continue; + } List<TunedInfo> infos = getCurrentTunedInfosInternalLocked( userState, pidUid.first, pidUid.second); callback.onCurrentTunedInfosUpdated(infos); @@ -1517,6 +1548,11 @@ public final class TvInputManagerService extends SystemService { getSessionLocked(sessionState.hardwareSessionToken, Process.SYSTEM_UID, resolvedUserId).setSurface(surface); } + boolean isVisible = (surface == null); + if (sessionState.isVisible != isVisible) { + sessionState.isVisible = isVisible; + notifyCurrentChannelInfosUpdatedLocked(userState); + } } catch (RemoteException | SessionNotFoundException e) { Slog.e(TAG, "error in setSurface", e); } @@ -1609,9 +1645,12 @@ public final class TvInputManagerService extends SystemService { UserState userState = getOrCreateUserStateLocked(resolvedUserId); SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userState); - sessionState.isCurrent = true; - sessionState.currentChannel = channelUri; - notifyCurrentChannelInfosUpdatedLocked(userState); + if (!sessionState.isCurrent + || !Objects.equals(sessionState.currentChannel, channelUri)) { + sessionState.isCurrent = true; + sessionState.currentChannel = channelUri; + notifyCurrentChannelInfosUpdatedLocked(userState); + } if (TvContract.isChannelUriForPassthroughInput(channelUri)) { // Do not log the watch history for passthrough inputs. return; @@ -2309,6 +2348,11 @@ public final class TvInputManagerService extends SystemService { @Override public List<TunedInfo> getCurrentTunedInfos(@UserIdInt int userId) { + if (mContext.checkCallingPermission(android.Manifest.permission.ACCESS_TUNED_INFO) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException( + "The caller does not have access tuned info permission"); + } int callingPid = Binder.getCallingPid(); int callingUid = Binder.getCallingUid(); final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, @@ -2524,7 +2568,8 @@ public final class TvInputManagerService extends SystemService { state.inputId, watchedProgramsAccess ? state.currentChannel : null, state.isRecordingSession, - isForeground(state.callingPid), + state.isVisible, + state.isMainSession, appType, appTag)); } @@ -2532,23 +2577,6 @@ public final class TvInputManagerService extends SystemService { return channelInfos; } - private 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(int callingPid, int callingUid) { return mContext.checkPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS, callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; @@ -2788,6 +2816,8 @@ public final class TvInputManagerService extends SystemService { private boolean isCurrent = false; private Uri currentChannel = null; + private boolean isVisible = false; + private boolean isMainSession = false; private SessionState(IBinder sessionToken, String inputId, ComponentName componentName, boolean isRecordingSession, ITvInputClient client, int seq, int callingUid, @@ -3039,16 +3069,19 @@ public final class TvInputManagerService extends SystemService { if (mSessionState.session == null || mSessionState.client == null) { return; } - mSessionState.isCurrent = true; - mSessionState.currentChannel = channelUri; - UserState userState = getOrCreateUserStateLocked(mSessionState.userId); - notifyCurrentChannelInfosUpdatedLocked(userState); try { // TODO: Consider adding this channel change in the watch log. When we do // that, how we can protect the watch log from malicious tv inputs should // be addressed. e.g. add a field which represents where the channel change // originated from. mSessionState.client.onChannelRetuned(channelUri, mSessionState.seq); + if (!mSessionState.isCurrent + || !Objects.equals(mSessionState.currentChannel, channelUri)) { + UserState userState = getOrCreateUserStateLocked(mSessionState.userId); + mSessionState.isCurrent = true; + mSessionState.currentChannel = channelUri; + notifyCurrentChannelInfosUpdatedLocked(userState); + } } catch (RemoteException e) { Slog.e(TAG, "error in onChannelRetuned", e); } |