Android11实现能同时开多个录屏应用(或者共享屏幕或投屏时录屏)

2024-03-19 09:36

本文主要是介绍Android11实现能同时开多个录屏应用(或者共享屏幕或投屏时录屏),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.概述

Android原生对MediaProjection的管理逻辑,是如果服务端已经保存有MediaProjection的实例,那么再次创建的时候,之前的MediaProjection实例就会被暂停,并且引用指向新的实例,也就导致了当开启后一个录屏应用时,前一个录屏应用会被暂停。为了能实现多个录屏应用同时录屏(或者共享屏幕或投屏的时候录屏),我们需要对framework进行修改,只需对一个文件进行修改:frameworks/base/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java

2.实现能同时开启多个MediaProjection

大致的思路就是修改MediaProjectionManagerService.java,使得MediaProjectionManagerService服务端能够保存多个MediaProjeciton实例。

/** Copyright (C) 2014 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 com.android.server.media.projection;import android.Manifest;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.IProcessObserver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.hardware.display.DisplayManager;
import android.media.MediaRouter;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionCallback;
import android.media.projection.IMediaProjectionManager;
import android.media.projection.IMediaProjectionWatcherCallback;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.Watchdog;import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** Manages MediaProjection sessions.** The {@link MediaProjectionManagerService} manages the creation and lifetime of MediaProjections,* as well as the capabilities they grant. Any service using MediaProjection tokens as permission* grants <b>must</b> validate the token before use by calling {@link* IMediaProjectionService#isValidMediaProjection}.*/
public final class MediaProjectionManagerService extends SystemServiceimplements Watchdog.Monitor {private static final boolean REQUIRE_FG_SERVICE_FOR_PROJECTION = true;private static final String TAG = "MediaProjectionManagerService";private static boolean DEBUG = false;private final Object mLock = new Object(); // Protects the list of media projectionsprivate final Map<IBinder, IBinder.DeathRecipient> mDeathEaters;private final CallbackDelegate mCallbackDelegate;private final Context mContext;private final AppOpsManager mAppOps;private final ActivityManagerInternal mActivityManagerInternal;private final PackageManager mPackageManager;private final MediaRouter mMediaRouter;private final MediaRouterCallback mMediaRouterCallback;private MediaRouter.RouteInfo mMediaRouteInfo;private Map<IBinder, MediaProjectionEntry> mEntryMap;public MediaProjectionManagerService(Context context) {super(context);mContext = context;mDeathEaters = new ArrayMap<IBinder, IBinder.DeathRecipient>();mCallbackDelegate = new CallbackDelegate();mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);mPackageManager = mContext.getPackageManager();mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);mMediaRouterCallback = new MediaRouterCallback();mEntryMap = new ArrayMap<IBinder, MediaProjectionEntry>();if ("userdebug".equals(SystemProperties.get("ro.build.type", "user"))) {DEBUG = true;}Watchdog.getInstance().addMonitor(this);}@Overridepublic void onStart() {logd("onStart");publishBinderService(Context.MEDIA_PROJECTION_SERVICE, new BinderService(),false /*allowIsolated*/);mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, mMediaRouterCallback,MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);if (REQUIRE_FG_SERVICE_FOR_PROJECTION) {mActivityManagerInternal.registerProcessObserver(new IProcessObserver.Stub() {@Overridepublic void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {}@Overridepublic void onForegroundServicesChanged(int pid, int uid, int serviceTypes) {MediaProjectionManagerService.this.handleForegroundServicesChanged(pid, uid,serviceTypes);}@Overridepublic void onProcessDied(int pid, int uid) {}});}}@Overridepublic void onSwitchUser(int userId) {logd("onSwitchUser: userId=" + userId);mMediaRouter.rebindAsUser(userId);synchronized (mLock) {if (mEntryMap != null && !mEntryMap.isEmpty()) {for (MediaProjectionEntry entry : mEntryMap.values()) {if (entry != null) {entry.getProjectionGrant().stop();}}}}}@Overridepublic void monitor() {synchronized (mLock) { /* check for deadlock */ }}/*** Called when the set of active foreground service types for a given {@code uid / pid} changes.* We will stop the active projection grant if its owner targets {@code Q} or higher and has no* started foreground services of type {@code FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION}.*/private void handleForegroundServicesChanged(int pid, int uid, int serviceTypes) {synchronized (mLock) {logd("handleForegroundServicesChanged: pid=" + pid + ", uid=" + uid+ ", serviceTypes=" + serviceTypes);if (mEntryMap == null || mEntryMap.isEmpty()) {return;}String uidpid = String.format("%s_%s", uid, pid);MediaProjectionEntry entry = getMediaProjectionEntryByUidPid(uidpid);if (entry == null || entry.getProjectionGrant() == null) {return;}MediaProjection mediaProjection = entry.getProjectionGrant();if (!mediaProjection.requiresForegroundService()) {return;}if ((serviceTypes & ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION) != 0) {return;}mediaProjection.stop();logd("handleForegroundServicesChanged: MediaProjection="+ mediaProjection.getProjectionInfo() + " hashCode "+ mediaProjection.asBinder().hashCode() + " stoped");}}private void startProjectionLocked(final String uidpid, final MediaProjection projection) {logd("startProjectionLocked: calling uidpid " + uidpid+ ", MediaProjection=" + projection.getProjectionInfo()+ " hashCode=" + projection.asBinder().hashCode()+ ", current MediaProjection count " + mEntryMap.size());if (mMediaRouteInfo != null) {mMediaRouter.getFallbackRoute().select();}List<IBinder> removedMediaProjections = new ArrayList<>();logd(">>> MediaProjections:");if (mEntryMap.isEmpty()) {logd(">>> null");} else {for (MediaProjectionEntry entry : mEntryMap.values()) {if (entry != null) {logd(">>> " + entry.getProjectionGrant().getProjectionInfo()+ " hashCode " + entry.getProjectionToken().hashCode());MediaProjection mp = entry.getProjectionGrant();if (mp.packageName.equals(projection.packageName)) {removedMediaProjections.add(entry.getProjectionToken());}} else {logd(">>> null");}}}for (IBinder token : removedMediaProjections) {MediaProjectionEntry entry = mEntryMap.get(token);if (entry != null) {MediaProjection mp = entry.getProjectionGrant();logd(mp.packageName + " already existing MediaProjection="+ mp.getProjectionInfo() + " hashCode " + mp.asBinder().hashCode()+ ", remove it");mp.stop();}}MediaProjectionEntry entry = new MediaProjectionEntry(uidpid, projection.asBinder(),projection);mEntryMap.put(projection.asBinder(), entry);dispatchStart(projection);logd("startProjectionLocked: start success, "+ "current MediaProjection count " + mEntryMap.size());}private void stopProjectionLocked(final MediaProjection projection) {logd("stopProjectionLocked: MediaProjection=" + projection.getProjectionInfo()+ " hashCode " + projection.asBinder().hashCode());mEntryMap.remove(projection.asBinder());dispatchStop(projection);logd("stopProjectionLocked: stop success, "+ "remaining MediaProjection count " + mEntryMap.size());}private void addCallback(final IMediaProjectionWatcherCallback callback) {logd("addCallback: callback=" + callback);IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {@Overridepublic void binderDied() {removeCallback(callback);}};synchronized (mLock) {mCallbackDelegate.add(callback);linkDeathRecipientLocked(callback, deathRecipient);}}private void removeCallback(IMediaProjectionWatcherCallback callback) {synchronized (mLock) {logd("removeCallback: callback=" + callback);unlinkDeathRecipientLocked(callback);mCallbackDelegate.remove(callback);}}private void linkDeathRecipientLocked(IMediaProjectionWatcherCallback callback,IBinder.DeathRecipient deathRecipient) {try {logd("linkDeathRecipientLocked: callback=" + callback+ ", deathRecipient=" + deathRecipient);final IBinder token = callback.asBinder();token.linkToDeath(deathRecipient, 0);mDeathEaters.put(token, deathRecipient);} catch (RemoteException e) {Slog.e(TAG, "Unable to link to death for media projection monitoring callback", e);}}private void unlinkDeathRecipientLocked(IMediaProjectionWatcherCallback callback) {logd("unlinkDeathRecipientLocked: callback=" + callback);final IBinder token = callback.asBinder();IBinder.DeathRecipient deathRecipient = mDeathEaters.remove(token);if (deathRecipient != null) {token.unlinkToDeath(deathRecipient, 0);}}private void dispatchStart(MediaProjection projection) {logd("dispatchStart: MediaProjection=" + projection.getProjectionInfo()+ " hashCode " + projection.asBinder().hashCode());mCallbackDelegate.dispatchStart(projection);}private void dispatchStop(MediaProjection projection) {logd("dispatchStop: MediaProjection=" + projection.getProjectionInfo()+ " hashCode " + projection.asBinder().hashCode());mCallbackDelegate.dispatchStop(projection);}private boolean isValidMediaProjection(IBinder token) {synchronized (mLock) {logd("isValidMediaProjection: MediaProjection hashCode " + token.hashCode());if (mEntryMap == null && mEntryMap.isEmpty()) {return false;}for (MediaProjectionEntry entry : mEntryMap.values()) {if (entry != null && entry.getProjectionToken().equals(token)) {logd("isValidMediaProjection: MediaProjection hashCode "+ token.hashCode() + " is valid");return true;}}logd("isValidMediaProjection: MediaProjection hashCode "+ token.hashCode() + " is invalid");return false;}}private MediaProjectionInfo getActiveProjectionInfo(String uidpid) {synchronized (mLock) {if (mEntryMap == null || mEntryMap.isEmpty()) {return null;}MediaProjectionEntry entry = getMediaProjectionEntryByUidPid(uidpid);if (entry == null) {return null;}logd("getActiveProjectionInfo: MediaProjection="+ entry.getProjectionGrant().getProjectionInfo()+ " hashCode " + entry.getProjectionToken().hashCode());return entry.getProjectionGrant().getProjectionInfo();}}private void dump(final PrintWriter pw) {pw.println("MEDIA PROJECTION MANAGER (dumpsys media_projection)");synchronized (mLock) {pw.println("Media Projection: ");if (mEntryMap != null && !mEntryMap.isEmpty()) {for (MediaProjectionEntry entry : mEntryMap.values()) {if (entry != null) {entry.dump(pw);}}} else {pw.println("null");}}}private final class BinderService extends IMediaProjectionManager.Stub {@Override // Binder callpublic boolean hasProjectionPermission(int uid, String packageName) {logd("BinderService hasProjectionPermission: uid=" + uid+ ", packageName=" + packageName);long token = Binder.clearCallingIdentity();boolean hasPermission = false;try {hasPermission |= checkPermission(packageName,android.Manifest.permission.CAPTURE_VIDEO_OUTPUT)|| mAppOps.noteOpNoThrow(AppOpsManager.OP_PROJECT_MEDIA, uid, packageName)== AppOpsManager.MODE_ALLOWED;} finally {Binder.restoreCallingIdentity(token);}return hasPermission;}@Override // Binder callpublic IMediaProjection createProjection(int uid, String packageName, int type,boolean isPermanentGrant) {final String uidpid = getCallingUidPid();logd("BinderService createProjection: uid=" + uid+ ", packageName=" + packageName + ", " + "type=" + type+ ", isPermanentGrant=" + isPermanentGrant + ", calling uidpid " + uidpid);if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION)!= PackageManager.PERMISSION_GRANTED) {throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to grant "+ "projection permission");}if (packageName == null || packageName.isEmpty()) {throw new IllegalArgumentException("package name must not be empty");}final UserHandle callingUser = Binder.getCallingUserHandle();long callingToken = Binder.clearCallingIdentity();MediaProjection projection;try {ApplicationInfo ai;try {ai = mPackageManager.getApplicationInfoAsUser(packageName, 0, callingUser);} catch (NameNotFoundException e) {throw new IllegalArgumentException("No package matching :" + packageName);}projection = new MediaProjection(type, uid, packageName, ai.targetSdkVersion,ai.isPrivilegedApp());if (isPermanentGrant) {mAppOps.setMode(AppOpsManager.OP_PROJECT_MEDIA,projection.uid, projection.packageName, AppOpsManager.MODE_ALLOWED);}} finally {Binder.restoreCallingIdentity(callingToken);}return projection;}@Override // Binder callpublic boolean isValidMediaProjection(IMediaProjection projection) {return MediaProjectionManagerService.this.isValidMediaProjection(projection.asBinder());}@Override // Binder callpublic MediaProjectionInfo getActiveProjectionInfo() {final String uidpid = getCallingUidPid();logd("BinderService getActiveProjectionInfo: calling uidpid " + uidpid);if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION)!= PackageManager.PERMISSION_GRANTED) {throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add "+ "projection callbacks");}final long token = Binder.clearCallingIdentity();try {return MediaProjectionManagerService.this.getActiveProjectionInfo(uidpid);} finally {Binder.restoreCallingIdentity(token);}}@Override // Binder callpublic void stopActiveProjection() {final String uidpid = getCallingUidPid();logd("BinderService stopActiveProjection: calling uidpid " + uidpid);if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION)!= PackageManager.PERMISSION_GRANTED) {throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add "+ "projection callbacks");}final long token = Binder.clearCallingIdentity();try {MediaProjectionEntry entry = getMediaProjectionEntryByUidPid(uidpid);if (entry != null) {logd("BinderService stopActiveProjection: MediaProjection="+ entry.getProjectionGrant().getProjectionInfo()+ " hashCode " + entry.getProjectionToken().hashCode());entry.getProjectionGrant().stop();}} finally {Binder.restoreCallingIdentity(token);}}@Override //Binder callpublic void addCallback(final IMediaProjectionWatcherCallback callback) {logd("BinderService addCallback: callback=" + callback);if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION)!= PackageManager.PERMISSION_GRANTED) {throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add "+ "projection callbacks");}final long token = Binder.clearCallingIdentity();try {MediaProjectionManagerService.this.addCallback(callback);} finally {Binder.restoreCallingIdentity(token);}}@Overridepublic void removeCallback(IMediaProjectionWatcherCallback callback) {logd("BinderService removeCallback: callback=" + callback);if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION)!= PackageManager.PERMISSION_GRANTED) {throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to remove "+ "projection callbacks");}final long token = Binder.clearCallingIdentity();try {MediaProjectionManagerService.this.removeCallback(callback);} finally {Binder.restoreCallingIdentity(token);}}@Override // Binder callpublic void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;final long token = Binder.clearCallingIdentity();try {MediaProjectionManagerService.this.dump(pw);} finally {Binder.restoreCallingIdentity(token);}}private boolean checkPermission(String packageName, String permission) {return mContext.getPackageManager().checkPermission(permission, packageName)== PackageManager.PERMISSION_GRANTED;}}private final class MediaProjection extends IMediaProjection.Stub {public final int uid;public final String packageName;public final UserHandle userHandle;private final int mTargetSdkVersion;private final boolean mIsPrivileged;private final int mType;private IMediaProjectionCallback mCallback;private IBinder mToken;private IBinder.DeathRecipient mDeathEater;private boolean mRestoreSystemAlertWindow;MediaProjection(int type, int uid, String packageName, int targetSdkVersion,boolean isPrivileged) {mType = type;this.uid = uid;this.packageName = packageName;userHandle = new UserHandle(UserHandle.getUserId(uid));mTargetSdkVersion = targetSdkVersion;mIsPrivileged = isPrivileged;}@Override // Binder callpublic boolean canProjectVideo() {return mType == MediaProjectionManager.TYPE_MIRRORING ||mType == MediaProjectionManager.TYPE_SCREEN_CAPTURE;}@Override // Binder callpublic boolean canProjectSecureVideo() {return false;}@Override // Binder callpublic boolean canProjectAudio() {return mType == MediaProjectionManager.TYPE_MIRRORING|| mType == MediaProjectionManager.TYPE_PRESENTATION|| mType == MediaProjectionManager.TYPE_SCREEN_CAPTURE;}@Override // Binder callpublic int applyVirtualDisplayFlags(int flags) {if (mType == MediaProjectionManager.TYPE_SCREEN_CAPTURE) {flags &= ~DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR| DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;return flags;} else if (mType == MediaProjectionManager.TYPE_MIRRORING) {flags &= ~(DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR);flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY |DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;return flags;} else if (mType == MediaProjectionManager.TYPE_PRESENTATION) {flags &= ~DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION |DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;return flags;} else {throw new RuntimeException("Unknown MediaProjection type");}}@Override // Binder callpublic void start(final IMediaProjectionCallback callback) {if (callback == null) {throw new IllegalArgumentException("callback must not be null");}synchronized (mLock) {final String uidpid = getCallingUidPid();logd("MediaProjection start: calling uidpid " + uidpid+ ", callback=" + callback);if (isValidMediaProjection(asBinder())) {Slog.w(TAG, "UID " + Binder.getCallingUid()+ " attempted to start already started MediaProjection");return;}if (REQUIRE_FG_SERVICE_FOR_PROJECTION&& requiresForegroundService()&& !mActivityManagerInternal.hasRunningForegroundService(uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)) {throw new SecurityException("Media projections require a foreground service"+ " of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION");}mCallback = callback;registerCallback(mCallback);try {mToken = callback.asBinder();mDeathEater = new IBinder.DeathRecipient() {@Overridepublic void binderDied() {mCallbackDelegate.remove(asBinder(), callback);stop();}};mToken.linkToDeath(mDeathEater, 0);} catch (RemoteException e) {Slog.w(TAG, "MediaProjectionCallbacks must be valid, aborting " +"MediaProjection", e);return;}if (mType == MediaProjectionManager.TYPE_SCREEN_CAPTURE) {final long token = Binder.clearCallingIdentity();try {// We allow an app running a current screen capture session to use// SYSTEM_ALERT_WINDOW for the duration of the session, to enable// them to overlay their UX on top of what is being captured.// We only do this if the app requests the permission, and the appop// is in its default state (the user has neither explicitly allowed nor// disallowed it).final PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageName, PackageManager.GET_PERMISSIONS,UserHandle.getUserId(uid));if (ArrayUtils.contains(packageInfo.requestedPermissions,Manifest.permission.SYSTEM_ALERT_WINDOW)) {final int currentMode = mAppOps.unsafeCheckOpRawNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid, packageName);if (currentMode == AppOpsManager.MODE_DEFAULT) {mAppOps.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid,packageName, AppOpsManager.MODE_ALLOWED);mRestoreSystemAlertWindow = true;}}} catch (PackageManager.NameNotFoundException e) {Slog.w(TAG, "Package not found, aborting MediaProjection", e);return;} finally {Binder.restoreCallingIdentity(token);}}startProjectionLocked(uidpid, this);}}@Override // Binder callpublic void stop() {synchronized (mLock) {logd("MediaProjection stop: calling uidpid " + getCallingUidPid());if (!isValidMediaProjection(asBinder())) {Slog.w(TAG, "Attempted to stop inactive MediaProjection "+ "(uid=" + Binder.getCallingUid() + ", "+ "pid=" + Binder.getCallingPid() + ")");return;}if (mRestoreSystemAlertWindow) {final long token = Binder.clearCallingIdentity();try {// Put the appop back how it was, unless it has been changed from what// we set it to.// Note that WindowManager takes care of removing any existing overlay// windows when we do this.final int currentMode = mAppOps.unsafeCheckOpRawNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid, packageName);if (currentMode == AppOpsManager.MODE_ALLOWED) {mAppOps.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uid, packageName,AppOpsManager.MODE_DEFAULT);}mRestoreSystemAlertWindow = false;} finally {Binder.restoreCallingIdentity(token);}}stopProjectionLocked(this);mToken.unlinkToDeath(mDeathEater, 0);mToken = null;unregisterCallbacks();mCallback = null;}}@Overridepublic void registerCallback(IMediaProjectionCallback callback) {if (callback == null) {throw new IllegalArgumentException("callback must not be null");}logd("MediaProjection registerCallback: MediaProjection(hashCode "+ asBinder().hashCode() + ") callback=" + callback);mCallbackDelegate.add(asBinder(), callback);}@Overridepublic void unregisterCallback(IMediaProjectionCallback callback) {if (callback == null) {throw new IllegalArgumentException("callback must not be null");}logd("MediaProjection unregisterCallback: MediaProjection(hashCode "+ asBinder().hashCode() + ") callback=" + callback);mCallbackDelegate.remove(asBinder(), callback);}private void unregisterCallbacks() {logd("MediaProjection unregister callbacks for MediaProjection(hashCode "+ asBinder().hashCode() + ") because media projection stopped");mCallbackDelegate.remove(asBinder());}public MediaProjectionInfo getProjectionInfo() {return new MediaProjectionInfo(packageName, userHandle);}boolean requiresForegroundService() {return mTargetSdkVersion >= Build.VERSION_CODES.Q && !mIsPrivileged;}public void dump(PrintWriter pw) {pw.println("(" + packageName + ", uid=" + uid + "): " + typeToString(mType));}}private class MediaRouterCallback extends MediaRouter.SimpleCallback {@Overridepublic void onRouteSelected(MediaRouter router, int type, MediaRouter.RouteInfo info) {synchronized (mLock) {logd("MediaRouterCallback onRouteSelected: router=" + router + ", type="+ type + ", info=" + info + ", calling uidpid " + getCallingUidPid());if ((type & MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY) != 0) {mMediaRouteInfo = info;for (MediaProjectionEntry entry : mEntryMap.values()) {if (entry != null) {entry.getProjectionGrant().stop();}}}}}@Overridepublic void onRouteUnselected(MediaRouter route, int type, MediaRouter.RouteInfo info) {logd("MediaRouterCallback onRouteUnselected: router=" + route+ ", type=" + type + ", info=" + info);if (mMediaRouteInfo == info) {mMediaRouteInfo = null;}}}private static class CallbackDelegate {private Map<IBinder, Map<IBinder, IMediaProjectionCallback>> mClientCallbacks;private Map<IBinder, IMediaProjectionWatcherCallback> mWatcherCallbacks;private Handler mHandler;private Object mLock = new Object();public CallbackDelegate() {mHandler = new Handler(Looper.getMainLooper(), null, true /*async*/);mClientCallbacks = new ArrayMap<IBinder, Map<IBinder, IMediaProjectionCallback>>();mWatcherCallbacks = new ArrayMap<IBinder, IMediaProjectionWatcherCallback>();}public void add(IBinder token, IMediaProjectionCallback callback) {synchronized (mLock) {if (mClientCallbacks.get(token) == null) {mClientCallbacks.put(token, new ArrayMap<IBinder, IMediaProjectionCallback>());logd("CallbackDelegate add: create callback container for "+ "MediaProjection(hashCode " + token.hashCode() + ")");}Map<IBinder, IMediaProjectionCallback> callbacks = mClientCallbacks.get(token);callbacks.put(callback.asBinder(), callback);logd("CallbackDelegate add callback: MediaProjection(hashCode "+ token.hashCode() + ") callback=" + callback+ ", remaining IMediaProjectionCallback for MediaProjection(hashCode "+ token.hashCode() + ") count " + callbacks.size()+ ", remaining IMediaProjectionCallback count "+ mClientCallbacks.size());}}public void add(IMediaProjectionWatcherCallback callback) {synchronized (mLock) {mWatcherCallbacks.put(callback.asBinder(), callback);logd("CallbackDelegate add callback " + callback.hashCode()+ ", remaining IMediaProjectionWatcherCallback count "+ mWatcherCallbacks.size());}}public void remove(IBinder token, IMediaProjectionCallback callback) {synchronized (mLock) {Map<IBinder, IMediaProjectionCallback> callbacks = mClientCallbacks.get(token);if (callbacks != null) {callbacks.remove(callback.asBinder());logd("CallbackDelegate remove callback: MediaProjection(hashCode "+ token.hashCode() + ") callback=" + callback+ ", remaining IMediaProjectionCallback for MediaProjection(hashCode "+ token.hashCode() + ") count " + callbacks.size()+ ", remaining IMediaProjectionCallback count "+ mClientCallbacks.size());if (callbacks.isEmpty()) {mClientCallbacks.remove(token);}}}}public void remove(IBinder token) {synchronized (mLock) {mClientCallbacks.remove(token);logd("CallbackDelegate remove all callbacks for MediaProjection(hashCode "+ token.hashCode() + ")");}}public void remove(IMediaProjectionWatcherCallback callback) {synchronized (mLock) {mWatcherCallbacks.remove(callback.asBinder());logd("CallbackDelegate remove callback " + callback.hashCode()+ ", remaining IMediaProjectionWatcherCallback count "+ mWatcherCallbacks.size());}}public void dispatchStart(MediaProjection projection) {if (projection == null) {Slog.e(TAG, "Tried to dispatch start notification for a null media projection."+ " Ignoring!");return;}synchronized (mLock) {logd("CallbackDelegate dispatchStart: projection="+ projection.getProjectionInfo() + " hashCode "+ projection.asBinder().hashCode()+ ", IMediaProjectionWatcherCallback count " + mWatcherCallbacks.size());for (IMediaProjectionWatcherCallback callback : mWatcherCallbacks.values()) {MediaProjectionInfo info = projection.getProjectionInfo();mHandler.post(new WatcherStartCallback(info, callback));}}}public void dispatchStop(MediaProjection projection) {if (projection == null) {Slog.e(TAG, "Tried to dispatch stop notification for a null media projection."+ " Ignoring!");return;}synchronized (mLock) {logd("CallbackDelegate dispatchStop: projection="+ projection.getProjectionInfo() + " hashCode "+ projection.asBinder().hashCode()+ ", IMediaProjectionWatcherCallback count " + mWatcherCallbacks.size()+ ", IMediaProjectionCallback count " + mClientCallbacks.size());Map<IBinder, IMediaProjectionCallback> callbacks =mClientCallbacks.get(projection.asBinder());if (callbacks != null) {for (IMediaProjectionCallback cb : callbacks.values()) {logd("CallbackDelegate dispatchStop: post IMediaProjectionCallback=" + cb);mHandler.post(new ClientStopCallback(cb));}}for (IMediaProjectionWatcherCallback callback : mWatcherCallbacks.values()) {MediaProjectionInfo info = projection.getProjectionInfo();mHandler.post(new WatcherStopCallback(info, callback));}}}}private static final class WatcherStartCallback implements Runnable {private IMediaProjectionWatcherCallback mCallback;private MediaProjectionInfo mInfo;public WatcherStartCallback(MediaProjectionInfo info,IMediaProjectionWatcherCallback callback) {mInfo = info;mCallback = callback;}@Overridepublic void run() {try {mCallback.onStart(mInfo);} catch (RemoteException e) {Slog.w(TAG, "Failed to notify media projection has stopped", e);}}}private static final class WatcherStopCallback implements Runnable {private IMediaProjectionWatcherCallback mCallback;private MediaProjectionInfo mInfo;public WatcherStopCallback(MediaProjectionInfo info,IMediaProjectionWatcherCallback callback) {mInfo = info;mCallback = callback;}@Overridepublic void run() {try {mCallback.onStop(mInfo);} catch (RemoteException e) {Slog.w(TAG, "Failed to notify media projection has stopped", e);}}}private static final class ClientStopCallback implements Runnable {private IMediaProjectionCallback mCallback;public ClientStopCallback(IMediaProjectionCallback callback) {mCallback = callback;}@Overridepublic void run() {try {mCallback.onStop();} catch (RemoteException e) {Slog.w(TAG, "Failed to notify media projection has stopped", e);}}}private static String typeToString(int type) {switch (type) {case MediaProjectionManager.TYPE_SCREEN_CAPTURE:return "TYPE_SCREEN_CAPTURE";case MediaProjectionManager.TYPE_MIRRORING:return "TYPE_MIRRORING";case MediaProjectionManager.TYPE_PRESENTATION:return "TYPE_PRESENTATION";}return Integer.toString(type);}private static final class MediaProjectionEntry {private String mUidPid;private IBinder mProjectionToken;private MediaProjection mProjectionGrant;public MediaProjectionEntry(String uidpid, IBinder token, MediaProjection mp) {this.mUidPid = uidpid;this.mProjectionToken = token;this.mProjectionGrant = mp;}public String getUidPid() {return mUidPid;}public IBinder getProjectionToken() {return mProjectionToken;}public MediaProjection getProjectionGrant() {return mProjectionGrant;}public void dump(PrintWriter pw) {pw.println("(" + mProjectionGrant.packageName + ", uid_pid=" + mUidPid+ ", token=" + mProjectionToken.hashCode() + "): "+ typeToString(mProjectionGrant.mType));}}private MediaProjectionEntry getMediaProjectionEntryByUidPid(String uidpid) {if (mEntryMap == null || mEntryMap.isEmpty() || uidpid == null || uidpid.length() <= 0) {return null;}for (MediaProjectionEntry entry : mEntryMap.values()) {if (entry != null && uidpid.equals(entry.getUidPid())) {return entry;}}return null;}private String getCallingUidPid() {int pid = Binder.getCallingPid();int uid = Binder.getCallingUid();return String.format("%s_%s", uid, pid);}private static void logd(String msg) {logd(TAG, msg);}private static void loge(String msg) {loge(TAG, msg);}private static void logd(String tag, String msg) {if (DEBUG) {Slog.d(tag, msg);}}private static void loge(String tag, String msg) {Slog.e(tag, msg);}}

这篇关于Android11实现能同时开多个录屏应用(或者共享屏幕或投屏时录屏)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/825542

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

zoj3820(树的直径的应用)

题意:在一颗树上找两个点,使得所有点到选择与其更近的一个点的距离的最大值最小。 思路:如果是选择一个点的话,那么点就是直径的中点。现在考虑两个点的情况,先求树的直径,再把直径最中间的边去掉,再求剩下的两个子树中直径的中点。 代码如下: #include <stdio.h>#include <string.h>#include <algorithm>#include <map>#

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo