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);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/754766.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Cookie 信息泄露 Cookie未设置http only属性 原理以及修复方法

漏洞名称&#xff1a;Cookie信息泄露、Cookie安全性漏洞、Cookie未设置httponly属性 漏洞描述&#xff1a; cookie的属性设置不当可能会造成系统用户安全隐患&#xff0c;Cookie信息泄露是Cookiehttp only配置缺陷引起的&#xff0c;在设置Cookie时&#xff0c;可以设置的一个…

Visual Studio .NET 中常用的文件类型

Visual Studio .NET 中常用的文件类型 扩展名名称描述.slnVisual studio .NET解决方案文件.sln文件为解决方案资源管理器提供显示管理文件的图形接口所需的信息。打开.sln文件能快捷地打开整个项目的所有文件.csprojVisual C# 项目文件一个特殊的XML文档&#xff0c;主要用来控…

SQLiteC/C++接口详细介绍sqlite3_stmt类简介

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;十八&#xff09; 下一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;一&#xff09; 预准备语句对象 typedef struct sqlite3_stmt sqlite3_stmt…

【洛谷 P9232】[蓝桥杯 2023 省 A] 更小的数 题解(字符串+区间DP)

[蓝桥杯 2023 省 A] 更小的数 题目描述 小蓝有一个长度均为 n n n 且仅由数字字符 0 ∼ 9 0 \sim 9 0∼9 组成的字符串&#xff0c;下标从 0 0 0 到 n − 1 n-1 n−1&#xff0c;你可以将其视作是一个具有 n n n 位的十进制数字 n u m num num&#xff0c;小蓝可以从 n…

java 程序连接 redis 集群 的时候报错 MUTLI is currently not supported in cluster mode

找了半天找不到,为什么国内文章环境是真的差&#xff0c; redis 集群不支持事务&#xff0c;而你的方法上面估计使用了 spring 的事务导致错误具体解决&#xff1a; Transactional(propagation Propagation.NOT_SUPPORTED)public <T> void removeMultiCacheMapValue…

内置泵电源,热保护电路等功能的场扫描电路D78040,偏转电流可达1.7Ap-p,可用于中小型显示器。

D78040是一款场扫描电路&#xff0c;偏转电流可达1.7Ap-p&#xff0c;可用于中小型显示器。 二 特 点 1、有内置泵电源 2、垂直输出电路 3、热保护电路 4、偏转电流可达1.7Ap-p 三 基本参数 四 应用电路图 1、应用线路 2、PIN5脚输出波形如下&#xff1a;

6-高维空间:机器如何面对越来越复杂的问题

声明 本文章基于哔哩哔哩付费课程《小白也能听懂的人工智能原理》。仅供学习记录、分享&#xff0c;严禁他用&#xff01;&#xff01;如有侵权&#xff0c;请联系删除 目录 一、知识引入 &#xff08;一&#xff09;二维输入数据 &#xff08;二&#xff09;数据特征维度 …

一级指针和二级指针

一级指针 形式&#xff1a;int a 2; int *p &a; 解释&#xff1a; int*p &a表示一级指针p指向变量a的值。此时一级指针p存放的是a的地址&#xff0c;*p解引用是a的值。 作用&#xff1a; c中随处可见。不多言。 二级指针 形式&#xff1a;int a 2; int *p &…

C语言calloc函数的特点,效率低。但是进行初始化操作

#define _CRT_SECURE_NO_WARNINGS 1 #include<stdlib.h> #include<string.h> #include<errno.h> #include<stdio.h> int main() { int *p (int *)calloc(10,sizeof(int)); //初始化&#xff0c;效率低&#xff0c;然而malloc函数相反&#xf…

Linux/Ubuntu/Debian的终端中和的区别

下边举例说明&#xff1a; “cd /home & wine ps.exe”和“cd /home && wine ps.exe”之间的区别在于命令在类 Unix shell 环境&#xff08;例如 Linux 或 macOS&#xff09;中执行的方式&#xff1a; ‘cd /home & wine ps.exe’: 在此命令中&#xff0c;“…

最细致最简单的 Arm 架构搭建 Harbor

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; ARM离线版本安装 官方提供了一个 arm 版本&#xff0c;但是好久都没更新了&#xff0c;地址&#xff1a;https://github.com/goharbor/harbor-arm 。 也不知道为什么不更新&#xff0c;我看…

数据机构-2

线性表 概念 顺序表 示例&#xff1a;创建一个存储学生信息的顺序表 表头&#xff08;Tlen总长度&#xff0c; Clen当前长度&#xff09; 函数 #include <seqlist.c> #include <stdio.h> #include <stdlib.h> #include "seqlist.h" #include &…

Auto-DataProcessing:一组让制作数据集变轻松的脚本

前言 最近跟同学参加了个比赛&#xff0c;我负责Object-Detection的技术实现&#xff0c;需要从网上扒大量的数据(主办方每种识别物就给了一张demo&#x1f923;)&#xff0c;发现数据准备是一个真的是一个非常重要但又耗时耗力的过程。对我来说&#xff0c;给我一类待识别的标…

sqlite简单的增删改查

1&#xff0c;插入数据 (INSERT): INSERT INTO table_name (column1, column2, column3, ...) VALUES (value1, value2, value3, ...); //示例&#xff1a; INSERT INTO employees (name, age, department) VALUES (John Doe, 30, Sales);2&#xff0c;查询数据 (SELECT): S…

路由器级联

目录 一、实现功能二、实现步骤2.1 接线步骤 三、效果3.1 常规连接3.2 路由器级联 一、实现功能 主路由器&#xff1a;可有WiFi功能&#xff0c;LAN口下接各设备&#xff0c;并接一个辅路由器辅路由器&#xff1a;开启WiFi功能&#xff0c;有线或无线下接各设备功能&#xff1…

学上一招,使用 telnet 调试邮件发送

文章目录 背景正常的debug日志报错的debug日志分析SMTP协议相关命令SMTP 命令扩展 SMTP 命令使用 telnet 测试 SMTP 通信 - 邮件调试小帮手!正常情况报错情况SMTP 响应码Java 报错信息参考🔊 背景:生产环境,在 K8s 中部署发送邮件的微服务(多实例),发现不同的 pod 发送邮…

一.Netedit的简要介绍

目录 一.Netedit的简要介绍 功能二:删除项(delete) 功能三:选择项(select) 功能四:移动项(move)

IDEA直接打包Docker镜像

以下为使用IDEA打包Docker镜像并推送到远程仓库&#xff08;使用Windows打包Docker镜像并推送到远程仓库&#xff09;教程 1 安装Docker Desktop 下载地址&#xff1a;https://www.docker.com/products/docker-desktop/ 安装成功后&#xff0c;可在cmd查看版本号 2 启动Do…

qt开发记录

文章目录 1、QT介绍2、信号和槽机制3、提升类4、播放媒体5、增加新的组件6、联合vscode开发 1、QT介绍 打开的主页面介绍&#xff0c;下面是一张新的工程的QT编辑栏&#xff0c;我这里大致分为三类&#xff0c;分别标注如下&#xff1a; 下面对这三个进行分别说明&#xff1a…

leetcode刷题(javaScript)——动态规划相关场景题总结

动态规划在 JavaScript 刷题中有一定的难度&#xff0c;但也是非常常见和重要的算法思想。动态规划通常适用于需要求解最优解、最大值、最小值等问题的场景&#xff0c;可以将复杂问题拆分成子问题&#xff0c;通过存储子问题的解来避免重复计算&#xff0c;从而提高效率。 理解…